Skip to main content

polyglot_sql/
scope.rs

1//! Scope Analysis Module
2//!
3//! This module provides scope analysis for SQL queries, enabling detection of
4//! correlated subqueries, column references, and scope relationships.
5//!
6//! Ported from sqlglot's optimizer/scope.py
7
8use crate::expressions::{Expression, Identifier, JoinKind, Literal, TableRef};
9use crate::traversal::ExpressionWalk;
10use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, HashSet, VecDeque};
12#[cfg(feature = "bindings")]
13use ts_rs::TS;
14
15/// Type of scope in a SQL query
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17#[cfg_attr(feature = "bindings", derive(TS))]
18#[cfg_attr(feature = "bindings", ts(export))]
19pub enum ScopeType {
20    /// Root scope of the query
21    Root,
22    /// Subquery scope (e.g., WHERE x IN (SELECT ...))
23    Subquery,
24    /// Derived table scope (e.g., FROM (SELECT ...) AS t)
25    DerivedTable,
26    /// Common Table Expression scope
27    Cte,
28    /// Union/Intersect/Except scope
29    SetOperation,
30    /// User-Defined Table Function scope
31    Udtf,
32}
33
34/// Semantic kind of a source registered in a scope.
35#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "snake_case")]
37pub enum SourceKind {
38    /// Root query or statement context
39    Root,
40    /// Physical table source
41    Table,
42    /// Derived table/subquery source
43    DerivedTable,
44    /// Common Table Expression source
45    Cte,
46    /// Virtual table source such as UNNEST / UDTF
47    Virtual,
48    /// Unresolved or synthetic fallback source
49    Unknown,
50}
51
52impl Default for SourceKind {
53    fn default() -> Self {
54        Self::Unknown
55    }
56}
57
58/// Unwrap query containers without creating a new lexical context.
59pub(crate) fn scope_query(expression: &Expression) -> &Expression {
60    match expression {
61        Expression::Cte(cte) => scope_query(&cte.this),
62        Expression::Subquery(subquery) => scope_query(&subquery.this),
63        Expression::Paren(paren) => scope_query(&paren.this),
64        Expression::Alias(alias) => scope_query(&alias.this),
65        Expression::Prepare(prepare) => scope_query(&prepare.statement),
66        Expression::CreateTable(create) if create.as_select.is_some() => {
67            scope_query(create.as_select.as_ref().unwrap())
68        }
69        _ => expression,
70    }
71}
72
73/// A lightweight resolution view containing only selected sources. CTE
74/// declarations remain available, but do not themselves cause ambiguity.
75pub(crate) fn selected_reference_scope(scope: &Scope) -> Scope {
76    let query = scope_query(&scope.expression);
77    let aliases: HashSet<_> = walk_in_scope(query, false)
78        .filter_map(|node| match node {
79            Expression::Table(table) => {
80                Some(table.alias.as_ref().unwrap_or(&table.name).name.clone())
81            }
82            _ => None,
83        })
84        .collect();
85    let mut selected = Scope::new(query.clone());
86    selected.cte_sources = scope.cte_sources.clone();
87    selected.sources = scope
88        .sources
89        .iter()
90        .filter(|(name, source)| {
91            // Source keys use the spelling of the actual FROM/JOIN binding.
92            // Folding here can accidentally retain an unused, quoted CTE.
93            source.kind != SourceKind::Cte || aliases.contains(*name)
94        })
95        .map(|(name, source)| (name.clone(), source.clone()))
96        .collect();
97    selected
98}
99
100/// Information about a source (table or subquery) in a scope
101#[derive(Debug, Clone)]
102pub struct SourceInfo {
103    /// The source expression (Table or subquery)
104    pub expression: Expression,
105    /// Whether this source is a scope (vs. a plain table)
106    pub is_scope: bool,
107    /// Semantic source kind for lineage consumers.
108    pub kind: SourceKind,
109    /// User-written alias, when it should be preserved separately from lineage name.
110    pub alias: Option<String>,
111    /// Canonical lineage source name, e.g. synthetic `_0` for virtual sources.
112    pub lineage_name: Option<String>,
113}
114
115impl SourceInfo {
116    pub fn new(expression: Expression, is_scope: bool, kind: SourceKind) -> Self {
117        Self {
118            expression,
119            is_scope,
120            kind,
121            alias: None,
122            lineage_name: None,
123        }
124    }
125
126    pub fn with_alias(mut self, alias: impl Into<String>) -> Self {
127        self.alias = Some(alias.into());
128        self
129    }
130
131    pub fn with_lineage_name(mut self, lineage_name: impl Into<String>) -> Self {
132        self.lineage_name = Some(lineage_name.into());
133        self
134    }
135}
136
137/// A column reference found in a scope
138#[derive(Debug, Clone, PartialEq, Eq, Hash)]
139pub struct ColumnRef {
140    /// The table/alias qualifier (if any)
141    pub table: Option<String>,
142    /// The column name
143    pub name: String,
144}
145
146/// Represents a scope in a SQL query
147///
148/// A scope is the context of a SELECT statement and its sources.
149/// Scopes can be nested (subqueries, CTEs, derived tables) and form a tree.
150#[derive(Debug, Clone)]
151pub struct Scope {
152    /// The expression at the root of this scope
153    pub expression: Expression,
154
155    /// Type of this scope relative to its parent
156    pub scope_type: ScopeType,
157
158    /// Mapping of source names to their info
159    pub sources: HashMap<String, SourceInfo>,
160
161    /// Sources from LATERAL views (have access to preceding sources)
162    pub lateral_sources: HashMap<String, SourceInfo>,
163
164    /// CTE sources available to this scope
165    pub cte_sources: HashMap<String, SourceInfo>,
166
167    /// If this is a derived table or CTE with alias columns, this is that list
168    /// e.g., `SELECT * FROM (SELECT ...) AS y(col1, col2)` => ["col1", "col2"]
169    pub outer_columns: Vec<String>,
170
171    /// Whether this scope can potentially be correlated
172    /// (true for subqueries and UDTFs)
173    pub can_be_correlated: bool,
174
175    /// Whether this derived table sees only preceding FROM/JOIN sources.
176    pub is_lateral: bool,
177
178    /// Child subquery scopes
179    pub subquery_scopes: Vec<Scope>,
180
181    /// Child derived table scopes
182    pub derived_table_scopes: Vec<Scope>,
183
184    /// Child CTE scopes
185    pub cte_scopes: Vec<Scope>,
186
187    /// Child UDTF (User Defined Table Function) scopes
188    pub udtf_scopes: Vec<Scope>,
189
190    /// Combined derived_table_scopes + udtf_scopes in definition order
191    pub table_scopes: Vec<Scope>,
192
193    /// Union/set operation scopes (left and right)
194    pub union_scopes: Vec<Scope>,
195
196    /// Cached columns
197    columns_cache: Option<Vec<ColumnRef>>,
198
199    /// Cached external columns
200    external_columns_cache: Option<Vec<ColumnRef>>,
201}
202
203impl Scope {
204    /// Create a new root scope
205    pub fn new(expression: Expression) -> Self {
206        Self {
207            expression,
208            scope_type: ScopeType::Root,
209            sources: HashMap::new(),
210            lateral_sources: HashMap::new(),
211            cte_sources: HashMap::new(),
212            outer_columns: Vec::new(),
213            can_be_correlated: false,
214            is_lateral: false,
215            subquery_scopes: Vec::new(),
216            derived_table_scopes: Vec::new(),
217            cte_scopes: Vec::new(),
218            udtf_scopes: Vec::new(),
219            table_scopes: Vec::new(),
220            union_scopes: Vec::new(),
221            columns_cache: None,
222            external_columns_cache: None,
223        }
224    }
225
226    /// Create a child scope branching from this one
227    pub fn branch(&self, expression: Expression, scope_type: ScopeType) -> Self {
228        self.branch_with_options(expression, scope_type, None, None, None)
229    }
230
231    /// Create a child scope with additional options
232    pub fn branch_with_options(
233        &self,
234        expression: Expression,
235        scope_type: ScopeType,
236        sources: Option<HashMap<String, SourceInfo>>,
237        lateral_sources: Option<HashMap<String, SourceInfo>>,
238        outer_columns: Option<Vec<String>>,
239    ) -> Self {
240        let can_be_correlated = self.can_be_correlated
241            || scope_type == ScopeType::Subquery
242            || scope_type == ScopeType::Udtf;
243
244        Self {
245            expression,
246            scope_type,
247            sources: sources.unwrap_or_default(),
248            lateral_sources: lateral_sources.unwrap_or_default(),
249            cte_sources: self.cte_sources.clone(),
250            outer_columns: outer_columns.unwrap_or_default(),
251            can_be_correlated,
252            is_lateral: false,
253            subquery_scopes: Vec::new(),
254            derived_table_scopes: Vec::new(),
255            cte_scopes: Vec::new(),
256            udtf_scopes: Vec::new(),
257            table_scopes: Vec::new(),
258            union_scopes: Vec::new(),
259            columns_cache: None,
260            external_columns_cache: None,
261        }
262    }
263
264    /// Clear all cached properties
265    pub fn clear_cache(&mut self) {
266        self.columns_cache = None;
267        self.external_columns_cache = None;
268    }
269
270    /// Add a source to this scope
271    pub fn add_source(&mut self, name: String, expression: Expression, is_scope: bool) {
272        let kind = if is_scope {
273            SourceKind::DerivedTable
274        } else {
275            SourceKind::Table
276        };
277        self.add_source_info(name, SourceInfo::new(expression, is_scope, kind));
278    }
279
280    /// Add a preconfigured source to this scope
281    pub fn add_source_info(&mut self, name: String, info: SourceInfo) {
282        self.sources.insert(name, info);
283        self.clear_cache();
284    }
285
286    /// Add a virtual source such as UNNEST / UDTF.
287    pub fn add_virtual_source(&mut self, alias: String, expression: Expression) {
288        let lineage_name = self.next_virtual_source_name();
289        let info = SourceInfo::new(expression, false, SourceKind::Virtual)
290            .with_alias(alias.clone())
291            .with_lineage_name(lineage_name);
292        self.add_source_info(alias, info);
293    }
294
295    fn next_virtual_source_name(&self) -> String {
296        let count = self
297            .sources
298            .values()
299            .filter(|source| source.kind == SourceKind::Virtual)
300            .count();
301        format!("_{}", count)
302    }
303
304    /// Add a lateral source to this scope
305    pub fn add_lateral_source(&mut self, name: String, expression: Expression, is_scope: bool) {
306        let kind = if is_scope {
307            SourceKind::DerivedTable
308        } else {
309            SourceKind::Table
310        };
311        let info = SourceInfo::new(expression.clone(), is_scope, kind);
312        self.sources.insert(name.clone(), info.clone());
313        self.lateral_sources.insert(name, info);
314        self.clear_cache();
315    }
316
317    /// Add a CTE source to this scope
318    pub fn add_cte_source(&mut self, name: String, expression: Expression) {
319        let info = SourceInfo::new(expression, true, SourceKind::Cte);
320        self.cte_sources.insert(name.clone(), info.clone());
321        self.sources.insert(name, info);
322        self.clear_cache();
323    }
324
325    /// Rename a source
326    pub fn rename_source(&mut self, old_name: &str, new_name: String) {
327        if let Some(source) = self.sources.remove(old_name) {
328            self.sources.insert(new_name, source);
329        }
330        self.clear_cache();
331    }
332
333    /// Remove a source
334    pub fn remove_source(&mut self, name: &str) {
335        self.sources.remove(name);
336        self.clear_cache();
337    }
338
339    /// Collect all column references in this scope
340    pub fn columns(&mut self) -> &[ColumnRef] {
341        if self.columns_cache.is_none() {
342            let mut columns = Vec::new();
343            collect_columns(&self.expression, &mut columns);
344            self.columns_cache = Some(columns);
345        }
346        self.columns_cache.as_ref().unwrap()
347    }
348
349    /// Collect projected output column names for this scope's query expression.
350    ///
351    /// This is intended for result schema style output columns (e.g. UNION
352    /// outputs), unlike [`Self::columns`], which returns raw referenced columns.
353    pub fn output_columns(&self) -> Vec<String> {
354        crate::ast_transforms::get_output_column_names(&self.expression)
355    }
356
357    /// Get all source names in this scope
358    pub fn source_names(&self) -> HashSet<String> {
359        let mut names: HashSet<String> = self.sources.keys().cloned().collect();
360        names.extend(self.cte_sources.keys().cloned());
361        names
362    }
363
364    /// Get columns that reference sources outside this scope
365    pub fn external_columns(&mut self) -> Vec<ColumnRef> {
366        if self.external_columns_cache.is_some() {
367            return self.external_columns_cache.clone().unwrap();
368        }
369
370        let source_names = self.source_names();
371        let columns = self.columns().to_vec();
372
373        let external: Vec<ColumnRef> = columns
374            .into_iter()
375            .filter(|col| {
376                // A column is external if it has a table qualifier that's not in our sources
377                match &col.table {
378                    Some(table) => !source_names.contains(table),
379                    None => false, // Unqualified columns might be local
380                }
381            })
382            .collect();
383
384        self.external_columns_cache = Some(external.clone());
385        external
386    }
387
388    /// Get columns that reference sources in this scope (not external)
389    pub fn local_columns(&mut self) -> Vec<ColumnRef> {
390        let external_set: HashSet<_> = self.external_columns().into_iter().collect();
391        let columns = self.columns().to_vec();
392
393        columns
394            .into_iter()
395            .filter(|col| !external_set.contains(col))
396            .collect()
397    }
398
399    /// Get unqualified columns (columns without table qualifier)
400    pub fn unqualified_columns(&mut self) -> Vec<ColumnRef> {
401        self.columns()
402            .iter()
403            .filter(|c| c.table.is_none())
404            .cloned()
405            .collect()
406    }
407
408    /// Get columns for a specific source
409    pub fn source_columns(&mut self, source_name: &str) -> Vec<ColumnRef> {
410        self.columns()
411            .iter()
412            .filter(|col| col.table.as_deref() == Some(source_name))
413            .cloned()
414            .collect()
415    }
416
417    /// Determine if this scope is a correlated subquery
418    ///
419    /// A subquery is correlated if:
420    /// 1. It can be correlated (is a subquery or UDTF), AND
421    /// 2. It references columns from outer scopes
422    pub fn is_correlated_subquery(&mut self) -> bool {
423        self.can_be_correlated && !self.external_columns().is_empty()
424    }
425
426    /// Check if this is a subquery scope
427    pub fn is_subquery(&self) -> bool {
428        self.scope_type == ScopeType::Subquery
429    }
430
431    /// Check if this is a derived table scope
432    pub fn is_derived_table(&self) -> bool {
433        self.scope_type == ScopeType::DerivedTable
434    }
435
436    /// Check if this is a CTE scope
437    pub fn is_cte(&self) -> bool {
438        self.scope_type == ScopeType::Cte
439    }
440
441    /// Check if this is the root scope
442    pub fn is_root(&self) -> bool {
443        self.scope_type == ScopeType::Root
444    }
445
446    /// Check if this is a UDTF scope
447    pub fn is_udtf(&self) -> bool {
448        self.scope_type == ScopeType::Udtf
449    }
450
451    /// Check if this is a union/set operation scope
452    pub fn is_union(&self) -> bool {
453        self.scope_type == ScopeType::SetOperation
454    }
455
456    /// Traverse all scopes in this tree (depth-first post-order)
457    pub fn traverse(&self) -> Vec<&Scope> {
458        let mut result = Vec::new();
459        self.traverse_impl(&mut result);
460        result
461    }
462
463    fn traverse_impl<'a>(&'a self, result: &mut Vec<&'a Scope>) {
464        // First traverse children
465        for scope in &self.cte_scopes {
466            scope.traverse_impl(result);
467        }
468        for scope in &self.union_scopes {
469            scope.traverse_impl(result);
470        }
471        for scope in &self.table_scopes {
472            scope.traverse_impl(result);
473        }
474        for scope in &self.subquery_scopes {
475            scope.traverse_impl(result);
476        }
477        // Then add self
478        result.push(self);
479    }
480
481    /// Count references to each scope in this tree
482    pub fn ref_count(&self) -> HashMap<usize, usize> {
483        let mut counts: HashMap<usize, usize> = HashMap::new();
484
485        for scope in self.traverse() {
486            for (_, source_info) in scope.sources.iter() {
487                if source_info.is_scope {
488                    let id = &source_info.expression as *const _ as usize;
489                    *counts.entry(id).or_insert(0) += 1;
490                }
491            }
492        }
493
494        counts
495    }
496}
497
498/// Collect all column references from an expression tree
499fn collect_columns(expr: &Expression, columns: &mut Vec<ColumnRef>) {
500    match expr {
501        Expression::Column(col) => {
502            columns.push(ColumnRef {
503                table: col.table.as_ref().map(|t| t.name.clone()),
504                name: col.name.name.clone(),
505            });
506        }
507        Expression::Select(select) => {
508            // Collect from SELECT expressions
509            for e in &select.expressions {
510                collect_columns(e, columns);
511            }
512            // Collect from JOIN ON / MATCH_CONDITION clauses
513            for join in &select.joins {
514                if let Some(on) = &join.on {
515                    collect_columns(on, columns);
516                }
517                if let Some(match_condition) = &join.match_condition {
518                    collect_columns(match_condition, columns);
519                }
520            }
521            // Collect from WHERE
522            if let Some(where_clause) = &select.where_clause {
523                collect_columns(&where_clause.this, columns);
524            }
525            // Collect from HAVING
526            if let Some(having) = &select.having {
527                collect_columns(&having.this, columns);
528            }
529            // Collect from ORDER BY
530            if let Some(order_by) = &select.order_by {
531                for ord in &order_by.expressions {
532                    collect_columns(&ord.this, columns);
533                }
534            }
535            // Collect from GROUP BY
536            if let Some(group_by) = &select.group_by {
537                for e in &group_by.expressions {
538                    collect_columns(e, columns);
539                }
540            }
541            // Note: We don't recurse into FROM/JOIN source subqueries here
542            // as those create their own scopes.
543        }
544        // Binary operations
545        Expression::And(bin)
546        | Expression::Or(bin)
547        | Expression::Add(bin)
548        | Expression::Sub(bin)
549        | Expression::Mul(bin)
550        | Expression::Div(bin)
551        | Expression::Mod(bin)
552        | Expression::Eq(bin)
553        | Expression::Neq(bin)
554        | Expression::Lt(bin)
555        | Expression::Lte(bin)
556        | Expression::Gt(bin)
557        | Expression::Gte(bin)
558        | Expression::BitwiseAnd(bin)
559        | Expression::BitwiseOr(bin)
560        | Expression::BitwiseXor(bin)
561        | Expression::Concat(bin) => {
562            collect_columns(&bin.left, columns);
563            collect_columns(&bin.right, columns);
564        }
565        // LIKE/ILIKE operations
566        Expression::Like(like) | Expression::ILike(like) => {
567            collect_columns(&like.left, columns);
568            collect_columns(&like.right, columns);
569            if let Some(escape) = &like.escape {
570                collect_columns(escape, columns);
571            }
572        }
573        // Unary operations
574        Expression::Not(un) | Expression::Neg(un) | Expression::BitwiseNot(un) => {
575            collect_columns(&un.this, columns);
576        }
577        Expression::Function(func) => {
578            for arg in &func.args {
579                collect_columns(arg, columns);
580            }
581        }
582        Expression::AggregateFunction(agg) => {
583            for arg in &agg.args {
584                collect_columns(arg, columns);
585            }
586        }
587        Expression::WindowFunction(wf) => {
588            collect_columns(&wf.this, columns);
589            for e in &wf.over.partition_by {
590                collect_columns(e, columns);
591            }
592            for e in &wf.over.order_by {
593                collect_columns(&e.this, columns);
594            }
595        }
596        Expression::Alias(alias) => {
597            collect_columns(&alias.this, columns);
598        }
599        Expression::Case(case) => {
600            if let Some(operand) = &case.operand {
601                collect_columns(operand, columns);
602            }
603            for (when_expr, then_expr) in &case.whens {
604                collect_columns(when_expr, columns);
605                collect_columns(then_expr, columns);
606            }
607            if let Some(else_clause) = &case.else_ {
608                collect_columns(else_clause, columns);
609            }
610        }
611        Expression::Paren(paren) => {
612            collect_columns(&paren.this, columns);
613        }
614        Expression::Ordered(ord) => {
615            collect_columns(&ord.this, columns);
616        }
617        Expression::In(in_expr) => {
618            collect_columns(&in_expr.this, columns);
619            for e in &in_expr.expressions {
620                collect_columns(e, columns);
621            }
622            // Note: in_expr.query is a subquery - creates its own scope
623        }
624        Expression::Between(between) => {
625            collect_columns(&between.this, columns);
626            collect_columns(&between.low, columns);
627            collect_columns(&between.high, columns);
628        }
629        Expression::IsNull(is_null) => {
630            collect_columns(&is_null.this, columns);
631        }
632        Expression::Cast(cast) => {
633            collect_columns(&cast.this, columns);
634        }
635        Expression::Extract(extract) => {
636            collect_columns(&extract.this, columns);
637        }
638        Expression::Exists(_) | Expression::Subquery(_) => {
639            // These create their own scopes - don't collect from here
640        }
641        Expression::Prepare(prepare) => {
642            collect_columns(&prepare.statement, columns);
643        }
644        _ => {
645            // For other expressions, we might need to add more cases
646        }
647    }
648}
649
650/// Build scope tree from an expression
651///
652/// This traverses the expression tree and builds a hierarchy of Scope objects
653/// that track sources and column references at each level.
654pub fn build_scope(expression: &Expression) -> Scope {
655    build_scope_with_ctes(expression, &HashMap::new())
656}
657
658/// Build a query scope with CTE definitions inherited from its lexical parent.
659pub(crate) fn build_scope_with_ctes(
660    expression: &Expression,
661    ctes: &HashMap<String, SourceInfo>,
662) -> Scope {
663    let mut root = Scope::new(expression.clone());
664    root.cte_sources = ctes.clone();
665    build_scope_impl(expression, &mut root);
666    root
667}
668
669fn build_scope_impl(expression: &Expression, current_scope: &mut Scope) {
670    match expression {
671        Expression::Prepare(prepare) => {
672            build_scope_impl(&prepare.statement, current_scope);
673        }
674        Expression::Select(select) => {
675            // Process CTEs first
676            if let Some(with) = &select.with {
677                process_ctes(with, current_scope);
678            }
679
680            // Register relations in order. CTE declarations are available for
681            // FROM lookup, but only selected preceding bindings are lateral inputs.
682            let mut preceding_sources = HashSet::new();
683            for table in select.from.iter().flat_map(|from| &from.expressions) {
684                add_relation_to_scope(table, current_scope, &mut preceding_sources);
685            }
686            for join in &select.joins {
687                if matches!(join.kind, JoinKind::Array | JoinKind::LeftArray) {
688                    add_array_join_to_scope(&join.this, current_scope, &mut preceding_sources);
689                } else {
690                    add_relation_to_scope(&join.this, current_scope, &mut preceding_sources);
691                }
692            }
693
694            // Process table-generating lateral views (Hive/Spark style UDTFs).
695            for lateral_view in &select.lateral_views {
696                add_lateral_view_to_scope(lateral_view, current_scope);
697            }
698
699            // Process subqueries in WHERE, SELECT expressions, etc.
700            collect_subqueries(expression, current_scope);
701        }
702        Expression::JoinedTable(joined) => {
703            let mut preceding_sources = HashSet::new();
704            add_join_group_to_scope(joined, current_scope, &mut preceding_sources);
705        }
706        Expression::Union(union) => {
707            if let Some(with) = &union.with {
708                process_ctes(with, current_scope);
709            }
710
711            let mut left_scope = current_scope.branch(union.left.clone(), ScopeType::SetOperation);
712            build_scope_impl(&union.left, &mut left_scope);
713
714            let mut right_scope =
715                current_scope.branch(union.right.clone(), ScopeType::SetOperation);
716            build_scope_impl(&union.right, &mut right_scope);
717
718            current_scope.union_scopes.push(left_scope);
719            current_scope.union_scopes.push(right_scope);
720        }
721        Expression::Intersect(intersect) => {
722            if let Some(with) = &intersect.with {
723                process_ctes(with, current_scope);
724            }
725
726            let mut left_scope =
727                current_scope.branch(intersect.left.clone(), ScopeType::SetOperation);
728            build_scope_impl(&intersect.left, &mut left_scope);
729
730            let mut right_scope =
731                current_scope.branch(intersect.right.clone(), ScopeType::SetOperation);
732            build_scope_impl(&intersect.right, &mut right_scope);
733
734            current_scope.union_scopes.push(left_scope);
735            current_scope.union_scopes.push(right_scope);
736        }
737        Expression::Except(except) => {
738            if let Some(with) = &except.with {
739                process_ctes(with, current_scope);
740            }
741
742            let mut left_scope = current_scope.branch(except.left.clone(), ScopeType::SetOperation);
743            build_scope_impl(&except.left, &mut left_scope);
744
745            let mut right_scope =
746                current_scope.branch(except.right.clone(), ScopeType::SetOperation);
747            build_scope_impl(&except.right, &mut right_scope);
748
749            current_scope.union_scopes.push(left_scope);
750            current_scope.union_scopes.push(right_scope);
751        }
752        Expression::CreateTable(create) => {
753            // Handle CREATE TABLE ... AS [WITH ...] SELECT ...
754            // Process CTEs if present
755            if let Some(with) = &create.with_cte {
756                process_ctes(with, current_scope);
757            }
758            // Traverse the AS SELECT body
759            if let Some(as_select) = &create.as_select {
760                build_scope_impl(as_select, current_scope);
761            }
762        }
763        Expression::Subquery(subquery) => {
764            build_scope_impl(&subquery.this, current_scope);
765        }
766        Expression::Paren(paren) => {
767            build_scope_impl(&paren.this, current_scope);
768        }
769        Expression::Alias(alias) => {
770            build_scope_impl(&alias.this, current_scope);
771        }
772        _ => {}
773    }
774}
775
776fn process_ctes(with: &crate::expressions::With, current_scope: &mut Scope) {
777    for cte in &with.ctes {
778        let cte_name = cte.alias.name.clone();
779        let cte_expr = Expression::Cte(Box::new(cte.clone()));
780        let mut cte_scope = current_scope.branch(cte_expr.clone(), ScopeType::Cte);
781
782        if with.recursive && cte_body_self_references(cte) {
783            cte_scope.add_cte_source(cte_name.clone(), cte_expr.clone());
784        }
785
786        build_scope_impl(&cte.this, &mut cte_scope);
787        current_scope.add_cte_source(cte_name, cte_expr);
788        current_scope.cte_scopes.push(cte_scope);
789    }
790}
791
792fn cte_body_self_references(cte: &crate::expressions::Cte) -> bool {
793    let cte_name = cte.alias.name.as_str();
794    !cte.this
795        .find_all(|expr| match expr {
796            Expression::Table(table) if table.schema.is_none() && table.catalog.is_none() => {
797                table.name.name.eq_ignore_ascii_case(cte_name)
798            }
799            _ => false,
800        })
801        .is_empty()
802}
803
804fn add_join_group_to_scope(
805    joined: &crate::expressions::JoinedTable,
806    scope: &mut Scope,
807    preceding_sources: &mut HashSet<String>,
808) {
809    add_relation_to_scope(&joined.left, scope, preceding_sources);
810    for join in &joined.joins {
811        add_relation_to_scope(&join.this, scope, preceding_sources);
812        if let Some(condition) = &join.on {
813            collect_subqueries_in_expr(condition, scope);
814        }
815        if let Some(condition) = &join.match_condition {
816            collect_subqueries_in_expr(condition, scope);
817        }
818    }
819    for lateral_view in &joined.lateral_views {
820        add_lateral_view_to_scope(lateral_view, scope);
821    }
822}
823
824/// Unaliased join groups expose each member to following lateral relations.
825fn add_relation_to_scope(
826    expr: &Expression,
827    scope: &mut Scope,
828    preceding_sources: &mut HashSet<String>,
829) {
830    match expr {
831        Expression::JoinedTable(joined) if joined.alias.is_none() => {
832            add_join_group_to_scope(joined, scope, preceding_sources);
833        }
834        Expression::Paren(paren) => add_relation_to_scope(&paren.this, scope, preceding_sources),
835        _ => {
836            if let Some(name) = add_table_to_scope(expr, scope, preceding_sources) {
837                preceding_sources.insert(name);
838            }
839        }
840    }
841}
842
843fn add_table_to_scope(
844    expr: &Expression,
845    scope: &mut Scope,
846    preceding_sources: &HashSet<String>,
847) -> Option<String> {
848    match expr {
849        Expression::Table(table) => {
850            let name = table
851                .alias
852                .as_ref()
853                .map(|a| a.name.clone())
854                .unwrap_or_else(|| table.name.name.clone());
855            let cte_source = if table.schema.is_none() && table.catalog.is_none() {
856                scope.cte_sources.get(&table.name.name).or_else(|| {
857                    scope
858                        .cte_sources
859                        .iter()
860                        .find(|(cte_name, _)| cte_name.eq_ignore_ascii_case(&table.name.name))
861                        .map(|(_, source)| source)
862                })
863            } else {
864                None
865            };
866
867            if let Some(source) = cte_source {
868                scope.add_source_info(name.clone(), source.clone());
869            } else {
870                let mut source = SourceInfo::new(expr.clone(), false, SourceKind::Table);
871                if let Some(alias) = &table.alias {
872                    source = source.with_alias(alias.name.clone());
873                }
874                scope.add_source_info(name.clone(), source);
875            }
876            Some(name)
877        }
878        Expression::Subquery(subquery) => {
879            let name = subquery
880                .alias
881                .as_ref()
882                .map(|a| a.name.clone())
883                .unwrap_or_default();
884
885            let mut derived_scope = scope.branch(subquery.this.clone(), ScopeType::DerivedTable);
886            if subquery.lateral {
887                derived_scope.is_lateral = true;
888                derived_scope.can_be_correlated = true;
889                derived_scope.lateral_sources = preceding_sources
890                    .iter()
891                    .filter_map(|name| {
892                        scope
893                            .sources
894                            .get(name)
895                            .map(|source| (name.clone(), source.clone()))
896                    })
897                    .collect();
898            }
899            build_scope_impl(&subquery.this, &mut derived_scope);
900
901            scope.add_source(name.clone(), expr.clone(), true);
902            scope.derived_table_scopes.push(derived_scope);
903            Some(name)
904        }
905        Expression::JoinedTable(joined) => {
906            let alias = joined.alias.as_ref()?;
907            let mut derived_scope = scope.branch(expr.clone(), ScopeType::DerivedTable);
908            build_scope_impl(expr, &mut derived_scope);
909            scope.add_source(alias.name.clone(), expr.clone(), true);
910            scope.derived_table_scopes.push(derived_scope);
911            Some(alias.name.clone())
912        }
913        Expression::Unnest(unnest) => {
914            if let Some(alias) = &unnest.alias {
915                scope.add_virtual_source(alias.name.clone(), expr.clone());
916            }
917            unnest.alias.as_ref().map(|alias| alias.name.clone())
918        }
919        Expression::Values(values) => {
920            let name = values
921                .alias
922                .as_ref()
923                .map(|alias| alias.name.clone())
924                .unwrap_or_default();
925            scope.add_virtual_source(name.clone(), expr.clone());
926            Some(name)
927        }
928        Expression::Alias(alias) if matches!(&alias.this, Expression::Unnest(_)) => {
929            scope.add_virtual_source(alias.alias.name.clone(), expr.clone());
930            Some(alias.alias.name.clone())
931        }
932        Expression::Alias(alias)
933            if is_query_like_relation(&alias.this)
934                || matches!(scope_query(&alias.this), Expression::JoinedTable(_)) =>
935        {
936            let outer_columns = alias
937                .column_aliases
938                .iter()
939                .map(|column| column.name.clone())
940                .collect::<Vec<_>>();
941            let mut derived_scope = scope.branch_with_options(
942                alias.this.clone(),
943                ScopeType::DerivedTable,
944                None,
945                None,
946                Some(outer_columns),
947            );
948            build_scope_impl(&alias.this, &mut derived_scope);
949
950            scope.add_source(alias.alias.name.clone(), expr.clone(), true);
951            scope.derived_table_scopes.push(derived_scope);
952            Some(alias.alias.name.clone())
953        }
954        Expression::Lateral(lateral) => {
955            if let Some(alias) = &lateral.alias {
956                scope.add_virtual_source(alias.clone(), expr.clone());
957            }
958            lateral.alias.clone()
959        }
960        Expression::LateralView(lateral_view) => {
961            add_lateral_view_to_scope(lateral_view, scope);
962            lateral_view
963                .table_alias
964                .as_ref()
965                .or_else(|| lateral_view.column_aliases.first())
966                .map(|alias| alias.name.clone())
967        }
968        Expression::Pivot(pivot) => {
969            let name =
970                pivot_source_name(&pivot.this, pivot.alias.as_ref().map(|a| a.name.as_str()));
971            scope.add_source_info(
972                name.clone(),
973                SourceInfo::new(expr.clone(), false, SourceKind::DerivedTable),
974            );
975            add_pivot_inner_scope(&pivot.this, scope);
976            Some(name)
977        }
978        Expression::Unpivot(unpivot) => {
979            let name = pivot_source_name(
980                &unpivot.this,
981                unpivot.alias.as_ref().map(|a| a.name.as_str()),
982            );
983            scope.add_source_info(
984                name.clone(),
985                SourceInfo::new(expr.clone(), false, SourceKind::DerivedTable),
986            );
987            add_pivot_inner_scope(&unpivot.this, scope);
988            Some(name)
989        }
990        Expression::Paren(paren) => add_table_to_scope(&paren.this, scope, preceding_sources),
991        _ => identifier_table(expr).and_then(|table| {
992            add_table_to_scope(
993                &Expression::Table(Box::new(table)),
994                scope,
995                preceding_sources,
996            )
997        }),
998    }
999}
1000
1001/// Snowflake `IDENTIFIER('db.schema.table')` names a table with a string literal.
1002fn identifier_table(expr: &Expression) -> Option<TableRef> {
1003    let (function, alias) = match expr {
1004        Expression::Function(function) => (function, None),
1005        Expression::Alias(alias) => match &alias.this {
1006            Expression::Function(function) => (function, Some(alias)),
1007            _ => return None,
1008        },
1009        _ => return None,
1010    };
1011    if !function.name.eq_ignore_ascii_case("IDENTIFIER") {
1012        return None;
1013    }
1014    let [Expression::Literal(literal)] = function.args.as_slice() else {
1015        return None;
1016    };
1017    let Literal::String(name) = literal.as_ref() else {
1018        return None;
1019    };
1020    let mut parts = identifier_table_parts(name)?;
1021    let mut table = TableRef::new("");
1022    table.name = parts.pop().filter(|part| !part.is_empty())?;
1023    table.schema = parts.pop();
1024    table.catalog = parts.pop();
1025    if let Some(alias) = alias {
1026        table.alias = Some(alias.alias.clone());
1027        table.column_aliases = alias.column_aliases.clone();
1028    }
1029    parts.is_empty().then_some(table)
1030}
1031
1032/// Split a literal object name without treating quoted dots as separators.
1033fn identifier_table_parts(name: &str) -> Option<Vec<Identifier>> {
1034    let mut chars = name.chars().peekable();
1035    let mut parts = Vec::new();
1036    loop {
1037        while chars.next_if(|c| c.is_whitespace()).is_some() {}
1038        let quoted = chars.next_if_eq(&'"').is_some();
1039        let mut part = String::new();
1040        if quoted {
1041            loop {
1042                match chars.next()? {
1043                    '"' if chars.next_if_eq(&'"').is_some() => part.push('"'),
1044                    '"' => break,
1045                    ch => part.push(ch),
1046                }
1047            }
1048        } else {
1049            while let Some(ch) =
1050                chars.next_if(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '$'))
1051            {
1052                part.push(ch);
1053            }
1054            if part.starts_with(|c: char| c.is_ascii_digit() || c == '$') {
1055                return None;
1056            }
1057        }
1058        // Snowflake's db..table notation refers to the PUBLIC schema.
1059        if part.is_empty() {
1060            if quoted || parts.len() != 1 || chars.peek() != Some(&'.') {
1061                return None;
1062            }
1063            part.push_str("PUBLIC");
1064        }
1065        parts.push(if quoted {
1066            Identifier::quoted(part)
1067        } else {
1068            Identifier::new(part)
1069        });
1070        while chars.next_if(|c| c.is_whitespace()).is_some() {}
1071        match chars.next() {
1072            None => return Some(parts),
1073            Some('.') if parts.len() < 3 => {}
1074            _ => return None,
1075        }
1076    }
1077}
1078
1079fn is_query_like_relation(expr: &Expression) -> bool {
1080    match expr {
1081        Expression::Select(_)
1082        | Expression::Values(_)
1083        | Expression::Subquery(_)
1084        | Expression::Union(_)
1085        | Expression::Intersect(_)
1086        | Expression::Except(_) => true,
1087        Expression::Paren(paren) => is_query_like_relation(&paren.this),
1088        _ => false,
1089    }
1090}
1091
1092fn pivot_source_name(source: &Expression, explicit_alias: Option<&str>) -> String {
1093    if let Some(alias) = explicit_alias {
1094        return alias.to_string();
1095    }
1096
1097    match source {
1098        Expression::Table(table) => table
1099            .alias
1100            .as_ref()
1101            .map(|alias| alias.name.clone())
1102            .unwrap_or_else(|| table.name.name.clone()),
1103        Expression::Subquery(subquery) => subquery
1104            .alias
1105            .as_ref()
1106            .map(|alias| alias.name.clone())
1107            .unwrap_or_else(|| "_0".to_string()),
1108        Expression::Paren(paren) => pivot_source_name(&paren.this, explicit_alias),
1109        _ => "_0".to_string(),
1110    }
1111}
1112
1113fn add_pivot_inner_scope(source: &Expression, scope: &mut Scope) {
1114    match source {
1115        Expression::Subquery(subquery) => {
1116            let mut derived_scope = scope.branch(subquery.this.clone(), ScopeType::DerivedTable);
1117            build_scope_impl(&subquery.this, &mut derived_scope);
1118            scope.derived_table_scopes.push(derived_scope);
1119        }
1120        Expression::Paren(paren) => add_pivot_inner_scope(&paren.this, scope),
1121        _ => {}
1122    }
1123}
1124
1125fn add_lateral_view_to_scope(lateral_view: &crate::expressions::LateralView, scope: &mut Scope) {
1126    let alias = lateral_view
1127        .table_alias
1128        .as_ref()
1129        .or_else(|| lateral_view.column_aliases.first())
1130        .map(|alias| alias.name.clone());
1131
1132    if let Some(alias) = alias {
1133        scope.add_virtual_source(
1134            alias,
1135            Expression::LateralView(Box::new(lateral_view.clone())),
1136        );
1137    }
1138}
1139
1140/// ClickHouse `ARRAY JOIN arr AS item` binds each aliased array as a virtual source.
1141fn add_array_join_to_scope(
1142    expr: &Expression,
1143    scope: &mut Scope,
1144    preceding_sources: &mut HashSet<String>,
1145) {
1146    let items = match expr {
1147        Expression::Tuple(tuple) => tuple.expressions.as_slice(),
1148        item => std::slice::from_ref(item),
1149    };
1150    for item in items {
1151        if let Expression::Alias(alias) = item {
1152            scope.add_virtual_source(alias.alias.name.clone(), item.clone());
1153            preceding_sources.insert(alias.alias.name.clone());
1154        }
1155    }
1156}
1157
1158fn collect_subqueries(expr: &Expression, parent_scope: &mut Scope) {
1159    if matches!(expr, Expression::Select(_)) {
1160        use crate::ast_children::ChildPathSegment::{Field, Index};
1161
1162        // Include JOIN predicates, ORDER BY and QUALIFY as well as projections
1163        // and WHERE, but do not register FROM/JOIN queries again as scalar
1164        // subqueries (including derived tables without aliases).
1165        crate::ast_children::for_each_child(expr, |path, child| {
1166            if !matches!(
1167                path,
1168                [Field("from" | "with" | "lateral_views"), ..]
1169                    | [Field("joins"), Index(_), Field("this"), ..]
1170            ) {
1171                collect_subqueries_in_expr(child, parent_scope);
1172            }
1173        });
1174    }
1175}
1176
1177fn collect_subqueries_in_expr(expr: &Expression, parent_scope: &mut Scope) {
1178    let mut seen = HashSet::new();
1179    for node in walk_in_scope(expr, false) {
1180        let query = match node {
1181            Expression::Subquery(subquery) if subquery.alias.is_none() => Some(&subquery.this),
1182            Expression::Exists(exists) => Some(&exists.this),
1183            Expression::In(in_expr) => in_expr.query.as_ref(),
1184            Expression::Any(quantified) | Expression::All(quantified) => Some(&quantified.subquery),
1185            _ => None,
1186        };
1187
1188        let Some(query) = query else {
1189            continue;
1190        };
1191
1192        let key = query as *const Expression as usize;
1193        if !seen.insert(key) {
1194            continue;
1195        }
1196
1197        let mut sub_scope = parent_scope.branch(query.clone(), ScopeType::Subquery);
1198        build_scope_impl(query, &mut sub_scope);
1199        parent_scope.subquery_scopes.push(sub_scope);
1200    }
1201}
1202
1203/// Walk within a scope, yielding expressions without crossing scope boundaries.
1204///
1205/// This iterator visits all nodes in the syntax tree, stopping at nodes that
1206/// start child scopes (CTEs, derived tables, subqueries in FROM/JOIN).
1207///
1208/// # Arguments
1209/// * `expression` - The expression to walk
1210/// * `bfs` - If true, uses breadth-first search; otherwise uses depth-first search
1211///
1212/// # Returns
1213/// An iterator over expressions within the scope
1214pub fn walk_in_scope<'a>(
1215    expression: &'a Expression,
1216    bfs: bool,
1217) -> impl Iterator<Item = &'a Expression> {
1218    WalkInScopeIter::new(expression, bfs)
1219}
1220
1221/// Iterator for walking within a scope
1222struct WalkInScopeIter<'a> {
1223    queue: VecDeque<&'a Expression>,
1224    bfs: bool,
1225}
1226
1227impl<'a> WalkInScopeIter<'a> {
1228    fn new(expression: &'a Expression, bfs: bool) -> Self {
1229        let mut queue = VecDeque::new();
1230        queue.push_back(expression);
1231        Self { queue, bfs }
1232    }
1233
1234    fn should_stop_at(&self, expr: &Expression, is_root: bool) -> bool {
1235        if is_root {
1236            return false;
1237        }
1238
1239        // Stop at CTE definitions
1240        if matches!(expr, Expression::Cte(_)) {
1241            return true;
1242        }
1243
1244        // Stop at subqueries that are derived tables (in FROM/JOIN)
1245        if let Expression::Subquery(subquery) = expr {
1246            if subquery.alias.is_some() {
1247                return true;
1248            }
1249        }
1250
1251        // Stop at standalone SELECT/UNION/etc that would be subqueries
1252        if matches!(
1253            expr,
1254            Expression::Select(_)
1255                | Expression::Union(_)
1256                | Expression::Intersect(_)
1257                | Expression::Except(_)
1258        ) {
1259            return true;
1260        }
1261
1262        false
1263    }
1264
1265    fn get_children(&self, expr: &'a Expression) -> Vec<&'a Expression> {
1266        let mut children = Vec::new();
1267
1268        match expr {
1269            Expression::Prepare(prepare) => {
1270                children.push(&prepare.statement);
1271            }
1272            Expression::Select(select) => {
1273                // Walk SELECT expressions
1274                for e in &select.expressions {
1275                    children.push(e);
1276                }
1277                // Walk FROM (but tables/subqueries create new scopes)
1278                if let Some(from) = &select.from {
1279                    for table in &from.expressions {
1280                        if !self.should_stop_at(table, false) {
1281                            children.push(table);
1282                        }
1283                    }
1284                }
1285                // Walk JOINs (but their sources create new scopes)
1286                for join in &select.joins {
1287                    if !self.should_stop_at(&join.this, false) {
1288                        children.push(&join.this);
1289                    }
1290                    if let Some(on) = &join.on {
1291                        children.push(on);
1292                    }
1293                    if let Some(condition) = &join.match_condition {
1294                        children.push(condition);
1295                    }
1296                }
1297                // Walk WHERE
1298                if let Some(where_clause) = &select.where_clause {
1299                    children.push(&where_clause.this);
1300                }
1301                // Walk GROUP BY
1302                if let Some(group_by) = &select.group_by {
1303                    for e in &group_by.expressions {
1304                        children.push(e);
1305                    }
1306                }
1307                // Walk HAVING
1308                if let Some(having) = &select.having {
1309                    children.push(&having.this);
1310                }
1311                if let Some(qualify) = &select.qualify {
1312                    children.push(&qualify.this);
1313                }
1314                // Walk ORDER BY
1315                if let Some(order_by) = &select.order_by {
1316                    for ord in &order_by.expressions {
1317                        children.push(&ord.this);
1318                    }
1319                }
1320                // Walk LIMIT
1321                if let Some(limit) = &select.limit {
1322                    children.push(&limit.this);
1323                }
1324                // Walk OFFSET
1325                if let Some(offset) = &select.offset {
1326                    children.push(&offset.this);
1327                }
1328            }
1329            Expression::And(bin)
1330            | Expression::Or(bin)
1331            | Expression::Add(bin)
1332            | Expression::Sub(bin)
1333            | Expression::Mul(bin)
1334            | Expression::Div(bin)
1335            | Expression::Mod(bin)
1336            | Expression::Eq(bin)
1337            | Expression::Neq(bin)
1338            | Expression::Lt(bin)
1339            | Expression::Lte(bin)
1340            | Expression::Gt(bin)
1341            | Expression::Gte(bin)
1342            | Expression::BitwiseAnd(bin)
1343            | Expression::BitwiseOr(bin)
1344            | Expression::BitwiseXor(bin)
1345            | Expression::Concat(bin) => {
1346                children.push(&bin.left);
1347                children.push(&bin.right);
1348            }
1349            Expression::Like(like) | Expression::ILike(like) => {
1350                children.push(&like.left);
1351                children.push(&like.right);
1352                if let Some(escape) = &like.escape {
1353                    children.push(escape);
1354                }
1355            }
1356            Expression::Not(un) | Expression::Neg(un) | Expression::BitwiseNot(un) => {
1357                children.push(&un.this);
1358            }
1359            Expression::Function(func) => {
1360                for arg in &func.args {
1361                    children.push(arg);
1362                }
1363            }
1364            Expression::AggregateFunction(agg) => {
1365                for arg in &agg.args {
1366                    children.push(arg);
1367                }
1368            }
1369            Expression::WindowFunction(wf) => {
1370                children.push(&wf.this);
1371                for e in &wf.over.partition_by {
1372                    children.push(e);
1373                }
1374                for e in &wf.over.order_by {
1375                    children.push(&e.this);
1376                }
1377            }
1378            Expression::Alias(alias) => {
1379                children.push(&alias.this);
1380            }
1381            Expression::Case(case) => {
1382                if let Some(operand) = &case.operand {
1383                    children.push(operand);
1384                }
1385                for (when_expr, then_expr) in &case.whens {
1386                    children.push(when_expr);
1387                    children.push(then_expr);
1388                }
1389                if let Some(else_clause) = &case.else_ {
1390                    children.push(else_clause);
1391                }
1392            }
1393            Expression::Paren(paren) => {
1394                children.push(&paren.this);
1395            }
1396            Expression::Ordered(ord) => {
1397                children.push(&ord.this);
1398            }
1399            Expression::In(in_expr) => {
1400                children.push(&in_expr.this);
1401                for e in &in_expr.expressions {
1402                    children.push(e);
1403                }
1404                // Note: in_expr.query creates a new scope - don't traverse
1405            }
1406            Expression::Between(between) => {
1407                children.push(&between.this);
1408                children.push(&between.low);
1409                children.push(&between.high);
1410            }
1411            Expression::IsNull(is_null) => {
1412                children.push(&is_null.this);
1413            }
1414            Expression::Cast(cast) => {
1415                children.push(&cast.this);
1416            }
1417            Expression::Extract(extract) => {
1418                children.push(&extract.this);
1419            }
1420            Expression::Coalesce(coalesce) => {
1421                for e in &coalesce.expressions {
1422                    children.push(e);
1423                }
1424            }
1425            Expression::NullIf(nullif) => {
1426                children.push(&nullif.this);
1427                children.push(&nullif.expression);
1428            }
1429            Expression::Table(_table) => {
1430                // Tables don't have child expressions to traverse within scope
1431                // (joins are handled at the Select level)
1432            }
1433            Expression::TryCatch(try_catch) => {
1434                for stmt in &try_catch.try_body {
1435                    children.push(stmt);
1436                }
1437                if let Some(catch_body) = &try_catch.catch_body {
1438                    for stmt in catch_body {
1439                        children.push(stmt);
1440                    }
1441                }
1442            }
1443            Expression::Column(_) | Expression::Literal(_) | Expression::Identifier(_) => {
1444                // Leaf nodes - no children
1445            }
1446            // Subqueries and Exists create new scopes - don't traverse into them
1447            Expression::Subquery(_) | Expression::Exists(_) => {}
1448            _ => {
1449                // Use the canonical AST traversal for other scalar expressions
1450                // (typed functions, field access, filters, etc.). Scope
1451                // boundaries are still enforced by should_stop_at.
1452                children.extend(expr.children());
1453            }
1454        }
1455
1456        children
1457    }
1458}
1459
1460impl<'a> Iterator for WalkInScopeIter<'a> {
1461    type Item = &'a Expression;
1462
1463    fn next(&mut self) -> Option<Self::Item> {
1464        let expr = if self.bfs {
1465            self.queue.pop_front()?
1466        } else {
1467            self.queue.pop_back()?
1468        };
1469
1470        // Get children that don't cross scope boundaries
1471        let children = self.get_children(expr);
1472
1473        if self.bfs {
1474            for child in children {
1475                if !self.should_stop_at(child, false) {
1476                    self.queue.push_back(child);
1477                }
1478            }
1479        } else {
1480            for child in children.into_iter().rev() {
1481                if !self.should_stop_at(child, false) {
1482                    self.queue.push_back(child);
1483                }
1484            }
1485        }
1486
1487        Some(expr)
1488    }
1489}
1490
1491/// Find the first expression matching the predicate within this scope.
1492///
1493/// This does NOT traverse into subscopes.
1494///
1495/// # Arguments
1496/// * `expression` - The root expression
1497/// * `predicate` - Function that returns true for matching expressions
1498/// * `bfs` - If true, uses breadth-first search; otherwise depth-first
1499///
1500/// # Returns
1501/// The first matching expression, or None
1502pub fn find_in_scope<'a, F>(
1503    expression: &'a Expression,
1504    predicate: F,
1505    bfs: bool,
1506) -> Option<&'a Expression>
1507where
1508    F: Fn(&Expression) -> bool,
1509{
1510    walk_in_scope(expression, bfs).find(|e| predicate(e))
1511}
1512
1513/// Find all expressions matching the predicate within this scope.
1514///
1515/// This does NOT traverse into subscopes.
1516///
1517/// # Arguments
1518/// * `expression` - The root expression
1519/// * `predicate` - Function that returns true for matching expressions
1520/// * `bfs` - If true, uses breadth-first search; otherwise depth-first
1521///
1522/// # Returns
1523/// A vector of matching expressions
1524pub fn find_all_in_scope<'a, F>(
1525    expression: &'a Expression,
1526    predicate: F,
1527    bfs: bool,
1528) -> Vec<&'a Expression>
1529where
1530    F: Fn(&Expression) -> bool,
1531{
1532    walk_in_scope(expression, bfs)
1533        .filter(|e| predicate(e))
1534        .collect()
1535}
1536
1537/// Traverse an expression by its "scopes".
1538///
1539/// Returns a list of all scopes in depth-first post-order.
1540///
1541/// # Arguments
1542/// * `expression` - The expression to traverse
1543///
1544/// # Returns
1545/// A vector of all scopes found
1546pub fn traverse_scope(expression: &Expression) -> Vec<Scope> {
1547    match expression {
1548        Expression::Select(_)
1549        | Expression::Union(_)
1550        | Expression::Intersect(_)
1551        | Expression::Except(_)
1552        | Expression::Prepare(_)
1553        | Expression::CreateTable(_) => {
1554            let root = build_scope(expression);
1555            root.traverse().into_iter().cloned().collect()
1556        }
1557        _ => Vec::new(),
1558    }
1559}
1560
1561#[cfg(test)]
1562mod tests {
1563    use super::*;
1564    use crate::parser::Parser;
1565
1566    fn parse_and_build_scope(sql: &str) -> Scope {
1567        let ast = Parser::parse_sql(sql).expect("Failed to parse SQL");
1568        build_scope(&ast[0])
1569    }
1570
1571    #[test]
1572    fn unaliased_join_members_are_visible_to_following_lateral_relations() {
1573        let scope = parse_and_build_scope(
1574            "SELECT d.id FROM alpha a JOIN (beta b JOIN gamma c ON b.id = c.id) \
1575             ON a.id = b.id CROSS JOIN LATERAL (SELECT c.id) AS d",
1576        );
1577        assert!(scope.sources.contains_key("a"));
1578        assert!(scope.sources.contains_key("b"));
1579        assert!(scope.sources.contains_key("c"));
1580        let lateral = scope.derived_table_scopes.last().expect("lateral scope");
1581        assert!(lateral.is_lateral);
1582        assert_eq!(
1583            lateral
1584                .lateral_sources
1585                .keys()
1586                .map(String::as_str)
1587                .collect::<HashSet<_>>(),
1588            HashSet::from(["a", "b", "c"]),
1589        );
1590    }
1591
1592    #[test]
1593    fn aliased_join_group_hides_member_bindings_from_its_parent() {
1594        let scope = parse_and_build_scope(
1595            "SELECT grouped.id FROM (beta b JOIN gamma c ON b.id = c.id) AS grouped",
1596        );
1597        assert_eq!(scope.sources.len(), 1);
1598        assert!(scope.sources.contains_key("grouped"));
1599        let group = scope
1600            .derived_table_scopes
1601            .first()
1602            .expect("join group scope");
1603        assert!(group.sources.contains_key("b"));
1604        assert!(group.sources.contains_key("c"));
1605    }
1606
1607    #[test]
1608    fn test_simple_select_scope() {
1609        let mut scope = parse_and_build_scope("SELECT a, b FROM t");
1610
1611        assert!(scope.is_root());
1612        assert!(!scope.can_be_correlated);
1613        assert!(scope.sources.contains_key("t"));
1614
1615        let columns = scope.columns();
1616        assert_eq!(columns.len(), 2);
1617    }
1618
1619    #[test]
1620    fn test_derived_table_scope() {
1621        let mut scope = parse_and_build_scope("SELECT x.a FROM (SELECT a FROM t) AS x");
1622
1623        assert!(scope.sources.contains_key("x"));
1624        assert_eq!(scope.derived_table_scopes.len(), 1);
1625
1626        let derived = &mut scope.derived_table_scopes[0];
1627        assert!(derived.is_derived_table());
1628        assert!(derived.sources.contains_key("t"));
1629    }
1630
1631    #[test]
1632    fn test_lateral_preceding_sources_472() {
1633        let scope = parse_and_build_scope(
1634            "WITH c AS (SELECT a FROM t) SELECT n.a FROM t, LATERAL (SELECT t.a) n, c",
1635        );
1636        let lateral = &scope.derived_table_scopes[0];
1637        assert!(lateral.is_lateral);
1638        assert!(lateral.can_be_correlated);
1639        assert_eq!(lateral.lateral_sources.len(), 1);
1640        assert!(lateral.lateral_sources.contains_key("t"));
1641        assert!(!lateral.sources.contains_key("t"));
1642        let scope = parse_and_build_scope("SELECT n.a FROM t, (SELECT t.a) n");
1643        assert!(!scope.derived_table_scopes[0].is_lateral);
1644        assert!(scope.derived_table_scopes[0].lateral_sources.is_empty());
1645    }
1646
1647    #[test]
1648    fn test_clickhouse_array_join_aliases_are_virtual_sources() {
1649        for (sql, aliases) in [
1650            ("SELECT item FROM t ARRAY JOIN arr AS item", &["item"][..]),
1651            (
1652                "SELECT a, b FROM t LEFT ARRAY JOIN arr AS a, t.nested AS b",
1653                &["a", "b"],
1654            ),
1655            ("SELECT arr FROM t ARRAY JOIN arr", &[]),
1656        ] {
1657            let ast = crate::parse_one(sql, crate::dialects::DialectType::ClickHouse).unwrap();
1658            let scope = build_scope(&ast);
1659            let mut virtual_sources: Vec<_> = scope
1660                .sources
1661                .iter()
1662                .filter(|(_, source)| source.kind == SourceKind::Virtual)
1663                .map(|(name, _)| name.as_str())
1664                .collect();
1665            virtual_sources.sort();
1666            assert_eq!(virtual_sources, aliases, "{sql}");
1667            assert_eq!(scope.sources.len(), aliases.len() + 1, "{sql}");
1668        }
1669    }
1670
1671    #[test]
1672    fn test_snowflake_identifier_string_is_a_table_source() {
1673        let ast = crate::parse_one(
1674            "SELECT o.id FROM IDENTIFIER('db.s.orders') AS o, IDENTIFIER('customers'), IDENTIFIER($t)",
1675            crate::dialects::DialectType::Snowflake,
1676        )
1677        .unwrap();
1678        let scope = build_scope(&ast);
1679        assert_eq!(scope.sources.len(), 2);
1680        let Expression::Table(orders) = &scope.sources["o"].expression else {
1681            panic!("expected a table source");
1682        };
1683        assert_eq!(orders.catalog.as_ref().unwrap().name, "db");
1684        assert_eq!(orders.schema.as_ref().unwrap().name, "s");
1685        assert_eq!(orders.name.name, "orders");
1686        assert_eq!(scope.sources["o"].kind, SourceKind::Table);
1687        assert!(scope.sources.contains_key("customers"));
1688    }
1689
1690    #[test]
1691    fn test_subquery_collection_respects_relation_boundaries() {
1692        let scope = parse_and_build_scope(
1693            "SELECT t.a FROM t JOIN (SELECT a FROM s) ON t.a = 1 \
1694             WHERE EXISTS (SELECT 1 FROM u) ORDER BY (SELECT MAX(a) FROM v)",
1695        );
1696        assert_eq!(scope.derived_table_scopes.len(), 1);
1697        assert_eq!(scope.subquery_scopes.len(), 2);
1698        let scope = parse_and_build_scope(
1699            "SELECT t.a FROM t JOIN s ON EXISTS (SELECT 1 FROM u WHERE u.a = t.a)",
1700        );
1701        assert_eq!(scope.subquery_scopes.len(), 1);
1702        assert!(scope.subquery_scopes[0].sources.contains_key("u"));
1703    }
1704
1705    #[test]
1706    fn test_non_correlated_subquery() {
1707        let mut scope = parse_and_build_scope("SELECT * FROM t WHERE EXISTS (SELECT b FROM s)");
1708
1709        assert_eq!(scope.subquery_scopes.len(), 1);
1710
1711        let subquery = &mut scope.subquery_scopes[0];
1712        assert!(subquery.is_subquery());
1713        assert!(subquery.can_be_correlated);
1714
1715        // The subquery references only 's', which is in its own sources
1716        assert!(subquery.sources.contains_key("s"));
1717        assert!(!subquery.is_correlated_subquery());
1718    }
1719
1720    #[test]
1721    fn test_correlated_subquery() {
1722        let mut scope =
1723            parse_and_build_scope("SELECT * FROM t WHERE EXISTS (SELECT b FROM s WHERE s.x = t.y)");
1724
1725        assert_eq!(scope.subquery_scopes.len(), 1);
1726
1727        let subquery = &mut scope.subquery_scopes[0];
1728        assert!(subquery.is_subquery());
1729        assert!(subquery.can_be_correlated);
1730
1731        // The subquery references 't.y' which is external
1732        let external = subquery.external_columns();
1733        assert!(!external.is_empty());
1734        assert!(external.iter().any(|c| c.table.as_deref() == Some("t")));
1735        assert!(subquery.is_correlated_subquery());
1736    }
1737
1738    #[test]
1739    fn test_cte_scope() {
1740        let scope = parse_and_build_scope("WITH cte AS (SELECT a FROM t) SELECT * FROM cte");
1741
1742        assert_eq!(scope.cte_scopes.len(), 1);
1743        assert!(scope.cte_sources.contains_key("cte"));
1744
1745        let cte = &scope.cte_scopes[0];
1746        assert!(cte.is_cte());
1747    }
1748
1749    #[test]
1750    fn test_multiple_sources() {
1751        let scope = parse_and_build_scope("SELECT t.a, s.b FROM t JOIN s ON t.id = s.id");
1752
1753        assert!(scope.sources.contains_key("t"));
1754        assert!(scope.sources.contains_key("s"));
1755        assert_eq!(scope.sources.len(), 2);
1756    }
1757
1758    #[test]
1759    fn test_aliased_table() {
1760        let scope = parse_and_build_scope("SELECT x.a FROM t AS x");
1761
1762        // Should be indexed by alias, not original name
1763        assert!(scope.sources.contains_key("x"));
1764        assert!(!scope.sources.contains_key("t"));
1765    }
1766
1767    #[test]
1768    fn test_local_columns() {
1769        let mut scope = parse_and_build_scope("SELECT t.a, t.b, s.c FROM t JOIN s ON t.id = s.id");
1770
1771        let local = scope.local_columns();
1772        // All columns are local since both t and s are in scope.
1773        // Includes JOIN ON references (t.id, s.id).
1774        assert_eq!(local.len(), 5);
1775        assert!(local.iter().all(|c| c.table.is_some()));
1776    }
1777
1778    #[test]
1779    fn test_columns_include_join_on_clause_references() {
1780        let mut scope = parse_and_build_scope(
1781            "SELECT o.total FROM orders o JOIN customers c ON c.id = o.customer_id",
1782        );
1783
1784        let cols: Vec<String> = scope
1785            .columns()
1786            .iter()
1787            .map(|c| match &c.table {
1788                Some(t) => format!("{}.{}", t, c.name),
1789                None => c.name.clone(),
1790            })
1791            .collect();
1792
1793        assert!(cols.contains(&"o.total".to_string()));
1794        assert!(cols.contains(&"c.id".to_string()));
1795        assert!(cols.contains(&"o.customer_id".to_string()));
1796    }
1797
1798    #[test]
1799    fn test_unqualified_columns() {
1800        let mut scope = parse_and_build_scope("SELECT a, b, t.c FROM t");
1801
1802        let unqualified = scope.unqualified_columns();
1803        // Only a and b are unqualified
1804        assert_eq!(unqualified.len(), 2);
1805        assert!(unqualified.iter().all(|c| c.table.is_none()));
1806    }
1807
1808    #[test]
1809    fn test_source_columns() {
1810        let mut scope = parse_and_build_scope("SELECT t.a, t.b, s.c FROM t JOIN s ON t.id = s.id");
1811
1812        let t_cols = scope.source_columns("t");
1813        // t.a, t.b, and t.id from JOIN condition
1814        assert!(t_cols.len() >= 2);
1815        assert!(t_cols.iter().all(|c| c.table.as_deref() == Some("t")));
1816
1817        let s_cols = scope.source_columns("s");
1818        // s.c and s.id from JOIN condition
1819        assert!(s_cols.len() >= 1);
1820        assert!(s_cols.iter().all(|c| c.table.as_deref() == Some("s")));
1821    }
1822
1823    #[test]
1824    fn test_rename_source() {
1825        let mut scope = parse_and_build_scope("SELECT a FROM t");
1826
1827        assert!(scope.sources.contains_key("t"));
1828        scope.rename_source("t", "new_name".to_string());
1829        assert!(!scope.sources.contains_key("t"));
1830        assert!(scope.sources.contains_key("new_name"));
1831    }
1832
1833    #[test]
1834    fn test_remove_source() {
1835        let mut scope = parse_and_build_scope("SELECT a FROM t");
1836
1837        assert!(scope.sources.contains_key("t"));
1838        scope.remove_source("t");
1839        assert!(!scope.sources.contains_key("t"));
1840    }
1841
1842    #[test]
1843    fn test_walk_in_scope() {
1844        let ast = Parser::parse_sql("SELECT a, b FROM t WHERE a > 1").expect("Failed to parse");
1845        let expr = &ast[0];
1846
1847        // Walk should visit all expressions within the scope
1848        let walked: Vec<_> = walk_in_scope(expr, true).collect();
1849        assert!(!walked.is_empty());
1850
1851        // Should include the root SELECT
1852        assert!(walked.iter().any(|e| matches!(e, Expression::Select(_))));
1853        // Should include columns
1854        assert!(walked.iter().any(|e| matches!(e, Expression::Column(_))));
1855    }
1856
1857    #[test]
1858    fn test_find_in_scope() {
1859        let ast = Parser::parse_sql("SELECT a, b FROM t WHERE a > 1").expect("Failed to parse");
1860        let expr = &ast[0];
1861
1862        // Find the first column
1863        let found = find_in_scope(expr, |e| matches!(e, Expression::Column(_)), true);
1864        assert!(found.is_some());
1865        assert!(matches!(found.unwrap(), Expression::Column(_)));
1866    }
1867
1868    #[test]
1869    fn test_find_all_in_scope() {
1870        let ast = Parser::parse_sql("SELECT a, b, c FROM t").expect("Failed to parse");
1871        let expr = &ast[0];
1872
1873        // Find all columns
1874        let found = find_all_in_scope(expr, |e| matches!(e, Expression::Column(_)), true);
1875        assert_eq!(found.len(), 3);
1876    }
1877
1878    #[test]
1879    fn test_traverse_scope() {
1880        let ast =
1881            Parser::parse_sql("SELECT a FROM (SELECT b FROM t) AS x").expect("Failed to parse");
1882        let expr = &ast[0];
1883
1884        let scopes = traverse_scope(expr);
1885        // traverse_scope returns all scopes via Scope::traverse
1886        // which includes derived table and root scopes
1887        assert!(!scopes.is_empty());
1888        // The root scope is always included
1889        assert!(scopes.iter().any(|s| s.is_root()));
1890    }
1891
1892    #[test]
1893    fn test_branch_with_options() {
1894        let ast = Parser::parse_sql("SELECT a FROM t").expect("Failed to parse");
1895        let scope = build_scope(&ast[0]);
1896
1897        let child = scope.branch_with_options(
1898            ast[0].clone(),
1899            ScopeType::Subquery, // Use Subquery to test can_be_correlated
1900            None,
1901            None,
1902            Some(vec!["col1".to_string(), "col2".to_string()]),
1903        );
1904
1905        assert_eq!(child.outer_columns, vec!["col1", "col2"]);
1906        assert!(child.can_be_correlated); // Subqueries are correlated
1907    }
1908
1909    #[test]
1910    fn test_is_udtf() {
1911        let ast = Parser::parse_sql("SELECT a FROM t").expect("Failed to parse");
1912        let scope = Scope::new(ast[0].clone());
1913        assert!(!scope.is_udtf());
1914
1915        let root = build_scope(&ast[0]);
1916        let udtf_scope = root.branch(ast[0].clone(), ScopeType::Udtf);
1917        assert!(udtf_scope.is_udtf());
1918    }
1919
1920    #[test]
1921    fn test_is_union() {
1922        let scope = parse_and_build_scope("SELECT a FROM t UNION SELECT b FROM s");
1923
1924        assert!(scope.is_root());
1925        assert_eq!(scope.union_scopes.len(), 2);
1926        // The children are set operation scopes
1927        assert!(scope.union_scopes[0].is_union());
1928        assert!(scope.union_scopes[1].is_union());
1929    }
1930
1931    #[test]
1932    fn test_union_output_columns() {
1933        let scope = parse_and_build_scope(
1934            "SELECT id, name FROM customers UNION ALL SELECT id, name FROM employees",
1935        );
1936        assert_eq!(scope.output_columns(), vec!["id", "name"]);
1937    }
1938
1939    #[test]
1940    fn test_clear_cache() {
1941        let mut scope = parse_and_build_scope("SELECT t.a FROM t");
1942
1943        // First call populates cache
1944        let _ = scope.columns();
1945        assert!(scope.columns_cache.is_some());
1946
1947        // Clear cache
1948        scope.clear_cache();
1949        assert!(scope.columns_cache.is_none());
1950        assert!(scope.external_columns_cache.is_none());
1951    }
1952
1953    #[test]
1954    fn test_scope_traverse() {
1955        let scope = parse_and_build_scope(
1956            "WITH cte AS (SELECT a FROM t) SELECT * FROM cte WHERE EXISTS (SELECT b FROM s)",
1957        );
1958
1959        let traversed = scope.traverse();
1960        // Should include: CTE scope, subquery scope, root scope
1961        assert!(traversed.len() >= 3);
1962    }
1963
1964    #[test]
1965    fn test_create_table_as_select_scope() {
1966        // Simple CTAS
1967        let scope = parse_and_build_scope("CREATE TABLE out_table AS SELECT 1 AS id FROM src");
1968        assert!(
1969            scope.sources.contains_key("src"),
1970            "CTAS scope should contain the FROM table"
1971        );
1972        assert!(
1973            !scope.sources.contains_key("out_table"),
1974            "CTAS target table should not be treated as a source"
1975        );
1976
1977        // CTAS with multiple FROM tables
1978        let scope = parse_and_build_scope(
1979            "CREATE TABLE out_table AS SELECT a.id FROM foo AS a JOIN bar AS b ON a.id = b.id",
1980        );
1981        assert!(scope.sources.contains_key("a"));
1982        assert!(scope.sources.contains_key("b"));
1983        assert!(
1984            !scope.sources.contains_key("out_table"),
1985            "CTAS target table should not be treated as a source"
1986        );
1987
1988        // CTAS with CTEs
1989        let scope = parse_and_build_scope(
1990            "CREATE TABLE out_table AS WITH cte AS (SELECT 1 AS id FROM src) SELECT * FROM cte",
1991        );
1992        assert!(
1993            scope.sources.contains_key("cte"),
1994            "CTAS with CTE should resolve CTE as source"
1995        );
1996        assert!(
1997            !scope.sources.contains_key("out_table"),
1998            "CTAS target table should not be treated as a source"
1999        );
2000        assert_eq!(scope.cte_scopes.len(), 1);
2001    }
2002
2003    #[test]
2004    fn test_create_table_as_select_traverse() {
2005        let ast = Parser::parse_sql("CREATE TABLE t AS SELECT a FROM src").unwrap();
2006        let scopes = traverse_scope(&ast[0]);
2007        assert!(
2008            !scopes.is_empty(),
2009            "traverse_scope should return scopes for CTAS"
2010        );
2011    }
2012}