1#![cfg_attr(
38 not(any(feature = "ast-tools", feature = "generate", feature = "semantic")),
39 allow(dead_code)
40)]
41
42use crate::expressions::{Expression, TableRef};
43use std::collections::{HashMap, VecDeque};
44
45pub type NodeId = usize;
47
48#[derive(Debug, Clone)]
50pub struct ParentInfo {
51 pub parent_id: Option<NodeId>,
53 pub arg_key: String,
55 pub index: Option<usize>,
57}
58
59#[derive(Debug, Default)]
73pub struct TreeContext {
74 nodes: HashMap<NodeId, ParentInfo>,
76 next_id: NodeId,
78 path: Vec<(NodeId, String, Option<usize>)>,
80}
81
82impl TreeContext {
83 pub fn new() -> Self {
85 Self::default()
86 }
87
88 pub fn build(root: &Expression) -> Self {
90 let mut ctx = Self::new();
91 ctx.visit_expr(root);
92 ctx
93 }
94
95 fn visit_expr(&mut self, expr: &Expression) -> NodeId {
97 let id = self.next_id;
98 self.next_id += 1;
99
100 let parent_info = if let Some((parent_id, arg_key, index)) = self.path.last() {
102 ParentInfo {
103 parent_id: Some(*parent_id),
104 arg_key: arg_key.clone(),
105 index: *index,
106 }
107 } else {
108 ParentInfo {
109 parent_id: None,
110 arg_key: String::new(),
111 index: None,
112 }
113 };
114 self.nodes.insert(id, parent_info);
115
116 crate::ast_children::for_each_child(expr, |child_path, child| {
117 let (key, index) = child_location(child_path);
118 self.path.push((id, key, index));
119 self.visit_expr(child);
120 self.path.pop();
121 });
122
123 id
124 }
125
126 pub fn get(&self, id: NodeId) -> Option<&ParentInfo> {
128 self.nodes.get(&id)
129 }
130
131 pub fn depth_of(&self, id: NodeId) -> usize {
133 let mut depth = 0;
134 let mut current = id;
135 while let Some(info) = self.nodes.get(¤t) {
136 if let Some(parent_id) = info.parent_id {
137 depth += 1;
138 current = parent_id;
139 } else {
140 break;
141 }
142 }
143 depth
144 }
145
146 pub fn ancestors_of(&self, id: NodeId) -> Vec<NodeId> {
148 let mut ancestors = Vec::new();
149 let mut current = id;
150 while let Some(info) = self.nodes.get(¤t) {
151 if let Some(parent_id) = info.parent_id {
152 ancestors.push(parent_id);
153 current = parent_id;
154 } else {
155 break;
156 }
157 }
158 ancestors
159 }
160}
161
162fn child_location(path: &[crate::ast_children::ChildPathSegment]) -> (String, Option<usize>) {
163 use crate::ast_children::ChildPathSegment;
164
165 let mut key = String::new();
166 let mut index = None;
167 for (position, segment) in path.iter().enumerate() {
168 match segment {
169 ChildPathSegment::Field(field) => {
170 if !key.is_empty() {
171 key.push('.');
172 }
173 key.push_str(field);
174 }
175 ChildPathSegment::Index(value) if position + 1 == path.len() => {
176 index = Some(*value);
177 }
178 ChildPathSegment::Index(value) => {
179 key.push('[');
180 key.push_str(&value.to_string());
181 key.push(']');
182 }
183 }
184 }
185 (key, index)
186}
187
188pub struct DfsIter<'a> {
197 stack: Vec<&'a Expression>,
198}
199
200impl<'a> DfsIter<'a> {
201 pub fn new(root: &'a Expression) -> Self {
203 Self { stack: vec![root] }
204 }
205}
206
207impl<'a> Iterator for DfsIter<'a> {
208 type Item = &'a Expression;
209
210 fn next(&mut self) -> Option<Self::Item> {
211 let expr = self.stack.pop()?;
212
213 let child_start = self.stack.len();
214 crate::ast_children::for_each_child(expr, |_, child| self.stack.push(child));
215 self.stack[child_start..].reverse();
216
217 Some(expr)
218 }
219}
220
221pub struct BfsIter<'a> {
229 queue: VecDeque<&'a Expression>,
230}
231
232impl<'a> BfsIter<'a> {
233 pub fn new(root: &'a Expression) -> Self {
235 let mut queue = VecDeque::new();
236 queue.push_back(root);
237 Self { queue }
238 }
239}
240
241impl<'a> Iterator for BfsIter<'a> {
242 type Item = &'a Expression;
243
244 fn next(&mut self) -> Option<Self::Item> {
245 let expr = self.queue.pop_front()?;
246
247 crate::ast_children::for_each_child(expr, |_, child| self.queue.push_back(child));
248
249 Some(expr)
250 }
251}
252
253pub trait ExpressionWalk {
259 fn dfs(&self) -> DfsIter<'_>;
264
265 fn bfs(&self) -> BfsIter<'_>;
269
270 fn find<F>(&self, predicate: F) -> Option<&Expression>
274 where
275 F: Fn(&Expression) -> bool;
276
277 fn find_all<F>(&self, predicate: F) -> Vec<&Expression>
281 where
282 F: Fn(&Expression) -> bool;
283
284 fn contains<F>(&self, predicate: F) -> bool
286 where
287 F: Fn(&Expression) -> bool;
288
289 fn count<F>(&self, predicate: F) -> usize
291 where
292 F: Fn(&Expression) -> bool;
293
294 fn children(&self) -> Vec<&Expression>;
299
300 fn tree_depth(&self) -> usize;
304
305 #[cfg(any(
311 feature = "transpile",
312 feature = "ast-tools",
313 feature = "generate",
314 feature = "semantic"
315 ))]
316 fn transform_owned<F>(self, fun: F) -> crate::Result<Expression>
317 where
318 F: Fn(Expression) -> crate::Result<Option<Expression>>,
319 Self: Sized;
320}
321
322impl ExpressionWalk for Expression {
323 fn dfs(&self) -> DfsIter<'_> {
324 DfsIter::new(self)
325 }
326
327 fn bfs(&self) -> BfsIter<'_> {
328 BfsIter::new(self)
329 }
330
331 fn find<F>(&self, predicate: F) -> Option<&Expression>
332 where
333 F: Fn(&Expression) -> bool,
334 {
335 self.dfs().find(|e| predicate(e))
336 }
337
338 fn find_all<F>(&self, predicate: F) -> Vec<&Expression>
339 where
340 F: Fn(&Expression) -> bool,
341 {
342 self.dfs().filter(|e| predicate(e)).collect()
343 }
344
345 fn contains<F>(&self, predicate: F) -> bool
346 where
347 F: Fn(&Expression) -> bool,
348 {
349 self.dfs().any(|e| predicate(e))
350 }
351
352 fn count<F>(&self, predicate: F) -> usize
353 where
354 F: Fn(&Expression) -> bool,
355 {
356 self.dfs().filter(|e| predicate(e)).count()
357 }
358
359 fn children(&self) -> Vec<&Expression> {
360 let mut result: Vec<&Expression> = Vec::new();
361 crate::ast_children::for_each_child(self, |_, child| result.push(child));
362 result
363 }
364
365 fn tree_depth(&self) -> usize {
366 let mut max_depth = 0usize;
367 let mut stack = vec![(self, 0usize)];
368 while let Some((node, depth)) = stack.pop() {
369 max_depth = max_depth.max(depth);
370 crate::ast_children::for_each_child(node, |_, child| {
371 stack.push((child, depth + 1));
372 });
373 }
374 max_depth
375 }
376
377 #[cfg(any(
378 feature = "transpile",
379 feature = "ast-tools",
380 feature = "generate",
381 feature = "semantic"
382 ))]
383 fn transform_owned<F>(self, fun: F) -> crate::Result<Expression>
384 where
385 F: Fn(Expression) -> crate::Result<Option<Expression>>,
386 {
387 transform(self, &fun)
388 }
389}
390
391#[cfg(any(
412 feature = "transpile",
413 feature = "ast-tools",
414 feature = "generate",
415 feature = "semantic"
416))]
417pub fn transform<F>(expr: Expression, fun: &F) -> crate::Result<Expression>
418where
419 F: Fn(Expression) -> crate::Result<Option<Expression>>,
420{
421 crate::dialects::transform_recursive(expr, &|e| match fun(e)? {
422 Some(transformed) => Ok(transformed),
423 None => Ok(Expression::Null(crate::expressions::Null)),
424 })
425}
426
427#[cfg(any(
448 feature = "transpile",
449 feature = "ast-tools",
450 feature = "generate",
451 feature = "semantic"
452))]
453pub fn transform_map<F>(expr: Expression, fun: &F) -> crate::Result<Expression>
454where
455 F: Fn(Expression) -> crate::Result<Expression>,
456{
457 crate::dialects::transform_recursive(expr, fun)
458}
459
460pub fn is_column(expr: &Expression) -> bool {
468 matches!(expr, Expression::Column(_))
469}
470
471pub fn is_literal(expr: &Expression) -> bool {
473 matches!(
474 expr,
475 Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_)
476 )
477}
478
479pub fn is_function(expr: &Expression) -> bool {
481 matches!(
482 expr,
483 Expression::Function(_) | Expression::AggregateFunction(_)
484 )
485}
486
487pub fn is_subquery(expr: &Expression) -> bool {
489 matches!(expr, Expression::Subquery(_))
490}
491
492pub fn is_select(expr: &Expression) -> bool {
494 matches!(expr, Expression::Select(_))
495}
496
497pub fn is_aggregate(expr: &Expression) -> bool {
503 matches!(
504 expr,
505 Expression::AggregateFunction(_)
506 | Expression::Count(_)
507 | Expression::Sum(_)
508 | Expression::Avg(_)
509 | Expression::Min(_)
510 | Expression::Max(_)
511 | Expression::GroupConcat(_)
512 | Expression::StringAgg(_)
513 | Expression::ListAgg(_)
514 | Expression::ArrayAgg(_)
515 | Expression::CountIf(_)
516 | Expression::SumIf(_)
517 | Expression::Stddev(_)
518 | Expression::StddevPop(_)
519 | Expression::StddevSamp(_)
520 | Expression::Variance(_)
521 | Expression::VarPop(_)
522 | Expression::VarSamp(_)
523 | Expression::Median(_)
524 | Expression::Mode(_)
525 | Expression::First(_)
526 | Expression::Last(_)
527 | Expression::AnyValue(_)
528 | Expression::ApproxDistinct(_)
529 | Expression::ApproxCountDistinct(_)
530 | Expression::ApproxPercentile(_)
531 | Expression::Percentile(_)
532 | Expression::PercentileCont(_)
533 | Expression::PercentileDisc(_)
534 | Expression::LogicalAnd(_)
535 | Expression::LogicalOr(_)
536 | Expression::Skewness(_)
537 | Expression::ArrayConcatAgg(_)
538 | Expression::ArrayUniqueAgg(_)
539 | Expression::BoolXorAgg(_)
540 | Expression::BitwiseAndAgg(_)
541 | Expression::BitwiseOrAgg(_)
542 | Expression::BitwiseXorAgg(_)
543 | Expression::JsonArrayAgg(_)
544 | Expression::JsonObjectAgg(_)
545 | Expression::JSONArrayAgg(_)
546 | Expression::JSONObjectAgg(_)
547 | Expression::JSONBObjectAgg(_)
548 | Expression::ParameterizedAgg(_)
549 | Expression::ArgMax(_)
550 | Expression::ArgMin(_)
551 | Expression::ApproxTopK(_)
552 | Expression::ApproxTopKAccumulate(_)
553 | Expression::ApproxTopKCombine(_)
554 | Expression::ApproxTopSum(_)
555 | Expression::ApproxQuantiles(_)
556 | Expression::Grouping(_)
557 | Expression::GroupingId(_)
558 | Expression::AnonymousAggFunc(_)
559 | Expression::CombinedAggFunc(_)
560 | Expression::CombinedParameterizedAgg(_)
561 | Expression::HashAgg(_)
562 | Expression::Hll(_)
563 | Expression::Minhash(_)
564 | Expression::ObjectAgg(_)
565 | Expression::AIAgg(_)
566 | Expression::Quantile(_)
567 | Expression::ApproxQuantile(_)
568 | Expression::Corr(_)
569 | Expression::CovarPop(_)
570 | Expression::CovarSamp(_)
571 | Expression::RegrValx(_)
572 | Expression::RegrValy(_)
573 | Expression::RegrAvgx(_)
574 | Expression::RegrAvgy(_)
575 | Expression::RegrCount(_)
576 | Expression::RegrIntercept(_)
577 | Expression::RegrR2(_)
578 | Expression::RegrSxx(_)
579 | Expression::RegrSxy(_)
580 | Expression::RegrSyy(_)
581 | Expression::RegrSlope(_)
582 )
583}
584
585pub fn is_window_function(expr: &Expression) -> bool {
587 matches!(expr, Expression::WindowFunction(_))
588}
589
590pub fn get_columns(expr: &Expression) -> Vec<&Expression> {
594 expr.find_all(is_column)
595}
596
597pub fn get_tables(expr: &Expression) -> Vec<&Expression> {
605 expr.find_all(|e| matches!(e, Expression::Table(_)))
606}
607
608pub fn get_all_tables(expr: &Expression) -> Vec<Expression> {
616 use std::collections::HashSet;
617
618 let mut seen = HashSet::new();
619 let mut result = Vec::new();
620
621 for node in expr.dfs() {
623 if let Expression::Table(t) = node {
624 let qname = table_ref_qualified_name(t);
625 if seen.insert(qname) {
626 result.push(node.clone());
627 }
628 }
629
630 let refs: Vec<&TableRef> = match node {
632 Expression::Insert(ins) => vec![&ins.table],
633 Expression::Update(upd) => {
634 let mut v = vec![&upd.table];
635 v.extend(upd.extra_tables.iter());
636 v
637 }
638 Expression::Delete(del) => {
639 let mut v = vec![&del.table];
640 v.extend(del.using.iter());
641 v
642 }
643 _ => continue,
644 };
645 for tref in refs {
646 if tref.name.name.is_empty() {
647 continue;
648 }
649 let qname = table_ref_qualified_name(tref);
650 if seen.insert(qname) {
651 result.push(Expression::Table(Box::new(tref.clone())));
652 }
653 }
654 }
655
656 result
657}
658
659fn table_ref_qualified_name(t: &TableRef) -> String {
661 let mut name = String::new();
662 if let Some(ref cat) = t.catalog {
663 name.push_str(&cat.name);
664 name.push('.');
665 }
666 if let Some(ref schema) = t.schema {
667 name.push_str(&schema.name);
668 name.push('.');
669 }
670 name.push_str(&t.name.name);
671 name
672}
673
674fn unwrap_merge_table(expr: &Expression) -> Option<&Expression> {
678 match expr {
679 Expression::Table(_) => Some(expr),
680 Expression::Alias(alias) => match &alias.this {
681 Expression::Table(_) => Some(&alias.this),
682 _ => None,
683 },
684 _ => None,
685 }
686}
687
688pub fn get_merge_target(expr: &Expression) -> Option<&Expression> {
693 match expr {
694 Expression::Merge(m) => unwrap_merge_table(&m.this),
695 _ => None,
696 }
697}
698
699pub fn get_merge_source(expr: &Expression) -> Option<&Expression> {
705 match expr {
706 Expression::Merge(m) => unwrap_merge_table(&m.using),
707 _ => None,
708 }
709}
710
711pub fn contains_aggregate(expr: &Expression) -> bool {
713 expr.contains(is_aggregate)
714}
715
716pub fn contains_window_function(expr: &Expression) -> bool {
718 expr.contains(is_window_function)
719}
720
721pub fn contains_subquery(expr: &Expression) -> bool {
723 expr.contains(is_subquery)
724}
725
726macro_rules! is_type {
732 ($name:ident, $($variant:pat),+ $(,)?) => {
733 pub fn $name(expr: &Expression) -> bool {
735 matches!(expr, $($variant)|+)
736 }
737 };
738}
739
740is_type!(is_insert, Expression::Insert(_));
742is_type!(is_update, Expression::Update(_));
743is_type!(is_delete, Expression::Delete(_));
744is_type!(is_merge, Expression::Merge(_));
745is_type!(is_union, Expression::Union(_));
746is_type!(is_intersect, Expression::Intersect(_));
747is_type!(is_except, Expression::Except(_));
748
749is_type!(is_boolean, Expression::Boolean(_));
751is_type!(is_null_literal, Expression::Null(_));
752is_type!(is_star, Expression::Star(_));
753is_type!(is_identifier, Expression::Identifier(_));
754is_type!(is_table, Expression::Table(_));
755
756is_type!(is_eq, Expression::Eq(_));
758is_type!(is_neq, Expression::Neq(_));
759is_type!(is_lt, Expression::Lt(_));
760is_type!(is_lte, Expression::Lte(_));
761is_type!(is_gt, Expression::Gt(_));
762is_type!(is_gte, Expression::Gte(_));
763is_type!(is_like, Expression::Like(_));
764is_type!(is_ilike, Expression::ILike(_));
765
766is_type!(is_add, Expression::Add(_));
768is_type!(is_sub, Expression::Sub(_));
769is_type!(is_mul, Expression::Mul(_));
770is_type!(is_div, Expression::Div(_));
771is_type!(is_mod, Expression::Mod(_));
772is_type!(is_concat, Expression::Concat(_));
773
774is_type!(is_and, Expression::And(_));
776is_type!(is_or, Expression::Or(_));
777is_type!(is_not, Expression::Not(_));
778
779is_type!(is_in, Expression::In(_));
781is_type!(is_between, Expression::Between(_));
782is_type!(is_is_null, Expression::IsNull(_));
783is_type!(is_exists, Expression::Exists(_));
784
785is_type!(is_count, Expression::Count(_));
787is_type!(is_sum, Expression::Sum(_));
788is_type!(is_avg, Expression::Avg(_));
789is_type!(is_min_func, Expression::Min(_));
790is_type!(is_max_func, Expression::Max(_));
791is_type!(is_coalesce, Expression::Coalesce(_));
792is_type!(is_null_if, Expression::NullIf(_));
793is_type!(is_cast, Expression::Cast(_));
794is_type!(is_try_cast, Expression::TryCast(_));
795is_type!(is_safe_cast, Expression::SafeCast(_));
796is_type!(is_case, Expression::Case(_));
797
798is_type!(is_from, Expression::From(_));
800is_type!(is_join, Expression::Join(_));
801is_type!(is_where, Expression::Where(_));
802is_type!(is_group_by, Expression::GroupBy(_));
803is_type!(is_having, Expression::Having(_));
804is_type!(is_order_by, Expression::OrderBy(_));
805is_type!(is_limit, Expression::Limit(_));
806is_type!(is_offset, Expression::Offset(_));
807is_type!(is_with, Expression::With(_));
808is_type!(is_cte, Expression::Cte(_));
809is_type!(is_alias, Expression::Alias(_));
810is_type!(is_paren, Expression::Paren(_));
811is_type!(is_ordered, Expression::Ordered(_));
812
813is_type!(is_create_table, Expression::CreateTable(_));
815is_type!(is_drop_table, Expression::DropTable(_));
816is_type!(is_alter_table, Expression::AlterTable(_));
817is_type!(is_create_index, Expression::CreateIndex(_));
818is_type!(is_drop_index, Expression::DropIndex(_));
819is_type!(is_create_view, Expression::CreateView(_));
820is_type!(is_drop_view, Expression::DropView(_));
821
822pub fn is_query(expr: &Expression) -> bool {
828 matches!(
829 expr,
830 Expression::Select(_)
831 | Expression::Insert(_)
832 | Expression::Update(_)
833 | Expression::Delete(_)
834 | Expression::Merge(_)
835 )
836}
837
838pub fn is_set_operation(expr: &Expression) -> bool {
840 matches!(
841 expr,
842 Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
843 )
844}
845
846pub fn is_comparison(expr: &Expression) -> bool {
848 matches!(
849 expr,
850 Expression::Eq(_)
851 | Expression::Neq(_)
852 | Expression::Lt(_)
853 | Expression::Lte(_)
854 | Expression::Gt(_)
855 | Expression::Gte(_)
856 | Expression::Like(_)
857 | Expression::ILike(_)
858 )
859}
860
861pub fn is_arithmetic(expr: &Expression) -> bool {
863 matches!(
864 expr,
865 Expression::Add(_)
866 | Expression::Sub(_)
867 | Expression::Mul(_)
868 | Expression::Div(_)
869 | Expression::Mod(_)
870 )
871}
872
873pub fn is_logical(expr: &Expression) -> bool {
875 matches!(
876 expr,
877 Expression::And(_) | Expression::Or(_) | Expression::Not(_)
878 )
879}
880
881pub fn is_ddl(expr: &Expression) -> bool {
883 matches!(
884 expr,
885 Expression::CreateTable(_)
886 | Expression::DropTable(_)
887 | Expression::Undrop(_)
888 | Expression::AlterTable(_)
889 | Expression::CreateIndex(_)
890 | Expression::DropIndex(_)
891 | Expression::CreateView(_)
892 | Expression::DropView(_)
893 | Expression::AlterView(_)
894 | Expression::CreateSchema(_)
895 | Expression::DropSchema(_)
896 | Expression::CreateDatabase(_)
897 | Expression::DropDatabase(_)
898 | Expression::CreateFunction(_)
899 | Expression::DropFunction(_)
900 | Expression::CreateProcedure(_)
901 | Expression::DropProcedure(_)
902 | Expression::CreateSequence(_)
903 | Expression::CreateSynonym(_)
904 | Expression::DropSequence(_)
905 | Expression::AlterSequence(_)
906 | Expression::CreateTrigger(_)
907 | Expression::DropTrigger(_)
908 | Expression::CreateType(_)
909 | Expression::DropType(_)
910 )
911}
912
913pub fn find_parent<'a>(root: &'a Expression, target: &Expression) -> Option<&'a Expression> {
920 fn search<'a>(node: &'a Expression, target: *const Expression) -> Option<&'a Expression> {
921 let mut result = None;
922 crate::ast_children::for_each_child(node, |_, child| {
923 if result.is_none() {
924 if std::ptr::eq(child, target) {
925 result = Some(node);
926 } else {
927 result = search(child, target);
928 }
929 }
930 });
931 result
932 }
933
934 search(root, target as *const Expression)
935}
936
937pub fn find_ancestor<'a, F>(
943 root: &'a Expression,
944 target: &Expression,
945 predicate: F,
946) -> Option<&'a Expression>
947where
948 F: Fn(&Expression) -> bool,
949{
950 fn build_path<'a>(
952 node: &'a Expression,
953 target: *const Expression,
954 path: &mut Vec<&'a Expression>,
955 ) -> bool {
956 if std::ptr::eq(node, target) {
957 return true;
958 }
959 path.push(node);
960 let mut found = false;
961 crate::ast_children::for_each_child(node, |_, child| {
962 if !found {
963 found = build_path(child, target, path);
964 }
965 });
966 if found {
967 return true;
968 }
969 path.pop();
970 false
971 }
972
973 let mut path = Vec::new();
974 if !build_path(root, target as *const Expression, &mut path) {
975 return None;
976 }
977
978 for ancestor in path.iter().rev() {
980 if predicate(ancestor) {
981 return Some(ancestor);
982 }
983 }
984 None
985}
986
987#[cfg(test)]
988mod tests {
989 use super::*;
990 use crate::expressions::{BinaryOp, Column, Identifier, LikeOp, Literal, TableRef};
991
992 fn make_column(name: &str) -> Expression {
993 Expression::boxed_column(Column {
994 name: Identifier {
995 name: name.to_string(),
996 quoted: false,
997 trailing_comments: vec![],
998 span: None,
999 },
1000 table: None,
1001 join_mark: false,
1002 trailing_comments: vec![],
1003 span: None,
1004 inferred_type: None,
1005 })
1006 }
1007
1008 fn make_literal(value: i64) -> Expression {
1009 Expression::Literal(Box::new(Literal::Number(value.to_string())))
1010 }
1011
1012 #[test]
1013 fn test_dfs_simple() {
1014 let left = make_column("a");
1015 let right = make_literal(1);
1016 let expr = Expression::Eq(Box::new(BinaryOp {
1017 left,
1018 right,
1019 left_comments: vec![],
1020 operator_comments: vec![],
1021 trailing_comments: vec![],
1022 inferred_type: None,
1023 }));
1024
1025 let nodes: Vec<_> = expr.dfs().collect();
1026 assert_eq!(nodes.len(), 3); assert!(matches!(nodes[0], Expression::Eq(_)));
1028 assert!(matches!(nodes[1], Expression::Column(_)));
1029 assert!(matches!(nodes[2], Expression::Literal(_)));
1030 }
1031
1032 #[test]
1033 fn test_find() {
1034 let left = make_column("a");
1035 let right = make_literal(1);
1036 let expr = Expression::Eq(Box::new(BinaryOp {
1037 left,
1038 right,
1039 left_comments: vec![],
1040 operator_comments: vec![],
1041 trailing_comments: vec![],
1042 inferred_type: None,
1043 }));
1044
1045 let column = expr.find(is_column);
1046 assert!(column.is_some());
1047 assert!(matches!(column.unwrap(), Expression::Column(_)));
1048
1049 let literal = expr.find(is_literal);
1050 assert!(literal.is_some());
1051 assert!(matches!(literal.unwrap(), Expression::Literal(_)));
1052 }
1053
1054 #[test]
1055 fn test_find_all() {
1056 let col1 = make_column("a");
1057 let col2 = make_column("b");
1058 let expr = Expression::And(Box::new(BinaryOp {
1059 left: col1,
1060 right: col2,
1061 left_comments: vec![],
1062 operator_comments: vec![],
1063 trailing_comments: vec![],
1064 inferred_type: None,
1065 }));
1066
1067 let columns = expr.find_all(is_column);
1068 assert_eq!(columns.len(), 2);
1069 }
1070
1071 #[test]
1072 fn test_contains() {
1073 let col = make_column("a");
1074 let lit = make_literal(1);
1075 let expr = Expression::Eq(Box::new(BinaryOp {
1076 left: col,
1077 right: lit,
1078 left_comments: vec![],
1079 operator_comments: vec![],
1080 trailing_comments: vec![],
1081 inferred_type: None,
1082 }));
1083
1084 assert!(expr.contains(is_column));
1085 assert!(expr.contains(is_literal));
1086 assert!(!expr.contains(is_subquery));
1087 }
1088
1089 #[test]
1090 fn test_count() {
1091 let col1 = make_column("a");
1092 let col2 = make_column("b");
1093 let lit = make_literal(1);
1094
1095 let inner = Expression::Add(Box::new(BinaryOp {
1096 left: col2,
1097 right: lit,
1098 left_comments: vec![],
1099 operator_comments: vec![],
1100 trailing_comments: vec![],
1101 inferred_type: None,
1102 }));
1103
1104 let expr = Expression::Eq(Box::new(BinaryOp {
1105 left: col1,
1106 right: inner,
1107 left_comments: vec![],
1108 operator_comments: vec![],
1109 trailing_comments: vec![],
1110 inferred_type: None,
1111 }));
1112
1113 assert_eq!(expr.count(is_column), 2);
1114 assert_eq!(expr.count(is_literal), 1);
1115 }
1116
1117 #[test]
1118 fn test_tree_depth() {
1119 let lit = make_literal(1);
1121 assert_eq!(lit.tree_depth(), 0);
1122
1123 let col = make_column("a");
1125 let expr = Expression::Eq(Box::new(BinaryOp {
1126 left: col,
1127 right: lit.clone(),
1128 left_comments: vec![],
1129 operator_comments: vec![],
1130 trailing_comments: vec![],
1131 inferred_type: None,
1132 }));
1133 assert_eq!(expr.tree_depth(), 1);
1134
1135 let inner = Expression::Add(Box::new(BinaryOp {
1137 left: make_column("b"),
1138 right: lit,
1139 left_comments: vec![],
1140 operator_comments: vec![],
1141 trailing_comments: vec![],
1142 inferred_type: None,
1143 }));
1144 let outer = Expression::Eq(Box::new(BinaryOp {
1145 left: make_column("a"),
1146 right: inner,
1147 left_comments: vec![],
1148 operator_comments: vec![],
1149 trailing_comments: vec![],
1150 inferred_type: None,
1151 }));
1152 assert_eq!(outer.tree_depth(), 2);
1153 }
1154
1155 #[test]
1156 fn test_tree_context() {
1157 let col = make_column("a");
1158 let lit = make_literal(1);
1159 let expr = Expression::Eq(Box::new(BinaryOp {
1160 left: col,
1161 right: lit,
1162 left_comments: vec![],
1163 operator_comments: vec![],
1164 trailing_comments: vec![],
1165 inferred_type: None,
1166 }));
1167
1168 let ctx = TreeContext::build(&expr);
1169
1170 let root_info = ctx.get(0).unwrap();
1172 assert!(root_info.parent_id.is_none());
1173
1174 let left_info = ctx.get(1).unwrap();
1176 assert_eq!(left_info.parent_id, Some(0));
1177 assert_eq!(left_info.arg_key, "left");
1178
1179 let right_info = ctx.get(2).unwrap();
1180 assert_eq!(right_info.parent_id, Some(0));
1181 assert_eq!(right_info.arg_key, "right");
1182 }
1183
1184 #[test]
1187 fn test_transform_rename_columns() {
1188 let ast = crate::parser::Parser::parse_sql("SELECT a, b FROM t").unwrap();
1189 let expr = ast[0].clone();
1190 let result = super::transform_map(expr, &|e| {
1191 if let Expression::Column(ref c) = e {
1192 if c.name.name == "a" {
1193 return Ok(Expression::boxed_column(Column {
1194 name: Identifier::new("alpha"),
1195 table: c.table.clone(),
1196 join_mark: false,
1197 trailing_comments: vec![],
1198 span: None,
1199 inferred_type: None,
1200 }));
1201 }
1202 }
1203 Ok(e)
1204 })
1205 .unwrap();
1206 let sql = crate::generator::Generator::sql(&result).unwrap();
1207 assert!(sql.contains("alpha"), "Expected 'alpha' in: {}", sql);
1208 assert!(sql.contains("b"), "Expected 'b' in: {}", sql);
1209 }
1210
1211 #[test]
1212 fn test_transform_noop() {
1213 let ast = crate::parser::Parser::parse_sql("SELECT 1 + 2").unwrap();
1214 let expr = ast[0].clone();
1215 let result = super::transform_map(expr.clone(), &|e| Ok(e)).unwrap();
1216 let sql1 = crate::generator::Generator::sql(&expr).unwrap();
1217 let sql2 = crate::generator::Generator::sql(&result).unwrap();
1218 assert_eq!(sql1, sql2);
1219 }
1220
1221 #[test]
1222 fn test_transform_nested() {
1223 let ast = crate::parser::Parser::parse_sql("SELECT a + b FROM t").unwrap();
1224 let expr = ast[0].clone();
1225 let result = super::transform_map(expr, &|e| {
1226 if let Expression::Column(ref c) = e {
1227 return Ok(Expression::Literal(Box::new(Literal::Number(
1228 if c.name.name == "a" { "1" } else { "2" }.to_string(),
1229 ))));
1230 }
1231 Ok(e)
1232 })
1233 .unwrap();
1234 let sql = crate::generator::Generator::sql(&result).unwrap();
1235 assert_eq!(sql, "SELECT 1 + 2 FROM t");
1236 }
1237
1238 #[test]
1239 fn test_transform_error() {
1240 let ast = crate::parser::Parser::parse_sql("SELECT a FROM t").unwrap();
1241 let expr = ast[0].clone();
1242 let result = super::transform_map(expr, &|e| {
1243 if let Expression::Column(ref c) = e {
1244 if c.name.name == "a" {
1245 return Err(crate::error::Error::parse("test error", 0, 0, 0, 0));
1246 }
1247 }
1248 Ok(e)
1249 });
1250 assert!(result.is_err());
1251 }
1252
1253 #[test]
1254 fn test_transform_owned_trait() {
1255 let ast = crate::parser::Parser::parse_sql("SELECT x FROM t").unwrap();
1256 let expr = ast[0].clone();
1257 let result = expr.transform_owned(|e| Ok(Some(e))).unwrap();
1258 let sql = crate::generator::Generator::sql(&result).unwrap();
1259 assert_eq!(sql, "SELECT x FROM t");
1260 }
1261
1262 #[test]
1265 fn test_children_leaf() {
1266 let lit = make_literal(1);
1267 assert_eq!(lit.children().len(), 0);
1268 }
1269
1270 #[test]
1271 fn test_children_binary_op() {
1272 let left = make_column("a");
1273 let right = make_literal(1);
1274 let expr = Expression::Eq(Box::new(BinaryOp {
1275 left,
1276 right,
1277 left_comments: vec![],
1278 operator_comments: vec![],
1279 trailing_comments: vec![],
1280 inferred_type: None,
1281 }));
1282 let children = expr.children();
1283 assert_eq!(children.len(), 2);
1284 assert!(matches!(children[0], Expression::Column(_)));
1285 assert!(matches!(children[1], Expression::Literal(_)));
1286 }
1287
1288 #[test]
1289 fn test_children_select() {
1290 let ast = crate::parser::Parser::parse_sql("SELECT a, b FROM t").unwrap();
1291 let expr = &ast[0];
1292 let children = expr.children();
1293 assert!(children.len() >= 2);
1295 }
1296
1297 #[test]
1298 fn test_children_follow_ast_field_order() {
1299 let ast = crate::parser::Parser::parse_sql("SELECT a, b FROM t").unwrap();
1300 let children = ast[0].children();
1301
1302 assert!(matches!(children[0], Expression::Column(column) if column.name.name == "a"));
1303 assert!(matches!(children[1], Expression::Column(column) if column.name.name == "b"));
1304 assert!(matches!(children[2], Expression::Table(table) if table.name.name == "t"));
1305 }
1306
1307 #[test]
1308 fn test_traversal_covers_previously_omitted_expression_fields() {
1309 let like = Expression::Like(Box::new(LikeOp {
1310 left: make_column("name"),
1311 right: Expression::Literal(Box::new(Literal::String("x%".to_string()))),
1312 escape: Some(Expression::Literal(Box::new(Literal::String(
1313 "!".to_string(),
1314 )))),
1315 quantifier: None,
1316 inferred_type: None,
1317 }));
1318 let nodes: Vec<_> = like.dfs().collect();
1319 assert_eq!(nodes.len(), 4);
1320 assert!(matches!(
1321 nodes[3],
1322 Expression::Literal(literal)
1323 if matches!(literal.as_ref(), Literal::String(value) if value == "!")
1324 ));
1325
1326 let mut table = TableRef::new("events");
1327 table.hints.push(make_column("table_hint"));
1328 table.identifier_func = Some(Box::new(Expression::identifier("dynamic_table")));
1329 let table = Expression::Table(Box::new(table));
1330 let children = table.children();
1331 assert_eq!(children.len(), 2);
1332 assert!(
1333 matches!(children[0], Expression::Column(column) if column.name.name == "table_hint")
1334 );
1335 assert!(
1336 matches!(children[1], Expression::Identifier(identifier) if identifier.name == "dynamic_table")
1337 );
1338 }
1339
1340 #[test]
1341 fn test_children_select_includes_from_and_join_sources() {
1342 let ast = crate::parser::Parser::parse_sql(
1343 "SELECT u.id FROM users u JOIN orders o ON u.id = o.user_id",
1344 )
1345 .unwrap();
1346 let expr = &ast[0];
1347 let children = expr.children();
1348
1349 let table_names: Vec<&str> = children
1350 .iter()
1351 .filter_map(|e| match e {
1352 Expression::Table(t) => Some(t.name.name.as_str()),
1353 _ => None,
1354 })
1355 .collect();
1356
1357 assert!(table_names.contains(&"users"));
1358 assert!(table_names.contains(&"orders"));
1359 }
1360
1361 #[test]
1362 fn test_get_tables_includes_insert_query_sources() {
1363 let ast = crate::parser::Parser::parse_sql(
1364 "INSERT INTO dst (id) SELECT s.id FROM src s JOIN dim d ON s.id = d.id",
1365 )
1366 .unwrap();
1367 let expr = &ast[0];
1368 let tables = get_tables(expr);
1369 let names: Vec<&str> = tables
1370 .iter()
1371 .filter_map(|e| match e {
1372 Expression::Table(t) => Some(t.name.name.as_str()),
1373 _ => None,
1374 })
1375 .collect();
1376
1377 assert!(names.contains(&"src"));
1378 assert!(names.contains(&"dim"));
1379 }
1380
1381 #[test]
1384 fn test_find_parent_binary() {
1385 let left = make_column("a");
1386 let right = make_literal(1);
1387 let expr = Expression::Eq(Box::new(BinaryOp {
1388 left,
1389 right,
1390 left_comments: vec![],
1391 operator_comments: vec![],
1392 trailing_comments: vec![],
1393 inferred_type: None,
1394 }));
1395
1396 let col = expr.find(is_column).unwrap();
1398 let parent = super::find_parent(&expr, col);
1399 assert!(parent.is_some());
1400 assert!(matches!(parent.unwrap(), Expression::Eq(_)));
1401 }
1402
1403 #[test]
1404 fn test_find_parent_root_has_none() {
1405 let lit = make_literal(1);
1406 let parent = super::find_parent(&lit, &lit);
1407 assert!(parent.is_none());
1408 }
1409
1410 #[test]
1413 fn test_find_ancestor_select() {
1414 let ast = crate::parser::Parser::parse_sql("SELECT a FROM t WHERE a > 1").unwrap();
1415 let expr = &ast[0];
1416
1417 let where_col = expr.dfs().find(|e| {
1419 if let Expression::Column(c) = e {
1420 c.name.name == "a"
1421 } else {
1422 false
1423 }
1424 });
1425 assert!(where_col.is_some());
1426
1427 let ancestor = super::find_ancestor(expr, where_col.unwrap(), is_select);
1429 assert!(ancestor.is_some());
1430 assert!(matches!(ancestor.unwrap(), Expression::Select(_)));
1431 }
1432
1433 #[test]
1434 fn test_find_ancestor_no_match() {
1435 let left = make_column("a");
1436 let right = make_literal(1);
1437 let expr = Expression::Eq(Box::new(BinaryOp {
1438 left,
1439 right,
1440 left_comments: vec![],
1441 operator_comments: vec![],
1442 trailing_comments: vec![],
1443 inferred_type: None,
1444 }));
1445
1446 let col = expr.find(is_column).unwrap();
1447 let ancestor = super::find_ancestor(&expr, col, is_select);
1448 assert!(ancestor.is_none());
1449 }
1450
1451 #[test]
1452 fn test_ancestors() {
1453 let col = make_column("a");
1454 let lit = make_literal(1);
1455 let inner = Expression::Add(Box::new(BinaryOp {
1456 left: col,
1457 right: lit,
1458 left_comments: vec![],
1459 operator_comments: vec![],
1460 trailing_comments: vec![],
1461 inferred_type: None,
1462 }));
1463 let outer = Expression::Eq(Box::new(BinaryOp {
1464 left: make_column("b"),
1465 right: inner,
1466 left_comments: vec![],
1467 operator_comments: vec![],
1468 trailing_comments: vec![],
1469 inferred_type: None,
1470 }));
1471
1472 let ctx = TreeContext::build(&outer);
1473
1474 let ancestors = ctx.ancestors_of(3);
1482 assert_eq!(ancestors, vec![2, 0]); }
1484
1485 #[test]
1486 fn test_get_merge_target_and_source() {
1487 let dialect = crate::Dialect::get(crate::dialects::DialectType::Generic);
1488
1489 let sql = "MERGE INTO orders o USING customers c ON o.customer_id = c.id WHEN MATCHED THEN UPDATE SET amount = amount + 100";
1491 let exprs = dialect.parse(sql).unwrap();
1492 let expr = &exprs[0];
1493
1494 assert!(is_merge(expr));
1495 assert!(is_query(expr));
1496
1497 let target = get_merge_target(expr).expect("should find target table");
1498 assert!(matches!(target, Expression::Table(_)));
1499 if let Expression::Table(t) = target {
1500 assert_eq!(t.name.name, "orders");
1501 }
1502
1503 let source = get_merge_source(expr).expect("should find source table");
1504 assert!(matches!(source, Expression::Table(_)));
1505 if let Expression::Table(t) = source {
1506 assert_eq!(t.name.name, "customers");
1507 }
1508 }
1509
1510 #[test]
1511 fn test_get_merge_source_subquery_returns_none() {
1512 let dialect = crate::Dialect::get(crate::dialects::DialectType::Generic);
1513
1514 let sql = "MERGE INTO orders o USING (SELECT * FROM customers) c ON o.customer_id = c.id WHEN MATCHED THEN DELETE";
1516 let exprs = dialect.parse(sql).unwrap();
1517 let expr = &exprs[0];
1518
1519 assert!(get_merge_target(expr).is_some());
1520 assert!(get_merge_source(expr).is_none());
1521 }
1522
1523 #[test]
1524 fn test_get_merge_on_non_merge_returns_none() {
1525 let dialect = crate::Dialect::get(crate::dialects::DialectType::Generic);
1526 let exprs = dialect.parse("SELECT 1").unwrap();
1527 assert!(get_merge_target(&exprs[0]).is_none());
1528 assert!(get_merge_source(&exprs[0]).is_none());
1529 }
1530
1531 #[test]
1532 fn test_get_tables_finds_tables_inside_in_subquery() {
1533 let dialect = crate::Dialect::get(crate::dialects::DialectType::Generic);
1534 let sql = "SELECT id, name FROM customers WHERE id IN (SELECT customer_id FROM orders WHERE amount > 1000)";
1535 let exprs = dialect.parse(sql).unwrap();
1536 let tables = get_tables(&exprs[0]);
1537 let names: Vec<&str> = tables
1538 .iter()
1539 .filter_map(|e| {
1540 if let Expression::Table(t) = e {
1541 Some(t.name.name.as_str())
1542 } else {
1543 None
1544 }
1545 })
1546 .collect();
1547 assert!(names.contains(&"customers"), "should find outer table");
1548 assert!(names.contains(&"orders"), "should find subquery table");
1549 }
1550
1551 #[test]
1552 fn test_get_tables_finds_tables_inside_exists_subquery() {
1553 let dialect = crate::Dialect::get(crate::dialects::DialectType::Generic);
1554 let sql = "SELECT * FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id)";
1555 let exprs = dialect.parse(sql).unwrap();
1556 let tables = get_tables(&exprs[0]);
1557 let names: Vec<&str> = tables
1558 .iter()
1559 .filter_map(|e| {
1560 if let Expression::Table(t) = e {
1561 Some(t.name.name.as_str())
1562 } else {
1563 None
1564 }
1565 })
1566 .collect();
1567 assert!(names.contains(&"customers"), "should find outer table");
1568 assert!(
1569 names.contains(&"orders"),
1570 "should find EXISTS subquery table"
1571 );
1572 }
1573
1574 #[test]
1575 fn test_get_tables_finds_tables_in_correlated_subquery() {
1576 let dialect = crate::Dialect::get(crate::dialects::DialectType::TSQL);
1577 let sql = "SELECT id, name FROM customers WHERE id IN (SELECT customer_id FROM orders WHERE amount > 1000)";
1578 let exprs = dialect.parse(sql).unwrap();
1579 let tables = get_tables(&exprs[0]);
1580 let names: Vec<&str> = tables
1581 .iter()
1582 .filter_map(|e| {
1583 if let Expression::Table(t) = e {
1584 Some(t.name.name.as_str())
1585 } else {
1586 None
1587 }
1588 })
1589 .collect();
1590 assert!(
1591 names.contains(&"customers"),
1592 "TSQL: should find outer table"
1593 );
1594 assert!(
1595 names.contains(&"orders"),
1596 "TSQL: should find subquery table"
1597 );
1598 }
1599}