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