rbatis/
crud.rs

1///PySql: gen select*,update*,insert*,delete* ... methods
2///```rust
3/// use rbs::value;
4/// use rbatis::{Error, RBatis};
5///
6/// #[derive(serde::Serialize, serde::Deserialize)]
7/// pub struct MockTable{
8///    pub id: Option<String>
9/// }
10/// rbatis::crud!(MockTable{}); //or crud!(MockTable{},"mock_table");
11///
12/// //use
13/// async fn test_use(rb:&RBatis) -> Result<(),Error>{
14///  let table = MockTable{id: Some("1".to_string())};
15///  let r = MockTable::insert(rb, &table).await;
16///  let r = MockTable::insert_batch(rb, std::slice::from_ref(&table),10).await;
17///
18///  let tables = MockTable::select_by_map(rb,value!{"id":"1"}).await;
19///  let tables = MockTable::select_all(rb).await;
20///  let tables = MockTable::select_by_map(rb,value!{"id":["1","2","3"]}).await;
21///  let tables = MockTable::select_by_map(rb,value!{"id":"1", "column": ["id", "name"]}).await?; 
22///
23///  let r = MockTable::update_by_map(rb, &table, value!{"id":"1"}).await;
24///
25///  let r = MockTable::delete_by_map(rb, value!{"id":"1"}).await;
26///  //... and more
27///  Ok(())
28/// }
29///
30///
31/// ```
32#[macro_export]
33macro_rules! crud {
34    ($table:ty{}) => {
35        $crate::impl_insert!($table {});
36        $crate::impl_select!($table {});
37        $crate::impl_update!($table {});
38        $crate::impl_delete!($table {});
39    };
40    ($table:ty{},$table_name:expr) => {
41        $crate::impl_insert!($table {}, $table_name);
42        $crate::impl_select!($table {}, $table_name);
43        $crate::impl_update!($table {}, $table_name);
44        $crate::impl_delete!($table {}, $table_name);
45    };
46}
47
48///PySql: gen sql => INSERT INTO table_name (column1,column2,column3,...) VALUES (value1,value2,value3,...);
49///
50/// example:
51///```rust
52/// use rbatis::{Error, RBatis};
53/// #[derive(serde::Serialize, serde::Deserialize)]
54/// pub struct MockTable{
55///   pub id: Option<String>
56/// }
57/// rbatis::impl_insert!(MockTable{});
58///
59/// //use
60/// async fn test_use(rb:&RBatis) -> Result<(),Error>{
61///  let table = MockTable{id: Some("1".to_string())};
62///  let r = MockTable::insert(rb, &table).await;
63///  let r = MockTable::insert_batch(rb, std::slice::from_ref(&table),10).await;
64///  Ok(())
65/// }
66/// ```
67///
68#[macro_export]
69macro_rules! impl_insert {
70    ($table:ty{}) => {
71        $crate::impl_insert!($table {}, "");
72    };
73    ($table:ty{},$table_name:expr) => {
74        impl $table {
75            pub async fn insert_batch(
76                executor: &dyn $crate::executor::Executor,
77                tables: &[$table],
78                batch_size: u64,
79            ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
80                use $crate::crud_traits::ColumnSet;
81                #[$crate::py_sql(
82                    "`insert into ${table_name} `
83                    trim ',':
84                     bind columns = tables.column_sets():
85                     for idx,table in tables:
86                      if idx == 0:
87                         `(`
88                         trim ',':
89                           for _,v in columns:
90                              ${v},
91                         `) VALUES `
92                      (
93                      trim ',':
94                       for _,v in columns:
95                         #{table[v]},
96                      ),
97                    "
98                )]
99                async fn insert_batch(
100                    executor: &dyn $crate::executor::Executor,
101                    tables: &[$table],
102                    table_name: &str,
103                ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error>
104                {
105                    impled!()
106                }
107                if tables.is_empty() {
108                    return Err($crate::rbdc::Error::from(
109                        "insert can not insert empty array tables!",
110                    ));
111                }
112                let mut table_name = $table_name.to_string();
113                if table_name.is_empty(){
114                         #[$crate::snake_name($table)]
115                         fn snake_name(){}
116                         table_name = snake_name();
117                }
118                let mut result = $crate::rbdc::db::ExecResult {
119                    rows_affected: 0,
120                    last_insert_id: rbs::Value::Null,
121                };
122                let ranges = $crate::plugin::Page::<()>::make_ranges(tables.len() as u64, batch_size);
123                for (offset, limit) in ranges {
124                    let exec_result = insert_batch(
125                        executor,
126                        &tables[offset as usize..limit as usize],
127                        table_name.as_str(),
128                    )
129                    .await?;
130                    result.rows_affected += exec_result.rows_affected;
131                    result.last_insert_id = exec_result.last_insert_id;
132                }
133                Ok(result)
134            }
135
136            pub async fn insert(
137                executor: &dyn $crate::executor::Executor,
138                table: &$table,
139            ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
140                <$table>::insert_batch(executor, std::slice::from_ref(table), 1).await
141            }
142        }
143    };
144}
145
146///PySql: gen sql => SELECT (column1,column2,column3,...) FROM table_name (column1,column2,column3,...)  *** WHERE ***
147///
148/// Supports selective column queries by specifying "column" key in condition:
149///```rust
150/// use rbs::value;
151/// use rbatis::{Error, RBatis};
152/// #[derive(serde::Serialize, serde::Deserialize)]
153/// pub struct MockTable{
154///   pub id: Option<String>,
155///   pub name: Option<String>,
156///   pub status: Option<String>
157/// }
158/// /// default
159///rbatis::impl_select!(MockTable{});
160///rbatis::impl_select!(MockTable{select_all_by_id(id:&str,name:&str) => "`where id = #{id} and name = #{name}`"});
161/// /// container result
162///rbatis::impl_select!(MockTable{select_by_id(id:String) -> Option => "`where id = #{id} limit 1`"});
163///rbatis::impl_select!(MockTable{select_by_id2(id:String) -> Vec => "`where id = #{id} limit 1`"});
164///
165/// //usage
166/// async fn test_select(rb:&RBatis) -> Result<(),Error>{
167///    // Select all columns
168///    let r = MockTable::select_by_map(rb,value!{"id":"1"}).await?;
169///
170///    // Select only specific columns (id and name)
171///    let r = MockTable::select_by_map(rb,value!{"id":"1", "column": ["id", "name"]}).await?;
172///
173///    let r = MockTable::select_all_by_id(rb,"1","xxx").await?;
174///    let r:Option<MockTable> = MockTable::select_by_id(rb,"1".to_string()).await?;
175///    let r:Vec<MockTable> = MockTable::select_by_id2(rb,"1".to_string()).await?;
176///    Ok(())
177/// }
178/// ```
179///
180#[macro_export]
181macro_rules! impl_select {
182    ($table:ty{}) => {
183        $crate::impl_select!($table{},"");
184    };
185    ($table:ty{},$table_name:expr) => {
186        $crate::impl_select!($table{select_all() => ""},$table_name);
187          impl $table {
188         pub async fn select_by_map(executor: &dyn $crate::executor::Executor, mut condition: rbs::Value) -> std::result::Result<Vec<$table>, $crate::rbdc::Error> {
189                use rbatis::crud_traits::ValueOperatorSql;
190
191                // Extract column specification and remove it from condition
192                let table_column = {
193                    let mut columns = String::new();
194                    let mut clean_map = rbs::value::map::ValueMap::with_capacity(condition.len());
195                    for (k, v) in condition {
196                            match k.as_str() {
197                                Some("column") => {
198                                    columns = match v {
199                                        rbs::Value::String(s) => s.clone(),
200                                        rbs::Value::Array(arr) => {
201                                            let cols: Vec<&str> = arr.iter()
202                                                .filter_map(|v| v.as_str())
203                                                .collect();
204                                            if cols.is_empty() { "*".to_string() } else { cols.join(", ") }
205                                        }
206                                        _ => "*".to_string(),
207                                    };
208                                }
209                                _ => { clean_map.insert(k.clone(), v.clone()); }
210                            }
211                    }
212                    if columns.is_empty() { columns = "*".to_string(); }
213                    condition = rbs::Value::Map(clean_map);
214                    columns
215                };
216
217                #[$crate::py_sql(
218          "`select ${table_column} from ${table_name}`
219           trim end=' where ':
220             ` where `
221             trim ' and ': for key,item in condition:
222                          if item == null:
223                             continue:
224                          if !item.is_array():
225                            ` and ${key.operator_sql()}#{item}`
226                          if item.is_array():
227                            ` and ${key} in (`
228                               trim ',': for _,item_array in item:
229                                    #{item_array},
230                            `)`
231        ")]
232                async fn select_by_map(
233                    executor: &dyn $crate::executor::Executor,
234                    table_name: String,
235                    table_column: &str,
236                    condition: &rbs::Value
237                ) -> std::result::Result<Vec<$table>, $crate::rbdc::Error> {
238                    for (_,v) in condition {
239                        if v.is_array() && v.is_empty(){
240                           return Ok(vec![]);
241                        }
242                    }
243                    impled!()
244                }
245                let mut table_name = $table_name.to_string();
246                if table_name.is_empty(){
247                         #[$crate::snake_name($table)]
248                         fn snake_name(){}
249                         table_name = snake_name();
250                }
251                select_by_map(executor, table_name, &table_column, &condition).await
252       }
253    }
254    };
255    ($table:ty{$fn_name:ident $(< $($gkey:ident:$gtype:path $(,)?)* >)? ($($param_key:ident:$param_type:ty $(,)?)*) => $sql:expr}$(,$table_name:expr)?) => {
256        $crate::impl_select!($table{$fn_name$(<$($gkey:$gtype,)*>)?($($param_key:$param_type,)*) ->Vec => $sql}$(,$table_name)?);
257    };
258    ($table:ty{$fn_name:ident $(< $($gkey:ident:$gtype:path $(,)?)* >)? ($($param_key:ident:$param_type:ty $(,)?)*) -> $container:tt => $sql:expr}$(,$table_name:expr)?) => {
259        impl $table{
260            pub async fn $fn_name $(<$($gkey:$gtype,)*>)? (executor: &dyn  $crate::executor::Executor,$($param_key:$param_type,)*) -> std::result::Result<$container<$table>,$crate::rbdc::Error>
261            {
262                     use rbatis::crud_traits::ValueOperatorSql;
263                     #[$crate::py_sql("`select ${table_column} from ${table_name} `\n",$sql)]
264                     async fn $fn_name$(<$($gkey: $gtype,)*>)?(executor: &dyn $crate::executor::Executor,table_column:&str,table_name:&str,$($param_key:$param_type,)*) -> std::result::Result<$container<$table>,$crate::rbdc::Error> {impled!()}
265                     
266                     let mut table_column = "*".to_string();
267                     let mut table_name = String::new();
268                     $(table_name = $table_name.to_string();)?
269                     if table_name.is_empty(){
270                         #[$crate::snake_name($table)]
271                         fn snake_name(){}
272                         table_name = snake_name();
273                     }
274                     $fn_name(executor,&table_column,&table_name,$($param_key ,)*).await
275            }
276        }
277    };
278}
279
280/// PySql: gen sql = UPDATE table_name SET column1=value1,column2=value2,... WHERE some_column=some_value;
281///
282/// Supports selective column updates by specifying "column" key in condition (GitHub issue #591):
283/// ```rust
284/// use rbs::value;
285/// use rbatis::{Error, RBatis};
286/// #[derive(serde::Serialize, serde::Deserialize)]
287/// pub struct MockTable{
288///   pub id: Option<String>,
289///   pub name: Option<String>,
290///   pub status: Option<String>
291/// }
292/// rbatis::impl_update!(MockTable{});
293/// //use
294/// async fn test_use(rb:&RBatis) -> Result<(),Error>{
295///  let table = MockTable{
296///     id: Some("1".to_string()),
297///     name: Some("test".to_string()),
298///     status: Some("active".to_string())
299///  };
300///  // Update all columns
301///  let r = MockTable::update_by_map(rb, &table, value!{"id":"1"}).await;
302///
303///  // Update only specific columns (name and status)
304///  let r = MockTable::update_by_map(rb, &table, value!{"id":"1", "column": ["name", "status"]}).await;
305///  Ok(())
306/// }
307/// ```
308#[macro_export]
309macro_rules! impl_update {
310    ($table:ty{}) => {
311        $crate::impl_update!(
312            $table{},
313            ""
314        );
315    };
316    ($table:ty{},$table_name:expr) => {
317       impl $table {
318            pub async fn update_by_map(
319                executor: &dyn $crate::executor::Executor,
320                table: &$table,
321                mut condition: rbs::Value
322            ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
323                use rbatis::crud_traits::{ValueOperatorSql, FilterByColumns};
324
325                // Extract column list for selective updates - implements GitHub issue #591
326                // This allows updating only specific columns by specifying them in the condition
327                // Example: update_by_map(&rb, &activity, value!{"id": "123", "column": ["name", "status"]})
328                let set_columns = {
329                    let mut columns = rbs::Value::Null;
330                    let mut clean_map = rbs::value::map::ValueMap::with_capacity(condition.len());
331                    for (k, v) in condition {
332                            match k.as_str() {
333                                Some("column") => {
334                                    // Normalize column specification to Array format for filter_by_columns
335                                    columns = match v {
336                                        rbs::Value::String(s) => {
337                                            rbs::Value::Array(vec![rbs::Value::String(s.clone())])
338                                        }
339                                        rbs::Value::Array(arr) => {
340                                            let filtered_array: Vec<rbs::Value> = arr.iter()
341                                                .filter(|v| v.as_str().is_some())
342                                                .cloned()
343                                                .collect();
344                                            if filtered_array.is_empty() {
345                                                rbs::Value::Null
346                                            } else {
347                                                rbs::Value::Array(filtered_array)
348                                            }
349                                        }
350                                        _ => rbs::Value::Null,
351                                    };
352                                }
353                                _ => { clean_map.insert(k.clone(), v.clone()); }
354                            }
355                    }
356                    condition = rbs::Value::Map(clean_map);
357                    columns
358                };
359                #[$crate::py_sql(
360                    "`update ${table_name}
361                      if skip_null == false:
362                        set collection='table',skips='id',skip_null=false:
363                      if skip_null == true:
364                        set collection='table',skips='id':
365                      trim end=' where ':
366                       ` where `
367                       trim ' and ': for key,item in condition:
368                            if item == null:
369                               continue:
370                            if !item.is_array():
371                              ` and ${key.operator_sql()}#{item}`
372                            if item.is_array():
373                              ` and ${key} in (`
374                                 trim ',': for _,item_array in item:
375                                      #{item_array},
376                              `)`
377                    "
378                )]
379                  async fn update_by_map_internal(
380                      executor: &dyn $crate::executor::Executor,
381                      table_name: String,
382                      table: &rbs::Value,
383                      condition: &rbs::Value,
384                      skip_null: bool,
385                  ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
386                      for (_,v) in condition {
387                        if v.is_array() && v.is_empty(){
388                           return Ok($crate::rbdc::db::ExecResult::default());
389                        }
390                      }
391                      impled!()
392                  }
393                  let mut table_name = $table_name.to_string();
394                  if table_name.is_empty(){
395                         #[$crate::snake_name($table)]
396                         fn snake_name(){}
397                         table_name = snake_name();
398                  }
399                  let table_value = rbs::value!(table);
400                  let mut skip_null = true;
401                  let table = if set_columns != rbs::Value::Null {
402                    skip_null = false;
403                    table_value.filter_by_columns(&set_columns)
404                  } else {
405                    table_value
406                  };
407                  update_by_map_internal(executor, table_name, &table, &condition, skip_null).await
408            }
409        }
410    };
411    ($table:ty{$fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) => $sql_where:expr}$(,$table_name:expr)?) => {
412        impl $table {
413            pub async fn $fn_name(
414                executor: &dyn $crate::executor::Executor,
415                table: &$table,
416                $($param_key:$param_type,)*
417            ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
418                use rbatis::crud_traits::ValueOperatorSql;
419                if $sql_where.is_empty(){
420                    return Err($crate::rbdc::Error::from("sql_where can't be empty!"));
421                }
422                #[$crate::py_sql("`update ${table_name}`\n set collection='table',skips='id':\n",$sql_where)]
423                  async fn $fn_name(
424                      executor: &dyn $crate::executor::Executor,
425                      table_name: String,
426                      table: &rbs::Value,
427                      $($param_key:$param_type,)*
428                  ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
429                      impled!()
430                  }
431                  let mut table_name = String::new();
432                  $(table_name = $table_name.to_string();)?
433                  if table_name.is_empty(){
434                         #[$crate::snake_name($table)]
435                         fn snake_name(){}
436                         table_name = snake_name();
437                  }
438                  let table = rbs::value!(table);
439                  $fn_name(executor, table_name, &table, $($param_key,)*).await
440            }
441        }
442    };
443}
444
445/// PySql: gen sql = DELETE FROM table_name WHERE some_column=some_value;
446///
447/// ```rust
448/// use rbs::value;
449/// use rbatis::{Error, RBatis};
450/// #[derive(serde::Serialize, serde::Deserialize)]
451/// pub struct MockTable{}
452/// rbatis::impl_delete!(MockTable{});
453///
454/// //use
455/// async fn test_use(rb:&RBatis) -> Result<(),Error>{
456///  let r = MockTable::delete_by_map(rb, value!{"id":"1"}).await;
457///  //... and more
458///  Ok(())
459/// }
460/// ```
461#[macro_export]
462macro_rules! impl_delete {
463    ($table:ty{}) => {
464        $crate::impl_delete!(
465            $table{},
466            ""
467        );
468    };
469    ($table:ty{},$table_name:expr) => {
470        impl $table {
471         pub async fn delete_by_map(executor: &dyn $crate::executor::Executor, condition: rbs::Value) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
472                use rbatis::crud_traits::ValueOperatorSql;
473                #[$crate::py_sql(
474         "`delete from ${table_name}`
475           trim end=' where ':
476             ` where `
477             trim ' and ': for key,item in condition:
478                          if item == null:
479                             continue:
480                          if !item.is_array():
481                            ` and ${key.operator_sql()}#{item}`
482                          if item.is_array():
483                            ` and ${key} in (`
484                               trim ',': for _,item_array in item:
485                                    #{item_array},
486                            `)`
487        ")]
488                async fn delete_by_map(
489                    executor: &dyn $crate::executor::Executor,
490                    table_name: String,
491                    condition: &rbs::Value
492                ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
493                    for (_,v) in condition {
494                        if v.is_array() && v.is_empty(){
495                           return Ok($crate::rbdc::db::ExecResult::default());
496                        }
497                    }
498                    impled!()
499                }
500                let mut table_name = $table_name.to_string();
501                if table_name.is_empty(){
502                         #[$crate::snake_name($table)]
503                         fn snake_name(){}
504                         table_name = snake_name();
505                }
506                delete_by_map(executor, table_name, &condition).await
507       }
508    }
509};
510( $ table:ty{$ fn_name:ident $(< $($gkey:ident:$gtype:path $(,)?)* >)? ($($param_key:ident:$param_type:ty$(,)?)*) => $sql_where:expr}$(,$table_name:expr)?) => {
511        impl $table {
512            pub async fn $fn_name$(<$($gkey:$gtype,)*>)?(
513                executor: &dyn $crate::executor::Executor,
514                $($param_key:$param_type,)*
515            ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
516                use rbatis::crud_traits::ValueOperatorSql;
517                if $sql_where.is_empty(){
518                    return Err($crate::rbdc::Error::from("sql_where can't be empty!"));
519                }
520                #[$crate::py_sql("`delete from ${table_name} `\n",$sql_where)]
521                async fn $fn_name$(<$($gkey: $gtype,)*>)?(
522                    executor: &dyn $crate::executor::Executor,
523                    table_name: String,
524                    $($param_key:$param_type,)*
525                ) -> std::result::Result<$crate::rbdc::db::ExecResult, $crate::rbdc::Error> {
526                    impled!()
527                }
528                let mut table_name = String::new();
529                $(table_name = $table_name.to_string();)?
530                if table_name.is_empty(){
531                         #[$crate::snake_name($table)]
532                         fn snake_name(){}
533                         table_name = snake_name();
534                }
535                $fn_name(executor, table_name, $($param_key,)*).await
536            }
537        }
538    };
539}
540
541
542/// pysql impl_select_page
543///
544/// do_count: default do_count is a bool param value to determine the statement type
545///
546/// ```rust
547/// #[derive(serde::Serialize, serde::Deserialize)]
548/// pub struct MockTable{}
549/// rbatis::impl_select_page!(MockTable{select_page() =>"
550///      if do_count == false:
551///        `order by create_time desc`"});
552/// ```
553///
554/// you can see ${page_no} = (page_no -1) * page_size;
555/// you can see ${page_size} = page_size;
556#[macro_export]
557macro_rules! impl_select_page {
558    ($table:ty{$fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) => $where_sql:expr}) => {
559        $crate::impl_select_page!(
560            $table{$fn_name($($param_key:$param_type,)*)=> $where_sql},
561            ""
562        );
563    };
564    ($table:ty{$fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) => $where_sql:expr}$(,$table_name:expr)?) => {
565        impl $table {
566            pub async fn $fn_name(
567                executor: &dyn $crate::executor::Executor,
568                page_request: &dyn $crate::plugin::IPageRequest,
569                $($param_key:$param_type,)*
570            ) -> std::result::Result<$crate::plugin::Page::<$table>, $crate::rbdc::Error> {
571                let mut table_column = "*".to_string();
572                let mut table_name = String::new();
573                let mut table_name = String::new();
574                $(table_name = $table_name.to_string();)?
575                if table_name.is_empty(){
576                         #[$crate::snake_name($table)]
577                         fn snake_name(){}
578                         table_name = snake_name();
579                }
580                $crate::pysql_select_page!($fn_name(
581                                     table_column:&str,
582                                     table_name: &str,
583                                     $($param_key:&$param_type,)*) -> $table => 
584               "`select ${table_column} from ${table_name} `\n",$where_sql);
585               
586                let page = $fn_name(executor,page_request,&table_column,&table_name,$(&$param_key,)*).await?;
587                Ok(page)
588            }
589        }
590    };
591}
592
593/// impl html_sql select page.
594///
595/// you must deal with 3 param:
596/// (do_count:bool,page_no:u64,page_size:u64)
597///
598/// you must deal with sql:
599/// return Vec<Record>(if param do_count = false)
600/// return u64(if param do_count = true)
601///
602/// you can see ${page_no} = (page_no -1) * page_size;
603/// you can see ${page_size} = page_size;
604///
605/// just like this example:
606/// ```html
607/// <select id="select_page_data">
608///         `select `
609///         <if test="do_count == true">
610///             `count(1) from table`
611///         </if>
612///         <if test="do_count == false">
613///             `* from table limit ${page_no},${page_size}`
614///         </if>
615///   </select>
616/// ```
617/// ```
618/// #[derive(serde::Serialize, serde::Deserialize)]
619/// pub struct MockTable{}
620/// //rbatis::htmlsql_select_page!(select_page_data(name: &str) -> MockTable => "example.html");
621/// rbatis::htmlsql_select_page!(select_page_data(name: &str) -> MockTable => r#"
622/// <select id="select_page_data">
623///  `select * from table  where id > 1  limit ${page_no},${page_size} `
624/// </select>"#);
625///
626/// rbatis::pysql_select_page!(pysql_select_page(name:&str) -> MockTable =>
627///     r#"`select * from activity where delete_flag = 0`
628///         if name != '':
629///            ` and name=#{name}`
630///       ` limit ${page_no},${page_size}`
631/// "#);
632/// ```
633#[macro_export]
634macro_rules! htmlsql_select_page {
635    ($fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) -> $table:ty => $($html_file:expr$(,)?)*) => {
636            pub async fn $fn_name(executor: &dyn $crate::executor::Executor, page_request: &dyn $crate::plugin::IPageRequest, $($param_key:$param_type,)*) -> std::result::Result<$crate::plugin::Page<$table>, $crate::rbdc::Error> {
637             #[$crate::html_sql($($html_file,)*)]
638             pub async fn $fn_name(executor: &dyn $crate::executor::Executor,do_count:bool,page_no:u64,page_size:u64,$($param_key: &$param_type,)*) -> std::result::Result<rbs::Value, $crate::rbdc::Error>{
639                 $crate::impled!()
640             }
641              let mut executor = executor;
642              let mut conn = None;
643              if executor.name().eq($crate::executor::Executor::name(executor.rb_ref())){
644                  conn = Some(executor.rb_ref().acquire().await?);
645                  match &conn {
646                      Some(c) => {
647                          executor = c;
648                      }
649                      None => {}
650                  }
651             }
652             let mut total = 0;
653             if page_request.do_count() {
654                if let Some(intercept) = executor.rb_ref().get_intercept::<$crate::plugin::intercept_page::PageIntercept>(){
655                    intercept.count_ids.insert(executor.id(),$crate::plugin::PageRequest::new(page_request.page_no(), page_request.page_size()));
656                }
657                let total_value = $fn_name(executor, true, page_request.offset(), page_request.page_size(), $(&$param_key,)*).await?;
658                total = $crate::decode(total_value).unwrap_or(0);
659             }
660             if let Some(intercept) = executor.rb_ref().get_intercept::<$crate::plugin::intercept_page::PageIntercept>(){
661                intercept.select_ids.insert(executor.id(),$crate::plugin::PageRequest::new(page_request.page_no(), page_request.page_size()));
662             }
663             let mut page = $crate::plugin::Page::<$table>::new(page_request.page_no(), page_request.page_size(), total,vec![]);
664             let records_value = $fn_name(executor, false, page_request.offset(), page_request.page_size(), $(&$param_key,)*).await?;
665             page.records = rbs::from_value(records_value)?;
666             Ok(page)
667         }
668    }
669}
670
671/// impl py_sql select page.
672///
673/// you must deal with 3 param:
674/// (do_count:bool,page_no:u64,page_size:u64)
675///
676/// you must deal with sql:
677/// return Vec<Record>(if param do_count = false)
678/// return u64(if param do_count = true)·
679///
680/// you can see ${page_no} = (page_no -1) * page_size;
681/// you can see ${page_size} = page_size;
682///
683/// just like this example:
684/// ```py
685/// `select * from activity where delete_flag = 0`
686///                   if name != '':
687///                     ` and name=#{name}`
688///                   if !ids.is_empty():
689///                     ` and id in `
690///                     ${ids.sql()}
691/// ```
692/// ```
693/// #[derive(serde::Serialize, serde::Deserialize)]
694/// pub struct MockTable{}
695/// rbatis::pysql_select_page!(pysql_select_page(name:&str) -> MockTable =>
696///     r#"`select * from activity where delete_flag = 0`
697///         if name != '':
698///            ` and name=#{name}`
699///       ` limit ${page_no},${page_size}`
700/// "#);
701/// ```
702#[macro_export]
703macro_rules! pysql_select_page {
704    ($fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) -> $table:ty => $($py_file:expr$(,)?)*) => {
705            pub async fn $fn_name(executor: &dyn $crate::executor::Executor, page_request: &dyn $crate::plugin::IPageRequest, $($param_key:$param_type,)*) -> std::result::Result<$crate::plugin::Page<$table>, $crate::rbdc::Error> {
706              #[$crate::py_sql($($py_file,)*)]
707              pub async fn $fn_name(executor: &dyn $crate::executor::Executor,do_count:bool,page_no:u64,page_size:u64,$($param_key: &$param_type,)*) -> std::result::Result<rbs::Value, $crate::rbdc::Error>{
708                 $crate::impled!()
709              }
710              let mut executor = executor;
711              let mut conn = None;
712              if executor.name().eq($crate::executor::Executor::name(executor.rb_ref())){
713                  conn = Some(executor.rb_ref().acquire().await?);
714                  match &conn {
715                      Some(c) => {
716                          executor = c;
717                      }
718                      None => {}
719                  }
720              }
721              let mut total = 0;
722              if page_request.do_count() {
723                 if let Some(intercept) = executor.rb_ref().get_intercept::<$crate::plugin::intercept_page::PageIntercept>(){
724                    intercept.count_ids.insert(executor.id(),$crate::plugin::PageRequest::new(page_request.page_no(), page_request.page_size()));
725                 }
726                 let total_value = $fn_name(executor, true, page_request.offset(), page_request.page_size(), $(&$param_key,)*).await?;
727                 total = $crate::decode(total_value).unwrap_or(0);
728              }
729              if let Some(intercept) = executor.rb_ref().get_intercept::<$crate::plugin::intercept_page::PageIntercept>(){
730                 intercept.select_ids.insert(executor.id(),$crate::plugin::PageRequest::new(page_request.page_no(), page_request.page_size()));
731              }
732              let mut page = $crate::plugin::Page::<$table>::new(page_request.page_no(), page_request.page_size(), total,vec![]);
733              let records_value = $fn_name(executor, false, page_request.offset(), page_request.page_size(), $(&$param_key,)*).await?;
734              page.records = rbs::from_value(records_value)?;
735              Ok(page)
736         }
737    }
738}
739
740/// use macro wrapper #[sql]
741/// for example:
742/// ```rust
743/// use rbatis::executor::Executor;
744/// rbatis::raw_sql!(test_same_id(rb: &dyn Executor, id: &u64)  -> Result<rbs::Value, rbatis::Error> =>
745/// "select * from table where id = ?"
746/// );
747/// ```
748#[macro_export]
749macro_rules! raw_sql {
750    ($fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) -> $return_type:ty => $sql_file:expr) => {
751       #[$crate::sql($sql_file)]
752       pub async fn $fn_name($($param_key: $param_type,)*) -> $return_type{
753           impled!()
754       }
755    }
756}
757
758/// use macro wrapper #[py_sql]
759/// for query example:
760/// ```rust
761/// use rbatis::executor::Executor;
762/// rbatis::pysql!(test_same_id(rb: &dyn Executor, id: &u64)  -> Result<rbs::Value, rbatis::Error> =>
763/// "select * from table where ${id} = 1
764///  if id != 0:
765///    `id = #{id}`"
766/// );
767/// ```
768/// for exec example:
769/// ```rust
770/// use rbatis::executor::Executor;
771/// use rbdc::db::ExecResult;
772/// rbatis::pysql!(test_same_id(rb: &dyn Executor, id: &u64)  -> Result<ExecResult, rbatis::Error> =>
773/// "`update activity set name = '1' where id = #{id}`"
774/// );
775/// ```
776#[macro_export]
777macro_rules! pysql {
778    ($fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) -> $return_type:ty => $py_file:expr) => {
779       #[$crate::py_sql($py_file)]
780       pub async fn $fn_name($($param_key: $param_type,)*) -> $return_type{
781          impled!()
782       }
783    }
784}
785
786/// use macro wrapper #[html_sql]
787/// for example query rbs::Value:
788/// ```rust
789/// use rbatis::executor::Executor;
790/// rbatis::htmlsql!(test_select(rb: &dyn Executor, id: &u64)  -> Result<rbs::Value, rbatis::Error> => r#"
791///             <mapper>
792///             <select id="test_same_id">
793///               `select ${id} from my_table`
794///             </select>
795///             </mapper>"#);
796/// ```
797/// exec (from file)
798/// ```rust
799/// use rbatis::executor::Executor;
800/// use rbdc::db::ExecResult;
801/// rbatis::htmlsql!(update_by_id(rb: &dyn Executor, id: &u64)  -> Result<ExecResult, rbatis::Error> => "example/example.html");
802/// ```
803/// query
804/// ```rust
805/// use rbatis::executor::Executor;
806/// #[derive(serde::Serialize, serde::Deserialize)]
807/// pub struct MyTable{
808///      pub id:Option<u64>,
809///      pub name:Option<String>,
810/// }
811/// rbatis::htmlsql!(test_select_table(rb: &dyn Executor, id: &u64)  -> Result<Vec<MyTable>, rbatis::Error> => r#"
812///             <mapper>
813///               <select id="test_same_id">
814///                 `select * from my_table`
815///               </select>
816///             </mapper>"#);
817/// ```
818#[macro_export]
819macro_rules! htmlsql {
820    ($fn_name:ident($($param_key:ident:$param_type:ty$(,)?)*) -> $return_type:ty => $html_file:expr) => {
821        #[$crate::html_sql($html_file)]
822        pub async fn $fn_name($($param_key: $param_type,)*) -> $return_type{
823              impled!()
824        }
825    }
826}