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 merge;
17mod routines;
18mod sources;
19mod types;
20pub use expressions::*;
21pub use merge::visit_stored_statement_merges;
22pub use routines::*;
23pub use sources::*;
24pub use types::*;
25
26pub type MergeCallback<'a> = &'a mut dyn FnMut(&mut crate::ast::MergeStmt) -> Result<(), SQLError>;
27
28pub type ExpressionCallback<'a> = &'a mut dyn FnMut(&mut Expr) -> Result<(), SQLError>;
29pub type SourceCallback<'a> = &'a mut dyn FnMut(&mut FromClause) -> Result<(), SQLError>;
30
31pub struct StoredAstVisitor<'a, R, F> {
32    pub source: Option<SourceCallback<'a>>,
33    pub merge: Option<MergeCallback<'a>>,
34    pub expression: Option<ExpressionCallback<'a>>,
35    pub ty: Option<&'a mut dyn FnMut(&mut String)>,
36    pub relation: &'a mut R,
37    pub routine: &'a mut F,
38}
39
40impl<R, F> StoredAstVisitor<'_, R, F>
41where
42    R: FnMut(&mut String) -> Result<(), SQLError>,
43    F: FnMut(&mut String, Option<&mut Option<crate::ast::FunctionBinding>>) -> Result<(), SQLError>,
44{
45    pub fn bind_statement(&mut self, statement: &mut Statement) -> Result<(), SQLError> {
46        let ctes = BTreeSet::new();
47        match statement {
48            Statement::Select(query) => self.bind_select(query, &ctes),
49            Statement::Insert(insert) => self.bind_insert(insert, &ctes),
50            Statement::Update(update) => self.bind_update(update, &ctes),
51            Statement::Delete(delete) => self.bind_delete(delete, &ctes),
52            Statement::Notify { .. } => Ok(()),
53            Statement::Values { rows } => {
54                for expression in rows.iter_mut().flatten() {
55                    self.bind_expr(expression, &ctes)?;
56                }
57                Ok(())
58            }
59            Statement::Merge(merge) => self.bind_merge(merge, &ctes),
60            _ => Err(SQLError::Internal(
61                "catalog-owned statement has an unsupported dependency shape".into(),
62            )),
63        }
64    }
65
66    fn bind_insert(
67        &mut self,
68        insert: &mut crate::ast::InsertStmt,
69        inherited: &BTreeSet<String>,
70    ) -> Result<(), SQLError> {
71        (self.relation)(&mut insert.table)?;
72        let visible = self.bind_ctes(&mut insert.with, inherited)?;
73        if let Some(source) = insert.select_source.as_deref_mut() {
74            self.bind_select(source, &visible)?;
75        }
76        for expression in insert.rows.iter_mut().flatten() {
77            self.bind_expr(expression, &visible)?;
78        }
79        if let Some(conflict) = &mut insert.on_conflict {
80            for expression in &mut conflict.expressions {
81                self.bind_expr(expression, &visible)?;
82            }
83            if let Some(predicate) = conflict.predicate.as_deref_mut() {
84                self.bind_expr(predicate, &visible)?;
85            }
86            if let crate::ast::OnConflictAction::Update {
87                assignments,
88                r#where,
89            } = &mut conflict.action
90            {
91                for (_, expression) in assignments {
92                    self.bind_expr(expression, &visible)?;
93                }
94                if let Some(expression) = r#where {
95                    self.bind_expr(expression, &visible)?;
96                }
97            }
98        }
99        for projection in &mut insert.returning {
100            self.bind_expr(&mut projection.expr, &visible)?;
101        }
102        Ok(())
103    }
104
105    fn bind_update(
106        &mut self,
107        update: &mut crate::ast::UpdateStmt,
108        inherited: &BTreeSet<String>,
109    ) -> Result<(), SQLError> {
110        (self.relation)(&mut update.table)?;
111        let visible = self.bind_ctes(&mut update.with, inherited)?;
112        if let Some(source) = &mut update.from {
113            self.bind_from(source, &visible)?;
114        }
115        for (_, expression) in &mut update.assignments {
116            self.bind_expr(expression, &visible)?;
117        }
118        if let Some(expression) = &mut update.r#where {
119            self.bind_expr(expression, &visible)?;
120        }
121        for projection in &mut update.returning {
122            self.bind_expr(&mut projection.expr, &visible)?;
123        }
124        Ok(())
125    }
126
127    fn bind_delete(
128        &mut self,
129        delete: &mut crate::ast::DeleteStmt,
130        inherited: &BTreeSet<String>,
131    ) -> Result<(), SQLError> {
132        (self.relation)(&mut delete.table)?;
133        let visible = self.bind_ctes(&mut delete.with, inherited)?;
134        if let Some(source) = &mut delete.using {
135            self.bind_from(source, &visible)?;
136        }
137        if let Some(expression) = &mut delete.r#where {
138            self.bind_expr(expression, &visible)?;
139        }
140        for projection in &mut delete.returning {
141            self.bind_expr(&mut projection.expr, &visible)?;
142        }
143        Ok(())
144    }
145
146    fn bind_ctes(
147        &mut self,
148        ctes: &mut [crate::ast::CTE],
149        inherited: &BTreeSet<String>,
150    ) -> Result<BTreeSet<String>, SQLError> {
151        let mut visible = inherited.clone();
152        let recursive = ctes.iter().any(|cte| cte.recursive).then(|| {
153            ctes.iter()
154                .map(|cte| cte.name.clone())
155                .collect::<BTreeSet<_>>()
156        });
157        for cte in ctes {
158            let body_scope = recursive.as_ref().map_or_else(
159                || visible.clone(),
160                |recursive| inherited.union(recursive).cloned().collect(),
161            );
162            match &mut cte.body {
163                crate::ast::CteBody::Query(query) => self.bind_select(query, &body_scope)?,
164                crate::ast::CteBody::Insert(plan) => self.bind_insert(plan, &body_scope)?,
165                crate::ast::CteBody::Update(plan) => self.bind_update(plan, &body_scope)?,
166                crate::ast::CteBody::Delete(plan) => self.bind_delete(plan, &body_scope)?,
167                crate::ast::CteBody::Merge(plan) => self.bind_merge(plan, &body_scope)?,
168            }
169            if let Some(cycle) = &mut cte.cycle {
170                self.bind_expr(&mut cycle.mark_value, &body_scope)?;
171                self.bind_expr(&mut cycle.mark_default, &body_scope)?;
172            }
173            visible.insert(cte.name.clone());
174        }
175        Ok(visible)
176    }
177
178    fn bind_select(
179        &mut self,
180        select: &mut SelectStmt,
181        inherited: &BTreeSet<String>,
182    ) -> Result<(), SQLError> {
183        let visible = self.bind_ctes(&mut select.with, inherited)?;
184        if let Some(source) = &mut select.from {
185            self.bind_from(source, &visible)?;
186        }
187        for projection in &mut select.projections {
188            self.bind_expr(&mut projection.expr, &visible)?;
189        }
190        for expression in select.values.iter_mut().flatten() {
191            self.bind_expr(expression, &visible)?;
192        }
193        if let Some(expression) = &mut select.r#where {
194            self.bind_expr(expression, &visible)?;
195        }
196        for expression in &mut select.group_by {
197            self.bind_expr(expression, &visible)?;
198        }
199        for expression in select.grouping_sets.iter_mut().flatten() {
200            self.bind_expr(expression, &visible)?;
201        }
202        if let Some(expression) = &mut select.having {
203            self.bind_expr(expression, &visible)?;
204        }
205        for order in &mut select.order_by {
206            self.bind_expr(&mut order.expr, &visible)?;
207        }
208        if let Some(expression) = &mut select.limit {
209            self.bind_expr(expression, &visible)?;
210        }
211        if let Some(expression) = &mut select.offset {
212            self.bind_expr(expression, &visible)?;
213        }
214        for expression in &mut select.distinct_on {
215            self.bind_expr(expression, &visible)?;
216        }
217        if let Some(set) = &mut select.set_op {
218            if let Some(left) = &mut set.left {
219                self.bind_select(left, &visible)?;
220            }
221            self.bind_select(&mut set.right, &visible)?;
222            for order in &mut set.combined_order_by {
223                self.bind_expr(&mut order.expr, &visible)?;
224            }
225            if let Some(expression) = &mut set.combined_limit {
226                self.bind_expr(expression, &visible)?;
227            }
228            if let Some(expression) = &mut set.combined_offset {
229                self.bind_expr(expression, &visible)?;
230            }
231        }
232        Ok(())
233    }
234
235    fn bind_from(
236        &mut self,
237        source: &mut FromClause,
238        visible_ctes: &BTreeSet<String>,
239    ) -> Result<(), SQLError> {
240        if let FromClause::Table { name, .. } = source {
241            let is_cte =
242                RelationIdentity::parse_reference(name)
243                    .ok()
244                    .is_some_and(|(schema, relation)| {
245                        schema.is_none() && visible_ctes.contains(&relation)
246                    });
247            if is_cte {
248                return Ok(());
249            }
250        }
251        if let Some(visit) = self.source.as_mut() {
252            visit(source)?;
253        }
254        match source {
255            FromClause::Table { name, .. } => {
256                (self.relation)(name)?;
257            }
258            FromClause::Join {
259                left, right, on, ..
260            } => {
261                self.bind_from(left, visible_ctes)?;
262                self.bind_from(right, visible_ctes)?;
263                if let Some(expression) = on {
264                    self.bind_expr(expression, visible_ctes)?;
265                }
266            }
267            FromClause::Values { rows, .. } => {
268                for expression in rows.iter_mut().flatten() {
269                    self.bind_expr(expression, visible_ctes)?;
270                }
271            }
272            FromClause::Function {
273                name,
274                binding,
275                relations,
276                args,
277                ..
278            } => {
279                (self.routine)(name, Some(binding))?;
280                if let Some(relations) = relations {
281                    (self.relation)(&mut relations.left)?;
282                    (self.relation)(&mut relations.right)?;
283                }
284                for expression in args {
285                    self.bind_expr(expression, visible_ctes)?;
286                }
287            }
288            FromClause::FunctionGroup { functions, .. } => {
289                for function in functions {
290                    (self.routine)(&mut function.name, Some(&mut function.binding))?;
291                    if let Some(relations) = &mut function.relations {
292                        (self.relation)(&mut relations.left)?;
293                        (self.relation)(&mut relations.right)?;
294                    }
295                    for expression in &mut function.args {
296                        self.bind_expr(expression, visible_ctes)?;
297                    }
298                }
299            }
300            FromClause::Subquery { body, .. } => self.bind_select(body, visible_ctes)?,
301        }
302        Ok(())
303    }
304
305    fn bind_expression_type(&mut self, expression: &mut Expr) -> Result<(), SQLError> {
306        if let Some(visit) = self.expression.as_mut() {
307            visit(expression)?;
308        }
309        if let (Some(visit), Expr::Cast { ty, .. } | Expr::TypedLiteral { ty, .. }) =
310            (self.ty.as_mut(), expression)
311        {
312            visit(ty);
313        }
314        Ok(())
315    }
316
317    pub fn bind_expr(
318        &mut self,
319        expression: &mut Expr,
320        visible_ctes: &BTreeSet<String>,
321    ) -> Result<(), SQLError> {
322        self.bind_expression_type(expression)?;
323        match expression {
324            Expr::Func {
325                name,
326                binding,
327                args,
328                order_by,
329                filter,
330                ..
331            } => {
332                for argument in args {
333                    self.bind_expr(argument, visible_ctes)?;
334                }
335                for order in order_by {
336                    self.bind_expr(&mut order.expr, visible_ctes)?;
337                }
338                if let Some(filter) = filter {
339                    self.bind_expr(filter, visible_ctes)?;
340                }
341                (self.routine)(name, Some(binding))?;
342            }
343            Expr::Array(items) | Expr::Row(items) | Expr::And(items) | Expr::Or(items) => {
344                for item in items {
345                    self.bind_expr(item, visible_ctes)?;
346                }
347            }
348            Expr::Binary { lhs, rhs, .. } => {
349                self.bind_expr(lhs, visible_ctes)?;
350                self.bind_expr(rhs, visible_ctes)?;
351            }
352            Expr::UnaryMinus(inner)
353            | Expr::Not(inner)
354            | Expr::IsNull { expr: inner, .. }
355            | Expr::Cast { expr: inner, .. } => self.bind_expr(inner, visible_ctes)?,
356            Expr::Between { expr, low, high } => {
357                self.bind_expr(expr, visible_ctes)?;
358                self.bind_expr(low, visible_ctes)?;
359                self.bind_expr(high, visible_ctes)?;
360            }
361            Expr::InList { expr, list, .. } => {
362                self.bind_expr(expr, visible_ctes)?;
363                for item in list {
364                    self.bind_expr(item, visible_ctes)?;
365                }
366            }
367            Expr::WindowCall { name, args, spec } => {
368                for argument in args {
369                    self.bind_expr(argument, visible_ctes)?;
370                }
371                for partition in &mut spec.partition_by {
372                    self.bind_expr(partition, visible_ctes)?;
373                }
374                for order in &mut spec.order_by {
375                    self.bind_expr(&mut order.expr, visible_ctes)?;
376                }
377                if let Some(frame) = &mut spec.frame {
378                    for bound in [&mut frame.start, &mut frame.end] {
379                        if let FrameBound::Preceding(inner) | FrameBound::Following(inner) = bound {
380                            self.bind_expr(inner, visible_ctes)?;
381                        }
382                    }
383                }
384                (self.routine)(name, None)?;
385            }
386            Expr::Case {
387                base,
388                when,
389                else_branch,
390            } => {
391                if let Some(base) = base {
392                    self.bind_expr(base, visible_ctes)?;
393                }
394                for (condition, result) in when {
395                    self.bind_expr(condition, visible_ctes)?;
396                    self.bind_expr(result, visible_ctes)?;
397                }
398                if let Some(branch) = else_branch {
399                    self.bind_expr(branch, visible_ctes)?;
400                }
401            }
402            Expr::ScalarSubquery(body) | Expr::Exists { body, .. } => {
403                self.bind_select(body, visible_ctes)?;
404            }
405            Expr::InSubquery { expr, body, .. } => {
406                self.bind_expr(expr, visible_ctes)?;
407                self.bind_select(body, visible_ctes)?;
408            }
409            Expr::Star
410            | Expr::QualifiedStar(_)
411            | Expr::Default
412            | Expr::Column(_)
413            | Expr::QualifiedColumn { .. }
414            | Expr::InternalColumn(_)
415            | Expr::Literal(_)
416            | Expr::TypedLiteral { .. }
417            | Expr::Param(_) => {}
418        }
419        Ok(())
420    }
421}