1use json::{object, JsonValue};
2
3static DISABLE_FIELD: &[&str] = &[
4 "default",
5 "select",
6 "delete",
7 "insert",
8 "update",
9 "order",
10 "group",
11 "user",
12 "password",
13 "desc",
14 "index",
15 "from",
16 "host",
17 "user",
18 "read",
19 "partition",
20];
21
22pub mod sql_safety {
24 static SQL_KEYWORDS: &[&str] = &[
25 "select", "insert", "update", "delete", "drop", "truncate", "alter", "create", "exec",
26 "execute", "union",
27 ];
28
29 static DANGEROUS_PATTERNS: &[&str] = &["--", "/*", "*/", ";", "xp_", "sp_", "0x"];
30
31 #[inline]
32 pub fn validate_table_name(name: &str) -> bool {
33 if name.is_empty() || name.len() > 128 {
34 return false;
35 }
36 let bytes = name.as_bytes();
37 let first = bytes[0];
38 if !first.is_ascii_alphabetic() && first != b'_' {
39 return false;
40 }
41 for &b in bytes {
42 if !b.is_ascii_alphanumeric() && b != b'_' {
43 return false;
44 }
45 }
46 let lower = name.to_lowercase();
47 if SQL_KEYWORDS.iter().any(|kw| lower == *kw) {
48 return false;
49 }
50 !DANGEROUS_PATTERNS.iter().any(|p| lower.contains(p))
51 }
52
53 #[inline]
54 pub fn validate_field_name(name: &str) -> bool {
55 if name.is_empty() || name.len() > 256 {
56 return false;
57 }
58 for part in name.split('.') {
59 if part.is_empty() {
60 return false;
61 }
62 let bytes = part.as_bytes();
63 let first = bytes[0];
64 if !first.is_ascii_alphabetic() && first != b'_' {
65 return false;
66 }
67 for &b in bytes {
68 if !b.is_ascii_alphanumeric() && b != b'_' {
69 return false;
70 }
71 }
72 }
73 let lower = name.to_lowercase();
74 if SQL_KEYWORDS.iter().any(|kw| lower == *kw) {
75 return false;
76 }
77 !DANGEROUS_PATTERNS.iter().any(|p| lower.contains(p))
78 }
79
80 #[inline]
81 pub fn escape_string(value: &str) -> String {
82 value
83 .replace('\\', "\\\\")
84 .replace('\'', "''")
85 .replace('\0', "")
86 .replace('\n', "\\n")
87 .replace('\r', "\\r")
88 }
89
90 #[inline]
91 pub fn validate_compare_orator(op: &str) -> bool {
92 matches!(
93 op.to_lowercase().as_str(),
94 "=" | "!="
95 | "<>"
96 | "<"
97 | ">"
98 | "<="
99 | ">="
100 | "like"
101 | "not like"
102 | "in"
103 | "not in"
104 | "notin"
105 | "between"
106 | "is"
107 | "isnot"
108 | "is not"
109 | "set"
110 | "location"
111 | "notlike"
112 | "json_contains"
113 )
114 }
115
116 #[cfg(test)]
117 mod tests {
118 use super::*;
119
120 #[test]
121 fn test_validate_table_name_valid() {
122 assert!(validate_table_name("users"));
123 assert!(validate_table_name("user_profiles"));
124 assert!(validate_table_name("_private_table"));
125 assert!(validate_table_name("Table123"));
126 assert!(validate_table_name("batch_insert_perf"));
127 assert!(validate_table_name("my_update_log"));
128 }
129
130 #[test]
131 fn test_validate_table_name_invalid() {
132 assert!(!validate_table_name(""));
133 assert!(!validate_table_name("123table"));
134 assert!(!validate_table_name("user-name"));
135 assert!(!validate_table_name("table.name"));
136 assert!(!validate_table_name("table name"));
137 }
138
139 #[test]
140 fn test_validate_table_name_sql_keywords() {
141 assert!(!validate_table_name("select"));
142 assert!(!validate_table_name("SELECT"));
143 assert!(!validate_table_name("insert"));
144 assert!(!validate_table_name("drop"));
145 assert!(!validate_table_name("union"));
146 }
147
148 #[test]
149 fn test_validate_table_name_dangerous_patterns() {
150 assert!(!validate_table_name("table;drop"));
151 assert!(!validate_table_name("table--comment"));
152 assert!(!validate_table_name("xp_cmdshell"));
153 assert!(!validate_table_name("sp_execute"));
154 }
155
156 #[test]
157 fn test_validate_table_name_length() {
158 let long_name = "a".repeat(128);
159 assert!(validate_table_name(&long_name));
160 let too_long = "a".repeat(129);
161 assert!(!validate_table_name(&too_long));
162 }
163
164 #[test]
165 fn test_validate_field_name_valid() {
166 assert!(validate_field_name("id"));
167 assert!(validate_field_name("user_name"));
168 assert!(validate_field_name("_hidden"));
169 assert!(validate_field_name("table1.field1"));
170 }
171
172 #[test]
173 fn test_validate_field_name_invalid() {
174 assert!(!validate_field_name(""));
175 assert!(!validate_field_name("123field"));
176 assert!(!validate_field_name("field-name"));
177 assert!(!validate_field_name(".field"));
178 assert!(!validate_field_name("field."));
179 }
180
181 #[test]
182 fn test_escape_string() {
183 assert_eq!(escape_string("hello"), "hello");
184 assert_eq!(escape_string("it's"), "it''s");
185 assert_eq!(escape_string("new\nline"), "new\\nline");
186 assert_eq!(escape_string("carriage\rreturn"), "carriage\\rreturn");
187 }
188
189 #[test]
190 fn test_escape_string_sql_injection() {
191 assert_eq!(
192 escape_string("'; DROP TABLE users; --"),
193 "''; DROP TABLE users; --"
194 );
195 assert_eq!(escape_string("1' OR '1'='1"), "1'' OR ''1''=''1");
196 }
197
198 #[test]
199 fn test_validate_compare_operator_valid() {
200 assert!(validate_compare_orator("="));
201 assert!(validate_compare_orator("!="));
202 assert!(validate_compare_orator("like"));
203 assert!(validate_compare_orator("LIKE"));
204 assert!(validate_compare_orator("in"));
205 assert!(validate_compare_orator("between"));
206 }
207
208 #[test]
209 fn test_validate_compare_operator_invalid() {
210 assert!(!validate_compare_orator(""));
211 assert!(!validate_compare_orator("invalid"));
212 assert!(!validate_compare_orator("=="));
213 assert!(!validate_compare_orator("&&"));
214 }
215 }
216}
217
218#[inline]
219pub fn quote_identifier(name: &str, mode: &str) -> String {
220 match mode {
221 "mysql" | "sqlite" => format!("`{}`", name),
222 "pgsql" => format!("\"{}\"", name),
223 "mssql" => format!("[{}]", name),
224 _ => name.to_string(),
225 }
226}
227
228#[cfg(feature = "db-mssql")]
229pub mod mssql;
230#[cfg(feature = "db-mysql")]
231pub mod mysql;
232#[cfg(feature = "db-mysql")]
233pub mod mysql_transaction;
234#[cfg(feature = "db-pgsql")]
235pub mod pgsql;
236#[cfg(feature = "db-pgsql")]
237pub mod pgsql_transaction;
238#[cfg(feature = "db-sqlite")]
239pub mod sqlite;
240#[cfg(feature = "db-sqlite")]
241pub mod sqlite_transaction;
242
243pub trait DbMode {
244 fn database_tables(&mut self) -> JsonValue;
246 fn database_create(&mut self, name: &str) -> bool;
248 fn backups(&mut self, _filename: &str) -> bool {
250 false
251 }
252 fn truncate(&mut self, table: &str) -> bool;
253}
254
255#[derive(Debug, Clone)]
256pub struct TableOptions {
257 table_name: String,
258 table_title: String,
259 table_key: String,
260 table_fields: JsonValue,
261 table_unique: Vec<String>,
262 table_index: Vec<Vec<String>>,
263 table_partition: bool,
264 table_partition_columns: JsonValue,
265}
266impl TableOptions {
267 pub fn set_table_name(&mut self, name: &str) {
268 self.table_name = name.to_string()
269 }
270 pub fn set_table_title(&mut self, name: &str) {
271 self.table_title = name.to_string()
272 }
273 pub fn set_table_key(&mut self, name: &str) {
274 self.table_key = name.to_string()
275 }
276 pub fn set_table_fields(&mut self, fields: JsonValue) {
277 self.table_fields = fields;
278 }
279 pub fn set_table_unique(&mut self, unique: Vec<&str>) {
280 self.table_unique = unique.iter().map(|s| s.to_string()).collect();
281 }
282 pub fn set_table_index(&mut self, index: Vec<Vec<&str>>) {
283 self.table_index = index
284 .iter()
285 .map(|s| s.iter().map(|s| s.to_string()).collect())
286 .collect();
287 }
288 pub fn set_table_partition(&mut self, index: bool) {
289 self.table_partition = index;
290 }
291 pub fn set_table_partition_columns(&mut self, index: JsonValue) {
292 self.table_partition_columns = index;
293 }
294}
295impl Default for TableOptions {
296 fn default() -> Self {
297 Self {
298 table_name: "".to_string(),
299 table_title: "".to_string(),
300 table_key: "".to_string(),
301 table_fields: JsonValue::Null,
302 table_unique: vec![],
303 table_index: vec![],
304 table_partition: false,
305 table_partition_columns: JsonValue::Null,
306 }
307 }
308}
309pub trait Mode: DbMode {
310 fn table_create(&mut self, options: TableOptions) -> JsonValue;
311 fn table_update(&mut self, options: TableOptions) -> JsonValue;
312 fn table_info(&mut self, table: &str) -> JsonValue;
314 fn table_is_exist(&mut self, name: &str) -> bool;
316 fn table(&mut self, name: &str) -> &mut Self;
318 fn change_table(&mut self, name: &str) -> &mut Self;
320
321 fn autoinc(&mut self) -> &mut Self;
323 fn timestamps(&mut self) -> &mut Self;
325 fn fetch_sql(&mut self) -> &mut Self;
327 fn order(&mut self, field: &str, by: bool) -> &mut Self;
329 fn group(&mut self, field: &str) -> &mut Self;
331 fn distinct(&mut self) -> &mut Self;
333 fn json(&mut self, field: &str) -> &mut Self;
335 fn location(&mut self, field: &str) -> &mut Self;
337 fn field(&mut self, field: &str) -> &mut Self;
339 fn field_raw(&mut self, expr: &str) -> &mut Self;
341 fn hidden(&mut self, name: &str) -> &mut Self;
343 fn where_and(&mut self, field: &str, compare: &str, value: JsonValue) -> &mut Self;
345 fn where_or(&mut self, field: &str, compare: &str, value: JsonValue) -> &mut Self;
346 fn where_raw(&mut self, expr: &str) -> &mut Self;
348
349 fn where_in_sub(&mut self, field: &str, sub_sql: &str) -> &mut Self;
351 fn where_not_in_sub(&mut self, field: &str, sub_sql: &str) -> &mut Self;
353
354 fn where_exists(&mut self, sub_sql: &str) -> &mut Self;
355 fn where_not_exists(&mut self, sub_sql: &str) -> &mut Self;
356
357 fn where_column(&mut self, field_a: &str, compare: &str, field_b: &str) -> &mut Self;
359 fn update_column(&mut self, field_a: &str, compare: &str) -> &mut Self;
361 fn page(&mut self, page: i32, limit: i32) -> &mut Self;
363 fn limit(&mut self, count: i32) -> &mut Self;
365 fn column(&mut self, field: &str) -> JsonValue;
367 fn count(&mut self) -> JsonValue;
369 fn max(&mut self, field: &str) -> JsonValue;
371 fn min(&mut self, field: &str) -> JsonValue;
373 fn sum(&mut self, field: &str) -> JsonValue;
375 fn avg(&mut self, field: &str) -> JsonValue;
377 fn having(&mut self, expr: &str) -> &mut Self;
379 fn select(&mut self) -> JsonValue;
381 fn find(&mut self) -> JsonValue;
383 fn value(&mut self, field: &str) -> JsonValue;
385 fn insert(&mut self, data: JsonValue) -> JsonValue;
387 fn insert_all(&mut self, data: JsonValue) -> JsonValue;
389
390 fn upsert(&mut self, data: JsonValue, conflict_fields: Vec<&str>) -> JsonValue;
392
393 fn update(&mut self, data: JsonValue) -> JsonValue;
395 fn update_all(&mut self, data: JsonValue) -> JsonValue;
397 fn delete(&mut self) -> JsonValue;
399
400 fn transaction(&mut self) -> bool;
402 fn commit(&mut self) -> bool;
404 fn rollback(&mut self) -> bool;
406 fn sql(&mut self, sql: &str) -> Result<JsonValue, String>;
408 fn sql_execute(&mut self, sql: &str) -> Result<JsonValue, String>;
409 fn inc(&mut self, field: &str, num: f64) -> &mut Self;
411 fn dec(&mut self, field: &str, num: f64) -> &mut Self;
413
414 fn buildsql(&mut self) -> String;
416
417 fn join_fields(&mut self, fields: Vec<&str>) -> &mut Self;
418 fn join(
424 &mut self,
425 main_table: &str,
426 main_fields: &str,
427 right_table: &str,
428 right_fields: &str,
429 ) -> &mut Self;
430
431 fn join_inner(&mut self, table: &str, main_fields: &str, second_fields: &str) -> &mut Self;
437
438 fn join_right(
440 &mut self,
441 main_table: &str,
442 main_fields: &str,
443 right_table: &str,
444 right_fields: &str,
445 ) -> &mut Self;
446
447 fn join_full(
449 &mut self,
450 main_table: &str,
451 main_fields: &str,
452 right_table: &str,
453 right_fields: &str,
454 ) -> &mut Self;
455
456 fn union(&mut self, sub_sql: &str) -> &mut Self;
457 fn union_all(&mut self, sub_sql: &str) -> &mut Self;
458 fn lock_for_update(&mut self) -> &mut Self;
459 fn lock_for_share(&mut self) -> &mut Self;
460}
461
462#[derive(Clone, Debug)]
463pub struct Params {
464 pub mode: String,
465 pub autoinc: bool,
466 pub table: String,
467 pub where_and: Vec<String>,
468 pub where_or: Vec<String>,
469 pub where_column: String,
470 pub update_column: Vec<String>,
471 pub inc_dec: JsonValue,
472 pub page: i32,
473 pub limit: i32,
474 pub fields: JsonValue,
475 pub top: String,
476 pub top2: String,
477 pub order: JsonValue,
478 pub group: JsonValue,
479 pub distinct: bool,
480 pub json: JsonValue,
481 pub location: JsonValue,
482 pub sql: bool,
483 pub join: Vec<String>,
484 pub join_inner: Vec<String>,
485 pub join_table: String,
486 pub having: Vec<String>,
487 pub limit_only: i32,
489 pub timestamps: bool,
490 pub unions: Vec<String>,
491 pub lock_mode: String,
492}
493
494impl Params {
495 pub fn default(mode: &str) -> Self {
496 Self {
497 mode: mode.to_string(),
498 autoinc: false,
499 table: "".to_string(),
500 where_and: vec![],
501 where_or: vec![],
502 where_column: "".to_string(),
503 update_column: vec![],
504 inc_dec: object! {},
505 page: -1,
506 limit: 10,
507 fields: object! {},
508 top: String::new(),
509 top2: String::new(),
510 order: object! {},
511 group: object! {},
512 distinct: false,
513 json: object! {},
514 location: object! {},
515 sql: false,
516 join: Vec::new(),
517 join_inner: Vec::new(),
518 join_table: "".to_string(),
519 having: vec![],
520 limit_only: -1,
521 timestamps: false,
522 unions: vec![],
523 lock_mode: "".to_string(),
524 }
525 }
526 pub fn where_sql(&mut self) -> String {
527 let mut where_and_sql = vec![];
528 let mut where_or_sql = vec![];
529 let mut sql = vec![];
530
531 for item in self.where_or.iter() {
532 where_or_sql.push(item.clone());
533 }
534 if !where_or_sql.is_empty() {
535 sql.push(format!(" ( {} ) ", where_or_sql.join(" OR ")));
536 }
537
538 for item in self.where_and.iter() {
539 where_and_sql.push(item.clone());
540 }
541 if !where_and_sql.is_empty() {
542 sql.push(where_and_sql.join(" AND "));
543 }
544
545 if !self.where_column.is_empty() {
546 sql.push(self.where_column.clone());
547 }
548
549 if !sql.is_empty() {
550 return format!("WHERE {}", sql.join(" AND "));
551 }
552 "".to_string()
553 }
554 pub fn page_limit_sql(&mut self) -> String {
555 if self.page == -1 {
556 if self.limit_only > 0 {
557 return format!("LIMIT {}", self.limit_only);
558 }
559 return "".to_string();
560 }
561 match self.mode.as_str() {
562 "mysql" => {
563 format!(
564 "LIMIT {},{}",
565 self.page * self.limit - self.limit,
566 self.limit
567 )
568 }
569 "sqlite" | "pgsql" => {
570 format!(
571 "LIMIT {} OFFSET {}",
572 self.limit,
573 self.page * self.limit - self.limit
574 )
575 }
576 _ => "".to_string(),
577 }
578 }
579 pub fn fields(&mut self) -> String {
580 let mut fields = vec![];
581 for (_, value) in self.fields.entries() {
582 match self.mode.as_str() {
583 "mssql" => {
584 fields.push(format!("{value}"));
585 }
586 "mysql" => {
587 if let Some(s) = value.as_str() {
588 if DISABLE_FIELD.contains(&s) {
589 fields.push(format!("`{value}`"));
590 } else {
591 fields.push(format!("{value}"));
592 }
593 } else {
594 fields.push(format!("{value}"));
595 }
596 }
597 _ => {
598 fields.push(format!("{value}"));
599 }
600 }
601 }
602 let fields = {
603 if fields.is_empty() {
604 "*".into()
605 } else {
606 fields.join(",")
607 }
608 };
609 match self.mode.as_str() {
610 "mysql" => fields.to_string(),
611 "sqlite" => fields.to_string(),
612 "mssql" => fields.to_string(),
613 _ => fields.to_string(),
614 }
615 }
616 pub fn top(&mut self) -> String {
617 match self.mode.as_str() {
618 "mssql" => {
619 let wheres = self.where_sql();
620 if !self.top2.is_empty() {
621 let order = self.order();
622 if order.is_empty() {
623 self.top = format!(
624 "(select ROW_NUMBER() OVER(ORDER BY rand()) as ROW,* from {} {}) as ",
625 self.table, wheres
626 );
627 } else {
628 self.top = format!(
629 "(select ROW_NUMBER() OVER({}) as ROW,* from {} {}) as ",
630 order, self.table, wheres
631 );
632 }
633 return self.top.to_string();
634 }
635 self.top.to_string()
636 }
637 _ => "".to_string(),
638 }
639 }
640 pub fn top2(&mut self) -> String {
641 match self.mode.as_str() {
642 "mssql" => {
643 if self.where_and.is_empty() && self.where_or.is_empty() && !self.top2.is_empty() {
644 return format!("where {}", self.top2);
645 }
646 if (!self.where_and.is_empty() || !self.where_or.is_empty())
647 && !self.top2.is_empty()
648 {
649 return format!("AND {}", self.top2);
650 }
651 self.top2.to_string()
652 }
653 _ => "".to_string(),
654 }
655 }
656 pub fn table(&mut self) -> String {
657 match self.mode.as_str() {
658 "mssql" => {
659 if !self.top2.is_empty() {
660 return "t".to_string();
661 }
662 self.table.to_string()
663 }
664 _ => self.table.to_string(),
665 }
666 }
667 pub fn join(&mut self) -> String {
668 match self.mode.as_str() {
669 "mssql" => self.join.join(" "),
670 _ => self.join.join(" "),
671 }
672 }
673
674 pub fn join_inner(&mut self) -> String {
676 match self.mode.as_str() {
677 "mysql" => {
678 let mut join_inner = "".to_string();
679 for item in self.join_inner.iter() {
680 join_inner = format!("{join_inner} {item}");
681 }
682 join_inner.to_string()
683 }
684 _ => "".to_string(),
685 }
686 }
687
688 pub fn order(&mut self) -> String {
689 let mut sql = vec![];
690 for (field, item) in self.order.entries() {
691 match self.mode.as_str() {
692 "mssql" => {
693 if DISABLE_FIELD.contains(&field) {
694 sql.push(format!("[{field}] {item}"));
695 } else {
696 sql.push(format!("{field} {item}"));
697 }
698 }
699
700 "pgsql" => {
701 if DISABLE_FIELD.contains(&field) {
702 sql.push(format!("\"{field}\" {item}"));
703 } else {
704 sql.push(format!("{field} {item}"));
705 }
706 }
707 _ => {
708 if DISABLE_FIELD.contains(&field) {
709 sql.push(format!("`{field}` {item}"));
710 } else {
711 sql.push(format!("{field} {item}"));
712 }
713 }
714 }
715 }
716 if !sql.is_empty() {
717 return format!("ORDER BY {}", sql.join(","));
718 }
719 "".to_string()
720 }
721 pub fn group(&mut self) -> String {
722 let mut sql = vec![];
723 for (_, field) in self.group.entries() {
724 let field_str = field.to_string();
725 if field_str.contains(".") {
726 sql.push(format!("{}", field));
727 } else {
728 match self.mode.as_str() {
729 "pgsql" => {
730 if DISABLE_FIELD.contains(&&*field_str) {
731 sql.push(format!("\"{field}\""));
732 } else {
733 sql.push(format!("{}.{}", self.table, field));
734 }
735 }
736 "mssql" => {
737 if DISABLE_FIELD.contains(&&*field_str) {
738 sql.push(format!("[{field}]"));
739 } else {
740 sql.push(format!("{}.{}", self.table, field));
741 }
742 }
743 _ => {
744 if DISABLE_FIELD.contains(&&*field_str) {
745 sql.push(format!("`{field}`"));
746 } else {
747 sql.push(format!("{}.{}", self.table, field));
748 }
749 }
750 }
751 }
752 }
753 if !sql.is_empty() {
754 return format!("GROUP BY {}", sql.join(","));
755 }
756 "".to_string()
757 }
758 pub fn having(&mut self) -> String {
759 if self.having.is_empty() {
760 return "".to_string();
761 }
762 format!("HAVING {}", self.having.join(" AND "))
763 }
764 pub fn distinct(&self) -> String {
765 if self.distinct {
766 "DISTINCT".to_string()
767 } else {
768 "".to_string()
769 }
770 }
771 pub fn select_sql(&mut self) -> String {
772 let base = format!(
773 "SELECT {} {} FROM {} {} {} {} {} {} {} {} {} {}",
774 self.distinct(),
775 self.fields(),
776 self.top(),
777 self.table(),
778 self.join(),
779 self.join_inner(),
780 self.where_sql(),
781 self.top2(),
782 self.group(),
783 self.having(),
784 self.order(),
785 self.page_limit_sql()
786 );
787 if self.unions.is_empty() {
788 if self.lock_mode.is_empty() {
789 base
790 } else {
791 format!("{base} {}", self.lock_mode)
792 }
793 } else {
794 let unions = self.unions.join(" ");
795 if self.lock_mode.is_empty() {
796 format!("{base} {unions}")
797 } else {
798 format!("{base} {unions} {}", self.lock_mode)
799 }
800 }
801 }
802}
803
804#[cfg(test)]
805mod params_tests {
806 use super::*;
807 use json::object;
808
809 #[test]
810 fn test_quote_identifier_mysql() {
811 assert_eq!(quote_identifier("name", "mysql"), "`name`");
812 assert_eq!(quote_identifier("user", "mysql"), "`user`");
813 }
814
815 #[test]
816 fn test_quote_identifier_sqlite() {
817 assert_eq!(quote_identifier("name", "sqlite"), "`name`");
818 assert_eq!(quote_identifier("order", "sqlite"), "`order`");
819 }
820
821 #[test]
822 fn test_quote_identifier_pgsql() {
823 assert_eq!(quote_identifier("name", "pgsql"), "\"name\"");
824 assert_eq!(quote_identifier("select", "pgsql"), "\"select\"");
825 }
826
827 #[test]
828 fn test_quote_identifier_mssql() {
829 assert_eq!(quote_identifier("name", "mssql"), "[name]");
830 assert_eq!(quote_identifier("index", "mssql"), "[index]");
831 }
832
833 #[test]
834 fn test_quote_identifier_unknown() {
835 assert_eq!(quote_identifier("name", "unknown"), "name");
836 assert_eq!(quote_identifier("field", ""), "field");
837 }
838
839 #[test]
840 fn test_table_options_default() {
841 let opts = TableOptions::default();
842 assert_eq!(opts.table_name, "");
843 assert_eq!(opts.table_title, "");
844 assert_eq!(opts.table_key, "");
845 assert!(opts.table_fields.is_null());
846 assert!(opts.table_unique.is_empty());
847 assert!(opts.table_index.is_empty());
848 assert!(!opts.table_partition);
849 assert!(opts.table_partition_columns.is_null());
850 }
851
852 #[test]
853 fn test_table_options_set_table_name() {
854 let mut opts = TableOptions::default();
855 opts.set_table_name("users");
856 assert_eq!(opts.table_name, "users");
857 }
858
859 #[test]
860 fn test_table_options_set_table_title() {
861 let mut opts = TableOptions::default();
862 opts.set_table_title("User Table");
863 assert_eq!(opts.table_title, "User Table");
864 }
865
866 #[test]
867 fn test_table_options_set_table_key() {
868 let mut opts = TableOptions::default();
869 opts.set_table_key("id");
870 assert_eq!(opts.table_key, "id");
871 }
872
873 #[test]
874 fn test_table_options_set_table_fields() {
875 let mut opts = TableOptions::default();
876 let fields = object! { "name" => "varchar", "age" => "int" };
877 opts.set_table_fields(fields);
878 assert_eq!(opts.table_fields["name"], "varchar");
879 assert_eq!(opts.table_fields["age"], "int");
880 }
881
882 #[test]
883 fn test_table_options_set_table_unique() {
884 let mut opts = TableOptions::default();
885 opts.set_table_unique(vec!["email", "username"]);
886 assert_eq!(
887 opts.table_unique,
888 vec!["email".to_string(), "username".to_string()]
889 );
890 }
891
892 #[test]
893 fn test_table_options_set_table_index() {
894 let mut opts = TableOptions::default();
895 opts.set_table_index(vec![vec!["name", "age"], vec!["email"]]);
896 assert_eq!(opts.table_index.len(), 2);
897 assert_eq!(
898 opts.table_index[0],
899 vec!["name".to_string(), "age".to_string()]
900 );
901 assert_eq!(opts.table_index[1], vec!["email".to_string()]);
902 }
903
904 #[test]
905 fn test_table_options_set_table_partition() {
906 let mut opts = TableOptions::default();
907 assert!(!opts.table_partition);
908 opts.set_table_partition(true);
909 assert!(opts.table_partition);
910 }
911
912 #[test]
913 fn test_table_options_set_table_partition_columns() {
914 let mut opts = TableOptions::default();
915 let cols = object! { "col1" => "range", "col2" => "hash" };
916 opts.set_table_partition_columns(cols);
917 assert_eq!(opts.table_partition_columns["col1"], "range");
918 assert_eq!(opts.table_partition_columns["col2"], "hash");
919 }
920
921 #[test]
922 fn test_params_default_mysql() {
923 let p = Params::default("mysql");
924 assert_eq!(p.mode, "mysql");
925 assert!(!p.autoinc);
926 assert_eq!(p.table, "");
927 assert!(p.where_and.is_empty());
928 assert!(p.where_or.is_empty());
929 assert_eq!(p.where_column, "");
930 assert!(p.update_column.is_empty());
931 assert!(p.inc_dec.is_object());
932 assert_eq!(p.page, -1);
933 assert_eq!(p.limit, 10);
934 assert!(p.fields.is_object());
935 assert_eq!(p.top, "");
936 assert_eq!(p.top2, "");
937 assert!(p.order.is_object());
938 assert!(p.group.is_object());
939 assert!(!p.distinct);
940 assert!(p.json.is_object());
941 assert!(p.location.is_object());
942 assert!(!p.sql);
943 assert!(p.join.is_empty());
944 assert!(p.join_inner.is_empty());
945 assert_eq!(p.join_table, "");
946 }
947
948 #[test]
949 fn test_params_default_pgsql() {
950 let p = Params::default("pgsql");
951 assert_eq!(p.mode, "pgsql");
952 assert_eq!(p.page, -1);
953 assert_eq!(p.limit, 10);
954 }
955
956 #[test]
957 fn test_params_default_mssql() {
958 let p = Params::default("mssql");
959 assert_eq!(p.mode, "mssql");
960 }
961
962 #[test]
963 fn test_params_default_sqlite() {
964 let p = Params::default("sqlite");
965 assert_eq!(p.mode, "sqlite");
966 }
967
968 #[test]
969 fn test_where_sql_empty() {
970 let mut p = Params::default("mysql");
971 assert_eq!(p.where_sql(), "");
972 }
973
974 #[test]
975 fn test_where_sql_only_and() {
976 let mut p = Params::default("mysql");
977 p.where_and.push("a = 1".to_string());
978 p.where_and.push("b = 2".to_string());
979 assert_eq!(p.where_sql(), "WHERE a = 1 AND b = 2");
980 }
981
982 #[test]
983 fn test_where_sql_only_or() {
984 let mut p = Params::default("mysql");
985 p.where_or.push("a = 1".to_string());
986 p.where_or.push("b = 2".to_string());
987 assert_eq!(p.where_sql(), "WHERE ( a = 1 OR b = 2 ) ");
988 }
989
990 #[test]
991 fn test_where_sql_and_plus_or() {
992 let mut p = Params::default("mysql");
993 p.where_and.push("c = 3".to_string());
994 p.where_or.push("a = 1".to_string());
995 p.where_or.push("b = 2".to_string());
996 let result = p.where_sql();
997 assert!(result.starts_with("WHERE"));
998 assert!(result.contains("( a = 1 OR b = 2 )"));
999 assert!(result.contains("c = 3"));
1000 assert!(result.contains(" AND "));
1001 }
1002
1003 #[test]
1004 fn test_where_sql_with_where_column() {
1005 let mut p = Params::default("mysql");
1006 p.where_column = "x.id = y.id".to_string();
1007 assert_eq!(p.where_sql(), "WHERE x.id = y.id");
1008 }
1009
1010 #[test]
1011 fn test_where_sql_all_three() {
1012 let mut p = Params::default("mysql");
1013 p.where_and.push("a = 1".to_string());
1014 p.where_or.push("b = 2".to_string());
1015 p.where_or.push("c = 3".to_string());
1016 p.where_column = "x.id = y.id".to_string();
1017 let result = p.where_sql();
1018 assert!(result.starts_with("WHERE"));
1019 assert!(result.contains("( b = 2 OR c = 3 )"));
1020 assert!(result.contains("a = 1"));
1021 assert!(result.contains("x.id = y.id"));
1022 }
1023
1024 #[test]
1025 fn test_page_limit_sql_default_page_neg1() {
1026 let mut p = Params::default("mysql");
1027 assert_eq!(p.page_limit_sql(), "");
1028 }
1029
1030 #[test]
1031 fn test_page_limit_sql_mysql() {
1032 let mut p = Params::default("mysql");
1033 p.page = 1;
1034 p.limit = 10;
1035 assert_eq!(p.page_limit_sql(), "LIMIT 0,10");
1036 }
1037
1038 #[test]
1039 fn test_page_limit_sql_mysql_page2() {
1040 let mut p = Params::default("mysql");
1041 p.page = 2;
1042 p.limit = 20;
1043 assert_eq!(p.page_limit_sql(), "LIMIT 20,20");
1044 }
1045
1046 #[test]
1047 fn test_page_limit_sql_sqlite() {
1048 let mut p = Params::default("sqlite");
1049 p.page = 1;
1050 p.limit = 10;
1051 assert_eq!(p.page_limit_sql(), "LIMIT 10 OFFSET 0");
1052 }
1053
1054 #[test]
1055 fn test_page_limit_sql_sqlite_page3() {
1056 let mut p = Params::default("sqlite");
1057 p.page = 3;
1058 p.limit = 5;
1059 assert_eq!(p.page_limit_sql(), "LIMIT 5 OFFSET 10");
1060 }
1061
1062 #[test]
1063 fn test_page_limit_sql_unknown_mode() {
1064 let mut p = Params::default("unknown");
1065 p.page = 1;
1066 p.limit = 10;
1067 assert_eq!(p.page_limit_sql(), "");
1068 }
1069
1070 #[test]
1071 fn test_fields_empty() {
1072 let mut p = Params::default("mysql");
1073 assert_eq!(p.fields(), "*");
1074 }
1075
1076 #[test]
1077 fn test_fields_mysql_with_disable_field() {
1078 let mut p = Params::default("mysql");
1079 p.fields = object! { "f1" => "order", "f2" => "name" };
1080 let result = p.fields();
1081 assert!(result.contains("`order`"));
1082 assert!(result.contains("name"));
1083 assert!(!result.contains("`name`"));
1084 }
1085
1086 #[test]
1087 fn test_fields_mysql_normal() {
1088 let mut p = Params::default("mysql");
1089 p.fields = object! { "f1" => "id", "f2" => "name" };
1090 let result = p.fields();
1091 assert!(result.contains("id"));
1092 assert!(result.contains("name"));
1093 }
1094
1095 #[test]
1096 fn test_fields_mssql() {
1097 let mut p = Params::default("mssql");
1098 p.fields = object! { "f1" => "id", "f2" => "name" };
1099 let result = p.fields();
1100 assert!(result.contains("id"));
1101 assert!(result.contains("name"));
1102 }
1103
1104 #[test]
1105 fn test_fields_other_mode() {
1106 let mut p = Params::default("pgsql");
1107 p.fields = object! { "f1" => "id", "f2" => "email" };
1108 let result = p.fields();
1109 assert!(result.contains("id"));
1110 assert!(result.contains("email"));
1111 }
1112
1113 #[test]
1114 fn test_top_non_mssql() {
1115 let mut p = Params::default("mysql");
1116 assert_eq!(p.top(), "");
1117 }
1118
1119 #[test]
1120 fn test_top_mssql_without_top2() {
1121 let mut p = Params::default("mssql");
1122 p.table = "users".to_string();
1123 assert_eq!(p.top(), "");
1124 }
1125
1126 #[test]
1127 fn test_top_mssql_with_top2() {
1128 let mut p = Params::default("mssql");
1129 p.table = "users".to_string();
1130 p.top2 = "ROW BETWEEN 1 AND 10".to_string();
1131 let result = p.top();
1132 assert!(result.contains("ROW_NUMBER"));
1133 assert!(result.contains("users"));
1134 assert!(result.contains("as "));
1135 }
1136
1137 #[test]
1138 fn test_top_mssql_with_top2_and_order() {
1139 let mut p = Params::default("mssql");
1140 p.table = "users".to_string();
1141 p.top2 = "ROW BETWEEN 1 AND 10".to_string();
1142 p.order = object! { "id" => "ASC" };
1143 let result = p.top();
1144 assert!(result.contains("ROW_NUMBER"));
1145 assert!(result.contains("ORDER BY"));
1146 assert!(result.contains("id ASC"));
1147 }
1148
1149 #[test]
1150 fn test_top2_non_mssql() {
1151 let mut p = Params::default("mysql");
1152 p.top2 = "something".to_string();
1153 assert_eq!(p.top2(), "");
1154 }
1155
1156 #[test]
1157 fn test_top2_mssql_no_where_with_top2() {
1158 let mut p = Params::default("mssql");
1159 p.top2 = "ROW BETWEEN 1 AND 10".to_string();
1160 let result = p.top2();
1161 assert_eq!(result, "where ROW BETWEEN 1 AND 10");
1162 }
1163
1164 #[test]
1165 fn test_top2_mssql_with_where_and_top2() {
1166 let mut p = Params::default("mssql");
1167 p.where_and.push("a = 1".to_string());
1168 p.top2 = "ROW BETWEEN 1 AND 10".to_string();
1169 let result = p.top2();
1170 assert_eq!(result, "AND ROW BETWEEN 1 AND 10");
1171 }
1172
1173 #[test]
1174 fn test_top2_mssql_empty_top2() {
1175 let mut p = Params::default("mssql");
1176 assert_eq!(p.top2(), "");
1177 }
1178
1179 #[test]
1180 fn test_table_mssql_with_top2() {
1181 let mut p = Params::default("mssql");
1182 p.table = "users".to_string();
1183 p.top2 = "ROW BETWEEN 1 AND 10".to_string();
1184 assert_eq!(p.table(), "t");
1185 }
1186
1187 #[test]
1188 fn test_table_mssql_without_top2() {
1189 let mut p = Params::default("mssql");
1190 p.table = "users".to_string();
1191 assert_eq!(p.table(), "users");
1192 }
1193
1194 #[test]
1195 fn test_table_other_modes() {
1196 let mut p = Params::default("mysql");
1197 p.table = "orders".to_string();
1198 assert_eq!(p.table(), "orders");
1199
1200 let mut p2 = Params::default("pgsql");
1201 p2.table = "products".to_string();
1202 assert_eq!(p2.table(), "products");
1203 }
1204
1205 #[test]
1206 fn test_join_empty() {
1207 let mut p = Params::default("mysql");
1208 assert_eq!(p.join(), "");
1209 }
1210
1211 #[test]
1212 fn test_join_with_entries() {
1213 let mut p = Params::default("mysql");
1214 p.join
1215 .push("LEFT JOIN orders ON users.id = orders.user_id".to_string());
1216 p.join
1217 .push("LEFT JOIN items ON orders.id = items.order_id".to_string());
1218 let result = p.join();
1219 assert!(result.contains("LEFT JOIN orders ON users.id = orders.user_id"));
1220 assert!(result.contains("LEFT JOIN items ON orders.id = items.order_id"));
1221 assert!(result.contains(" "));
1222 }
1223
1224 #[test]
1225 fn test_join_mssql() {
1226 let mut p = Params::default("mssql");
1227 p.join.push("JOIN t2 ON t1.id = t2.fk".to_string());
1228 let result = p.join();
1229 assert_eq!(result, "JOIN t2 ON t1.id = t2.fk");
1230 }
1231
1232 #[test]
1233 fn test_join_inner_empty() {
1234 let mut p = Params::default("mysql");
1235 assert_eq!(p.join_inner(), "");
1236 }
1237
1238 #[test]
1239 fn test_join_inner_mysql() {
1240 let mut p = Params::default("mysql");
1241 p.join_inner
1242 .push("INNER JOIN orders ON users.id = orders.uid".to_string());
1243 p.join_inner
1244 .push("INNER JOIN items ON orders.id = items.oid".to_string());
1245 let result = p.join_inner();
1246 assert!(result.contains("INNER JOIN orders ON users.id = orders.uid"));
1247 assert!(result.contains("INNER JOIN items ON orders.id = items.oid"));
1248 }
1249
1250 #[test]
1251 fn test_join_inner_non_mysql() {
1252 let mut p = Params::default("pgsql");
1253 p.join_inner
1254 .push("INNER JOIN orders ON users.id = orders.uid".to_string());
1255 assert_eq!(p.join_inner(), "");
1256
1257 let mut p2 = Params::default("mssql");
1258 p2.join_inner
1259 .push("INNER JOIN orders ON users.id = orders.uid".to_string());
1260 assert_eq!(p2.join_inner(), "");
1261 }
1262
1263 #[test]
1264 fn test_order_empty() {
1265 let mut p = Params::default("mysql");
1266 assert_eq!(p.order(), "");
1267 }
1268
1269 #[test]
1270 fn test_order_with_entries() {
1271 let mut p = Params::default("mysql");
1272 p.order = object! { "id" => "ASC", "name" => "DESC" };
1273 let result = p.order();
1274 assert!(result.starts_with("ORDER BY"));
1275 assert!(result.contains("id ASC"));
1276 assert!(result.contains("name DESC"));
1277 }
1278
1279 #[test]
1280 fn test_order_mysql_disable_field() {
1281 let mut p = Params::default("mysql");
1282 p.order = object! { "order" => "ASC" };
1283 let result = p.order();
1284 assert!(result.contains("`order` ASC"));
1285 }
1286
1287 #[test]
1288 fn test_order_pgsql_disable_field() {
1289 let mut p = Params::default("pgsql");
1290 p.order = object! { "order" => "DESC" };
1291 let result = p.order();
1292 assert!(result.contains("\"order\" DESC"));
1293 }
1294
1295 #[test]
1296 fn test_order_mssql_disable_field() {
1297 let mut p = Params::default("mssql");
1298 p.order = object! { "order" => "ASC" };
1299 let result = p.order();
1300 assert!(result.contains("[order] ASC"));
1301 }
1302
1303 #[test]
1304 fn test_group_empty() {
1305 let mut p = Params::default("mysql");
1306 assert_eq!(p.group(), "");
1307 }
1308
1309 #[test]
1310 fn test_group_with_entries() {
1311 let mut p = Params::default("mysql");
1312 p.table = "users".to_string();
1313 p.group = object! { "g1" => "status" };
1314 let result = p.group();
1315 assert!(result.starts_with("GROUP BY"));
1316 assert!(result.contains("users.status"));
1317 }
1318
1319 #[test]
1320 fn test_group_disable_field() {
1321 let mut p = Params::default("mysql");
1322 p.table = "users".to_string();
1323 p.group = object! { "g1" => "order" };
1324 let result = p.group();
1325 assert!(result.contains("`order`"));
1326 }
1327
1328 #[test]
1329 fn test_group_dotted_field() {
1330 let mut p = Params::default("mysql");
1331 p.table = "users".to_string();
1332 p.group = object! { "g1" => "orders.status" };
1333 let result = p.group();
1334 assert!(result.contains("orders.status"));
1335 assert!(!result.contains("users.orders.status"));
1336 }
1337
1338 #[test]
1339 fn test_distinct_false() {
1340 let p = Params::default("mysql");
1341 assert_eq!(p.distinct(), "");
1342 }
1343
1344 #[test]
1345 fn test_distinct_true() {
1346 let mut p = Params::default("mysql");
1347 p.distinct = true;
1348 assert_eq!(p.distinct(), "DISTINCT");
1349 }
1350
1351 #[test]
1352 fn test_select_sql_basic() {
1353 let mut p = Params::default("mysql");
1354 p.table = "users".to_string();
1355 let result = p.select_sql();
1356 assert!(result.contains("SELECT"));
1357 assert!(result.contains("*"));
1358 assert!(result.contains("FROM"));
1359 assert!(result.contains("users"));
1360 }
1361
1362 #[test]
1363 fn test_select_sql_with_where_and_order() {
1364 let mut p = Params::default("mysql");
1365 p.table = "users".to_string();
1366 p.where_and.push("age > 18".to_string());
1367 p.order = object! { "name" => "ASC" };
1368 p.page = 1;
1369 p.limit = 10;
1370 let result = p.select_sql();
1371 assert!(result.contains("SELECT"));
1372 assert!(result.contains("FROM"));
1373 assert!(result.contains("users"));
1374 assert!(result.contains("WHERE age > 18"));
1375 assert!(result.contains("ORDER BY name ASC"));
1376 assert!(result.contains("LIMIT 0,10"));
1377 }
1378
1379 #[test]
1380 fn test_select_sql_with_distinct_and_group() {
1381 let mut p = Params::default("mysql");
1382 p.table = "orders".to_string();
1383 p.distinct = true;
1384 p.group = object! { "g1" => "status" };
1385 p.fields = object! { "f1" => "status", "f2" => "count" };
1386 let result = p.select_sql();
1387 assert!(result.contains("DISTINCT"));
1388 assert!(result.contains("GROUP BY"));
1389 assert!(result.contains("orders.status"));
1390 }
1391
1392 #[test]
1393 fn test_having_empty() {
1394 let mut p = Params::default("mysql");
1395 assert_eq!(p.having(), "");
1396 }
1397
1398 #[test]
1399 fn test_having_single() {
1400 let mut p = Params::default("mysql");
1401 p.having.push("COUNT(*) > 5".to_string());
1402 assert_eq!(p.having(), "HAVING COUNT(*) > 5");
1403 }
1404
1405 #[test]
1406 fn test_having_multiple() {
1407 let mut p = Params::default("mysql");
1408 p.having.push("COUNT(*) > 5".to_string());
1409 p.having.push("SUM(amount) > 100".to_string());
1410 assert_eq!(p.having(), "HAVING COUNT(*) > 5 AND SUM(amount) > 100");
1411 }
1412
1413 #[test]
1414 fn test_select_sql_with_having() {
1415 let mut p = Params::default("mysql");
1416 p.table = "orders".to_string();
1417 p.group = object! { "g1" => "status" };
1418 p.having.push("COUNT(*) > 1".to_string());
1419 let result = p.select_sql();
1420 assert!(result.contains("GROUP BY"));
1421 assert!(result.contains("HAVING COUNT(*) > 1"));
1422 }
1423
1424 #[test]
1425 fn test_field_raw_in_select_sql() {
1426 let mut p = Params::default("mysql");
1427 p.table = "orders".to_string();
1428 p.fields["raw1"] = "SUM(amount) as total".into();
1429 p.fields["raw2"] = "COUNT(*) as cnt".into();
1430 let result = p.select_sql();
1431 assert!(result.contains("SUM(amount) as total"));
1432 assert!(result.contains("COUNT(*) as cnt"));
1433 assert!(!result.contains("orders.`SUM"));
1434 }
1435
1436 #[test]
1437 fn test_where_raw_in_select_sql() {
1438 let mut p = Params::default("mysql");
1439 p.table = "orders".to_string();
1440 p.where_and.push("YEAR(created_at) = 2026".to_string());
1441 let result = p.select_sql();
1442 assert!(result.contains("WHERE YEAR(created_at) = 2026"));
1443 }
1444
1445 #[test]
1446 fn test_limit_only_default() {
1447 let mut p = Params::default("mysql");
1448 assert_eq!(p.page_limit_sql(), "");
1449 }
1450
1451 #[test]
1452 fn test_limit_only_mysql() {
1453 let mut p = Params::default("mysql");
1454 p.limit_only = 5;
1455 assert_eq!(p.page_limit_sql(), "LIMIT 5");
1456 }
1457
1458 #[test]
1459 fn test_limit_only_sqlite() {
1460 let mut p = Params::default("sqlite");
1461 p.limit_only = 10;
1462 assert_eq!(p.page_limit_sql(), "LIMIT 10");
1463 }
1464
1465 #[test]
1466 fn test_limit_only_pgsql() {
1467 let mut p = Params::default("pgsql");
1468 p.limit_only = 3;
1469 assert_eq!(p.page_limit_sql(), "LIMIT 3");
1470 }
1471
1472 #[test]
1473 fn test_page_overrides_limit_only() {
1474 let mut p = Params::default("mysql");
1475 p.limit_only = 5;
1476 p.page = 2;
1477 p.limit = 10;
1478 assert_eq!(p.page_limit_sql(), "LIMIT 10,10");
1479 }
1480
1481 #[test]
1482 fn test_limit_only_in_select_sql() {
1483 let mut p = Params::default("mysql");
1484 p.table = "users".to_string();
1485 p.limit_only = 3;
1486 let result = p.select_sql();
1487 assert!(result.contains("LIMIT 3"));
1488 assert!(!result.contains("OFFSET"));
1489 }
1490
1491 #[test]
1492 fn test_where_in_sub() {
1493 let mut p = Params::default("mysql");
1494 p.table = "users".to_string();
1495 p.where_and
1496 .push("`status` IN (SELECT status FROM active_users)".to_string());
1497 let result = p.select_sql();
1498 assert!(result.contains("WHERE `status` IN (SELECT status FROM active_users)"));
1499 }
1500
1501 #[test]
1502 fn test_where_not_in_sub() {
1503 let mut p = Params::default("pgsql");
1504 p.table = "users".to_string();
1505 p.where_and
1506 .push("\"id\" NOT IN (SELECT user_id FROM banned)".to_string());
1507 let result = p.select_sql();
1508 assert!(result.contains("WHERE \"id\" NOT IN (SELECT user_id FROM banned)"));
1509 }
1510
1511 #[test]
1512 fn test_union_single() {
1513 let mut p = Params::default("mysql");
1514 p.table = "users".to_string();
1515 p.unions.push("UNION SELECT * FROM admins".to_string());
1516 let result = p.select_sql();
1517 assert!(result.contains("UNION SELECT * FROM admins"));
1518 }
1519
1520 #[test]
1521 fn test_union_all() {
1522 let mut p = Params::default("mysql");
1523 p.table = "users".to_string();
1524 p.unions.push("UNION ALL SELECT * FROM guests".to_string());
1525 let result = p.select_sql();
1526 assert!(result.contains("UNION ALL SELECT * FROM guests"));
1527 }
1528
1529 #[test]
1530 fn test_union_multiple() {
1531 let mut p = Params::default("mysql");
1532 p.table = "users".to_string();
1533 p.unions.push("UNION SELECT * FROM admins".to_string());
1534 p.unions.push("UNION ALL SELECT * FROM guests".to_string());
1535 let result = p.select_sql();
1536 assert!(result.contains("UNION SELECT * FROM admins"));
1537 assert!(result.contains("UNION ALL SELECT * FROM guests"));
1538 }
1539
1540 #[test]
1541 fn test_no_union() {
1542 let mut p = Params::default("mysql");
1543 p.table = "users".to_string();
1544 let result = p.select_sql();
1545 assert!(!result.contains("UNION"));
1546 }
1547
1548 #[test]
1549 fn test_timestamps_default_false() {
1550 let p = Params::default("mysql");
1551 assert!(!p.timestamps);
1552 }
1553
1554 #[test]
1555 fn test_lock_for_update_in_select_sql() {
1556 let mut p = Params::default("mysql");
1557 p.table = "users".to_string();
1558 p.lock_mode = "FOR UPDATE".to_string();
1559 let result = p.select_sql();
1560 assert!(result.contains("FOR UPDATE"));
1561 }
1562
1563 #[test]
1564 fn test_lock_for_share_in_select_sql() {
1565 let mut p = Params::default("pgsql");
1566 p.table = "users".to_string();
1567 p.lock_mode = "FOR SHARE".to_string();
1568 let result = p.select_sql();
1569 assert!(result.contains("FOR SHARE"));
1570 }
1571
1572 #[test]
1573 fn test_no_lock_mode() {
1574 let mut p = Params::default("mysql");
1575 p.table = "users".to_string();
1576 let result = p.select_sql();
1577 assert!(!result.contains("FOR UPDATE"));
1578 assert!(!result.contains("FOR SHARE"));
1579 }
1580
1581 #[test]
1582 fn test_where_exists_in_select_sql() {
1583 let mut p = Params::default("mysql");
1584 p.table = "users".to_string();
1585 p.where_and
1586 .push("EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)".to_string());
1587 let result = p.select_sql();
1588 assert!(result.contains("EXISTS (SELECT 1 FROM orders WHERE orders.user_id = users.id)"));
1589 }
1590
1591 #[test]
1592 fn test_where_not_exists_in_select_sql() {
1593 let mut p = Params::default("mysql");
1594 p.table = "users".to_string();
1595 p.where_and
1596 .push("NOT EXISTS (SELECT 1 FROM banned WHERE banned.uid = users.id)".to_string());
1597 let result = p.select_sql();
1598 assert!(result.contains("NOT EXISTS (SELECT 1 FROM banned WHERE banned.uid = users.id)"));
1599 }
1600
1601 #[test]
1602 fn test_lock_with_union() {
1603 let mut p = Params::default("mysql");
1604 p.table = "users".to_string();
1605 p.unions.push("UNION SELECT * FROM admins".to_string());
1606 p.lock_mode = "FOR UPDATE".to_string();
1607 let result = p.select_sql();
1608 assert!(result.contains("UNION SELECT * FROM admins"));
1609 assert!(result.contains("FOR UPDATE"));
1610 }
1611
1612 #[test]
1613 fn test_lock_mode_default_empty() {
1614 let p = Params::default("mysql");
1615 assert!(p.lock_mode.is_empty());
1616 }
1617
1618 #[test]
1619 fn test_unions_default_empty() {
1620 let p = Params::default("mysql");
1621 assert!(p.unions.is_empty());
1622 }
1623}