1use crate::ast::{Assignment, Expr, Literal};
29use crate::plan::PlanNode;
30use rustc_hash::FxHashMap;
31
32pub struct PlanCache {
38 cache: FxHashMap<u64, PlanNode>,
39 capacity: usize,
40 pub hits: u64,
41 pub misses: u64,
42}
43
44impl PlanCache {
45 pub fn new(capacity: usize) -> Self {
46 PlanCache {
47 cache: FxHashMap::default(),
48 capacity,
49 hits: 0,
50 misses: 0,
51 }
52 }
53
54 pub fn insert(&mut self, hash: u64, plan: PlanNode, source_literal_count: usize) {
72 if count_literal_slots(&plan) != source_literal_count {
73 return;
74 }
75 if self.cache.len() >= self.capacity && !self.cache.contains_key(&hash) {
76 self.cache.clear();
81 }
82 self.cache.insert(hash, plan);
83 }
84
85 pub fn get_with_substitution(&mut self, hash: u64, literals: &[Literal]) -> Option<PlanNode> {
97 match self.cache.get(&hash) {
98 Some(template) => {
99 self.hits += 1;
100 let mut plan = template.clone();
101 let mut idx = 0usize;
102 substitute_plan(&mut plan, literals, &mut idx);
103 debug_assert_eq!(
104 idx,
105 literals.len(),
106 "plan substitution consumed {idx} literals but query had {}",
107 literals.len(),
108 );
109 Some(plan)
110 }
111 None => {
112 self.misses += 1;
113 None
114 }
115 }
116 }
117
118 pub fn len(&self) -> usize {
119 self.cache.len()
120 }
121
122 pub fn is_empty(&self) -> bool {
123 self.cache.is_empty()
124 }
125
126 pub fn clear(&mut self) {
127 self.cache.clear();
128 }
129}
130
131pub(crate) fn substitute_plan(plan: &mut PlanNode, literals: &[Literal], idx: &mut usize) {
150 match plan {
151 PlanNode::SeqScan { .. } => {}
152 PlanNode::AliasScan { .. } => {}
153 PlanNode::IndexScan { key, .. } => {
154 substitute_expr(key, literals, idx);
155 }
156 PlanNode::RangeScan { start, end, .. } => {
157 if let Some((expr, _)) = start {
158 substitute_expr(expr, literals, idx);
159 }
160 if let Some((expr, _)) = end {
161 substitute_expr(expr, literals, idx);
162 }
163 }
164 PlanNode::Filter { input, predicate } => {
165 substitute_plan(input, literals, idx);
166 substitute_expr(predicate, literals, idx);
167 }
168 PlanNode::Project { input, fields } => {
169 substitute_plan(input, literals, idx);
170 for f in fields {
171 substitute_expr(&mut f.expr, literals, idx);
172 }
173 }
174 PlanNode::Sort { input, .. } => substitute_plan(input, literals, idx),
175 PlanNode::AlterTable { .. } => {}
176 PlanNode::DropTable { .. } => {}
177 PlanNode::Limit { input, count } => {
178 if let PlanNode::Offset {
187 input: inner,
188 count: off_count,
189 } = input.as_mut()
190 {
191 substitute_plan(inner, literals, idx);
192 substitute_expr(count, literals, idx);
193 substitute_expr(off_count, literals, idx);
194 } else {
195 substitute_plan(input, literals, idx);
196 substitute_expr(count, literals, idx);
197 }
198 }
199 PlanNode::Offset { input, count } => {
200 substitute_plan(input, literals, idx);
203 substitute_expr(count, literals, idx);
204 }
205 PlanNode::Aggregate { input, .. } => {
206 substitute_plan(input, literals, idx);
207 }
208 PlanNode::NestedLoopJoin {
209 left, right, on, ..
210 } => {
211 substitute_plan(left, literals, idx);
216 substitute_plan(right, literals, idx);
217 if let Some(pred) = on {
218 substitute_expr(pred, literals, idx);
219 }
220 }
221 PlanNode::Distinct { input } => {
222 substitute_plan(input, literals, idx);
223 }
224 PlanNode::GroupBy { input, having, .. } => {
225 substitute_plan(input, literals, idx);
226 if let Some(pred) = having {
227 substitute_expr(pred, literals, idx);
228 }
229 }
230 PlanNode::Insert { rows, .. } => {
231 for assignments in rows {
232 substitute_assignments(assignments, literals, idx);
233 }
234 }
235 PlanNode::Upsert {
236 assignments,
237 on_conflict,
238 ..
239 } => {
240 substitute_assignments(assignments, literals, idx);
241 substitute_assignments(on_conflict, literals, idx);
242 }
243 PlanNode::Update {
244 input, assignments, ..
245 } => {
246 substitute_plan(input, literals, idx);
247 substitute_assignments(assignments, literals, idx);
248 }
249 PlanNode::Delete { input, .. } => {
250 substitute_plan(input, literals, idx);
251 }
252 PlanNode::CreateTable { .. } => {}
253 PlanNode::CreateView { .. } => {}
254 PlanNode::RefreshView { .. } => {}
255 PlanNode::DropView { .. } => {}
256 PlanNode::Window { input, windows } => {
257 substitute_plan(input, literals, idx);
258 for w in windows {
259 for arg in &mut w.args {
260 substitute_expr(arg, literals, idx);
261 }
262 }
263 }
264 PlanNode::Union { left, right, .. } => {
265 substitute_plan(left, literals, idx);
266 substitute_plan(right, literals, idx);
267 }
268 PlanNode::Explain { input } => {
269 substitute_plan(input, literals, idx);
270 }
271 PlanNode::ListTypes | PlanNode::Describe { .. } => {}
272 PlanNode::Begin | PlanNode::Commit | PlanNode::Rollback => {}
273 }
274}
275
276fn substitute_assignments(assignments: &mut [Assignment], literals: &[Literal], idx: &mut usize) {
277 for a in assignments {
278 substitute_expr(&mut a.value, literals, idx);
279 }
280}
281
282pub(crate) fn count_literal_slots(plan: &PlanNode) -> usize {
288 let mut n = 0usize;
289 count_plan(plan, &mut n);
290 n
291}
292
293fn count_plan(plan: &PlanNode, n: &mut usize) {
294 match plan {
295 PlanNode::SeqScan { .. } => {}
296 PlanNode::AliasScan { .. } => {}
297 PlanNode::IndexScan { key, .. } => count_expr(key, n),
298 PlanNode::RangeScan { start, end, .. } => {
299 if let Some((expr, _)) = start {
300 count_expr(expr, n);
301 }
302 if let Some((expr, _)) = end {
303 count_expr(expr, n);
304 }
305 }
306 PlanNode::Filter { input, predicate } => {
307 count_plan(input, n);
308 count_expr(predicate, n);
309 }
310 PlanNode::Project { input, fields } => {
311 count_plan(input, n);
312 for f in fields {
313 count_expr(&f.expr, n);
314 }
315 }
316 PlanNode::Sort { input, .. } => count_plan(input, n),
317 PlanNode::Limit { input, count } => {
318 if let PlanNode::Offset {
323 input: inner,
324 count: off_count,
325 } = input.as_ref()
326 {
327 count_plan(inner, n);
328 count_expr(count, n);
329 count_expr(off_count, n);
330 } else {
331 count_plan(input, n);
332 count_expr(count, n);
333 }
334 }
335 PlanNode::Offset { input, count } => {
336 count_plan(input, n);
337 count_expr(count, n);
338 }
339 PlanNode::Aggregate { input, .. } => count_plan(input, n),
340 PlanNode::NestedLoopJoin {
341 left, right, on, ..
342 } => {
343 count_plan(left, n);
344 count_plan(right, n);
345 if let Some(pred) = on {
346 count_expr(pred, n);
347 }
348 }
349 PlanNode::Distinct { input } => count_plan(input, n),
350 PlanNode::GroupBy { input, having, .. } => {
351 count_plan(input, n);
352 if let Some(pred) = having {
353 count_expr(pred, n);
354 }
355 }
356 PlanNode::Insert { rows, .. } => {
357 for assignments in rows {
358 for a in assignments {
359 count_expr(&a.value, n);
360 }
361 }
362 }
363 PlanNode::Upsert {
364 assignments,
365 on_conflict,
366 ..
367 } => {
368 for a in assignments {
369 count_expr(&a.value, n);
370 }
371 for a in on_conflict {
372 count_expr(&a.value, n);
373 }
374 }
375 PlanNode::Update {
376 input, assignments, ..
377 } => {
378 count_plan(input, n);
379 for a in assignments {
380 count_expr(&a.value, n);
381 }
382 }
383 PlanNode::Delete { input, .. } => count_plan(input, n),
384 PlanNode::CreateTable { .. } => {}
385 PlanNode::AlterTable { .. } => {}
386 PlanNode::DropTable { .. } => {}
387 PlanNode::CreateView { .. } => {}
388 PlanNode::RefreshView { .. } => {}
389 PlanNode::DropView { .. } => {}
390 PlanNode::Window { input, windows } => {
391 count_plan(input, n);
392 for w in windows {
393 for arg in &w.args {
394 count_expr(arg, n);
395 }
396 }
397 }
398 PlanNode::Union { left, right, .. } => {
399 count_plan(left, n);
400 count_plan(right, n);
401 }
402 PlanNode::Explain { input } => {
403 count_plan(input, n);
404 }
405 PlanNode::ListTypes | PlanNode::Describe { .. } => {}
406 PlanNode::Begin | PlanNode::Commit | PlanNode::Rollback => {}
407 }
408}
409
410fn count_expr(expr: &Expr, n: &mut usize) {
411 match expr {
412 Expr::Literal(_) => *n += 1,
413 Expr::Field(_) | Expr::QualifiedField { .. } | Expr::Param(_) => {}
414 Expr::BinaryOp(l, _, r) => {
415 count_expr(l, n);
416 count_expr(r, n);
417 }
418 Expr::UnaryOp(_, inner) => count_expr(inner, n),
419 Expr::FunctionCall(_, inner) => count_expr(inner, n),
420 Expr::Coalesce(l, r) => {
421 count_expr(l, n);
422 count_expr(r, n);
423 }
424 Expr::InList { expr, list, .. } => {
425 count_expr(expr, n);
426 for item in list {
427 count_expr(item, n);
428 }
429 }
430 Expr::ScalarFunc(_, args) => {
431 for a in args {
432 count_expr(a, n);
433 }
434 }
435 Expr::Cast(inner, _) => count_expr(inner, n),
436 Expr::Case { whens, else_expr } => {
437 for (cond, result) in whens {
438 count_expr(cond, n);
439 count_expr(result, n);
440 }
441 if let Some(e) = else_expr {
442 count_expr(e, n);
443 }
444 }
445 Expr::InSubquery { expr, .. } => {
446 count_expr(expr, n);
447 }
450 Expr::ExistsSubquery { .. } => {
451 }
454 Expr::Window { args, .. } => {
455 for a in args {
456 count_expr(a, n);
457 }
458 }
459 Expr::ValueLit(_) => {}
462 Expr::Null => {}
463 }
464}
465
466fn substitute_expr(expr: &mut Expr, literals: &[Literal], idx: &mut usize) {
467 match expr {
468 Expr::Literal(_) => {
469 *expr = Expr::Literal(literals[*idx].clone());
473 *idx += 1;
474 }
475 Expr::Field(_) | Expr::QualifiedField { .. } | Expr::Param(_) => {}
476 Expr::BinaryOp(l, _, r) => {
477 substitute_expr(l, literals, idx);
478 substitute_expr(r, literals, idx);
479 }
480 Expr::UnaryOp(_, inner) => {
481 substitute_expr(inner, literals, idx);
482 }
483 Expr::FunctionCall(_, inner) => {
484 substitute_expr(inner, literals, idx);
485 }
486 Expr::Coalesce(l, r) => {
487 substitute_expr(l, literals, idx);
488 substitute_expr(r, literals, idx);
489 }
490 Expr::InList { expr, list, .. } => {
491 substitute_expr(expr, literals, idx);
492 for item in list {
493 substitute_expr(item, literals, idx);
494 }
495 }
496 Expr::ScalarFunc(_, args) => {
497 for a in args {
498 substitute_expr(a, literals, idx);
499 }
500 }
501 Expr::Cast(inner, _) => substitute_expr(inner, literals, idx),
502 Expr::Case { whens, else_expr } => {
503 for (cond, result) in whens {
504 substitute_expr(cond, literals, idx);
505 substitute_expr(result, literals, idx);
506 }
507 if let Some(e) = else_expr {
508 substitute_expr(e, literals, idx);
509 }
510 }
511 Expr::InSubquery { expr, .. } => {
512 substitute_expr(expr, literals, idx);
513 }
514 Expr::ExistsSubquery { .. } => {
515 }
518 Expr::Window { args, .. } => {
519 for a in args {
520 substitute_expr(a, literals, idx);
521 }
522 }
523 Expr::ValueLit(_) => {}
526 Expr::Null => {}
527 }
528}
529
530#[cfg(test)]
531mod tests {
532 use super::*;
533 use crate::canonicalize::canonicalize;
534 use crate::planner;
535
536 #[test]
537 fn test_cache_hit_substitutes_literal() {
538 let mut cache = PlanCache::new(100);
539
540 let q1 = "User filter .id = 42";
542 let (h1, lits1) = canonicalize(q1).unwrap();
543 let p1 = planner::plan(q1).unwrap();
544 cache.insert(h1, p1, lits1.len());
545
546 let q2 = "User filter .id = 99";
549 let (h2, lits2) = canonicalize(q2).unwrap();
550 assert_eq!(h1, h2, "different literals must hash the same");
551
552 let plan = cache.get_with_substitution(h2, &lits2).expect("hit");
553
554 match plan {
556 PlanNode::IndexScan { key, .. } => {
557 assert_eq!(key, Expr::Literal(Literal::Int(99)));
558 }
559 other => panic!("expected IndexScan, got {other:?}"),
560 }
561
562 assert_eq!(lits1, vec![Literal::Int(42)]);
565 assert_eq!(cache.hits, 1);
566 assert_eq!(cache.misses, 0);
567 }
568
569 #[test]
570 fn test_subquery_plan_not_cached() {
571 let mut cache = PlanCache::new(100);
578 let q = "User filter .id in (Ord filter .total > 100 { .user_id })";
579 let (h, lits) = canonicalize(q).unwrap();
580 assert_eq!(lits.len(), 1, "canonicalize collects the inner literal");
581 let plan = planner::plan(q).unwrap();
582 assert_eq!(
583 count_literal_slots(&plan),
584 0,
585 "the subquery literal is not a reachable substitution slot"
586 );
587 cache.insert(h, plan, lits.len());
588 assert!(cache.is_empty(), "subquery plans must not be cached (#137)");
589 assert!(cache.get_with_substitution(h, &lits).is_none());
590 }
591
592 #[test]
593 fn test_cache_miss_returns_none_and_bumps_counter() {
594 let mut cache = PlanCache::new(100);
595 assert!(cache.get_with_substitution(99999, &[]).is_none());
596 assert_eq!(cache.misses, 1);
597 assert_eq!(cache.hits, 0);
598 }
599
600 #[test]
601 fn test_multi_literal_filter_substitution() {
602 let mut cache = PlanCache::new(100);
603 let q1 = r#"User filter .age > 30 and .status = "active" { .name }"#;
604 let (h1, lits1) = canonicalize(q1).unwrap();
605 cache.insert(h1, planner::plan(q1).unwrap(), lits1.len());
606
607 let q2 = r#"User filter .age > 50 and .status = "pending" { .name }"#;
608 let (h2, lits2) = canonicalize(q2).unwrap();
609 let plan = cache.get_with_substitution(h2, &lits2).expect("hit");
610
611 let mut found = Vec::new();
613 collect_literals_for_test(&plan, &mut found);
614 assert_eq!(
615 found,
616 vec![Literal::Int(50), Literal::String("pending".into()),]
617 );
618 }
619
620 #[test]
621 fn test_grouped_join_query_shares_plan_across_literals() {
622 let mut cache = PlanCache::new(100);
628 let q1 = "User as u join Order as o on u.id = o.user_id \
629 group u.status having count(o.total) > 1 { u.status, n: count(o.total) }";
630 let (h1, lits1) = canonicalize(q1).unwrap();
631 let p1 = planner::plan(q1).unwrap();
632
633 assert_eq!(lits1.len(), 1, "only the HAVING literal is collected");
637 assert_eq!(
638 count_literal_slots(&p1),
639 lits1.len(),
640 "group keys/args are structural, so slots == literals (#137)"
641 );
642 cache.insert(h1, p1, lits1.len());
643 assert_eq!(cache.len(), 1, "grouped-join plan must cache");
644
645 let q2 = "User as u join Order as o on u.id = o.user_id \
646 group u.status having count(o.total) > 5 { u.status, n: count(o.total) }";
647 let (h2, lits2) = canonicalize(q2).unwrap();
648 assert_eq!(h1, h2, "different HAVING literal must hash the same");
649
650 let plan = cache.get_with_substitution(h2, &lits2).expect("hit");
651 let mut found = Vec::new();
652 collect_literals_for_test(&plan, &mut found);
653 assert_eq!(
654 found,
655 vec![Literal::Int(5)],
656 "new HAVING literal substituted"
657 );
658 assert_eq!(cache.hits, 1);
659 }
660
661 #[test]
662 fn test_update_by_pk_substitution() {
663 let mut cache = PlanCache::new(100);
664 let q1 = "User filter .id = 1 update { age := 100 }";
665 let (h1, lits1) = canonicalize(q1).unwrap();
666 cache.insert(h1, planner::plan(q1).unwrap(), lits1.len());
667
668 let q2 = "User filter .id = 7 update { age := 200 }";
669 let (h2, lits2) = canonicalize(q2).unwrap();
670 let plan = cache.get_with_substitution(h2, &lits2).expect("hit");
671
672 let mut found = Vec::new();
673 collect_literals_for_test(&plan, &mut found);
674 assert_eq!(found, vec![Literal::Int(7), Literal::Int(200)]);
675 }
676
677 #[test]
678 fn test_insert_substitution() {
679 let mut cache = PlanCache::new(100);
680 let q1 = r#"insert User { id := 1, name := "Alice", age := 20 }"#;
681 let (h1, lits1) = canonicalize(q1).unwrap();
682 cache.insert(h1, planner::plan(q1).unwrap(), lits1.len());
683
684 let q2 = r#"insert User { id := 2, name := "Bob", age := 30 }"#;
685 let (h2, lits2) = canonicalize(q2).unwrap();
686 let plan = cache.get_with_substitution(h2, &lits2).expect("hit");
687
688 let mut found = Vec::new();
689 collect_literals_for_test(&plan, &mut found);
690 assert_eq!(
691 found,
692 vec![
693 Literal::Int(2),
694 Literal::String("Bob".into()),
695 Literal::Int(30),
696 ]
697 );
698 }
699
700 #[test]
704 fn test_insert_uuid_sugar_cacheable_and_substitutes() {
705 let mut cache = PlanCache::new(100);
706 let q1 = r#"insert User { id := uuid("00000000-0000-0000-0000-000000000001") }"#;
707 let (h1, lits1) = canonicalize(q1).unwrap();
708 assert_eq!(lits1.len(), 1, "the inner string is the only literal");
709 let plan = planner::plan(q1).unwrap();
710 assert_eq!(
711 count_literal_slots(&plan),
712 1,
713 "Cast wrapping a Literal is a reachable substitution slot"
714 );
715 cache.insert(h1, plan, lits1.len());
716 assert!(!cache.is_empty(), "uuid() insert must be cacheable");
717
718 let q2 = r#"insert User { id := uuid("00000000-0000-0000-0000-000000000002") }"#;
719 let (h2, lits2) = canonicalize(q2).unwrap();
720 assert_eq!(h1, h2, "same shape hashes identically");
721 let subst = cache.get_with_substitution(h2, &lits2).expect("hit");
722
723 let mut found = Vec::new();
724 collect_literals_for_test(&subst, &mut found);
725 assert_eq!(
726 found,
727 vec![Literal::String(
728 "00000000-0000-0000-0000-000000000002".into()
729 )],
730 "the second call's uuid must be substituted in, not the cached one"
731 );
732 }
733
734 #[test]
739 fn test_two_arg_cast_uuid_not_cached() {
740 let mut cache = PlanCache::new(100);
741 let q = r#"User filter .id = cast(.other, "uuid")"#;
742 let (h, lits) = canonicalize(q).unwrap();
743 assert_eq!(
744 lits.len(),
745 1,
746 "canonicalize collects the cast-target string"
747 );
748 let plan = planner::plan(q).unwrap();
749 assert_eq!(
750 count_literal_slots(&plan),
751 0,
752 "the cast target is baked into the AST, not a slot"
753 );
754 cache.insert(h, plan, lits.len());
755 assert!(cache.is_empty(), "cast(x, \"uuid\") must not be cached");
756 }
757
758 #[test]
759 fn test_eviction_on_capacity() {
760 let mut cache = PlanCache::new(2);
761 let q1 = "User";
762 let q2 = "User filter .age > 1";
763 let _q3 = "User filter .age > 2";
764 let q3_distinct = "User filter .id = 5";
767
768 let (h1, lits1) = canonicalize(q1).unwrap();
769 let (h2, lits2) = canonicalize(q2).unwrap();
770 let (h3, lits3) = canonicalize(q3_distinct).unwrap();
771 cache.insert(h1, planner::plan(q1).unwrap(), lits1.len());
772 cache.insert(h2, planner::plan(q2).unwrap(), lits2.len());
773 cache.insert(h3, planner::plan(q3_distinct).unwrap(), lits3.len());
775 assert!(cache.cache.contains_key(&h3));
776 assert_eq!(cache.cache.len(), 1);
777 }
778
779 fn collect_literals_for_test(plan: &PlanNode, out: &mut Vec<Literal>) {
783 match plan {
784 PlanNode::SeqScan { .. } => {}
785 PlanNode::AliasScan { .. } => {}
786 PlanNode::IndexScan { key, .. } => collect_expr_literals(key, out),
787 PlanNode::RangeScan { start, end, .. } => {
788 if let Some((expr, _)) = start {
789 collect_expr_literals(expr, out);
790 }
791 if let Some((expr, _)) = end {
792 collect_expr_literals(expr, out);
793 }
794 }
795 PlanNode::Filter { input, predicate } => {
796 collect_literals_for_test(input, out);
797 collect_expr_literals(predicate, out);
798 }
799 PlanNode::Project { input, fields } => {
800 collect_literals_for_test(input, out);
801 for f in fields {
802 collect_expr_literals(&f.expr, out);
803 }
804 }
805 PlanNode::Sort { input, .. } => collect_literals_for_test(input, out),
806 PlanNode::Limit { input, count } => {
807 collect_literals_for_test(input, out);
808 collect_expr_literals(count, out);
809 }
810 PlanNode::Offset { input, count } => {
811 collect_literals_for_test(input, out);
812 collect_expr_literals(count, out);
813 }
814 PlanNode::Aggregate { input, .. } => collect_literals_for_test(input, out),
815 PlanNode::NestedLoopJoin {
816 left, right, on, ..
817 } => {
818 collect_literals_for_test(left, out);
819 collect_literals_for_test(right, out);
820 if let Some(pred) = on {
821 collect_expr_literals(pred, out);
822 }
823 }
824 PlanNode::Insert { rows, .. } => {
825 for assignments in rows {
826 for a in assignments {
827 collect_expr_literals(&a.value, out);
828 }
829 }
830 }
831 PlanNode::Upsert {
832 assignments,
833 on_conflict,
834 ..
835 } => {
836 for a in assignments {
837 collect_expr_literals(&a.value, out);
838 }
839 for a in on_conflict {
840 collect_expr_literals(&a.value, out);
841 }
842 }
843 PlanNode::Update {
844 input, assignments, ..
845 } => {
846 collect_literals_for_test(input, out);
847 for a in assignments {
848 collect_expr_literals(&a.value, out);
849 }
850 }
851 PlanNode::Distinct { input } => collect_literals_for_test(input, out),
852 PlanNode::GroupBy { input, having, .. } => {
853 collect_literals_for_test(input, out);
854 if let Some(pred) = having {
855 collect_expr_literals(pred, out);
856 }
857 }
858 PlanNode::Delete { input, .. } => collect_literals_for_test(input, out),
859 PlanNode::CreateTable { .. } => {}
860 PlanNode::AlterTable { .. } => {}
861 PlanNode::DropTable { .. } => {}
862 PlanNode::CreateView { .. } => {}
863 PlanNode::RefreshView { .. } => {}
864 PlanNode::DropView { .. } => {}
865 PlanNode::Window { input, windows } => {
866 collect_literals_for_test(input, out);
867 for w in windows {
868 for arg in &w.args {
869 collect_expr_literals(arg, out);
870 }
871 }
872 }
873 PlanNode::Union { left, right, .. } => {
874 collect_literals_for_test(left, out);
875 collect_literals_for_test(right, out);
876 }
877 PlanNode::Explain { input } => {
878 collect_literals_for_test(input, out);
879 }
880 PlanNode::ListTypes | PlanNode::Describe { .. } => {}
881 PlanNode::Begin | PlanNode::Commit | PlanNode::Rollback => {}
882 }
883 }
884
885 fn collect_expr_literals(expr: &Expr, out: &mut Vec<Literal>) {
886 match expr {
887 Expr::Literal(l) => out.push(l.clone()),
888 Expr::Field(_) | Expr::QualifiedField { .. } | Expr::Param(_) => {}
889 Expr::BinaryOp(l, _, r) => {
890 collect_expr_literals(l, out);
891 collect_expr_literals(r, out);
892 }
893 Expr::UnaryOp(_, inner) => collect_expr_literals(inner, out),
894 Expr::FunctionCall(_, inner) => collect_expr_literals(inner, out),
895 Expr::Coalesce(l, r) => {
896 collect_expr_literals(l, out);
897 collect_expr_literals(r, out);
898 }
899 Expr::InList { expr, list, .. } => {
900 collect_expr_literals(expr, out);
901 for item in list {
902 collect_expr_literals(item, out);
903 }
904 }
905 Expr::ScalarFunc(_, args) => {
906 for a in args {
907 collect_expr_literals(a, out);
908 }
909 }
910 Expr::Cast(inner, _) => collect_expr_literals(inner, out),
911 Expr::Case { whens, else_expr } => {
912 for (cond, result) in whens {
913 collect_expr_literals(cond, out);
914 collect_expr_literals(result, out);
915 }
916 if let Some(e) = else_expr {
917 collect_expr_literals(e, out);
918 }
919 }
920 Expr::InSubquery { expr, .. } => {
921 collect_expr_literals(expr, out);
922 }
923 Expr::ExistsSubquery { .. } => {}
924 Expr::Window { args, .. } => {
925 for a in args {
926 collect_expr_literals(a, out);
927 }
928 }
929 Expr::ValueLit(_) => {}
930 Expr::Null => {}
931 }
932 }
933}