Skip to main content

polyglot_sql/
ast_transforms.rs

1//! AST transform helpers and convenience getters.
2//!
3//! This module provides functions for common AST mutations (adding WHERE clauses,
4//! setting LIMIT/OFFSET, renaming columns/tables) and read-only extraction helpers
5//! (getting column names, table names, functions, etc.).
6//!
7//! Mutation functions take an owned [`Expression`] and return a new [`Expression`].
8//! Read-only getters take `&Expression`.
9
10use std::collections::{HashMap, HashSet};
11
12use crate::ast_mutation as engine;
13use crate::expressions::*;
14use crate::traversal::{is_aggregate, ExpressionWalk};
15
16/// Allocate case-insensitively unique names for synthesized AST scopes.
17#[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
73/// Apply a complete bottom-up rewrite, retaining the embedded wrapper callbacks
74/// historically exposed by these helpers. Physical children are visited only once.
75fn 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
82// Embedded TableRef/Join/Ordered/etc. fields are not Expression nodes in the
83// generated visitor. Preserve their helper callbacks after their expression
84// children have been rewritten, without traversing those children again.
85fn 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        // Time-travel wrappers also belong to embedded DML targets.
107        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
242// ---------------------------------------------------------------------------
243// SELECT clause
244// ---------------------------------------------------------------------------
245
246/// Append columns to the SELECT list of a query.
247///
248/// If `expr` is a `Select`, the given `columns` are appended to its expression list.
249/// Non-SELECT expressions are returned unchanged.
250pub 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
256/// Remove columns from the SELECT list where `predicate` returns `true`.
257pub 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
269/// Set or remove the DISTINCT flag on a SELECT.
270pub 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
276// ---------------------------------------------------------------------------
277// WHERE clause
278// ---------------------------------------------------------------------------
279
280/// Add a condition to the WHERE clause.
281///
282/// If the SELECT already has a WHERE clause, the new condition is combined with the
283/// existing one using AND (default) or OR (when `use_or` is `true`).
284/// If there is no WHERE clause, one is created.
285pub 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
312/// Remove the WHERE clause from a SELECT.
313pub 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
322// ---------------------------------------------------------------------------
323// LIMIT / OFFSET / ORDER BY
324// ---------------------------------------------------------------------------
325
326/// Set the LIMIT on a SELECT or set operation.
327pub fn set_limit(expr: Expression, limit: usize) -> Expression {
328    set_limit_expr(expr, Expression::number(limit as i64))
329}
330
331/// Set the LIMIT on a SELECT or set operation using an expression.
332pub 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
338/// Set the OFFSET on a SELECT or set operation.
339pub fn set_offset(expr: Expression, offset: usize) -> Expression {
340    set_offset_expr(expr, Expression::number(offset as i64))
341}
342
343/// Set the OFFSET on a SELECT or set operation using an expression.
344pub 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
350/// Set the ORDER BY clause on a SELECT or set operation.
351///
352/// Bare expressions are normalized to ascending order expressions. Existing
353/// `Ordered` expressions preserve their direction and null-ordering metadata.
354pub 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
361/// Remove both LIMIT and OFFSET from a SELECT.
362pub 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
372// ---------------------------------------------------------------------------
373// Renaming
374// ---------------------------------------------------------------------------
375
376/// Rename columns throughout the expression tree using the provided mapping.
377///
378/// Column names present as keys in `mapping` are replaced with their corresponding
379/// values. The replacement is case-sensitive.
380pub 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/// Options for table renaming.
393#[derive(Debug, Clone)]
394pub struct RenameTablesOptions {
395    /// Whether renamed table references should receive aliases.
396    pub alias_renamed_tables: bool,
397    /// Whether existing aliases should be preserved when aliasing renamed tables.
398    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
426/// Rename tables throughout the expression tree using the provided mapping.
427pub fn rename_tables(expr: Expression, mapping: &HashMap<String, String>) -> Expression {
428    rename_tables_with_options(expr, mapping, &RenameTablesOptions::default())
429}
430
431/// Rename tables throughout the expression tree using the provided mapping and options.
432pub 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
462/// Qualify all unqualified column references with the given `table_name`.
463///
464/// Columns that already have a table qualifier are left unchanged.
465pub 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
478// ---------------------------------------------------------------------------
479// Generic replacement
480// ---------------------------------------------------------------------------
481
482/// Replace nodes matching `predicate` with `replacement` (cloned for each match).
483pub 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
497/// Replace nodes matching `predicate` by applying `replacer` to the matched node.
498pub 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
512/// Remove (replace with a `Null`) all nodes matching `predicate`.
513///
514/// This is most useful for removing clauses or sub-expressions from a tree.
515/// Note that removing structural elements (e.g. the FROM clause) may produce
516/// invalid SQL; use with care.
517pub 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// ---------------------------------------------------------------------------
528// Convenience getters
529// ---------------------------------------------------------------------------
530
531/// Return a single named projection while preserving identifier quoting.
532#[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/// Borrow the leftmost projection list through query wrappers and set operations.
542#[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
557/// Collect all column names (as `String`) referenced in the expression tree.
558pub 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
571/// Collect projected output column names from a query expression.
572///
573/// This follows sqlglot-style query semantics:
574/// - For `SELECT`, returns names from the projection list.
575/// - For set operations (`UNION`/`INTERSECT`/`EXCEPT`), uses the left-most branch.
576/// - For `Subquery`, unwraps and evaluates the inner query.
577///
578/// Unlike [`get_column_names`], this does not return every referenced column in
579/// the AST and is suitable for result-schema style output names.
580pub fn get_output_column_names(expr: &Expression) -> Vec<String> {
581    output_column_names_from_query(expr)
582}
583
584/// Collect projected output column names using dialect-specific set-operation
585/// alignment rules.
586///
587/// This differs from [`get_output_column_names`] only for name-aligned set
588/// operations. When an output shape is not statically knowable (for example an
589/// unresolved wildcard), it preserves the existing leftmost-branch behavior.
590pub 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
639/// Collect all table names (as `String`) referenced in the expression tree.
640pub 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
776/// Collect all identifier references in the expression tree.
777pub fn get_identifiers(expr: &Expression) -> Vec<&Expression> {
778    expr.find_all(|e| matches!(e, Expression::Identifier(_)))
779}
780
781/// Collect all function call nodes in the expression tree.
782pub fn get_functions(expr: &Expression) -> Vec<&Expression> {
783    expr.find_all(crate::traversal::is_function)
784}
785
786/// Collect all literal value nodes in the expression tree.
787pub 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
796/// Collect all subquery nodes in the expression tree.
797pub fn get_subqueries(expr: &Expression) -> Vec<&Expression> {
798    expr.find_all(|e| matches!(e, Expression::Subquery(_)))
799}
800
801/// Collect all aggregate function nodes in the expression tree.
802///
803/// Includes typed aggregates (`Count`, `Sum`, `Avg`, `Min`, `Max`, etc.)
804/// and generic `AggregateFunction` nodes.
805pub fn get_aggregate_functions(expr: &Expression) -> Vec<&Expression> {
806    expr.find_all(is_aggregate)
807}
808
809/// Collect all window function nodes in the expression tree.
810pub fn get_window_functions(expr: &Expression) -> Vec<&Expression> {
811    expr.find_all(|e| matches!(e, Expression::WindowFunction(_)))
812}
813
814/// Count the total number of AST nodes in the expression tree.
815pub 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        // UPPER is a typed function (Expression::Upper), not Expression::Function
1125        // COUNT is Expression::Count, not Expression::AggregateFunction
1126        // So get_functions (which checks Function | AggregateFunction) may return 0
1127        // That's OK — we have separate get_aggregate_functions for typed aggs
1128        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}