1use crate::clause::{OrderBy, OrderDirection};
7use crate::subquery::SelectQuery;
8use sqlmodel_core::Value;
9
10pub use sqlmodel_core::Dialect;
14
15#[derive(Debug, Clone)]
17pub enum Expr {
18 Column {
20 table: Option<String>,
22 name: String,
24 },
25
26 Literal(Value),
28
29 Placeholder(usize),
31
32 Binary {
34 left: Box<Expr>,
35 op: BinaryOp,
36 right: Box<Expr>,
37 },
38
39 Unary { op: UnaryOp, expr: Box<Expr> },
41
42 Function { name: String, args: Vec<Expr> },
44
45 Case {
47 when_clauses: Vec<(Expr, Expr)>,
49 else_clause: Option<Box<Expr>>,
51 },
52
53 In {
55 expr: Box<Expr>,
56 values: Vec<Expr>,
57 negated: bool,
58 },
59
60 Between {
62 expr: Box<Expr>,
63 low: Box<Expr>,
64 high: Box<Expr>,
65 negated: bool,
66 },
67
68 IsNull { expr: Box<Expr>, negated: bool },
70
71 IsDistinctFrom {
73 left: Box<Expr>,
74 right: Box<Expr>,
75 negated: bool,
76 },
77
78 Cast { expr: Box<Expr>, type_name: String },
80
81 Like {
83 expr: Box<Expr>,
84 pattern: String,
85 negated: bool,
86 case_insensitive: bool,
87 },
88
89 Subquery(String),
91
92 Exists {
96 subquery: String,
98 params: Vec<Value>,
100 negated: bool,
102 },
103
104 ExistsQuery {
108 subquery: Box<SelectQuery>,
110 negated: bool,
112 },
113
114 Raw(String),
116
117 Paren(Box<Expr>),
119
120 CountStar,
122
123 Window {
125 function: Box<Expr>,
127 partition_by: Vec<Expr>,
129 order_by: Vec<OrderBy>,
131 frame: Option<WindowFrame>,
133 },
134
135 JsonExtract {
141 expr: Box<Expr>,
143 path: JsonPath,
145 },
146
147 JsonExtractText {
153 expr: Box<Expr>,
155 path: JsonPath,
157 },
158
159 JsonExtractPath {
164 expr: Box<Expr>,
166 path: Vec<String>,
168 },
169
170 JsonExtractPathText {
176 expr: Box<Expr>,
178 path: Vec<String>,
180 },
181
182 JsonContains {
188 expr: Box<Expr>,
190 other: Box<Expr>,
192 },
193
194 JsonContainedBy {
200 expr: Box<Expr>,
202 other: Box<Expr>,
204 },
205
206 JsonHasKey {
212 expr: Box<Expr>,
214 key: String,
216 },
217
218 JsonHasAnyKey {
224 expr: Box<Expr>,
226 keys: Vec<String>,
228 },
229
230 JsonHasAllKeys {
236 expr: Box<Expr>,
238 keys: Vec<String>,
240 },
241
242 JsonArrayLength {
248 expr: Box<Expr>,
250 },
251
252 JsonTypeof {
258 expr: Box<Expr>,
260 },
261}
262
263#[derive(Debug, Clone)]
265pub enum JsonPath {
266 Key(String),
268 Index(i64),
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub enum BinaryOp {
275 Eq,
278 Ne,
280 Lt,
282 Le,
284 Gt,
286 Ge,
288
289 And,
292 Or,
294
295 Add,
298 Sub,
300 Mul,
302 Div,
304 Mod,
306
307 BitAnd,
310 BitOr,
312 BitXor,
314
315 Concat,
318
319 ArrayContains,
322 ArrayContainedBy,
324 ArrayOverlap,
326}
327
328impl BinaryOp {
329 pub const fn as_str(self) -> &'static str {
331 match self {
332 BinaryOp::Eq => "=",
333 BinaryOp::Ne => "<>",
334 BinaryOp::Lt => "<",
335 BinaryOp::Le => "<=",
336 BinaryOp::Gt => ">",
337 BinaryOp::Ge => ">=",
338 BinaryOp::And => "AND",
339 BinaryOp::Or => "OR",
340 BinaryOp::Add => "+",
341 BinaryOp::Sub => "-",
342 BinaryOp::Mul => "*",
343 BinaryOp::Div => "/",
344 BinaryOp::Mod => "%",
345 BinaryOp::BitAnd => "&",
346 BinaryOp::BitOr => "|",
347 BinaryOp::BitXor => "^",
348 BinaryOp::Concat => "||",
349 BinaryOp::ArrayContains => "@>",
350 BinaryOp::ArrayContainedBy => "<@",
351 BinaryOp::ArrayOverlap => "&&",
352 }
353 }
354
355 pub const fn precedence(self) -> u8 {
357 match self {
358 BinaryOp::Or => 1,
359 BinaryOp::And => 2,
360 BinaryOp::Eq
361 | BinaryOp::Ne
362 | BinaryOp::Lt
363 | BinaryOp::Le
364 | BinaryOp::Gt
365 | BinaryOp::Ge
366 | BinaryOp::ArrayContains
367 | BinaryOp::ArrayContainedBy
368 | BinaryOp::ArrayOverlap => 3,
369 BinaryOp::BitOr => 4,
370 BinaryOp::BitXor => 5,
371 BinaryOp::BitAnd => 6,
372 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Concat => 7,
373 BinaryOp::Mul | BinaryOp::Div | BinaryOp::Mod => 8,
374 }
375 }
376}
377
378#[derive(Debug, Clone, Copy, PartialEq, Eq)]
380pub enum UnaryOp {
381 Not,
382 Neg,
383 BitwiseNot,
384}
385
386impl UnaryOp {
387 pub const fn as_str(&self) -> &'static str {
389 match self {
390 UnaryOp::Not => "NOT",
391 UnaryOp::Neg => "-",
392 UnaryOp::BitwiseNot => "~",
393 }
394 }
395}
396
397#[derive(Debug, Clone)]
401pub struct WindowFrame {
402 pub frame_type: WindowFrameType,
404 pub start: WindowFrameBound,
406 pub end: Option<WindowFrameBound>,
408}
409
410#[derive(Debug, Clone, Copy, PartialEq, Eq)]
412pub enum WindowFrameType {
413 Rows,
415 Range,
417 Groups,
419}
420
421impl WindowFrameType {
422 pub const fn as_str(self) -> &'static str {
424 match self {
425 WindowFrameType::Rows => "ROWS",
426 WindowFrameType::Range => "RANGE",
427 WindowFrameType::Groups => "GROUPS",
428 }
429 }
430}
431
432#[derive(Debug, Clone)]
434pub enum WindowFrameBound {
435 UnboundedPreceding,
437 UnboundedFollowing,
439 CurrentRow,
441 Preceding(u64),
443 Following(u64),
445}
446
447impl WindowFrameBound {
448 pub fn to_sql(&self) -> String {
450 match self {
451 WindowFrameBound::UnboundedPreceding => "UNBOUNDED PRECEDING".to_string(),
452 WindowFrameBound::UnboundedFollowing => "UNBOUNDED FOLLOWING".to_string(),
453 WindowFrameBound::CurrentRow => "CURRENT ROW".to_string(),
454 WindowFrameBound::Preceding(n) => format!("{n} PRECEDING"),
455 WindowFrameBound::Following(n) => format!("{n} FOLLOWING"),
456 }
457 }
458}
459
460impl Expr {
461 pub fn col(name: impl Into<String>) -> Self {
465 Expr::Column {
466 table: None,
467 name: name.into(),
468 }
469 }
470
471 pub fn qualified(table: impl Into<String>, column: impl Into<String>) -> Self {
473 Expr::Column {
474 table: Some(table.into()),
475 name: column.into(),
476 }
477 }
478
479 pub fn lit(value: impl Into<Value>) -> Self {
481 Expr::Literal(value.into())
482 }
483
484 pub fn null() -> Self {
486 Expr::Literal(Value::Null)
487 }
488
489 pub fn raw(sql: impl Into<String>) -> Self {
491 Expr::Raw(sql.into())
492 }
493
494 pub fn placeholder(index: usize) -> Self {
496 Expr::Placeholder(index)
497 }
498
499 pub fn eq(self, other: impl Into<Expr>) -> Self {
503 Expr::Binary {
504 left: Box::new(self),
505 op: BinaryOp::Eq,
506 right: Box::new(other.into()),
507 }
508 }
509
510 pub fn ne(self, other: impl Into<Expr>) -> Self {
512 Expr::Binary {
513 left: Box::new(self),
514 op: BinaryOp::Ne,
515 right: Box::new(other.into()),
516 }
517 }
518
519 pub fn lt(self, other: impl Into<Expr>) -> Self {
521 Expr::Binary {
522 left: Box::new(self),
523 op: BinaryOp::Lt,
524 right: Box::new(other.into()),
525 }
526 }
527
528 pub fn le(self, other: impl Into<Expr>) -> Self {
530 Expr::Binary {
531 left: Box::new(self),
532 op: BinaryOp::Le,
533 right: Box::new(other.into()),
534 }
535 }
536
537 pub fn gt(self, other: impl Into<Expr>) -> Self {
539 Expr::Binary {
540 left: Box::new(self),
541 op: BinaryOp::Gt,
542 right: Box::new(other.into()),
543 }
544 }
545
546 pub fn ge(self, other: impl Into<Expr>) -> Self {
548 Expr::Binary {
549 left: Box::new(self),
550 op: BinaryOp::Ge,
551 right: Box::new(other.into()),
552 }
553 }
554
555 pub fn and(self, other: impl Into<Expr>) -> Self {
559 Expr::Binary {
560 left: Box::new(self),
561 op: BinaryOp::And,
562 right: Box::new(other.into()),
563 }
564 }
565
566 pub fn or(self, other: impl Into<Expr>) -> Self {
568 Expr::Binary {
569 left: Box::new(self),
570 op: BinaryOp::Or,
571 right: Box::new(other.into()),
572 }
573 }
574
575 pub fn not(self) -> Self {
577 Expr::Unary {
578 op: UnaryOp::Not,
579 expr: Box::new(self),
580 }
581 }
582
583 pub fn is_null(self) -> Self {
587 Expr::IsNull {
588 expr: Box::new(self),
589 negated: false,
590 }
591 }
592
593 pub fn is_not_null(self) -> Self {
595 Expr::IsNull {
596 expr: Box::new(self),
597 negated: true,
598 }
599 }
600
601 pub fn is_distinct_from(self, other: impl Into<Expr>) -> Self {
606 Expr::IsDistinctFrom {
607 left: Box::new(self),
608 right: Box::new(other.into()),
609 negated: false,
610 }
611 }
612
613 pub fn is_not_distinct_from(self, other: impl Into<Expr>) -> Self {
618 Expr::IsDistinctFrom {
619 left: Box::new(self),
620 right: Box::new(other.into()),
621 negated: true,
622 }
623 }
624
625 pub fn cast(self, type_name: impl Into<String>) -> Self {
635 Expr::Cast {
636 expr: Box::new(self),
637 type_name: type_name.into(),
638 }
639 }
640
641 pub fn like(self, pattern: impl Into<String>) -> Self {
645 Expr::Like {
646 expr: Box::new(self),
647 pattern: pattern.into(),
648 negated: false,
649 case_insensitive: false,
650 }
651 }
652
653 pub fn not_like(self, pattern: impl Into<String>) -> Self {
655 Expr::Like {
656 expr: Box::new(self),
657 pattern: pattern.into(),
658 negated: true,
659 case_insensitive: false,
660 }
661 }
662
663 pub fn ilike(self, pattern: impl Into<String>) -> Self {
665 Expr::Like {
666 expr: Box::new(self),
667 pattern: pattern.into(),
668 negated: false,
669 case_insensitive: true,
670 }
671 }
672
673 pub fn not_ilike(self, pattern: impl Into<String>) -> Self {
675 Expr::Like {
676 expr: Box::new(self),
677 pattern: pattern.into(),
678 negated: true,
679 case_insensitive: true,
680 }
681 }
682
683 pub fn contains(self, pattern: impl AsRef<str>) -> Self {
691 let pattern = format!("%{}%", pattern.as_ref());
692 Expr::Like {
693 expr: Box::new(self),
694 pattern,
695 negated: false,
696 case_insensitive: false,
697 }
698 }
699
700 pub fn starts_with(self, pattern: impl AsRef<str>) -> Self {
708 let pattern = format!("{}%", pattern.as_ref());
709 Expr::Like {
710 expr: Box::new(self),
711 pattern,
712 negated: false,
713 case_insensitive: false,
714 }
715 }
716
717 pub fn ends_with(self, pattern: impl AsRef<str>) -> Self {
725 let pattern = format!("%{}", pattern.as_ref());
726 Expr::Like {
727 expr: Box::new(self),
728 pattern,
729 negated: false,
730 case_insensitive: false,
731 }
732 }
733
734 pub fn icontains(self, pattern: impl AsRef<str>) -> Self {
736 let pattern = format!("%{}%", pattern.as_ref());
737 Expr::Like {
738 expr: Box::new(self),
739 pattern,
740 negated: false,
741 case_insensitive: true,
742 }
743 }
744
745 pub fn istarts_with(self, pattern: impl AsRef<str>) -> Self {
747 let pattern = format!("{}%", pattern.as_ref());
748 Expr::Like {
749 expr: Box::new(self),
750 pattern,
751 negated: false,
752 case_insensitive: true,
753 }
754 }
755
756 pub fn iends_with(self, pattern: impl AsRef<str>) -> Self {
758 let pattern = format!("%{}", pattern.as_ref());
759 Expr::Like {
760 expr: Box::new(self),
761 pattern,
762 negated: false,
763 case_insensitive: true,
764 }
765 }
766
767 pub fn in_list(self, values: Vec<impl Into<Expr>>) -> Self {
771 if values.is_empty() {
772 return Expr::raw("1 = 0");
773 }
774 Expr::In {
775 expr: Box::new(self),
776 values: values.into_iter().map(Into::into).collect(),
777 negated: false,
778 }
779 }
780
781 pub fn not_in_list(self, values: Vec<impl Into<Expr>>) -> Self {
783 if values.is_empty() {
784 return Expr::raw("1 = 1");
785 }
786 Expr::In {
787 expr: Box::new(self),
788 values: values.into_iter().map(Into::into).collect(),
789 negated: true,
790 }
791 }
792
793 pub fn between(self, low: impl Into<Expr>, high: impl Into<Expr>) -> Self {
797 Expr::Between {
798 expr: Box::new(self),
799 low: Box::new(low.into()),
800 high: Box::new(high.into()),
801 negated: false,
802 }
803 }
804
805 pub fn not_between(self, low: impl Into<Expr>, high: impl Into<Expr>) -> Self {
807 Expr::Between {
808 expr: Box::new(self),
809 low: Box::new(low.into()),
810 high: Box::new(high.into()),
811 negated: true,
812 }
813 }
814
815 pub fn add(self, other: impl Into<Expr>) -> Self {
819 Expr::Binary {
820 left: Box::new(self),
821 op: BinaryOp::Add,
822 right: Box::new(other.into()),
823 }
824 }
825
826 pub fn sub(self, other: impl Into<Expr>) -> Self {
828 Expr::Binary {
829 left: Box::new(self),
830 op: BinaryOp::Sub,
831 right: Box::new(other.into()),
832 }
833 }
834
835 pub fn mul(self, other: impl Into<Expr>) -> Self {
837 Expr::Binary {
838 left: Box::new(self),
839 op: BinaryOp::Mul,
840 right: Box::new(other.into()),
841 }
842 }
843
844 pub fn div(self, other: impl Into<Expr>) -> Self {
846 Expr::Binary {
847 left: Box::new(self),
848 op: BinaryOp::Div,
849 right: Box::new(other.into()),
850 }
851 }
852
853 pub fn modulo(self, other: impl Into<Expr>) -> Self {
855 Expr::Binary {
856 left: Box::new(self),
857 op: BinaryOp::Mod,
858 right: Box::new(other.into()),
859 }
860 }
861
862 pub fn neg(self) -> Self {
864 Expr::Unary {
865 op: UnaryOp::Neg,
866 expr: Box::new(self),
867 }
868 }
869
870 pub fn concat(self, other: impl Into<Expr>) -> Self {
874 Expr::Binary {
875 left: Box::new(self),
876 op: BinaryOp::Concat,
877 right: Box::new(other.into()),
878 }
879 }
880
881 pub fn array_contains(self, other: impl Into<Expr>) -> Self {
885 Expr::Binary {
886 left: Box::new(self),
887 op: BinaryOp::ArrayContains,
888 right: Box::new(other.into()),
889 }
890 }
891
892 pub fn array_contained_by(self, other: impl Into<Expr>) -> Self {
894 Expr::Binary {
895 left: Box::new(self),
896 op: BinaryOp::ArrayContainedBy,
897 right: Box::new(other.into()),
898 }
899 }
900
901 pub fn array_overlap(self, other: impl Into<Expr>) -> Self {
903 Expr::Binary {
904 left: Box::new(self),
905 op: BinaryOp::ArrayOverlap,
906 right: Box::new(other.into()),
907 }
908 }
909
910 pub fn array_any_eq(self, value: impl Into<Expr>) -> Self {
914 let val = value.into();
915 Expr::Binary {
916 left: Box::new(val),
917 op: BinaryOp::Eq,
918 right: Box::new(Expr::Function {
919 name: "ANY".to_string(),
920 args: vec![self],
921 }),
922 }
923 }
924
925 pub fn bit_and(self, other: impl Into<Expr>) -> Self {
929 Expr::Binary {
930 left: Box::new(self),
931 op: BinaryOp::BitAnd,
932 right: Box::new(other.into()),
933 }
934 }
935
936 pub fn bit_or(self, other: impl Into<Expr>) -> Self {
938 Expr::Binary {
939 left: Box::new(self),
940 op: BinaryOp::BitOr,
941 right: Box::new(other.into()),
942 }
943 }
944
945 pub fn bit_xor(self, other: impl Into<Expr>) -> Self {
947 Expr::Binary {
948 left: Box::new(self),
949 op: BinaryOp::BitXor,
950 right: Box::new(other.into()),
951 }
952 }
953
954 pub fn bit_not(self) -> Self {
956 Expr::Unary {
957 op: UnaryOp::BitwiseNot,
958 expr: Box::new(self),
959 }
960 }
961
962 pub fn case() -> CaseBuilder {
974 CaseBuilder {
975 when_clauses: Vec::new(),
976 }
977 }
978
979 pub fn count_star() -> Self {
983 Expr::CountStar
984 }
985
986 pub fn count(self) -> Self {
988 Expr::Function {
989 name: "COUNT".to_string(),
990 args: vec![self],
991 }
992 }
993
994 pub fn sum(self) -> Self {
996 Expr::Function {
997 name: "SUM".to_string(),
998 args: vec![self],
999 }
1000 }
1001
1002 pub fn avg(self) -> Self {
1004 Expr::Function {
1005 name: "AVG".to_string(),
1006 args: vec![self],
1007 }
1008 }
1009
1010 pub fn min(self) -> Self {
1012 Expr::Function {
1013 name: "MIN".to_string(),
1014 args: vec![self],
1015 }
1016 }
1017
1018 pub fn max(self) -> Self {
1020 Expr::Function {
1021 name: "MAX".to_string(),
1022 args: vec![self],
1023 }
1024 }
1025
1026 pub fn function(name: impl Into<String>, args: Vec<Expr>) -> Self {
1028 Expr::Function {
1029 name: name.into(),
1030 args,
1031 }
1032 }
1033
1034 pub fn row_number() -> Self {
1039 Expr::Function {
1040 name: "ROW_NUMBER".to_string(),
1041 args: vec![],
1042 }
1043 }
1044
1045 pub fn rank() -> Self {
1048 Expr::Function {
1049 name: "RANK".to_string(),
1050 args: vec![],
1051 }
1052 }
1053
1054 pub fn dense_rank() -> Self {
1057 Expr::Function {
1058 name: "DENSE_RANK".to_string(),
1059 args: vec![],
1060 }
1061 }
1062
1063 pub fn percent_rank() -> Self {
1066 Expr::Function {
1067 name: "PERCENT_RANK".to_string(),
1068 args: vec![],
1069 }
1070 }
1071
1072 pub fn cume_dist() -> Self {
1075 Expr::Function {
1076 name: "CUME_DIST".to_string(),
1077 args: vec![],
1078 }
1079 }
1080
1081 pub fn ntile(n: i64) -> Self {
1084 Expr::Function {
1085 name: "NTILE".to_string(),
1086 args: vec![Expr::Literal(Value::BigInt(n))],
1087 }
1088 }
1089
1090 pub fn lag(self) -> Self {
1093 Expr::Function {
1094 name: "LAG".to_string(),
1095 args: vec![self],
1096 }
1097 }
1098
1099 pub fn lag_offset(self, offset: i64) -> Self {
1102 Expr::Function {
1103 name: "LAG".to_string(),
1104 args: vec![self, Expr::Literal(Value::BigInt(offset))],
1105 }
1106 }
1107
1108 pub fn lag_with_default(self, offset: i64, default: impl Into<Expr>) -> Self {
1111 Expr::Function {
1112 name: "LAG".to_string(),
1113 args: vec![self, Expr::Literal(Value::BigInt(offset)), default.into()],
1114 }
1115 }
1116
1117 pub fn lead(self) -> Self {
1120 Expr::Function {
1121 name: "LEAD".to_string(),
1122 args: vec![self],
1123 }
1124 }
1125
1126 pub fn lead_offset(self, offset: i64) -> Self {
1129 Expr::Function {
1130 name: "LEAD".to_string(),
1131 args: vec![self, Expr::Literal(Value::BigInt(offset))],
1132 }
1133 }
1134
1135 pub fn lead_with_default(self, offset: i64, default: impl Into<Expr>) -> Self {
1138 Expr::Function {
1139 name: "LEAD".to_string(),
1140 args: vec![self, Expr::Literal(Value::BigInt(offset)), default.into()],
1141 }
1142 }
1143
1144 pub fn first_value(self) -> Self {
1147 Expr::Function {
1148 name: "FIRST_VALUE".to_string(),
1149 args: vec![self],
1150 }
1151 }
1152
1153 pub fn last_value(self) -> Self {
1156 Expr::Function {
1157 name: "LAST_VALUE".to_string(),
1158 args: vec![self],
1159 }
1160 }
1161
1162 pub fn nth_value(self, n: i64) -> Self {
1165 Expr::Function {
1166 name: "NTH_VALUE".to_string(),
1167 args: vec![self, Expr::Literal(Value::BigInt(n))],
1168 }
1169 }
1170
1171 pub fn over(self) -> WindowBuilder {
1191 WindowBuilder {
1192 function: self,
1193 partition_by: Vec::new(),
1194 order_by: Vec::new(),
1195 frame: None,
1196 }
1197 }
1198
1199 pub fn coalesce(args: Vec<impl Into<Expr>>) -> Self {
1209 Expr::Function {
1210 name: "COALESCE".to_string(),
1211 args: args.into_iter().map(Into::into).collect(),
1212 }
1213 }
1214
1215 pub fn nullif(expr1: impl Into<Expr>, expr2: impl Into<Expr>) -> Self {
1223 Expr::Function {
1224 name: "NULLIF".to_string(),
1225 args: vec![expr1.into(), expr2.into()],
1226 }
1227 }
1228
1229 pub fn ifnull(expr1: impl Into<Expr>, expr2: impl Into<Expr>) -> Self {
1233 Expr::Function {
1235 name: "COALESCE".to_string(),
1236 args: vec![expr1.into(), expr2.into()],
1237 }
1238 }
1239
1240 pub fn upper(self) -> Self {
1244 Expr::Function {
1245 name: "UPPER".to_string(),
1246 args: vec![self],
1247 }
1248 }
1249
1250 pub fn lower(self) -> Self {
1252 Expr::Function {
1253 name: "LOWER".to_string(),
1254 args: vec![self],
1255 }
1256 }
1257
1258 pub fn length(self) -> Self {
1260 Expr::Function {
1261 name: "LENGTH".to_string(),
1262 args: vec![self],
1263 }
1264 }
1265
1266 pub fn trim(self) -> Self {
1268 Expr::Function {
1269 name: "TRIM".to_string(),
1270 args: vec![self],
1271 }
1272 }
1273
1274 pub fn ltrim(self) -> Self {
1276 Expr::Function {
1277 name: "LTRIM".to_string(),
1278 args: vec![self],
1279 }
1280 }
1281
1282 pub fn rtrim(self) -> Self {
1284 Expr::Function {
1285 name: "RTRIM".to_string(),
1286 args: vec![self],
1287 }
1288 }
1289
1290 pub fn substr(self, start: impl Into<Expr>, length: Option<impl Into<Expr>>) -> Self {
1296 let mut args = vec![self, start.into()];
1297 if let Some(len) = length {
1298 args.push(len.into());
1299 }
1300 Expr::Function {
1301 name: "SUBSTR".to_string(),
1302 args,
1303 }
1304 }
1305
1306 pub fn replace(self, from: impl Into<Expr>, to: impl Into<Expr>) -> Self {
1308 Expr::Function {
1309 name: "REPLACE".to_string(),
1310 args: vec![self, from.into(), to.into()],
1311 }
1312 }
1313
1314 pub fn abs(self) -> Self {
1318 Expr::Function {
1319 name: "ABS".to_string(),
1320 args: vec![self],
1321 }
1322 }
1323
1324 pub fn round(self, decimals: impl Into<Expr>) -> Self {
1326 Expr::Function {
1327 name: "ROUND".to_string(),
1328 args: vec![self, decimals.into()],
1329 }
1330 }
1331
1332 pub fn floor(self) -> Self {
1334 Expr::Function {
1335 name: "FLOOR".to_string(),
1336 args: vec![self],
1337 }
1338 }
1339
1340 pub fn ceil(self) -> Self {
1342 Expr::Function {
1343 name: "CEIL".to_string(),
1344 args: vec![self],
1345 }
1346 }
1347
1348 pub fn asc(self) -> OrderBy {
1352 OrderBy {
1353 expr: self,
1354 direction: OrderDirection::Asc,
1355 nulls: None,
1356 }
1357 }
1358
1359 pub fn desc(self) -> OrderBy {
1361 OrderBy {
1362 expr: self,
1363 direction: OrderDirection::Desc,
1364 nulls: None,
1365 }
1366 }
1367
1368 pub fn paren(self) -> Self {
1372 Expr::Paren(Box::new(self))
1373 }
1374
1375 pub fn subquery(sql: impl Into<String>) -> Self {
1377 Expr::Subquery(sql.into())
1378 }
1379
1380 pub fn exists(subquery_sql: impl Into<String>, params: Vec<Value>) -> Self {
1397 Expr::Exists {
1398 subquery: subquery_sql.into(),
1399 params,
1400 negated: false,
1401 }
1402 }
1403
1404 pub fn not_exists(subquery_sql: impl Into<String>, params: Vec<Value>) -> Self {
1419 Expr::Exists {
1420 subquery: subquery_sql.into(),
1421 params,
1422 negated: true,
1423 }
1424 }
1425
1426 pub fn exists_query(subquery: SelectQuery) -> Self {
1428 Expr::ExistsQuery {
1429 subquery: Box::new(subquery),
1430 negated: false,
1431 }
1432 }
1433
1434 pub fn not_exists_query(subquery: SelectQuery) -> Self {
1436 Expr::ExistsQuery {
1437 subquery: Box::new(subquery),
1438 negated: true,
1439 }
1440 }
1441
1442 pub fn json_get(self, key: impl Into<String>) -> Self {
1458 Expr::JsonExtract {
1459 expr: Box::new(self),
1460 path: JsonPath::Key(key.into()),
1461 }
1462 }
1463
1464 pub fn json_get_index(self, index: i64) -> Self {
1477 Expr::JsonExtract {
1478 expr: Box::new(self),
1479 path: JsonPath::Index(index),
1480 }
1481 }
1482
1483 pub fn json_get_text(self, key: impl Into<String>) -> Self {
1496 Expr::JsonExtractText {
1497 expr: Box::new(self),
1498 path: JsonPath::Key(key.into()),
1499 }
1500 }
1501
1502 pub fn json_get_text_index(self, index: i64) -> Self {
1510 Expr::JsonExtractText {
1511 expr: Box::new(self),
1512 path: JsonPath::Index(index),
1513 }
1514 }
1515
1516 pub fn json_path(self, path: &[&str]) -> Self {
1529 Expr::JsonExtractPath {
1530 expr: Box::new(self),
1531 path: path.iter().map(|s| (*s).to_string()).collect(),
1532 }
1533 }
1534
1535 pub fn json_path_text(self, path: &[&str]) -> Self {
1548 Expr::JsonExtractPathText {
1549 expr: Box::new(self),
1550 path: path.iter().map(|s| (*s).to_string()).collect(),
1551 }
1552 }
1553
1554 pub fn json_contains(self, other: impl Into<Expr>) -> Self {
1566 Expr::JsonContains {
1567 expr: Box::new(self),
1568 other: Box::new(other.into()),
1569 }
1570 }
1571
1572 pub fn json_contained_by(self, other: impl Into<Expr>) -> Self {
1583 Expr::JsonContainedBy {
1584 expr: Box::new(self),
1585 other: Box::new(other.into()),
1586 }
1587 }
1588
1589 pub fn json_has_key(self, key: impl Into<String>) -> Self {
1602 Expr::JsonHasKey {
1603 expr: Box::new(self),
1604 key: key.into(),
1605 }
1606 }
1607
1608 pub fn json_has_any_key(self, keys: &[&str]) -> Self {
1620 Expr::JsonHasAnyKey {
1621 expr: Box::new(self),
1622 keys: keys.iter().map(|s| (*s).to_string()).collect(),
1623 }
1624 }
1625
1626 pub fn json_has_all_keys(self, keys: &[&str]) -> Self {
1638 Expr::JsonHasAllKeys {
1639 expr: Box::new(self),
1640 keys: keys.iter().map(|s| (*s).to_string()).collect(),
1641 }
1642 }
1643
1644 pub fn json_array_length(self) -> Self {
1656 Expr::JsonArrayLength {
1657 expr: Box::new(self),
1658 }
1659 }
1660
1661 pub fn json_typeof(self) -> Self {
1673 Expr::JsonTypeof {
1674 expr: Box::new(self),
1675 }
1676 }
1677
1678 pub fn build(&self, params: &mut Vec<Value>, offset: usize) -> String {
1682 self.build_with_dialect(Dialect::Postgres, params, offset)
1683 }
1684
1685 pub fn build_with_dialect(
1687 &self,
1688 dialect: Dialect,
1689 params: &mut Vec<Value>,
1690 offset: usize,
1691 ) -> String {
1692 match self {
1693 Expr::Column { table, name } => {
1694 if let Some(t) = table {
1695 format!(
1696 "{}.{}",
1697 dialect.quote_identifier(t),
1698 dialect.quote_identifier(name)
1699 )
1700 } else {
1701 dialect.quote_identifier(name)
1702 }
1703 }
1704
1705 Expr::Literal(value) => {
1706 if matches!(value, Value::Default) {
1707 "DEFAULT".to_string()
1708 } else {
1709 params.push(value.clone());
1710 dialect.placeholder(offset + params.len())
1711 }
1712 }
1713
1714 Expr::Placeholder(idx) => dialect.placeholder(*idx),
1715
1716 Expr::Binary { left, op, right } => {
1717 let left_sql = left.build_with_dialect(dialect, params, offset);
1718 let right_sql = right.build_with_dialect(dialect, params, offset);
1719 if *op == BinaryOp::Concat && dialect == Dialect::Mysql {
1720 format!("CONCAT({left_sql}, {right_sql})")
1721 } else {
1722 format!("{left_sql} {} {right_sql}", op.as_str())
1723 }
1724 }
1725
1726 Expr::Unary { op, expr } => {
1727 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1728 match op {
1729 UnaryOp::Not => format!("NOT {expr_sql}"),
1730 UnaryOp::Neg => format!("-{expr_sql}"),
1731 UnaryOp::BitwiseNot => format!("~{expr_sql}"),
1732 }
1733 }
1734
1735 Expr::Function { name, args } => {
1736 let arg_sqls: Vec<_> = args
1737 .iter()
1738 .map(|a| a.build_with_dialect(dialect, params, offset))
1739 .collect();
1740 format!("{name}({})", arg_sqls.join(", "))
1741 }
1742
1743 Expr::Case {
1744 when_clauses,
1745 else_clause,
1746 } => {
1747 let mut sql = String::from("CASE");
1748 for (condition, result) in when_clauses {
1749 let cond_sql = condition.build_with_dialect(dialect, params, offset);
1750 let result_sql = result.build_with_dialect(dialect, params, offset);
1751 sql.push_str(&format!(" WHEN {cond_sql} THEN {result_sql}"));
1752 }
1753 if let Some(else_expr) = else_clause {
1754 let else_sql = else_expr.build_with_dialect(dialect, params, offset);
1755 sql.push_str(&format!(" ELSE {else_sql}"));
1756 }
1757 sql.push_str(" END");
1758 sql
1759 }
1760
1761 Expr::In {
1762 expr,
1763 values,
1764 negated,
1765 } => {
1766 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1767 let value_sqls: Vec<_> = values
1768 .iter()
1769 .map(|v| v.build_with_dialect(dialect, params, offset))
1770 .collect();
1771 let not_str = if *negated { "NOT " } else { "" };
1772 format!("{expr_sql} {not_str}IN ({})", value_sqls.join(", "))
1773 }
1774
1775 Expr::Between {
1776 expr,
1777 low,
1778 high,
1779 negated,
1780 } => {
1781 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1782 let low_sql = low.build_with_dialect(dialect, params, offset);
1783 let high_sql = high.build_with_dialect(dialect, params, offset);
1784 let not_str = if *negated { "NOT " } else { "" };
1785 format!("{expr_sql} {not_str}BETWEEN {low_sql} AND {high_sql}")
1786 }
1787
1788 Expr::IsNull { expr, negated } => {
1789 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1790 let not_str = if *negated { " NOT" } else { "" };
1791 format!("{expr_sql} IS{not_str} NULL")
1792 }
1793
1794 Expr::IsDistinctFrom {
1795 left,
1796 right,
1797 negated,
1798 } => {
1799 let left_sql = left.build_with_dialect(dialect, params, offset);
1800 let right_sql = right.build_with_dialect(dialect, params, offset);
1801 let not_str = if *negated { " NOT" } else { "" };
1802 format!("{left_sql} IS{not_str} DISTINCT FROM {right_sql}")
1804 }
1805
1806 Expr::Cast { expr, type_name } => {
1807 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1808 format!("CAST({expr_sql} AS {type_name})")
1809 }
1810
1811 Expr::Like {
1812 expr,
1813 pattern,
1814 negated,
1815 case_insensitive,
1816 } => {
1817 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1818 params.push(Value::Text(pattern.clone()));
1819 let param = dialect.placeholder(offset + params.len());
1820 let not_str = if *negated { "NOT " } else { "" };
1821 let op = if *case_insensitive && dialect.supports_ilike() {
1822 "ILIKE"
1823 } else if *case_insensitive {
1824 return format!("LOWER({expr_sql}) {not_str}LIKE LOWER({param})");
1826 } else {
1827 "LIKE"
1828 };
1829 format!("{expr_sql} {not_str}{op} {param}")
1830 }
1831
1832 Expr::Subquery(sql) => format!("({sql})"),
1833
1834 Expr::Exists {
1835 subquery,
1836 params: subquery_params,
1837 negated,
1838 } => {
1839 let start_idx = offset + params.len();
1843 params.extend(subquery_params.iter().cloned());
1844
1845 let adjusted_subquery = if subquery_params.is_empty() {
1849 subquery.clone()
1850 } else {
1851 adjust_placeholder_indices(subquery, start_idx, dialect)
1853 };
1854
1855 let not_str = if *negated { "NOT " } else { "" };
1856 format!("{not_str}EXISTS ({adjusted_subquery})")
1857 }
1858
1859 Expr::ExistsQuery { subquery, negated } => {
1860 let (subquery_sql, subquery_params) =
1861 subquery.build_exists_subquery_with_dialect(dialect);
1862 let start_idx = offset + params.len();
1863
1864 let adjusted_subquery = if subquery_params.is_empty() {
1865 subquery_sql
1866 } else {
1867 adjust_placeholder_indices(&subquery_sql, start_idx, dialect)
1868 };
1869
1870 params.extend(subquery_params.iter().cloned());
1871
1872 let not_str = if *negated { "NOT " } else { "" };
1873 format!("{not_str}EXISTS ({adjusted_subquery})")
1874 }
1875
1876 Expr::Raw(sql) => sql.clone(),
1877
1878 Expr::Paren(expr) => {
1879 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1880 format!("({expr_sql})")
1881 }
1882
1883 Expr::CountStar => "COUNT(*)".to_string(),
1884
1885 Expr::Window {
1886 function,
1887 partition_by,
1888 order_by,
1889 frame,
1890 } => {
1891 let func_sql = function.build_with_dialect(dialect, params, offset);
1892 let mut over_parts: Vec<String> = Vec::new();
1893
1894 if !partition_by.is_empty() {
1896 let partition_sqls: Vec<_> = partition_by
1897 .iter()
1898 .map(|e| e.build_with_dialect(dialect, params, offset))
1899 .collect();
1900 over_parts.push(format!("PARTITION BY {}", partition_sqls.join(", ")));
1901 }
1902
1903 if !order_by.is_empty() {
1905 let order_sqls: Vec<_> = order_by
1906 .iter()
1907 .map(|o| {
1908 let expr_sql = o.expr.build_with_dialect(dialect, params, offset);
1909 let dir = match o.direction {
1910 OrderDirection::Asc => "ASC",
1911 OrderDirection::Desc => "DESC",
1912 };
1913 let nulls = match o.nulls {
1914 Some(crate::clause::NullsOrder::First) => " NULLS FIRST",
1915 Some(crate::clause::NullsOrder::Last) => " NULLS LAST",
1916 None => "",
1917 };
1918 format!("{expr_sql} {dir}{nulls}")
1919 })
1920 .collect();
1921 over_parts.push(format!("ORDER BY {}", order_sqls.join(", ")));
1922 }
1923
1924 if let Some(f) = frame {
1926 let frame_sql = if let Some(end) = &f.end {
1927 format!(
1928 "{} BETWEEN {} AND {}",
1929 f.frame_type.as_str(),
1930 f.start.to_sql(),
1931 end.to_sql()
1932 )
1933 } else {
1934 format!("{} {}", f.frame_type.as_str(), f.start.to_sql())
1935 };
1936 over_parts.push(frame_sql);
1937 }
1938
1939 if over_parts.is_empty() {
1940 format!("{func_sql} OVER ()")
1941 } else {
1942 format!("{func_sql} OVER ({})", over_parts.join(" "))
1943 }
1944 }
1945
1946 Expr::JsonExtract { expr, path } => {
1948 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1949 match dialect {
1950 Dialect::Postgres => match path {
1951 JsonPath::Key(key) => format!("{expr_sql} -> '{key}'"),
1952 JsonPath::Index(idx) => format!("{expr_sql} -> {idx}"),
1953 },
1954 Dialect::Mysql => {
1955 let json_path = match path {
1956 JsonPath::Key(key) => format!("$.{key}"),
1957 JsonPath::Index(idx) => format!("$[{idx}]"),
1958 };
1959 format!("JSON_EXTRACT({expr_sql}, '{json_path}')")
1960 }
1961 Dialect::Sqlite => {
1962 let json_path = match path {
1963 JsonPath::Key(key) => format!("$.{key}"),
1964 JsonPath::Index(idx) => format!("$[{idx}]"),
1965 };
1966 format!("json_extract({expr_sql}, '{json_path}')")
1967 }
1968 }
1969 }
1970
1971 Expr::JsonExtractText { expr, path } => {
1972 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1973 match dialect {
1974 Dialect::Postgres => match path {
1975 JsonPath::Key(key) => format!("{expr_sql} ->> '{key}'"),
1976 JsonPath::Index(idx) => format!("{expr_sql} ->> {idx}"),
1977 },
1978 Dialect::Mysql => {
1979 let json_path = match path {
1980 JsonPath::Key(key) => format!("$.{key}"),
1981 JsonPath::Index(idx) => format!("$[{idx}]"),
1982 };
1983 format!("JSON_UNQUOTE(JSON_EXTRACT({expr_sql}, '{json_path}'))")
1984 }
1985 Dialect::Sqlite => {
1986 let json_path = match path {
1988 JsonPath::Key(key) => format!("$.{key}"),
1989 JsonPath::Index(idx) => format!("$[{idx}]"),
1990 };
1991 format!("json_extract({expr_sql}, '{json_path}')")
1992 }
1993 }
1994 }
1995
1996 Expr::JsonExtractPath { expr, path } => {
1997 let expr_sql = expr.build_with_dialect(dialect, params, offset);
1998 match dialect {
1999 Dialect::Postgres => {
2000 let path_array = path.join(", ");
2001 format!("{expr_sql} #> '{{{path_array}}}'")
2002 }
2003 Dialect::Mysql | Dialect::Sqlite => {
2004 let json_path = format!("$.{}", path.join("."));
2005 let func = if dialect == Dialect::Mysql {
2006 "JSON_EXTRACT"
2007 } else {
2008 "json_extract"
2009 };
2010 format!("{func}({expr_sql}, '{json_path}')")
2011 }
2012 }
2013 }
2014
2015 Expr::JsonExtractPathText { expr, path } => {
2016 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2017 match dialect {
2018 Dialect::Postgres => {
2019 let path_array = path.join(", ");
2020 format!("{expr_sql} #>> '{{{path_array}}}'")
2021 }
2022 Dialect::Mysql => {
2023 let json_path = format!("$.{}", path.join("."));
2024 format!("JSON_UNQUOTE(JSON_EXTRACT({expr_sql}, '{json_path}'))")
2025 }
2026 Dialect::Sqlite => {
2027 let json_path = format!("$.{}", path.join("."));
2028 format!("json_extract({expr_sql}, '{json_path}')")
2029 }
2030 }
2031 }
2032
2033 Expr::JsonContains { expr, other } => {
2034 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2035 let other_sql = other.build_with_dialect(dialect, params, offset);
2036 match dialect {
2037 Dialect::Postgres => format!("{expr_sql} @> {other_sql}"),
2038 Dialect::Mysql => format!("JSON_CONTAINS({expr_sql}, {other_sql})"),
2039 Dialect::Sqlite => {
2040 format!(
2042 "/* JSON containment not supported in SQLite */ ({expr_sql} = {other_sql})"
2043 )
2044 }
2045 }
2046 }
2047
2048 Expr::JsonContainedBy { expr, other } => {
2049 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2050 let other_sql = other.build_with_dialect(dialect, params, offset);
2051 match dialect {
2052 Dialect::Postgres => format!("{expr_sql} <@ {other_sql}"),
2053 Dialect::Mysql => format!("JSON_CONTAINS({other_sql}, {expr_sql})"),
2054 Dialect::Sqlite => {
2055 format!(
2056 "/* JSON contained-by not supported in SQLite */ ({expr_sql} = {other_sql})"
2057 )
2058 }
2059 }
2060 }
2061
2062 Expr::JsonHasKey { expr, key } => {
2063 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2064 match dialect {
2065 Dialect::Postgres => format!("{expr_sql} ? '{key}'"),
2066 Dialect::Mysql => format!("JSON_CONTAINS_PATH({expr_sql}, 'one', '$.{key}')"),
2067 Dialect::Sqlite => format!("json_type({expr_sql}, '$.{key}') IS NOT NULL"),
2068 }
2069 }
2070
2071 Expr::JsonHasAnyKey { expr, keys } => {
2072 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2073 match dialect {
2074 Dialect::Postgres => {
2075 let keys_array = keys
2076 .iter()
2077 .map(|k| format!("'{k}'"))
2078 .collect::<Vec<_>>()
2079 .join(", ");
2080 format!("{expr_sql} ?| array[{keys_array}]")
2081 }
2082 Dialect::Mysql => {
2083 let paths = keys
2084 .iter()
2085 .map(|k| format!("'$.{k}'"))
2086 .collect::<Vec<_>>()
2087 .join(", ");
2088 format!("JSON_CONTAINS_PATH({expr_sql}, 'one', {paths})")
2089 }
2090 Dialect::Sqlite => {
2091 let checks = keys
2092 .iter()
2093 .map(|k| format!("json_type({expr_sql}, '$.{k}') IS NOT NULL"))
2094 .collect::<Vec<_>>()
2095 .join(" OR ");
2096 format!("({checks})")
2097 }
2098 }
2099 }
2100
2101 Expr::JsonHasAllKeys { expr, keys } => {
2102 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2103 match dialect {
2104 Dialect::Postgres => {
2105 let keys_array = keys
2106 .iter()
2107 .map(|k| format!("'{k}'"))
2108 .collect::<Vec<_>>()
2109 .join(", ");
2110 format!("{expr_sql} ?& array[{keys_array}]")
2111 }
2112 Dialect::Mysql => {
2113 let paths = keys
2114 .iter()
2115 .map(|k| format!("'$.{k}'"))
2116 .collect::<Vec<_>>()
2117 .join(", ");
2118 format!("JSON_CONTAINS_PATH({expr_sql}, 'all', {paths})")
2119 }
2120 Dialect::Sqlite => {
2121 let checks = keys
2122 .iter()
2123 .map(|k| format!("json_type({expr_sql}, '$.{k}') IS NOT NULL"))
2124 .collect::<Vec<_>>()
2125 .join(" AND ");
2126 format!("({checks})")
2127 }
2128 }
2129 }
2130
2131 Expr::JsonArrayLength { expr } => {
2132 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2133 match dialect {
2134 Dialect::Postgres => format!("jsonb_array_length({expr_sql})"),
2135 Dialect::Mysql => format!("JSON_LENGTH({expr_sql})"),
2136 Dialect::Sqlite => format!("json_array_length({expr_sql})"),
2137 }
2138 }
2139
2140 Expr::JsonTypeof { expr } => {
2141 let expr_sql = expr.build_with_dialect(dialect, params, offset);
2142 match dialect {
2143 Dialect::Postgres => format!("jsonb_typeof({expr_sql})"),
2144 Dialect::Mysql => format!("JSON_TYPE({expr_sql})"),
2145 Dialect::Sqlite => format!("json_type({expr_sql})"),
2146 }
2147 }
2148 }
2149 }
2150}
2151
2152#[derive(Debug, Clone)]
2156pub struct CaseBuilder {
2157 when_clauses: Vec<(Expr, Expr)>,
2158}
2159
2160impl CaseBuilder {
2161 pub fn when(mut self, condition: impl Into<Expr>, result: impl Into<Expr>) -> Self {
2163 self.when_clauses.push((condition.into(), result.into()));
2164 self
2165 }
2166
2167 pub fn otherwise(self, else_result: impl Into<Expr>) -> Expr {
2169 Expr::Case {
2170 when_clauses: self.when_clauses,
2171 else_clause: Some(Box::new(else_result.into())),
2172 }
2173 }
2174
2175 pub fn end(self) -> Expr {
2177 Expr::Case {
2178 when_clauses: self.when_clauses,
2179 else_clause: None,
2180 }
2181 }
2182}
2183
2184#[derive(Debug, Clone)]
2188pub struct WindowBuilder {
2189 function: Expr,
2190 partition_by: Vec<Expr>,
2191 order_by: Vec<OrderBy>,
2192 frame: Option<WindowFrame>,
2193}
2194
2195impl WindowBuilder {
2196 pub fn partition_by(mut self, expr: impl Into<Expr>) -> Self {
2200 self.partition_by.push(expr.into());
2201 self
2202 }
2203
2204 pub fn partition_by_many(mut self, exprs: Vec<impl Into<Expr>>) -> Self {
2206 self.partition_by.extend(exprs.into_iter().map(Into::into));
2207 self
2208 }
2209
2210 pub fn order_by(mut self, order: OrderBy) -> Self {
2214 self.order_by.push(order);
2215 self
2216 }
2217
2218 pub fn order_by_asc(mut self, expr: impl Into<Expr>) -> Self {
2220 self.order_by.push(OrderBy {
2221 expr: expr.into(),
2222 direction: OrderDirection::Asc,
2223 nulls: None,
2224 });
2225 self
2226 }
2227
2228 pub fn order_by_desc(mut self, expr: impl Into<Expr>) -> Self {
2230 self.order_by.push(OrderBy {
2231 expr: expr.into(),
2232 direction: OrderDirection::Desc,
2233 nulls: None,
2234 });
2235 self
2236 }
2237
2238 pub fn rows_between(mut self, start: WindowFrameBound, end: WindowFrameBound) -> Self {
2246 self.frame = Some(WindowFrame {
2247 frame_type: WindowFrameType::Rows,
2248 start,
2249 end: Some(end),
2250 });
2251 self
2252 }
2253
2254 pub fn rows(mut self, start: WindowFrameBound) -> Self {
2262 self.frame = Some(WindowFrame {
2263 frame_type: WindowFrameType::Rows,
2264 start,
2265 end: None,
2266 });
2267 self
2268 }
2269
2270 pub fn range_between(mut self, start: WindowFrameBound, end: WindowFrameBound) -> Self {
2278 self.frame = Some(WindowFrame {
2279 frame_type: WindowFrameType::Range,
2280 start,
2281 end: Some(end),
2282 });
2283 self
2284 }
2285
2286 pub fn range(mut self, start: WindowFrameBound) -> Self {
2288 self.frame = Some(WindowFrame {
2289 frame_type: WindowFrameType::Range,
2290 start,
2291 end: None,
2292 });
2293 self
2294 }
2295
2296 pub fn groups_between(mut self, start: WindowFrameBound, end: WindowFrameBound) -> Self {
2298 self.frame = Some(WindowFrame {
2299 frame_type: WindowFrameType::Groups,
2300 start,
2301 end: Some(end),
2302 });
2303 self
2304 }
2305
2306 pub fn build(self) -> Expr {
2308 Expr::Window {
2309 function: Box::new(self.function),
2310 partition_by: self.partition_by,
2311 order_by: self.order_by,
2312 frame: self.frame,
2313 }
2314 }
2315}
2316
2317impl From<Value> for Expr {
2319 fn from(v: Value) -> Self {
2320 Expr::Literal(v)
2321 }
2322}
2323
2324impl From<&str> for Expr {
2325 fn from(s: &str) -> Self {
2326 Expr::Literal(Value::Text(s.to_string()))
2327 }
2328}
2329
2330impl From<String> for Expr {
2331 fn from(s: String) -> Self {
2332 Expr::Literal(Value::Text(s))
2333 }
2334}
2335
2336impl From<i32> for Expr {
2337 fn from(n: i32) -> Self {
2338 Expr::Literal(Value::Int(n))
2339 }
2340}
2341
2342impl From<i64> for Expr {
2343 fn from(n: i64) -> Self {
2344 Expr::Literal(Value::BigInt(n))
2345 }
2346}
2347
2348impl From<bool> for Expr {
2349 fn from(b: bool) -> Self {
2350 Expr::Literal(Value::Bool(b))
2351 }
2352}
2353
2354impl From<f64> for Expr {
2355 fn from(n: f64) -> Self {
2356 Expr::Literal(Value::Double(n))
2357 }
2358}
2359
2360impl From<f32> for Expr {
2361 fn from(n: f32) -> Self {
2362 Expr::Literal(Value::Float(n))
2363 }
2364}
2365
2366pub(crate) fn adjust_placeholder_indices(sql: &str, offset: usize, dialect: Dialect) -> String {
2373 if offset == 0 {
2374 return sql.to_string();
2375 }
2376
2377 match dialect {
2378 Dialect::Postgres => {
2379 let mut result = String::with_capacity(sql.len() + 20);
2381 let chars: Vec<char> = sql.chars().collect();
2382 let mut i = 0;
2383 let mut in_single_quote = false;
2384 let mut in_double_quote = false;
2385
2386 while i < chars.len() {
2387 let c = chars[i];
2388
2389 if in_single_quote {
2390 result.push(c);
2391 if c == '\'' {
2392 if i + 1 < chars.len() && chars[i + 1] == '\'' {
2393 result.push(chars[i + 1]);
2395 i += 2;
2396 continue;
2397 }
2398 in_single_quote = false;
2399 }
2400 i += 1;
2401 continue;
2402 }
2403
2404 if in_double_quote {
2405 result.push(c);
2406 if c == '"' {
2407 if i + 1 < chars.len() && chars[i + 1] == '"' {
2408 result.push(chars[i + 1]);
2410 i += 2;
2411 continue;
2412 }
2413 in_double_quote = false;
2414 }
2415 i += 1;
2416 continue;
2417 }
2418
2419 if c == '\'' {
2420 in_single_quote = true;
2421 result.push(c);
2422 i += 1;
2423 continue;
2424 }
2425 if c == '"' {
2426 in_double_quote = true;
2427 result.push(c);
2428 i += 1;
2429 continue;
2430 }
2431
2432 if c == '$' {
2433 let mut j = i + 1;
2434 while j < chars.len() && chars[j].is_ascii_digit() {
2435 j += 1;
2436 }
2437
2438 if j > i + 1 {
2439 let num_str: String = chars[i + 1..j].iter().collect();
2440 if let Ok(n) = num_str.parse::<usize>() {
2441 result.push_str(&format!("${}", n + offset));
2442 } else {
2443 result.push('$');
2444 result.push_str(&num_str);
2445 }
2446 i = j;
2447 continue;
2448 }
2449 }
2450
2451 result.push(c);
2452 i += 1;
2453 }
2454 result
2455 }
2456 Dialect::Sqlite => {
2457 let mut result = String::with_capacity(sql.len() + 20);
2459 let chars: Vec<char> = sql.chars().collect();
2460 let mut i = 0;
2461 let mut in_single_quote = false;
2462 let mut in_double_quote = false;
2463
2464 while i < chars.len() {
2465 let c = chars[i];
2466
2467 if in_single_quote {
2468 result.push(c);
2469 if c == '\'' {
2470 if i + 1 < chars.len() && chars[i + 1] == '\'' {
2471 result.push(chars[i + 1]);
2472 i += 2;
2473 continue;
2474 }
2475 in_single_quote = false;
2476 }
2477 i += 1;
2478 continue;
2479 }
2480
2481 if in_double_quote {
2482 result.push(c);
2483 if c == '"' {
2484 if i + 1 < chars.len() && chars[i + 1] == '"' {
2485 result.push(chars[i + 1]);
2486 i += 2;
2487 continue;
2488 }
2489 in_double_quote = false;
2490 }
2491 i += 1;
2492 continue;
2493 }
2494
2495 if c == '\'' {
2496 in_single_quote = true;
2497 result.push(c);
2498 i += 1;
2499 continue;
2500 }
2501 if c == '"' {
2502 in_double_quote = true;
2503 result.push(c);
2504 i += 1;
2505 continue;
2506 }
2507
2508 if c == '?' {
2509 let mut j = i + 1;
2510 while j < chars.len() && chars[j].is_ascii_digit() {
2511 j += 1;
2512 }
2513
2514 if j > i + 1 {
2515 let num_str: String = chars[i + 1..j].iter().collect();
2516 if let Ok(n) = num_str.parse::<usize>() {
2517 result.push_str(&format!("?{}", n + offset));
2518 } else {
2519 result.push('?');
2520 result.push_str(&num_str);
2521 }
2522 i = j;
2523 continue;
2524 }
2525 }
2526
2527 result.push(c);
2528 i += 1;
2529 }
2530 result
2531 }
2532 Dialect::Mysql => {
2533 sql.to_string()
2535 }
2536 }
2537}
2538
2539#[cfg(test)]
2542mod tests {
2543 use super::*;
2544
2545 #[test]
2548 fn test_column_simple() {
2549 let expr = Expr::col("name");
2550 let mut params = Vec::new();
2551 let sql = expr.build(&mut params, 0);
2552 assert_eq!(sql, "\"name\"");
2553 assert!(params.is_empty());
2554 }
2555
2556 #[test]
2557 fn test_column_qualified() {
2558 let expr = Expr::qualified("users", "name");
2559 let mut params = Vec::new();
2560 let sql = expr.build(&mut params, 0);
2561 assert_eq!(sql, "\"users\".\"name\"");
2562 assert!(params.is_empty());
2563 }
2564
2565 #[test]
2568 fn test_literal_int() {
2569 let expr = Expr::lit(42);
2570 let mut params = Vec::new();
2571 let sql = expr.build(&mut params, 0);
2572 assert_eq!(sql, "$1");
2573 assert_eq!(params.len(), 1);
2574 assert_eq!(params[0], Value::Int(42));
2575 }
2576
2577 #[test]
2578 fn test_literal_string() {
2579 let expr = Expr::lit("hello");
2580 let mut params = Vec::new();
2581 let sql = expr.build(&mut params, 0);
2582 assert_eq!(sql, "$1");
2583 assert_eq!(params[0], Value::Text("hello".to_string()));
2584 }
2585
2586 #[test]
2587 fn test_literal_null() {
2588 let expr = Expr::null();
2589 let mut params = Vec::new();
2590 let sql = expr.build(&mut params, 0);
2591 assert_eq!(sql, "$1");
2592 assert_eq!(params[0], Value::Null);
2593 }
2594
2595 #[test]
2598 fn test_eq() {
2599 let expr = Expr::col("age").eq(18);
2600 let mut params = Vec::new();
2601 let sql = expr.build(&mut params, 0);
2602 assert_eq!(sql, "\"age\" = $1");
2603 assert_eq!(params[0], Value::Int(18));
2604 }
2605
2606 #[test]
2607 fn test_ne() {
2608 let expr = Expr::col("status").ne("deleted");
2609 let mut params = Vec::new();
2610 let sql = expr.build(&mut params, 0);
2611 assert_eq!(sql, "\"status\" <> $1");
2612 }
2613
2614 #[test]
2615 fn test_lt_le_gt_ge() {
2616 let mut params = Vec::new();
2617
2618 let lt = Expr::col("age").lt(18).build(&mut params, 0);
2619 assert_eq!(lt, "\"age\" < $1");
2620
2621 params.clear();
2622 let le = Expr::col("age").le(18).build(&mut params, 0);
2623 assert_eq!(le, "\"age\" <= $1");
2624
2625 params.clear();
2626 let gt = Expr::col("age").gt(18).build(&mut params, 0);
2627 assert_eq!(gt, "\"age\" > $1");
2628
2629 params.clear();
2630 let ge = Expr::col("age").ge(18).build(&mut params, 0);
2631 assert_eq!(ge, "\"age\" >= $1");
2632 }
2633
2634 #[test]
2637 fn test_and() {
2638 let expr = Expr::col("a").eq(1).and(Expr::col("b").eq(2));
2639 let mut params = Vec::new();
2640 let sql = expr.build(&mut params, 0);
2641 assert_eq!(sql, "\"a\" = $1 AND \"b\" = $2");
2642 }
2643
2644 #[test]
2645 fn test_or() {
2646 let expr = Expr::col("a").eq(1).or(Expr::col("b").eq(2));
2647 let mut params = Vec::new();
2648 let sql = expr.build(&mut params, 0);
2649 assert_eq!(sql, "\"a\" = $1 OR \"b\" = $2");
2650 }
2651
2652 #[test]
2653 fn test_not() {
2654 let expr = Expr::col("active").not();
2655 let mut params = Vec::new();
2656 let sql = expr.build(&mut params, 0);
2657 assert_eq!(sql, "NOT \"active\"");
2658 }
2659
2660 #[test]
2663 fn test_is_null() {
2664 let expr = Expr::col("deleted_at").is_null();
2665 let mut params = Vec::new();
2666 let sql = expr.build(&mut params, 0);
2667 assert_eq!(sql, "\"deleted_at\" IS NULL");
2668 }
2669
2670 #[test]
2671 fn test_is_not_null() {
2672 let expr = Expr::col("name").is_not_null();
2673 let mut params = Vec::new();
2674 let sql = expr.build(&mut params, 0);
2675 assert_eq!(sql, "\"name\" IS NOT NULL");
2676 }
2677
2678 #[test]
2681 fn test_like() {
2682 let expr = Expr::col("name").like("%john%");
2683 let mut params = Vec::new();
2684 let sql = expr.build(&mut params, 0);
2685 assert_eq!(sql, "\"name\" LIKE $1");
2686 assert_eq!(params[0], Value::Text("%john%".to_string()));
2687 }
2688
2689 #[test]
2690 fn test_not_like() {
2691 let expr = Expr::col("name").not_like("%test%");
2692 let mut params = Vec::new();
2693 let sql = expr.build(&mut params, 0);
2694 assert_eq!(sql, "\"name\" NOT LIKE $1");
2695 }
2696
2697 #[test]
2698 fn test_ilike_postgres() {
2699 let expr = Expr::col("name").ilike("%JOHN%");
2700 let mut params = Vec::new();
2701 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
2702 assert_eq!(sql, "\"name\" ILIKE $1");
2703 }
2704
2705 #[test]
2706 fn test_ilike_fallback_sqlite() {
2707 let expr = Expr::col("name").ilike("%JOHN%");
2708 let mut params = Vec::new();
2709 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
2710 assert_eq!(sql, "LOWER(\"name\") LIKE LOWER(?1)");
2711 }
2712
2713 #[test]
2716 fn test_in_list() {
2717 let expr = Expr::col("status").in_list(vec![1, 2, 3]);
2718 let mut params = Vec::new();
2719 let sql = expr.build(&mut params, 0);
2720 assert_eq!(sql, "\"status\" IN ($1, $2, $3)");
2721 assert_eq!(params.len(), 3);
2722 }
2723
2724 #[test]
2725 fn test_not_in_list() {
2726 let expr = Expr::col("status").not_in_list(vec![4, 5]);
2727 let mut params = Vec::new();
2728 let sql = expr.build(&mut params, 0);
2729 assert_eq!(sql, "\"status\" NOT IN ($1, $2)");
2730 }
2731
2732 #[test]
2735 fn test_between() {
2736 let expr = Expr::col("age").between(18, 65);
2737 let mut params = Vec::new();
2738 let sql = expr.build(&mut params, 0);
2739 assert_eq!(sql, "\"age\" BETWEEN $1 AND $2");
2740 assert_eq!(params[0], Value::Int(18));
2741 assert_eq!(params[1], Value::Int(65));
2742 }
2743
2744 #[test]
2745 fn test_not_between() {
2746 let expr = Expr::col("age").not_between(0, 17);
2747 let mut params = Vec::new();
2748 let sql = expr.build(&mut params, 0);
2749 assert_eq!(sql, "\"age\" NOT BETWEEN $1 AND $2");
2750 }
2751
2752 #[test]
2755 fn test_arithmetic() {
2756 let mut params = Vec::new();
2757
2758 let add = Expr::col("a").add(Expr::col("b")).build(&mut params, 0);
2759 assert_eq!(add, "\"a\" + \"b\"");
2760
2761 let sub = Expr::col("a").sub(Expr::col("b")).build(&mut params, 0);
2762 assert_eq!(sub, "\"a\" - \"b\"");
2763
2764 let mul = Expr::col("a").mul(Expr::col("b")).build(&mut params, 0);
2765 assert_eq!(mul, "\"a\" * \"b\"");
2766
2767 let div = Expr::col("a").div(Expr::col("b")).build(&mut params, 0);
2768 assert_eq!(div, "\"a\" / \"b\"");
2769
2770 let modulo = Expr::col("a").modulo(Expr::col("b")).build(&mut params, 0);
2771 assert_eq!(modulo, "\"a\" % \"b\"");
2772 }
2773
2774 #[test]
2775 fn test_neg() {
2776 let expr = Expr::col("balance").neg();
2777 let mut params = Vec::new();
2778 let sql = expr.build(&mut params, 0);
2779 assert_eq!(sql, "-\"balance\"");
2780 }
2781
2782 #[test]
2785 fn test_bitwise() {
2786 let mut params = Vec::new();
2787
2788 let bit_and = Expr::col("flags")
2789 .bit_and(Expr::lit(0xFF))
2790 .build(&mut params, 0);
2791 assert_eq!(bit_and, "\"flags\" & $1");
2792
2793 params.clear();
2794 let or_sql = Expr::col("flags")
2795 .bit_or(Expr::lit(0x01))
2796 .build(&mut params, 0);
2797 assert_eq!(or_sql, "\"flags\" | $1");
2798
2799 params.clear();
2800 let xor_sql = Expr::col("flags")
2801 .bit_xor(Expr::lit(0x0F))
2802 .build(&mut params, 0);
2803 assert_eq!(xor_sql, "\"flags\" ^ $1");
2804
2805 let bit_not = Expr::col("flags").bit_not().build(&mut params, 0);
2806 assert_eq!(bit_not, "~\"flags\"");
2807 }
2808
2809 #[test]
2812 fn test_case_simple() {
2813 let expr = Expr::case()
2814 .when(Expr::col("status").eq("active"), "Yes")
2815 .when(Expr::col("status").eq("pending"), "Maybe")
2816 .otherwise("No");
2817
2818 let mut params = Vec::new();
2819 let sql = expr.build(&mut params, 0);
2820 assert_eq!(
2821 sql,
2822 "CASE WHEN \"status\" = $1 THEN $2 WHEN \"status\" = $3 THEN $4 ELSE $5 END"
2823 );
2824 assert_eq!(params.len(), 5);
2825 }
2826
2827 #[test]
2828 fn test_case_without_else() {
2829 let expr = Expr::case().when(Expr::col("age").gt(18), "adult").end();
2830
2831 let mut params = Vec::new();
2832 let sql = expr.build(&mut params, 0);
2833 assert_eq!(sql, "CASE WHEN \"age\" > $1 THEN $2 END");
2834 }
2835
2836 #[test]
2839 fn test_count_star() {
2840 let expr = Expr::count_star();
2841 let mut params = Vec::new();
2842 let sql = expr.build(&mut params, 0);
2843 assert_eq!(sql, "COUNT(*)");
2844 }
2845
2846 #[test]
2847 fn test_count() {
2848 let expr = Expr::col("id").count();
2849 let mut params = Vec::new();
2850 let sql = expr.build(&mut params, 0);
2851 assert_eq!(sql, "COUNT(\"id\")");
2852 }
2853
2854 #[test]
2855 fn test_aggregates() {
2856 let mut params = Vec::new();
2857
2858 let sum = Expr::col("amount").sum().build(&mut params, 0);
2859 assert_eq!(sum, "SUM(\"amount\")");
2860
2861 let avg = Expr::col("price").avg().build(&mut params, 0);
2862 assert_eq!(avg, "AVG(\"price\")");
2863
2864 let min = Expr::col("age").min().build(&mut params, 0);
2865 assert_eq!(min, "MIN(\"age\")");
2866
2867 let max = Expr::col("score").max().build(&mut params, 0);
2868 assert_eq!(max, "MAX(\"score\")");
2869 }
2870
2871 #[test]
2874 fn test_function() {
2875 let expr = Expr::function("UPPER", vec![Expr::col("name")]);
2876 let mut params = Vec::new();
2877 let sql = expr.build(&mut params, 0);
2878 assert_eq!(sql, "UPPER(\"name\")");
2879 }
2880
2881 #[test]
2882 fn test_function_multiple_args() {
2883 let expr = Expr::function("COALESCE", vec![Expr::col("name"), Expr::lit("Unknown")]);
2884 let mut params = Vec::new();
2885 let sql = expr.build(&mut params, 0);
2886 assert_eq!(sql, "COALESCE(\"name\", $1)");
2887 }
2888
2889 #[test]
2892 fn test_order_asc() {
2893 let order = Expr::col("name").asc();
2894 let mut params = Vec::new();
2895 let sql = order.build(Dialect::Postgres, &mut params, 0);
2896 assert_eq!(sql, "\"name\" ASC");
2897 }
2898
2899 #[test]
2900 fn test_order_desc() {
2901 let order = Expr::col("created_at").desc();
2902 let mut params = Vec::new();
2903 let sql = order.build(Dialect::Postgres, &mut params, 0);
2904 assert_eq!(sql, "\"created_at\" DESC");
2905 }
2906
2907 #[test]
2908 fn test_order_nulls() {
2909 let order_first = Expr::col("name").asc().nulls_first();
2910 let mut params = Vec::new();
2911 let sql = order_first.build(Dialect::Postgres, &mut params, 0);
2912 assert_eq!(sql, "\"name\" ASC NULLS FIRST");
2913
2914 let order_last = Expr::col("name").desc().nulls_last();
2915 let sql = order_last.build(Dialect::Postgres, &mut params, 0);
2916 assert_eq!(sql, "\"name\" DESC NULLS LAST");
2917 }
2918
2919 #[test]
2922 fn test_dialect_postgres() {
2923 let expr = Expr::col("id").eq(1);
2924 let mut params = Vec::new();
2925 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
2926 assert_eq!(sql, "\"id\" = $1");
2927 }
2928
2929 #[test]
2930 fn test_dialect_sqlite() {
2931 let expr = Expr::col("id").eq(1);
2932 let mut params = Vec::new();
2933 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
2934 assert_eq!(sql, "\"id\" = ?1");
2935 }
2936
2937 #[test]
2938 fn test_dialect_mysql() {
2939 let expr = Expr::col("id").eq(1);
2940 let mut params = Vec::new();
2941 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
2942 assert_eq!(sql, "`id` = ?");
2943 }
2944
2945 #[test]
2948 fn test_complex_nested() {
2949 let expr = Expr::col("age")
2951 .gt(18)
2952 .and(Expr::col("status").eq("active"))
2953 .paren()
2954 .or(Expr::col("is_admin").eq(true));
2955
2956 let mut params = Vec::new();
2957 let sql = expr.build(&mut params, 0);
2958 assert_eq!(
2959 sql,
2960 "(\"age\" > $1 AND \"status\" = $2) OR \"is_admin\" = $3"
2961 );
2962 }
2963
2964 #[test]
2965 fn test_parameter_offset() {
2966 let expr = Expr::col("name").eq("test");
2967 let mut params = Vec::new();
2968 let sql = expr.build(&mut params, 5);
2969 assert_eq!(sql, "\"name\" = $6");
2970 }
2971
2972 #[test]
2975 fn test_concat() {
2976 let expr = Expr::col("first_name")
2977 .concat(" ")
2978 .concat(Expr::col("last_name"));
2979 let mut params = Vec::new();
2980 let sql = expr.build(&mut params, 0);
2981 assert_eq!(sql, "\"first_name\" || $1 || \"last_name\"");
2982 }
2983
2984 #[test]
2987 fn test_placeholder() {
2988 let expr = Expr::col("id").eq(Expr::placeholder(1));
2989 let mut params = Vec::new();
2990 let sql = expr.build(&mut params, 0);
2991 assert_eq!(sql, "\"id\" = $1");
2992 assert!(params.is_empty()); }
2994
2995 #[test]
2998 fn test_subquery() {
2999 let expr = Expr::col("dept_id").in_list(vec![Expr::subquery(
3000 "SELECT id FROM departments WHERE active = true",
3001 )]);
3002 let mut params = Vec::new();
3003 let sql = expr.build(&mut params, 0);
3004 assert_eq!(
3005 sql,
3006 "\"dept_id\" IN ((SELECT id FROM departments WHERE active = true))"
3007 );
3008 }
3009
3010 #[test]
3013 fn test_raw() {
3014 let expr = Expr::raw("NOW()");
3015 let mut params = Vec::new();
3016 let sql = expr.build(&mut params, 0);
3017 assert_eq!(sql, "NOW()");
3018 }
3019
3020 #[test]
3023 fn test_precedence() {
3024 assert!(BinaryOp::Mul.precedence() > BinaryOp::Add.precedence());
3025 assert!(BinaryOp::And.precedence() > BinaryOp::Or.precedence());
3026 assert!(BinaryOp::Eq.precedence() > BinaryOp::And.precedence());
3027 }
3028
3029 #[test]
3032 fn test_quote_identifier_escapes_postgres() {
3033 assert_eq!(Dialect::Postgres.quote_identifier("simple"), "\"simple\"");
3035 assert_eq!(
3036 Dialect::Postgres.quote_identifier("with\"quote"),
3037 "\"with\"\"quote\""
3038 );
3039 assert_eq!(
3040 Dialect::Postgres.quote_identifier("multi\"\"quotes"),
3041 "\"multi\"\"\"\"quotes\""
3042 );
3043 }
3044
3045 #[test]
3046 fn test_quote_identifier_escapes_sqlite() {
3047 assert_eq!(Dialect::Sqlite.quote_identifier("simple"), "\"simple\"");
3049 assert_eq!(
3050 Dialect::Sqlite.quote_identifier("with\"quote"),
3051 "\"with\"\"quote\""
3052 );
3053 }
3054
3055 #[test]
3056 fn test_quote_identifier_escapes_mysql() {
3057 assert_eq!(Dialect::Mysql.quote_identifier("simple"), "`simple`");
3059 assert_eq!(
3060 Dialect::Mysql.quote_identifier("with`backtick"),
3061 "`with``backtick`"
3062 );
3063 assert_eq!(
3064 Dialect::Mysql.quote_identifier("multi``ticks"),
3065 "`multi````ticks`"
3066 );
3067 }
3068
3069 #[test]
3072 fn test_window_row_number_empty_over() {
3073 let expr = Expr::row_number().over().build();
3074 let mut params = Vec::new();
3075 let sql = expr.build(&mut params, 0);
3076 assert_eq!(sql, "ROW_NUMBER() OVER ()");
3077 }
3078
3079 #[test]
3080 fn test_window_row_number_order_by() {
3081 let expr = Expr::row_number()
3082 .over()
3083 .order_by_desc(Expr::col("created_at"))
3084 .build();
3085 let mut params = Vec::new();
3086 let sql = expr.build(&mut params, 0);
3087 assert_eq!(sql, "ROW_NUMBER() OVER (ORDER BY \"created_at\" DESC)");
3088 }
3089
3090 #[test]
3091 fn test_window_partition_by() {
3092 let expr = Expr::row_number()
3093 .over()
3094 .partition_by(Expr::col("department"))
3095 .order_by_asc(Expr::col("hire_date"))
3096 .build();
3097 let mut params = Vec::new();
3098 let sql = expr.build(&mut params, 0);
3099 assert_eq!(
3100 sql,
3101 "ROW_NUMBER() OVER (PARTITION BY \"department\" ORDER BY \"hire_date\" ASC)"
3102 );
3103 }
3104
3105 #[test]
3106 fn test_window_multiple_partition_by() {
3107 let expr = Expr::rank()
3108 .over()
3109 .partition_by(Expr::col("region"))
3110 .partition_by(Expr::col("product"))
3111 .order_by_desc(Expr::col("sales"))
3112 .build();
3113 let mut params = Vec::new();
3114 let sql = expr.build(&mut params, 0);
3115 assert_eq!(
3116 sql,
3117 "RANK() OVER (PARTITION BY \"region\", \"product\" ORDER BY \"sales\" DESC)"
3118 );
3119 }
3120
3121 #[test]
3122 fn test_window_dense_rank() {
3123 let expr = Expr::dense_rank()
3124 .over()
3125 .order_by_asc(Expr::col("score"))
3126 .build();
3127 let mut params = Vec::new();
3128 let sql = expr.build(&mut params, 0);
3129 assert_eq!(sql, "DENSE_RANK() OVER (ORDER BY \"score\" ASC)");
3130 }
3131
3132 #[test]
3133 fn test_window_ntile() {
3134 let expr = Expr::ntile(4)
3135 .over()
3136 .order_by_asc(Expr::col("salary"))
3137 .build();
3138 let mut params = Vec::new();
3139 let sql = expr.build(&mut params, 0);
3140 assert_eq!(sql, "NTILE($1) OVER (ORDER BY \"salary\" ASC)");
3141 assert_eq!(params[0], Value::BigInt(4));
3142 }
3143
3144 #[test]
3145 fn test_window_lag() {
3146 let expr = Expr::col("price")
3147 .lag()
3148 .over()
3149 .order_by_asc(Expr::col("date"))
3150 .build();
3151 let mut params = Vec::new();
3152 let sql = expr.build(&mut params, 0);
3153 assert_eq!(sql, "LAG(\"price\") OVER (ORDER BY \"date\" ASC)");
3154 }
3155
3156 #[test]
3157 fn test_window_lag_with_offset() {
3158 let expr = Expr::col("price")
3159 .lag_offset(3)
3160 .over()
3161 .order_by_asc(Expr::col("date"))
3162 .build();
3163 let mut params = Vec::new();
3164 let sql = expr.build(&mut params, 0);
3165 assert_eq!(sql, "LAG(\"price\", $1) OVER (ORDER BY \"date\" ASC)");
3166 assert_eq!(params[0], Value::BigInt(3));
3167 }
3168
3169 #[test]
3170 fn test_window_lead_with_default() {
3171 let expr = Expr::col("price")
3172 .lead_with_default(1, 0)
3173 .over()
3174 .order_by_asc(Expr::col("date"))
3175 .build();
3176 let mut params = Vec::new();
3177 let sql = expr.build(&mut params, 0);
3178 assert_eq!(sql, "LEAD(\"price\", $1, $2) OVER (ORDER BY \"date\" ASC)");
3179 assert_eq!(params[0], Value::BigInt(1));
3180 assert_eq!(params[1], Value::Int(0));
3181 }
3182
3183 #[test]
3184 fn test_window_first_value() {
3185 let expr = Expr::col("salary")
3186 .first_value()
3187 .over()
3188 .partition_by(Expr::col("department"))
3189 .order_by_desc(Expr::col("salary"))
3190 .build();
3191 let mut params = Vec::new();
3192 let sql = expr.build(&mut params, 0);
3193 assert_eq!(
3194 sql,
3195 "FIRST_VALUE(\"salary\") OVER (PARTITION BY \"department\" ORDER BY \"salary\" DESC)"
3196 );
3197 }
3198
3199 #[test]
3200 fn test_window_last_value() {
3201 let expr = Expr::col("amount")
3202 .last_value()
3203 .over()
3204 .order_by_asc(Expr::col("created_at"))
3205 .build();
3206 let mut params = Vec::new();
3207 let sql = expr.build(&mut params, 0);
3208 assert_eq!(
3209 sql,
3210 "LAST_VALUE(\"amount\") OVER (ORDER BY \"created_at\" ASC)"
3211 );
3212 }
3213
3214 #[test]
3215 fn test_window_nth_value() {
3216 let expr = Expr::col("score")
3217 .nth_value(3)
3218 .over()
3219 .order_by_desc(Expr::col("score"))
3220 .build();
3221 let mut params = Vec::new();
3222 let sql = expr.build(&mut params, 0);
3223 assert_eq!(
3224 sql,
3225 "NTH_VALUE(\"score\", $1) OVER (ORDER BY \"score\" DESC)"
3226 );
3227 assert_eq!(params[0], Value::BigInt(3));
3228 }
3229
3230 #[test]
3231 fn test_window_aggregate_sum() {
3232 let expr = Expr::col("amount")
3233 .sum()
3234 .over()
3235 .partition_by(Expr::col("customer_id"))
3236 .build();
3237 let mut params = Vec::new();
3238 let sql = expr.build(&mut params, 0);
3239 assert_eq!(sql, "SUM(\"amount\") OVER (PARTITION BY \"customer_id\")");
3240 }
3241
3242 #[test]
3243 fn test_window_aggregate_avg_running() {
3244 let expr = Expr::col("price")
3245 .avg()
3246 .over()
3247 .order_by_asc(Expr::col("date"))
3248 .rows_between(
3249 WindowFrameBound::UnboundedPreceding,
3250 WindowFrameBound::CurrentRow,
3251 )
3252 .build();
3253 let mut params = Vec::new();
3254 let sql = expr.build(&mut params, 0);
3255 assert_eq!(
3256 sql,
3257 "AVG(\"price\") OVER (ORDER BY \"date\" ASC ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)"
3258 );
3259 }
3260
3261 #[test]
3262 fn test_window_frame_rows_preceding() {
3263 let expr = Expr::col("value")
3264 .sum()
3265 .over()
3266 .order_by_asc(Expr::col("idx"))
3267 .rows_between(WindowFrameBound::Preceding(2), WindowFrameBound::CurrentRow)
3268 .build();
3269 let mut params = Vec::new();
3270 let sql = expr.build(&mut params, 0);
3271 assert_eq!(
3272 sql,
3273 "SUM(\"value\") OVER (ORDER BY \"idx\" ASC ROWS BETWEEN 2 PRECEDING AND CURRENT ROW)"
3274 );
3275 }
3276
3277 #[test]
3278 fn test_window_frame_rows_following() {
3279 let expr = Expr::col("value")
3280 .avg()
3281 .over()
3282 .order_by_asc(Expr::col("idx"))
3283 .rows_between(WindowFrameBound::CurrentRow, WindowFrameBound::Following(3))
3284 .build();
3285 let mut params = Vec::new();
3286 let sql = expr.build(&mut params, 0);
3287 assert_eq!(
3288 sql,
3289 "AVG(\"value\") OVER (ORDER BY \"idx\" ASC ROWS BETWEEN CURRENT ROW AND 3 FOLLOWING)"
3290 );
3291 }
3292
3293 #[test]
3294 fn test_window_frame_range_unbounded() {
3295 let expr = Expr::col("total")
3296 .sum()
3297 .over()
3298 .partition_by(Expr::col("category"))
3299 .order_by_asc(Expr::col("date"))
3300 .range_between(
3301 WindowFrameBound::UnboundedPreceding,
3302 WindowFrameBound::UnboundedFollowing,
3303 )
3304 .build();
3305 let mut params = Vec::new();
3306 let sql = expr.build(&mut params, 0);
3307 assert_eq!(
3308 sql,
3309 "SUM(\"total\") OVER (PARTITION BY \"category\" ORDER BY \"date\" ASC RANGE BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)"
3310 );
3311 }
3312
3313 #[test]
3314 fn test_window_frame_rows_single_bound() {
3315 let expr = Expr::col("value")
3316 .sum()
3317 .over()
3318 .order_by_asc(Expr::col("idx"))
3319 .rows(WindowFrameBound::UnboundedPreceding)
3320 .build();
3321 let mut params = Vec::new();
3322 let sql = expr.build(&mut params, 0);
3323 assert_eq!(
3324 sql,
3325 "SUM(\"value\") OVER (ORDER BY \"idx\" ASC ROWS UNBOUNDED PRECEDING)"
3326 );
3327 }
3328
3329 #[test]
3330 fn test_window_percent_rank() {
3331 let expr = Expr::percent_rank()
3332 .over()
3333 .order_by_asc(Expr::col("score"))
3334 .build();
3335 let mut params = Vec::new();
3336 let sql = expr.build(&mut params, 0);
3337 assert_eq!(sql, "PERCENT_RANK() OVER (ORDER BY \"score\" ASC)");
3338 }
3339
3340 #[test]
3341 fn test_window_cume_dist() {
3342 let expr = Expr::cume_dist()
3343 .over()
3344 .partition_by(Expr::col("group_id"))
3345 .order_by_asc(Expr::col("value"))
3346 .build();
3347 let mut params = Vec::new();
3348 let sql = expr.build(&mut params, 0);
3349 assert_eq!(
3350 sql,
3351 "CUME_DIST() OVER (PARTITION BY \"group_id\" ORDER BY \"value\" ASC)"
3352 );
3353 }
3354
3355 #[test]
3356 fn test_window_frame_groups() {
3357 let expr = Expr::col("amount")
3358 .sum()
3359 .over()
3360 .order_by_asc(Expr::col("group_rank"))
3361 .groups_between(
3362 WindowFrameBound::Preceding(1),
3363 WindowFrameBound::Following(1),
3364 )
3365 .build();
3366 let mut params = Vec::new();
3367 let sql = expr.build(&mut params, 0);
3368 assert_eq!(
3369 sql,
3370 "SUM(\"amount\") OVER (ORDER BY \"group_rank\" ASC GROUPS BETWEEN 1 PRECEDING AND 1 FOLLOWING)"
3371 );
3372 }
3373
3374 #[test]
3377 fn test_exists_basic() {
3378 let expr = Expr::exists(
3380 "SELECT 1 FROM orders WHERE orders.customer_id = customers.id",
3381 vec![],
3382 );
3383 let mut params = Vec::new();
3384 let sql = expr.build(&mut params, 0);
3385 assert_eq!(
3386 sql,
3387 "EXISTS (SELECT 1 FROM orders WHERE orders.customer_id = customers.id)"
3388 );
3389 assert!(params.is_empty());
3390 }
3391
3392 #[test]
3393 fn test_not_exists() {
3394 let expr = Expr::not_exists(
3396 "SELECT 1 FROM orders WHERE orders.customer_id = customers.id",
3397 vec![],
3398 );
3399 let mut params = Vec::new();
3400 let sql = expr.build(&mut params, 0);
3401 assert_eq!(
3402 sql,
3403 "NOT EXISTS (SELECT 1 FROM orders WHERE orders.customer_id = customers.id)"
3404 );
3405 assert!(params.is_empty());
3406 }
3407
3408 #[test]
3409 fn test_exists_with_params() {
3410 let expr = Expr::exists(
3412 "SELECT 1 FROM orders WHERE status = $1",
3413 vec![Value::Text("active".to_string())],
3414 );
3415 let mut params = Vec::new();
3416 let sql = expr.build(&mut params, 0);
3417 assert_eq!(sql, "EXISTS (SELECT 1 FROM orders WHERE status = $1)");
3418 assert_eq!(params.len(), 1);
3419 assert_eq!(params[0], Value::Text("active".to_string()));
3420 }
3421
3422 #[test]
3423 fn test_exists_with_params_offset() {
3424 let expr = Expr::exists(
3426 "SELECT 1 FROM orders WHERE status = $1 AND type = $2",
3427 vec![
3428 Value::Text("active".to_string()),
3429 Value::Text("online".to_string()),
3430 ],
3431 );
3432 let mut params = Vec::new();
3433 let sql = expr.build(&mut params, 3);
3435 assert_eq!(
3436 sql,
3437 "EXISTS (SELECT 1 FROM orders WHERE status = $4 AND type = $5)"
3438 );
3439 assert_eq!(params.len(), 2);
3440 }
3441
3442 #[test]
3443 fn test_exists_in_where_clause() {
3444 let exists_expr = Expr::exists("SELECT 1 FROM orders o WHERE o.customer_id = c.id", vec![]);
3446 let expr = Expr::col("active").eq(true).and(exists_expr);
3447 let mut params = Vec::new();
3448 let sql = expr.build(&mut params, 0);
3449 assert_eq!(
3450 sql,
3451 "\"active\" = $1 AND EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id)"
3452 );
3453 assert_eq!(params[0], Value::Bool(true));
3454 }
3455
3456 #[test]
3457 fn test_exists_sqlite_dialect() {
3458 let expr = Expr::exists(
3459 "SELECT 1 FROM orders WHERE status = ?1",
3460 vec![Value::Text("active".to_string())],
3461 );
3462 let mut params = Vec::new();
3463 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
3464 assert_eq!(sql, "EXISTS (SELECT 1 FROM orders WHERE status = ?1)");
3465 assert_eq!(params.len(), 1);
3466 }
3467
3468 #[test]
3469 fn test_exists_sqlite_with_offset() {
3470 let expr = Expr::exists(
3471 "SELECT 1 FROM orders WHERE status = ?1",
3472 vec![Value::Text("active".to_string())],
3473 );
3474 let mut params = Vec::new();
3475 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 2);
3476 assert_eq!(sql, "EXISTS (SELECT 1 FROM orders WHERE status = ?3)");
3477 assert_eq!(params.len(), 1);
3478 }
3479
3480 #[test]
3481 fn test_exists_mysql_dialect() {
3482 let expr = Expr::exists(
3484 "SELECT 1 FROM orders WHERE status = ?",
3485 vec![Value::Text("active".to_string())],
3486 );
3487 let mut params = Vec::new();
3488 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3489 assert_eq!(sql, "EXISTS (SELECT 1 FROM orders WHERE status = ?)");
3490 assert_eq!(params.len(), 1);
3491 }
3492
3493 #[test]
3494 fn test_not_exists_with_offset() {
3495 let expr = Expr::not_exists(
3496 "SELECT 1 FROM orders WHERE status = $1",
3497 vec![Value::Text("pending".to_string())],
3498 );
3499 let mut params = Vec::new();
3500 let sql = expr.build(&mut params, 5);
3501 assert_eq!(sql, "NOT EXISTS (SELECT 1 FROM orders WHERE status = $6)");
3502 assert_eq!(params.len(), 1);
3503 }
3504
3505 #[test]
3508 fn test_adjust_placeholder_indices_postgres() {
3509 let sql = "SELECT * FROM t WHERE a = $1 AND b = $2";
3510 let adjusted = super::adjust_placeholder_indices(sql, 3, Dialect::Postgres);
3511 assert_eq!(adjusted, "SELECT * FROM t WHERE a = $4 AND b = $5");
3512 }
3513
3514 #[test]
3515 fn test_adjust_placeholder_indices_sqlite() {
3516 let sql = "SELECT * FROM t WHERE a = ?1 AND b = ?2";
3517 let adjusted = super::adjust_placeholder_indices(sql, 3, Dialect::Sqlite);
3518 assert_eq!(adjusted, "SELECT * FROM t WHERE a = ?4 AND b = ?5");
3519 }
3520
3521 #[test]
3522 fn test_adjust_placeholder_indices_zero_offset() {
3523 let sql = "SELECT * FROM t WHERE a = $1";
3524 let adjusted = super::adjust_placeholder_indices(sql, 0, Dialect::Postgres);
3525 assert_eq!(adjusted, sql);
3526 }
3527
3528 #[test]
3529 fn test_adjust_placeholder_indices_mysql() {
3530 let sql = "SELECT * FROM t WHERE a = ? AND b = ?";
3532 let adjusted = super::adjust_placeholder_indices(sql, 3, Dialect::Mysql);
3533 assert_eq!(adjusted, sql);
3534 }
3535
3536 #[test]
3537 fn test_adjust_placeholder_indices_postgres_ignores_quoted_literals() {
3538 let sql = "SELECT '$1' AS s, col FROM t WHERE a = $1 AND note = 'it''s $2'";
3539 let adjusted = super::adjust_placeholder_indices(sql, 3, Dialect::Postgres);
3540 assert_eq!(
3541 adjusted,
3542 "SELECT '$1' AS s, col FROM t WHERE a = $4 AND note = 'it''s $2'"
3543 );
3544 }
3545
3546 #[test]
3547 fn test_adjust_placeholder_indices_sqlite_ignores_quoted_literals() {
3548 let sql = "SELECT '?1' AS s, col FROM t WHERE a = ?1 AND note = 'keep ?2'";
3549 let adjusted = super::adjust_placeholder_indices(sql, 3, Dialect::Sqlite);
3550 assert_eq!(
3551 adjusted,
3552 "SELECT '?1' AS s, col FROM t WHERE a = ?4 AND note = 'keep ?2'"
3553 );
3554 }
3555
3556 #[test]
3559 fn test_json_get_key_postgres() {
3560 let expr = Expr::col("data").json_get("name");
3561 let mut params = Vec::new();
3562 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3563 assert_eq!(sql, "\"data\" -> 'name'");
3564 assert!(params.is_empty());
3565 }
3566
3567 #[test]
3568 fn test_json_get_key_mysql() {
3569 let expr = Expr::col("data").json_get("name");
3570 let mut params = Vec::new();
3571 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3572 assert_eq!(sql, "JSON_EXTRACT(`data`, '$.name')");
3573 assert!(params.is_empty());
3574 }
3575
3576 #[test]
3577 fn test_json_get_key_sqlite() {
3578 let expr = Expr::col("data").json_get("name");
3579 let mut params = Vec::new();
3580 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
3581 assert_eq!(sql, "json_extract(\"data\", '$.name')");
3582 assert!(params.is_empty());
3583 }
3584
3585 #[test]
3586 fn test_json_get_index_postgres() {
3587 let expr = Expr::col("items").json_get_index(0);
3588 let mut params = Vec::new();
3589 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3590 assert_eq!(sql, "\"items\" -> 0");
3591 }
3592
3593 #[test]
3594 fn test_json_get_index_mysql() {
3595 let expr = Expr::col("items").json_get_index(0);
3596 let mut params = Vec::new();
3597 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3598 assert_eq!(sql, "JSON_EXTRACT(`items`, '$[0]')");
3599 }
3600
3601 #[test]
3602 fn test_json_get_text_postgres() {
3603 let expr = Expr::col("data").json_get_text("name");
3604 let mut params = Vec::new();
3605 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3606 assert_eq!(sql, "\"data\" ->> 'name'");
3607 }
3608
3609 #[test]
3610 fn test_json_get_text_mysql() {
3611 let expr = Expr::col("data").json_get_text("name");
3612 let mut params = Vec::new();
3613 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3614 assert_eq!(sql, "JSON_UNQUOTE(JSON_EXTRACT(`data`, '$.name'))");
3615 }
3616
3617 #[test]
3618 fn test_json_path_postgres() {
3619 let expr = Expr::col("data").json_path(&["address", "city"]);
3620 let mut params = Vec::new();
3621 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3622 assert_eq!(sql, "\"data\" #> '{address, city}'");
3623 }
3624
3625 #[test]
3626 fn test_json_path_mysql() {
3627 let expr = Expr::col("data").json_path(&["address", "city"]);
3628 let mut params = Vec::new();
3629 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3630 assert_eq!(sql, "JSON_EXTRACT(`data`, '$.address.city')");
3631 }
3632
3633 #[test]
3634 fn test_json_path_text_postgres() {
3635 let expr = Expr::col("data").json_path_text(&["address", "city"]);
3636 let mut params = Vec::new();
3637 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3638 assert_eq!(sql, "\"data\" #>> '{address, city}'");
3639 }
3640
3641 #[test]
3642 fn test_json_contains_postgres() {
3643 let expr = Expr::col("tags").json_contains(Expr::raw("'[\"rust\"]'"));
3644 let mut params = Vec::new();
3645 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3646 assert_eq!(sql, "\"tags\" @> '[\"rust\"]'");
3647 }
3648
3649 #[test]
3650 fn test_json_contains_mysql() {
3651 let expr = Expr::col("tags").json_contains(Expr::raw("'[\"rust\"]'"));
3652 let mut params = Vec::new();
3653 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3654 assert_eq!(sql, "JSON_CONTAINS(`tags`, '[\"rust\"]')");
3655 }
3656
3657 #[test]
3658 fn test_json_has_key_postgres() {
3659 let expr = Expr::col("data").json_has_key("email");
3660 let mut params = Vec::new();
3661 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3662 assert_eq!(sql, "\"data\" ? 'email'");
3663 }
3664
3665 #[test]
3666 fn test_json_has_key_mysql() {
3667 let expr = Expr::col("data").json_has_key("email");
3668 let mut params = Vec::new();
3669 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3670 assert_eq!(sql, "JSON_CONTAINS_PATH(`data`, 'one', '$.email')");
3671 }
3672
3673 #[test]
3674 fn test_json_has_key_sqlite() {
3675 let expr = Expr::col("data").json_has_key("email");
3676 let mut params = Vec::new();
3677 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
3678 assert_eq!(sql, "json_type(\"data\", '$.email') IS NOT NULL");
3679 }
3680
3681 #[test]
3682 fn test_json_has_any_key_postgres() {
3683 let expr = Expr::col("data").json_has_any_key(&["email", "phone"]);
3684 let mut params = Vec::new();
3685 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3686 assert_eq!(sql, "\"data\" ?| array['email', 'phone']");
3687 }
3688
3689 #[test]
3690 fn test_json_has_all_keys_postgres() {
3691 let expr = Expr::col("data").json_has_all_keys(&["email", "phone"]);
3692 let mut params = Vec::new();
3693 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3694 assert_eq!(sql, "\"data\" ?& array['email', 'phone']");
3695 }
3696
3697 #[test]
3698 fn test_json_array_length_postgres() {
3699 let expr = Expr::col("items").json_array_length();
3700 let mut params = Vec::new();
3701 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3702 assert_eq!(sql, "jsonb_array_length(\"items\")");
3703 }
3704
3705 #[test]
3706 fn test_json_array_length_mysql() {
3707 let expr = Expr::col("items").json_array_length();
3708 let mut params = Vec::new();
3709 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3710 assert_eq!(sql, "JSON_LENGTH(`items`)");
3711 }
3712
3713 #[test]
3714 fn test_json_array_length_sqlite() {
3715 let expr = Expr::col("items").json_array_length();
3716 let mut params = Vec::new();
3717 let sql = expr.build_with_dialect(Dialect::Sqlite, &mut params, 0);
3718 assert_eq!(sql, "json_array_length(\"items\")");
3719 }
3720
3721 #[test]
3722 fn test_json_typeof_postgres() {
3723 let expr = Expr::col("data").json_typeof();
3724 let mut params = Vec::new();
3725 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3726 assert_eq!(sql, "jsonb_typeof(\"data\")");
3727 }
3728
3729 #[test]
3730 fn test_json_typeof_mysql() {
3731 let expr = Expr::col("data").json_typeof();
3732 let mut params = Vec::new();
3733 let sql = expr.build_with_dialect(Dialect::Mysql, &mut params, 0);
3734 assert_eq!(sql, "JSON_TYPE(`data`)");
3735 }
3736
3737 #[test]
3738 fn test_json_chained_extraction() {
3739 let expr = Expr::col("data").json_get("user").json_get_text("name");
3741 let mut params = Vec::new();
3742 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3743 assert_eq!(sql, "\"data\" -> 'user' ->> 'name'");
3746 }
3747
3748 #[test]
3749 fn test_json_in_where_clause() {
3750 let expr = Expr::col("data").json_get_text("status").eq("active");
3752 let mut params = Vec::new();
3753 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3754 assert_eq!(sql, "\"data\" ->> 'status' = $1");
3755 assert_eq!(params.len(), 1);
3756 }
3757
3758 #[test]
3761 fn test_array_contains() {
3762 let expr = Expr::col("tags").array_contains(Expr::col("other_tags"));
3763 let mut params = Vec::new();
3764 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3765 assert_eq!(sql, "\"tags\" @> \"other_tags\"");
3766 }
3767
3768 #[test]
3769 fn test_array_contained_by() {
3770 let expr = Expr::col("tags").array_contained_by(Expr::col("all_tags"));
3771 let mut params = Vec::new();
3772 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3773 assert_eq!(sql, "\"tags\" <@ \"all_tags\"");
3774 }
3775
3776 #[test]
3777 fn test_array_overlap() {
3778 let expr = Expr::col("tags").array_overlap(Expr::col("search_tags"));
3779 let mut params = Vec::new();
3780 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3781 assert_eq!(sql, "\"tags\" && \"search_tags\"");
3782 }
3783
3784 #[test]
3785 fn test_array_any_eq() {
3786 let expr = Expr::col("tags").array_any_eq("admin");
3787 let mut params = Vec::new();
3788 let sql = expr.build_with_dialect(Dialect::Postgres, &mut params, 0);
3789 assert_eq!(sql, "$1 = ANY(\"tags\")");
3790 assert_eq!(params.len(), 1);
3791 }
3792}