use rudb_plan::{Arm, Expr, ExprRef, Plan, Slice};
pub(crate) fn rebuild(
plan: &mut Plan,
expr: ExprRef,
child: &mut impl FnMut(&mut Plan, ExprRef) -> ExprRef,
) -> ExprRef {
let ty = plan.expr_type(expr).clone();
match *plan.expr(expr) {
Expr::Column(_) | Expr::Constant(_) => expr,
Expr::Cast { input, try_cast } => {
let rewritten = child(plan, input);
if rewritten == input {
expr
} else {
plan.add_expr(Expr::Cast { input: rewritten, try_cast }, ty)
}
}
Expr::Compare { op, left, right } => {
let rewritten_left = child(plan, left);
let rewritten_right = child(plan, right);
if rewritten_left == left && rewritten_right == right {
expr
} else {
plan.add_expr(
Expr::Compare { op, left: rewritten_left, right: rewritten_right },
ty,
)
}
}
Expr::Conjunction { op, children } => match list(plan, children, child) {
None => expr,
Some(children) => plan.add_expr(Expr::Conjunction { op, children }, ty),
},
Expr::Function { name, args } => match list(plan, args, child) {
None => expr,
Some(args) => plan.add_expr(Expr::Function { name, args }, ty),
},
Expr::Aggregate { name, args, distinct, filter } => {
let rewritten_args = list(plan, args, child);
let rewritten_filter = filter.map(|inner| child(plan, inner));
if rewritten_args.is_none() && rewritten_filter == filter {
expr
} else {
let args = rewritten_args.unwrap_or(args);
plan.add_expr(
Expr::Aggregate { name, args, distinct, filter: rewritten_filter },
ty,
)
}
}
Expr::Case { arms, otherwise } => {
let held = plan.arm_list(arms).to_vec();
let rewritten: Vec<Arm> = held
.iter()
.map(|arm| Arm { when: child(plan, arm.when), then: child(plan, arm.then) })
.collect();
let rewritten_otherwise = otherwise.map(|inner| child(plan, inner));
if rewritten == held && rewritten_otherwise == otherwise {
expr
} else {
let arms = plan.add_arms(&rewritten);
plan.add_expr(Expr::Case { arms, otherwise: rewritten_otherwise }, ty)
}
}
}
}
pub(crate) fn list(
plan: &mut Plan,
slice: Slice,
child: &mut impl FnMut(&mut Plan, ExprRef) -> ExprRef,
) -> Option<Slice> {
let held = plan.expr_list(slice).to_vec();
let rewritten: Vec<ExprRef> = held.iter().map(|&expr| child(plan, expr)).collect();
(rewritten != held).then(|| plan.add_expr_list(&rewritten))
}