Skip to main content

uqa_sql/schema/
dependencies.rs

1//
2// Unified Query Algebra
3//
4// Copyright (c) 2023-2026 Cognica, Inc.
5//
6
7//! Traverse stored expressions and recognize their column dependencies.
8
9pub fn walk_schema_expr_mut(
10    expression: &mut crate::ast::Expr,
11    visit: &mut impl FnMut(&mut crate::ast::Expr) -> Result<(), String>,
12) -> Result<(), String> {
13    use crate::ast::{Expr, FrameBound};
14
15    visit(expression)?;
16    match expression {
17        Expr::Func {
18            args,
19            order_by,
20            filter,
21            ..
22        } => {
23            for argument in args {
24                walk_schema_expr_mut(argument, visit)?;
25            }
26            for order in order_by {
27                walk_schema_expr_mut(&mut order.expr, visit)?;
28            }
29            if let Some(filter) = filter {
30                walk_schema_expr_mut(filter, visit)?;
31            }
32        }
33        Expr::Array(items) | Expr::Row(items) | Expr::And(items) | Expr::Or(items) => {
34            for item in items {
35                walk_schema_expr_mut(item, visit)?;
36            }
37        }
38        Expr::Binary { lhs, rhs, .. } => {
39            walk_schema_expr_mut(lhs, visit)?;
40            walk_schema_expr_mut(rhs, visit)?;
41        }
42        Expr::Not(inner)
43        | Expr::UnaryMinus(inner)
44        | Expr::IsNull { expr: inner, .. }
45        | Expr::Cast { expr: inner, .. } => {
46            walk_schema_expr_mut(inner, visit)?;
47        }
48        Expr::Between { expr, low, high } => {
49            walk_schema_expr_mut(expr, visit)?;
50            walk_schema_expr_mut(low, visit)?;
51            walk_schema_expr_mut(high, visit)?;
52        }
53        Expr::InList { expr, list, .. } => {
54            walk_schema_expr_mut(expr, visit)?;
55            for item in list {
56                walk_schema_expr_mut(item, visit)?;
57            }
58        }
59        Expr::WindowCall { args, spec, .. } => {
60            for argument in args.iter_mut().chain(&mut spec.partition_by) {
61                walk_schema_expr_mut(argument, visit)?;
62            }
63            for order in &mut spec.order_by {
64                walk_schema_expr_mut(&mut order.expr, visit)?;
65            }
66            if let Some(frame) = &mut spec.frame {
67                for bound in [&mut frame.start, &mut frame.end] {
68                    match bound {
69                        FrameBound::Preceding(expression) | FrameBound::Following(expression) => {
70                            walk_schema_expr_mut(expression, visit)?;
71                        }
72                        FrameBound::UnboundedPreceding
73                        | FrameBound::UnboundedFollowing
74                        | FrameBound::CurrentRow => {}
75                    }
76                }
77            }
78        }
79        Expr::Case {
80            base,
81            when,
82            else_branch,
83        } => {
84            if let Some(base) = base {
85                walk_schema_expr_mut(base, visit)?;
86            }
87            for (condition, result) in when {
88                walk_schema_expr_mut(condition, visit)?;
89                walk_schema_expr_mut(result, visit)?;
90            }
91            if let Some(else_branch) = else_branch {
92                walk_schema_expr_mut(else_branch, visit)?;
93            }
94        }
95        Expr::ScalarSubquery(_) | Expr::Exists { .. } | Expr::InSubquery { .. } => {
96            return Err(
97                "schema expression contains a subquery whose dependencies cannot be rewritten safely"
98                    .into(),
99            );
100        }
101        Expr::Default
102        | Expr::Star
103        | Expr::QualifiedStar(_)
104        | Expr::Column(_)
105        | Expr::QualifiedColumn { .. }
106        | Expr::InternalColumn(_)
107        | Expr::Literal(_)
108        | Expr::TypedLiteral { .. }
109        | Expr::Param(_) => {}
110    }
111    Ok(())
112}
113
114pub fn schema_expr_references_column(expression: &crate::ast::Expr, column: &str) -> bool {
115    let mut expression = expression.clone();
116    let mut referenced = false;
117    let result = walk_schema_expr_mut(&mut expression, &mut |node| {
118        referenced |= match node {
119            crate::ast::Expr::Star | crate::ast::Expr::QualifiedStar(_) => true,
120            crate::ast::Expr::Column(name)
121            | crate::ast::Expr::QualifiedColumn { column: name, .. } => name == column,
122            _ => false,
123        };
124        Ok(())
125    });
126    result.is_err() || referenced
127}
128
129pub mod regclass;
130pub mod registration;
131pub mod rewrites;