1use super::ast::{SelectStatement, SimpleWhenBranch, SqlExpression, WhenBranch};
75
76pub fn map_children(
82 expr: SqlExpression,
83 mut f: impl FnMut(SqlExpression) -> SqlExpression,
84) -> SqlExpression {
85 map_children_crossing(expr, &mut f, |f, e| f(e), |_, stmt| stmt)
89}
90
91pub fn map_children_crossing<C>(
109 expr: SqlExpression,
110 ctx: &mut C,
111 mut f: impl FnMut(&mut C, SqlExpression) -> SqlExpression,
112 mut f_stmt: impl FnMut(&mut C, Box<SelectStatement>) -> Box<SelectStatement>,
113) -> SqlExpression {
114 match expr {
115 e @ (SqlExpression::Column(_)
117 | SqlExpression::StringLiteral(_)
118 | SqlExpression::NumberLiteral(_)
119 | SqlExpression::BooleanLiteral(_)
120 | SqlExpression::Null
121 | SqlExpression::DateTimeConstructor { .. }
122 | SqlExpression::DateTimeToday { .. }) => e,
123
124 SqlExpression::ScalarSubquery { query } => SqlExpression::ScalarSubquery {
126 query: f_stmt(ctx, query),
127 },
128
129 SqlExpression::MethodCall {
131 object,
132 method,
133 args,
134 } => SqlExpression::MethodCall {
135 object,
136 method,
137 args: args.into_iter().map(|e| f(&mut *ctx, e)).collect(),
138 },
139
140 SqlExpression::ChainedMethodCall { base, method, args } => {
141 SqlExpression::ChainedMethodCall {
142 base: Box::new(f(&mut *ctx, *base)),
143 method,
144 args: args.into_iter().map(|e| f(&mut *ctx, e)).collect(),
145 }
146 }
147
148 SqlExpression::FunctionCall {
149 name,
150 args,
151 distinct,
152 } => SqlExpression::FunctionCall {
153 name,
154 args: args.into_iter().map(|e| f(&mut *ctx, e)).collect(),
155 distinct,
156 },
157
158 SqlExpression::WindowFunction {
159 name,
160 args,
161 mut window_spec,
162 } => {
163 let args = args.into_iter().map(|e| f(&mut *ctx, e)).collect();
164 for item in &mut window_spec.order_by {
166 let taken = std::mem::replace(&mut item.expr, SqlExpression::Null);
167 item.expr = f(&mut *ctx, taken);
168 }
169 SqlExpression::WindowFunction {
170 name,
171 args,
172 window_spec,
173 }
174 }
175
176 SqlExpression::BinaryOp { left, op, right } => SqlExpression::BinaryOp {
177 left: Box::new(f(&mut *ctx, *left)),
178 op,
179 right: Box::new(f(&mut *ctx, *right)),
180 },
181
182 SqlExpression::InList { expr, values } => SqlExpression::InList {
183 expr: Box::new(f(&mut *ctx, *expr)),
184 values: values.into_iter().map(|e| f(&mut *ctx, e)).collect(),
185 },
186
187 SqlExpression::NotInList { expr, values } => SqlExpression::NotInList {
188 expr: Box::new(f(&mut *ctx, *expr)),
189 values: values.into_iter().map(|e| f(&mut *ctx, e)).collect(),
190 },
191
192 SqlExpression::Between { expr, lower, upper } => SqlExpression::Between {
193 expr: Box::new(f(&mut *ctx, *expr)),
194 lower: Box::new(f(&mut *ctx, *lower)),
195 upper: Box::new(f(&mut *ctx, *upper)),
196 },
197
198 SqlExpression::Not { expr } => SqlExpression::Not {
199 expr: Box::new(f(&mut *ctx, *expr)),
200 },
201
202 SqlExpression::CaseExpression {
203 when_branches,
204 else_branch,
205 } => SqlExpression::CaseExpression {
206 when_branches: when_branches
207 .into_iter()
208 .map(|b| WhenBranch {
209 condition: Box::new(f(&mut *ctx, *b.condition)),
210 result: Box::new(f(&mut *ctx, *b.result)),
211 })
212 .collect(),
213 else_branch: else_branch.map(|e| Box::new(f(&mut *ctx, *e))),
214 },
215
216 SqlExpression::SimpleCaseExpression {
217 expr,
218 when_branches,
219 else_branch,
220 } => SqlExpression::SimpleCaseExpression {
221 expr: Box::new(f(&mut *ctx, *expr)),
222 when_branches: when_branches
223 .into_iter()
224 .map(|b| SimpleWhenBranch {
225 value: Box::new(f(&mut *ctx, *b.value)),
226 result: Box::new(f(&mut *ctx, *b.result)),
227 })
228 .collect(),
229 else_branch: else_branch.map(|e| Box::new(f(&mut *ctx, *e))),
230 },
231
232 SqlExpression::Unnest { column, delimiter } => SqlExpression::Unnest {
233 column: Box::new(f(&mut *ctx, *column)),
234 delimiter,
235 },
236
237 SqlExpression::InSubquery { expr, subquery } => SqlExpression::InSubquery {
239 expr: Box::new(f(&mut *ctx, *expr)),
240 subquery: f_stmt(ctx, subquery),
241 },
242
243 SqlExpression::NotInSubquery { expr, subquery } => SqlExpression::NotInSubquery {
244 expr: Box::new(f(&mut *ctx, *expr)),
245 subquery: f_stmt(ctx, subquery),
246 },
247
248 SqlExpression::InSubqueryTuple { exprs, subquery } => SqlExpression::InSubqueryTuple {
249 exprs: exprs.into_iter().map(|e| f(&mut *ctx, e)).collect(),
250 subquery: f_stmt(ctx, subquery),
251 },
252
253 SqlExpression::NotInSubqueryTuple { exprs, subquery } => {
254 SqlExpression::NotInSubqueryTuple {
255 exprs: exprs.into_iter().map(|e| f(&mut *ctx, e)).collect(),
256 subquery: f_stmt(ctx, subquery),
257 }
258 }
259 }
260}
261
262pub fn visit_children<'a>(expr: &'a SqlExpression, mut f: impl FnMut(&'a SqlExpression)) {
268 visit_children_crossing(expr, &mut f, |f, e| f(e), |_, _| {});
271}
272
273pub fn visit_children_crossing<'a, C>(
281 expr: &'a SqlExpression,
282 ctx: &mut C,
283 mut f: impl FnMut(&mut C, &'a SqlExpression),
284 mut f_stmt: impl FnMut(&mut C, &'a SelectStatement),
285) {
286 match expr {
287 SqlExpression::Column(_)
289 | SqlExpression::StringLiteral(_)
290 | SqlExpression::NumberLiteral(_)
291 | SqlExpression::BooleanLiteral(_)
292 | SqlExpression::Null
293 | SqlExpression::DateTimeConstructor { .. }
294 | SqlExpression::DateTimeToday { .. } => {}
295
296 SqlExpression::ScalarSubquery { query } => f_stmt(ctx, query),
298
299 SqlExpression::MethodCall { args, .. } | SqlExpression::FunctionCall { args, .. } => {
301 args.iter().for_each(|e| f(&mut *ctx, e));
302 }
303
304 SqlExpression::ChainedMethodCall { base, args, .. } => {
305 f(&mut *ctx, base);
306 args.iter().for_each(|e| f(&mut *ctx, e));
307 }
308
309 SqlExpression::WindowFunction {
310 args, window_spec, ..
311 } => {
312 args.iter().for_each(|e| f(&mut *ctx, e));
313 window_spec
315 .order_by
316 .iter()
317 .for_each(|item| f(&mut *ctx, &item.expr));
318 }
319
320 SqlExpression::BinaryOp { left, right, .. } => {
321 f(&mut *ctx, left);
322 f(&mut *ctx, right);
323 }
324
325 SqlExpression::InList { expr, values } | SqlExpression::NotInList { expr, values } => {
326 f(&mut *ctx, expr);
327 values.iter().for_each(|e| f(&mut *ctx, e));
328 }
329
330 SqlExpression::Between { expr, lower, upper } => {
331 f(&mut *ctx, expr);
332 f(&mut *ctx, lower);
333 f(&mut *ctx, upper);
334 }
335
336 SqlExpression::Not { expr } | SqlExpression::Unnest { column: expr, .. } => {
337 f(&mut *ctx, expr)
338 }
339
340 SqlExpression::CaseExpression {
341 when_branches,
342 else_branch,
343 } => {
344 for branch in when_branches {
345 f(&mut *ctx, &branch.condition);
346 f(&mut *ctx, &branch.result);
347 }
348 if let Some(e) = else_branch {
349 f(&mut *ctx, e);
350 }
351 }
352
353 SqlExpression::SimpleCaseExpression {
354 expr,
355 when_branches,
356 else_branch,
357 } => {
358 f(&mut *ctx, expr);
359 for branch in when_branches {
360 f(&mut *ctx, &branch.value);
361 f(&mut *ctx, &branch.result);
362 }
363 if let Some(e) = else_branch {
364 f(&mut *ctx, e);
365 }
366 }
367
368 SqlExpression::InSubquery { expr, subquery }
370 | SqlExpression::NotInSubquery { expr, subquery } => {
371 f(&mut *ctx, expr);
372 f_stmt(ctx, subquery);
373 }
374
375 SqlExpression::InSubqueryTuple { exprs, subquery }
376 | SqlExpression::NotInSubqueryTuple { exprs, subquery } => {
377 exprs.iter().for_each(|e| f(&mut *ctx, e));
378 f_stmt(ctx, subquery);
379 }
380 }
381}
382
383pub fn visit_all<'a>(expr: &'a SqlExpression, f: &mut impl FnMut(&'a SqlExpression)) {
389 f(expr);
390 visit_children(expr, |child| visit_all(child, f));
391}
392
393#[cfg(test)]
394mod tests {
395 use super::*;
396 use crate::sql::parser::ast::ColumnRef;
397 use crate::sql::recursive_parser::Parser;
398
399 fn expr_of(select_expr: &str) -> SqlExpression {
401 let sql = format!("SELECT {select_expr} FROM t");
402 let stmt = Parser::new(&sql)
403 .parse()
404 .unwrap_or_else(|e| panic!("{sql} should parse: {e}"));
405
406 stmt.select_items
407 .into_iter()
408 .find_map(|item| match item {
409 crate::sql::parser::ast::SelectItem::Expression { expr, .. } => Some(expr),
410 _ => None,
411 })
412 .expect("expected a projected expression")
413 }
414
415 fn columns(expr: &SqlExpression) -> Vec<String> {
417 let mut found = Vec::new();
418 visit_all(expr, &mut |e| {
419 if let SqlExpression::Column(c) = e {
420 found.push(c.name.clone());
421 }
422 });
423 found
424 }
425
426 fn rename_all(expr: SqlExpression, to: &str) -> SqlExpression {
428 match expr {
429 SqlExpression::Column(_) => SqlExpression::Column(ColumnRef::unquoted(to.to_string())),
430 other => map_children(other, |e| rename_all(e, to)),
431 }
432 }
433
434 #[test]
435 fn visit_all_reaches_case_branches() {
436 let expr = expr_of("CASE WHEN a > 1 THEN b ELSE c END");
437 let mut found = columns(&expr);
438 found.sort();
439 assert_eq!(found, vec!["a", "b", "c"]);
440 }
441
442 #[test]
443 fn visit_all_reaches_nested_function_args() {
444 let expr = expr_of("UPPER(TRIM(name))");
445 assert_eq!(columns(&expr), vec!["name"]);
446 }
447
448 #[test]
449 fn visit_all_reaches_between_operands() {
450 let expr = expr_of("x BETWEEN lo AND hi");
451 assert_eq!(columns(&expr), vec!["x", "lo", "hi"]);
452 }
453
454 #[test]
457 fn visit_all_reaches_window_order_by() {
458 let expr = expr_of("ROW_NUMBER() OVER (ORDER BY created_at)");
459 assert!(
460 columns(&expr).contains(&"created_at".to_string()),
461 "window ORDER BY expressions must be reachable"
462 );
463 }
464
465 #[test]
468 fn walkers_do_not_cross_into_subqueries() {
469 let expr = expr_of("(SELECT MAX(inner_col) FROM other)");
470 assert!(
471 matches!(expr, SqlExpression::ScalarSubquery { .. }),
472 "expected a scalar subquery"
473 );
474 assert!(
475 columns(&expr).is_empty(),
476 "must not descend into a subquery's own scope"
477 );
478
479 let stmt = Parser::new("SELECT a FROM t WHERE outer_col IN (SELECT x FROM other)")
481 .parse()
482 .expect("should parse");
483 let cond = &stmt.where_clause.expect("where clause").conditions[0].expr;
484 assert_eq!(columns(cond), vec!["outer_col"]);
485 }
486
487 #[test]
493 fn crossing_walkers_do_reach_into_subqueries() {
494 let expr = expr_of("(SELECT MAX(inner_col) FROM other)");
496 let mut statements_seen = 0;
497 visit_children_crossing(&expr, &mut statements_seen, |_, _| {}, |n, _| *n += 1);
498 assert_eq!(
499 statements_seen, 1,
500 "a scalar subquery's statement must be reachable when crossing"
501 );
502
503 let rewritten = map_children_crossing(
505 expr,
506 &mut (),
507 |_, e| e,
508 |_, mut stmt| {
509 stmt.limit = Some(1);
510 stmt
511 },
512 );
513 match rewritten {
514 SqlExpression::ScalarSubquery { query } => assert_eq!(query.limit, Some(1)),
515 other => panic!("expected a scalar subquery, got {other:?}"),
516 }
517 }
518
519 #[test]
522 fn crossing_reaches_tuple_subquery_operands_and_statement() {
523 let stmt = Parser::new("SELECT a FROM t WHERE (a, b) IN (SELECT x, y FROM u)")
524 .parse()
525 .expect("should parse");
526 let cond = &stmt.where_clause.expect("where clause").conditions[0].expr;
527
528 let mut ctx = (Vec::<String>::new(), 0);
529 visit_children_crossing(
530 cond,
531 &mut ctx,
532 |ctx, e| {
533 if let SqlExpression::Column(c) = e {
534 ctx.0.push(c.name.clone());
535 }
536 },
537 |ctx, _| ctx.1 += 1,
538 );
539
540 assert_eq!(ctx.0, vec!["a", "b"], "same-scope operands must be visited");
541 assert_eq!(ctx.1, 1, "the subquery statement must be visited too");
542 }
543
544 #[test]
545 fn map_children_rewrites_nested_expressions() {
546 let expr = expr_of("CASE WHEN a > 1 THEN UPPER(b) ELSE c END");
547 let renamed = rename_all(expr, "z");
548 assert_eq!(columns(&renamed), vec!["z", "z", "z"]);
549 }
550
551 #[test]
552 fn map_children_rewrites_window_order_by() {
553 let expr = expr_of("ROW_NUMBER() OVER (ORDER BY created_at)");
554 let renamed = rename_all(expr, "z");
555 assert_eq!(columns(&renamed), vec!["z"]);
556 }
557
558 #[test]
559 fn map_children_leaves_leaves_alone() {
560 let expr = expr_of("42");
561 let mapped = map_children(expr, |_| panic!("a literal has no children"));
562 assert!(matches!(mapped, SqlExpression::NumberLiteral(ref n) if n == "42"));
563 }
564}