1use std::collections::{HashMap, HashSet};
11
12use crate::ast_mutation as engine;
13use crate::expressions::*;
14use crate::traversal::{is_aggregate, ExpressionWalk};
15
16#[cfg(feature = "generate")]
18#[derive(Default)]
19pub(crate) struct AstNames(HashSet<String>);
20
21#[cfg(feature = "generate")]
22impl AstNames {
23 pub(crate) fn collect(&mut self, expression: &Expression) {
24 for node in expression.dfs() {
25 match node {
26 Expression::Column(c) => {
27 self.reserve(&c.name);
28 if let Some(table) = &c.table {
29 self.reserve(table);
30 }
31 }
32 Expression::Identifier(id) => self.reserve(id),
33 Expression::Alias(a) => {
34 self.reserve(&a.alias);
35 for id in &a.column_aliases {
36 self.reserve(id);
37 }
38 }
39 Expression::Table(t) => {
40 self.reserve(&t.name);
41 if let Some(alias) = &t.alias {
42 self.reserve(alias);
43 }
44 }
45 Expression::Subquery(s) => {
46 if let Some(alias) = &s.alias {
47 self.reserve(alias);
48 }
49 for id in &s.column_aliases {
50 self.reserve(id);
51 }
52 }
53 _ => {}
54 }
55 }
56 }
57
58 fn reserve(&mut self, id: &Identifier) {
59 self.0.insert(id.name.to_ascii_lowercase());
60 }
61
62 pub(crate) fn fresh(&mut self, stem: &str) -> String {
63 let mut name = stem.to_string();
64 let mut suffix = 0;
65 while !self.0.insert(name.to_ascii_lowercase()) {
66 suffix += 1;
67 name = format!("{stem}_{suffix}");
68 }
69 name
70 }
71}
72
73fn xform<F: Fn(Expression) -> Expression>(expr: Expression, fun: F) -> Expression {
76 crate::traversal::transform_all(expr, &|node| {
77 transform_embedded_wrappers(node, &fun).map(&fun)
78 })
79 .unwrap_or_else(|_| Expression::Null(Null))
80}
81
82fn transform_embedded_wrappers<F: Fn(Expression) -> Expression>(
86 expr: Expression,
87 fun: &F,
88) -> crate::Result<Expression> {
89 macro_rules! boxed_wrapper {
90 ($value:expr, $variant:ident) => {
91 match fun(Expression::$variant(Box::new($value))) {
92 Expression::$variant(value) => *value,
93 _ => {
94 return Err(crate::Error::Internal(
95 concat!(
96 stringify!($variant),
97 " transformation returned a different node type"
98 )
99 .to_string(),
100 ))
101 }
102 }
103 };
104 }
105 let table = |table: TableRef| -> crate::Result<TableRef> {
106 let node = transform_embedded_wrappers(Expression::Table(Box::new(table)), fun)?;
108 match fun(node) {
109 Expression::Table(table) => Ok(*table),
110 _ => Err(crate::Error::Internal(
111 "Table transformation returned a different node type".into(),
112 )),
113 }
114 };
115 let joins = |joins: Vec<Join>| -> crate::Result<Vec<Join>> {
116 joins
117 .into_iter()
118 .map(|join| Ok(boxed_wrapper!(join, Join)))
119 .collect()
120 };
121 let ordered = |items: Vec<Ordered>| -> Vec<Ordered> {
122 items
123 .into_iter()
124 .map(
125 |item| match fun(Expression::Ordered(Box::new(item.clone()))) {
126 Expression::Ordered(item) => *item,
127 _ => item,
128 },
129 )
130 .collect()
131 };
132 let order_by = |mut order: OrderBy| {
133 order.expressions = ordered(order.expressions);
134 order
135 };
136 Ok(match expr {
137 Expression::Select(mut select) => {
138 select.joins = joins(select.joins)?;
139 select.order_by = select.order_by.map(order_by);
140 if let Some(windows) = &mut select.windows {
141 for window in windows {
142 window.spec.order_by = ordered(std::mem::take(&mut window.spec.order_by));
143 }
144 }
145 Expression::Select(select)
146 }
147 Expression::Update(mut update) => {
148 update.table = table(update.table)?;
149 update.extra_tables = update
150 .extra_tables
151 .into_iter()
152 .map(table)
153 .collect::<crate::Result<_>>()?;
154 update.table_joins = joins(update.table_joins)?;
155 if let Some(from) = update.from_clause.take() {
156 update.from_clause = Some(boxed_wrapper!(from, From));
157 }
158 update.from_joins = joins(update.from_joins)?;
159 update.order_by = update.order_by.map(order_by);
160 Expression::Update(update)
161 }
162 Expression::Delete(mut delete) => {
163 delete.table = table(delete.table)?;
164 delete.using = delete
165 .using
166 .into_iter()
167 .map(table)
168 .collect::<crate::Result<_>>()?;
169 delete.tables = delete
170 .tables
171 .into_iter()
172 .map(table)
173 .collect::<crate::Result<_>>()?;
174 delete.joins = joins(delete.joins)?;
175 delete.order_by = delete.order_by.map(order_by);
176 Expression::Delete(delete)
177 }
178 Expression::Upsert(mut upsert) => {
179 upsert.table = table(upsert.table)?;
180 Expression::Upsert(upsert)
181 }
182 Expression::JoinedTable(mut joined) => {
183 joined.joins = joins(joined.joins)?;
184 Expression::JoinedTable(joined)
185 }
186 Expression::WindowFunction(mut window) => {
187 window.over.order_by = ordered(window.over.order_by);
188 Expression::WindowFunction(window)
189 }
190 Expression::WithinGroup(mut within) => {
191 within.order_by = ordered(within.order_by);
192 Expression::WithinGroup(within)
193 }
194 Expression::StringAgg(mut agg) => {
195 agg.order_by = agg.order_by.map(ordered);
196 Expression::StringAgg(agg)
197 }
198 Expression::Union(mut union) => {
199 union.order_by = union.order_by.take().map(order_by);
200 Expression::Union(union)
201 }
202 Expression::Intersect(mut intersect) => {
203 intersect.order_by = intersect.order_by.take().map(order_by);
204 Expression::Intersect(intersect)
205 }
206 Expression::Except(mut except) => {
207 except.order_by = except.order_by.take().map(order_by);
208 Expression::Except(except)
209 }
210 Expression::Cast(mut cast) => {
211 cast.to =
212 crate::dialects::transform_data_type_recursive(cast.to, &|node| Ok(fun(node)))?;
213 Expression::Cast(cast)
214 }
215 Expression::TryCast(mut cast) => {
216 cast.to =
217 crate::dialects::transform_data_type_recursive(cast.to, &|node| Ok(fun(node)))?;
218 Expression::TryCast(cast)
219 }
220 Expression::SafeCast(mut cast) => {
221 cast.to =
222 crate::dialects::transform_data_type_recursive(cast.to, &|node| Ok(fun(node)))?;
223 Expression::SafeCast(cast)
224 }
225 Expression::Table(mut table) => {
226 if let Some(when) = table.when.take() {
227 if let Expression::HistoricalData(when) = fun(Expression::HistoricalData(when)) {
228 table.when = Some(when);
229 }
230 }
231 if let Some(changes) = table.changes.take() {
232 if let Expression::Changes(changes) = fun(Expression::Changes(changes)) {
233 table.changes = Some(changes);
234 }
235 }
236 Expression::Table(table)
237 }
238 other => other,
239 })
240}
241
242pub fn add_select_columns(expr: Expression, columns: Vec<Expression>) -> Expression {
251 let mut expression = expr;
252 let _ = engine::append_select(&mut expression, columns, true);
253 expression
254}
255
256pub fn remove_select_columns<F: Fn(&Expression) -> bool>(
258 expr: Expression,
259 predicate: F,
260) -> Expression {
261 if let Expression::Select(mut sel) = expr {
262 sel.expressions.retain(|e| !predicate(e));
263 Expression::Select(sel)
264 } else {
265 expr
266 }
267}
268
269pub fn set_distinct(expr: Expression, distinct: bool) -> Expression {
271 let mut expression = expr;
272 let _ = engine::apply_distinct(&mut expression, distinct);
273 expression
274}
275
276pub fn add_where(expr: Expression, condition: Expression, use_or: bool) -> Expression {
286 if !use_or {
287 if !matches!(expr, Expression::Select(_)) {
288 return expr;
289 }
290 let mut expression = expr;
291 let _ = engine::apply_where(&mut expression, condition, true);
292 return expression;
293 }
294 if let Expression::Select(mut sel) = expr {
295 sel.where_clause = Some(match sel.where_clause.take() {
296 Some(existing) => {
297 let combined = if use_or {
298 Expression::Or(Box::new(BinaryOp::new(existing.this, condition)))
299 } else {
300 Expression::And(Box::new(BinaryOp::new(existing.this, condition)))
301 };
302 Where { this: combined }
303 }
304 None => Where { this: condition },
305 });
306 Expression::Select(sel)
307 } else {
308 expr
309 }
310}
311
312pub fn remove_where(expr: Expression) -> Expression {
314 if let Expression::Select(mut sel) = expr {
315 sel.where_clause = None;
316 Expression::Select(sel)
317 } else {
318 expr
319 }
320}
321
322pub fn set_limit(expr: Expression, limit: usize) -> Expression {
328 set_limit_expr(expr, Expression::number(limit as i64))
329}
330
331pub fn set_limit_expr(expr: Expression, limit: Expression) -> Expression {
333 let mut expression = expr;
334 let _ = engine::apply_limit(&mut expression, limit);
335 expression
336}
337
338pub fn set_offset(expr: Expression, offset: usize) -> Expression {
340 set_offset_expr(expr, Expression::number(offset as i64))
341}
342
343pub fn set_offset_expr(expr: Expression, offset: Expression) -> Expression {
345 let mut expression = expr;
346 let _ = engine::apply_offset(&mut expression, offset);
347 expression
348}
349
350pub fn set_order_by(expr: Expression, expressions: Vec<Expression>) -> Expression {
355 let mut expression = expr;
356 let values = expressions.into_iter().map(engine::ordered).collect();
357 let _ = engine::apply_order_by(&mut expression, values, false);
358 expression
359}
360
361pub fn remove_limit_offset(expr: Expression) -> Expression {
363 if let Expression::Select(mut sel) = expr {
364 sel.limit = None;
365 sel.offset = None;
366 Expression::Select(sel)
367 } else {
368 expr
369 }
370}
371
372pub fn rename_columns(expr: Expression, mapping: &HashMap<String, String>) -> Expression {
381 xform(expr, |node| match node {
382 Expression::Column(mut col) => {
383 if let Some(new_name) = mapping.get(&col.name.name) {
384 col.name.name = new_name.clone();
385 }
386 Expression::Column(col)
387 }
388 other => other,
389 })
390}
391
392#[derive(Debug, Clone)]
394pub struct RenameTablesOptions {
395 pub alias_renamed_tables: bool,
397 pub preserve_existing_aliases: bool,
399}
400
401impl Default for RenameTablesOptions {
402 fn default() -> Self {
403 Self {
404 alias_renamed_tables: false,
405 preserve_existing_aliases: true,
406 }
407 }
408}
409
410impl RenameTablesOptions {
411 pub fn new() -> Self {
412 Self::default()
413 }
414
415 pub fn with_alias_renamed_tables(mut self, alias: bool) -> Self {
416 self.alias_renamed_tables = alias;
417 self
418 }
419
420 pub fn with_preserve_existing_aliases(mut self, preserve: bool) -> Self {
421 self.preserve_existing_aliases = preserve;
422 self
423 }
424}
425
426pub fn rename_tables(expr: Expression, mapping: &HashMap<String, String>) -> Expression {
428 rename_tables_with_options(expr, mapping, &RenameTablesOptions::default())
429}
430
431pub fn rename_tables_with_options(
433 expr: Expression,
434 mapping: &HashMap<String, String>,
435 options: &RenameTablesOptions,
436) -> Expression {
437 xform(expr, |node| match node {
438 Expression::Table(mut tbl) => {
439 if let Some(new_name) = mapping.get(&tbl.name.name) {
440 tbl.name.name = new_name.clone();
441 if options.alias_renamed_tables
442 && (!options.preserve_existing_aliases || tbl.alias.is_none())
443 {
444 tbl.alias = Some(Identifier::new(new_name));
445 tbl.alias_explicit_as = true;
446 }
447 }
448 Expression::Table(tbl)
449 }
450 Expression::Column(mut col) => {
451 if let Some(ref mut table_id) = col.table {
452 if let Some(new_name) = mapping.get(&table_id.name) {
453 table_id.name = new_name.clone();
454 }
455 }
456 Expression::Column(col)
457 }
458 other => other,
459 })
460}
461
462pub fn qualify_columns(expr: Expression, table_name: &str) -> Expression {
466 let table = table_name.to_string();
467 xform(expr, move |node| match node {
468 Expression::Column(mut col) => {
469 if col.table.is_none() {
470 col.table = Some(Identifier::new(&table));
471 }
472 Expression::Column(col)
473 }
474 other => other,
475 })
476}
477
478pub fn replace_nodes<F: Fn(&Expression) -> bool>(
484 expr: Expression,
485 predicate: F,
486 replacement: Expression,
487) -> Expression {
488 xform(expr, |node| {
489 if predicate(&node) {
490 replacement.clone()
491 } else {
492 node
493 }
494 })
495}
496
497pub fn replace_by_type<F, R>(expr: Expression, predicate: F, replacer: R) -> Expression
499where
500 F: Fn(&Expression) -> bool,
501 R: Fn(Expression) -> Expression,
502{
503 xform(expr, |node| {
504 if predicate(&node) {
505 replacer(node)
506 } else {
507 node
508 }
509 })
510}
511
512pub fn remove_nodes<F: Fn(&Expression) -> bool>(expr: Expression, predicate: F) -> Expression {
518 xform(expr, |node| {
519 if predicate(&node) {
520 Expression::Null(Null)
521 } else {
522 node
523 }
524 })
525}
526
527#[cfg(feature = "generate")]
533pub(crate) fn output_identifier(expression: &Expression) -> Option<&Identifier> {
534 match expression {
535 Expression::Alias(a) if a.column_aliases.is_empty() => Some(&a.alias),
536 Expression::Column(c) => Some(&c.name),
537 _ => None,
538 }
539}
540
541#[cfg(feature = "generate")]
543pub(crate) fn query_projections(mut query: &Expression) -> Option<&[Expression]> {
544 loop {
545 query = match query {
546 Expression::Select(s) => return Some(&s.expressions),
547 Expression::Union(s) => &s.left,
548 Expression::Intersect(s) => &s.left,
549 Expression::Except(s) => &s.left,
550 Expression::Subquery(s) => &s.this,
551 Expression::Paren(p) => &p.this,
552 _ => return None,
553 };
554 }
555}
556
557pub fn get_column_names(expr: &Expression) -> Vec<String> {
559 expr.find_all(|e| matches!(e, Expression::Column(_)))
560 .into_iter()
561 .filter_map(|e| {
562 if let Expression::Column(col) = e {
563 Some(col.name.name.clone())
564 } else {
565 None
566 }
567 })
568 .collect()
569}
570
571pub fn get_output_column_names(expr: &Expression) -> Vec<String> {
581 output_column_names_from_query(expr)
582}
583
584pub fn get_output_column_names_for_dialect(
591 expr: &Expression,
592 dialect: Option<crate::dialects::DialectType>,
593) -> Vec<String> {
594 crate::set_operation::query_output_identifiers(expr, dialect)
595 .map(|identifiers| {
596 identifiers
597 .into_iter()
598 .map(|identifier| identifier.name)
599 .collect()
600 })
601 .unwrap_or_else(|_| output_column_names_from_query(expr))
602}
603
604fn output_column_names_from_query(expr: &Expression) -> Vec<String> {
605 match expr {
606 Expression::Select(select) => select_output_column_names(select),
607 Expression::Union(union) => output_column_names_from_query(&union.left),
608 Expression::Intersect(intersect) => output_column_names_from_query(&intersect.left),
609 Expression::Except(except) => output_column_names_from_query(&except.left),
610 Expression::Subquery(subquery) => output_column_names_from_query(&subquery.this),
611 _ => Vec::new(),
612 }
613}
614
615fn select_output_column_names(select: &Select) -> Vec<String> {
616 let mut names = Vec::new();
617 for expr in &select.expressions {
618 if let Some(name) = expression_output_name(expr) {
619 names.push(name);
620 }
621 }
622 names
623}
624
625fn expression_output_name(expr: &Expression) -> Option<String> {
626 match expr {
627 Expression::Alias(alias) => Some(alias.alias.name.clone()),
628 Expression::Column(col) => Some(col.name.name.clone()),
629 Expression::Star(_) => Some("*".to_string()),
630 Expression::Identifier(id) => Some(id.name.clone()),
631 Expression::Aliases(aliases) => aliases.expressions.iter().find_map(|e| match e {
632 Expression::Identifier(id) => Some(id.name.clone()),
633 _ => None,
634 }),
635 _ => None,
636 }
637}
638
639pub fn get_table_names(expr: &Expression) -> Vec<String> {
641 fn collect_cte_aliases(with_clause: &With, aliases: &mut HashSet<String>) {
642 for cte in &with_clause.ctes {
643 aliases.insert(cte.alias.name.clone());
644 }
645 }
646
647 fn push_table_ref_name(
648 table: &TableRef,
649 cte_aliases: &HashSet<String>,
650 names: &mut Vec<String>,
651 ) {
652 let name = table.name.name.clone();
653 if !name.is_empty() && !cte_aliases.contains(&name) {
654 names.push(name);
655 }
656 }
657
658 let mut cte_aliases: HashSet<String> = HashSet::new();
659 for node in expr.dfs() {
660 match node {
661 Expression::Select(select) => {
662 if let Some(with) = &select.with {
663 collect_cte_aliases(with, &mut cte_aliases);
664 }
665 }
666 Expression::Insert(insert) => {
667 if let Some(with) = &insert.with {
668 collect_cte_aliases(with, &mut cte_aliases);
669 }
670 }
671 Expression::Update(update) => {
672 if let Some(with) = &update.with {
673 collect_cte_aliases(with, &mut cte_aliases);
674 }
675 }
676 Expression::Delete(delete) => {
677 if let Some(with) = &delete.with {
678 collect_cte_aliases(with, &mut cte_aliases);
679 }
680 }
681 Expression::Union(union) => {
682 if let Some(with) = &union.with {
683 collect_cte_aliases(with, &mut cte_aliases);
684 }
685 }
686 Expression::Intersect(intersect) => {
687 if let Some(with) = &intersect.with {
688 collect_cte_aliases(with, &mut cte_aliases);
689 }
690 }
691 Expression::Except(except) => {
692 if let Some(with) = &except.with {
693 collect_cte_aliases(with, &mut cte_aliases);
694 }
695 }
696 Expression::CreateTable(create) => {
697 if let Some(with) = &create.with_cte {
698 collect_cte_aliases(with, &mut cte_aliases);
699 }
700 }
701 Expression::Merge(merge) => {
702 if let Some(with_) = &merge.with_ {
703 if let Expression::With(with_clause) = with_.as_ref() {
704 collect_cte_aliases(with_clause, &mut cte_aliases);
705 }
706 }
707 }
708 _ => {}
709 }
710 }
711
712 let mut names = Vec::new();
713 for node in expr.dfs() {
714 match node {
715 Expression::Table(tbl) => {
716 let name = tbl.name.name.clone();
717 if !name.is_empty() && !cte_aliases.contains(&name) {
718 names.push(name);
719 }
720 }
721 Expression::Upsert(upsert) => {
722 push_table_ref_name(&upsert.table, &cte_aliases, &mut names);
723 }
724 Expression::Insert(insert) => {
725 push_table_ref_name(&insert.table, &cte_aliases, &mut names);
726 }
727 Expression::Update(update) => {
728 push_table_ref_name(&update.table, &cte_aliases, &mut names);
729 for table in &update.extra_tables {
730 push_table_ref_name(table, &cte_aliases, &mut names);
731 }
732 }
733 Expression::Delete(delete) => {
734 push_table_ref_name(&delete.table, &cte_aliases, &mut names);
735 for table in &delete.using {
736 push_table_ref_name(table, &cte_aliases, &mut names);
737 }
738 for table in &delete.tables {
739 push_table_ref_name(table, &cte_aliases, &mut names);
740 }
741 }
742 Expression::CreateTable(create) => {
743 push_table_ref_name(&create.name, &cte_aliases, &mut names);
744 if let Some(as_select) = &create.as_select {
745 names.extend(get_table_names(as_select));
746 }
747 if let Some(with) = &create.with_cte {
748 for cte in &with.ctes {
749 names.extend(get_table_names(&cte.this));
750 }
751 }
752 }
753 Expression::Cache(cache) => {
754 let name = cache.table.name.clone();
755 if !name.is_empty() && !cte_aliases.contains(&name) {
756 names.push(name);
757 }
758 }
759 Expression::Uncache(uncache) => {
760 let name = uncache.table.name.clone();
761 if !name.is_empty() && !cte_aliases.contains(&name) {
762 names.push(name);
763 }
764 }
765 Expression::CreateSynonym(synonym) => {
766 push_table_ref_name(&synonym.name, &cte_aliases, &mut names);
767 push_table_ref_name(&synonym.target, &cte_aliases, &mut names);
768 }
769 _ => {}
770 }
771 }
772
773 names
774}
775
776pub fn get_identifiers(expr: &Expression) -> Vec<&Expression> {
778 expr.find_all(|e| matches!(e, Expression::Identifier(_)))
779}
780
781pub fn get_functions(expr: &Expression) -> Vec<&Expression> {
783 expr.find_all(crate::traversal::is_function)
784}
785
786pub fn get_literals(expr: &Expression) -> Vec<&Expression> {
788 expr.find_all(|e| {
789 matches!(
790 e,
791 Expression::Literal(_) | Expression::Boolean(_) | Expression::Null(_)
792 )
793 })
794}
795
796pub fn get_subqueries(expr: &Expression) -> Vec<&Expression> {
798 expr.find_all(|e| matches!(e, Expression::Subquery(_)))
799}
800
801pub fn get_aggregate_functions(expr: &Expression) -> Vec<&Expression> {
806 expr.find_all(is_aggregate)
807}
808
809pub fn get_window_functions(expr: &Expression) -> Vec<&Expression> {
811 expr.find_all(|e| matches!(e, Expression::WindowFunction(_)))
812}
813
814pub fn node_count(expr: &Expression) -> usize {
816 expr.dfs().count()
817}
818
819#[cfg(test)]
820mod tests {
821 use super::*;
822 use crate::parser::Parser;
823
824 fn parse_one(sql: &str) -> Expression {
825 let mut exprs = Parser::parse_sql(sql).unwrap();
826 exprs.remove(0)
827 }
828
829 #[test]
830 fn test_add_where() {
831 let expr = parse_one("SELECT a FROM t");
832 let cond = Expression::Eq(Box::new(BinaryOp::new(
833 Expression::column("b"),
834 Expression::number(1),
835 )));
836 let result = add_where(expr, cond, false);
837 let sql = result.sql();
838 assert!(sql.contains("WHERE"), "Expected WHERE in: {}", sql);
839 assert!(sql.contains("b = 1"), "Expected condition in: {}", sql);
840 }
841
842 #[test]
843 fn test_add_where_combines_with_and() {
844 let expr = parse_one("SELECT a FROM t WHERE x = 1");
845 let cond = Expression::Eq(Box::new(BinaryOp::new(
846 Expression::column("y"),
847 Expression::number(2),
848 )));
849 let result = add_where(expr, cond, false);
850 let sql = result.sql();
851 assert!(sql.contains("AND"), "Expected AND in: {}", sql);
852 }
853
854 #[test]
855 fn test_remove_where() {
856 let expr = parse_one("SELECT a FROM t WHERE x = 1");
857 let result = remove_where(expr);
858 let sql = result.sql();
859 assert!(!sql.contains("WHERE"), "Should not contain WHERE: {}", sql);
860 }
861
862 #[test]
863 fn test_set_limit() {
864 let expr = parse_one("SELECT a FROM t");
865 let result = set_limit(expr, 10);
866 let sql = result.sql();
867 assert!(sql.contains("LIMIT 10"), "Expected LIMIT in: {}", sql);
868 }
869
870 #[test]
871 fn test_set_limit_on_set_operation() {
872 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
873 let result = set_limit(expr, 10);
874 let sql = result.sql();
875 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u LIMIT 10");
876 }
877
878 #[test]
879 fn test_set_offset() {
880 let expr = parse_one("SELECT a FROM t");
881 let result = set_offset(expr, 5);
882 let sql = result.sql();
883 assert!(sql.contains("OFFSET 5"), "Expected OFFSET in: {}", sql);
884 }
885
886 #[test]
887 fn test_set_offset_on_set_operation() {
888 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
889 let result = set_offset(expr, 5);
890 let sql = result.sql();
891 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u OFFSET 5");
892 }
893
894 #[test]
895 fn test_set_order_by_on_set_operation() {
896 let expr = parse_one("SELECT a FROM t UNION ALL SELECT a FROM u");
897 let result = set_order_by(expr, vec![Expression::column("a")]);
898 let sql = result.sql();
899 assert_eq!(sql, "SELECT a FROM t UNION ALL SELECT a FROM u ORDER BY a");
900 }
901
902 #[test]
903 fn test_remove_limit_offset() {
904 let expr = parse_one("SELECT a FROM t LIMIT 10 OFFSET 5");
905 let result = remove_limit_offset(expr);
906 let sql = result.sql();
907 assert!(!sql.contains("LIMIT"), "Should not contain LIMIT: {}", sql);
908 assert!(
909 !sql.contains("OFFSET"),
910 "Should not contain OFFSET: {}",
911 sql
912 );
913 }
914
915 #[test]
916 fn test_get_column_names() {
917 let expr = parse_one("SELECT a, b, c FROM t");
918 let names = get_column_names(&expr);
919 assert!(names.contains(&"a".to_string()));
920 assert!(names.contains(&"b".to_string()));
921 assert!(names.contains(&"c".to_string()));
922 }
923
924 #[test]
925 fn test_get_output_column_names_select() {
926 let expr = parse_one("SELECT a, b AS c, 1 FROM t");
927 let names = get_output_column_names(&expr);
928 assert_eq!(names, vec!["a".to_string(), "c".to_string()]);
929 }
930
931 #[test]
932 fn test_get_output_column_names_union_left_projection() {
933 let expr =
934 parse_one("SELECT id, name FROM customers UNION ALL SELECT id, name FROM employees");
935 let names = get_output_column_names(&expr);
936 assert_eq!(names, vec!["id".to_string(), "name".to_string()]);
937 }
938
939 #[test]
940 fn test_get_output_column_names_union_uses_left_aliases() {
941 let expr = parse_one("SELECT id AS c1, name AS c2 FROM t1 UNION SELECT x, y FROM t2");
942 let names = get_output_column_names(&expr);
943 assert_eq!(names, vec!["c1".to_string(), "c2".to_string()]);
944 }
945
946 #[test]
947 fn test_get_output_column_names_uses_dialect_by_name_layout() {
948 for dialect in [
949 crate::dialects::DialectType::DuckDB,
950 crate::dialects::DialectType::Snowflake,
951 ] {
952 let expr = crate::parse_one(
953 "SELECT 1 AS left_value UNION ALL BY NAME SELECT 2 AS right_value",
954 dialect,
955 )
956 .expect("parse");
957 assert_eq!(
958 get_output_column_names_for_dialect(&expr, Some(dialect)),
959 vec!["left_value", "right_value"]
960 );
961 }
962
963 let expr = crate::parse_one(
964 "SELECT 1 AS a, 2 AS b UNION ALL BY NAME SELECT 3 AS b, 4 AS a",
965 crate::dialects::DialectType::BigQuery,
966 )
967 .expect("parse");
968 assert_eq!(
969 get_output_column_names_for_dialect(
970 &expr,
971 Some(crate::dialects::DialectType::BigQuery),
972 ),
973 vec!["a", "b"]
974 );
975 }
976
977 #[test]
978 fn test_get_column_names_union_still_returns_all_references() {
979 let expr =
980 parse_one("SELECT id, name FROM customers UNION ALL SELECT id, name FROM employees");
981 let names = get_column_names(&expr);
982 assert_eq!(
983 names,
984 vec![
985 "id".to_string(),
986 "name".to_string(),
987 "id".to_string(),
988 "name".to_string()
989 ]
990 );
991 }
992
993 #[test]
994 fn test_get_table_names() {
995 let expr = parse_one("SELECT a FROM users");
996 let names = get_table_names(&expr);
997 assert_eq!(names, vec!["users".to_string()]);
998 }
999
1000 #[test]
1001 fn test_get_table_names_excludes_cte_aliases() {
1002 let expr = parse_one(
1003 "WITH cte AS (SELECT * FROM users) SELECT * FROM cte JOIN orders o ON cte.id = o.id",
1004 );
1005 let names = get_table_names(&expr);
1006 assert!(names.iter().any(|n| n == "users"));
1007 assert!(names.iter().any(|n| n == "orders"));
1008 assert!(!names.iter().any(|n| n == "cte"));
1009 }
1010
1011 #[test]
1012 fn test_get_table_names_includes_dml_targets() {
1013 let insert_expr = parse_one("INSERT INTO users (id) VALUES (1)");
1014 let insert_names = get_table_names(&insert_expr);
1015 assert!(insert_names.iter().any(|n| n == "users"));
1016
1017 let update_expr =
1018 parse_one("UPDATE users SET name = 'x' FROM accounts WHERE users.id = accounts.id");
1019 let update_names = get_table_names(&update_expr);
1020 assert!(update_names.iter().any(|n| n == "users"));
1021 assert!(update_names.iter().any(|n| n == "accounts"));
1022
1023 let delete_expr =
1024 parse_one("DELETE FROM users USING accounts WHERE users.id = accounts.id");
1025 let delete_names = get_table_names(&delete_expr);
1026 assert!(delete_names.iter().any(|n| n == "users"));
1027 assert!(delete_names.iter().any(|n| n == "accounts"));
1028
1029 let create_expr = parse_one("CREATE TABLE out_table AS SELECT 1 AS id FROM src");
1030 let create_names = get_table_names(&create_expr);
1031 assert!(create_names.iter().any(|n| n == "out_table"));
1032 assert!(create_names.iter().any(|n| n == "src"));
1033 }
1034
1035 #[test]
1036 fn test_node_count() {
1037 let expr = parse_one("SELECT a FROM t");
1038 let count = node_count(&expr);
1039 assert!(count > 0, "Expected non-zero node count");
1040 }
1041
1042 #[test]
1043 fn test_rename_columns() {
1044 let expr = parse_one("SELECT old_name FROM t");
1045 let mut mapping = HashMap::new();
1046 mapping.insert("old_name".to_string(), "new_name".to_string());
1047 let result = rename_columns(expr, &mapping);
1048 let sql = result.sql();
1049 assert!(sql.contains("new_name"), "Expected new_name in: {}", sql);
1050 assert!(
1051 !sql.contains("old_name"),
1052 "Should not contain old_name: {}",
1053 sql
1054 );
1055 }
1056
1057 #[test]
1058 fn test_rename_tables() {
1059 let expr = parse_one("SELECT a FROM old_table");
1060 let mut mapping = HashMap::new();
1061 mapping.insert("old_table".to_string(), "new_table".to_string());
1062 let result = rename_tables(expr, &mapping);
1063 let sql = result.sql();
1064 assert!(sql.contains("new_table"), "Expected new_table in: {}", sql);
1065 }
1066
1067 #[test]
1068 fn test_rename_tables_with_alias_renamed_tables() {
1069 let expr = parse_one("SELECT a FROM old_table");
1070 let mut mapping = HashMap::new();
1071 mapping.insert("old_table".to_string(), "new_table".to_string());
1072 let options = RenameTablesOptions::new().with_alias_renamed_tables(true);
1073 let result = rename_tables_with_options(expr, &mapping, &options);
1074 let sql = result.sql();
1075
1076 assert_eq!(sql, "SELECT a FROM new_table AS new_table");
1077 }
1078
1079 #[test]
1080 fn test_rename_tables_with_alias_preserves_existing_alias() {
1081 let expr = parse_one("SELECT a FROM old_table AS t");
1082 let mut mapping = HashMap::new();
1083 mapping.insert("old_table".to_string(), "new_table".to_string());
1084 let options = RenameTablesOptions::new().with_alias_renamed_tables(true);
1085 let result = rename_tables_with_options(expr, &mapping, &options);
1086 let sql = result.sql();
1087
1088 assert_eq!(sql, "SELECT a FROM new_table AS t");
1089 }
1090
1091 #[test]
1092 fn test_set_distinct() {
1093 let expr = parse_one("SELECT a FROM t");
1094 let result = set_distinct(expr, true);
1095 let sql = result.sql();
1096 assert!(sql.contains("DISTINCT"), "Expected DISTINCT in: {}", sql);
1097 }
1098
1099 #[test]
1100 fn test_add_select_columns() {
1101 let expr = parse_one("SELECT a FROM t");
1102 let result = add_select_columns(expr, vec![Expression::column("b")]);
1103 let sql = result.sql();
1104 assert!(
1105 sql.contains("a, b") || sql.contains("a,b"),
1106 "Expected a, b in: {}",
1107 sql
1108 );
1109 }
1110
1111 #[test]
1112 fn test_qualify_columns() {
1113 let expr = parse_one("SELECT a, b FROM t");
1114 let result = qualify_columns(expr, "t");
1115 let sql = result.sql();
1116 assert!(sql.contains("t.a"), "Expected t.a in: {}", sql);
1117 assert!(sql.contains("t.b"), "Expected t.b in: {}", sql);
1118 }
1119
1120 #[test]
1121 fn test_get_functions() {
1122 let expr = parse_one("SELECT COUNT(*), UPPER(name) FROM t");
1123 let funcs = get_functions(&expr);
1124 let _ = funcs.len();
1129 }
1130
1131 #[test]
1132 fn test_get_aggregate_functions() {
1133 let expr = crate::parse_one(
1134 "SELECT COUNT_IF(numeric_value > 0), MEDIAN(numeric_value), FIRST(numeric_value) FROM source_table",
1135 crate::dialects::DialectType::DuckDB,
1136 )
1137 .unwrap();
1138 let aggs = get_aggregate_functions(&expr);
1139 let aggregate_types: Vec<_> = aggs.iter().map(|agg| agg.variant_name()).collect();
1140
1141 assert_eq!(aggregate_types, vec!["count_if", "median", "first"]);
1142 }
1143
1144 #[test]
1145 fn test_get_duckdb_null_preserving_arg_extrema() {
1146 let expr = crate::parse_one(
1147 "SELECT ARG_MAX_NULL(label, score), ARG_MIN_NULL(label, score) FROM source_table",
1148 crate::dialects::DialectType::DuckDB,
1149 )
1150 .unwrap();
1151 let aggregates = get_aggregate_functions(&expr);
1152
1153 assert_eq!(aggregates.len(), 2);
1154 for (aggregate, expected_name) in
1155 aggregates.into_iter().zip(["ARG_MAX_NULL", "ARG_MIN_NULL"])
1156 {
1157 let Expression::AggregateFunction(function) = aggregate else {
1158 panic!("expected a generic aggregate node, got {aggregate:?}");
1159 };
1160 assert_eq!(function.name, expected_name);
1161 assert_eq!(function.args.len(), 2);
1162 }
1163 }
1164}