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 =
293                RelationIdentity::parse_reference(name)
294                    .ok()
295                    .is_some_and(|(schema, relation)| {
296                        schema.is_none() && visible_ctes.contains(&relation)
297                    });
298            if is_cte {
299                return Ok(());
300            }
301        }
302        if let Some(visit) = self.source.as_mut() {
303            visit(source)?;
304        }
305        match source {
306            FromClause::Table { name, .. } => {
307                (self.relation)(name)?;
308            }
309            FromClause::Join {
310                left, right, on, ..
311            } => {
312                self.bind_from(left, visible_ctes)?;
313                self.bind_from(right, visible_ctes)?;
314                if let Some(expression) = on {
315                    self.bind_expr(expression, visible_ctes)?;
316                }
317            }
318            FromClause::Values { rows, .. } => {
319                for expression in rows.iter_mut().flatten() {
320                    self.bind_expr(expression, visible_ctes)?;
321                }
322            }
323            FromClause::Function {
324                name,
325                binding,
326                relations,
327                args,
328                ..
329            } => {
330                (self.routine)(name, Some(binding))?;
331                if let Some(relations) = relations {
332                    (self.relation)(&mut relations.left)?;
333                    (self.relation)(&mut relations.right)?;
334                }
335                for expression in args {
336                    self.bind_expr(expression, visible_ctes)?;
337                }
338            }
339            FromClause::FunctionGroup { functions, .. } => {
340                for function in functions {
341                    (self.routine)(&mut function.name, Some(&mut function.binding))?;
342                    if let Some(relations) = &mut function.relations {
343                        (self.relation)(&mut relations.left)?;
344                        (self.relation)(&mut relations.right)?;
345                    }
346                    for expression in &mut function.args {
347                        self.bind_expr(expression, visible_ctes)?;
348                    }
349                }
350            }
351            FromClause::Subquery { body, .. } => self.bind_select(body, visible_ctes)?,
352        }
353        Ok(())
354    }
355
356    fn bind_expression_type(&mut self, expression: &mut Expr) -> Result<(), SQLError> {
357        if let Some(visit) = self.expression.as_mut() {
358            visit(expression)?;
359        }
360        if let (
361            Some(visit),
362            Expr::Cast { ty, .. }
363            | Expr::TypedLiteral { ty, .. }
364            | Expr::CompositeRow {
365                binding: crate::ast::CompositeRowBinding { ty, .. },
366                ..
367            },
368        ) = (self.ty.as_mut(), expression)
369        {
370            visit(ty);
371        }
372        Ok(())
373    }
374
375    /// Bind a window call's arguments, `FILTER`, keys and frame offsets.
376    fn bind_window_call(
377        &mut self,
378        args: &mut [Expr],
379        filter: Option<&mut Expr>,
380        spec: &mut crate::ast::WindowSpec,
381        visible_ctes: &BTreeSet<String>,
382    ) -> Result<(), SQLError> {
383        for argument in args {
384            self.bind_expr(argument, visible_ctes)?;
385        }
386        if let Some(filter) = filter {
387            self.bind_expr(filter, visible_ctes)?;
388        }
389        for partition in &mut spec.partition_by {
390            self.bind_expr(partition, visible_ctes)?;
391        }
392        for order in &mut spec.order_by {
393            self.bind_expr(&mut order.expr, visible_ctes)?;
394        }
395        if let Some(frame) = &mut spec.frame {
396            for bound in [&mut frame.start, &mut frame.end] {
397                if let FrameBound::Preceding(inner) | FrameBound::Following(inner) = bound {
398                    self.bind_expr(inner, visible_ctes)?;
399                }
400            }
401        }
402        Ok(())
403    }
404
405    fn bind_projection(
406        &mut self,
407        projection: &mut crate::ast::Projection,
408        visible_ctes: &BTreeSet<String>,
409    ) -> Result<(), SQLError> {
410        if let Some(visit) = self.projection.as_mut() {
411            visit(projection)?;
412        }
413        self.bind_expr(&mut projection.expr, visible_ctes)
414    }
415
416    pub fn bind_expr(
417        &mut self,
418        expression: &mut Expr,
419        visible_ctes: &BTreeSet<String>,
420    ) -> Result<(), SQLError> {
421        self.bind_expression_type(expression)?;
422        match expression {
423            Expr::Func {
424                name,
425                binding,
426                args,
427                order_by,
428                filter,
429                ..
430            } => {
431                for argument in args {
432                    self.bind_expr(argument, visible_ctes)?;
433                }
434                for order in order_by {
435                    self.bind_expr(&mut order.expr, visible_ctes)?;
436                }
437                if let Some(filter) = filter {
438                    self.bind_expr(filter, visible_ctes)?;
439                }
440                (self.routine)(name, Some(binding))?;
441            }
442            Expr::Array(items)
443            | Expr::Row(items)
444            | Expr::CompositeRow { items, .. }
445            | Expr::And(items)
446            | Expr::Or(items) => {
447                for item in items {
448                    self.bind_expr(item, visible_ctes)?;
449                }
450            }
451            Expr::Binary { lhs, rhs, .. } => {
452                self.bind_expr(lhs, visible_ctes)?;
453                self.bind_expr(rhs, visible_ctes)?;
454            }
455            Expr::UnaryMinus(inner)
456            | Expr::Not(inner)
457            | Expr::IsNull { expr: inner, .. }
458            | Expr::Cast { expr: inner, .. } => self.bind_expr(inner, visible_ctes)?,
459            Expr::Between { expr, low, high } => {
460                self.bind_expr(expr, visible_ctes)?;
461                self.bind_expr(low, visible_ctes)?;
462                self.bind_expr(high, visible_ctes)?;
463            }
464            Expr::InList { expr, list, .. } => {
465                self.bind_expr(expr, visible_ctes)?;
466                for item in list {
467                    self.bind_expr(item, visible_ctes)?;
468                }
469            }
470            Expr::WindowCall {
471                name,
472                args,
473                spec,
474                filter,
475                ..
476            } => {
477                self.bind_window_call(args, filter.as_deref_mut(), spec, visible_ctes)?;
478                (self.routine)(name, None)?;
479            }
480            Expr::Case {
481                base,
482                when,
483                else_branch,
484            } => {
485                if let Some(base) = base {
486                    self.bind_expr(base, visible_ctes)?;
487                }
488                for (condition, result) in when {
489                    self.bind_expr(condition, visible_ctes)?;
490                    self.bind_expr(result, visible_ctes)?;
491                }
492                if let Some(branch) = else_branch {
493                    self.bind_expr(branch, visible_ctes)?;
494                }
495            }
496            Expr::ScalarSubquery(body) | Expr::Exists { body, .. } => {
497                self.bind_select(body, visible_ctes)?;
498            }
499            Expr::InSubquery { expr, body, .. } => {
500                self.bind_expr(expr, visible_ctes)?;
501                self.bind_select(body, visible_ctes)?;
502            }
503            Expr::Star
504            | Expr::QualifiedStar(_)
505            | Expr::Default
506            | Expr::Column(_)
507            | Expr::QualifiedColumn { .. }
508            | Expr::InternalColumn(_)
509            | Expr::Literal(_)
510            | Expr::TypedLiteral { .. }
511            | Expr::Param(_) => {}
512        }
513        Ok(())
514    }
515}