#[expect(
clippy::too_many_lines,
reason = "one traversal covers every stored expression child"
)]
pub fn walk_schema_expr_mut(
expression: &mut crate::ast::Expr,
visit: &mut impl FnMut(&mut crate::ast::Expr) -> Result<(), String>,
) -> Result<(), String> {
use crate::ast::{Expr, FrameBound};
visit(expression)?;
match expression {
Expr::Func {
args,
order_by,
filter,
..
} => {
for argument in args {
walk_schema_expr_mut(argument, visit)?;
}
for order in order_by {
walk_schema_expr_mut(&mut order.expr, visit)?;
}
if let Some(filter) = filter {
walk_schema_expr_mut(filter, visit)?;
}
}
Expr::Array(items)
| Expr::Row(items)
| Expr::CompositeRow { items, .. }
| Expr::And(items)
| Expr::Or(items) => {
for item in items {
walk_schema_expr_mut(item, visit)?;
}
}
Expr::Binary { lhs, rhs, .. } => {
walk_schema_expr_mut(lhs, visit)?;
walk_schema_expr_mut(rhs, visit)?;
}
Expr::Not(inner)
| Expr::UnaryMinus(inner)
| Expr::IsNull { expr: inner, .. }
| Expr::Cast { expr: inner, .. } => {
walk_schema_expr_mut(inner, visit)?;
}
Expr::Between { expr, low, high } => {
walk_schema_expr_mut(expr, visit)?;
walk_schema_expr_mut(low, visit)?;
walk_schema_expr_mut(high, visit)?;
}
Expr::InList { expr, list, .. } => {
walk_schema_expr_mut(expr, visit)?;
for item in list {
walk_schema_expr_mut(item, visit)?;
}
}
Expr::WindowCall {
args, spec, filter, ..
} => {
for expression in args
.iter_mut()
.chain(filter.as_deref_mut())
.chain(&mut spec.partition_by)
.chain(spec.order_by.iter_mut().map(|order| &mut order.expr))
{
walk_schema_expr_mut(expression, visit)?;
}
for bound in spec
.frame
.iter_mut()
.flat_map(|frame| [&mut frame.start, &mut frame.end])
{
if let FrameBound::Preceding(expression) | FrameBound::Following(expression) = bound
{
walk_schema_expr_mut(expression, visit)?;
}
}
}
Expr::Case {
base,
when,
else_branch,
} => {
if let Some(base) = base {
walk_schema_expr_mut(base, visit)?;
}
for (condition, result) in when {
walk_schema_expr_mut(condition, visit)?;
walk_schema_expr_mut(result, visit)?;
}
if let Some(else_branch) = else_branch {
walk_schema_expr_mut(else_branch, visit)?;
}
}
Expr::ScalarSubquery(_) | Expr::Exists { .. } | Expr::InSubquery { .. } => {
return Err(
"schema expression contains a subquery whose dependencies cannot be rewritten safely"
.into(),
);
}
Expr::Default
| Expr::Star
| Expr::QualifiedStar(_)
| Expr::Column(_)
| Expr::QualifiedColumn { .. }
| Expr::InternalColumn(_)
| Expr::Literal(_)
| Expr::TypedLiteral { .. }
| Expr::Param(_) => {}
}
Ok(())
}
pub fn schema_expr_references_column(expression: &crate::ast::Expr, column: &str) -> bool {
let mut expression = expression.clone();
let mut referenced = false;
let result = walk_schema_expr_mut(&mut expression, &mut |node| {
referenced |= match node {
crate::ast::Expr::Star | crate::ast::Expr::QualifiedStar(_) => true,
crate::ast::Expr::Column(name)
| crate::ast::Expr::QualifiedColumn { column: name, .. } => name == column,
_ => false,
};
Ok(())
});
result.is_err() || referenced
}
pub mod oid_alias;
pub mod regclass;
pub mod registration;
pub mod rewrites;