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