1mod display;
10pub use display::quote_ident_if_needed;
11pub mod rebase;
12
13use std::fmt;
14use std::sync::Arc;
15
16use fsqlite_types::{SqliteValue, TypeAffinity};
17
18#[derive(Clone, Copy, PartialEq, Eq, Hash)]
28pub struct Span {
29 pub start: u32,
31 pub end: u32,
33}
34
35impl Span {
36 #[must_use]
38 pub const fn new(start: u32, end: u32) -> Self {
39 Self { start, end }
40 }
41
42 pub const ZERO: Self = Self { start: 0, end: 0 };
44
45 #[must_use]
47 pub const fn merge(self, other: Self) -> Self {
48 let start = if self.start < other.start {
49 self.start
50 } else {
51 other.start
52 };
53 let end = if self.end > other.end {
54 self.end
55 } else {
56 other.end
57 };
58 Self { start, end }
59 }
60
61 #[must_use]
63 pub const fn len(self) -> u32 {
64 self.end - self.start
65 }
66
67 #[must_use]
69 pub const fn is_empty(self) -> bool {
70 self.start == self.end
71 }
72}
73
74impl fmt::Debug for Span {
75 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 write!(f, "{}..{}", self.start, self.end)
77 }
78}
79
80impl fmt::Display for Span {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 write!(f, "{}..{}", self.start, self.end)
83 }
84}
85
86#[derive(Debug, Clone, PartialEq)]
95pub enum Statement {
96 Select(SelectStatement),
98 Insert(InsertStatement),
99 Update(UpdateStatement),
100 Delete(DeleteStatement),
101
102 CreateTable(CreateTableStatement),
104 CreateIndex(CreateIndexStatement),
105 CreateView(CreateViewStatement),
106 CreateTrigger(CreateTriggerStatement),
107 CreateVirtualTable(CreateVirtualTableStatement),
108 Drop(DropStatement),
109 AlterTable(AlterTableStatement),
110
111 Begin(BeginStatement),
113 Commit,
114 Rollback(RollbackStatement),
115 Savepoint(String),
116 Release(String),
117
118 Attach(AttachStatement),
120 Detach(String),
121 Pragma(PragmaStatement),
122 Vacuum(VacuumStatement),
123
124 Reindex(Option<QualifiedName>),
126 Analyze(Option<QualifiedName>),
127 Explain { query_plan: bool, stmt: Box<Self> },
128}
129
130#[derive(Debug, Clone, PartialEq, Eq, Hash)]
136pub struct QualifiedName {
137 pub schema: Option<String>,
139 pub name: String,
141}
142
143impl QualifiedName {
144 #[must_use]
146 pub fn bare(name: impl Into<String>) -> Self {
147 Self {
148 schema: None,
149 name: name.into(),
150 }
151 }
152
153 #[must_use]
155 pub fn qualified(schema: impl Into<String>, name: impl Into<String>) -> Self {
156 Self {
157 schema: Some(schema.into()),
158 name: name.into(),
159 }
160 }
161}
162
163impl fmt::Display for QualifiedName {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 display::write_qualified_name(f, self)
166 }
167}
168
169#[derive(Debug, Clone, PartialEq, Eq)]
177pub struct TypeName {
178 pub name: String,
180 pub arg1: Option<String>,
182 pub arg2: Option<String>,
184}
185
186#[derive(Debug, Clone, PartialEq)]
192pub enum Literal {
193 Integer(i64),
195 Float(f64),
197 String(String),
199 Blob(Vec<u8>),
201 Null,
203 True,
205 False,
207 CurrentTime,
209 CurrentDate,
211 CurrentTimestamp,
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, Hash)]
222pub struct ColumnRef {
223 pub schema: Option<Arc<str>>,
227 pub table: Option<Arc<str>>,
229 pub column: Arc<str>,
231}
232
233impl ColumnRef {
234 #[must_use]
236 pub fn bare(column: impl Into<Arc<str>>) -> Self {
237 Self {
238 schema: None,
239 table: None,
240 column: column.into(),
241 }
242 }
243
244 #[must_use]
246 pub fn qualified(table: impl Into<Arc<str>>, column: impl Into<Arc<str>>) -> Self {
247 Self {
248 schema: None,
249 table: Some(table.into()),
250 column: column.into(),
251 }
252 }
253
254 #[must_use]
259 pub fn schema_qualified(
260 schema: impl Into<Arc<str>>,
261 table: impl Into<Arc<str>>,
262 column: impl Into<Arc<str>>,
263 ) -> Self {
264 Self {
265 schema: Some(schema.into()),
266 table: Some(table.into()),
267 column: column.into(),
268 }
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
278pub enum BinaryOp {
279 Add,
281 Subtract,
282 Multiply,
283 Divide,
284 Modulo,
285
286 Concat,
288
289 Eq,
291 Ne,
292 Lt,
293 Le,
294 Gt,
295 Ge,
296 Is,
297 IsNot,
298
299 And,
301 Or,
302
303 BitAnd,
305 BitOr,
306 ShiftLeft,
307 ShiftRight,
308}
309
310impl fmt::Display for BinaryOp {
311 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
312 f.write_str(match self {
313 Self::Add => "+",
314 Self::Subtract => "-",
315 Self::Multiply => "*",
316 Self::Divide => "/",
317 Self::Modulo => "%",
318 Self::Concat => "||",
319 Self::Eq => "=",
320 Self::Ne => "!=",
321 Self::Lt => "<",
322 Self::Le => "<=",
323 Self::Gt => ">",
324 Self::Ge => ">=",
325 Self::Is => "IS",
326 Self::IsNot => "IS NOT",
327 Self::And => "AND",
328 Self::Or => "OR",
329 Self::BitAnd => "&",
330 Self::BitOr => "|",
331 Self::ShiftLeft => "<<",
332 Self::ShiftRight => ">>",
333 })
334 }
335}
336
337#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
339pub enum UnaryOp {
340 Negate,
342 Plus,
344 BitNot,
346 Not,
348}
349
350impl fmt::Display for UnaryOp {
351 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
352 f.write_str(match self {
353 Self::Negate => "-",
354 Self::Plus => "+",
355 Self::BitNot => "~",
356 Self::Not => "NOT",
357 })
358 }
359}
360
361#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
363pub enum LikeOp {
364 Like,
365 Glob,
366 Match,
367 Regexp,
368}
369
370#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
372pub enum JsonArrow {
373 Arrow,
375 DoubleArrow,
377}
378
379impl JsonArrow {
380 #[must_use]
382 pub const fn sql_function_name(self) -> &'static str {
383 match self {
384 Self::Arrow => "->",
385 Self::DoubleArrow => "->>",
386 }
387 }
388}
389
390#[derive(Debug, Clone)]
402pub enum BoundCollation {
403 Unspecified,
405 Binary,
407 Named(String),
409}
410
411impl BoundCollation {
412 #[must_use]
417 pub fn from_declared_name(name: Option<String>) -> Self {
418 match name {
419 Some(name) if name.eq_ignore_ascii_case("BINARY") => Self::Binary,
420 Some(name) => Self::Named(name),
421 None => Self::Binary,
422 }
423 }
424
425 #[must_use]
430 pub fn as_name(&self) -> Option<&str> {
431 match self {
432 Self::Unspecified => None,
433 Self::Binary => Some("BINARY"),
434 Self::Named(name) => Some(name),
435 }
436 }
437}
438
439impl From<Option<String>> for BoundCollation {
440 fn from(name: Option<String>) -> Self {
441 Self::from_declared_name(name)
442 }
443}
444
445impl PartialEq for BoundCollation {
446 fn eq(&self, other: &Self) -> bool {
447 match (self.as_name(), other.as_name()) {
448 (None, None) => true,
449 (Some(left), Some(right)) => left.eq_ignore_ascii_case(right),
450 (None, Some(_)) | (Some(_), None) => false,
451 }
452 }
453}
454
455impl Eq for BoundCollation {}
456
457#[derive(Debug, Clone)]
462pub enum Expr {
463 Literal(Literal, Span),
465
466 #[doc(hidden)]
472 BoundOuterValue {
473 value: SqliteValue,
475 collation: BoundCollation,
477 affinity: Option<TypeAffinity>,
479 span: Span,
481 },
482
483 Column(ColumnRef, Span),
485
486 BinaryOp {
488 left: Box<Self>,
489 op: BinaryOp,
490 right: Box<Self>,
491 span: Span,
492 },
493
494 UnaryOp {
496 op: UnaryOp,
497 expr: Box<Self>,
498 span: Span,
499 },
500
501 Between {
503 expr: Box<Self>,
504 low: Box<Self>,
505 high: Box<Self>,
506 not: bool,
507 span: Span,
508 },
509
510 In {
512 expr: Box<Self>,
513 set: InSet,
514 not: bool,
515 span: Span,
516 },
517
518 Like {
520 expr: Box<Self>,
521 pattern: Box<Self>,
522 escape: Option<Box<Self>>,
523 op: LikeOp,
524 not: bool,
525 span: Span,
526 },
527
528 Case {
530 operand: Option<Box<Self>>,
531 whens: Vec<(Self, Self)>,
532 else_expr: Option<Box<Self>>,
533 span: Span,
534 },
535
536 Cast {
538 expr: Box<Self>,
539 type_name: TypeName,
540 span: Span,
541 },
542
543 Exists {
545 subquery: Box<SelectStatement>,
546 not: bool,
547 span: Span,
548 },
549
550 Subquery(Box<SelectStatement>, Span),
552
553 FunctionCall {
555 name: String,
556 args: FunctionArgs,
557 distinct: bool,
558 order_by: Vec<OrderingTerm>,
560 filter: Option<Box<Self>>,
561 over: Option<WindowSpec>,
562 span: Span,
563 },
564
565 Collate {
567 expr: Box<Self>,
568 collation: String,
569 span: Span,
570 },
571
572 IsNull {
574 expr: Box<Self>,
575 not: bool,
576 span: Span,
577 },
578
579 Raise {
581 action: RaiseAction,
582 message: Option<String>,
583 span: Span,
584 },
585
586 JsonAccess {
588 expr: Box<Self>,
589 path: Box<Self>,
590 arrow: JsonArrow,
591 span: Span,
592 },
593
594 RowValue(Vec<Self>, Span),
596
597 Placeholder(PlaceholderType, Span),
599}
600
601impl Expr {
602 #[must_use]
604 pub const fn span(&self) -> Span {
605 match self {
606 Self::Literal(_, s)
607 | Self::Column(_, s)
608 | Self::Subquery(_, s)
609 | Self::RowValue(_, s)
610 | Self::Placeholder(_, s) => *s,
611 Self::BoundOuterValue { span, .. }
612 | Self::BinaryOp { span, .. }
613 | Self::UnaryOp { span, .. }
614 | Self::Between { span, .. }
615 | Self::In { span, .. }
616 | Self::Like { span, .. }
617 | Self::Case { span, .. }
618 | Self::Cast { span, .. }
619 | Self::Exists { span, .. }
620 | Self::FunctionCall { span, .. }
621 | Self::Collate { span, .. }
622 | Self::IsNull { span, .. }
623 | Self::Raise { span, .. }
624 | Self::JsonAccess { span, .. } => *span,
625 }
626 }
627}
628
629impl PartialEq for Expr {
645 #[allow(clippy::too_many_lines)]
646 fn eq(&self, other: &Self) -> bool {
647 match (self, other) {
648 (Self::Literal(a, _), Self::Literal(b, _)) => a == b,
649 (
650 Self::BoundOuterValue {
651 value: v1,
652 collation: c1,
653 affinity: a1,
654 ..
655 },
656 Self::BoundOuterValue {
657 value: v2,
658 collation: c2,
659 affinity: a2,
660 ..
661 },
662 ) => v1.storage_class() == v2.storage_class() && v1 == v2 && c1 == c2 && a1 == a2,
663 (Self::Column(a, _), Self::Column(b, _)) => a == b,
664 (
665 Self::BinaryOp {
666 left: l1,
667 op: o1,
668 right: r1,
669 ..
670 },
671 Self::BinaryOp {
672 left: l2,
673 op: o2,
674 right: r2,
675 ..
676 },
677 ) => o1 == o2 && l1 == l2 && r1 == r2,
678 (
679 Self::UnaryOp {
680 op: o1, expr: e1, ..
681 },
682 Self::UnaryOp {
683 op: o2, expr: e2, ..
684 },
685 ) => o1 == o2 && e1 == e2,
686 (
687 Self::Between {
688 expr: e1,
689 low: l1,
690 high: h1,
691 not: n1,
692 ..
693 },
694 Self::Between {
695 expr: e2,
696 low: l2,
697 high: h2,
698 not: n2,
699 ..
700 },
701 ) => n1 == n2 && e1 == e2 && l1 == l2 && h1 == h2,
702 (
703 Self::In {
704 expr: e1,
705 set: s1,
706 not: n1,
707 ..
708 },
709 Self::In {
710 expr: e2,
711 set: s2,
712 not: n2,
713 ..
714 },
715 ) => n1 == n2 && e1 == e2 && s1 == s2,
716 (
717 Self::Like {
718 expr: e1,
719 pattern: p1,
720 escape: esc1,
721 op: o1,
722 not: n1,
723 ..
724 },
725 Self::Like {
726 expr: e2,
727 pattern: p2,
728 escape: esc2,
729 op: o2,
730 not: n2,
731 ..
732 },
733 ) => o1 == o2 && n1 == n2 && e1 == e2 && p1 == p2 && esc1 == esc2,
734 (
735 Self::Case {
736 operand: o1,
737 whens: w1,
738 else_expr: e1,
739 ..
740 },
741 Self::Case {
742 operand: o2,
743 whens: w2,
744 else_expr: e2,
745 ..
746 },
747 ) => o1 == o2 && w1 == w2 && e1 == e2,
748 (
749 Self::Cast {
750 expr: e1,
751 type_name: t1,
752 ..
753 },
754 Self::Cast {
755 expr: e2,
756 type_name: t2,
757 ..
758 },
759 ) => e1 == e2 && t1 == t2,
760 (
761 Self::Exists {
762 subquery: s1,
763 not: n1,
764 ..
765 },
766 Self::Exists {
767 subquery: s2,
768 not: n2,
769 ..
770 },
771 ) => n1 == n2 && s1 == s2,
772 (Self::Subquery(s1, _), Self::Subquery(s2, _)) => s1 == s2,
773 (
774 Self::FunctionCall {
775 name: n1,
776 args: a1,
777 distinct: d1,
778 order_by: ob1,
779 filter: f1,
780 over: ov1,
781 ..
782 },
783 Self::FunctionCall {
784 name: n2,
785 args: a2,
786 distinct: d2,
787 order_by: ob2,
788 filter: f2,
789 over: ov2,
790 ..
791 },
792 ) => {
793 n1.eq_ignore_ascii_case(n2)
794 && a1 == a2
795 && d1 == d2
796 && ob1 == ob2
797 && f1 == f2
798 && ov1 == ov2
799 }
800 (
801 Self::Collate {
802 expr: e1,
803 collation: c1,
804 ..
805 },
806 Self::Collate {
807 expr: e2,
808 collation: c2,
809 ..
810 },
811 ) => e1 == e2 && c1.eq_ignore_ascii_case(c2),
812 (
813 Self::IsNull {
814 expr: e1, not: n1, ..
815 },
816 Self::IsNull {
817 expr: e2, not: n2, ..
818 },
819 ) => n1 == n2 && e1 == e2,
820 (
821 Self::Raise {
822 action: a1,
823 message: m1,
824 ..
825 },
826 Self::Raise {
827 action: a2,
828 message: m2,
829 ..
830 },
831 ) => a1 == a2 && m1 == m2,
832 (
833 Self::JsonAccess {
834 expr: e1,
835 path: p1,
836 arrow: a1,
837 ..
838 },
839 Self::JsonAccess {
840 expr: e2,
841 path: p2,
842 arrow: a2,
843 ..
844 },
845 ) => a1 == a2 && e1 == e2 && p1 == p2,
846 (Self::RowValue(v1, _), Self::RowValue(v2, _)) => v1 == v2,
847 (Self::Placeholder(p1, _), Self::Placeholder(p2, _)) => p1 == p2,
848 _ => false,
849 }
850 }
851}
852
853#[derive(Debug, Clone, PartialEq)]
855pub enum InSet {
856 List(Vec<Expr>),
858 Subquery(Box<SelectStatement>),
860 Table(QualifiedName),
862}
863
864#[derive(Debug, Clone, PartialEq)]
866pub enum FunctionArgs {
867 Star,
869 List(Vec<Expr>),
871}
872
873#[derive(Debug, Clone, Copy, PartialEq)]
878pub enum SqlFunctionArgsRef<'a> {
879 Star,
881 List(&'a [Expr]),
883 Pair(&'a Expr, &'a Expr),
885}
886
887impl<'a> SqlFunctionArgsRef<'a> {
888 #[must_use]
893 pub const fn len(self) -> usize {
894 match self {
895 Self::Star => 0,
896 Self::List(args) => args.len(),
897 Self::Pair(_, _) => 2,
898 }
899 }
900
901 #[must_use]
903 pub const fn is_empty(self) -> bool {
904 self.len() == 0
905 }
906
907 #[must_use]
909 pub fn arity_i32(self) -> i32 {
910 i32::try_from(self.len()).unwrap_or(i32::MAX)
911 }
912
913 #[must_use]
915 pub const fn is_star(self) -> bool {
916 matches!(self, Self::Star)
917 }
918
919 #[must_use]
921 pub fn get(self, index: usize) -> Option<&'a Expr> {
922 match self {
923 Self::List(args) => args.get(index),
924 Self::Pair(first, _) if index == 0 => Some(first),
925 Self::Pair(_, second) if index == 1 => Some(second),
926 Self::Star | Self::Pair(_, _) => None,
927 }
928 }
929
930 #[must_use]
932 pub fn first(self) -> Option<&'a Expr> {
933 self.get(0)
934 }
935
936 pub fn iter(self) -> impl DoubleEndedIterator<Item = &'a Expr> + ExactSizeIterator + Clone {
938 SqlFunctionArgsIter {
939 args: self,
940 indices: 0..self.len(),
941 }
942 }
943
944 #[must_use]
949 pub const fn as_list(self) -> Option<&'a [Expr]> {
950 match self {
951 Self::List(args) => Some(args),
952 Self::Star | Self::Pair(_, _) => None,
953 }
954 }
955
956 #[must_use]
958 pub fn to_owned(self) -> FunctionArgs {
959 match self {
960 Self::Star => FunctionArgs::Star,
961 Self::List(args) => FunctionArgs::List(args.to_vec()),
962 Self::Pair(first, second) => FunctionArgs::List(vec![first.clone(), second.clone()]),
963 }
964 }
965}
966
967#[derive(Debug, Clone)]
968struct SqlFunctionArgsIter<'a> {
969 args: SqlFunctionArgsRef<'a>,
970 indices: std::ops::Range<usize>,
971}
972
973impl<'a> Iterator for SqlFunctionArgsIter<'a> {
974 type Item = &'a Expr;
975
976 fn next(&mut self) -> Option<Self::Item> {
977 self.indices.next().and_then(|index| self.args.get(index))
978 }
979
980 fn size_hint(&self) -> (usize, Option<usize>) {
981 self.indices.size_hint()
982 }
983}
984
985impl DoubleEndedIterator for SqlFunctionArgsIter<'_> {
986 fn next_back(&mut self) -> Option<Self::Item> {
987 self.indices
988 .next_back()
989 .and_then(|index| self.args.get(index))
990 }
991}
992
993impl ExactSizeIterator for SqlFunctionArgsIter<'_> {
994 fn len(&self) -> usize {
995 self.indices.len()
996 }
997}
998
999impl std::iter::FusedIterator for SqlFunctionArgsIter<'_> {}
1000
1001#[derive(Debug, Clone, Copy)]
1007pub struct SqlFunctionCallRef<'a> {
1008 pub name: &'a str,
1010 pub args: SqlFunctionArgsRef<'a>,
1012 pub distinct: bool,
1014 pub order_by: &'a [OrderingTerm],
1016 pub filter: Option<&'a Expr>,
1018 pub over: Option<&'a WindowSpec>,
1020}
1021
1022impl Expr {
1023 #[must_use]
1028 pub fn as_sql_function_call(&self) -> Option<SqlFunctionCallRef<'_>> {
1029 match self {
1030 Self::FunctionCall {
1031 name,
1032 args,
1033 distinct,
1034 order_by,
1035 filter,
1036 over,
1037 ..
1038 } => Some(SqlFunctionCallRef {
1039 name,
1040 args: match args {
1041 FunctionArgs::Star => SqlFunctionArgsRef::Star,
1042 FunctionArgs::List(args) => SqlFunctionArgsRef::List(args),
1043 },
1044 distinct: *distinct,
1045 order_by,
1046 filter: filter.as_deref(),
1047 over: over.as_ref(),
1048 }),
1049 Self::JsonAccess {
1050 expr, path, arrow, ..
1051 } => Some(SqlFunctionCallRef {
1052 name: arrow.sql_function_name(),
1053 args: SqlFunctionArgsRef::Pair(expr, path),
1054 distinct: false,
1055 order_by: &[],
1056 filter: None,
1057 over: None,
1058 }),
1059 _ => None,
1060 }
1061 }
1062}
1063
1064#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1066pub enum PlaceholderType {
1067 Anonymous,
1069 Numbered(u32),
1071 ColonNamed(String),
1073 AtNamed(String),
1075 DollarNamed(String),
1077}
1078
1079#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1081pub enum RaiseAction {
1082 Ignore,
1083 Rollback,
1084 Abort,
1085 Fail,
1086}
1087
1088#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1099pub enum WindowReference {
1100 Direct(String),
1102 Base(String),
1104}
1105
1106impl WindowReference {
1107 #[must_use]
1109 pub fn name(&self) -> &str {
1110 match self {
1111 Self::Direct(name) | Self::Base(name) => name,
1112 }
1113 }
1114}
1115
1116#[derive(Debug, Clone, PartialEq)]
1118pub struct WindowSpec {
1119 pub window_ref: Option<WindowReference>,
1121 pub partition_by: Vec<Expr>,
1123 pub order_by: Vec<OrderingTerm>,
1125 pub frame: Option<FrameSpec>,
1127}
1128
1129#[derive(Debug, Clone, PartialEq)]
1131pub struct FrameSpec {
1132 pub frame_type: FrameType,
1134 pub start: FrameBound,
1136 pub end: Option<FrameBound>,
1138 pub exclude: Option<FrameExclude>,
1140}
1141
1142#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1144pub enum FrameType {
1145 Rows,
1146 Range,
1147 Groups,
1148}
1149
1150#[derive(Debug, Clone, PartialEq)]
1152pub enum FrameBound {
1153 UnboundedPreceding,
1155 Preceding(Box<Expr>),
1157 CurrentRow,
1159 Following(Box<Expr>),
1161 UnboundedFollowing,
1163}
1164
1165#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1167pub enum FrameExclude {
1168 NoOthers,
1169 CurrentRow,
1170 Group,
1171 Ties,
1172}
1173
1174#[derive(Debug, Clone, PartialEq)]
1180pub struct SelectStatement {
1181 pub with: Option<WithClause>,
1183 pub body: SelectBody,
1185 pub order_by: Vec<OrderingTerm>,
1187 pub limit: Option<LimitClause>,
1189}
1190
1191#[derive(Debug, Clone, PartialEq)]
1193pub struct WithClause {
1194 pub recursive: bool,
1196 pub ctes: Vec<Cte>,
1198}
1199
1200#[derive(Debug, Clone, PartialEq)]
1202pub struct Cte {
1203 pub name: String,
1205 pub columns: Vec<String>,
1207 pub materialized: Option<CteMaterialized>,
1209 pub query: SelectStatement,
1211}
1212
1213#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1215pub enum CteMaterialized {
1216 Materialized,
1217 NotMaterialized,
1218}
1219
1220impl WithClause {
1221 pub fn normalize_recursive(&mut self) {
1226 if !self.recursive && self.ctes.iter().any(Cte::is_self_referential) {
1227 self.recursive = true;
1228 }
1229 }
1230}
1231
1232impl Cte {
1233 #[must_use]
1238 pub fn is_self_referential(&self) -> bool {
1239 select_references_table(&self.query, &self.name)
1240 }
1241}
1242
1243fn select_references_table(select: &SelectStatement, name: &str) -> bool {
1244 std::iter::once(&select.body.select)
1245 .chain(select.body.compounds.iter().map(|(_, core)| core))
1246 .any(|core| select_core_references_table(core, name))
1247}
1248
1249fn select_core_references_table(core: &SelectCore, name: &str) -> bool {
1250 match core {
1251 SelectCore::Select {
1252 from: Some(from), ..
1253 } => from_references_table(from, name),
1254 SelectCore::Select { from: None, .. } | SelectCore::Values(_) => false,
1255 }
1256}
1257
1258fn from_references_table(from: &FromClause, name: &str) -> bool {
1259 table_source_references_table(&from.source, name)
1260 || from
1261 .joins
1262 .iter()
1263 .any(|join| table_source_references_table(&join.table, name))
1264}
1265
1266fn table_source_references_table(source: &TableOrSubquery, name: &str) -> bool {
1267 match source {
1268 TableOrSubquery::Table {
1269 name: qualified, ..
1270 } => qualified.schema.is_none() && qualified.name.eq_ignore_ascii_case(name),
1271 TableOrSubquery::ParenJoin(inner) => from_references_table(inner, name),
1272 TableOrSubquery::Subquery { .. } | TableOrSubquery::TableFunction { .. } => false,
1273 }
1274}
1275
1276#[derive(Debug, Clone, PartialEq)]
1278pub struct SelectBody {
1279 pub select: SelectCore,
1281 pub compounds: Vec<(CompoundOp, SelectCore)>,
1283}
1284
1285#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1287pub enum CompoundOp {
1288 Union,
1289 UnionAll,
1290 Intersect,
1291 Except,
1292}
1293
1294#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1302pub enum ValuesRepresentation {
1303 Deferred {
1305 force_union_all_from: Option<usize>,
1308 },
1309 Frozen {
1311 donor_row: Option<usize>,
1314 },
1315}
1316
1317#[derive(Debug, Clone, PartialEq)]
1324pub struct ValuesClause {
1325 rows: Vec<Vec<Expr>>,
1326 representation: ValuesRepresentation,
1327}
1328
1329impl ValuesClause {
1330 #[must_use]
1332 pub fn new(rows: Vec<Vec<Expr>>) -> Self {
1333 Self::parsed(rows, None)
1334 }
1335
1336 #[must_use]
1342 pub fn parsed(rows: Vec<Vec<Expr>>, force_union_all_from: Option<usize>) -> Self {
1343 assert!(
1344 force_union_all_from.is_none_or(|index| index < rows.len()),
1345 "forced VALUES row must be present"
1346 );
1347 Self {
1348 rows,
1349 representation: ValuesRepresentation::Deferred {
1350 force_union_all_from,
1351 },
1352 }
1353 }
1354
1355 #[must_use]
1357 pub const fn representation(&self) -> ValuesRepresentation {
1358 self.representation
1359 }
1360
1361 #[must_use]
1363 pub const fn force_union_all_from(&self) -> Option<usize> {
1364 match self.representation {
1365 ValuesRepresentation::Deferred {
1366 force_union_all_from,
1367 } => force_union_all_from,
1368 ValuesRepresentation::Frozen { .. } => None,
1369 }
1370 }
1371
1372 #[must_use]
1374 pub const fn is_frozen(&self) -> bool {
1375 matches!(self.representation, ValuesRepresentation::Frozen { .. })
1376 }
1377
1378 pub fn freeze_donor_row(&mut self, donor_row: Option<usize>) {
1385 assert!(!self.is_frozen(), "VALUES donor is already frozen");
1386 assert_eq!(
1387 donor_row.is_some(),
1388 !self.rows.is_empty(),
1389 "non-empty VALUES clauses require a donor row"
1390 );
1391 assert!(
1392 donor_row.is_none_or(|index| index < self.rows.len()),
1393 "VALUES donor row must be present"
1394 );
1395 self.representation = ValuesRepresentation::Frozen { donor_row };
1396 }
1397
1398 #[must_use]
1400 pub const fn donor_row_index(&self) -> Option<usize> {
1401 match self.representation {
1402 ValuesRepresentation::Frozen { donor_row } => donor_row,
1403 ValuesRepresentation::Deferred { .. } => None,
1404 }
1405 }
1406
1407 #[must_use]
1409 pub fn donor_row(&self) -> Option<&[Expr]> {
1410 self.donor_row_index()
1411 .and_then(|index| self.rows.get(index))
1412 .map(Vec::as_slice)
1413 }
1414
1415 #[must_use]
1417 pub fn rows(&self) -> &[Vec<Expr>] {
1418 &self.rows
1419 }
1420
1421 pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut [Expr]> + ExactSizeIterator {
1423 self.rows.iter_mut().map(Vec::as_mut_slice)
1424 }
1425
1426 #[must_use]
1428 pub fn into_rows(self) -> Vec<Vec<Expr>> {
1429 self.rows
1430 }
1431
1432 pub fn replace_rows_preserving_representation(&mut self, rows: Vec<Vec<Expr>>) {
1438 assert_eq!(
1439 self.rows.len(),
1440 rows.len(),
1441 "VALUES rewrites must preserve row identity"
1442 );
1443 self.rows = rows;
1444 }
1445}
1446
1447impl Default for ValuesClause {
1448 fn default() -> Self {
1449 Self::new(Vec::new())
1450 }
1451}
1452
1453impl From<Vec<Vec<Expr>>> for ValuesClause {
1454 fn from(rows: Vec<Vec<Expr>>) -> Self {
1455 Self::new(rows)
1456 }
1457}
1458
1459impl std::ops::Deref for ValuesClause {
1460 type Target = [Vec<Expr>];
1461
1462 fn deref(&self) -> &Self::Target {
1463 self.rows()
1464 }
1465}
1466
1467impl<'a> IntoIterator for &'a ValuesClause {
1468 type Item = &'a Vec<Expr>;
1469 type IntoIter = std::slice::Iter<'a, Vec<Expr>>;
1470
1471 fn into_iter(self) -> Self::IntoIter {
1472 self.rows.iter()
1473 }
1474}
1475
1476#[derive(Debug, Clone, PartialEq)]
1478#[allow(clippy::large_enum_variant)]
1479pub enum SelectCore {
1480 Select {
1482 distinct: Distinctness,
1483 columns: Vec<ResultColumn>,
1484 from: Option<FromClause>,
1485 where_clause: Option<Box<Expr>>,
1486 group_by: Vec<Expr>,
1487 having: Option<Box<Expr>>,
1488 windows: Vec<WindowDef>,
1489 },
1490 Values(ValuesClause),
1492}
1493
1494#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1496pub enum Distinctness {
1497 #[default]
1498 All,
1499 Distinct,
1500}
1501
1502#[derive(Debug, Clone, PartialEq)]
1504#[allow(clippy::large_enum_variant)]
1505pub enum ResultColumn {
1506 Star,
1508 TableStar(QualifiedName),
1510 Expr { expr: Expr, alias: Option<String> },
1512}
1513
1514#[derive(Debug, Clone, PartialEq)]
1516pub struct FromClause {
1517 pub source: TableOrSubquery,
1519 pub joins: Vec<JoinClause>,
1521}
1522
1523#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1533pub enum TimeTravelTarget {
1534 CommitSequence(u64),
1536 Timestamp(String),
1538}
1539
1540#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1542pub struct TimeTravelClause {
1543 pub target: TimeTravelTarget,
1544}
1545
1546#[derive(Debug, Clone, PartialEq)]
1548pub enum TableOrSubquery {
1549 Table {
1551 name: QualifiedName,
1552 alias: Option<String>,
1553 index_hint: Option<IndexHint>,
1554 time_travel: Option<TimeTravelClause>,
1555 },
1556 Subquery {
1558 query: Box<SelectStatement>,
1559 alias: Option<String>,
1560 },
1561 TableFunction {
1563 name: String,
1564 args: Vec<Expr>,
1565 alias: Option<String>,
1566 },
1567 ParenJoin(Box<FromClause>),
1569}
1570
1571#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1573pub enum IndexHint {
1574 IndexedBy(String),
1576 NotIndexed,
1578}
1579
1580#[derive(Debug, Clone, PartialEq)]
1582pub struct JoinClause {
1583 pub join_type: JoinType,
1585 pub table: TableOrSubquery,
1587 pub constraint: Option<JoinConstraint>,
1589}
1590
1591#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1593pub struct JoinType {
1594 pub natural: bool,
1596 pub kind: JoinKind,
1598}
1599
1600#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1602pub enum JoinKind {
1603 Cross,
1605 Inner,
1607 Left,
1609 Right,
1611 Full,
1613}
1614
1615#[derive(Debug, Clone, PartialEq)]
1617pub enum JoinConstraint {
1618 On(Expr),
1619 Using(Vec<String>),
1620}
1621
1622#[derive(Debug, Clone, PartialEq)]
1624pub struct WindowDef {
1625 pub name: String,
1627 pub spec: WindowSpec,
1629}
1630
1631#[derive(Debug, Clone, PartialEq)]
1633pub struct OrderingTerm {
1634 pub expr: Expr,
1636 pub direction: Option<SortDirection>,
1638 pub nulls: Option<NullsOrder>,
1640}
1641
1642#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1644pub enum SortDirection {
1645 Asc,
1646 Desc,
1647}
1648
1649#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1651pub enum NullsOrder {
1652 First,
1653 Last,
1654}
1655
1656#[derive(Debug, Clone, PartialEq)]
1658pub struct LimitClause {
1659 pub limit: Expr,
1660 pub offset: Option<Expr>,
1661}
1662
1663#[derive(Debug, Clone, PartialEq)]
1669pub struct InsertStatement {
1670 pub with: Option<WithClause>,
1672 pub or_conflict: Option<ConflictAction>,
1674 pub table: QualifiedName,
1676 pub alias: Option<String>,
1678 pub columns: Vec<String>,
1680 pub source: InsertSource,
1682 pub upsert: Vec<UpsertClause>,
1684 pub returning: Vec<ResultColumn>,
1686}
1687
1688#[derive(Debug, Clone, PartialEq)]
1690pub enum InsertSource {
1691 Values(Vec<Vec<Expr>>),
1693 Select(Box<SelectStatement>),
1695 DefaultValues,
1697}
1698
1699#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1701pub enum ConflictAction {
1702 Rollback,
1703 Abort,
1704 Fail,
1705 Ignore,
1706 Replace,
1707}
1708
1709#[derive(Debug, Clone, PartialEq)]
1711pub struct UpsertClause {
1712 pub target: Option<UpsertTarget>,
1714 pub action: UpsertAction,
1716}
1717
1718#[derive(Debug, Clone, PartialEq)]
1720pub struct UpsertTarget {
1721 pub columns: Vec<IndexedColumn>,
1723 pub where_clause: Option<Expr>,
1725}
1726
1727#[derive(Debug, Clone, PartialEq)]
1729pub enum UpsertAction {
1730 Nothing,
1731 Update {
1732 assignments: Vec<Assignment>,
1733 where_clause: Option<Box<Expr>>,
1734 },
1735}
1736
1737#[derive(Debug, Clone, PartialEq)]
1743pub struct UpdateStatement {
1744 pub with: Option<WithClause>,
1746 pub or_conflict: Option<ConflictAction>,
1748 pub table: QualifiedTableRef,
1750 pub assignments: Vec<Assignment>,
1752 pub from: Option<FromClause>,
1754 pub where_clause: Option<Expr>,
1756 pub returning: Vec<ResultColumn>,
1758 pub order_by: Vec<OrderingTerm>,
1760 pub limit: Option<LimitClause>,
1762}
1763
1764#[derive(Debug, Clone, PartialEq)]
1766pub struct Assignment {
1767 pub target: AssignmentTarget,
1769 pub value: Expr,
1771}
1772
1773#[derive(Debug, Clone, PartialEq, Eq)]
1775pub enum AssignmentTarget {
1776 Column(String),
1778 ColumnList(Vec<String>),
1780}
1781
1782#[derive(Debug, Clone, PartialEq, Eq)]
1784pub struct QualifiedTableRef {
1785 pub name: QualifiedName,
1786 pub alias: Option<String>,
1787 pub index_hint: Option<IndexHint>,
1788 pub time_travel: Option<TimeTravelClause>,
1789}
1790
1791#[derive(Debug, Clone, PartialEq)]
1797pub struct DeleteStatement {
1798 pub with: Option<WithClause>,
1800 pub table: QualifiedTableRef,
1802 pub where_clause: Option<Expr>,
1804 pub returning: Vec<ResultColumn>,
1806 pub order_by: Vec<OrderingTerm>,
1808 pub limit: Option<LimitClause>,
1810}
1811
1812#[derive(Debug, Clone, PartialEq)]
1818#[allow(clippy::struct_excessive_bools)]
1819pub struct CreateTableStatement {
1820 pub if_not_exists: bool,
1822 pub temporary: bool,
1824 pub name: QualifiedName,
1826 pub body: CreateTableBody,
1828 pub without_rowid: bool,
1830 pub strict: bool,
1832}
1833
1834#[derive(Debug, Clone, PartialEq)]
1836pub enum CreateTableBody {
1837 Columns {
1839 columns: Vec<ColumnDef>,
1840 constraints: Vec<TableConstraint>,
1841 },
1842 AsSelect(Box<SelectStatement>),
1844}
1845
1846#[derive(Debug, Clone, PartialEq)]
1848pub struct ColumnDef {
1849 pub name: String,
1851 pub type_name: Option<TypeName>,
1853 pub constraints: Vec<ColumnConstraint>,
1855}
1856
1857#[derive(Debug, Clone, PartialEq)]
1859pub struct ColumnConstraint {
1860 pub name: Option<String>,
1862 pub kind: ColumnConstraintKind,
1864}
1865
1866#[derive(Debug, Clone, PartialEq)]
1868pub enum ColumnConstraintKind {
1869 PrimaryKey {
1870 direction: Option<SortDirection>,
1871 conflict: Option<ConflictAction>,
1872 autoincrement: bool,
1873 },
1874 NotNull {
1875 conflict: Option<ConflictAction>,
1876 },
1877 Null,
1878 Unique {
1879 conflict: Option<ConflictAction>,
1880 },
1881 Check(Expr),
1882 Default(DefaultValue),
1883 Collate(String),
1884 ForeignKey(ForeignKeyClause),
1885 Generated {
1886 expr: Expr,
1887 storage: Option<GeneratedStorage>,
1888 },
1889}
1890
1891#[derive(Debug, Clone, PartialEq)]
1893pub enum DefaultValue {
1894 Expr(Expr),
1895 ParenExpr(Expr),
1897}
1898
1899#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1901pub enum GeneratedStorage {
1902 Stored,
1903 Virtual,
1904}
1905
1906#[derive(Debug, Clone, PartialEq)]
1908pub struct TableConstraint {
1909 pub name: Option<String>,
1911 pub kind: TableConstraintKind,
1913}
1914
1915#[derive(Debug, Clone, PartialEq)]
1917pub enum TableConstraintKind {
1918 PrimaryKey {
1919 columns: Vec<IndexedColumn>,
1920 conflict: Option<ConflictAction>,
1921 },
1922 Unique {
1923 columns: Vec<IndexedColumn>,
1924 conflict: Option<ConflictAction>,
1925 },
1926 Check(Expr),
1927 ForeignKey {
1928 columns: Vec<String>,
1929 clause: ForeignKeyClause,
1930 },
1931}
1932
1933#[derive(Debug, Clone, PartialEq)]
1935pub struct IndexedColumn {
1936 pub expr: Expr,
1938 pub collation: Option<String>,
1940 pub direction: Option<SortDirection>,
1942}
1943
1944#[derive(Debug, Clone, PartialEq, Eq)]
1946pub struct ForeignKeyClause {
1947 pub table: String,
1949 pub columns: Vec<String>,
1951 pub actions: Vec<ForeignKeyAction>,
1953 pub deferrable: Option<Deferrable>,
1955}
1956
1957#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1959pub struct ForeignKeyAction {
1960 pub trigger: ForeignKeyTrigger,
1961 pub action: ForeignKeyActionType,
1962}
1963
1964#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1966pub enum ForeignKeyTrigger {
1967 OnDelete,
1968 OnUpdate,
1969}
1970
1971#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1973pub enum ForeignKeyActionType {
1974 SetNull,
1975 SetDefault,
1976 Cascade,
1977 Restrict,
1978 NoAction,
1979}
1980
1981#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1983pub struct Deferrable {
1984 pub not: bool,
1985 pub initially: Option<DeferrableInitially>,
1986}
1987
1988#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1990pub enum DeferrableInitially {
1991 Deferred,
1992 Immediate,
1993}
1994
1995#[derive(Debug, Clone, PartialEq)]
2001pub struct CreateIndexStatement {
2002 pub unique: bool,
2004 pub if_not_exists: bool,
2006 pub name: QualifiedName,
2008 pub table: String,
2010 pub columns: Vec<IndexedColumn>,
2012 pub where_clause: Option<Expr>,
2014}
2015
2016#[derive(Debug, Clone, PartialEq)]
2022pub struct CreateViewStatement {
2023 pub if_not_exists: bool,
2025 pub temporary: bool,
2027 pub name: QualifiedName,
2029 pub columns: Vec<String>,
2031 pub query: SelectStatement,
2033}
2034
2035#[derive(Debug, Clone, PartialEq)]
2041pub struct CreateTriggerStatement {
2042 pub if_not_exists: bool,
2044 pub temporary: bool,
2046 pub name: QualifiedName,
2048 pub timing: TriggerTiming,
2050 pub event: TriggerEvent,
2052 pub table: String,
2054 pub for_each_row: bool,
2056 pub when: Option<Expr>,
2058 pub body: Vec<Statement>,
2060}
2061
2062#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2064pub enum TriggerTiming {
2065 Before,
2066 After,
2067 InsteadOf,
2068}
2069
2070#[derive(Debug, Clone, PartialEq, Eq)]
2072pub enum TriggerEvent {
2073 Insert,
2074 Delete,
2075 Update(Vec<String>),
2076}
2077
2078#[derive(Debug, Clone, PartialEq, Eq)]
2084pub struct CreateVirtualTableStatement {
2085 pub if_not_exists: bool,
2087 pub name: QualifiedName,
2089 pub module: String,
2091 pub args: Vec<String>,
2093}
2094
2095#[derive(Debug, Clone, PartialEq, Eq)]
2101pub struct DropStatement {
2102 pub object_type: DropObjectType,
2104 pub if_exists: bool,
2106 pub name: QualifiedName,
2108}
2109
2110#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2112pub enum DropObjectType {
2113 Table,
2114 View,
2115 Index,
2116 Trigger,
2117}
2118
2119#[derive(Debug, Clone, PartialEq)]
2125pub struct AlterTableStatement {
2126 pub table: QualifiedName,
2128 pub action: AlterTableAction,
2130}
2131
2132#[derive(Debug, Clone, PartialEq)]
2134pub enum AlterTableAction {
2135 RenameTo(String),
2137 RenameColumn { old: String, new: String },
2139 AddColumn(ColumnDef),
2141 DropColumn(String),
2143}
2144
2145#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2151pub struct BeginStatement {
2152 pub mode: Option<TransactionMode>,
2154}
2155
2156#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2158pub enum TransactionMode {
2159 Deferred,
2160 Immediate,
2161 Exclusive,
2162 Concurrent,
2164}
2165
2166#[derive(Debug, Clone, PartialEq, Eq)]
2168pub struct RollbackStatement {
2169 pub to_savepoint: Option<String>,
2171}
2172
2173#[derive(Debug, Clone, PartialEq)]
2179pub struct AttachStatement {
2180 pub expr: Expr,
2182 pub schema: String,
2184}
2185
2186#[derive(Debug, Clone, PartialEq)]
2192pub struct PragmaStatement {
2193 pub name: QualifiedName,
2195 pub value: Option<PragmaValue>,
2197}
2198
2199#[derive(Debug, Clone, PartialEq)]
2201pub enum PragmaValue {
2202 Assign(Expr),
2204 Call(Expr),
2206}
2207
2208#[derive(Debug, Clone, PartialEq)]
2214pub struct VacuumStatement {
2215 pub schema: Option<String>,
2217 pub into: Option<Expr>,
2219}
2220
2221#[derive(Debug, Clone, PartialEq, Eq)]
2227pub struct ResolvedColumn {
2228 pub table_idx: usize,
2230 pub column_idx: usize,
2232 pub table_name: String,
2234 pub column_name: String,
2236}
2237
2238#[derive(Debug, Clone, PartialEq, Eq)]
2240pub struct TableSchema {
2241 pub name: String,
2243 pub alias: Option<String>,
2245 pub columns: Vec<String>,
2247}
2248
2249impl TableSchema {
2250 #[must_use]
2252 pub fn effective_name(&self) -> &str {
2253 self.alias.as_deref().unwrap_or(&self.name)
2254 }
2255}
2256
2257#[derive(Debug, Clone, PartialEq, Eq)]
2259pub enum ResolveError {
2260 NoSuchTable { name: String, span: Span },
2262 NoSuchColumn {
2264 table: String,
2265 column: String,
2266 span: Span,
2267 },
2268 AmbiguousColumn {
2270 column: String,
2271 candidates: Vec<String>,
2272 span: Span,
2273 },
2274 ColumnNotFound { column: String, span: Span },
2276 NoOuterTable { name: String, span: Span },
2278}
2279
2280impl fmt::Display for ResolveError {
2281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2282 match self {
2283 Self::NoSuchTable { name, span } => {
2284 write!(f, "no such table: {name} at {span}")
2285 }
2286 Self::NoSuchColumn {
2287 table,
2288 column,
2289 span,
2290 } => {
2291 write!(f, "no such column: {table}.{column} at {span}")
2292 }
2293 Self::AmbiguousColumn {
2294 column,
2295 candidates,
2296 span,
2297 } => {
2298 write!(
2299 f,
2300 "ambiguous column name: {column} (candidates: {}) at {span}",
2301 candidates.join(", ")
2302 )
2303 }
2304 Self::ColumnNotFound { column, span } => {
2305 write!(f, "no such column: {column} at {span}")
2306 }
2307 Self::NoOuterTable { name, span } => {
2308 write!(f, "no such table in outer scope: {name} at {span}")
2309 }
2310 }
2311 }
2312}
2313
2314impl std::error::Error for ResolveError {}
2315
2316#[derive(Debug, Clone)]
2318pub struct ResolverScope {
2319 pub tables: Vec<TableSchema>,
2321 pub parent: Option<Box<Self>>,
2323}
2324
2325impl ResolverScope {
2326 #[must_use]
2328 pub fn new(tables: Vec<TableSchema>) -> Self {
2329 Self {
2330 tables,
2331 parent: None,
2332 }
2333 }
2334
2335 #[must_use]
2337 pub fn child(self, tables: Vec<TableSchema>) -> Self {
2338 Self {
2339 tables,
2340 parent: Some(Box::new(self)),
2341 }
2342 }
2343
2344 pub fn resolve(&self, col: &ColumnRef, span: Span) -> Result<ResolvedColumn, ResolveError> {
2350 match &col.table {
2351 Some(table_name) => self.resolve_qualified(table_name, &col.column, span),
2352 None => self.resolve_unqualified(&col.column, span),
2353 }
2354 }
2355
2356 fn resolve_qualified(
2357 &self,
2358 table_name: &str,
2359 column: &str,
2360 span: Span,
2361 ) -> Result<ResolvedColumn, ResolveError> {
2362 for (idx, table) in self.tables.iter().enumerate() {
2363 if table.effective_name().eq_ignore_ascii_case(table_name) {
2364 return match table
2365 .columns
2366 .iter()
2367 .position(|c| c.eq_ignore_ascii_case(column))
2368 {
2369 Some(col_idx) => Ok(ResolvedColumn {
2370 table_idx: idx,
2371 column_idx: col_idx,
2372 table_name: table.effective_name().to_owned(),
2373 column_name: table.columns[col_idx].clone(),
2374 }),
2375 None => Err(ResolveError::NoSuchColumn {
2376 table: table_name.to_owned(),
2377 column: column.to_owned(),
2378 span,
2379 }),
2380 };
2381 }
2382 }
2383
2384 if let Some(ref parent) = self.parent {
2386 return parent.resolve_qualified(table_name, column, span);
2387 }
2388
2389 Err(ResolveError::NoSuchTable {
2390 name: table_name.to_owned(),
2391 span,
2392 })
2393 }
2394
2395 fn resolve_unqualified(
2396 &self,
2397 column: &str,
2398 span: Span,
2399 ) -> Result<ResolvedColumn, ResolveError> {
2400 let mut found: Option<ResolvedColumn> = None;
2401 let mut candidates = Vec::new();
2402
2403 for (idx, table) in self.tables.iter().enumerate() {
2404 if let Some(col_idx) = table
2405 .columns
2406 .iter()
2407 .position(|c| c.eq_ignore_ascii_case(column))
2408 {
2409 candidates.push(table.effective_name().to_owned());
2410 found = Some(ResolvedColumn {
2411 table_idx: idx,
2412 column_idx: col_idx,
2413 table_name: table.effective_name().to_owned(),
2414 column_name: table.columns[col_idx].clone(),
2415 });
2416 }
2417 }
2418
2419 match candidates.len() {
2420 0 => {
2421 if let Some(ref parent) = self.parent {
2423 return parent.resolve_unqualified(column, span);
2424 }
2425 Err(ResolveError::ColumnNotFound {
2426 column: column.to_owned(),
2427 span,
2428 })
2429 }
2430 1 => found.ok_or_else(|| ResolveError::ColumnNotFound {
2431 column: column.to_owned(),
2432 span,
2433 }),
2434 _ => Err(ResolveError::AmbiguousColumn {
2435 column: column.to_owned(),
2436 candidates,
2437 span,
2438 }),
2439 }
2440 }
2441
2442 #[must_use]
2446 pub fn expand_star(&self) -> Vec<(String, String)> {
2447 let mut result = Vec::new();
2448 for table in &self.tables {
2449 for col in &table.columns {
2450 result.push((table.effective_name().to_owned(), col.clone()));
2451 }
2452 }
2453 result
2454 }
2455
2456 pub fn expand_table_star(
2458 &self,
2459 table_name: &str,
2460 span: Span,
2461 ) -> Result<Vec<(String, String)>, ResolveError> {
2462 for table in &self.tables {
2463 if table.effective_name().eq_ignore_ascii_case(table_name) {
2464 return Ok(table
2465 .columns
2466 .iter()
2467 .map(|c| (table.effective_name().to_owned(), c.clone()))
2468 .collect());
2469 }
2470 }
2471 Err(ResolveError::NoSuchTable {
2472 name: table_name.to_owned(),
2473 span,
2474 })
2475 }
2476}
2477
2478#[cfg(test)]
2483mod tests {
2484 use super::*;
2485
2486 #[test]
2489 fn test_ast_statement_variants_dml() {
2490 let _ = Statement::Select(SelectStatement {
2491 with: None,
2492 body: SelectBody {
2493 select: SelectCore::Values(
2494 vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]].into(),
2495 ),
2496 compounds: vec![],
2497 },
2498 order_by: vec![],
2499 limit: None,
2500 });
2501
2502 let _ = Statement::Insert(InsertStatement {
2503 with: None,
2504 or_conflict: None,
2505 table: QualifiedName::bare("t"),
2506 alias: None,
2507 columns: vec![],
2508 source: InsertSource::DefaultValues,
2509 upsert: vec![],
2510 returning: vec![],
2511 });
2512
2513 let table_ref = QualifiedTableRef {
2514 name: QualifiedName::bare("t"),
2515 alias: None,
2516 index_hint: None,
2517 time_travel: None,
2518 };
2519 let _ = Statement::Update(UpdateStatement {
2520 with: None,
2521 or_conflict: None,
2522 table: table_ref.clone(),
2523 assignments: vec![],
2524 from: None,
2525 where_clause: None,
2526 returning: vec![],
2527 order_by: vec![],
2528 limit: None,
2529 });
2530 let _ = Statement::Delete(DeleteStatement {
2531 with: None,
2532 table: table_ref,
2533 where_clause: None,
2534 returning: vec![],
2535 order_by: vec![],
2536 limit: None,
2537 });
2538 }
2539
2540 #[test]
2541 fn test_ast_statement_variants_ddl() {
2542 let _ = Statement::CreateTable(CreateTableStatement {
2543 if_not_exists: false,
2544 temporary: false,
2545 name: QualifiedName::bare("t"),
2546 body: CreateTableBody::Columns {
2547 columns: vec![],
2548 constraints: vec![],
2549 },
2550 without_rowid: false,
2551 strict: false,
2552 });
2553
2554 let _ = Statement::CreateIndex(CreateIndexStatement {
2555 unique: false,
2556 if_not_exists: false,
2557 name: QualifiedName::bare("idx"),
2558 table: "t".to_owned(),
2559 columns: vec![],
2560 where_clause: None,
2561 });
2562
2563 let _ = Statement::CreateView(CreateViewStatement {
2564 if_not_exists: false,
2565 temporary: false,
2566 name: QualifiedName::bare("v"),
2567 columns: vec![],
2568 query: SelectStatement {
2569 with: None,
2570 body: SelectBody {
2571 select: SelectCore::Values(vec![].into()),
2572 compounds: vec![],
2573 },
2574 order_by: vec![],
2575 limit: None,
2576 },
2577 });
2578
2579 let _ = Statement::CreateTrigger(CreateTriggerStatement {
2580 if_not_exists: false,
2581 temporary: false,
2582 name: QualifiedName::bare("tr"),
2583 timing: TriggerTiming::Before,
2584 event: TriggerEvent::Insert,
2585 table: "t".to_owned(),
2586 for_each_row: true,
2587 when: None,
2588 body: vec![],
2589 });
2590
2591 let _ = Statement::CreateVirtualTable(CreateVirtualTableStatement {
2592 if_not_exists: false,
2593 name: QualifiedName::bare("vt"),
2594 module: "fts5".to_owned(),
2595 args: vec!["content".to_owned()],
2596 });
2597
2598 let _ = Statement::Drop(DropStatement {
2599 object_type: DropObjectType::Table,
2600 if_exists: false,
2601 name: QualifiedName::bare("t"),
2602 });
2603
2604 let _ = Statement::AlterTable(AlterTableStatement {
2605 table: QualifiedName::bare("t"),
2606 action: AlterTableAction::RenameTo("t2".to_owned()),
2607 });
2608 }
2609
2610 #[test]
2611 fn test_ast_statement_variants_txn_and_misc() {
2612 let _ = Statement::Begin(BeginStatement { mode: None });
2613 let _ = Statement::Commit;
2614 let _ = Statement::Rollback(RollbackStatement { to_savepoint: None });
2615
2616 let _ = Statement::Savepoint("sp1".to_owned());
2617 let _ = Statement::Release("sp1".to_owned());
2618
2619 let _ = Statement::Attach(AttachStatement {
2620 expr: Expr::Literal(Literal::String("file.db".to_owned()), Span::ZERO),
2621 schema: "aux".to_owned(),
2622 });
2623 let _ = Statement::Detach("aux".to_owned());
2624
2625 let _ = Statement::Pragma(PragmaStatement {
2626 name: QualifiedName::bare("cache_size"),
2627 value: None,
2628 });
2629 let _ = Statement::Vacuum(VacuumStatement {
2630 schema: None,
2631 into: None,
2632 });
2633
2634 let _ = Statement::Reindex(None);
2635 let _ = Statement::Analyze(None);
2636
2637 let _ = Statement::Explain {
2638 query_plan: true,
2639 stmt: Box::new(Statement::Commit),
2640 };
2641 }
2642
2643 #[test]
2644 fn test_ast_select_body_with_compounds() {
2645 let core1 =
2646 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]].into());
2647 let core2 =
2648 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(2), Span::ZERO)]].into());
2649 let core3 =
2650 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(3), Span::ZERO)]].into());
2651
2652 let body = SelectBody {
2653 select: core1,
2654 compounds: vec![(CompoundOp::Union, core2), (CompoundOp::Intersect, core3)],
2655 };
2656
2657 assert_eq!(body.compounds.len(), 2);
2658 assert_eq!(body.compounds[0].0, CompoundOp::Union);
2659 assert_eq!(body.compounds[1].0, CompoundOp::Intersect);
2660 }
2661
2662 #[test]
2663 fn test_ast_values_as_first_class() {
2664 let values = SelectCore::Values(
2665 vec![
2666 vec![
2667 Expr::Literal(Literal::Integer(1), Span::ZERO),
2668 Expr::Literal(Literal::Integer(2), Span::ZERO),
2669 ],
2670 vec![
2671 Expr::Literal(Literal::Integer(3), Span::ZERO),
2672 Expr::Literal(Literal::Integer(4), Span::ZERO),
2673 ],
2674 ]
2675 .into(),
2676 );
2677
2678 assert!(matches!(values, SelectCore::Values(ref rows) if rows.len() == 2));
2679
2680 let select = SelectCore::Select {
2682 distinct: Distinctness::All,
2683 columns: vec![],
2684 from: None,
2685 where_clause: None,
2686 group_by: vec![],
2687 having: None,
2688 windows: vec![],
2689 };
2690 assert!(!matches!(select, SelectCore::Values(_)));
2691 }
2692
2693 #[test]
2694 fn values_clause_preserves_deferred_and_frozen_representation_invariants() {
2695 let rows = vec![
2696 vec![Expr::Literal(Literal::Integer(1), Span::ZERO)],
2697 vec![Expr::Literal(Literal::Integer(2), Span::ZERO)],
2698 ];
2699 let mut values = ValuesClause::parsed(rows.clone(), Some(1));
2700
2701 assert_eq!(values.rows(), rows.as_slice());
2702 assert_eq!(values.force_union_all_from(), Some(1));
2703 assert_eq!(
2704 values.representation(),
2705 ValuesRepresentation::Deferred {
2706 force_union_all_from: Some(1),
2707 }
2708 );
2709 assert!(!values.is_frozen());
2710 assert_eq!(values.donor_row_index(), None);
2711 assert_eq!(values.donor_row(), None);
2712
2713 let first_row = values.iter_mut().next().expect("first row must exist");
2714 first_row[0] = Expr::Literal(Literal::Integer(9), Span::ZERO);
2715 assert_eq!(values[0][0], Expr::Literal(Literal::Integer(9), Span::ZERO));
2716
2717 let replacement = vec![
2718 vec![Expr::Literal(Literal::Integer(3), Span::ZERO)],
2719 vec![Expr::Literal(Literal::Integer(4), Span::ZERO)],
2720 ];
2721 values.replace_rows_preserving_representation(replacement.clone());
2722 values.freeze_donor_row(Some(1));
2723
2724 assert!(values.is_frozen());
2725 assert_eq!(values.force_union_all_from(), None);
2726 assert_eq!(values.donor_row_index(), Some(1));
2727 assert_eq!(values.donor_row(), Some(replacement[1].as_slice()));
2728 assert_eq!(
2729 values.representation(),
2730 ValuesRepresentation::Frozen { donor_row: Some(1) }
2731 );
2732 assert_eq!(values.clone().into_rows(), replacement);
2733 }
2734
2735 #[test]
2736 fn empty_values_clause_can_freeze_without_a_donor() {
2737 let mut values = ValuesClause::default();
2738 values.freeze_donor_row(None);
2739
2740 assert!(values.is_empty());
2741 assert!(values.is_frozen());
2742 assert_eq!(values.donor_row(), None);
2743 assert!(values.into_rows().is_empty());
2744 }
2745
2746 #[test]
2747 #[should_panic(expected = "forced VALUES row must be present")]
2748 fn values_clause_rejects_an_invalid_forced_row() {
2749 let _ = ValuesClause::parsed(
2750 vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]],
2751 Some(1),
2752 );
2753 }
2754
2755 #[test]
2756 #[should_panic(expected = "VALUES donor row must be present")]
2757 fn values_clause_rejects_an_invalid_donor_row() {
2758 let mut values =
2759 ValuesClause::new(vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]]);
2760 values.freeze_donor_row(Some(1));
2761 }
2762
2763 #[test]
2764 #[allow(clippy::too_many_lines)]
2765 fn test_ast_expr_variants_core() {
2766 let span = Span::new(0, 10);
2767 let dummy = || Box::new(Expr::Literal(Literal::Null, span));
2768
2769 let exprs: Vec<Expr> = vec![
2770 Expr::Literal(Literal::Integer(42), span),
2771 Expr::Column(ColumnRef::bare("x"), span),
2772 Expr::BinaryOp {
2773 left: dummy(),
2774 op: BinaryOp::Add,
2775 right: dummy(),
2776 span,
2777 },
2778 Expr::UnaryOp {
2779 op: UnaryOp::Negate,
2780 expr: dummy(),
2781 span,
2782 },
2783 Expr::Between {
2784 expr: dummy(),
2785 low: dummy(),
2786 high: dummy(),
2787 not: false,
2788 span,
2789 },
2790 Expr::In {
2791 expr: dummy(),
2792 set: InSet::List(vec![]),
2793 not: false,
2794 span,
2795 },
2796 Expr::Like {
2797 expr: dummy(),
2798 pattern: dummy(),
2799 escape: None,
2800 op: LikeOp::Like,
2801 not: false,
2802 span,
2803 },
2804 Expr::Case {
2805 operand: None,
2806 whens: vec![],
2807 else_expr: None,
2808 span,
2809 },
2810 Expr::Cast {
2811 expr: dummy(),
2812 type_name: TypeName {
2813 name: "INTEGER".to_owned(),
2814 arg1: None,
2815 arg2: None,
2816 },
2817 span,
2818 },
2819 Expr::Collate {
2820 expr: dummy(),
2821 collation: "NOCASE".to_owned(),
2822 span,
2823 },
2824 Expr::IsNull {
2825 expr: dummy(),
2826 not: false,
2827 span,
2828 },
2829 Expr::JsonAccess {
2830 expr: dummy(),
2831 path: dummy(),
2832 arrow: JsonArrow::Arrow,
2833 span,
2834 },
2835 Expr::RowValue(vec![], span),
2836 Expr::Placeholder(PlaceholderType::Anonymous, span),
2837 ];
2838
2839 for expr in &exprs {
2840 assert_eq!(expr.span(), span);
2841 }
2842 }
2843
2844 #[test]
2845 fn test_ast_expr_variants_subqueries_and_calls() {
2846 let span = Span::new(0, 10);
2847 let dummy = || Box::new(Expr::Literal(Literal::Null, span));
2848
2849 let empty_select = SelectStatement {
2850 with: None,
2851 body: SelectBody {
2852 select: SelectCore::Values(vec![].into()),
2853 compounds: vec![],
2854 },
2855 order_by: vec![],
2856 limit: None,
2857 };
2858
2859 let exprs: Vec<Expr> = vec![
2860 Expr::Exists {
2861 subquery: Box::new(empty_select.clone()),
2862 not: false,
2863 span,
2864 },
2865 Expr::Subquery(Box::new(empty_select), span),
2866 Expr::FunctionCall {
2867 name: "count".to_owned(),
2868 args: FunctionArgs::Star,
2869 distinct: false,
2870 order_by: vec![],
2871 filter: None,
2872 over: None,
2873 span,
2874 },
2875 Expr::Raise {
2876 action: RaiseAction::Abort,
2877 message: Some("error".to_owned()),
2878 span,
2879 },
2880 Expr::UnaryOp {
2882 op: UnaryOp::Negate,
2883 expr: dummy(),
2884 span,
2885 },
2886 ];
2887
2888 for expr in &exprs {
2889 assert_eq!(expr.span(), span);
2890 }
2891 }
2892
2893 #[test]
2894 fn test_ast_function_call_with_window() {
2895 let span = Span::new(0, 30);
2896 let expr = Expr::FunctionCall {
2897 name: "row_number".to_owned(),
2898 args: FunctionArgs::List(vec![]),
2899 distinct: false,
2900 order_by: vec![],
2901 filter: None,
2902 over: Some(WindowSpec {
2903 window_ref: None,
2904 partition_by: vec![Expr::Column(ColumnRef::bare("dept"), span)],
2905 order_by: vec![OrderingTerm {
2906 expr: Expr::Column(ColumnRef::bare("salary"), span),
2907 direction: Some(SortDirection::Desc),
2908 nulls: None,
2909 }],
2910 frame: Some(FrameSpec {
2911 frame_type: FrameType::Rows,
2912 start: FrameBound::UnboundedPreceding,
2913 end: Some(FrameBound::CurrentRow),
2914 exclude: None,
2915 }),
2916 }),
2917 span,
2918 };
2919
2920 assert!(matches!(expr, Expr::FunctionCall { over: Some(_), .. }));
2921 if let Expr::FunctionCall {
2922 over: Some(ref win),
2923 ..
2924 } = expr
2925 {
2926 assert_eq!(win.partition_by.len(), 1);
2927 assert_eq!(win.order_by.len(), 1);
2928 assert!(win.frame.is_some());
2929 }
2930 }
2931
2932 #[test]
2933 fn test_ast_like_with_escape() {
2934 let span = Span::ZERO;
2935 let expr = Expr::Like {
2936 expr: Box::new(Expr::Column(ColumnRef::bare("name"), span)),
2937 pattern: Box::new(Expr::Literal(Literal::String("foo%".to_owned()), span)),
2938 escape: Some(Box::new(Expr::Literal(
2939 Literal::String("\\".to_owned()),
2940 span,
2941 ))),
2942 op: LikeOp::Like,
2943 not: false,
2944 span,
2945 };
2946
2947 assert!(matches!(
2948 expr,
2949 Expr::Like {
2950 escape: Some(_),
2951 ..
2952 }
2953 ));
2954 if let Expr::Like {
2955 escape: Some(ref esc),
2956 ..
2957 } = expr
2958 {
2959 assert!(matches!(esc.as_ref(), Expr::Literal(Literal::String(_), _)));
2960 }
2961 }
2962
2963 #[test]
2964 fn test_ast_json_access_arrow_types() {
2965 let span = Span::ZERO;
2966 let arrow = Expr::JsonAccess {
2967 expr: Box::new(Expr::Column(ColumnRef::bare("data"), span)),
2968 path: Box::new(Expr::Literal(Literal::String("$.name".to_owned()), span)),
2969 arrow: JsonArrow::Arrow,
2970 span,
2971 };
2972 let double_arrow = Expr::JsonAccess {
2973 expr: Box::new(Expr::Column(ColumnRef::bare("data"), span)),
2974 path: Box::new(Expr::Literal(Literal::String("$.name".to_owned()), span)),
2975 arrow: JsonArrow::DoubleArrow,
2976 span,
2977 };
2978
2979 assert!(matches!(
2980 (&arrow, &double_arrow),
2981 (
2982 Expr::JsonAccess {
2983 arrow: JsonArrow::Arrow,
2984 ..
2985 },
2986 Expr::JsonAccess {
2987 arrow: JsonArrow::DoubleArrow,
2988 ..
2989 }
2990 )
2991 ));
2992 }
2993
2994 #[test]
2995 fn test_sql_function_args_ref_preserves_shape_and_order() {
2996 let args = [
2997 Expr::Literal(Literal::Integer(10), Span::ZERO),
2998 Expr::Literal(Literal::Integer(20), Span::ZERO),
2999 Expr::Literal(Literal::Integer(30), Span::ZERO),
3000 ];
3001
3002 let star = SqlFunctionArgsRef::Star;
3003 assert_eq!(star.len(), 0);
3004 assert_eq!(star.arity_i32(), 0);
3005 assert!(star.is_empty());
3006 assert!(star.is_star());
3007 assert_eq!(star.first(), None);
3008 assert_eq!(star.iter().len(), 0);
3009 assert_eq!(star.as_list(), None);
3010 assert_eq!(star.to_owned(), FunctionArgs::Star);
3011
3012 let empty: [Expr; 0] = [];
3013 let empty_list = SqlFunctionArgsRef::List(&empty);
3014 assert!(empty_list.is_empty());
3015 assert!(!empty_list.is_star());
3016 assert_eq!(empty_list.as_list(), Some(empty.as_slice()));
3017 assert_eq!(empty_list.to_owned(), FunctionArgs::List(vec![]));
3018
3019 let list = SqlFunctionArgsRef::List(&args);
3020 assert_eq!(list.len(), 3);
3021 assert_eq!(list.arity_i32(), 3);
3022 assert_eq!(list.first(), Some(&args[0]));
3023 assert_eq!(list.get(2), Some(&args[2]));
3024 assert_eq!(list.get(3), None);
3025 assert_eq!(
3026 list.iter().collect::<Vec<_>>(),
3027 args.iter().collect::<Vec<_>>()
3028 );
3029 assert_eq!(list.as_list(), Some(args.as_slice()));
3030 assert_eq!(list.to_owned(), FunctionArgs::List(args.to_vec()));
3031
3032 let pair = SqlFunctionArgsRef::Pair(&args[0], &args[2]);
3033 assert_eq!(pair.len(), 2);
3034 assert_eq!(pair.arity_i32(), 2);
3035 assert!(!pair.is_star());
3036 assert_eq!(pair.as_list(), None);
3037 assert_eq!(pair.get(0), Some(&args[0]));
3038 assert_eq!(pair.get(1), Some(&args[2]));
3039 assert_eq!(pair.get(2), None);
3040
3041 let mut iter = pair.iter();
3042 assert_eq!(iter.len(), 2);
3043 assert_eq!(iter.next(), Some(&args[0]));
3044 assert_eq!(iter.next_back(), Some(&args[2]));
3045 assert_eq!(iter.next(), None);
3046 assert_eq!(
3047 pair.to_owned(),
3048 FunctionArgs::List(vec![args[0].clone(), args[2].clone()])
3049 );
3050 }
3051
3052 #[test]
3053 fn test_explicit_function_call_has_borrowed_semantic_view() {
3054 let span = Span::new(4, 24);
3055 let expr = Expr::FunctionCall {
3056 name: "total".to_owned(),
3057 args: FunctionArgs::List(vec![Expr::Column(ColumnRef::bare("amount"), span)]),
3058 distinct: true,
3059 order_by: vec![OrderingTerm {
3060 expr: Expr::Column(ColumnRef::bare("sequence"), span),
3061 direction: Some(SortDirection::Desc),
3062 nulls: Some(NullsOrder::Last),
3063 }],
3064 filter: Some(Box::new(Expr::Literal(Literal::Integer(1), span))),
3065 over: Some(WindowSpec {
3066 window_ref: None,
3067 partition_by: vec![],
3068 order_by: vec![],
3069 frame: None,
3070 }),
3071 span,
3072 };
3073
3074 let Some(call) = expr.as_sql_function_call() else {
3075 panic!("explicit function call must have a semantic call view");
3076 };
3077 assert_eq!(call.name, "total");
3078 assert_eq!(call.args.len(), 1);
3079 assert!(!call.args.is_star());
3080 assert!(call.distinct);
3081 assert_eq!(call.order_by.len(), 1);
3082 assert!(matches!(
3083 call.filter,
3084 Some(Expr::Literal(Literal::Integer(1), _))
3085 ));
3086 assert!(call.over.is_some());
3087
3088 let star_expr = Expr::FunctionCall {
3089 name: "count".to_owned(),
3090 args: FunctionArgs::Star,
3091 distinct: false,
3092 order_by: vec![],
3093 filter: None,
3094 over: None,
3095 span,
3096 };
3097 let Some(star_call) = star_expr.as_sql_function_call() else {
3098 panic!("star function call must have a semantic call view");
3099 };
3100 assert!(star_call.args.is_star());
3101 assert_eq!(star_call.args.len(), 0);
3102 }
3103
3104 #[test]
3105 fn test_json_access_has_borrowed_semantic_function_call_view() {
3106 for (arrow, expected_name) in [(JsonArrow::Arrow, "->"), (JsonArrow::DoubleArrow, "->>")] {
3107 assert_eq!(arrow.sql_function_name(), expected_name);
3108
3109 let expr = Expr::JsonAccess {
3110 expr: Box::new(Expr::Column(ColumnRef::bare("document"), Span::ZERO)),
3111 path: Box::new(Expr::Literal(
3112 Literal::String("$.field".to_owned()),
3113 Span::ZERO,
3114 )),
3115 arrow,
3116 span: Span::ZERO,
3117 };
3118
3119 let Some(call) = expr.as_sql_function_call() else {
3120 panic!("JSON access must have a semantic call view");
3121 };
3122 assert_eq!(call.name, expected_name);
3123 assert_eq!(call.args.len(), 2);
3124 assert!(!call.args.is_star());
3125 assert!(!call.distinct);
3126 assert!(call.order_by.is_empty());
3127 assert_eq!(call.filter, None);
3128 assert_eq!(call.over, None);
3129
3130 let SqlFunctionArgsRef::Pair(document, path) = call.args else {
3131 panic!("JSON access must expose its operands as a borrowed pair");
3132 };
3133 assert!(matches!(
3134 document,
3135 Expr::Column(column, _) if column.column.as_ref() == "document"
3136 ));
3137 assert!(matches!(
3138 path,
3139 Expr::Literal(Literal::String(value), _) if value == "$.field"
3140 ));
3141 }
3142
3143 let literal = Expr::Literal(Literal::Integer(1), Span::ZERO);
3144 assert!(literal.as_sql_function_call().is_none());
3145 }
3146
3147 #[test]
3148 fn test_ast_row_value() {
3149 let span = Span::ZERO;
3150 let rv = Expr::RowValue(
3151 vec![
3152 Expr::Column(ColumnRef::bare("a"), span),
3153 Expr::Column(ColumnRef::bare("b"), span),
3154 Expr::Column(ColumnRef::bare("c"), span),
3155 ],
3156 span,
3157 );
3158
3159 assert!(matches!(rv, Expr::RowValue(_, _)));
3160 if let Expr::RowValue(ref elems, _) = rv {
3161 assert_eq!(elems.len(), 3);
3162 }
3163 }
3164
3165 fn make_scope_t1_t2() -> ResolverScope {
3168 ResolverScope::new(vec![
3169 TableSchema {
3170 name: "t1".to_owned(),
3171 alias: None,
3172 columns: vec!["a".to_owned(), "b".to_owned()],
3173 },
3174 TableSchema {
3175 name: "t2".to_owned(),
3176 alias: None,
3177 columns: vec!["c".to_owned(), "d".to_owned()],
3178 },
3179 ])
3180 }
3181
3182 #[test]
3183 fn test_resolve_unambiguous_column() {
3184 let scope = make_scope_t1_t2();
3185 let result = scope
3186 .resolve(&ColumnRef::bare("a"), Span::ZERO)
3187 .expect("should resolve");
3188 assert_eq!(result.table_name, "t1");
3189 assert_eq!(result.column_name, "a");
3190 assert_eq!(result.table_idx, 0);
3191 assert_eq!(result.column_idx, 0);
3192 }
3193
3194 #[test]
3195 fn test_resolve_ambiguous_column_error() {
3196 let scope = ResolverScope::new(vec![
3197 TableSchema {
3198 name: "t1".to_owned(),
3199 alias: None,
3200 columns: vec!["x".to_owned(), "y".to_owned()],
3201 },
3202 TableSchema {
3203 name: "t2".to_owned(),
3204 alias: None,
3205 columns: vec!["x".to_owned(), "z".to_owned()],
3206 },
3207 ]);
3208
3209 let err = scope
3210 .resolve(&ColumnRef::bare("x"), Span::ZERO)
3211 .unwrap_err();
3212 assert!(matches!(err, ResolveError::AmbiguousColumn { .. }));
3213 if let ResolveError::AmbiguousColumn {
3214 column, candidates, ..
3215 } = err
3216 {
3217 assert_eq!(column, "x");
3218 assert_eq!(candidates, vec!["t1", "t2"]);
3219 }
3220 }
3221
3222 #[test]
3223 fn test_resolve_qualified_column() {
3224 let scope = make_scope_t1_t2();
3225
3226 let result = scope
3227 .resolve(&ColumnRef::qualified("t1", "a"), Span::ZERO)
3228 .expect("should resolve");
3229 assert_eq!(result.table_name, "t1");
3230 assert_eq!(result.column_name, "a");
3231
3232 let err = scope
3233 .resolve(&ColumnRef::qualified("t1", "nonexistent"), Span::ZERO)
3234 .unwrap_err();
3235 assert!(matches!(err, ResolveError::NoSuchColumn { .. }));
3236 }
3237
3238 #[test]
3239 fn test_resolve_alias_binding() {
3240 let scope = ResolverScope::new(vec![TableSchema {
3241 name: "users".to_owned(),
3242 alias: Some("u".to_owned()),
3243 columns: vec!["id".to_owned(), "name".to_owned()],
3244 }]);
3245
3246 let result = scope
3247 .resolve(&ColumnRef::qualified("u", "name"), Span::ZERO)
3248 .expect("should resolve via alias");
3249 assert_eq!(result.table_name, "u");
3250 assert_eq!(result.column_name, "name");
3251 }
3252
3253 #[test]
3254 fn test_resolve_star_expansion() {
3255 let scope = make_scope_t1_t2();
3256 let expanded = scope.expand_star();
3257 assert_eq!(
3258 expanded,
3259 vec![
3260 ("t1".to_owned(), "a".to_owned()),
3261 ("t1".to_owned(), "b".to_owned()),
3262 ("t2".to_owned(), "c".to_owned()),
3263 ("t2".to_owned(), "d".to_owned()),
3264 ]
3265 );
3266 }
3267
3268 #[test]
3269 fn test_resolve_qualified_star() {
3270 let scope = make_scope_t1_t2();
3271 let expanded = scope.expand_table_star("t1", Span::ZERO).unwrap();
3272 assert_eq!(
3273 expanded,
3274 vec![
3275 ("t1".to_owned(), "a".to_owned()),
3276 ("t1".to_owned(), "b".to_owned()),
3277 ]
3278 );
3279 }
3280
3281 #[test]
3282 fn test_resolve_subquery_scope() {
3283 let outer = ResolverScope::new(vec![TableSchema {
3286 name: "t1".to_owned(),
3287 alias: None,
3288 columns: vec!["a".to_owned(), "b".to_owned()],
3289 }]);
3290
3291 let inner = outer.child(vec![TableSchema {
3292 name: "t2".to_owned(),
3293 alias: None,
3294 columns: vec!["c".to_owned(), "d".to_owned()],
3295 }]);
3296
3297 let result = inner
3299 .resolve(&ColumnRef::qualified("t2", "c"), Span::ZERO)
3300 .expect("inner table");
3301 assert_eq!(result.table_name, "t2");
3302
3303 let result = inner
3305 .resolve(&ColumnRef::qualified("t1", "a"), Span::ZERO)
3306 .expect("correlated outer reference");
3307 assert_eq!(result.table_name, "t1");
3308 assert_eq!(result.column_name, "a");
3309 }
3310
3311 #[test]
3312 fn test_resolve_scope_shadowing() {
3313 let outer = ResolverScope::new(vec![TableSchema {
3315 name: "t1".to_owned(),
3316 alias: None,
3317 columns: vec!["outer_col".to_owned()],
3318 }]);
3319
3320 let inner = outer.child(vec![TableSchema {
3321 name: "t1".to_owned(),
3322 alias: None,
3323 columns: vec!["inner_col".to_owned()],
3324 }]);
3325
3326 let result = inner
3328 .resolve(&ColumnRef::qualified("t1", "inner_col"), Span::ZERO)
3329 .expect("inner shadows outer");
3330 assert_eq!(result.column_name, "inner_col");
3331
3332 let err = inner
3334 .resolve(&ColumnRef::qualified("t1", "outer_col"), Span::ZERO)
3335 .unwrap_err();
3336 assert!(matches!(err, ResolveError::NoSuchColumn { .. }));
3337 }
3338
3339 #[test]
3340 fn test_resolve_nonexistent_table_error() {
3341 let scope = make_scope_t1_t2();
3342 let err = scope
3343 .resolve(&ColumnRef::qualified("nonexistent", "a"), Span::ZERO)
3344 .unwrap_err();
3345 assert!(matches!(err, ResolveError::NoSuchTable { .. }));
3346 }
3347
3348 #[test]
3349 fn test_resolve_unqualified_column_not_found() {
3350 let scope = make_scope_t1_t2();
3351 let err = scope
3352 .resolve(&ColumnRef::bare("nonexistent"), Span::ZERO)
3353 .unwrap_err();
3354 assert!(matches!(err, ResolveError::ColumnNotFound { .. }));
3355 if let ResolveError::ColumnNotFound { column, .. } = err {
3356 assert_eq!(column, "nonexistent");
3357 }
3358 }
3359
3360 #[test]
3361 fn test_resolve_column_in_order_by() {
3362 let scope = ResolverScope::new(vec![TableSchema {
3364 name: "result".to_owned(),
3365 alias: None,
3366 columns: vec!["total".to_owned()],
3367 }]);
3368
3369 let result = scope
3370 .resolve(&ColumnRef::bare("total"), Span::ZERO)
3371 .expect("order by alias");
3372 assert_eq!(result.column_name, "total");
3373 }
3374
3375 #[test]
3378 fn test_span_merge() {
3379 let a = Span::new(5, 10);
3380 let b = Span::new(15, 20);
3381 let merged = a.merge(b);
3382 assert_eq!(merged.start, 5);
3383 assert_eq!(merged.end, 20);
3384 }
3385
3386 #[test]
3387 fn test_span_len_is_empty() {
3388 let s = Span::new(10, 20);
3389 assert_eq!(s.len(), 10);
3390 assert!(!s.is_empty());
3391
3392 assert!(Span::ZERO.is_empty());
3393 }
3394
3395 #[test]
3398 fn test_qualified_name_display() {
3399 let bare = QualifiedName::bare("users");
3400 assert_eq!(bare.to_string(), "users");
3401
3402 let qual = QualifiedName::qualified("main", "users");
3403 assert_eq!(qual.to_string(), "main.users");
3404
3405 let keyword = QualifiedName::bare("order");
3406 assert_eq!(keyword.to_string(), "\"order\"");
3407
3408 let qualified_keyword = QualifiedName::qualified("main", "group");
3409 assert_eq!(qualified_keyword.to_string(), "main.\"group\"");
3410
3411 let keyword_schema = QualifiedName::qualified("order", "group");
3412 assert_eq!(keyword_schema.to_string(), "\"order\".\"group\"");
3413 }
3414
3415 #[test]
3418 fn test_binary_op_display() {
3419 assert_eq!(BinaryOp::Add.to_string(), "+");
3420 assert_eq!(BinaryOp::Concat.to_string(), "||");
3421 assert_eq!(BinaryOp::And.to_string(), "AND");
3422 assert_eq!(BinaryOp::IsNot.to_string(), "IS NOT");
3423 }
3424
3425 #[test]
3426 fn test_unary_op_display() {
3427 assert_eq!(UnaryOp::Negate.to_string(), "-");
3428 assert_eq!(UnaryOp::Not.to_string(), "NOT");
3429 }
3430
3431 #[test]
3434 fn test_unary_op_display_all_variants() {
3435 assert_eq!(UnaryOp::Plus.to_string(), "+");
3436 assert_eq!(UnaryOp::BitNot.to_string(), "~");
3437 }
3438
3439 #[test]
3440 fn test_binary_op_display_all_variants() {
3441 assert_eq!(BinaryOp::Subtract.to_string(), "-");
3442 assert_eq!(BinaryOp::Multiply.to_string(), "*");
3443 assert_eq!(BinaryOp::Divide.to_string(), "/");
3444 assert_eq!(BinaryOp::Modulo.to_string(), "%");
3445 assert_eq!(BinaryOp::Eq.to_string(), "=");
3446 assert_eq!(BinaryOp::Ne.to_string(), "!=");
3447 assert_eq!(BinaryOp::Lt.to_string(), "<");
3448 assert_eq!(BinaryOp::Le.to_string(), "<=");
3449 assert_eq!(BinaryOp::Gt.to_string(), ">");
3450 assert_eq!(BinaryOp::Ge.to_string(), ">=");
3451 assert_eq!(BinaryOp::Is.to_string(), "IS");
3452 assert_eq!(BinaryOp::Or.to_string(), "OR");
3453 assert_eq!(BinaryOp::BitAnd.to_string(), "&");
3454 assert_eq!(BinaryOp::BitOr.to_string(), "|");
3455 assert_eq!(BinaryOp::ShiftLeft.to_string(), "<<");
3456 assert_eq!(BinaryOp::ShiftRight.to_string(), ">>");
3457 }
3458
3459 #[test]
3460 fn test_span_debug_format() {
3461 let s = Span::new(10, 25);
3462 assert_eq!(format!("{s:?}"), "10..25");
3463 }
3464
3465 #[test]
3466 fn test_span_display_format() {
3467 let s = Span::new(0, 42);
3468 assert_eq!(format!("{s}"), "0..42");
3469 }
3470
3471 #[test]
3472 fn test_span_zero_properties() {
3473 assert_eq!(Span::ZERO.start, 0);
3474 assert_eq!(Span::ZERO.end, 0);
3475 assert_eq!(Span::ZERO.len(), 0);
3476 assert!(Span::ZERO.is_empty());
3477 }
3478
3479 #[test]
3480 fn test_span_merge_overlapping() {
3481 let a = Span::new(5, 15);
3482 let b = Span::new(10, 20);
3483 let merged = a.merge(b);
3484 assert_eq!(merged.start, 5);
3485 assert_eq!(merged.end, 20);
3486 }
3487
3488 #[test]
3489 fn test_span_merge_reversed_order() {
3490 let a = Span::new(20, 30);
3491 let b = Span::new(5, 10);
3492 let merged = a.merge(b);
3493 assert_eq!(merged.start, 5);
3494 assert_eq!(merged.end, 30);
3495 }
3496
3497 #[test]
3498 fn test_table_schema_effective_name_with_alias() {
3499 let schema = TableSchema {
3500 name: "users".to_owned(),
3501 alias: Some("u".to_owned()),
3502 columns: vec!["id".to_owned()],
3503 };
3504 assert_eq!(schema.effective_name(), "u");
3505 }
3506
3507 #[test]
3508 fn test_table_schema_effective_name_without_alias() {
3509 let schema = TableSchema {
3510 name: "users".to_owned(),
3511 alias: None,
3512 columns: vec!["id".to_owned()],
3513 };
3514 assert_eq!(schema.effective_name(), "users");
3515 }
3516
3517 #[test]
3518 fn test_resolve_case_insensitive_table() {
3519 let scope = ResolverScope::new(vec![TableSchema {
3520 name: "Users".to_owned(),
3521 alias: None,
3522 columns: vec!["Id".to_owned(), "Name".to_owned()],
3523 }]);
3524
3525 let result = scope
3527 .resolve(&ColumnRef::qualified("users", "id"), Span::ZERO)
3528 .expect("case-insensitive table match");
3529 assert_eq!(result.table_name, "Users");
3530 assert_eq!(result.column_name, "Id");
3531 }
3532
3533 #[test]
3534 fn test_resolve_case_insensitive_unqualified() {
3535 let scope = ResolverScope::new(vec![TableSchema {
3536 name: "T".to_owned(),
3537 alias: None,
3538 columns: vec!["COL_A".to_owned()],
3539 }]);
3540
3541 let result = scope
3542 .resolve(&ColumnRef::bare("col_a"), Span::ZERO)
3543 .expect("case-insensitive unqualified match");
3544 assert_eq!(result.column_name, "COL_A");
3545 }
3546
3547 #[test]
3548 fn test_expand_table_star_nonexistent() {
3549 let scope = make_scope_t1_t2();
3550 let err = scope
3551 .expand_table_star("nonexistent", Span::ZERO)
3552 .unwrap_err();
3553 assert!(matches!(err, ResolveError::NoSuchTable { .. }));
3554 }
3555
3556 #[test]
3557 fn test_resolve_error_display_no_such_table() {
3558 let err = ResolveError::NoSuchTable {
3559 name: "foo".to_owned(),
3560 span: Span::new(5, 8),
3561 };
3562 assert_eq!(err.to_string(), "no such table: foo at 5..8");
3563 }
3564
3565 #[test]
3566 fn test_resolve_error_display_no_such_column() {
3567 let err = ResolveError::NoSuchColumn {
3568 table: "t1".to_owned(),
3569 column: "bar".to_owned(),
3570 span: Span::new(10, 16),
3571 };
3572 assert_eq!(err.to_string(), "no such column: t1.bar at 10..16");
3573 }
3574
3575 #[test]
3576 fn test_resolve_error_display_ambiguous() {
3577 let err = ResolveError::AmbiguousColumn {
3578 column: "id".to_owned(),
3579 candidates: vec!["users".to_owned(), "orders".to_owned()],
3580 span: Span::ZERO,
3581 };
3582 let msg = err.to_string();
3583 assert!(msg.contains("ambiguous column name: id"));
3584 assert!(msg.contains("users, orders"));
3585 }
3586
3587 #[test]
3588 fn test_resolve_error_display_column_not_found() {
3589 let err = ResolveError::ColumnNotFound {
3590 column: "xyz".to_owned(),
3591 span: Span::new(0, 3),
3592 };
3593 assert_eq!(err.to_string(), "no such column: xyz at 0..3");
3594 }
3595
3596 #[test]
3597 fn test_resolve_error_display_no_outer_table() {
3598 let err = ResolveError::NoOuterTable {
3599 name: "outer_t".to_owned(),
3600 span: Span::new(1, 8),
3601 };
3602 assert_eq!(
3603 err.to_string(),
3604 "no such table in outer scope: outer_t at 1..8"
3605 );
3606 }
3607
3608 #[test]
3609 fn test_resolve_error_is_std_error() {
3610 let err: Box<dyn std::error::Error> = Box::new(ResolveError::ColumnNotFound {
3611 column: "x".to_owned(),
3612 span: Span::ZERO,
3613 });
3614 assert!(!err.to_string().is_empty());
3616 }
3617
3618 #[test]
3619 fn test_resolve_unqualified_from_parent_scope() {
3620 let outer = ResolverScope::new(vec![TableSchema {
3621 name: "outer_t".to_owned(),
3622 alias: None,
3623 columns: vec!["outer_col".to_owned()],
3624 }]);
3625 let inner = outer.child(vec![TableSchema {
3626 name: "inner_t".to_owned(),
3627 alias: None,
3628 columns: vec!["inner_col".to_owned()],
3629 }]);
3630
3631 let result = inner
3633 .resolve(&ColumnRef::bare("outer_col"), Span::ZERO)
3634 .expect("correlated unqualified from parent");
3635 assert_eq!(result.table_name, "outer_t");
3636 assert_eq!(result.column_name, "outer_col");
3637 }
3638
3639 #[test]
3640 fn test_distinctness_default_is_all() {
3641 assert_eq!(Distinctness::default(), Distinctness::All);
3642 }
3643
3644 #[test]
3645 fn test_transaction_mode_concurrent() {
3646 let begin = BeginStatement {
3647 mode: Some(TransactionMode::Concurrent),
3648 };
3649 assert_eq!(begin.mode, Some(TransactionMode::Concurrent));
3650 }
3651
3652 #[test]
3653 fn test_transaction_mode_all_variants() {
3654 let modes = [
3655 TransactionMode::Deferred,
3656 TransactionMode::Immediate,
3657 TransactionMode::Exclusive,
3658 TransactionMode::Concurrent,
3659 ];
3660 for (i, a) in modes.iter().enumerate() {
3662 for (j, b) in modes.iter().enumerate() {
3663 assert_eq!(i == j, a == b, "modes {i} and {j} distinctness");
3664 }
3665 }
3666 }
3667
3668 #[test]
3669 fn test_conflict_action_all_variants() {
3670 let actions = [
3671 ConflictAction::Rollback,
3672 ConflictAction::Abort,
3673 ConflictAction::Fail,
3674 ConflictAction::Ignore,
3675 ConflictAction::Replace,
3676 ];
3677 assert_eq!(actions.len(), 5);
3678 for (i, a) in actions.iter().enumerate() {
3679 for (j, b) in actions.iter().enumerate() {
3680 assert_eq!(i == j, a == b);
3681 }
3682 }
3683 }
3684
3685 #[test]
3686 fn test_compound_op_all_variants() {
3687 let ops = [
3688 CompoundOp::Union,
3689 CompoundOp::UnionAll,
3690 CompoundOp::Intersect,
3691 CompoundOp::Except,
3692 ];
3693 assert_eq!(ops.len(), 4);
3694 assert_ne!(CompoundOp::Union, CompoundOp::UnionAll);
3695 }
3696
3697 #[test]
3698 fn test_drop_object_type_variants() {
3699 let types = [
3700 DropObjectType::Table,
3701 DropObjectType::View,
3702 DropObjectType::Index,
3703 DropObjectType::Trigger,
3704 ];
3705 assert_eq!(types.len(), 4);
3706 assert_ne!(DropObjectType::Table, DropObjectType::View);
3707 }
3708
3709 #[test]
3710 fn test_like_op_variants() {
3711 let ops = [LikeOp::Like, LikeOp::Glob, LikeOp::Match, LikeOp::Regexp];
3712 assert_eq!(ops.len(), 4);
3713 assert_ne!(LikeOp::Like, LikeOp::Glob);
3714 }
3715
3716 #[test]
3717 fn test_placeholder_type_variants() {
3718 let _ = PlaceholderType::Anonymous;
3719 let _ = PlaceholderType::Numbered(1);
3720 let _ = PlaceholderType::ColonNamed("param".to_owned());
3721 let _ = PlaceholderType::AtNamed("param".to_owned());
3722 let _ = PlaceholderType::DollarNamed("param".to_owned());
3723 assert_ne!(PlaceholderType::Anonymous, PlaceholderType::Numbered(1));
3724 assert_ne!(
3726 PlaceholderType::ColonNamed("a".to_owned()),
3727 PlaceholderType::AtNamed("a".to_owned()),
3728 );
3729 }
3730
3731 #[test]
3732 fn test_raise_action_variants() {
3733 let actions = [
3734 RaiseAction::Ignore,
3735 RaiseAction::Rollback,
3736 RaiseAction::Abort,
3737 RaiseAction::Fail,
3738 ];
3739 assert_eq!(actions.len(), 4);
3740 assert_ne!(RaiseAction::Ignore, RaiseAction::Rollback);
3741 }
3742
3743 #[test]
3744 fn test_trigger_timing_variants() {
3745 let timings = [
3746 TriggerTiming::Before,
3747 TriggerTiming::After,
3748 TriggerTiming::InsteadOf,
3749 ];
3750 assert_eq!(timings.len(), 3);
3751 assert_ne!(TriggerTiming::Before, TriggerTiming::After);
3752 }
3753
3754 #[test]
3755 fn test_trigger_event_update_with_columns() {
3756 let ev = TriggerEvent::Update(vec!["col1".to_owned(), "col2".to_owned()]);
3757 assert!(matches!(ev, TriggerEvent::Update(ref cols) if cols.len() == 2));
3758 assert_ne!(TriggerEvent::Insert, TriggerEvent::Delete);
3759 }
3760
3761 #[test]
3762 fn test_frame_type_variants() {
3763 let types = [FrameType::Rows, FrameType::Range, FrameType::Groups];
3764 assert_eq!(types.len(), 3);
3765 assert_ne!(FrameType::Rows, FrameType::Groups);
3766 }
3767
3768 #[test]
3769 fn test_frame_exclude_variants() {
3770 let excludes = [
3771 FrameExclude::NoOthers,
3772 FrameExclude::CurrentRow,
3773 FrameExclude::Group,
3774 FrameExclude::Ties,
3775 ];
3776 assert_eq!(excludes.len(), 4);
3777 }
3778
3779 #[test]
3780 fn test_sort_direction_and_nulls_order() {
3781 assert_ne!(SortDirection::Asc, SortDirection::Desc);
3782 assert_ne!(NullsOrder::First, NullsOrder::Last);
3783 }
3784
3785 #[test]
3786 fn test_generated_storage_variants() {
3787 assert_ne!(GeneratedStorage::Stored, GeneratedStorage::Virtual);
3788 }
3789
3790 #[test]
3791 fn test_cte_materialized_variants() {
3792 assert_ne!(
3793 CteMaterialized::Materialized,
3794 CteMaterialized::NotMaterialized
3795 );
3796 }
3797
3798 #[test]
3799 fn test_in_set_table_variant() {
3800 let set = InSet::Table(QualifiedName::bare("lookup"));
3801 assert!(matches!(set, InSet::Table(ref n) if n.name == "lookup"));
3802 }
3803
3804 #[test]
3805 fn test_function_args_star_vs_list() {
3806 let star = FunctionArgs::Star;
3807 let list = FunctionArgs::List(vec![]);
3808 assert_ne!(star, list);
3809 }
3810
3811 #[test]
3812 fn test_insert_source_default_values() {
3813 let src = InsertSource::DefaultValues;
3814 assert!(matches!(src, InsertSource::DefaultValues));
3815 assert_ne!(InsertSource::DefaultValues, InsertSource::Values(vec![]),);
3816 }
3817
3818 #[test]
3819 fn test_pragma_value_variants() {
3820 let span = Span::ZERO;
3821 let assign = PragmaValue::Assign(Expr::Literal(Literal::Integer(100), span));
3822 let call = PragmaValue::Call(Expr::Literal(Literal::Integer(100), span));
3823 assert_ne!(assign, call);
3824 }
3825
3826 #[test]
3827 fn test_column_ref_constructors() {
3828 let bare = ColumnRef::bare("col");
3829 assert!(bare.table.is_none());
3830 assert_eq!(bare.column.as_ref(), "col");
3831
3832 let qual = ColumnRef::qualified("tbl", "col");
3833 assert_eq!(qual.table.as_deref(), Some("tbl"));
3834 assert_eq!(qual.column.as_ref(), "col");
3835 }
3836
3837 #[test]
3838 fn test_qualified_name_constructors() {
3839 let bare = QualifiedName::bare("t");
3840 assert!(bare.schema.is_none());
3841 assert_eq!(bare.name, "t");
3842
3843 let qual = QualifiedName::qualified("main", "t");
3844 assert_eq!(qual.schema.as_deref(), Some("main"));
3845 assert_eq!(qual.name, "t");
3846 }
3847
3848 #[test]
3849 fn test_deferrable_initially_variants() {
3850 let deferred = Deferrable {
3851 not: false,
3852 initially: Some(DeferrableInitially::Deferred),
3853 };
3854 let immediate = Deferrable {
3855 not: false,
3856 initially: Some(DeferrableInitially::Immediate),
3857 };
3858 assert_ne!(deferred, immediate);
3859
3860 let not_deferrable = Deferrable {
3861 not: true,
3862 initially: None,
3863 };
3864 assert_ne!(deferred, not_deferrable);
3865 }
3866
3867 #[test]
3868 fn test_foreign_key_action_types() {
3869 let types = [
3870 ForeignKeyActionType::SetNull,
3871 ForeignKeyActionType::SetDefault,
3872 ForeignKeyActionType::Cascade,
3873 ForeignKeyActionType::Restrict,
3874 ForeignKeyActionType::NoAction,
3875 ];
3876 assert_eq!(types.len(), 5);
3877 assert_ne!(
3878 ForeignKeyActionType::Cascade,
3879 ForeignKeyActionType::Restrict
3880 );
3881 }
3882
3883 #[test]
3884 fn test_foreign_key_trigger_variants() {
3885 assert_ne!(ForeignKeyTrigger::OnDelete, ForeignKeyTrigger::OnUpdate);
3886 }
3887
3888 #[test]
3889 fn test_index_hint_variants() {
3890 let indexed = IndexHint::IndexedBy("idx_name".to_owned());
3891 let not_indexed = IndexHint::NotIndexed;
3892 assert_ne!(indexed, not_indexed);
3893 }
3894
3895 #[test]
3896 fn test_join_kind_all_variants() {
3897 let kinds = [
3898 JoinKind::Cross,
3899 JoinKind::Inner,
3900 JoinKind::Left,
3901 JoinKind::Right,
3902 JoinKind::Full,
3903 ];
3904 assert_eq!(kinds.len(), 5);
3905 assert_ne!(JoinKind::Left, JoinKind::Right);
3906 }
3907
3908 #[test]
3909 fn test_join_type_natural_flag() {
3910 let natural_inner = JoinType {
3911 natural: true,
3912 kind: JoinKind::Inner,
3913 };
3914 let regular_inner = JoinType {
3915 natural: false,
3916 kind: JoinKind::Inner,
3917 };
3918 assert_ne!(natural_inner, regular_inner);
3919 }
3920
3921 #[test]
3922 fn test_alter_table_all_actions() {
3923 let rename = AlterTableAction::RenameTo("new_name".to_owned());
3924 let rename_col = AlterTableAction::RenameColumn {
3925 old: "old_col".to_owned(),
3926 new: "new_col".to_owned(),
3927 };
3928 let add_col = AlterTableAction::AddColumn(ColumnDef {
3929 name: "new_col".to_owned(),
3930 type_name: Some(TypeName {
3931 name: "INTEGER".to_owned(),
3932 arg1: None,
3933 arg2: None,
3934 }),
3935 constraints: vec![],
3936 });
3937 let drop_col = AlterTableAction::DropColumn("old_col".to_owned());
3938
3939 assert_ne!(rename, rename_col);
3941 assert_ne!(add_col, drop_col);
3942 }
3943
3944 #[test]
3945 #[allow(clippy::approx_constant)]
3946 fn test_literal_all_variants() {
3947 let _ = Literal::Integer(42);
3948 let _ = Literal::Float(3.14);
3949 let _ = Literal::String("hello".to_owned());
3950 let _ = Literal::Blob(vec![0xDE, 0xAD]);
3951 let _ = Literal::Null;
3952 let _ = Literal::True;
3953 let _ = Literal::False;
3954 let _ = Literal::CurrentTime;
3955 let _ = Literal::CurrentDate;
3956 let _ = Literal::CurrentTimestamp;
3957 assert_ne!(Literal::True, Literal::False);
3958 assert_ne!(Literal::CurrentTime, Literal::CurrentDate);
3959 }
3960
3961 #[test]
3962 fn test_json_arrow_variants() {
3963 assert_ne!(JsonArrow::Arrow, JsonArrow::DoubleArrow);
3964 }
3965
3966 #[test]
3967 fn test_upsert_action_nothing_vs_update() {
3968 let nothing = UpsertAction::Nothing;
3969 let update = UpsertAction::Update {
3970 assignments: vec![],
3971 where_clause: None,
3972 };
3973 assert_ne!(nothing, update);
3974 }
3975
3976 #[test]
3977 fn test_assignment_target_variants() {
3978 let single = AssignmentTarget::Column("col".to_owned());
3979 let multi = AssignmentTarget::ColumnList(vec!["a".to_owned(), "b".to_owned()]);
3980 assert_ne!(single, multi);
3981 }
3982
3983 #[test]
3984 fn test_type_name_with_args() {
3985 let simple = TypeName {
3986 name: "INTEGER".to_owned(),
3987 arg1: None,
3988 arg2: None,
3989 };
3990 let varchar = TypeName {
3991 name: "VARCHAR".to_owned(),
3992 arg1: Some("255".to_owned()),
3993 arg2: None,
3994 };
3995 let decimal = TypeName {
3996 name: "DECIMAL".to_owned(),
3997 arg1: Some("10".to_owned()),
3998 arg2: Some("2".to_owned()),
3999 };
4000 assert_ne!(simple, varchar);
4001 assert_ne!(varchar, decimal);
4002 }
4003
4004 #[test]
4005 fn test_frame_bound_variants() {
4006 let span = Span::ZERO;
4007 let _ = FrameBound::UnboundedPreceding;
4008 let _ = FrameBound::Preceding(Box::new(Expr::Literal(Literal::Integer(1), span)));
4009 let _ = FrameBound::CurrentRow;
4010 let _ = FrameBound::Following(Box::new(Expr::Literal(Literal::Integer(1), span)));
4011 let _ = FrameBound::UnboundedFollowing;
4012 assert_ne!(FrameBound::UnboundedPreceding, FrameBound::CurrentRow);
4013 }
4014
4015 #[test]
4016 fn test_result_column_variants() {
4017 let span = Span::ZERO;
4018 let star = ResultColumn::Star;
4019 let table_star = ResultColumn::TableStar(QualifiedName::bare("t1"));
4020 let expr = ResultColumn::Expr {
4021 expr: Expr::Literal(Literal::Integer(1), span),
4022 alias: Some("one".to_owned()),
4023 };
4024 assert_ne!(star, table_star);
4025 assert!(matches!(expr, ResultColumn::Expr { alias: Some(_), .. }));
4026 }
4027
4028 #[test]
4029 fn test_expr_eq_ignores_span() {
4030 let a = Expr::Literal(Literal::Integer(42), Span::new(0, 2));
4033 let b = Expr::Literal(Literal::Integer(42), Span::new(100, 102));
4034 assert_eq!(a, b);
4035
4036 let c = Expr::BinaryOp {
4038 left: Box::new(Expr::Column(ColumnRef::bare("x"), Span::new(0, 1))),
4039 op: BinaryOp::Gt,
4040 right: Box::new(Expr::Literal(Literal::Integer(0), Span::new(4, 5))),
4041 span: Span::new(0, 5),
4042 };
4043 let d = Expr::BinaryOp {
4044 left: Box::new(Expr::Column(ColumnRef::bare("x"), Span::new(50, 51))),
4045 op: BinaryOp::Gt,
4046 right: Box::new(Expr::Literal(Literal::Integer(0), Span::new(55, 56))),
4047 span: Span::new(50, 56),
4048 };
4049 assert_eq!(c, d);
4050
4051 let e = Expr::Literal(Literal::Integer(99), Span::ZERO);
4053 assert_ne!(a, e);
4054 }
4055
4056 #[test]
4057 fn test_bound_outer_value_equality_preserves_storage_and_metadata() {
4058 let bound = |value, collation, affinity, span| Expr::BoundOuterValue {
4059 value,
4060 collation,
4061 affinity,
4062 span,
4063 };
4064
4065 let integer = bound(
4066 SqliteValue::Integer(42),
4067 BoundCollation::Binary,
4068 Some(TypeAffinity::Integer),
4069 Span::new(0, 2),
4070 );
4071 let same_semantics = bound(
4072 SqliteValue::Integer(42),
4073 BoundCollation::Named("binary".to_owned()),
4074 Some(TypeAffinity::Integer),
4075 Span::new(100, 102),
4076 );
4077 assert_eq!(integer.span(), Span::new(0, 2));
4078 assert_eq!(integer, same_semantics);
4079
4080 let real = bound(
4081 SqliteValue::Float(42.0),
4082 BoundCollation::Binary,
4083 Some(TypeAffinity::Integer),
4084 Span::ZERO,
4085 );
4086 assert_ne!(integer, real, "INTEGER and REAL storage must stay distinct");
4087
4088 let nocase = bound(
4089 SqliteValue::Integer(42),
4090 BoundCollation::Named("NOCASE".to_owned()),
4091 Some(TypeAffinity::Integer),
4092 Span::ZERO,
4093 );
4094 assert_ne!(integer, nocase);
4095
4096 let unspecified = bound(
4097 SqliteValue::Integer(42),
4098 BoundCollation::Unspecified,
4099 Some(TypeAffinity::Integer),
4100 Span::ZERO,
4101 );
4102 assert_ne!(integer, unspecified);
4103 assert_eq!(BoundCollation::Unspecified.as_name(), None);
4104 assert_eq!(BoundCollation::Binary.as_name(), Some("BINARY"));
4105
4106 let numeric = bound(
4107 SqliteValue::Integer(42),
4108 BoundCollation::Binary,
4109 Some(TypeAffinity::Numeric),
4110 Span::ZERO,
4111 );
4112 assert_ne!(integer, numeric);
4113 }
4114}