1mod display;
10pub mod rebase;
11
12use std::fmt;
13use std::sync::Arc;
14
15use fsqlite_types::{SqliteValue, TypeAffinity};
16
17#[derive(Clone, Copy, PartialEq, Eq, Hash)]
27pub struct Span {
28 pub start: u32,
30 pub end: u32,
32}
33
34impl Span {
35 #[must_use]
37 pub const fn new(start: u32, end: u32) -> Self {
38 Self { start, end }
39 }
40
41 pub const ZERO: Self = Self { start: 0, end: 0 };
43
44 #[must_use]
46 pub const fn merge(self, other: Self) -> Self {
47 let start = if self.start < other.start {
48 self.start
49 } else {
50 other.start
51 };
52 let end = if self.end > other.end {
53 self.end
54 } else {
55 other.end
56 };
57 Self { start, end }
58 }
59
60 #[must_use]
62 pub const fn len(self) -> u32 {
63 self.end - self.start
64 }
65
66 #[must_use]
68 pub const fn is_empty(self) -> bool {
69 self.start == self.end
70 }
71}
72
73impl fmt::Debug for Span {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75 write!(f, "{}..{}", self.start, self.end)
76 }
77}
78
79impl fmt::Display for Span {
80 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81 write!(f, "{}..{}", self.start, self.end)
82 }
83}
84
85#[derive(Debug, Clone, PartialEq)]
94pub enum Statement {
95 Select(SelectStatement),
97 Insert(InsertStatement),
98 Update(UpdateStatement),
99 Delete(DeleteStatement),
100
101 CreateTable(CreateTableStatement),
103 CreateIndex(CreateIndexStatement),
104 CreateView(CreateViewStatement),
105 CreateTrigger(CreateTriggerStatement),
106 CreateVirtualTable(CreateVirtualTableStatement),
107 Drop(DropStatement),
108 AlterTable(AlterTableStatement),
109
110 Begin(BeginStatement),
112 Commit,
113 Rollback(RollbackStatement),
114 Savepoint(String),
115 Release(String),
116
117 Attach(AttachStatement),
119 Detach(String),
120 Pragma(PragmaStatement),
121 Vacuum(VacuumStatement),
122
123 Reindex(Option<QualifiedName>),
125 Analyze(Option<QualifiedName>),
126 Explain { query_plan: bool, stmt: Box<Self> },
127}
128
129#[derive(Debug, Clone, PartialEq, Eq, Hash)]
135pub struct QualifiedName {
136 pub schema: Option<String>,
138 pub name: String,
140}
141
142impl QualifiedName {
143 #[must_use]
145 pub fn bare(name: impl Into<String>) -> Self {
146 Self {
147 schema: None,
148 name: name.into(),
149 }
150 }
151
152 #[must_use]
154 pub fn qualified(schema: impl Into<String>, name: impl Into<String>) -> Self {
155 Self {
156 schema: Some(schema.into()),
157 name: name.into(),
158 }
159 }
160}
161
162impl fmt::Display for QualifiedName {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 display::write_qualified_name(f, self)
165 }
166}
167
168#[derive(Debug, Clone, PartialEq, Eq)]
176pub struct TypeName {
177 pub name: String,
179 pub arg1: Option<String>,
181 pub arg2: Option<String>,
183}
184
185#[derive(Debug, Clone, PartialEq)]
191pub enum Literal {
192 Integer(i64),
194 Float(f64),
196 String(String),
198 Blob(Vec<u8>),
200 Null,
202 True,
204 False,
206 CurrentTime,
208 CurrentDate,
210 CurrentTimestamp,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Hash)]
220pub struct ColumnRef {
221 pub table: Option<Arc<str>>,
223 pub column: Arc<str>,
225}
226
227impl ColumnRef {
228 #[must_use]
230 pub fn bare(column: impl Into<Arc<str>>) -> Self {
231 Self {
232 table: None,
233 column: column.into(),
234 }
235 }
236
237 #[must_use]
239 pub fn qualified(table: impl Into<Arc<str>>, column: impl Into<Arc<str>>) -> Self {
240 Self {
241 table: Some(table.into()),
242 column: column.into(),
243 }
244 }
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
253pub enum BinaryOp {
254 Add,
256 Subtract,
257 Multiply,
258 Divide,
259 Modulo,
260
261 Concat,
263
264 Eq,
266 Ne,
267 Lt,
268 Le,
269 Gt,
270 Ge,
271 Is,
272 IsNot,
273
274 And,
276 Or,
277
278 BitAnd,
280 BitOr,
281 ShiftLeft,
282 ShiftRight,
283}
284
285impl fmt::Display for BinaryOp {
286 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287 f.write_str(match self {
288 Self::Add => "+",
289 Self::Subtract => "-",
290 Self::Multiply => "*",
291 Self::Divide => "/",
292 Self::Modulo => "%",
293 Self::Concat => "||",
294 Self::Eq => "=",
295 Self::Ne => "!=",
296 Self::Lt => "<",
297 Self::Le => "<=",
298 Self::Gt => ">",
299 Self::Ge => ">=",
300 Self::Is => "IS",
301 Self::IsNot => "IS NOT",
302 Self::And => "AND",
303 Self::Or => "OR",
304 Self::BitAnd => "&",
305 Self::BitOr => "|",
306 Self::ShiftLeft => "<<",
307 Self::ShiftRight => ">>",
308 })
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
314pub enum UnaryOp {
315 Negate,
317 Plus,
319 BitNot,
321 Not,
323}
324
325impl fmt::Display for UnaryOp {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 f.write_str(match self {
328 Self::Negate => "-",
329 Self::Plus => "+",
330 Self::BitNot => "~",
331 Self::Not => "NOT",
332 })
333 }
334}
335
336#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
338pub enum LikeOp {
339 Like,
340 Glob,
341 Match,
342 Regexp,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
347pub enum JsonArrow {
348 Arrow,
350 DoubleArrow,
352}
353
354impl JsonArrow {
355 #[must_use]
357 pub const fn sql_function_name(self) -> &'static str {
358 match self {
359 Self::Arrow => "->",
360 Self::DoubleArrow => "->>",
361 }
362 }
363}
364
365#[derive(Debug, Clone)]
377pub enum BoundCollation {
378 Unspecified,
380 Binary,
382 Named(String),
384}
385
386impl BoundCollation {
387 #[must_use]
392 pub fn from_declared_name(name: Option<String>) -> Self {
393 match name {
394 Some(name) if name.eq_ignore_ascii_case("BINARY") => Self::Binary,
395 Some(name) => Self::Named(name),
396 None => Self::Binary,
397 }
398 }
399
400 #[must_use]
405 pub fn as_name(&self) -> Option<&str> {
406 match self {
407 Self::Unspecified => None,
408 Self::Binary => Some("BINARY"),
409 Self::Named(name) => Some(name),
410 }
411 }
412}
413
414impl From<Option<String>> for BoundCollation {
415 fn from(name: Option<String>) -> Self {
416 Self::from_declared_name(name)
417 }
418}
419
420impl PartialEq for BoundCollation {
421 fn eq(&self, other: &Self) -> bool {
422 match (self.as_name(), other.as_name()) {
423 (None, None) => true,
424 (Some(left), Some(right)) => left.eq_ignore_ascii_case(right),
425 (None, Some(_)) | (Some(_), None) => false,
426 }
427 }
428}
429
430impl Eq for BoundCollation {}
431
432#[derive(Debug, Clone)]
437pub enum Expr {
438 Literal(Literal, Span),
440
441 #[doc(hidden)]
447 BoundOuterValue {
448 value: SqliteValue,
450 collation: BoundCollation,
452 affinity: Option<TypeAffinity>,
454 span: Span,
456 },
457
458 Column(ColumnRef, Span),
460
461 BinaryOp {
463 left: Box<Self>,
464 op: BinaryOp,
465 right: Box<Self>,
466 span: Span,
467 },
468
469 UnaryOp {
471 op: UnaryOp,
472 expr: Box<Self>,
473 span: Span,
474 },
475
476 Between {
478 expr: Box<Self>,
479 low: Box<Self>,
480 high: Box<Self>,
481 not: bool,
482 span: Span,
483 },
484
485 In {
487 expr: Box<Self>,
488 set: InSet,
489 not: bool,
490 span: Span,
491 },
492
493 Like {
495 expr: Box<Self>,
496 pattern: Box<Self>,
497 escape: Option<Box<Self>>,
498 op: LikeOp,
499 not: bool,
500 span: Span,
501 },
502
503 Case {
505 operand: Option<Box<Self>>,
506 whens: Vec<(Self, Self)>,
507 else_expr: Option<Box<Self>>,
508 span: Span,
509 },
510
511 Cast {
513 expr: Box<Self>,
514 type_name: TypeName,
515 span: Span,
516 },
517
518 Exists {
520 subquery: Box<SelectStatement>,
521 not: bool,
522 span: Span,
523 },
524
525 Subquery(Box<SelectStatement>, Span),
527
528 FunctionCall {
530 name: String,
531 args: FunctionArgs,
532 distinct: bool,
533 order_by: Vec<OrderingTerm>,
535 filter: Option<Box<Self>>,
536 over: Option<WindowSpec>,
537 span: Span,
538 },
539
540 Collate {
542 expr: Box<Self>,
543 collation: String,
544 span: Span,
545 },
546
547 IsNull {
549 expr: Box<Self>,
550 not: bool,
551 span: Span,
552 },
553
554 Raise {
556 action: RaiseAction,
557 message: Option<String>,
558 span: Span,
559 },
560
561 JsonAccess {
563 expr: Box<Self>,
564 path: Box<Self>,
565 arrow: JsonArrow,
566 span: Span,
567 },
568
569 RowValue(Vec<Self>, Span),
571
572 Placeholder(PlaceholderType, Span),
574}
575
576impl Expr {
577 #[must_use]
579 pub const fn span(&self) -> Span {
580 match self {
581 Self::Literal(_, s)
582 | Self::Column(_, s)
583 | Self::Subquery(_, s)
584 | Self::RowValue(_, s)
585 | Self::Placeholder(_, s) => *s,
586 Self::BoundOuterValue { span, .. }
587 | Self::BinaryOp { span, .. }
588 | Self::UnaryOp { span, .. }
589 | Self::Between { span, .. }
590 | Self::In { span, .. }
591 | Self::Like { span, .. }
592 | Self::Case { span, .. }
593 | Self::Cast { span, .. }
594 | Self::Exists { span, .. }
595 | Self::FunctionCall { span, .. }
596 | Self::Collate { span, .. }
597 | Self::IsNull { span, .. }
598 | Self::Raise { span, .. }
599 | Self::JsonAccess { span, .. } => *span,
600 }
601 }
602}
603
604impl PartialEq for Expr {
620 #[allow(clippy::too_many_lines)]
621 fn eq(&self, other: &Self) -> bool {
622 match (self, other) {
623 (Self::Literal(a, _), Self::Literal(b, _)) => a == b,
624 (
625 Self::BoundOuterValue {
626 value: v1,
627 collation: c1,
628 affinity: a1,
629 ..
630 },
631 Self::BoundOuterValue {
632 value: v2,
633 collation: c2,
634 affinity: a2,
635 ..
636 },
637 ) => v1.storage_class() == v2.storage_class() && v1 == v2 && c1 == c2 && a1 == a2,
638 (Self::Column(a, _), Self::Column(b, _)) => a == b,
639 (
640 Self::BinaryOp {
641 left: l1,
642 op: o1,
643 right: r1,
644 ..
645 },
646 Self::BinaryOp {
647 left: l2,
648 op: o2,
649 right: r2,
650 ..
651 },
652 ) => o1 == o2 && l1 == l2 && r1 == r2,
653 (
654 Self::UnaryOp {
655 op: o1, expr: e1, ..
656 },
657 Self::UnaryOp {
658 op: o2, expr: e2, ..
659 },
660 ) => o1 == o2 && e1 == e2,
661 (
662 Self::Between {
663 expr: e1,
664 low: l1,
665 high: h1,
666 not: n1,
667 ..
668 },
669 Self::Between {
670 expr: e2,
671 low: l2,
672 high: h2,
673 not: n2,
674 ..
675 },
676 ) => n1 == n2 && e1 == e2 && l1 == l2 && h1 == h2,
677 (
678 Self::In {
679 expr: e1,
680 set: s1,
681 not: n1,
682 ..
683 },
684 Self::In {
685 expr: e2,
686 set: s2,
687 not: n2,
688 ..
689 },
690 ) => n1 == n2 && e1 == e2 && s1 == s2,
691 (
692 Self::Like {
693 expr: e1,
694 pattern: p1,
695 escape: esc1,
696 op: o1,
697 not: n1,
698 ..
699 },
700 Self::Like {
701 expr: e2,
702 pattern: p2,
703 escape: esc2,
704 op: o2,
705 not: n2,
706 ..
707 },
708 ) => o1 == o2 && n1 == n2 && e1 == e2 && p1 == p2 && esc1 == esc2,
709 (
710 Self::Case {
711 operand: o1,
712 whens: w1,
713 else_expr: e1,
714 ..
715 },
716 Self::Case {
717 operand: o2,
718 whens: w2,
719 else_expr: e2,
720 ..
721 },
722 ) => o1 == o2 && w1 == w2 && e1 == e2,
723 (
724 Self::Cast {
725 expr: e1,
726 type_name: t1,
727 ..
728 },
729 Self::Cast {
730 expr: e2,
731 type_name: t2,
732 ..
733 },
734 ) => e1 == e2 && t1 == t2,
735 (
736 Self::Exists {
737 subquery: s1,
738 not: n1,
739 ..
740 },
741 Self::Exists {
742 subquery: s2,
743 not: n2,
744 ..
745 },
746 ) => n1 == n2 && s1 == s2,
747 (Self::Subquery(s1, _), Self::Subquery(s2, _)) => s1 == s2,
748 (
749 Self::FunctionCall {
750 name: n1,
751 args: a1,
752 distinct: d1,
753 order_by: ob1,
754 filter: f1,
755 over: ov1,
756 ..
757 },
758 Self::FunctionCall {
759 name: n2,
760 args: a2,
761 distinct: d2,
762 order_by: ob2,
763 filter: f2,
764 over: ov2,
765 ..
766 },
767 ) => {
768 n1.eq_ignore_ascii_case(n2)
769 && a1 == a2
770 && d1 == d2
771 && ob1 == ob2
772 && f1 == f2
773 && ov1 == ov2
774 }
775 (
776 Self::Collate {
777 expr: e1,
778 collation: c1,
779 ..
780 },
781 Self::Collate {
782 expr: e2,
783 collation: c2,
784 ..
785 },
786 ) => e1 == e2 && c1.eq_ignore_ascii_case(c2),
787 (
788 Self::IsNull {
789 expr: e1, not: n1, ..
790 },
791 Self::IsNull {
792 expr: e2, not: n2, ..
793 },
794 ) => n1 == n2 && e1 == e2,
795 (
796 Self::Raise {
797 action: a1,
798 message: m1,
799 ..
800 },
801 Self::Raise {
802 action: a2,
803 message: m2,
804 ..
805 },
806 ) => a1 == a2 && m1 == m2,
807 (
808 Self::JsonAccess {
809 expr: e1,
810 path: p1,
811 arrow: a1,
812 ..
813 },
814 Self::JsonAccess {
815 expr: e2,
816 path: p2,
817 arrow: a2,
818 ..
819 },
820 ) => a1 == a2 && e1 == e2 && p1 == p2,
821 (Self::RowValue(v1, _), Self::RowValue(v2, _)) => v1 == v2,
822 (Self::Placeholder(p1, _), Self::Placeholder(p2, _)) => p1 == p2,
823 _ => false,
824 }
825 }
826}
827
828#[derive(Debug, Clone, PartialEq)]
830pub enum InSet {
831 List(Vec<Expr>),
833 Subquery(Box<SelectStatement>),
835 Table(QualifiedName),
837}
838
839#[derive(Debug, Clone, PartialEq)]
841pub enum FunctionArgs {
842 Star,
844 List(Vec<Expr>),
846}
847
848#[derive(Debug, Clone, Copy, PartialEq)]
853pub enum SqlFunctionArgsRef<'a> {
854 Star,
856 List(&'a [Expr]),
858 Pair(&'a Expr, &'a Expr),
860}
861
862impl<'a> SqlFunctionArgsRef<'a> {
863 #[must_use]
868 pub const fn len(self) -> usize {
869 match self {
870 Self::Star => 0,
871 Self::List(args) => args.len(),
872 Self::Pair(_, _) => 2,
873 }
874 }
875
876 #[must_use]
878 pub const fn is_empty(self) -> bool {
879 self.len() == 0
880 }
881
882 #[must_use]
884 pub fn arity_i32(self) -> i32 {
885 i32::try_from(self.len()).unwrap_or(i32::MAX)
886 }
887
888 #[must_use]
890 pub const fn is_star(self) -> bool {
891 matches!(self, Self::Star)
892 }
893
894 #[must_use]
896 pub fn get(self, index: usize) -> Option<&'a Expr> {
897 match self {
898 Self::List(args) => args.get(index),
899 Self::Pair(first, _) if index == 0 => Some(first),
900 Self::Pair(_, second) if index == 1 => Some(second),
901 Self::Star | Self::Pair(_, _) => None,
902 }
903 }
904
905 #[must_use]
907 pub fn first(self) -> Option<&'a Expr> {
908 self.get(0)
909 }
910
911 pub fn iter(self) -> impl DoubleEndedIterator<Item = &'a Expr> + ExactSizeIterator + Clone {
913 SqlFunctionArgsIter {
914 args: self,
915 indices: 0..self.len(),
916 }
917 }
918
919 #[must_use]
924 pub const fn as_list(self) -> Option<&'a [Expr]> {
925 match self {
926 Self::List(args) => Some(args),
927 Self::Star | Self::Pair(_, _) => None,
928 }
929 }
930
931 #[must_use]
933 pub fn to_owned(self) -> FunctionArgs {
934 match self {
935 Self::Star => FunctionArgs::Star,
936 Self::List(args) => FunctionArgs::List(args.to_vec()),
937 Self::Pair(first, second) => FunctionArgs::List(vec![first.clone(), second.clone()]),
938 }
939 }
940}
941
942#[derive(Debug, Clone)]
943struct SqlFunctionArgsIter<'a> {
944 args: SqlFunctionArgsRef<'a>,
945 indices: std::ops::Range<usize>,
946}
947
948impl<'a> Iterator for SqlFunctionArgsIter<'a> {
949 type Item = &'a Expr;
950
951 fn next(&mut self) -> Option<Self::Item> {
952 self.indices.next().and_then(|index| self.args.get(index))
953 }
954
955 fn size_hint(&self) -> (usize, Option<usize>) {
956 self.indices.size_hint()
957 }
958}
959
960impl DoubleEndedIterator for SqlFunctionArgsIter<'_> {
961 fn next_back(&mut self) -> Option<Self::Item> {
962 self.indices
963 .next_back()
964 .and_then(|index| self.args.get(index))
965 }
966}
967
968impl ExactSizeIterator for SqlFunctionArgsIter<'_> {
969 fn len(&self) -> usize {
970 self.indices.len()
971 }
972}
973
974impl std::iter::FusedIterator for SqlFunctionArgsIter<'_> {}
975
976#[derive(Debug, Clone, Copy)]
982pub struct SqlFunctionCallRef<'a> {
983 pub name: &'a str,
985 pub args: SqlFunctionArgsRef<'a>,
987 pub distinct: bool,
989 pub order_by: &'a [OrderingTerm],
991 pub filter: Option<&'a Expr>,
993 pub over: Option<&'a WindowSpec>,
995}
996
997impl Expr {
998 #[must_use]
1003 pub fn as_sql_function_call(&self) -> Option<SqlFunctionCallRef<'_>> {
1004 match self {
1005 Self::FunctionCall {
1006 name,
1007 args,
1008 distinct,
1009 order_by,
1010 filter,
1011 over,
1012 ..
1013 } => Some(SqlFunctionCallRef {
1014 name,
1015 args: match args {
1016 FunctionArgs::Star => SqlFunctionArgsRef::Star,
1017 FunctionArgs::List(args) => SqlFunctionArgsRef::List(args),
1018 },
1019 distinct: *distinct,
1020 order_by,
1021 filter: filter.as_deref(),
1022 over: over.as_ref(),
1023 }),
1024 Self::JsonAccess {
1025 expr, path, arrow, ..
1026 } => Some(SqlFunctionCallRef {
1027 name: arrow.sql_function_name(),
1028 args: SqlFunctionArgsRef::Pair(expr, path),
1029 distinct: false,
1030 order_by: &[],
1031 filter: None,
1032 over: None,
1033 }),
1034 _ => None,
1035 }
1036 }
1037}
1038
1039#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1041pub enum PlaceholderType {
1042 Anonymous,
1044 Numbered(u32),
1046 ColonNamed(String),
1048 AtNamed(String),
1050 DollarNamed(String),
1052}
1053
1054#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1056pub enum RaiseAction {
1057 Ignore,
1058 Rollback,
1059 Abort,
1060 Fail,
1061}
1062
1063#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1074pub enum WindowReference {
1075 Direct(String),
1077 Base(String),
1079}
1080
1081impl WindowReference {
1082 #[must_use]
1084 pub fn name(&self) -> &str {
1085 match self {
1086 Self::Direct(name) | Self::Base(name) => name,
1087 }
1088 }
1089}
1090
1091#[derive(Debug, Clone, PartialEq)]
1093pub struct WindowSpec {
1094 pub window_ref: Option<WindowReference>,
1096 pub partition_by: Vec<Expr>,
1098 pub order_by: Vec<OrderingTerm>,
1100 pub frame: Option<FrameSpec>,
1102}
1103
1104#[derive(Debug, Clone, PartialEq)]
1106pub struct FrameSpec {
1107 pub frame_type: FrameType,
1109 pub start: FrameBound,
1111 pub end: Option<FrameBound>,
1113 pub exclude: Option<FrameExclude>,
1115}
1116
1117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1119pub enum FrameType {
1120 Rows,
1121 Range,
1122 Groups,
1123}
1124
1125#[derive(Debug, Clone, PartialEq)]
1127pub enum FrameBound {
1128 UnboundedPreceding,
1130 Preceding(Box<Expr>),
1132 CurrentRow,
1134 Following(Box<Expr>),
1136 UnboundedFollowing,
1138}
1139
1140#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1142pub enum FrameExclude {
1143 NoOthers,
1144 CurrentRow,
1145 Group,
1146 Ties,
1147}
1148
1149#[derive(Debug, Clone, PartialEq)]
1155pub struct SelectStatement {
1156 pub with: Option<WithClause>,
1158 pub body: SelectBody,
1160 pub order_by: Vec<OrderingTerm>,
1162 pub limit: Option<LimitClause>,
1164}
1165
1166#[derive(Debug, Clone, PartialEq)]
1168pub struct WithClause {
1169 pub recursive: bool,
1171 pub ctes: Vec<Cte>,
1173}
1174
1175#[derive(Debug, Clone, PartialEq)]
1177pub struct Cte {
1178 pub name: String,
1180 pub columns: Vec<String>,
1182 pub materialized: Option<CteMaterialized>,
1184 pub query: SelectStatement,
1186}
1187
1188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1190pub enum CteMaterialized {
1191 Materialized,
1192 NotMaterialized,
1193}
1194
1195#[derive(Debug, Clone, PartialEq)]
1197pub struct SelectBody {
1198 pub select: SelectCore,
1200 pub compounds: Vec<(CompoundOp, SelectCore)>,
1202}
1203
1204#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1206pub enum CompoundOp {
1207 Union,
1208 UnionAll,
1209 Intersect,
1210 Except,
1211}
1212
1213#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1221pub enum ValuesRepresentation {
1222 Deferred {
1224 force_union_all_from: Option<usize>,
1227 },
1228 Frozen {
1230 donor_row: Option<usize>,
1233 },
1234}
1235
1236#[derive(Debug, Clone, PartialEq)]
1243pub struct ValuesClause {
1244 rows: Vec<Vec<Expr>>,
1245 representation: ValuesRepresentation,
1246}
1247
1248impl ValuesClause {
1249 #[must_use]
1251 pub fn new(rows: Vec<Vec<Expr>>) -> Self {
1252 Self::parsed(rows, None)
1253 }
1254
1255 #[must_use]
1261 pub fn parsed(rows: Vec<Vec<Expr>>, force_union_all_from: Option<usize>) -> Self {
1262 assert!(
1263 force_union_all_from.is_none_or(|index| index < rows.len()),
1264 "forced VALUES row must be present"
1265 );
1266 Self {
1267 rows,
1268 representation: ValuesRepresentation::Deferred {
1269 force_union_all_from,
1270 },
1271 }
1272 }
1273
1274 #[must_use]
1276 pub const fn representation(&self) -> ValuesRepresentation {
1277 self.representation
1278 }
1279
1280 #[must_use]
1282 pub const fn force_union_all_from(&self) -> Option<usize> {
1283 match self.representation {
1284 ValuesRepresentation::Deferred {
1285 force_union_all_from,
1286 } => force_union_all_from,
1287 ValuesRepresentation::Frozen { .. } => None,
1288 }
1289 }
1290
1291 #[must_use]
1293 pub const fn is_frozen(&self) -> bool {
1294 matches!(self.representation, ValuesRepresentation::Frozen { .. })
1295 }
1296
1297 pub fn freeze_donor_row(&mut self, donor_row: Option<usize>) {
1304 assert!(!self.is_frozen(), "VALUES donor is already frozen");
1305 assert_eq!(
1306 donor_row.is_some(),
1307 !self.rows.is_empty(),
1308 "non-empty VALUES clauses require a donor row"
1309 );
1310 assert!(
1311 donor_row.is_none_or(|index| index < self.rows.len()),
1312 "VALUES donor row must be present"
1313 );
1314 self.representation = ValuesRepresentation::Frozen { donor_row };
1315 }
1316
1317 #[must_use]
1319 pub const fn donor_row_index(&self) -> Option<usize> {
1320 match self.representation {
1321 ValuesRepresentation::Frozen { donor_row } => donor_row,
1322 ValuesRepresentation::Deferred { .. } => None,
1323 }
1324 }
1325
1326 #[must_use]
1328 pub fn donor_row(&self) -> Option<&[Expr]> {
1329 self.donor_row_index()
1330 .and_then(|index| self.rows.get(index))
1331 .map(Vec::as_slice)
1332 }
1333
1334 #[must_use]
1336 pub fn rows(&self) -> &[Vec<Expr>] {
1337 &self.rows
1338 }
1339
1340 pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut [Expr]> + ExactSizeIterator {
1342 self.rows.iter_mut().map(Vec::as_mut_slice)
1343 }
1344
1345 #[must_use]
1347 pub fn into_rows(self) -> Vec<Vec<Expr>> {
1348 self.rows
1349 }
1350
1351 pub fn replace_rows_preserving_representation(&mut self, rows: Vec<Vec<Expr>>) {
1357 assert_eq!(
1358 self.rows.len(),
1359 rows.len(),
1360 "VALUES rewrites must preserve row identity"
1361 );
1362 self.rows = rows;
1363 }
1364}
1365
1366impl Default for ValuesClause {
1367 fn default() -> Self {
1368 Self::new(Vec::new())
1369 }
1370}
1371
1372impl From<Vec<Vec<Expr>>> for ValuesClause {
1373 fn from(rows: Vec<Vec<Expr>>) -> Self {
1374 Self::new(rows)
1375 }
1376}
1377
1378impl std::ops::Deref for ValuesClause {
1379 type Target = [Vec<Expr>];
1380
1381 fn deref(&self) -> &Self::Target {
1382 self.rows()
1383 }
1384}
1385
1386impl<'a> IntoIterator for &'a ValuesClause {
1387 type Item = &'a Vec<Expr>;
1388 type IntoIter = std::slice::Iter<'a, Vec<Expr>>;
1389
1390 fn into_iter(self) -> Self::IntoIter {
1391 self.rows.iter()
1392 }
1393}
1394
1395#[derive(Debug, Clone, PartialEq)]
1397#[allow(clippy::large_enum_variant)]
1398pub enum SelectCore {
1399 Select {
1401 distinct: Distinctness,
1402 columns: Vec<ResultColumn>,
1403 from: Option<FromClause>,
1404 where_clause: Option<Box<Expr>>,
1405 group_by: Vec<Expr>,
1406 having: Option<Box<Expr>>,
1407 windows: Vec<WindowDef>,
1408 },
1409 Values(ValuesClause),
1411}
1412
1413#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
1415pub enum Distinctness {
1416 #[default]
1417 All,
1418 Distinct,
1419}
1420
1421#[derive(Debug, Clone, PartialEq)]
1423#[allow(clippy::large_enum_variant)]
1424pub enum ResultColumn {
1425 Star,
1427 TableStar(QualifiedName),
1429 Expr { expr: Expr, alias: Option<String> },
1431}
1432
1433#[derive(Debug, Clone, PartialEq)]
1435pub struct FromClause {
1436 pub source: TableOrSubquery,
1438 pub joins: Vec<JoinClause>,
1440}
1441
1442#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1452pub enum TimeTravelTarget {
1453 CommitSequence(u64),
1455 Timestamp(String),
1457}
1458
1459#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1461pub struct TimeTravelClause {
1462 pub target: TimeTravelTarget,
1463}
1464
1465#[derive(Debug, Clone, PartialEq)]
1467pub enum TableOrSubquery {
1468 Table {
1470 name: QualifiedName,
1471 alias: Option<String>,
1472 index_hint: Option<IndexHint>,
1473 time_travel: Option<TimeTravelClause>,
1474 },
1475 Subquery {
1477 query: Box<SelectStatement>,
1478 alias: Option<String>,
1479 },
1480 TableFunction {
1482 name: String,
1483 args: Vec<Expr>,
1484 alias: Option<String>,
1485 },
1486 ParenJoin(Box<FromClause>),
1488}
1489
1490#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1492pub enum IndexHint {
1493 IndexedBy(String),
1495 NotIndexed,
1497}
1498
1499#[derive(Debug, Clone, PartialEq)]
1501pub struct JoinClause {
1502 pub join_type: JoinType,
1504 pub table: TableOrSubquery,
1506 pub constraint: Option<JoinConstraint>,
1508}
1509
1510#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1512pub struct JoinType {
1513 pub natural: bool,
1515 pub kind: JoinKind,
1517}
1518
1519#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1521pub enum JoinKind {
1522 Cross,
1524 Inner,
1526 Left,
1528 Right,
1530 Full,
1532}
1533
1534#[derive(Debug, Clone, PartialEq)]
1536pub enum JoinConstraint {
1537 On(Expr),
1538 Using(Vec<String>),
1539}
1540
1541#[derive(Debug, Clone, PartialEq)]
1543pub struct WindowDef {
1544 pub name: String,
1546 pub spec: WindowSpec,
1548}
1549
1550#[derive(Debug, Clone, PartialEq)]
1552pub struct OrderingTerm {
1553 pub expr: Expr,
1555 pub direction: Option<SortDirection>,
1557 pub nulls: Option<NullsOrder>,
1559}
1560
1561#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1563pub enum SortDirection {
1564 Asc,
1565 Desc,
1566}
1567
1568#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1570pub enum NullsOrder {
1571 First,
1572 Last,
1573}
1574
1575#[derive(Debug, Clone, PartialEq)]
1577pub struct LimitClause {
1578 pub limit: Expr,
1579 pub offset: Option<Expr>,
1580}
1581
1582#[derive(Debug, Clone, PartialEq)]
1588pub struct InsertStatement {
1589 pub with: Option<WithClause>,
1591 pub or_conflict: Option<ConflictAction>,
1593 pub table: QualifiedName,
1595 pub alias: Option<String>,
1597 pub columns: Vec<String>,
1599 pub source: InsertSource,
1601 pub upsert: Vec<UpsertClause>,
1603 pub returning: Vec<ResultColumn>,
1605}
1606
1607#[derive(Debug, Clone, PartialEq)]
1609pub enum InsertSource {
1610 Values(Vec<Vec<Expr>>),
1612 Select(Box<SelectStatement>),
1614 DefaultValues,
1616}
1617
1618#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1620pub enum ConflictAction {
1621 Rollback,
1622 Abort,
1623 Fail,
1624 Ignore,
1625 Replace,
1626}
1627
1628#[derive(Debug, Clone, PartialEq)]
1630pub struct UpsertClause {
1631 pub target: Option<UpsertTarget>,
1633 pub action: UpsertAction,
1635}
1636
1637#[derive(Debug, Clone, PartialEq)]
1639pub struct UpsertTarget {
1640 pub columns: Vec<IndexedColumn>,
1642 pub where_clause: Option<Expr>,
1644}
1645
1646#[derive(Debug, Clone, PartialEq)]
1648pub enum UpsertAction {
1649 Nothing,
1650 Update {
1651 assignments: Vec<Assignment>,
1652 where_clause: Option<Box<Expr>>,
1653 },
1654}
1655
1656#[derive(Debug, Clone, PartialEq)]
1662pub struct UpdateStatement {
1663 pub with: Option<WithClause>,
1665 pub or_conflict: Option<ConflictAction>,
1667 pub table: QualifiedTableRef,
1669 pub assignments: Vec<Assignment>,
1671 pub from: Option<FromClause>,
1673 pub where_clause: Option<Expr>,
1675 pub returning: Vec<ResultColumn>,
1677 pub order_by: Vec<OrderingTerm>,
1679 pub limit: Option<LimitClause>,
1681}
1682
1683#[derive(Debug, Clone, PartialEq)]
1685pub struct Assignment {
1686 pub target: AssignmentTarget,
1688 pub value: Expr,
1690}
1691
1692#[derive(Debug, Clone, PartialEq, Eq)]
1694pub enum AssignmentTarget {
1695 Column(String),
1697 ColumnList(Vec<String>),
1699}
1700
1701#[derive(Debug, Clone, PartialEq, Eq)]
1703pub struct QualifiedTableRef {
1704 pub name: QualifiedName,
1705 pub alias: Option<String>,
1706 pub index_hint: Option<IndexHint>,
1707 pub time_travel: Option<TimeTravelClause>,
1708}
1709
1710#[derive(Debug, Clone, PartialEq)]
1716pub struct DeleteStatement {
1717 pub with: Option<WithClause>,
1719 pub table: QualifiedTableRef,
1721 pub where_clause: Option<Expr>,
1723 pub returning: Vec<ResultColumn>,
1725 pub order_by: Vec<OrderingTerm>,
1727 pub limit: Option<LimitClause>,
1729}
1730
1731#[derive(Debug, Clone, PartialEq)]
1737#[allow(clippy::struct_excessive_bools)]
1738pub struct CreateTableStatement {
1739 pub if_not_exists: bool,
1741 pub temporary: bool,
1743 pub name: QualifiedName,
1745 pub body: CreateTableBody,
1747 pub without_rowid: bool,
1749 pub strict: bool,
1751}
1752
1753#[derive(Debug, Clone, PartialEq)]
1755pub enum CreateTableBody {
1756 Columns {
1758 columns: Vec<ColumnDef>,
1759 constraints: Vec<TableConstraint>,
1760 },
1761 AsSelect(Box<SelectStatement>),
1763}
1764
1765#[derive(Debug, Clone, PartialEq)]
1767pub struct ColumnDef {
1768 pub name: String,
1770 pub type_name: Option<TypeName>,
1772 pub constraints: Vec<ColumnConstraint>,
1774}
1775
1776#[derive(Debug, Clone, PartialEq)]
1778pub struct ColumnConstraint {
1779 pub name: Option<String>,
1781 pub kind: ColumnConstraintKind,
1783}
1784
1785#[derive(Debug, Clone, PartialEq)]
1787pub enum ColumnConstraintKind {
1788 PrimaryKey {
1789 direction: Option<SortDirection>,
1790 conflict: Option<ConflictAction>,
1791 autoincrement: bool,
1792 },
1793 NotNull {
1794 conflict: Option<ConflictAction>,
1795 },
1796 Null,
1797 Unique {
1798 conflict: Option<ConflictAction>,
1799 },
1800 Check(Expr),
1801 Default(DefaultValue),
1802 Collate(String),
1803 ForeignKey(ForeignKeyClause),
1804 Generated {
1805 expr: Expr,
1806 storage: Option<GeneratedStorage>,
1807 },
1808}
1809
1810#[derive(Debug, Clone, PartialEq)]
1812pub enum DefaultValue {
1813 Expr(Expr),
1814 ParenExpr(Expr),
1816}
1817
1818#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1820pub enum GeneratedStorage {
1821 Stored,
1822 Virtual,
1823}
1824
1825#[derive(Debug, Clone, PartialEq)]
1827pub struct TableConstraint {
1828 pub name: Option<String>,
1830 pub kind: TableConstraintKind,
1832}
1833
1834#[derive(Debug, Clone, PartialEq)]
1836pub enum TableConstraintKind {
1837 PrimaryKey {
1838 columns: Vec<IndexedColumn>,
1839 conflict: Option<ConflictAction>,
1840 },
1841 Unique {
1842 columns: Vec<IndexedColumn>,
1843 conflict: Option<ConflictAction>,
1844 },
1845 Check(Expr),
1846 ForeignKey {
1847 columns: Vec<String>,
1848 clause: ForeignKeyClause,
1849 },
1850}
1851
1852#[derive(Debug, Clone, PartialEq)]
1854pub struct IndexedColumn {
1855 pub expr: Expr,
1857 pub collation: Option<String>,
1859 pub direction: Option<SortDirection>,
1861}
1862
1863#[derive(Debug, Clone, PartialEq, Eq)]
1865pub struct ForeignKeyClause {
1866 pub table: String,
1868 pub columns: Vec<String>,
1870 pub actions: Vec<ForeignKeyAction>,
1872 pub deferrable: Option<Deferrable>,
1874}
1875
1876#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1878pub struct ForeignKeyAction {
1879 pub trigger: ForeignKeyTrigger,
1880 pub action: ForeignKeyActionType,
1881}
1882
1883#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1885pub enum ForeignKeyTrigger {
1886 OnDelete,
1887 OnUpdate,
1888}
1889
1890#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1892pub enum ForeignKeyActionType {
1893 SetNull,
1894 SetDefault,
1895 Cascade,
1896 Restrict,
1897 NoAction,
1898}
1899
1900#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1902pub struct Deferrable {
1903 pub not: bool,
1904 pub initially: Option<DeferrableInitially>,
1905}
1906
1907#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1909pub enum DeferrableInitially {
1910 Deferred,
1911 Immediate,
1912}
1913
1914#[derive(Debug, Clone, PartialEq)]
1920pub struct CreateIndexStatement {
1921 pub unique: bool,
1923 pub if_not_exists: bool,
1925 pub name: QualifiedName,
1927 pub table: String,
1929 pub columns: Vec<IndexedColumn>,
1931 pub where_clause: Option<Expr>,
1933}
1934
1935#[derive(Debug, Clone, PartialEq)]
1941pub struct CreateViewStatement {
1942 pub if_not_exists: bool,
1944 pub temporary: bool,
1946 pub name: QualifiedName,
1948 pub columns: Vec<String>,
1950 pub query: SelectStatement,
1952}
1953
1954#[derive(Debug, Clone, PartialEq)]
1960pub struct CreateTriggerStatement {
1961 pub if_not_exists: bool,
1963 pub temporary: bool,
1965 pub name: QualifiedName,
1967 pub timing: TriggerTiming,
1969 pub event: TriggerEvent,
1971 pub table: String,
1973 pub for_each_row: bool,
1975 pub when: Option<Expr>,
1977 pub body: Vec<Statement>,
1979}
1980
1981#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1983pub enum TriggerTiming {
1984 Before,
1985 After,
1986 InsteadOf,
1987}
1988
1989#[derive(Debug, Clone, PartialEq, Eq)]
1991pub enum TriggerEvent {
1992 Insert,
1993 Delete,
1994 Update(Vec<String>),
1995}
1996
1997#[derive(Debug, Clone, PartialEq, Eq)]
2003pub struct CreateVirtualTableStatement {
2004 pub if_not_exists: bool,
2006 pub name: QualifiedName,
2008 pub module: String,
2010 pub args: Vec<String>,
2012}
2013
2014#[derive(Debug, Clone, PartialEq, Eq)]
2020pub struct DropStatement {
2021 pub object_type: DropObjectType,
2023 pub if_exists: bool,
2025 pub name: QualifiedName,
2027}
2028
2029#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2031pub enum DropObjectType {
2032 Table,
2033 View,
2034 Index,
2035 Trigger,
2036}
2037
2038#[derive(Debug, Clone, PartialEq)]
2044pub struct AlterTableStatement {
2045 pub table: QualifiedName,
2047 pub action: AlterTableAction,
2049}
2050
2051#[derive(Debug, Clone, PartialEq)]
2053pub enum AlterTableAction {
2054 RenameTo(String),
2056 RenameColumn { old: String, new: String },
2058 AddColumn(ColumnDef),
2060 DropColumn(String),
2062}
2063
2064#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2070pub struct BeginStatement {
2071 pub mode: Option<TransactionMode>,
2073}
2074
2075#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2077pub enum TransactionMode {
2078 Deferred,
2079 Immediate,
2080 Exclusive,
2081 Concurrent,
2083}
2084
2085#[derive(Debug, Clone, PartialEq, Eq)]
2087pub struct RollbackStatement {
2088 pub to_savepoint: Option<String>,
2090}
2091
2092#[derive(Debug, Clone, PartialEq)]
2098pub struct AttachStatement {
2099 pub expr: Expr,
2101 pub schema: String,
2103}
2104
2105#[derive(Debug, Clone, PartialEq)]
2111pub struct PragmaStatement {
2112 pub name: QualifiedName,
2114 pub value: Option<PragmaValue>,
2116}
2117
2118#[derive(Debug, Clone, PartialEq)]
2120pub enum PragmaValue {
2121 Assign(Expr),
2123 Call(Expr),
2125}
2126
2127#[derive(Debug, Clone, PartialEq)]
2133pub struct VacuumStatement {
2134 pub schema: Option<String>,
2136 pub into: Option<Expr>,
2138}
2139
2140#[derive(Debug, Clone, PartialEq, Eq)]
2146pub struct ResolvedColumn {
2147 pub table_idx: usize,
2149 pub column_idx: usize,
2151 pub table_name: String,
2153 pub column_name: String,
2155}
2156
2157#[derive(Debug, Clone, PartialEq, Eq)]
2159pub struct TableSchema {
2160 pub name: String,
2162 pub alias: Option<String>,
2164 pub columns: Vec<String>,
2166}
2167
2168impl TableSchema {
2169 #[must_use]
2171 pub fn effective_name(&self) -> &str {
2172 self.alias.as_deref().unwrap_or(&self.name)
2173 }
2174}
2175
2176#[derive(Debug, Clone, PartialEq, Eq)]
2178pub enum ResolveError {
2179 NoSuchTable { name: String, span: Span },
2181 NoSuchColumn {
2183 table: String,
2184 column: String,
2185 span: Span,
2186 },
2187 AmbiguousColumn {
2189 column: String,
2190 candidates: Vec<String>,
2191 span: Span,
2192 },
2193 ColumnNotFound { column: String, span: Span },
2195 NoOuterTable { name: String, span: Span },
2197}
2198
2199impl fmt::Display for ResolveError {
2200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2201 match self {
2202 Self::NoSuchTable { name, span } => {
2203 write!(f, "no such table: {name} at {span}")
2204 }
2205 Self::NoSuchColumn {
2206 table,
2207 column,
2208 span,
2209 } => {
2210 write!(f, "no such column: {table}.{column} at {span}")
2211 }
2212 Self::AmbiguousColumn {
2213 column,
2214 candidates,
2215 span,
2216 } => {
2217 write!(
2218 f,
2219 "ambiguous column name: {column} (candidates: {}) at {span}",
2220 candidates.join(", ")
2221 )
2222 }
2223 Self::ColumnNotFound { column, span } => {
2224 write!(f, "no such column: {column} at {span}")
2225 }
2226 Self::NoOuterTable { name, span } => {
2227 write!(f, "no such table in outer scope: {name} at {span}")
2228 }
2229 }
2230 }
2231}
2232
2233impl std::error::Error for ResolveError {}
2234
2235#[derive(Debug, Clone)]
2237pub struct ResolverScope {
2238 pub tables: Vec<TableSchema>,
2240 pub parent: Option<Box<Self>>,
2242}
2243
2244impl ResolverScope {
2245 #[must_use]
2247 pub fn new(tables: Vec<TableSchema>) -> Self {
2248 Self {
2249 tables,
2250 parent: None,
2251 }
2252 }
2253
2254 #[must_use]
2256 pub fn child(self, tables: Vec<TableSchema>) -> Self {
2257 Self {
2258 tables,
2259 parent: Some(Box::new(self)),
2260 }
2261 }
2262
2263 pub fn resolve(&self, col: &ColumnRef, span: Span) -> Result<ResolvedColumn, ResolveError> {
2269 match &col.table {
2270 Some(table_name) => self.resolve_qualified(table_name, &col.column, span),
2271 None => self.resolve_unqualified(&col.column, span),
2272 }
2273 }
2274
2275 fn resolve_qualified(
2276 &self,
2277 table_name: &str,
2278 column: &str,
2279 span: Span,
2280 ) -> Result<ResolvedColumn, ResolveError> {
2281 for (idx, table) in self.tables.iter().enumerate() {
2282 if table.effective_name().eq_ignore_ascii_case(table_name) {
2283 return match table
2284 .columns
2285 .iter()
2286 .position(|c| c.eq_ignore_ascii_case(column))
2287 {
2288 Some(col_idx) => Ok(ResolvedColumn {
2289 table_idx: idx,
2290 column_idx: col_idx,
2291 table_name: table.effective_name().to_owned(),
2292 column_name: table.columns[col_idx].clone(),
2293 }),
2294 None => Err(ResolveError::NoSuchColumn {
2295 table: table_name.to_owned(),
2296 column: column.to_owned(),
2297 span,
2298 }),
2299 };
2300 }
2301 }
2302
2303 if let Some(ref parent) = self.parent {
2305 return parent.resolve_qualified(table_name, column, span);
2306 }
2307
2308 Err(ResolveError::NoSuchTable {
2309 name: table_name.to_owned(),
2310 span,
2311 })
2312 }
2313
2314 fn resolve_unqualified(
2315 &self,
2316 column: &str,
2317 span: Span,
2318 ) -> Result<ResolvedColumn, ResolveError> {
2319 let mut found: Option<ResolvedColumn> = None;
2320 let mut candidates = Vec::new();
2321
2322 for (idx, table) in self.tables.iter().enumerate() {
2323 if let Some(col_idx) = table
2324 .columns
2325 .iter()
2326 .position(|c| c.eq_ignore_ascii_case(column))
2327 {
2328 candidates.push(table.effective_name().to_owned());
2329 found = Some(ResolvedColumn {
2330 table_idx: idx,
2331 column_idx: col_idx,
2332 table_name: table.effective_name().to_owned(),
2333 column_name: table.columns[col_idx].clone(),
2334 });
2335 }
2336 }
2337
2338 match candidates.len() {
2339 0 => {
2340 if let Some(ref parent) = self.parent {
2342 return parent.resolve_unqualified(column, span);
2343 }
2344 Err(ResolveError::ColumnNotFound {
2345 column: column.to_owned(),
2346 span,
2347 })
2348 }
2349 1 => found.ok_or_else(|| ResolveError::ColumnNotFound {
2350 column: column.to_owned(),
2351 span,
2352 }),
2353 _ => Err(ResolveError::AmbiguousColumn {
2354 column: column.to_owned(),
2355 candidates,
2356 span,
2357 }),
2358 }
2359 }
2360
2361 #[must_use]
2365 pub fn expand_star(&self) -> Vec<(String, String)> {
2366 let mut result = Vec::new();
2367 for table in &self.tables {
2368 for col in &table.columns {
2369 result.push((table.effective_name().to_owned(), col.clone()));
2370 }
2371 }
2372 result
2373 }
2374
2375 pub fn expand_table_star(
2377 &self,
2378 table_name: &str,
2379 span: Span,
2380 ) -> Result<Vec<(String, String)>, ResolveError> {
2381 for table in &self.tables {
2382 if table.effective_name().eq_ignore_ascii_case(table_name) {
2383 return Ok(table
2384 .columns
2385 .iter()
2386 .map(|c| (table.effective_name().to_owned(), c.clone()))
2387 .collect());
2388 }
2389 }
2390 Err(ResolveError::NoSuchTable {
2391 name: table_name.to_owned(),
2392 span,
2393 })
2394 }
2395}
2396
2397#[cfg(test)]
2402mod tests {
2403 use super::*;
2404
2405 #[test]
2408 fn test_ast_statement_variants_dml() {
2409 let _ = Statement::Select(SelectStatement {
2410 with: None,
2411 body: SelectBody {
2412 select: SelectCore::Values(
2413 vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]].into(),
2414 ),
2415 compounds: vec![],
2416 },
2417 order_by: vec![],
2418 limit: None,
2419 });
2420
2421 let _ = Statement::Insert(InsertStatement {
2422 with: None,
2423 or_conflict: None,
2424 table: QualifiedName::bare("t"),
2425 alias: None,
2426 columns: vec![],
2427 source: InsertSource::DefaultValues,
2428 upsert: vec![],
2429 returning: vec![],
2430 });
2431
2432 let table_ref = QualifiedTableRef {
2433 name: QualifiedName::bare("t"),
2434 alias: None,
2435 index_hint: None,
2436 time_travel: None,
2437 };
2438 let _ = Statement::Update(UpdateStatement {
2439 with: None,
2440 or_conflict: None,
2441 table: table_ref.clone(),
2442 assignments: vec![],
2443 from: None,
2444 where_clause: None,
2445 returning: vec![],
2446 order_by: vec![],
2447 limit: None,
2448 });
2449 let _ = Statement::Delete(DeleteStatement {
2450 with: None,
2451 table: table_ref,
2452 where_clause: None,
2453 returning: vec![],
2454 order_by: vec![],
2455 limit: None,
2456 });
2457 }
2458
2459 #[test]
2460 fn test_ast_statement_variants_ddl() {
2461 let _ = Statement::CreateTable(CreateTableStatement {
2462 if_not_exists: false,
2463 temporary: false,
2464 name: QualifiedName::bare("t"),
2465 body: CreateTableBody::Columns {
2466 columns: vec![],
2467 constraints: vec![],
2468 },
2469 without_rowid: false,
2470 strict: false,
2471 });
2472
2473 let _ = Statement::CreateIndex(CreateIndexStatement {
2474 unique: false,
2475 if_not_exists: false,
2476 name: QualifiedName::bare("idx"),
2477 table: "t".to_owned(),
2478 columns: vec![],
2479 where_clause: None,
2480 });
2481
2482 let _ = Statement::CreateView(CreateViewStatement {
2483 if_not_exists: false,
2484 temporary: false,
2485 name: QualifiedName::bare("v"),
2486 columns: vec![],
2487 query: SelectStatement {
2488 with: None,
2489 body: SelectBody {
2490 select: SelectCore::Values(vec![].into()),
2491 compounds: vec![],
2492 },
2493 order_by: vec![],
2494 limit: None,
2495 },
2496 });
2497
2498 let _ = Statement::CreateTrigger(CreateTriggerStatement {
2499 if_not_exists: false,
2500 temporary: false,
2501 name: QualifiedName::bare("tr"),
2502 timing: TriggerTiming::Before,
2503 event: TriggerEvent::Insert,
2504 table: "t".to_owned(),
2505 for_each_row: true,
2506 when: None,
2507 body: vec![],
2508 });
2509
2510 let _ = Statement::CreateVirtualTable(CreateVirtualTableStatement {
2511 if_not_exists: false,
2512 name: QualifiedName::bare("vt"),
2513 module: "fts5".to_owned(),
2514 args: vec!["content".to_owned()],
2515 });
2516
2517 let _ = Statement::Drop(DropStatement {
2518 object_type: DropObjectType::Table,
2519 if_exists: false,
2520 name: QualifiedName::bare("t"),
2521 });
2522
2523 let _ = Statement::AlterTable(AlterTableStatement {
2524 table: QualifiedName::bare("t"),
2525 action: AlterTableAction::RenameTo("t2".to_owned()),
2526 });
2527 }
2528
2529 #[test]
2530 fn test_ast_statement_variants_txn_and_misc() {
2531 let _ = Statement::Begin(BeginStatement { mode: None });
2532 let _ = Statement::Commit;
2533 let _ = Statement::Rollback(RollbackStatement { to_savepoint: None });
2534
2535 let _ = Statement::Savepoint("sp1".to_owned());
2536 let _ = Statement::Release("sp1".to_owned());
2537
2538 let _ = Statement::Attach(AttachStatement {
2539 expr: Expr::Literal(Literal::String("file.db".to_owned()), Span::ZERO),
2540 schema: "aux".to_owned(),
2541 });
2542 let _ = Statement::Detach("aux".to_owned());
2543
2544 let _ = Statement::Pragma(PragmaStatement {
2545 name: QualifiedName::bare("cache_size"),
2546 value: None,
2547 });
2548 let _ = Statement::Vacuum(VacuumStatement {
2549 schema: None,
2550 into: None,
2551 });
2552
2553 let _ = Statement::Reindex(None);
2554 let _ = Statement::Analyze(None);
2555
2556 let _ = Statement::Explain {
2557 query_plan: true,
2558 stmt: Box::new(Statement::Commit),
2559 };
2560 }
2561
2562 #[test]
2563 fn test_ast_select_body_with_compounds() {
2564 let core1 =
2565 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]].into());
2566 let core2 =
2567 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(2), Span::ZERO)]].into());
2568 let core3 =
2569 SelectCore::Values(vec![vec![Expr::Literal(Literal::Integer(3), Span::ZERO)]].into());
2570
2571 let body = SelectBody {
2572 select: core1,
2573 compounds: vec![(CompoundOp::Union, core2), (CompoundOp::Intersect, core3)],
2574 };
2575
2576 assert_eq!(body.compounds.len(), 2);
2577 assert_eq!(body.compounds[0].0, CompoundOp::Union);
2578 assert_eq!(body.compounds[1].0, CompoundOp::Intersect);
2579 }
2580
2581 #[test]
2582 fn test_ast_values_as_first_class() {
2583 let values = SelectCore::Values(
2584 vec![
2585 vec![
2586 Expr::Literal(Literal::Integer(1), Span::ZERO),
2587 Expr::Literal(Literal::Integer(2), Span::ZERO),
2588 ],
2589 vec![
2590 Expr::Literal(Literal::Integer(3), Span::ZERO),
2591 Expr::Literal(Literal::Integer(4), Span::ZERO),
2592 ],
2593 ]
2594 .into(),
2595 );
2596
2597 assert!(matches!(values, SelectCore::Values(ref rows) if rows.len() == 2));
2598
2599 let select = SelectCore::Select {
2601 distinct: Distinctness::All,
2602 columns: vec![],
2603 from: None,
2604 where_clause: None,
2605 group_by: vec![],
2606 having: None,
2607 windows: vec![],
2608 };
2609 assert!(!matches!(select, SelectCore::Values(_)));
2610 }
2611
2612 #[test]
2613 fn values_clause_preserves_deferred_and_frozen_representation_invariants() {
2614 let rows = vec![
2615 vec![Expr::Literal(Literal::Integer(1), Span::ZERO)],
2616 vec![Expr::Literal(Literal::Integer(2), Span::ZERO)],
2617 ];
2618 let mut values = ValuesClause::parsed(rows.clone(), Some(1));
2619
2620 assert_eq!(values.rows(), rows.as_slice());
2621 assert_eq!(values.force_union_all_from(), Some(1));
2622 assert_eq!(
2623 values.representation(),
2624 ValuesRepresentation::Deferred {
2625 force_union_all_from: Some(1),
2626 }
2627 );
2628 assert!(!values.is_frozen());
2629 assert_eq!(values.donor_row_index(), None);
2630 assert_eq!(values.donor_row(), None);
2631
2632 let first_row = values.iter_mut().next().expect("first row must exist");
2633 first_row[0] = Expr::Literal(Literal::Integer(9), Span::ZERO);
2634 assert_eq!(values[0][0], Expr::Literal(Literal::Integer(9), Span::ZERO));
2635
2636 let replacement = vec![
2637 vec![Expr::Literal(Literal::Integer(3), Span::ZERO)],
2638 vec![Expr::Literal(Literal::Integer(4), Span::ZERO)],
2639 ];
2640 values.replace_rows_preserving_representation(replacement.clone());
2641 values.freeze_donor_row(Some(1));
2642
2643 assert!(values.is_frozen());
2644 assert_eq!(values.force_union_all_from(), None);
2645 assert_eq!(values.donor_row_index(), Some(1));
2646 assert_eq!(values.donor_row(), Some(replacement[1].as_slice()));
2647 assert_eq!(
2648 values.representation(),
2649 ValuesRepresentation::Frozen { donor_row: Some(1) }
2650 );
2651 assert_eq!(values.clone().into_rows(), replacement);
2652 }
2653
2654 #[test]
2655 fn empty_values_clause_can_freeze_without_a_donor() {
2656 let mut values = ValuesClause::default();
2657 values.freeze_donor_row(None);
2658
2659 assert!(values.is_empty());
2660 assert!(values.is_frozen());
2661 assert_eq!(values.donor_row(), None);
2662 assert!(values.into_rows().is_empty());
2663 }
2664
2665 #[test]
2666 #[should_panic(expected = "forced VALUES row must be present")]
2667 fn values_clause_rejects_an_invalid_forced_row() {
2668 let _ = ValuesClause::parsed(
2669 vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]],
2670 Some(1),
2671 );
2672 }
2673
2674 #[test]
2675 #[should_panic(expected = "VALUES donor row must be present")]
2676 fn values_clause_rejects_an_invalid_donor_row() {
2677 let mut values =
2678 ValuesClause::new(vec![vec![Expr::Literal(Literal::Integer(1), Span::ZERO)]]);
2679 values.freeze_donor_row(Some(1));
2680 }
2681
2682 #[test]
2683 #[allow(clippy::too_many_lines)]
2684 fn test_ast_expr_variants_core() {
2685 let span = Span::new(0, 10);
2686 let dummy = || Box::new(Expr::Literal(Literal::Null, span));
2687
2688 let exprs: Vec<Expr> = vec![
2689 Expr::Literal(Literal::Integer(42), span),
2690 Expr::Column(ColumnRef::bare("x"), span),
2691 Expr::BinaryOp {
2692 left: dummy(),
2693 op: BinaryOp::Add,
2694 right: dummy(),
2695 span,
2696 },
2697 Expr::UnaryOp {
2698 op: UnaryOp::Negate,
2699 expr: dummy(),
2700 span,
2701 },
2702 Expr::Between {
2703 expr: dummy(),
2704 low: dummy(),
2705 high: dummy(),
2706 not: false,
2707 span,
2708 },
2709 Expr::In {
2710 expr: dummy(),
2711 set: InSet::List(vec![]),
2712 not: false,
2713 span,
2714 },
2715 Expr::Like {
2716 expr: dummy(),
2717 pattern: dummy(),
2718 escape: None,
2719 op: LikeOp::Like,
2720 not: false,
2721 span,
2722 },
2723 Expr::Case {
2724 operand: None,
2725 whens: vec![],
2726 else_expr: None,
2727 span,
2728 },
2729 Expr::Cast {
2730 expr: dummy(),
2731 type_name: TypeName {
2732 name: "INTEGER".to_owned(),
2733 arg1: None,
2734 arg2: None,
2735 },
2736 span,
2737 },
2738 Expr::Collate {
2739 expr: dummy(),
2740 collation: "NOCASE".to_owned(),
2741 span,
2742 },
2743 Expr::IsNull {
2744 expr: dummy(),
2745 not: false,
2746 span,
2747 },
2748 Expr::JsonAccess {
2749 expr: dummy(),
2750 path: dummy(),
2751 arrow: JsonArrow::Arrow,
2752 span,
2753 },
2754 Expr::RowValue(vec![], span),
2755 Expr::Placeholder(PlaceholderType::Anonymous, span),
2756 ];
2757
2758 for expr in &exprs {
2759 assert_eq!(expr.span(), span);
2760 }
2761 }
2762
2763 #[test]
2764 fn test_ast_expr_variants_subqueries_and_calls() {
2765 let span = Span::new(0, 10);
2766 let dummy = || Box::new(Expr::Literal(Literal::Null, span));
2767
2768 let empty_select = SelectStatement {
2769 with: None,
2770 body: SelectBody {
2771 select: SelectCore::Values(vec![].into()),
2772 compounds: vec![],
2773 },
2774 order_by: vec![],
2775 limit: None,
2776 };
2777
2778 let exprs: Vec<Expr> = vec![
2779 Expr::Exists {
2780 subquery: Box::new(empty_select.clone()),
2781 not: false,
2782 span,
2783 },
2784 Expr::Subquery(Box::new(empty_select), span),
2785 Expr::FunctionCall {
2786 name: "count".to_owned(),
2787 args: FunctionArgs::Star,
2788 distinct: false,
2789 order_by: vec![],
2790 filter: None,
2791 over: None,
2792 span,
2793 },
2794 Expr::Raise {
2795 action: RaiseAction::Abort,
2796 message: Some("error".to_owned()),
2797 span,
2798 },
2799 Expr::UnaryOp {
2801 op: UnaryOp::Negate,
2802 expr: dummy(),
2803 span,
2804 },
2805 ];
2806
2807 for expr in &exprs {
2808 assert_eq!(expr.span(), span);
2809 }
2810 }
2811
2812 #[test]
2813 fn test_ast_function_call_with_window() {
2814 let span = Span::new(0, 30);
2815 let expr = Expr::FunctionCall {
2816 name: "row_number".to_owned(),
2817 args: FunctionArgs::List(vec![]),
2818 distinct: false,
2819 order_by: vec![],
2820 filter: None,
2821 over: Some(WindowSpec {
2822 window_ref: None,
2823 partition_by: vec![Expr::Column(ColumnRef::bare("dept"), span)],
2824 order_by: vec![OrderingTerm {
2825 expr: Expr::Column(ColumnRef::bare("salary"), span),
2826 direction: Some(SortDirection::Desc),
2827 nulls: None,
2828 }],
2829 frame: Some(FrameSpec {
2830 frame_type: FrameType::Rows,
2831 start: FrameBound::UnboundedPreceding,
2832 end: Some(FrameBound::CurrentRow),
2833 exclude: None,
2834 }),
2835 }),
2836 span,
2837 };
2838
2839 assert!(matches!(expr, Expr::FunctionCall { over: Some(_), .. }));
2840 if let Expr::FunctionCall {
2841 over: Some(ref win),
2842 ..
2843 } = expr
2844 {
2845 assert_eq!(win.partition_by.len(), 1);
2846 assert_eq!(win.order_by.len(), 1);
2847 assert!(win.frame.is_some());
2848 }
2849 }
2850
2851 #[test]
2852 fn test_ast_like_with_escape() {
2853 let span = Span::ZERO;
2854 let expr = Expr::Like {
2855 expr: Box::new(Expr::Column(ColumnRef::bare("name"), span)),
2856 pattern: Box::new(Expr::Literal(Literal::String("foo%".to_owned()), span)),
2857 escape: Some(Box::new(Expr::Literal(
2858 Literal::String("\\".to_owned()),
2859 span,
2860 ))),
2861 op: LikeOp::Like,
2862 not: false,
2863 span,
2864 };
2865
2866 assert!(matches!(
2867 expr,
2868 Expr::Like {
2869 escape: Some(_),
2870 ..
2871 }
2872 ));
2873 if let Expr::Like {
2874 escape: Some(ref esc),
2875 ..
2876 } = expr
2877 {
2878 assert!(matches!(esc.as_ref(), Expr::Literal(Literal::String(_), _)));
2879 }
2880 }
2881
2882 #[test]
2883 fn test_ast_json_access_arrow_types() {
2884 let span = Span::ZERO;
2885 let arrow = Expr::JsonAccess {
2886 expr: Box::new(Expr::Column(ColumnRef::bare("data"), span)),
2887 path: Box::new(Expr::Literal(Literal::String("$.name".to_owned()), span)),
2888 arrow: JsonArrow::Arrow,
2889 span,
2890 };
2891 let double_arrow = Expr::JsonAccess {
2892 expr: Box::new(Expr::Column(ColumnRef::bare("data"), span)),
2893 path: Box::new(Expr::Literal(Literal::String("$.name".to_owned()), span)),
2894 arrow: JsonArrow::DoubleArrow,
2895 span,
2896 };
2897
2898 assert!(matches!(
2899 (&arrow, &double_arrow),
2900 (
2901 Expr::JsonAccess {
2902 arrow: JsonArrow::Arrow,
2903 ..
2904 },
2905 Expr::JsonAccess {
2906 arrow: JsonArrow::DoubleArrow,
2907 ..
2908 }
2909 )
2910 ));
2911 }
2912
2913 #[test]
2914 fn test_sql_function_args_ref_preserves_shape_and_order() {
2915 let args = [
2916 Expr::Literal(Literal::Integer(10), Span::ZERO),
2917 Expr::Literal(Literal::Integer(20), Span::ZERO),
2918 Expr::Literal(Literal::Integer(30), Span::ZERO),
2919 ];
2920
2921 let star = SqlFunctionArgsRef::Star;
2922 assert_eq!(star.len(), 0);
2923 assert_eq!(star.arity_i32(), 0);
2924 assert!(star.is_empty());
2925 assert!(star.is_star());
2926 assert_eq!(star.first(), None);
2927 assert_eq!(star.iter().len(), 0);
2928 assert_eq!(star.as_list(), None);
2929 assert_eq!(star.to_owned(), FunctionArgs::Star);
2930
2931 let empty: [Expr; 0] = [];
2932 let empty_list = SqlFunctionArgsRef::List(&empty);
2933 assert!(empty_list.is_empty());
2934 assert!(!empty_list.is_star());
2935 assert_eq!(empty_list.as_list(), Some(empty.as_slice()));
2936 assert_eq!(empty_list.to_owned(), FunctionArgs::List(vec![]));
2937
2938 let list = SqlFunctionArgsRef::List(&args);
2939 assert_eq!(list.len(), 3);
2940 assert_eq!(list.arity_i32(), 3);
2941 assert_eq!(list.first(), Some(&args[0]));
2942 assert_eq!(list.get(2), Some(&args[2]));
2943 assert_eq!(list.get(3), None);
2944 assert_eq!(
2945 list.iter().collect::<Vec<_>>(),
2946 args.iter().collect::<Vec<_>>()
2947 );
2948 assert_eq!(list.as_list(), Some(args.as_slice()));
2949 assert_eq!(list.to_owned(), FunctionArgs::List(args.to_vec()));
2950
2951 let pair = SqlFunctionArgsRef::Pair(&args[0], &args[2]);
2952 assert_eq!(pair.len(), 2);
2953 assert_eq!(pair.arity_i32(), 2);
2954 assert!(!pair.is_star());
2955 assert_eq!(pair.as_list(), None);
2956 assert_eq!(pair.get(0), Some(&args[0]));
2957 assert_eq!(pair.get(1), Some(&args[2]));
2958 assert_eq!(pair.get(2), None);
2959
2960 let mut iter = pair.iter();
2961 assert_eq!(iter.len(), 2);
2962 assert_eq!(iter.next(), Some(&args[0]));
2963 assert_eq!(iter.next_back(), Some(&args[2]));
2964 assert_eq!(iter.next(), None);
2965 assert_eq!(
2966 pair.to_owned(),
2967 FunctionArgs::List(vec![args[0].clone(), args[2].clone()])
2968 );
2969 }
2970
2971 #[test]
2972 fn test_explicit_function_call_has_borrowed_semantic_view() {
2973 let span = Span::new(4, 24);
2974 let expr = Expr::FunctionCall {
2975 name: "total".to_owned(),
2976 args: FunctionArgs::List(vec![Expr::Column(ColumnRef::bare("amount"), span)]),
2977 distinct: true,
2978 order_by: vec![OrderingTerm {
2979 expr: Expr::Column(ColumnRef::bare("sequence"), span),
2980 direction: Some(SortDirection::Desc),
2981 nulls: Some(NullsOrder::Last),
2982 }],
2983 filter: Some(Box::new(Expr::Literal(Literal::Integer(1), span))),
2984 over: Some(WindowSpec {
2985 window_ref: None,
2986 partition_by: vec![],
2987 order_by: vec![],
2988 frame: None,
2989 }),
2990 span,
2991 };
2992
2993 let Some(call) = expr.as_sql_function_call() else {
2994 panic!("explicit function call must have a semantic call view");
2995 };
2996 assert_eq!(call.name, "total");
2997 assert_eq!(call.args.len(), 1);
2998 assert!(!call.args.is_star());
2999 assert!(call.distinct);
3000 assert_eq!(call.order_by.len(), 1);
3001 assert!(matches!(
3002 call.filter,
3003 Some(Expr::Literal(Literal::Integer(1), _))
3004 ));
3005 assert!(call.over.is_some());
3006
3007 let star_expr = Expr::FunctionCall {
3008 name: "count".to_owned(),
3009 args: FunctionArgs::Star,
3010 distinct: false,
3011 order_by: vec![],
3012 filter: None,
3013 over: None,
3014 span,
3015 };
3016 let Some(star_call) = star_expr.as_sql_function_call() else {
3017 panic!("star function call must have a semantic call view");
3018 };
3019 assert!(star_call.args.is_star());
3020 assert_eq!(star_call.args.len(), 0);
3021 }
3022
3023 #[test]
3024 fn test_json_access_has_borrowed_semantic_function_call_view() {
3025 for (arrow, expected_name) in [(JsonArrow::Arrow, "->"), (JsonArrow::DoubleArrow, "->>")] {
3026 assert_eq!(arrow.sql_function_name(), expected_name);
3027
3028 let expr = Expr::JsonAccess {
3029 expr: Box::new(Expr::Column(ColumnRef::bare("document"), Span::ZERO)),
3030 path: Box::new(Expr::Literal(
3031 Literal::String("$.field".to_owned()),
3032 Span::ZERO,
3033 )),
3034 arrow,
3035 span: Span::ZERO,
3036 };
3037
3038 let Some(call) = expr.as_sql_function_call() else {
3039 panic!("JSON access must have a semantic call view");
3040 };
3041 assert_eq!(call.name, expected_name);
3042 assert_eq!(call.args.len(), 2);
3043 assert!(!call.args.is_star());
3044 assert!(!call.distinct);
3045 assert!(call.order_by.is_empty());
3046 assert_eq!(call.filter, None);
3047 assert_eq!(call.over, None);
3048
3049 let SqlFunctionArgsRef::Pair(document, path) = call.args else {
3050 panic!("JSON access must expose its operands as a borrowed pair");
3051 };
3052 assert!(matches!(
3053 document,
3054 Expr::Column(column, _) if column.column.as_ref() == "document"
3055 ));
3056 assert!(matches!(
3057 path,
3058 Expr::Literal(Literal::String(value), _) if value == "$.field"
3059 ));
3060 }
3061
3062 let literal = Expr::Literal(Literal::Integer(1), Span::ZERO);
3063 assert!(literal.as_sql_function_call().is_none());
3064 }
3065
3066 #[test]
3067 fn test_ast_row_value() {
3068 let span = Span::ZERO;
3069 let rv = Expr::RowValue(
3070 vec![
3071 Expr::Column(ColumnRef::bare("a"), span),
3072 Expr::Column(ColumnRef::bare("b"), span),
3073 Expr::Column(ColumnRef::bare("c"), span),
3074 ],
3075 span,
3076 );
3077
3078 assert!(matches!(rv, Expr::RowValue(_, _)));
3079 if let Expr::RowValue(ref elems, _) = rv {
3080 assert_eq!(elems.len(), 3);
3081 }
3082 }
3083
3084 fn make_scope_t1_t2() -> ResolverScope {
3087 ResolverScope::new(vec![
3088 TableSchema {
3089 name: "t1".to_owned(),
3090 alias: None,
3091 columns: vec!["a".to_owned(), "b".to_owned()],
3092 },
3093 TableSchema {
3094 name: "t2".to_owned(),
3095 alias: None,
3096 columns: vec!["c".to_owned(), "d".to_owned()],
3097 },
3098 ])
3099 }
3100
3101 #[test]
3102 fn test_resolve_unambiguous_column() {
3103 let scope = make_scope_t1_t2();
3104 let result = scope
3105 .resolve(&ColumnRef::bare("a"), Span::ZERO)
3106 .expect("should resolve");
3107 assert_eq!(result.table_name, "t1");
3108 assert_eq!(result.column_name, "a");
3109 assert_eq!(result.table_idx, 0);
3110 assert_eq!(result.column_idx, 0);
3111 }
3112
3113 #[test]
3114 fn test_resolve_ambiguous_column_error() {
3115 let scope = ResolverScope::new(vec![
3116 TableSchema {
3117 name: "t1".to_owned(),
3118 alias: None,
3119 columns: vec!["x".to_owned(), "y".to_owned()],
3120 },
3121 TableSchema {
3122 name: "t2".to_owned(),
3123 alias: None,
3124 columns: vec!["x".to_owned(), "z".to_owned()],
3125 },
3126 ]);
3127
3128 let err = scope
3129 .resolve(&ColumnRef::bare("x"), Span::ZERO)
3130 .unwrap_err();
3131 assert!(matches!(err, ResolveError::AmbiguousColumn { .. }));
3132 if let ResolveError::AmbiguousColumn {
3133 column, candidates, ..
3134 } = err
3135 {
3136 assert_eq!(column, "x");
3137 assert_eq!(candidates, vec!["t1", "t2"]);
3138 }
3139 }
3140
3141 #[test]
3142 fn test_resolve_qualified_column() {
3143 let scope = make_scope_t1_t2();
3144
3145 let result = scope
3146 .resolve(&ColumnRef::qualified("t1", "a"), Span::ZERO)
3147 .expect("should resolve");
3148 assert_eq!(result.table_name, "t1");
3149 assert_eq!(result.column_name, "a");
3150
3151 let err = scope
3152 .resolve(&ColumnRef::qualified("t1", "nonexistent"), Span::ZERO)
3153 .unwrap_err();
3154 assert!(matches!(err, ResolveError::NoSuchColumn { .. }));
3155 }
3156
3157 #[test]
3158 fn test_resolve_alias_binding() {
3159 let scope = ResolverScope::new(vec![TableSchema {
3160 name: "users".to_owned(),
3161 alias: Some("u".to_owned()),
3162 columns: vec!["id".to_owned(), "name".to_owned()],
3163 }]);
3164
3165 let result = scope
3166 .resolve(&ColumnRef::qualified("u", "name"), Span::ZERO)
3167 .expect("should resolve via alias");
3168 assert_eq!(result.table_name, "u");
3169 assert_eq!(result.column_name, "name");
3170 }
3171
3172 #[test]
3173 fn test_resolve_star_expansion() {
3174 let scope = make_scope_t1_t2();
3175 let expanded = scope.expand_star();
3176 assert_eq!(
3177 expanded,
3178 vec![
3179 ("t1".to_owned(), "a".to_owned()),
3180 ("t1".to_owned(), "b".to_owned()),
3181 ("t2".to_owned(), "c".to_owned()),
3182 ("t2".to_owned(), "d".to_owned()),
3183 ]
3184 );
3185 }
3186
3187 #[test]
3188 fn test_resolve_qualified_star() {
3189 let scope = make_scope_t1_t2();
3190 let expanded = scope.expand_table_star("t1", Span::ZERO).unwrap();
3191 assert_eq!(
3192 expanded,
3193 vec![
3194 ("t1".to_owned(), "a".to_owned()),
3195 ("t1".to_owned(), "b".to_owned()),
3196 ]
3197 );
3198 }
3199
3200 #[test]
3201 fn test_resolve_subquery_scope() {
3202 let outer = ResolverScope::new(vec![TableSchema {
3205 name: "t1".to_owned(),
3206 alias: None,
3207 columns: vec!["a".to_owned(), "b".to_owned()],
3208 }]);
3209
3210 let inner = outer.child(vec![TableSchema {
3211 name: "t2".to_owned(),
3212 alias: None,
3213 columns: vec!["c".to_owned(), "d".to_owned()],
3214 }]);
3215
3216 let result = inner
3218 .resolve(&ColumnRef::qualified("t2", "c"), Span::ZERO)
3219 .expect("inner table");
3220 assert_eq!(result.table_name, "t2");
3221
3222 let result = inner
3224 .resolve(&ColumnRef::qualified("t1", "a"), Span::ZERO)
3225 .expect("correlated outer reference");
3226 assert_eq!(result.table_name, "t1");
3227 assert_eq!(result.column_name, "a");
3228 }
3229
3230 #[test]
3231 fn test_resolve_scope_shadowing() {
3232 let outer = ResolverScope::new(vec![TableSchema {
3234 name: "t1".to_owned(),
3235 alias: None,
3236 columns: vec!["outer_col".to_owned()],
3237 }]);
3238
3239 let inner = outer.child(vec![TableSchema {
3240 name: "t1".to_owned(),
3241 alias: None,
3242 columns: vec!["inner_col".to_owned()],
3243 }]);
3244
3245 let result = inner
3247 .resolve(&ColumnRef::qualified("t1", "inner_col"), Span::ZERO)
3248 .expect("inner shadows outer");
3249 assert_eq!(result.column_name, "inner_col");
3250
3251 let err = inner
3253 .resolve(&ColumnRef::qualified("t1", "outer_col"), Span::ZERO)
3254 .unwrap_err();
3255 assert!(matches!(err, ResolveError::NoSuchColumn { .. }));
3256 }
3257
3258 #[test]
3259 fn test_resolve_nonexistent_table_error() {
3260 let scope = make_scope_t1_t2();
3261 let err = scope
3262 .resolve(&ColumnRef::qualified("nonexistent", "a"), Span::ZERO)
3263 .unwrap_err();
3264 assert!(matches!(err, ResolveError::NoSuchTable { .. }));
3265 }
3266
3267 #[test]
3268 fn test_resolve_unqualified_column_not_found() {
3269 let scope = make_scope_t1_t2();
3270 let err = scope
3271 .resolve(&ColumnRef::bare("nonexistent"), Span::ZERO)
3272 .unwrap_err();
3273 assert!(matches!(err, ResolveError::ColumnNotFound { .. }));
3274 if let ResolveError::ColumnNotFound { column, .. } = err {
3275 assert_eq!(column, "nonexistent");
3276 }
3277 }
3278
3279 #[test]
3280 fn test_resolve_column_in_order_by() {
3281 let scope = ResolverScope::new(vec![TableSchema {
3283 name: "result".to_owned(),
3284 alias: None,
3285 columns: vec!["total".to_owned()],
3286 }]);
3287
3288 let result = scope
3289 .resolve(&ColumnRef::bare("total"), Span::ZERO)
3290 .expect("order by alias");
3291 assert_eq!(result.column_name, "total");
3292 }
3293
3294 #[test]
3297 fn test_span_merge() {
3298 let a = Span::new(5, 10);
3299 let b = Span::new(15, 20);
3300 let merged = a.merge(b);
3301 assert_eq!(merged.start, 5);
3302 assert_eq!(merged.end, 20);
3303 }
3304
3305 #[test]
3306 fn test_span_len_is_empty() {
3307 let s = Span::new(10, 20);
3308 assert_eq!(s.len(), 10);
3309 assert!(!s.is_empty());
3310
3311 assert!(Span::ZERO.is_empty());
3312 }
3313
3314 #[test]
3317 fn test_qualified_name_display() {
3318 let bare = QualifiedName::bare("users");
3319 assert_eq!(bare.to_string(), "users");
3320
3321 let qual = QualifiedName::qualified("main", "users");
3322 assert_eq!(qual.to_string(), "main.users");
3323
3324 let keyword = QualifiedName::bare("order");
3325 assert_eq!(keyword.to_string(), "\"order\"");
3326
3327 let qualified_keyword = QualifiedName::qualified("main", "group");
3328 assert_eq!(qualified_keyword.to_string(), "main.\"group\"");
3329
3330 let keyword_schema = QualifiedName::qualified("order", "group");
3331 assert_eq!(keyword_schema.to_string(), "\"order\".\"group\"");
3332 }
3333
3334 #[test]
3337 fn test_binary_op_display() {
3338 assert_eq!(BinaryOp::Add.to_string(), "+");
3339 assert_eq!(BinaryOp::Concat.to_string(), "||");
3340 assert_eq!(BinaryOp::And.to_string(), "AND");
3341 assert_eq!(BinaryOp::IsNot.to_string(), "IS NOT");
3342 }
3343
3344 #[test]
3345 fn test_unary_op_display() {
3346 assert_eq!(UnaryOp::Negate.to_string(), "-");
3347 assert_eq!(UnaryOp::Not.to_string(), "NOT");
3348 }
3349
3350 #[test]
3353 fn test_unary_op_display_all_variants() {
3354 assert_eq!(UnaryOp::Plus.to_string(), "+");
3355 assert_eq!(UnaryOp::BitNot.to_string(), "~");
3356 }
3357
3358 #[test]
3359 fn test_binary_op_display_all_variants() {
3360 assert_eq!(BinaryOp::Subtract.to_string(), "-");
3361 assert_eq!(BinaryOp::Multiply.to_string(), "*");
3362 assert_eq!(BinaryOp::Divide.to_string(), "/");
3363 assert_eq!(BinaryOp::Modulo.to_string(), "%");
3364 assert_eq!(BinaryOp::Eq.to_string(), "=");
3365 assert_eq!(BinaryOp::Ne.to_string(), "!=");
3366 assert_eq!(BinaryOp::Lt.to_string(), "<");
3367 assert_eq!(BinaryOp::Le.to_string(), "<=");
3368 assert_eq!(BinaryOp::Gt.to_string(), ">");
3369 assert_eq!(BinaryOp::Ge.to_string(), ">=");
3370 assert_eq!(BinaryOp::Is.to_string(), "IS");
3371 assert_eq!(BinaryOp::Or.to_string(), "OR");
3372 assert_eq!(BinaryOp::BitAnd.to_string(), "&");
3373 assert_eq!(BinaryOp::BitOr.to_string(), "|");
3374 assert_eq!(BinaryOp::ShiftLeft.to_string(), "<<");
3375 assert_eq!(BinaryOp::ShiftRight.to_string(), ">>");
3376 }
3377
3378 #[test]
3379 fn test_span_debug_format() {
3380 let s = Span::new(10, 25);
3381 assert_eq!(format!("{s:?}"), "10..25");
3382 }
3383
3384 #[test]
3385 fn test_span_display_format() {
3386 let s = Span::new(0, 42);
3387 assert_eq!(format!("{s}"), "0..42");
3388 }
3389
3390 #[test]
3391 fn test_span_zero_properties() {
3392 assert_eq!(Span::ZERO.start, 0);
3393 assert_eq!(Span::ZERO.end, 0);
3394 assert_eq!(Span::ZERO.len(), 0);
3395 assert!(Span::ZERO.is_empty());
3396 }
3397
3398 #[test]
3399 fn test_span_merge_overlapping() {
3400 let a = Span::new(5, 15);
3401 let b = Span::new(10, 20);
3402 let merged = a.merge(b);
3403 assert_eq!(merged.start, 5);
3404 assert_eq!(merged.end, 20);
3405 }
3406
3407 #[test]
3408 fn test_span_merge_reversed_order() {
3409 let a = Span::new(20, 30);
3410 let b = Span::new(5, 10);
3411 let merged = a.merge(b);
3412 assert_eq!(merged.start, 5);
3413 assert_eq!(merged.end, 30);
3414 }
3415
3416 #[test]
3417 fn test_table_schema_effective_name_with_alias() {
3418 let schema = TableSchema {
3419 name: "users".to_owned(),
3420 alias: Some("u".to_owned()),
3421 columns: vec!["id".to_owned()],
3422 };
3423 assert_eq!(schema.effective_name(), "u");
3424 }
3425
3426 #[test]
3427 fn test_table_schema_effective_name_without_alias() {
3428 let schema = TableSchema {
3429 name: "users".to_owned(),
3430 alias: None,
3431 columns: vec!["id".to_owned()],
3432 };
3433 assert_eq!(schema.effective_name(), "users");
3434 }
3435
3436 #[test]
3437 fn test_resolve_case_insensitive_table() {
3438 let scope = ResolverScope::new(vec![TableSchema {
3439 name: "Users".to_owned(),
3440 alias: None,
3441 columns: vec!["Id".to_owned(), "Name".to_owned()],
3442 }]);
3443
3444 let result = scope
3446 .resolve(&ColumnRef::qualified("users", "id"), Span::ZERO)
3447 .expect("case-insensitive table match");
3448 assert_eq!(result.table_name, "Users");
3449 assert_eq!(result.column_name, "Id");
3450 }
3451
3452 #[test]
3453 fn test_resolve_case_insensitive_unqualified() {
3454 let scope = ResolverScope::new(vec![TableSchema {
3455 name: "T".to_owned(),
3456 alias: None,
3457 columns: vec!["COL_A".to_owned()],
3458 }]);
3459
3460 let result = scope
3461 .resolve(&ColumnRef::bare("col_a"), Span::ZERO)
3462 .expect("case-insensitive unqualified match");
3463 assert_eq!(result.column_name, "COL_A");
3464 }
3465
3466 #[test]
3467 fn test_expand_table_star_nonexistent() {
3468 let scope = make_scope_t1_t2();
3469 let err = scope
3470 .expand_table_star("nonexistent", Span::ZERO)
3471 .unwrap_err();
3472 assert!(matches!(err, ResolveError::NoSuchTable { .. }));
3473 }
3474
3475 #[test]
3476 fn test_resolve_error_display_no_such_table() {
3477 let err = ResolveError::NoSuchTable {
3478 name: "foo".to_owned(),
3479 span: Span::new(5, 8),
3480 };
3481 assert_eq!(err.to_string(), "no such table: foo at 5..8");
3482 }
3483
3484 #[test]
3485 fn test_resolve_error_display_no_such_column() {
3486 let err = ResolveError::NoSuchColumn {
3487 table: "t1".to_owned(),
3488 column: "bar".to_owned(),
3489 span: Span::new(10, 16),
3490 };
3491 assert_eq!(err.to_string(), "no such column: t1.bar at 10..16");
3492 }
3493
3494 #[test]
3495 fn test_resolve_error_display_ambiguous() {
3496 let err = ResolveError::AmbiguousColumn {
3497 column: "id".to_owned(),
3498 candidates: vec!["users".to_owned(), "orders".to_owned()],
3499 span: Span::ZERO,
3500 };
3501 let msg = err.to_string();
3502 assert!(msg.contains("ambiguous column name: id"));
3503 assert!(msg.contains("users, orders"));
3504 }
3505
3506 #[test]
3507 fn test_resolve_error_display_column_not_found() {
3508 let err = ResolveError::ColumnNotFound {
3509 column: "xyz".to_owned(),
3510 span: Span::new(0, 3),
3511 };
3512 assert_eq!(err.to_string(), "no such column: xyz at 0..3");
3513 }
3514
3515 #[test]
3516 fn test_resolve_error_display_no_outer_table() {
3517 let err = ResolveError::NoOuterTable {
3518 name: "outer_t".to_owned(),
3519 span: Span::new(1, 8),
3520 };
3521 assert_eq!(
3522 err.to_string(),
3523 "no such table in outer scope: outer_t at 1..8"
3524 );
3525 }
3526
3527 #[test]
3528 fn test_resolve_error_is_std_error() {
3529 let err: Box<dyn std::error::Error> = Box::new(ResolveError::ColumnNotFound {
3530 column: "x".to_owned(),
3531 span: Span::ZERO,
3532 });
3533 assert!(!err.to_string().is_empty());
3535 }
3536
3537 #[test]
3538 fn test_resolve_unqualified_from_parent_scope() {
3539 let outer = ResolverScope::new(vec![TableSchema {
3540 name: "outer_t".to_owned(),
3541 alias: None,
3542 columns: vec!["outer_col".to_owned()],
3543 }]);
3544 let inner = outer.child(vec![TableSchema {
3545 name: "inner_t".to_owned(),
3546 alias: None,
3547 columns: vec!["inner_col".to_owned()],
3548 }]);
3549
3550 let result = inner
3552 .resolve(&ColumnRef::bare("outer_col"), Span::ZERO)
3553 .expect("correlated unqualified from parent");
3554 assert_eq!(result.table_name, "outer_t");
3555 assert_eq!(result.column_name, "outer_col");
3556 }
3557
3558 #[test]
3559 fn test_distinctness_default_is_all() {
3560 assert_eq!(Distinctness::default(), Distinctness::All);
3561 }
3562
3563 #[test]
3564 fn test_transaction_mode_concurrent() {
3565 let begin = BeginStatement {
3566 mode: Some(TransactionMode::Concurrent),
3567 };
3568 assert_eq!(begin.mode, Some(TransactionMode::Concurrent));
3569 }
3570
3571 #[test]
3572 fn test_transaction_mode_all_variants() {
3573 let modes = [
3574 TransactionMode::Deferred,
3575 TransactionMode::Immediate,
3576 TransactionMode::Exclusive,
3577 TransactionMode::Concurrent,
3578 ];
3579 for (i, a) in modes.iter().enumerate() {
3581 for (j, b) in modes.iter().enumerate() {
3582 assert_eq!(i == j, a == b, "modes {i} and {j} distinctness");
3583 }
3584 }
3585 }
3586
3587 #[test]
3588 fn test_conflict_action_all_variants() {
3589 let actions = [
3590 ConflictAction::Rollback,
3591 ConflictAction::Abort,
3592 ConflictAction::Fail,
3593 ConflictAction::Ignore,
3594 ConflictAction::Replace,
3595 ];
3596 assert_eq!(actions.len(), 5);
3597 for (i, a) in actions.iter().enumerate() {
3598 for (j, b) in actions.iter().enumerate() {
3599 assert_eq!(i == j, a == b);
3600 }
3601 }
3602 }
3603
3604 #[test]
3605 fn test_compound_op_all_variants() {
3606 let ops = [
3607 CompoundOp::Union,
3608 CompoundOp::UnionAll,
3609 CompoundOp::Intersect,
3610 CompoundOp::Except,
3611 ];
3612 assert_eq!(ops.len(), 4);
3613 assert_ne!(CompoundOp::Union, CompoundOp::UnionAll);
3614 }
3615
3616 #[test]
3617 fn test_drop_object_type_variants() {
3618 let types = [
3619 DropObjectType::Table,
3620 DropObjectType::View,
3621 DropObjectType::Index,
3622 DropObjectType::Trigger,
3623 ];
3624 assert_eq!(types.len(), 4);
3625 assert_ne!(DropObjectType::Table, DropObjectType::View);
3626 }
3627
3628 #[test]
3629 fn test_like_op_variants() {
3630 let ops = [LikeOp::Like, LikeOp::Glob, LikeOp::Match, LikeOp::Regexp];
3631 assert_eq!(ops.len(), 4);
3632 assert_ne!(LikeOp::Like, LikeOp::Glob);
3633 }
3634
3635 #[test]
3636 fn test_placeholder_type_variants() {
3637 let _ = PlaceholderType::Anonymous;
3638 let _ = PlaceholderType::Numbered(1);
3639 let _ = PlaceholderType::ColonNamed("param".to_owned());
3640 let _ = PlaceholderType::AtNamed("param".to_owned());
3641 let _ = PlaceholderType::DollarNamed("param".to_owned());
3642 assert_ne!(PlaceholderType::Anonymous, PlaceholderType::Numbered(1));
3643 assert_ne!(
3645 PlaceholderType::ColonNamed("a".to_owned()),
3646 PlaceholderType::AtNamed("a".to_owned()),
3647 );
3648 }
3649
3650 #[test]
3651 fn test_raise_action_variants() {
3652 let actions = [
3653 RaiseAction::Ignore,
3654 RaiseAction::Rollback,
3655 RaiseAction::Abort,
3656 RaiseAction::Fail,
3657 ];
3658 assert_eq!(actions.len(), 4);
3659 assert_ne!(RaiseAction::Ignore, RaiseAction::Rollback);
3660 }
3661
3662 #[test]
3663 fn test_trigger_timing_variants() {
3664 let timings = [
3665 TriggerTiming::Before,
3666 TriggerTiming::After,
3667 TriggerTiming::InsteadOf,
3668 ];
3669 assert_eq!(timings.len(), 3);
3670 assert_ne!(TriggerTiming::Before, TriggerTiming::After);
3671 }
3672
3673 #[test]
3674 fn test_trigger_event_update_with_columns() {
3675 let ev = TriggerEvent::Update(vec!["col1".to_owned(), "col2".to_owned()]);
3676 assert!(matches!(ev, TriggerEvent::Update(ref cols) if cols.len() == 2));
3677 assert_ne!(TriggerEvent::Insert, TriggerEvent::Delete);
3678 }
3679
3680 #[test]
3681 fn test_frame_type_variants() {
3682 let types = [FrameType::Rows, FrameType::Range, FrameType::Groups];
3683 assert_eq!(types.len(), 3);
3684 assert_ne!(FrameType::Rows, FrameType::Groups);
3685 }
3686
3687 #[test]
3688 fn test_frame_exclude_variants() {
3689 let excludes = [
3690 FrameExclude::NoOthers,
3691 FrameExclude::CurrentRow,
3692 FrameExclude::Group,
3693 FrameExclude::Ties,
3694 ];
3695 assert_eq!(excludes.len(), 4);
3696 }
3697
3698 #[test]
3699 fn test_sort_direction_and_nulls_order() {
3700 assert_ne!(SortDirection::Asc, SortDirection::Desc);
3701 assert_ne!(NullsOrder::First, NullsOrder::Last);
3702 }
3703
3704 #[test]
3705 fn test_generated_storage_variants() {
3706 assert_ne!(GeneratedStorage::Stored, GeneratedStorage::Virtual);
3707 }
3708
3709 #[test]
3710 fn test_cte_materialized_variants() {
3711 assert_ne!(
3712 CteMaterialized::Materialized,
3713 CteMaterialized::NotMaterialized
3714 );
3715 }
3716
3717 #[test]
3718 fn test_in_set_table_variant() {
3719 let set = InSet::Table(QualifiedName::bare("lookup"));
3720 assert!(matches!(set, InSet::Table(ref n) if n.name == "lookup"));
3721 }
3722
3723 #[test]
3724 fn test_function_args_star_vs_list() {
3725 let star = FunctionArgs::Star;
3726 let list = FunctionArgs::List(vec![]);
3727 assert_ne!(star, list);
3728 }
3729
3730 #[test]
3731 fn test_insert_source_default_values() {
3732 let src = InsertSource::DefaultValues;
3733 assert!(matches!(src, InsertSource::DefaultValues));
3734 assert_ne!(InsertSource::DefaultValues, InsertSource::Values(vec![]),);
3735 }
3736
3737 #[test]
3738 fn test_pragma_value_variants() {
3739 let span = Span::ZERO;
3740 let assign = PragmaValue::Assign(Expr::Literal(Literal::Integer(100), span));
3741 let call = PragmaValue::Call(Expr::Literal(Literal::Integer(100), span));
3742 assert_ne!(assign, call);
3743 }
3744
3745 #[test]
3746 fn test_column_ref_constructors() {
3747 let bare = ColumnRef::bare("col");
3748 assert!(bare.table.is_none());
3749 assert_eq!(bare.column.as_ref(), "col");
3750
3751 let qual = ColumnRef::qualified("tbl", "col");
3752 assert_eq!(qual.table.as_deref(), Some("tbl"));
3753 assert_eq!(qual.column.as_ref(), "col");
3754 }
3755
3756 #[test]
3757 fn test_qualified_name_constructors() {
3758 let bare = QualifiedName::bare("t");
3759 assert!(bare.schema.is_none());
3760 assert_eq!(bare.name, "t");
3761
3762 let qual = QualifiedName::qualified("main", "t");
3763 assert_eq!(qual.schema.as_deref(), Some("main"));
3764 assert_eq!(qual.name, "t");
3765 }
3766
3767 #[test]
3768 fn test_deferrable_initially_variants() {
3769 let deferred = Deferrable {
3770 not: false,
3771 initially: Some(DeferrableInitially::Deferred),
3772 };
3773 let immediate = Deferrable {
3774 not: false,
3775 initially: Some(DeferrableInitially::Immediate),
3776 };
3777 assert_ne!(deferred, immediate);
3778
3779 let not_deferrable = Deferrable {
3780 not: true,
3781 initially: None,
3782 };
3783 assert_ne!(deferred, not_deferrable);
3784 }
3785
3786 #[test]
3787 fn test_foreign_key_action_types() {
3788 let types = [
3789 ForeignKeyActionType::SetNull,
3790 ForeignKeyActionType::SetDefault,
3791 ForeignKeyActionType::Cascade,
3792 ForeignKeyActionType::Restrict,
3793 ForeignKeyActionType::NoAction,
3794 ];
3795 assert_eq!(types.len(), 5);
3796 assert_ne!(
3797 ForeignKeyActionType::Cascade,
3798 ForeignKeyActionType::Restrict
3799 );
3800 }
3801
3802 #[test]
3803 fn test_foreign_key_trigger_variants() {
3804 assert_ne!(ForeignKeyTrigger::OnDelete, ForeignKeyTrigger::OnUpdate);
3805 }
3806
3807 #[test]
3808 fn test_index_hint_variants() {
3809 let indexed = IndexHint::IndexedBy("idx_name".to_owned());
3810 let not_indexed = IndexHint::NotIndexed;
3811 assert_ne!(indexed, not_indexed);
3812 }
3813
3814 #[test]
3815 fn test_join_kind_all_variants() {
3816 let kinds = [
3817 JoinKind::Cross,
3818 JoinKind::Inner,
3819 JoinKind::Left,
3820 JoinKind::Right,
3821 JoinKind::Full,
3822 ];
3823 assert_eq!(kinds.len(), 5);
3824 assert_ne!(JoinKind::Left, JoinKind::Right);
3825 }
3826
3827 #[test]
3828 fn test_join_type_natural_flag() {
3829 let natural_inner = JoinType {
3830 natural: true,
3831 kind: JoinKind::Inner,
3832 };
3833 let regular_inner = JoinType {
3834 natural: false,
3835 kind: JoinKind::Inner,
3836 };
3837 assert_ne!(natural_inner, regular_inner);
3838 }
3839
3840 #[test]
3841 fn test_alter_table_all_actions() {
3842 let rename = AlterTableAction::RenameTo("new_name".to_owned());
3843 let rename_col = AlterTableAction::RenameColumn {
3844 old: "old_col".to_owned(),
3845 new: "new_col".to_owned(),
3846 };
3847 let add_col = AlterTableAction::AddColumn(ColumnDef {
3848 name: "new_col".to_owned(),
3849 type_name: Some(TypeName {
3850 name: "INTEGER".to_owned(),
3851 arg1: None,
3852 arg2: None,
3853 }),
3854 constraints: vec![],
3855 });
3856 let drop_col = AlterTableAction::DropColumn("old_col".to_owned());
3857
3858 assert_ne!(rename, rename_col);
3860 assert_ne!(add_col, drop_col);
3861 }
3862
3863 #[test]
3864 #[allow(clippy::approx_constant)]
3865 fn test_literal_all_variants() {
3866 let _ = Literal::Integer(42);
3867 let _ = Literal::Float(3.14);
3868 let _ = Literal::String("hello".to_owned());
3869 let _ = Literal::Blob(vec![0xDE, 0xAD]);
3870 let _ = Literal::Null;
3871 let _ = Literal::True;
3872 let _ = Literal::False;
3873 let _ = Literal::CurrentTime;
3874 let _ = Literal::CurrentDate;
3875 let _ = Literal::CurrentTimestamp;
3876 assert_ne!(Literal::True, Literal::False);
3877 assert_ne!(Literal::CurrentTime, Literal::CurrentDate);
3878 }
3879
3880 #[test]
3881 fn test_json_arrow_variants() {
3882 assert_ne!(JsonArrow::Arrow, JsonArrow::DoubleArrow);
3883 }
3884
3885 #[test]
3886 fn test_upsert_action_nothing_vs_update() {
3887 let nothing = UpsertAction::Nothing;
3888 let update = UpsertAction::Update {
3889 assignments: vec![],
3890 where_clause: None,
3891 };
3892 assert_ne!(nothing, update);
3893 }
3894
3895 #[test]
3896 fn test_assignment_target_variants() {
3897 let single = AssignmentTarget::Column("col".to_owned());
3898 let multi = AssignmentTarget::ColumnList(vec!["a".to_owned(), "b".to_owned()]);
3899 assert_ne!(single, multi);
3900 }
3901
3902 #[test]
3903 fn test_type_name_with_args() {
3904 let simple = TypeName {
3905 name: "INTEGER".to_owned(),
3906 arg1: None,
3907 arg2: None,
3908 };
3909 let varchar = TypeName {
3910 name: "VARCHAR".to_owned(),
3911 arg1: Some("255".to_owned()),
3912 arg2: None,
3913 };
3914 let decimal = TypeName {
3915 name: "DECIMAL".to_owned(),
3916 arg1: Some("10".to_owned()),
3917 arg2: Some("2".to_owned()),
3918 };
3919 assert_ne!(simple, varchar);
3920 assert_ne!(varchar, decimal);
3921 }
3922
3923 #[test]
3924 fn test_frame_bound_variants() {
3925 let span = Span::ZERO;
3926 let _ = FrameBound::UnboundedPreceding;
3927 let _ = FrameBound::Preceding(Box::new(Expr::Literal(Literal::Integer(1), span)));
3928 let _ = FrameBound::CurrentRow;
3929 let _ = FrameBound::Following(Box::new(Expr::Literal(Literal::Integer(1), span)));
3930 let _ = FrameBound::UnboundedFollowing;
3931 assert_ne!(FrameBound::UnboundedPreceding, FrameBound::CurrentRow);
3932 }
3933
3934 #[test]
3935 fn test_result_column_variants() {
3936 let span = Span::ZERO;
3937 let star = ResultColumn::Star;
3938 let table_star = ResultColumn::TableStar(QualifiedName::bare("t1"));
3939 let expr = ResultColumn::Expr {
3940 expr: Expr::Literal(Literal::Integer(1), span),
3941 alias: Some("one".to_owned()),
3942 };
3943 assert_ne!(star, table_star);
3944 assert!(matches!(expr, ResultColumn::Expr { alias: Some(_), .. }));
3945 }
3946
3947 #[test]
3948 fn test_expr_eq_ignores_span() {
3949 let a = Expr::Literal(Literal::Integer(42), Span::new(0, 2));
3952 let b = Expr::Literal(Literal::Integer(42), Span::new(100, 102));
3953 assert_eq!(a, b);
3954
3955 let c = Expr::BinaryOp {
3957 left: Box::new(Expr::Column(ColumnRef::bare("x"), Span::new(0, 1))),
3958 op: BinaryOp::Gt,
3959 right: Box::new(Expr::Literal(Literal::Integer(0), Span::new(4, 5))),
3960 span: Span::new(0, 5),
3961 };
3962 let d = Expr::BinaryOp {
3963 left: Box::new(Expr::Column(ColumnRef::bare("x"), Span::new(50, 51))),
3964 op: BinaryOp::Gt,
3965 right: Box::new(Expr::Literal(Literal::Integer(0), Span::new(55, 56))),
3966 span: Span::new(50, 56),
3967 };
3968 assert_eq!(c, d);
3969
3970 let e = Expr::Literal(Literal::Integer(99), Span::ZERO);
3972 assert_ne!(a, e);
3973 }
3974
3975 #[test]
3976 fn test_bound_outer_value_equality_preserves_storage_and_metadata() {
3977 let bound = |value, collation, affinity, span| Expr::BoundOuterValue {
3978 value,
3979 collation,
3980 affinity,
3981 span,
3982 };
3983
3984 let integer = bound(
3985 SqliteValue::Integer(42),
3986 BoundCollation::Binary,
3987 Some(TypeAffinity::Integer),
3988 Span::new(0, 2),
3989 );
3990 let same_semantics = bound(
3991 SqliteValue::Integer(42),
3992 BoundCollation::Named("binary".to_owned()),
3993 Some(TypeAffinity::Integer),
3994 Span::new(100, 102),
3995 );
3996 assert_eq!(integer.span(), Span::new(0, 2));
3997 assert_eq!(integer, same_semantics);
3998
3999 let real = bound(
4000 SqliteValue::Float(42.0),
4001 BoundCollation::Binary,
4002 Some(TypeAffinity::Integer),
4003 Span::ZERO,
4004 );
4005 assert_ne!(integer, real, "INTEGER and REAL storage must stay distinct");
4006
4007 let nocase = bound(
4008 SqliteValue::Integer(42),
4009 BoundCollation::Named("NOCASE".to_owned()),
4010 Some(TypeAffinity::Integer),
4011 Span::ZERO,
4012 );
4013 assert_ne!(integer, nocase);
4014
4015 let unspecified = bound(
4016 SqliteValue::Integer(42),
4017 BoundCollation::Unspecified,
4018 Some(TypeAffinity::Integer),
4019 Span::ZERO,
4020 );
4021 assert_ne!(integer, unspecified);
4022 assert_eq!(BoundCollation::Unspecified.as_name(), None);
4023 assert_eq!(BoundCollation::Binary.as_name(), Some("BINARY"));
4024
4025 let numeric = bound(
4026 SqliteValue::Integer(42),
4027 BoundCollation::Binary,
4028 Some(TypeAffinity::Numeric),
4029 Span::ZERO,
4030 );
4031 assert_ne!(integer, numeric);
4032 }
4033}