Introduce walker expressions for ast::Expr

This commit is contained in:
Jussi Saurio
2025-05-23 15:48:33 +03:00
parent 597020bc0c
commit 40a4d162bc

View File

@@ -2600,3 +2600,390 @@ pub fn unwrap_parens_owned(expr: ast::Expr) -> Result<(ast::Expr, usize)> {
_ => Ok((expr, paren_count)),
}
}
/// Recursively walks an immutable expression, applying a function to each sub-expression.
pub fn walk_expr<F>(expr: &ast::Expr, func: &mut F) -> Result<()>
where
F: FnMut(&ast::Expr) -> Result<()>,
{
func(expr)?;
match expr {
ast::Expr::Between {
lhs, start, end, ..
} => {
walk_expr(lhs, func)?;
walk_expr(start, func)?;
walk_expr(end, func)?;
}
ast::Expr::Binary(lhs, _, rhs) => {
walk_expr(lhs, func)?;
walk_expr(rhs, func)?;
}
ast::Expr::Case {
base,
when_then_pairs,
else_expr,
} => {
if let Some(base_expr) = base {
walk_expr(base_expr, func)?;
}
for (when_expr, then_expr) in when_then_pairs {
walk_expr(when_expr, func)?;
walk_expr(then_expr, func)?;
}
if let Some(else_expr) = else_expr {
walk_expr(else_expr, func)?;
}
}
ast::Expr::Cast { expr, .. } => {
walk_expr(expr, func)?;
}
ast::Expr::Collate(expr, _) => {
walk_expr(expr, func)?;
}
ast::Expr::Exists(_select) | ast::Expr::Subquery(_select) => {
// TODO: Walk through select statements if needed
}
ast::Expr::FunctionCall {
args,
order_by,
filter_over,
..
} => {
if let Some(args) = args {
for arg in args {
walk_expr(arg, func)?;
}
}
if let Some(order_by) = order_by {
for sort_col in order_by {
walk_expr(&sort_col.expr, func)?;
}
}
if let Some(filter_over) = filter_over {
if let Some(filter_clause) = &filter_over.filter_clause {
walk_expr(filter_clause, func)?;
}
if let Some(over_clause) = &filter_over.over_clause {
match over_clause.as_ref() {
ast::Over::Window(window) => {
if let Some(partition_by) = &window.partition_by {
for part_expr in partition_by {
walk_expr(part_expr, func)?;
}
}
if let Some(order_by_clause) = &window.order_by {
for sort_col in order_by_clause {
walk_expr(&sort_col.expr, func)?;
}
}
if let Some(frame_clause) = &window.frame_clause {
walk_expr_frame_bound(&frame_clause.start, func)?;
if let Some(end_bound) = &frame_clause.end {
walk_expr_frame_bound(end_bound, func)?;
}
}
}
ast::Over::Name(_) => {}
}
}
}
}
ast::Expr::FunctionCallStar { filter_over, .. } => {
if let Some(filter_over) = filter_over {
if let Some(filter_clause) = &filter_over.filter_clause {
walk_expr(filter_clause, func)?;
}
if let Some(over_clause) = &filter_over.over_clause {
match over_clause.as_ref() {
ast::Over::Window(window) => {
if let Some(partition_by) = &window.partition_by {
for part_expr in partition_by {
walk_expr(part_expr, func)?;
}
}
if let Some(order_by_clause) = &window.order_by {
for sort_col in order_by_clause {
walk_expr(&sort_col.expr, func)?;
}
}
if let Some(frame_clause) = &window.frame_clause {
walk_expr_frame_bound(&frame_clause.start, func)?;
if let Some(end_bound) = &frame_clause.end {
walk_expr_frame_bound(end_bound, func)?;
}
}
}
ast::Over::Name(_) => {}
}
}
}
}
ast::Expr::InList { lhs, rhs, .. } => {
walk_expr(lhs, func)?;
if let Some(rhs_exprs) = rhs {
for expr in rhs_exprs {
walk_expr(expr, func)?;
}
}
}
ast::Expr::InSelect { lhs, rhs: _, .. } => {
walk_expr(lhs, func)?;
// TODO: Walk through select statements if needed
}
ast::Expr::InTable { lhs, args, .. } => {
walk_expr(lhs, func)?;
if let Some(arg_exprs) = args {
for expr in arg_exprs {
walk_expr(expr, func)?;
}
}
}
ast::Expr::IsNull(expr) | ast::Expr::NotNull(expr) => {
walk_expr(expr, func)?;
}
ast::Expr::Like {
lhs, rhs, escape, ..
} => {
walk_expr(lhs, func)?;
walk_expr(rhs, func)?;
if let Some(esc_expr) = escape {
walk_expr(esc_expr, func)?;
}
}
ast::Expr::Parenthesized(exprs) => {
for expr in exprs {
walk_expr(expr, func)?;
}
}
ast::Expr::Raise(_, expr) => {
if let Some(raise_expr) = expr {
walk_expr(raise_expr, func)?;
}
}
ast::Expr::Unary(_, expr) => {
walk_expr(expr, func)?;
}
ast::Expr::Id(_)
| ast::Expr::Column { .. }
| ast::Expr::RowId { .. }
| ast::Expr::Literal(_)
| ast::Expr::DoublyQualified(..)
| ast::Expr::Name(_)
| ast::Expr::Qualified(..)
| ast::Expr::Variable(_) => {
// No nested expressions
}
}
Ok(())
}
fn walk_expr_frame_bound<F>(bound: &ast::FrameBound, func: &mut F) -> Result<()>
where
F: FnMut(&ast::Expr) -> Result<()>,
{
match bound {
ast::FrameBound::Following(expr) | ast::FrameBound::Preceding(expr) => {
walk_expr(expr, func)?;
}
ast::FrameBound::CurrentRow
| ast::FrameBound::UnboundedFollowing
| ast::FrameBound::UnboundedPreceding => {}
}
Ok(())
}
/// Recursively walks a mutable expression, applying a function to each sub-expression.
pub fn walk_expr_mut<F>(expr: &mut ast::Expr, func: &mut F) -> Result<()>
where
F: FnMut(&mut ast::Expr) -> Result<()>,
{
func(expr)?;
match expr {
ast::Expr::Between {
lhs, start, end, ..
} => {
walk_expr_mut(lhs, func)?;
walk_expr_mut(start, func)?;
walk_expr_mut(end, func)?;
}
ast::Expr::Binary(lhs, _, rhs) => {
walk_expr_mut(lhs, func)?;
walk_expr_mut(rhs, func)?;
}
ast::Expr::Case {
base,
when_then_pairs,
else_expr,
} => {
if let Some(base_expr) = base {
walk_expr_mut(base_expr, func)?;
}
for (when_expr, then_expr) in when_then_pairs {
walk_expr_mut(when_expr, func)?;
walk_expr_mut(then_expr, func)?;
}
if let Some(else_expr) = else_expr {
walk_expr_mut(else_expr, func)?;
}
}
ast::Expr::Cast { expr, .. } => {
walk_expr_mut(expr, func)?;
}
ast::Expr::Collate(expr, _) => {
walk_expr_mut(expr, func)?;
}
ast::Expr::Exists(_) | ast::Expr::Subquery(_) => {
// TODO: Walk through select statements if needed
}
ast::Expr::FunctionCall {
args,
order_by,
filter_over,
..
} => {
if let Some(args) = args {
for arg in args {
walk_expr_mut(arg, func)?;
}
}
if let Some(order_by) = order_by {
for sort_col in order_by {
walk_expr_mut(&mut sort_col.expr, func)?;
}
}
if let Some(filter_over) = filter_over {
if let Some(filter_clause) = &mut filter_over.filter_clause {
walk_expr_mut(filter_clause, func)?;
}
if let Some(over_clause) = &mut filter_over.over_clause {
match over_clause.as_mut() {
ast::Over::Window(window) => {
if let Some(partition_by) = &mut window.partition_by {
for part_expr in partition_by {
walk_expr_mut(part_expr, func)?;
}
}
if let Some(order_by_clause) = &mut window.order_by {
for sort_col in order_by_clause {
walk_expr_mut(&mut sort_col.expr, func)?;
}
}
if let Some(frame_clause) = &mut window.frame_clause {
walk_expr_mut_frame_bound(&mut frame_clause.start, func)?;
if let Some(end_bound) = &mut frame_clause.end {
walk_expr_mut_frame_bound(end_bound, func)?;
}
}
}
ast::Over::Name(_) => {}
}
}
}
}
ast::Expr::FunctionCallStar { filter_over, .. } => {
if let Some(filter_over) = filter_over {
if let Some(filter_clause) = &mut filter_over.filter_clause {
walk_expr_mut(filter_clause, func)?;
}
if let Some(over_clause) = &mut filter_over.over_clause {
match over_clause.as_mut() {
ast::Over::Window(window) => {
if let Some(partition_by) = &mut window.partition_by {
for part_expr in partition_by {
walk_expr_mut(part_expr, func)?;
}
}
if let Some(order_by_clause) = &mut window.order_by {
for sort_col in order_by_clause {
walk_expr_mut(&mut sort_col.expr, func)?;
}
}
if let Some(frame_clause) = &mut window.frame_clause {
walk_expr_mut_frame_bound(&mut frame_clause.start, func)?;
if let Some(end_bound) = &mut frame_clause.end {
walk_expr_mut_frame_bound(end_bound, func)?;
}
}
}
ast::Over::Name(_) => {}
}
}
}
}
ast::Expr::InList { lhs, rhs, .. } => {
walk_expr_mut(lhs, func)?;
if let Some(rhs_exprs) = rhs {
for expr in rhs_exprs {
walk_expr_mut(expr, func)?;
}
}
}
ast::Expr::InSelect { lhs, rhs: _, .. } => {
walk_expr_mut(lhs, func)?;
// TODO: Walk through select statements if needed
}
ast::Expr::InTable { lhs, args, .. } => {
walk_expr_mut(lhs, func)?;
if let Some(arg_exprs) = args {
for expr in arg_exprs {
walk_expr_mut(expr, func)?;
}
}
}
ast::Expr::IsNull(expr) | ast::Expr::NotNull(expr) => {
walk_expr_mut(expr, func)?;
}
ast::Expr::Like {
lhs, rhs, escape, ..
} => {
walk_expr_mut(lhs, func)?;
walk_expr_mut(rhs, func)?;
if let Some(esc_expr) = escape {
walk_expr_mut(esc_expr, func)?;
}
}
ast::Expr::Parenthesized(exprs) => {
for expr in exprs {
walk_expr_mut(expr, func)?;
}
}
ast::Expr::Raise(_, expr) => {
if let Some(raise_expr) = expr {
walk_expr_mut(raise_expr, func)?;
}
}
ast::Expr::Unary(_, expr) => {
walk_expr_mut(expr, func)?;
}
ast::Expr::Id(_)
| ast::Expr::Column { .. }
| ast::Expr::RowId { .. }
| ast::Expr::Literal(_)
| ast::Expr::DoublyQualified(..)
| ast::Expr::Name(_)
| ast::Expr::Qualified(..)
| ast::Expr::Variable(_) => {
// No nested expressions
}
}
Ok(())
}
fn walk_expr_mut_frame_bound<F>(bound: &mut ast::FrameBound, func: &mut F) -> Result<()>
where
F: FnMut(&mut ast::Expr) -> Result<()>,
{
match bound {
ast::FrameBound::Following(expr) | ast::FrameBound::Preceding(expr) => {
walk_expr_mut(expr, func)?;
}
ast::FrameBound::CurrentRow
| ast::FrameBound::UnboundedFollowing
| ast::FrameBound::UnboundedPreceding => {}
}
Ok(())
}