Skip to main content

uqa_sql/catalog/stored_ast/
mod.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Traversal and identity binding for durable SQL syntax trees.
8
9use crate::{
10    ast::{Expr, FrameBound, FromClause, SelectStmt, Statement},
11    SQLError,
12};
13use std::collections::BTreeSet;
14use uqa_core::RelationIdentity;
15mod expressions;
16mod literals;
17mod merge;
18mod routines;
19mod sites;
20mod sources;
21mod types;
22pub use expressions::*;
23pub use literals::read_unknown_stored_literal;
24pub use merge::visit_stored_statement_merges;
25pub use routines::*;
26pub use sites::*;
27pub use sources::*;
28pub use types::*;
29
30pub type MergeCallback<'a> = &'a mut dyn FnMut(&mut crate::ast::MergeStmt) -> Result<(), SQLError>;
31
32pub type ExpressionCallback<'a> = &'a mut dyn FnMut(&mut Expr) -> Result<(), SQLError>;
33pub type ProjectionCallback<'a> =
34    &'a mut dyn FnMut(&mut crate::ast::Projection) -> Result<(), SQLError>;
35pub type SourceCallback<'a> = &'a mut dyn FnMut(&mut FromClause) -> Result<(), SQLError>;
36
37pub struct StoredAstVisitor<'a, R, F> {
38    pub source: Option<SourceCallback<'a>>,
39    pub merge: Option<MergeCallback<'a>>,
40    pub expression: Option<ExpressionCallback<'a>>,
41    /// Called for each item of a select list or `RETURNING` list before its expression is visited.
42    pub projection: Option<ProjectionCallback<'a>>,
43    pub ty: Option<&'a mut dyn FnMut(&mut String)>,
44    pub relation: &'a mut R,
45    pub routine: &'a mut F,
46}
47
48impl<R, F> StoredAstVisitor<'_, R, F>
49where
50    R: FnMut(&mut String) -> Result<(), SQLError>,
51    F: FnMut(&mut String, Option<&mut Option<crate::ast::FunctionBinding>>) -> Result<(), SQLError>,
52{
53    pub fn bind_statement(&mut self, statement: &mut Statement) -> Result<(), SQLError> {
54        let ctes = BTreeSet::new();
55        match statement {
56            Statement::Select(query) => self.bind_select(query, &ctes),
57            Statement::Insert(insert) => self.bind_insert(insert, &ctes),
58            Statement::Update(update) => self.bind_update(update, &ctes),
59            Statement::Delete(delete) => self.bind_delete(delete, &ctes),
60            Statement::Notify { .. } => Ok(()),
61            Statement::Values { rows } => {
62                for expression in rows.iter_mut().flatten() {
63                    self.bind_expr(expression, &ctes)?;
64                }
65                Ok(())
66            }
67            Statement::Merge(merge) => self.bind_merge(merge, &ctes),
68            _ => Err(SQLError::Internal(
69                "catalog-owned statement has an unsupported dependency shape".into(),
70            )),
71        }
72    }
73
74    fn bind_insert(
75        &mut self,
76        insert: &mut crate::ast::InsertStmt,
77        inherited: &BTreeSet<String>,
78    ) -> Result<(), SQLError> {
79        (self.relation)(&mut insert.table)?;
80        let visible = self.bind_ctes(&mut insert.with, inherited)?;
81        if let Some(source) = insert.select_source.as_deref_mut() {
82            self.bind_select(source, &visible)?;
83        }
84        for expression in insert
85            .columns
86            .iter_mut()
87            .flat_map(crate::ast::AssignmentTarget::expressions_mut)
88            .chain(insert.rows.iter_mut().flatten())
89        {
90            self.bind_expr(expression, &visible)?;
91        }
92        if let Some(conflict) = &mut insert.on_conflict {
93            for expression in &mut conflict.expressions {
94                self.bind_expr(expression, &visible)?;
95            }
96            if let Some(predicate) = conflict.predicate.as_deref_mut() {
97                self.bind_expr(predicate, &visible)?;
98            }
99            if let crate::ast::OnConflictAction::Update {
100                assignments,
101                r#where,
102            } = &mut conflict.action
103            {
104                for expression in assignments.iter_mut().flat_map(|(target, value)| {
105                    target.expressions_mut().chain(std::iter::once(value))
106                }) {
107                    self.bind_expr(expression, &visible)?;
108                }
109                if let Some(expression) = r#where {
110                    self.bind_expr(expression, &visible)?;
111                }
112            }
113        }
114        for projection in &mut insert.returning {
115            self.bind_projection(projection, &visible)?;
116        }
117        Ok(())
118    }
119
120    fn bind_update(
121        &mut self,
122        update: &mut crate::ast::UpdateStmt,
123        inherited: &BTreeSet<String>,
124    ) -> Result<(), SQLError> {
125        (self.relation)(&mut update.table)?;
126        let visible = self.bind_ctes(&mut update.with, inherited)?;
127        if let Some(source) = &mut update.from {
128            self.bind_from(source, &visible)?;
129        }
130        for expression in update
131            .assignments
132            .iter_mut()
133            .flat_map(|(target, value)| target.expressions_mut().chain(std::iter::once(value)))
134        {
135            self.bind_expr(expression, &visible)?;
136        }
137        if let Some(expression) = &mut update.r#where {
138            self.bind_expr(expression, &visible)?;
139        }
140        for projection in &mut update.returning {
141            self.bind_projection(projection, &visible)?;
142        }
143        Ok(())
144    }
145
146    fn bind_delete(
147        &mut self,
148        delete: &mut crate::ast::DeleteStmt,
149        inherited: &BTreeSet<String>,
150    ) -> Result<(), SQLError> {
151        (self.relation)(&mut delete.table)?;
152        let visible = self.bind_ctes(&mut delete.with, inherited)?;
153        if let Some(source) = &mut delete.using {
154            self.bind_from(source, &visible)?;
155        }
156        if let Some(expression) = &mut delete.r#where {
157            self.bind_expr(expression, &visible)?;
158        }
159        for projection in &mut delete.returning {
160            self.bind_projection(projection, &visible)?;
161        }
162        Ok(())
163    }
164
165    fn bind_ctes(
166        &mut self,
167        ctes: &mut [crate::ast::CTE],
168        inherited: &BTreeSet<String>,
169    ) -> Result<BTreeSet<String>, SQLError> {
170        let mut visible = inherited.clone();
171        let recursive = ctes.iter().any(|cte| cte.recursive).then(|| {
172            ctes.iter()
173                .map(|cte| cte.name.clone())
174                .collect::<BTreeSet<_>>()
175        });
176        for cte in ctes {
177            let body_scope = recursive.as_ref().map_or_else(
178                || visible.clone(),
179                |recursive| inherited.union(recursive).cloned().collect(),
180            );
181            match &mut cte.body {
182                crate::ast::CteBody::Query(query) => self.bind_select(query, &body_scope)?,
183                crate::ast::CteBody::Insert(plan) => self.bind_insert(plan, &body_scope)?,
184                crate::ast::CteBody::Update(plan) => self.bind_update(plan, &body_scope)?,
185                crate::ast::CteBody::Delete(plan) => self.bind_delete(plan, &body_scope)?,
186                crate::ast::CteBody::Merge(plan) => self.bind_merge(plan, &body_scope)?,
187            }
188            if let Some(cycle) = &mut cte.cycle {
189                self.bind_expr(&mut cycle.mark_value, &body_scope)?;
190                self.bind_expr(&mut cycle.mark_default, &body_scope)?;
191            }
192            visible.insert(cte.name.clone());
193        }
194        Ok(visible)
195    }
196
197    fn bind_select(
198        &mut self,
199        select: &mut SelectStmt,
200        inherited: &BTreeSet<String>,
201    ) -> Result<(), SQLError> {
202        let visible = self.bind_ctes(&mut select.with, inherited)?;
203        if let Some(set) = select.set_op.as_mut().filter(|set| set.left.is_some()) {
204            self.bind_set_operation(set, &visible)?;
205            let left = set
206                .left
207                .as_deref()
208                .expect("explicit set-operation left input");
209            // These fields mirror the left input for AST compatibility; they are not another syntax occurrence.
210            select.projections.clone_from(&left.projections);
211            select.values.clone_from(&left.values);
212            select.from.clone_from(&left.from);
213            select.r#where.clone_from(&left.r#where);
214            select.group_by.clone_from(&left.group_by);
215            select.order_by.clone_from(&left.order_by);
216            select.limit.clone_from(&left.limit);
217            select.with_ties = left.with_ties;
218            select.offset.clone_from(&left.offset);
219            return Ok(());
220        }
221        if let Some(source) = &mut select.from {
222            self.bind_from(source, &visible)?;
223        }
224        for projection in &mut select.projections {
225            self.bind_projection(projection, &visible)?;
226        }
227        for expression in select.values.iter_mut().flatten() {
228            self.bind_expr(expression, &visible)?;
229        }
230        if let Some(expression) = &mut select.r#where {
231            self.bind_expr(expression, &visible)?;
232        }
233        for expression in &mut select.group_by {
234            self.bind_expr(expression, &visible)?;
235        }
236        for expression in select.grouping_sets.iter_mut().flatten() {
237            self.bind_expr(expression, &visible)?;
238        }
239        if let Some(expression) = &mut select.having {
240            self.bind_expr(expression, &visible)?;
241        }
242        for order in &mut select.order_by {
243            self.bind_expr(&mut order.expr, &visible)?;
244        }
245        if let Some(expression) = &mut select.limit {
246            self.bind_expr(expression, &visible)?;
247        }
248        if let Some(expression) = &mut select.offset {
249            self.bind_expr(expression, &visible)?;
250        }
251        for expression in &mut select.distinct_on {
252            self.bind_expr(expression, &visible)?;
253        }
254        for window in &mut select.windows {
255            for expression in window.spec.expressions_mut() {
256                self.bind_expr(expression, &visible)?;
257            }
258        }
259        if let Some(set) = &mut select.set_op {
260            self.bind_set_operation(set, &visible)?;
261        }
262        Ok(())
263    }
264
265    fn bind_set_operation(
266        &mut self,
267        set: &mut crate::ast::SetOp,
268        visible: &BTreeSet<String>,
269    ) -> Result<(), SQLError> {
270        if let Some(left) = &mut set.left {
271            self.bind_select(left, visible)?;
272        }
273        self.bind_select(&mut set.right, visible)?;
274        for order in &mut set.combined_order_by {
275            self.bind_expr(&mut order.expr, visible)?;
276        }
277        if let Some(expression) = &mut set.combined_limit {
278            self.bind_expr(expression, visible)?;
279        }
280        if let Some(expression) = &mut set.combined_offset {
281            self.bind_expr(expression, visible)?;
282        }
283        Ok(())
284    }
285
286    fn bind_from(
287        &mut self,
288        source: &mut FromClause,
289        visible_ctes: &BTreeSet<String>,
290    ) -> Result<(), SQLError> {
291        if let FromClause::Table { name, .. } = source {
292            let is_cte = RelationIdentity::parse_reference(name).is_ok_and(|(schema, relation)| {
293                schema.is_none() && visible_ctes.contains(&relation)
294            });
295            if is_cte {
296                return Ok(());
297            }
298        }
299        if let Some(visit) = self.source.as_mut() {
300            visit(source)?;
301        }
302        match source {
303            FromClause::Table { name, .. } => {
304                (self.relation)(name)?;
305            }
306            FromClause::Join {
307                left, right, on, ..
308            } => {
309                self.bind_from(left, visible_ctes)?;
310                self.bind_from(right, visible_ctes)?;
311                if let Some(expression) = on {
312                    self.bind_expr(expression, visible_ctes)?;
313                }
314            }
315            FromClause::Values { rows, .. } => {
316                for expression in rows.iter_mut().flatten() {
317                    self.bind_expr(expression, visible_ctes)?;
318                }
319            }
320            FromClause::Function {
321                name,
322                binding,
323                relations,
324                args,
325                ..
326            } => {
327                (self.routine)(name, Some(binding))?;
328                if let Some(relations) = relations {
329                    (self.relation)(&mut relations.left)?;
330                    (self.relation)(&mut relations.right)?;
331                }
332                for expression in args {
333                    self.bind_expr(expression, visible_ctes)?;
334                }
335            }
336            FromClause::FunctionGroup { functions, .. } => {
337                for function in functions {
338                    (self.routine)(&mut function.name, Some(&mut function.binding))?;
339                    if let Some(relations) = &mut function.relations {
340                        (self.relation)(&mut relations.left)?;
341                        (self.relation)(&mut relations.right)?;
342                    }
343                    for expression in &mut function.args {
344                        self.bind_expr(expression, visible_ctes)?;
345                    }
346                }
347            }
348            FromClause::Subquery { body, .. } => self.bind_select(body, visible_ctes)?,
349        }
350        Ok(())
351    }
352
353    fn bind_expression_type(&mut self, expression: &mut Expr) -> Result<(), SQLError> {
354        if let Some(visit) = self.expression.as_mut() {
355            visit(expression)?;
356        }
357        if let (
358            Some(visit),
359            Expr::Cast { ty, .. }
360            | Expr::TypedLiteral { ty, .. }
361            | Expr::CompositeRow {
362                binding: crate::ast::CompositeRowBinding { ty, .. },
363                ..
364            },
365        ) = (self.ty.as_mut(), expression)
366        {
367            visit(ty);
368        }
369        Ok(())
370    }
371
372    /// Bind a window call's arguments, `FILTER`, keys and frame offsets.
373    fn bind_window_call(
374        &mut self,
375        args: &mut [Expr],
376        filter: Option<&mut Expr>,
377        spec: &mut crate::ast::WindowSpec,
378        visible_ctes: &BTreeSet<String>,
379    ) -> Result<(), SQLError> {
380        for argument in args {
381            self.bind_expr(argument, visible_ctes)?;
382        }
383        if let Some(filter) = filter {
384            self.bind_expr(filter, visible_ctes)?;
385        }
386        for partition in &mut spec.partition_by {
387            self.bind_expr(partition, visible_ctes)?;
388        }
389        for order in &mut spec.order_by {
390            self.bind_expr(&mut order.expr, visible_ctes)?;
391        }
392        if let Some(frame) = &mut spec.frame {
393            for bound in [&mut frame.start, &mut frame.end] {
394                if let FrameBound::Preceding(inner) | FrameBound::Following(inner) = bound {
395                    self.bind_expr(inner, visible_ctes)?;
396                }
397            }
398        }
399        Ok(())
400    }
401
402    fn bind_projection(
403        &mut self,
404        projection: &mut crate::ast::Projection,
405        visible_ctes: &BTreeSet<String>,
406    ) -> Result<(), SQLError> {
407        if let Some(visit) = self.projection.as_mut() {
408            visit(projection)?;
409        }
410        self.bind_expr(&mut projection.expr, visible_ctes)
411    }
412
413    pub fn bind_expr(
414        &mut self,
415        expression: &mut Expr,
416        visible_ctes: &BTreeSet<String>,
417    ) -> Result<(), SQLError> {
418        self.bind_expression_type(expression)?;
419        match expression {
420            Expr::Func {
421                name,
422                binding,
423                args,
424                order_by,
425                filter,
426                ..
427            } => {
428                for argument in args {
429                    self.bind_expr(argument, visible_ctes)?;
430                }
431                for order in order_by {
432                    self.bind_expr(&mut order.expr, visible_ctes)?;
433                }
434                if let Some(filter) = filter {
435                    self.bind_expr(filter, visible_ctes)?;
436                }
437                (self.routine)(name, Some(binding))?;
438            }
439            Expr::Array(items)
440            | Expr::Row(items)
441            | Expr::CompositeRow { items, .. }
442            | Expr::And(items)
443            | Expr::Or(items) => {
444                for item in items {
445                    self.bind_expr(item, visible_ctes)?;
446                }
447            }
448            Expr::Binary { lhs, rhs, .. } => {
449                self.bind_expr(lhs, visible_ctes)?;
450                self.bind_expr(rhs, visible_ctes)?;
451            }
452            Expr::UnaryMinus(inner)
453            | Expr::Not(inner)
454            | Expr::IsNull { expr: inner, .. }
455            | Expr::Cast { expr: inner, .. } => self.bind_expr(inner, visible_ctes)?,
456            Expr::Between { expr, low, high } => {
457                self.bind_expr(expr, visible_ctes)?;
458                self.bind_expr(low, visible_ctes)?;
459                self.bind_expr(high, visible_ctes)?;
460            }
461            Expr::InList { expr, list, .. } => {
462                self.bind_expr(expr, visible_ctes)?;
463                for item in list {
464                    self.bind_expr(item, visible_ctes)?;
465                }
466            }
467            Expr::WindowCall {
468                name,
469                args,
470                spec,
471                filter,
472                ..
473            } => {
474                self.bind_window_call(args, filter.as_deref_mut(), spec, visible_ctes)?;
475                (self.routine)(name, None)?;
476            }
477            Expr::Case {
478                base,
479                when,
480                else_branch,
481            } => {
482                if let Some(base) = base {
483                    self.bind_expr(base, visible_ctes)?;
484                }
485                for (condition, result) in when {
486                    self.bind_expr(condition, visible_ctes)?;
487                    self.bind_expr(result, visible_ctes)?;
488                }
489                if let Some(branch) = else_branch {
490                    self.bind_expr(branch, visible_ctes)?;
491                }
492            }
493            Expr::ScalarSubquery(body) | Expr::Exists { body, .. } => {
494                self.bind_select(body, visible_ctes)?;
495            }
496            Expr::InSubquery { expr, body, .. } => {
497                self.bind_expr(expr, visible_ctes)?;
498                self.bind_select(body, visible_ctes)?;
499            }
500            Expr::Star
501            | Expr::QualifiedStar(_)
502            | Expr::Default
503            | Expr::Column(_)
504            | Expr::QualifiedColumn { .. }
505            | Expr::InternalColumn(_)
506            | Expr::Literal(_)
507            | Expr::TypedLiteral { .. }
508            | Expr::Param(_) => {}
509        }
510        Ok(())
511    }
512}