clt_database/translate/expr/
walk.rs1use super::*;
2use crate::function::{Deterministic, ExtFunc};
3
4pub enum WalkControl {
5 Continue, SkipChildren, }
8
9pub fn walk_expr<'a, F>(expr: &'a ast::Expr, func: &mut F) -> Result<WalkControl>
11where
12 F: FnMut(&'a ast::Expr) -> Result<WalkControl>,
13{
14 enum WalkItem<'a> {
15 Expr(&'a ast::Expr),
16 FrameBound(&'a ast::FrameBound),
17 }
18
19 type Stack<'b> = smallvec::SmallVec<[WalkItem<'b>; 4]>;
20
21 let mut stack: Stack<'a> = smallvec::smallvec![WalkItem::Expr(expr)];
22 let push_over_clause_walk_items = |stack: &mut Stack<'a>, over_clause: &'a ast::Over| {
23 if let ast::Over::Window(window) = over_clause {
24 if let Some(frame_clause) = &window.frame_clause {
25 if let Some(end_bound) = &frame_clause.end {
26 stack.push(WalkItem::FrameBound(end_bound));
27 }
28 stack.push(WalkItem::FrameBound(&frame_clause.start));
29 }
30 for sort_col in window.order_by.iter().rev() {
31 stack.push(WalkItem::Expr(&sort_col.expr));
32 }
33 for part_expr in window.partition_by.iter().rev() {
34 stack.push(WalkItem::Expr(part_expr));
35 }
36 }
37 };
38 while let Some(item) = stack.pop() {
39 match item {
40 WalkItem::Expr(expr) => {
41 if matches!(func(expr)?, WalkControl::SkipChildren) {
42 continue;
43 }
44 match expr {
45 ast::Expr::SubqueryResult { lhs, .. } => {
46 if let Some(lhs) = lhs {
47 stack.push(WalkItem::Expr(lhs));
48 }
49 }
50 ast::Expr::Between {
51 lhs, start, end, ..
52 } => {
53 stack.push(WalkItem::Expr(end));
54 stack.push(WalkItem::Expr(start));
55 stack.push(WalkItem::Expr(lhs));
56 }
57 ast::Expr::Binary(lhs, _, rhs) => {
58 stack.push(WalkItem::Expr(rhs));
59 stack.push(WalkItem::Expr(lhs));
60 }
61 ast::Expr::Case {
62 base,
63 when_then_pairs,
64 else_expr,
65 } => {
66 if let Some(else_expr) = else_expr {
67 stack.push(WalkItem::Expr(else_expr));
68 }
69 for (when_expr, then_expr) in when_then_pairs.iter().rev() {
70 stack.push(WalkItem::Expr(then_expr));
71 stack.push(WalkItem::Expr(when_expr));
72 }
73 if let Some(base_expr) = base {
74 stack.push(WalkItem::Expr(base_expr));
75 }
76 }
77 ast::Expr::Cast { expr, .. } | ast::Expr::Collate(expr, _) => {
78 stack.push(WalkItem::Expr(expr));
79 }
80 ast::Expr::Exists(_select) | ast::Expr::Subquery(_select) => {
81 }
83 ast::Expr::FunctionCall {
84 args,
85 order_by,
86 within_group,
87 filter_over,
88 ..
89 } => {
90 if let Some(over_clause) = &filter_over.over_clause {
91 push_over_clause_walk_items(&mut stack, over_clause);
92 }
93 if let Some(filter_clause) = &filter_over.filter_clause {
94 stack.push(WalkItem::Expr(filter_clause));
95 }
96 for sort_col in within_group.iter().rev() {
97 stack.push(WalkItem::Expr(&sort_col.expr));
98 }
99 for sort_col in order_by.iter().rev() {
100 stack.push(WalkItem::Expr(&sort_col.expr));
101 }
102 for arg in args.iter().rev() {
103 stack.push(WalkItem::Expr(arg));
104 }
105 }
106 ast::Expr::FunctionCallStar { filter_over, .. } => {
107 if let Some(over_clause) = &filter_over.over_clause {
108 push_over_clause_walk_items(&mut stack, over_clause);
109 }
110 if let Some(filter_clause) = &filter_over.filter_clause {
111 stack.push(WalkItem::Expr(filter_clause));
112 }
113 }
114 ast::Expr::InList { lhs, rhs, .. } => {
115 for expr in rhs.iter().rev() {
116 stack.push(WalkItem::Expr(expr));
117 }
118 stack.push(WalkItem::Expr(lhs));
119 }
120 ast::Expr::InSelect { lhs, rhs: _, .. } => {
121 stack.push(WalkItem::Expr(lhs));
122 }
124 ast::Expr::InTable { lhs, args, .. } => {
125 for expr in args.iter().rev() {
126 stack.push(WalkItem::Expr(expr));
127 }
128 stack.push(WalkItem::Expr(lhs));
129 }
130 ast::Expr::IsNull(expr) | ast::Expr::NotNull(expr) => {
131 stack.push(WalkItem::Expr(expr));
132 }
133 ast::Expr::Like {
134 lhs, rhs, escape, ..
135 } => {
136 if let Some(esc_expr) = escape {
137 stack.push(WalkItem::Expr(esc_expr));
138 }
139 stack.push(WalkItem::Expr(rhs));
140 stack.push(WalkItem::Expr(lhs));
141 }
142 ast::Expr::Parenthesized(exprs) => {
143 for expr in exprs.iter().rev() {
144 stack.push(WalkItem::Expr(expr));
145 }
146 }
147 ast::Expr::Raise(_, expr) => {
148 if let Some(raise_expr) = expr {
149 stack.push(WalkItem::Expr(raise_expr));
150 }
151 }
152 ast::Expr::Unary(_, expr) => {
153 stack.push(WalkItem::Expr(expr));
154 }
155 ast::Expr::Array { .. } | ast::Expr::Subscript { .. } => {
156 unreachable!(
157 "Array and Subscript are desugared into function calls by the parser"
158 )
159 }
160 ast::Expr::Id(_)
161 | ast::Expr::Column { .. }
162 | ast::Expr::RowId { .. }
163 | ast::Expr::Literal(_)
164 | ast::Expr::DoublyQualified(..)
165 | ast::Expr::Name(_)
166 | ast::Expr::Qualified(..)
167 | ast::Expr::Variable(_)
168 | ast::Expr::Register(_)
169 | ast::Expr::Default => {}
170 ast::Expr::FieldAccess { base, .. } => {
171 stack.push(WalkItem::Expr(base));
172 }
173 }
174 }
175 WalkItem::FrameBound(bound) => match bound {
176 ast::FrameBound::Following(expr) | ast::FrameBound::Preceding(expr) => {
177 stack.push(WalkItem::Expr(expr));
178 }
179 ast::FrameBound::CurrentRow
180 | ast::FrameBound::UnboundedFollowing
181 | ast::FrameBound::UnboundedPreceding => {}
182 },
183 }
184 }
185 Ok(WalkControl::Continue)
186}
187
188pub fn expr_references_subquery_id(expr: &ast::Expr, subquery_id: TableInternalId) -> bool {
189 let mut found = false;
190 let _ = walk_expr(expr, &mut |e: &ast::Expr| -> Result<WalkControl> {
191 if let ast::Expr::SubqueryResult {
192 subquery_id: sid, ..
193 } = e
194 {
195 if *sid == subquery_id {
196 found = true;
197 return Ok(WalkControl::SkipChildren);
198 }
199 }
200 Ok(WalkControl::Continue)
201 });
202 found
203}
204
205pub fn expr_references_any_subquery(expr: &ast::Expr) -> bool {
206 let mut found = false;
207 let _ = walk_expr(expr, &mut |e: &ast::Expr| -> Result<WalkControl> {
208 if matches!(e, ast::Expr::SubqueryResult { .. }) {
209 found = true;
210 return Ok(WalkControl::SkipChildren);
211 }
212 Ok(WalkControl::Continue)
213 });
214 found
215}
216
217pub fn expr_contains_nondeterministic_scalar_function(
227 expr: &ast::Expr,
228 resolver: &Resolver<'_>,
229) -> Result<bool> {
230 fn is_nondeterministic_scalar_like_function(func: &Func) -> bool {
231 match func {
232 Func::Agg(_) | Func::Window(_) => false,
240
241 Func::External(external) if matches!(external.func, ExtFunc::Aggregate { .. }) => false,
247
248 _ => !func.is_deterministic(),
249 }
250 }
251
252 let mut found = false;
253 crate::util::walk_expr_with_subqueries(expr, &mut |e| -> Result<WalkControl> {
254 if found {
255 return Ok(WalkControl::SkipChildren);
256 }
257
258 let func = match e {
259 ast::Expr::FunctionCall { name, args, .. } => {
260 resolver.resolve_function(name.as_str(), args.len())?
261 }
262 ast::Expr::FunctionCallStar { name, .. } => {
263 resolver.resolve_function(name.as_str(), 0)?
264 }
265 _ => None,
266 };
267
268 if func
271 .as_ref()
272 .is_some_and(is_nondeterministic_scalar_like_function)
273 {
274 found = true;
275 return Ok(WalkControl::SkipChildren);
276 }
277
278 Ok(WalkControl::Continue)
279 })?;
280
281 Ok(found)
282}
283
284pub fn walk_expr_mut<'a, F>(expr: &'a mut ast::Expr, func: &mut F) -> Result<WalkControl>
286where
287 F: FnMut(&mut ast::Expr) -> Result<WalkControl>,
288{
289 enum WalkItem<'a> {
290 Expr(&'a mut ast::Expr),
291 FrameBound(&'a mut ast::FrameBound),
292 }
293
294 type Stack<'a> = smallvec::SmallVec<[WalkItem<'a>; 4]>;
295
296 fn push_over_clause_walk_items<'a>(stack: &mut Stack<'a>, over_clause: &'a mut ast::Over) {
297 if let ast::Over::Window(window) = over_clause {
298 if let Some(frame_clause) = &mut window.frame_clause {
299 if let Some(end_bound) = &mut frame_clause.end {
300 stack.push(WalkItem::FrameBound(end_bound));
301 }
302 stack.push(WalkItem::FrameBound(&mut frame_clause.start));
303 }
304 for sort_col in window.order_by.iter_mut().rev() {
305 stack.push(WalkItem::Expr(&mut sort_col.expr));
306 }
307 for part_expr in window.partition_by.iter_mut().rev() {
308 stack.push(WalkItem::Expr(part_expr));
309 }
310 }
311 }
312
313 let mut stack: Stack<'a> = smallvec::smallvec![WalkItem::Expr(expr)];
314 while let Some(item) = stack.pop() {
315 match item {
316 WalkItem::Expr(expr) => {
317 if matches!(func(expr)?, WalkControl::SkipChildren) {
318 continue;
319 }
320 match expr {
321 ast::Expr::SubqueryResult { lhs, .. } => {
322 if let Some(lhs) = lhs {
323 stack.push(WalkItem::Expr(lhs));
324 }
325 }
326 ast::Expr::Between {
327 lhs, start, end, ..
328 } => {
329 stack.push(WalkItem::Expr(end));
330 stack.push(WalkItem::Expr(start));
331 stack.push(WalkItem::Expr(lhs));
332 }
333 ast::Expr::Binary(lhs, _, rhs) => {
334 stack.push(WalkItem::Expr(rhs));
335 stack.push(WalkItem::Expr(lhs));
336 }
337 ast::Expr::Case {
338 base,
339 when_then_pairs,
340 else_expr,
341 } => {
342 if let Some(else_expr) = else_expr {
343 stack.push(WalkItem::Expr(else_expr));
344 }
345 for (when_expr, then_expr) in when_then_pairs.iter_mut().rev() {
346 stack.push(WalkItem::Expr(then_expr));
347 stack.push(WalkItem::Expr(when_expr));
348 }
349 if let Some(base_expr) = base {
350 stack.push(WalkItem::Expr(base_expr));
351 }
352 }
353 ast::Expr::Cast { expr, .. } | ast::Expr::Collate(expr, _) => {
354 stack.push(WalkItem::Expr(expr));
355 }
356 ast::Expr::Exists(_) | ast::Expr::Subquery(_) => {
357 }
359 ast::Expr::FunctionCall {
360 args,
361 order_by,
362 within_group,
363 filter_over,
364 ..
365 } => {
366 if let Some(over_clause) = &mut filter_over.over_clause {
367 push_over_clause_walk_items(&mut stack, over_clause);
368 }
369 if let Some(filter_clause) = &mut filter_over.filter_clause {
370 stack.push(WalkItem::Expr(filter_clause));
371 }
372 for sort_col in within_group.iter_mut().rev() {
373 stack.push(WalkItem::Expr(&mut sort_col.expr));
374 }
375 for sort_col in order_by.iter_mut().rev() {
376 stack.push(WalkItem::Expr(&mut sort_col.expr));
377 }
378 for arg in args.iter_mut().rev() {
379 stack.push(WalkItem::Expr(arg));
380 }
381 }
382 ast::Expr::FunctionCallStar { filter_over, .. } => {
383 if let Some(over_clause) = &mut filter_over.over_clause {
384 push_over_clause_walk_items(&mut stack, over_clause);
385 }
386 if let Some(filter_clause) = &mut filter_over.filter_clause {
387 stack.push(WalkItem::Expr(filter_clause));
388 }
389 }
390 ast::Expr::InList { lhs, rhs, .. } => {
391 for expr in rhs.iter_mut().rev() {
392 stack.push(WalkItem::Expr(expr));
393 }
394 stack.push(WalkItem::Expr(lhs));
395 }
396 ast::Expr::InSelect { lhs, rhs: _, .. } => {
397 stack.push(WalkItem::Expr(lhs));
398 }
400 ast::Expr::InTable { lhs, args, .. } => {
401 for expr in args.iter_mut().rev() {
402 stack.push(WalkItem::Expr(expr));
403 }
404 stack.push(WalkItem::Expr(lhs));
405 }
406 ast::Expr::IsNull(expr) | ast::Expr::NotNull(expr) => {
407 stack.push(WalkItem::Expr(expr));
408 }
409 ast::Expr::Like {
410 lhs, rhs, escape, ..
411 } => {
412 if let Some(esc_expr) = escape {
413 stack.push(WalkItem::Expr(esc_expr));
414 }
415 stack.push(WalkItem::Expr(rhs));
416 stack.push(WalkItem::Expr(lhs));
417 }
418 ast::Expr::Parenthesized(exprs) => {
419 for expr in exprs.iter_mut().rev() {
420 stack.push(WalkItem::Expr(expr));
421 }
422 }
423 ast::Expr::Raise(_, expr) => {
424 if let Some(raise_expr) = expr {
425 stack.push(WalkItem::Expr(raise_expr));
426 }
427 }
428 ast::Expr::Unary(_, expr) => {
429 stack.push(WalkItem::Expr(expr));
430 }
431 ast::Expr::Array { .. } | ast::Expr::Subscript { .. } => {
432 unreachable!(
433 "Array and Subscript are desugared into function calls by the parser"
434 )
435 }
436 ast::Expr::Id(_)
437 | ast::Expr::Column { .. }
438 | ast::Expr::RowId { .. }
439 | ast::Expr::Literal(_)
440 | ast::Expr::DoublyQualified(..)
441 | ast::Expr::Name(_)
442 | ast::Expr::Qualified(..)
443 | ast::Expr::Variable(_)
444 | ast::Expr::Register(_)
445 | ast::Expr::Default => {}
446 ast::Expr::FieldAccess { base, .. } => {
447 stack.push(WalkItem::Expr(base));
448 }
449 }
450 }
451 WalkItem::FrameBound(bound) => match bound {
452 ast::FrameBound::Following(expr) | ast::FrameBound::Preceding(expr) => {
453 stack.push(WalkItem::Expr(expr));
454 }
455 ast::FrameBound::CurrentRow
456 | ast::FrameBound::UnboundedFollowing
457 | ast::FrameBound::UnboundedPreceding => {}
458 },
459 }
460 }
461 Ok(WalkControl::Continue)
462}