1use alloc::string::String;
14use alloc::vec::Vec;
15
16use spg_sql::ast::{
17 BinOp, ColumnName, Cte, Expr, FromJoin, JoinKind, LimitExpr, Literal, SelectItem,
18 SelectStatement, TableRef, UnOp,
19};
20
21pub static PULLUP_LIMIT1_FIRE_COUNT: core::sync::atomic::AtomicU64 =
26 core::sync::atomic::AtomicU64::new(0);
27
28pub static BATCHED_SCALAR_KEYED_FIRE_COUNT: core::sync::atomic::AtomicU64 =
33 core::sync::atomic::AtomicU64::new(0);
34pub static BATCHED_SCALAR_KEYED_PROBE_COUNT: core::sync::atomic::AtomicU64 =
35 core::sync::atomic::AtomicU64::new(0);
36pub static BATCHED_SCALAR_FALL_THROUGH_COUNT: core::sync::atomic::AtomicU64 =
37 core::sync::atomic::AtomicU64::new(0);
38
39pub static EXISTS_PULLUP_FIRE_COUNT: core::sync::atomic::AtomicU64 =
48 core::sync::atomic::AtomicU64::new(0);
49pub static EXISTS_BATCH_FIRE_COUNT: core::sync::atomic::AtomicU64 =
50 core::sync::atomic::AtomicU64::new(0);
51pub static EXISTS_BATCH_FALL_THROUGH_COUNT: core::sync::atomic::AtomicU64 =
52 core::sync::atomic::AtomicU64::new(0);
53
54pub static EXISTS_PULLUP_MULTICOL_DISABLE: core::sync::atomic::AtomicBool =
61 core::sync::atomic::AtomicBool::new(false);
62
63use spg_storage::{Row, Value};
64
65use crate::eval::{self, EvalContext};
66use crate::substitute::value_to_literal_expr;
67use crate::{
68 CancelToken, Engine, EngineError, QueryResult, aggregate, memoize, order_by_value_cmp, reorder,
69 value_cmp, visit_expr_columns_and_subqueries,
70};
71
72impl Engine {
73 pub(crate) fn eval_expr_with_correlated(
81 &self,
82 expr: &Expr,
83 row: &Row<'static>,
84 ctx: &EvalContext<'_>,
85 cancel: CancelToken<'_>,
86 mut memo: Option<&mut memoize::MemoizeCache>,
87 ) -> Result<Value<'static>, EngineError> {
88 let has_subq = if let Some(m) = memo.as_deref_mut() {
93 let key = core::ptr::from_ref::<Expr>(expr) as usize;
94 match m.has_subquery.get(&key) {
95 Some(b) => *b,
96 None => {
97 let b = expr_has_subquery(expr);
98 m.has_subquery.insert(key, b);
99 b
100 }
101 }
102 } else {
103 expr_has_subquery(expr)
104 };
105 if !has_subq {
106 if let Some(m) = memo.as_deref_mut()
111 && expr_may_use_in_set(expr)
112 {
113 return eval_with_in_sets(expr, row, ctx, m);
114 }
115 return eval::eval_expr(expr, row, ctx).map_err(EngineError::Eval);
116 }
117 if let Some(m) = memo.as_deref_mut() {
123 let key = core::ptr::from_ref::<Expr>(expr) as usize;
124 let plan_hit = m.expr_plans.contains_key(&key);
129 let exists_plan_hit = m.exists_plans.contains_key(&key);
130 let mut subs: Vec<&SelectStatement> = Vec::new();
131 let mut exists_subs: Vec<&SelectStatement> = Vec::new();
132 if !plan_hit {
133 collect_scalar_subqueries(expr, &mut subs);
134 }
135 if !exists_plan_hit {
136 collect_exists_subqueries(expr, &mut exists_subs);
137 }
138 if !plan_hit && !subs.is_empty() {
139 let mut plan: Vec<Option<alloc::rc::Rc<memoize::GroupMap>>> =
140 Vec::with_capacity(subs.len());
141 for sub in &subs {
142 let repr = alloc::format!("{sub}");
143 if !m.group_maps.contains_key(&repr) {
144 let built = self
145 .try_batch_correlated_scalar(sub, None, cancel)?
146 .map(alloc::rc::Rc::new);
147 m.group_maps.insert(repr.clone(), built);
148 }
149 plan.push(m.group_maps.get(&repr).cloned().flatten());
150 }
151 let mut template = expr.clone();
152 hollow_scalar_subqueries(&mut template);
153 m.expr_plans.insert(key, (subs.len(), plan, template));
154 }
155 if !exists_plan_hit && !exists_subs.is_empty() {
161 let mut eplan: Vec<Option<alloc::rc::Rc<memoize::ExistsSet>>> =
162 Vec::with_capacity(exists_subs.len());
163 for sub in &exists_subs {
164 let built = self
165 .try_batch_correlated_exists(sub, cancel)?
166 .map(alloc::rc::Rc::new);
167 if built.is_some() {
168 EXISTS_BATCH_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
169 } else {
170 EXISTS_BATCH_FALL_THROUGH_COUNT
171 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
172 }
173 eplan.push(built);
174 }
175 m.exists_plans.insert(key, eplan);
176 }
177 let scalar_ready = m
182 .expr_plans
183 .get(&key)
184 .map(|(_, plan, _)| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
185 .unwrap_or(false);
186 let exists_ready = m
187 .exists_plans
188 .get(&key)
189 .map(|plan| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
190 .unwrap_or(false);
191 if scalar_ready || exists_ready {
192 let scalar_plan = m
200 .expr_plans
201 .get(&key)
202 .map(|(_, plan, template)| (plan.clone(), template.clone()));
203 let exists_plan = m.exists_plans.get(&key).cloned();
204 let mut e = match &scalar_plan {
205 Some((_, template)) => template.clone(),
206 None => expr.clone(),
207 };
208 let mut all_ok = true;
209 if let Some((plan, _)) = &scalar_plan {
210 let mut idx = 0usize;
211 all_ok &= splice_planned_subqueries(&mut e, plan, &mut idx, row, ctx)?;
212 }
213 if all_ok && let Some(plan) = &exists_plan {
214 let mut idx = 0usize;
215 all_ok &= splice_planned_exists(&mut e, plan, &mut idx, row, ctx)?;
216 }
217 if all_ok {
218 if expr_has_subquery(&e) {
219 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
220 }
221 return eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval);
222 }
223 }
224 }
225 let mut e = expr.clone();
226 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
227 eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval)
228 }
229
230 fn resolve_correlated_in_expr(
231 &self,
232 e: &mut Expr,
233 row: &Row<'static>,
234 ctx: &EvalContext<'_>,
235 cancel: CancelToken<'_>,
236 mut memo: Option<&mut memoize::MemoizeCache>,
237 ) -> Result<(), EngineError> {
238 match e {
239 Expr::AggregateOrdered { call, order_by, .. } => {
240 self.resolve_correlated_in_expr(call, row, ctx, cancel, memo.as_deref_mut())?;
241 for o in order_by.iter_mut() {
242 self.resolve_correlated_in_expr(
243 &mut o.expr,
244 row,
245 ctx,
246 cancel,
247 memo.as_deref_mut(),
248 )?;
249 }
250 }
251 Expr::ScalarSubquery(inner) => {
252 if memo.is_some() {
265 let ptr_key = core::ptr::from_ref::<SelectStatement>(&**inner) as usize;
266 let entry_known = memo
267 .as_ref()
268 .is_some_and(|m| m.group_maps_by_ptr.contains_key(&ptr_key));
269 if !entry_known {
270 let built = self
271 .try_batch_correlated_scalar(inner, None, cancel)?
272 .map(alloc::rc::Rc::new);
273 if let Some(m) = memo.as_deref_mut() {
274 m.group_maps_by_ptr.insert(ptr_key, built);
275 }
276 }
277 if let Some(m) = memo.as_deref_mut()
278 && let Some(Some(gm)) = m.group_maps_by_ptr.get(&ptr_key)
279 {
280 let (outer_col, map, empty_default) = gm.as_ref();
281 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
282 .map_err(EngineError::Eval)?;
283 let v = if matches!(key_v, Value::Null) {
290 Value::Null
291 } else {
292 map.get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
293 .cloned()
294 .unwrap_or_else(|| empty_default.clone())
295 };
296 *e = value_to_literal_expr(v)?;
297 return Ok(());
298 }
299 }
300 let cache_key = memo.as_ref().map(|_| memoize::CacheKey {
305 subquery_repr: alloc::format!("{}", **inner),
306 outer_values: row.values.iter().cloned().map(Value::into_owned).collect(),
307 });
308 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key.as_ref())
309 && let Some(cached) = cache.get(k)
310 {
311 *e = value_to_literal_expr(cached)?;
312 return Ok(());
313 }
314 if let Some(v) = self.try_scalar_count_pk_eq_probe(inner, row, ctx)? {
326 *e = value_to_literal_expr(v)?;
327 return Ok(());
328 }
329 let mut s = (**inner).clone();
330 substitute_outer_columns(&mut s, row, ctx);
331 let r = self.exec_select_cancel(&s, cancel)?;
332 let QueryResult::Rows { rows, .. } = r else {
333 return Err(EngineError::Unsupported(
334 "scalar subquery: inner did not return rows".into(),
335 ));
336 };
337 let value = match rows.as_slice() {
338 [] => Value::Null,
339 [r0] => r0.values.first().cloned().unwrap_or(Value::Null),
340 _ => {
341 return Err(EngineError::Unsupported(alloc::format!(
342 "scalar subquery returned {} rows; expected 0 or 1",
343 rows.len()
344 )));
345 }
346 };
347 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key) {
348 cache.insert(k, value.clone());
349 }
350 *e = value_to_literal_expr(value)?;
351 }
352 Expr::Exists { subquery, negated } => {
353 if memo.is_some() {
360 let repr = alloc::format!("{}", **subquery);
361 let known = memo
362 .as_ref()
363 .is_some_and(|m| m.exists_sets.contains_key(&repr));
364 if !known {
365 let built = self
366 .try_batch_correlated_exists(subquery, cancel)?
367 .map(alloc::rc::Rc::new);
368 if let Some(m) = memo.as_deref_mut() {
369 m.exists_sets.insert(repr.clone(), built);
370 }
371 }
372 if let Some(m) = memo.as_deref_mut()
373 && let Some(Some(es)) = m.exists_sets.get(&repr)
374 {
375 let (outer_cols, set) = es.as_ref();
376 let mut key_vals: Vec<Value<'static>> =
377 Vec::with_capacity(outer_cols.len());
378 let mut any_null = false;
379 for oc in outer_cols {
380 let v = eval::eval_expr(&Expr::Column(oc.clone()), row, ctx)
381 .map_err(EngineError::Eval)?;
382 if matches!(v, Value::Null) {
383 any_null = true;
384 }
385 key_vals.push(v);
386 }
387 let present = !any_null && set.contains(&aggregate::encode_key(&key_vals));
389 let bit = if *negated { !present } else { present };
390 *e = Expr::Literal(Literal::Bool(bit));
391 return Ok(());
392 }
393 }
394 let mut s = (**subquery).clone();
395 substitute_outer_columns(&mut s, row, ctx);
396 let r = self.exec_select_cancel(&s, cancel)?;
397 let exists = matches!(r, QueryResult::Rows { rows, .. } if !rows.is_empty());
398 let bit = if *negated { !exists } else { exists };
399 *e = Expr::Literal(Literal::Bool(bit));
400 }
401 Expr::InSubquery {
402 expr: lhs,
403 subquery,
404 negated,
405 } => {
406 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
407 let lhs_val = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
408 let mut s = (**subquery).clone();
409 substitute_outer_columns(&mut s, row, ctx);
410 let r = self.exec_select_cancel(&s, cancel)?;
411 let QueryResult::Rows { columns, rows, .. } = r else {
412 return Err(EngineError::Unsupported(
413 "IN-subquery: inner did not return rows".into(),
414 ));
415 };
416 if columns.len() != 1 {
417 return Err(EngineError::Unsupported(alloc::format!(
418 "IN-subquery must project exactly one column; got {}",
419 columns.len()
420 )));
421 }
422 let mut found = false;
423 let mut any_null = false;
424 for r0 in rows {
425 let v = r0.values.into_iter().next().unwrap_or(Value::Null);
426 if v.is_null() {
427 any_null = true;
428 continue;
429 }
430 if value_cmp(&v, &lhs_val) == core::cmp::Ordering::Equal {
431 found = true;
432 break;
433 }
434 }
435 let bit = if found {
436 !*negated
437 } else if any_null {
438 return Err(EngineError::Unsupported(
439 "IN-subquery with NULL in result and no match: NULL semantics not yet implemented".into(),
440 ));
441 } else {
442 *negated
443 };
444 *e = Expr::Literal(Literal::Bool(bit));
445 }
446 Expr::Binary { lhs, rhs, .. } => {
447 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
448 self.resolve_correlated_in_expr(rhs, row, ctx, cancel, memo.as_deref_mut())?;
449 }
450 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
451 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
452 }
453 Expr::Like { expr, pattern, .. } => {
454 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
455 self.resolve_correlated_in_expr(pattern, row, ctx, cancel, memo.as_deref_mut())?;
456 }
457 Expr::FunctionCall { args, .. } => {
458 for a in args {
459 self.resolve_correlated_in_expr(a, row, ctx, cancel, memo.as_deref_mut())?;
460 }
461 }
462 Expr::Extract { source, .. } => {
463 self.resolve_correlated_in_expr(source, row, ctx, cancel, memo.as_deref_mut())?;
464 }
465 Expr::WindowFunction { .. }
466 | Expr::Literal(_)
467 | Expr::Placeholder(_)
468 | Expr::Column(_) => {}
469 Expr::Array(items) => {
471 for elem in items {
472 self.resolve_correlated_in_expr(elem, row, ctx, cancel, memo.as_deref_mut())?;
473 }
474 }
475 Expr::ArraySubscript { target, index } => {
476 self.resolve_correlated_in_expr(target, row, ctx, cancel, memo.as_deref_mut())?;
477 self.resolve_correlated_in_expr(index, row, ctx, cancel, memo.as_deref_mut())?;
478 }
479 Expr::AnyAll { expr, array, .. } => {
480 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
481 self.resolve_correlated_in_expr(array, row, ctx, cancel, memo.as_deref_mut())?;
482 }
483 Expr::InList { expr, list, .. } => {
484 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
485 for item in list {
486 self.resolve_correlated_in_expr(item, row, ctx, cancel, memo.as_deref_mut())?;
487 }
488 }
489 Expr::Case {
490 operand,
491 branches,
492 else_branch,
493 } => {
494 if let Some(o) = operand {
495 self.resolve_correlated_in_expr(o, row, ctx, cancel, memo.as_deref_mut())?;
496 }
497 for (w, t) in branches {
498 self.resolve_correlated_in_expr(w, row, ctx, cancel, memo.as_deref_mut())?;
499 self.resolve_correlated_in_expr(t, row, ctx, cancel, memo.as_deref_mut())?;
500 }
501 if let Some(e) = else_branch {
502 self.resolve_correlated_in_expr(e, row, ctx, cancel, memo.as_deref_mut())?;
503 }
504 }
505 }
506 Ok(())
507 }
508
509 pub(crate) fn subquery_replacement(
519 &self,
520 e: &Expr,
521 cancel: CancelToken<'_>,
522 ) -> Result<Option<Expr>, EngineError> {
523 match e {
524 Expr::ScalarSubquery(inner) => {
525 if select_is_correlated(inner) {
530 return Ok(None);
531 }
532 let mut s = (**inner).clone();
533 self.resolve_select_subqueries(&mut s, cancel)?;
536 let r = match self.exec_bare_select_cancel(&s, cancel) {
537 Ok(r) => r,
538 Err(e) if is_correlation_error(&e) => return Ok(None),
539 Err(e) => return Err(e),
540 };
541 let QueryResult::Rows { rows, .. } = r else {
542 return Err(EngineError::Unsupported(
543 "scalar subquery: inner statement did not return rows".into(),
544 ));
545 };
546 let value = match rows.as_slice() {
547 [] => Value::Null,
548 [row] => row.values.first().cloned().unwrap_or(Value::Null),
549 _ => {
550 return Err(EngineError::Unsupported(alloc::format!(
551 "scalar subquery returned {} rows; expected 0 or 1",
552 rows.len()
553 )));
554 }
555 };
556 Ok(Some(value_to_literal_expr(value)?))
557 }
558 Expr::Exists { subquery, negated } => {
559 if select_is_correlated(subquery) {
560 return Ok(None);
561 }
562 let mut s = (**subquery).clone();
563 self.resolve_select_subqueries(&mut s, cancel)?;
564 let r = match self.exec_bare_select_cancel(&s, cancel) {
565 Ok(r) => r,
566 Err(e) if is_correlation_error(&e) => return Ok(None),
567 Err(e) => return Err(e),
568 };
569 let exists = match r {
570 QueryResult::Rows { rows, .. } => !rows.is_empty(),
571 QueryResult::CommandOk { .. } => false,
572 };
573 let bit = if *negated { !exists } else { exists };
574 Ok(Some(Expr::Literal(Literal::Bool(bit))))
575 }
576 Expr::InSubquery {
577 expr,
578 subquery,
579 negated,
580 } => {
581 if select_is_correlated(subquery) {
582 return Ok(None);
583 }
584 let mut s = (**subquery).clone();
585 self.resolve_select_subqueries(&mut s, cancel)?;
586 let r = match self.exec_bare_select_cancel(&s, cancel) {
587 Ok(r) => r,
588 Err(e) if is_correlation_error(&e) => return Ok(None),
589 Err(e) => return Err(e),
590 };
591 let QueryResult::Rows { columns, rows, .. } = r else {
592 return Err(EngineError::Unsupported(
593 "IN-subquery: inner statement did not return rows".into(),
594 ));
595 };
596 if columns.len() != 1 {
597 return Err(EngineError::Unsupported(alloc::format!(
598 "IN-subquery must project exactly one column; got {}",
599 columns.len()
600 )));
601 }
602 let mut list: Vec<Expr> = Vec::with_capacity(rows.len());
608 for row in rows {
609 let v = row.values.into_iter().next().unwrap_or(Value::Null);
610 list.push(value_to_literal_expr(v)?);
611 }
612 Ok(Some(Expr::InList {
613 expr: expr.clone(),
614 list,
615 negated: *negated,
616 }))
617 }
618 _ => Ok(None),
619 }
620 }
621}
622
623impl Engine {
624 pub(crate) fn try_batch_correlated_scalar(
633 &self,
634 inner: &SelectStatement,
635 restrict: Option<(&[Row<'static>], &EvalContext<'_>)>,
636 cancel: CancelToken<'_>,
637 ) -> Result<Option<memoize::GroupMap>, EngineError> {
638 use spg_sql::ast::{BinOp, SelectItem as SI};
639 if !inner.ctes.is_empty()
640 || !inner.unions.is_empty()
641 || inner.group_by.is_some()
642 || inner.having.is_some()
643 || inner.distinct
644 || inner.items.len() != 1
645 || inner.order_by.len() > 1
646 || inner.offset.is_some()
647 {
648 return Ok(None);
649 }
650 if let Some(le) = inner.limit
652 && le.as_literal() != Some(1)
653 {
654 return Ok(None);
655 }
656 let Some(from) = &inner.from else {
657 return Ok(None);
658 };
659 if from.primary.lateral_subquery.is_some() || from.primary.unnest_expr.is_some() {
660 return Ok(None);
661 }
662 let mut inner_aliases: Vec<String> = Vec::new();
664 inner_aliases.push(
665 from.primary
666 .alias
667 .clone()
668 .unwrap_or_else(|| from.primary.name.clone()),
669 );
670 for j in &from.joins {
671 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
672 return Ok(None);
673 }
674 inner_aliases.push(
675 j.table
676 .alias
677 .clone()
678 .unwrap_or_else(|| j.table.name.clone()),
679 );
680 }
681 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
682 match &c.qualifier {
683 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
684 None => false,
685 }
686 };
687 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
688 match &c.qualifier {
689 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
690 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
693 }
694 };
695 let all_inner = |e: &Expr| -> bool {
698 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
699 let mut subs: Vec<&SelectStatement> = Vec::new();
700 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
701 subs.push(sub)
702 });
703 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
704 };
705 let Some(w) = &inner.where_ else {
706 return Ok(None);
707 };
708 let conjuncts = reorder::split_and_conjunctions(w);
709 let mut corr: Option<(spg_sql::ast::ColumnName, spg_sql::ast::ColumnName)> = None; let mut rest: Vec<&Expr> = Vec::new();
711 for c in conjuncts {
712 if let Expr::Binary {
713 lhs,
714 op: BinOp::Eq,
715 rhs,
716 } = c
717 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
718 {
719 let pair = if is_inner(a) && is_outer(b) {
720 Some((a.clone(), b.clone()))
721 } else if is_inner(b) && is_outer(a) {
722 Some((b.clone(), a.clone()))
723 } else {
724 None
725 };
726 if let Some(p) = pair {
727 if corr.is_some() {
728 return Ok(None); }
730 corr = Some(p);
731 continue;
732 }
733 }
734 if !all_inner(c) {
735 return Ok(None);
736 }
737 rest.push(c);
738 }
739 let Some((inner_col, outer_col)) = corr else {
740 return Ok(None);
741 };
742 let SI::Expr { expr: out_expr, .. } = &inner.items[0] else {
743 return Ok(None);
744 };
745 if !all_inner(out_expr) {
746 return Ok(None);
747 }
748 let order = inner.order_by.first();
749 if let Some(o) = order
750 && !all_inner(&o.expr)
751 {
752 return Ok(None);
753 }
754 let mut batch = inner.clone();
757 batch.limit = None;
758 batch.offset = None;
759 batch.order_by = Vec::new();
760 batch.where_ = rest
761 .iter()
762 .map(|e| (*e).clone())
763 .reduce(|a, b| Expr::Binary {
764 lhs: alloc::boxed::Box::new(a),
765 op: BinOp::And,
766 rhs: alloc::boxed::Box::new(b),
767 });
768 let mut items: Vec<SI> = alloc::vec![SI::Expr {
769 expr: Expr::Column(inner_col.clone()),
770 alias: None,
771 }];
772 if let Some(o) = order {
773 items.push(SI::Expr {
774 expr: o.expr.clone(),
775 alias: None,
776 });
777 }
778 items.push(SI::Expr {
779 expr: out_expr.clone(),
780 alias: None,
781 });
782 batch.items = items;
783 if aggregate::contains_aggregate(out_expr) {
795 batch.group_by = Some(alloc::vec![Expr::Column(inner_col.clone())]);
796 }
797 let keyed: Option<(&[Row<'static>], &EvalContext<'_>)> =
820 restrict.and_then(|(rows, rctx)| {
821 let driver_name: &str = if from.joins.is_empty() {
823 from.primary.name.as_str()
824 } else {
825 let q = inner_col.qualifier.as_deref()?;
826 let primary_alias = from
827 .primary
828 .alias
829 .as_deref()
830 .unwrap_or(from.primary.name.as_str());
831 if primary_alias.eq_ignore_ascii_case(q) {
832 from.primary.name.as_str()
833 } else {
834 from.joins
835 .iter()
836 .find(|j| {
837 j.table
838 .alias
839 .as_deref()
840 .unwrap_or(j.table.name.as_str())
841 .eq_ignore_ascii_case(q)
842 })
843 .map(|j| j.table.name.as_str())?
844 }
845 };
846 let table = self.active_catalog().get(driver_name)?;
847 let pos = table
848 .schema()
849 .columns
850 .iter()
851 .position(|c| c.name.eq_ignore_ascii_case(&inner_col.name))?;
852 table.index_on(pos)?;
853 if rows.len().saturating_mul(4) >= table.row_count() {
868 return None;
869 }
870 if !from.joins.is_empty() {
876 let driver_alias = inner_col.qualifier.as_deref()?;
877 if !reorder::drive_from(&mut batch, driver_alias) {
878 return None;
879 }
880 }
881 Some((rows, rctx))
882 });
883 let rows = if let Some((restrict_rows, rctx)) = keyed {
884 BATCHED_SCALAR_KEYED_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
885 let mut seen: alloc::collections::BTreeSet<String> =
900 alloc::collections::BTreeSet::new();
901 let mut key_lits: Vec<Expr> = Vec::new();
902 for srow in restrict_rows {
903 cancel.check()?;
904 let kv = eval::eval_expr(&Expr::Column(outer_col.clone()), srow, rctx)
905 .map_err(EngineError::Eval)?;
906 if matches!(kv, Value::Null) {
907 continue;
908 }
909 if !seen.insert(aggregate::encode_key(core::slice::from_ref(&kv))) {
910 continue;
911 }
912 key_lits.push(value_to_literal_expr(kv)?);
913 }
914 if key_lits.is_empty() {
915 Vec::new()
916 } else {
917 let in_pred = Expr::InList {
918 expr: alloc::boxed::Box::new(Expr::Column(inner_col.clone())),
919 list: key_lits,
920 negated: false,
921 };
922 let mut probe = batch.clone();
923 probe.where_ = Some(match probe.where_.take() {
924 Some(w) => Expr::Binary {
925 lhs: alloc::boxed::Box::new(w),
926 op: BinOp::And,
927 rhs: alloc::boxed::Box::new(in_pred),
928 },
929 None => in_pred,
930 });
931 BATCHED_SCALAR_KEYED_PROBE_COUNT
932 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
933 if let QueryResult::Rows { rows, .. } = self.exec_select_cancel(&probe, cancel)? {
934 rows
935 } else {
936 Vec::new()
937 }
938 }
939 } else {
940 BATCHED_SCALAR_FALL_THROUGH_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
941 let r = self.exec_select_cancel(&batch, cancel)?;
942 let QueryResult::Rows { rows, .. } = r else {
943 return Ok(None);
944 };
945 rows
946 };
947 let has_order = order.is_some();
948 let (desc, nf) = order
949 .map(|o| (o.desc, o.nulls_first))
950 .unwrap_or((false, None));
951 let mut best: alloc::collections::BTreeMap<String, (Option<Value>, Value)> =
952 alloc::collections::BTreeMap::new();
953 for row in rows {
954 let key_v = row.values.first().cloned().unwrap_or(Value::Null);
955 if matches!(key_v, Value::Null) {
956 continue;
957 }
958 let key = aggregate::encode_key(core::slice::from_ref(&key_v));
959 let (ord_v, out_v) = if has_order {
960 (
961 Some(row.values.get(1).cloned().unwrap_or(Value::Null)),
962 row.values.get(2).cloned().unwrap_or(Value::Null),
963 )
964 } else {
965 (None, row.values.get(1).cloned().unwrap_or(Value::Null))
966 };
967 match best.get(&key) {
968 None => {
969 best.insert(key, (ord_v, out_v));
970 }
971 Some((cur_ord, _)) if has_order => {
972 let cand = ord_v.clone().unwrap_or(Value::Null);
976 let cur = cur_ord.clone().unwrap_or(Value::Null);
977 if order_by_value_cmp(desc, nf, &cand, &cur) == core::cmp::Ordering::Less {
978 best.insert(key, (ord_v, out_v));
979 }
980 }
981 Some(_) => {} }
983 }
984 let map = best.into_iter().map(|(k, (_, v))| (k, v)).collect();
985 let empty_default = scalar_subquery_empty_default(inner);
990 Ok(Some((outer_col, map, empty_default)))
991 }
992}
993
994impl Engine {
995 pub(crate) fn try_batch_correlated_exists(
1014 &self,
1015 inner: &SelectStatement,
1016 cancel: CancelToken<'_>,
1017 ) -> Result<Option<memoize::ExistsSet>, EngineError> {
1018 use spg_sql::ast::SelectItem as SI;
1019 if !inner.ctes.is_empty()
1020 || !inner.unions.is_empty()
1021 || inner.group_by.is_some()
1022 || inner.having.is_some()
1023 || inner.distinct
1024 {
1025 return Ok(None);
1026 }
1027 let Some(from) = &inner.from else {
1028 return Ok(None);
1029 };
1030 if from.primary.lateral_subquery.is_some()
1031 || from.primary.unnest_expr.is_some()
1032 || from.primary.generate_series_args.is_some()
1033 || from.primary.as_of_segment.is_some()
1034 {
1035 return Ok(None);
1036 }
1037 let mut inner_aliases: Vec<String> = Vec::new();
1038 inner_aliases.push(
1039 from.primary
1040 .alias
1041 .clone()
1042 .unwrap_or_else(|| from.primary.name.clone()),
1043 );
1044 for j in &from.joins {
1045 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
1046 return Ok(None);
1047 }
1048 inner_aliases.push(
1049 j.table
1050 .alias
1051 .clone()
1052 .unwrap_or_else(|| j.table.name.clone()),
1053 );
1054 }
1055 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
1056 match &c.qualifier {
1057 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1058 None => false,
1059 }
1060 };
1061 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
1062 match &c.qualifier {
1063 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1064 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
1065 }
1066 };
1067 let all_inner = |e: &Expr| -> bool {
1068 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1069 let mut subs: Vec<&SelectStatement> = Vec::new();
1070 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
1071 subs.push(sub)
1072 });
1073 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
1074 };
1075 let Some(w) = &inner.where_ else {
1076 return Ok(None);
1077 };
1078 let conjuncts = reorder::split_and_conjunctions(w);
1079 let mut inner_keys: Vec<spg_sql::ast::ColumnName> = Vec::new();
1080 let mut outer_cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1081 let mut rest: Vec<&Expr> = Vec::new();
1082 for c in conjuncts {
1083 if let Expr::Binary {
1084 lhs,
1085 op: BinOp::Eq,
1086 rhs,
1087 } = c
1088 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1089 {
1090 let pair = if is_inner(a) && is_outer(b) {
1091 Some((a.clone(), b.clone()))
1092 } else if is_inner(b) && is_outer(a) {
1093 Some((b.clone(), a.clone()))
1094 } else {
1095 None
1096 };
1097 if let Some((ic, oc)) = pair {
1098 inner_keys.push(ic);
1099 outer_cols.push(oc);
1100 continue;
1101 }
1102 }
1103 if !all_inner(c) {
1107 return Ok(None);
1108 }
1109 rest.push(c);
1110 }
1111 if inner_keys.is_empty() {
1112 return Ok(None); }
1114 let mut batch = inner.clone();
1118 batch.limit = None;
1119 batch.offset = None;
1120 batch.order_by = Vec::new();
1121 batch.distinct = false;
1122 batch.where_ = rest
1123 .iter()
1124 .map(|e| (*e).clone())
1125 .reduce(|a, b| Expr::Binary {
1126 lhs: alloc::boxed::Box::new(a),
1127 op: BinOp::And,
1128 rhs: alloc::boxed::Box::new(b),
1129 });
1130 batch.items = inner_keys
1131 .iter()
1132 .map(|c| SI::Expr {
1133 expr: Expr::Column(c.clone()),
1134 alias: None,
1135 })
1136 .collect();
1137 let r = self.exec_select_cancel(&batch, cancel)?;
1138 let QueryResult::Rows { rows, .. } = r else {
1139 return Ok(None);
1140 };
1141 let n = inner_keys.len();
1142 let mut set: alloc::collections::BTreeSet<String> = alloc::collections::BTreeSet::new();
1143 for row in rows {
1144 let keys = row.values.get(..n).unwrap_or(&row.values);
1145 if keys.iter().any(|v| matches!(v, Value::Null)) {
1148 continue;
1149 }
1150 set.insert(aggregate::encode_key(keys));
1151 }
1152 Ok(Some((outer_cols, set)))
1153 }
1154}
1155
1156impl Engine {
1157 pub(crate) fn pull_up_correlated_limit_one_subqueries(
1231 &self,
1232 stmt: &mut SelectStatement,
1233 ) -> bool {
1234 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
1242 return false;
1243 }
1244 let outer_aliases: alloc::collections::BTreeSet<String> = {
1247 let from = stmt.from.as_ref().expect("from present");
1248 let mut s = alloc::collections::BTreeSet::new();
1249 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
1250 s.insert(
1251 t.alias
1252 .clone()
1253 .unwrap_or_else(|| t.name.clone())
1254 .to_ascii_lowercase(),
1255 );
1256 };
1257 push(&mut s, &from.primary);
1258 for j in &from.joins {
1259 push(&mut s, &j.table);
1260 }
1261 s
1262 };
1263 let outer_has_group_by = stmt.group_by.is_some() || stmt.group_by_all;
1264 let mut new_ctes: Vec<Cte> = Vec::new();
1265 let mut new_joins: Vec<FromJoin> = Vec::new();
1266 let cte_seed = stmt.ctes.len();
1267 for item in &mut stmt.items {
1268 if let SelectItem::Expr { expr, .. } = item {
1269 self.pull_up_walk_limit_one(
1270 expr,
1271 false,
1272 &outer_aliases,
1273 outer_has_group_by,
1274 cte_seed,
1275 &mut new_ctes,
1276 &mut new_joins,
1277 );
1278 }
1279 }
1280 if new_ctes.is_empty() {
1281 return false;
1282 }
1283 PULLUP_LIMIT1_FIRE_COUNT
1284 .fetch_add(new_ctes.len() as u64, core::sync::atomic::Ordering::Relaxed);
1285 stmt.ctes.extend(new_ctes);
1286 stmt.from
1287 .as_mut()
1288 .expect("from present")
1289 .joins
1290 .extend(new_joins);
1291 true
1292 }
1293
1294 #[allow(clippy::too_many_arguments)]
1299 fn pull_up_walk_limit_one(
1300 &self,
1301 e: &mut Expr,
1302 in_agg: bool,
1303 outer_aliases: &alloc::collections::BTreeSet<String>,
1304 outer_has_group_by: bool,
1305 cte_seed: usize,
1306 ctes_out: &mut Vec<Cte>,
1307 joins_out: &mut Vec<FromJoin>,
1308 ) {
1309 match e {
1310 Expr::ScalarSubquery(inner) => {
1311 if let Some((cte, join, cte_col)) =
1312 self.try_pull_up_limit_one(inner, outer_aliases, cte_seed + ctes_out.len())
1313 {
1314 ctes_out.push(cte);
1315 joins_out.push(join);
1316 let col_expr = Expr::Column(cte_col);
1326 *e = if outer_has_group_by && !in_agg {
1327 Expr::FunctionCall {
1328 name: "max".into(),
1329 args: alloc::vec![col_expr],
1330 }
1331 } else {
1332 col_expr
1333 };
1334 }
1335 }
1338 Expr::FunctionCall { name, args } => {
1339 let child = in_agg || aggregate::is_aggregate_name(name);
1340 for a in args.iter_mut() {
1341 self.pull_up_walk_limit_one(
1342 a,
1343 child,
1344 outer_aliases,
1345 outer_has_group_by,
1346 cte_seed,
1347 ctes_out,
1348 joins_out,
1349 );
1350 }
1351 }
1352 Expr::AggregateOrdered {
1353 call,
1354 order_by,
1355 filter,
1356 ..
1357 } => {
1358 self.pull_up_walk_limit_one(
1359 call,
1360 true,
1361 outer_aliases,
1362 outer_has_group_by,
1363 cte_seed,
1364 ctes_out,
1365 joins_out,
1366 );
1367 for o in order_by.iter_mut() {
1368 self.pull_up_walk_limit_one(
1369 &mut o.expr,
1370 true,
1371 outer_aliases,
1372 outer_has_group_by,
1373 cte_seed,
1374 ctes_out,
1375 joins_out,
1376 );
1377 }
1378 if let Some(f) = filter {
1379 self.pull_up_walk_limit_one(
1380 f,
1381 true,
1382 outer_aliases,
1383 outer_has_group_by,
1384 cte_seed,
1385 ctes_out,
1386 joins_out,
1387 );
1388 }
1389 }
1390 Expr::Binary { lhs, rhs, .. } => {
1391 self.pull_up_walk_limit_one(
1392 lhs,
1393 in_agg,
1394 outer_aliases,
1395 outer_has_group_by,
1396 cte_seed,
1397 ctes_out,
1398 joins_out,
1399 );
1400 self.pull_up_walk_limit_one(
1401 rhs,
1402 in_agg,
1403 outer_aliases,
1404 outer_has_group_by,
1405 cte_seed,
1406 ctes_out,
1407 joins_out,
1408 );
1409 }
1410 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
1411 self.pull_up_walk_limit_one(
1412 expr,
1413 in_agg,
1414 outer_aliases,
1415 outer_has_group_by,
1416 cte_seed,
1417 ctes_out,
1418 joins_out,
1419 );
1420 }
1421 Expr::Like { expr, pattern, .. } => {
1422 self.pull_up_walk_limit_one(
1423 expr,
1424 in_agg,
1425 outer_aliases,
1426 outer_has_group_by,
1427 cte_seed,
1428 ctes_out,
1429 joins_out,
1430 );
1431 self.pull_up_walk_limit_one(
1432 pattern,
1433 in_agg,
1434 outer_aliases,
1435 outer_has_group_by,
1436 cte_seed,
1437 ctes_out,
1438 joins_out,
1439 );
1440 }
1441 Expr::InList { expr, list, .. } => {
1442 self.pull_up_walk_limit_one(
1443 expr,
1444 in_agg,
1445 outer_aliases,
1446 outer_has_group_by,
1447 cte_seed,
1448 ctes_out,
1449 joins_out,
1450 );
1451 for it in list.iter_mut() {
1452 self.pull_up_walk_limit_one(
1453 it,
1454 in_agg,
1455 outer_aliases,
1456 outer_has_group_by,
1457 cte_seed,
1458 ctes_out,
1459 joins_out,
1460 );
1461 }
1462 }
1463 Expr::Case {
1464 operand,
1465 branches,
1466 else_branch,
1467 } => {
1468 if let Some(o) = operand {
1469 self.pull_up_walk_limit_one(
1470 o,
1471 in_agg,
1472 outer_aliases,
1473 outer_has_group_by,
1474 cte_seed,
1475 ctes_out,
1476 joins_out,
1477 );
1478 }
1479 for (w, t) in branches.iter_mut() {
1480 self.pull_up_walk_limit_one(
1481 w,
1482 in_agg,
1483 outer_aliases,
1484 outer_has_group_by,
1485 cte_seed,
1486 ctes_out,
1487 joins_out,
1488 );
1489 self.pull_up_walk_limit_one(
1490 t,
1491 in_agg,
1492 outer_aliases,
1493 outer_has_group_by,
1494 cte_seed,
1495 ctes_out,
1496 joins_out,
1497 );
1498 }
1499 if let Some(eb) = else_branch {
1500 self.pull_up_walk_limit_one(
1501 eb,
1502 in_agg,
1503 outer_aliases,
1504 outer_has_group_by,
1505 cte_seed,
1506 ctes_out,
1507 joins_out,
1508 );
1509 }
1510 }
1511 _ => {}
1514 }
1515 }
1516
1517 fn try_pull_up_limit_one(
1523 &self,
1524 inner: &SelectStatement,
1525 outer_aliases: &alloc::collections::BTreeSet<String>,
1526 alias_n: usize,
1527 ) -> Option<(Cte, FromJoin, ColumnName)> {
1528 return None;
1554 #[allow(unreachable_code)]
1555 if !inner.ctes.is_empty()
1557 || !inner.unions.is_empty()
1558 || inner.group_by.is_some()
1559 || inner.group_by_all
1560 || inner.having.is_some()
1561 || inner.distinct
1562 || inner.offset.is_some()
1563 || inner.items.len() != 1
1564 || inner.order_by.is_empty()
1565 {
1566 return None;
1567 }
1568 match inner.limit {
1571 Some(LimitExpr::Literal(1)) => {}
1572 _ => return None,
1573 }
1574 let from = inner.from.as_ref()?;
1575 if !from.joins.is_empty()
1578 || from.primary.lateral_subquery.is_some()
1579 || from.primary.unnest_expr.is_some()
1580 || from.primary.generate_series_args.is_some()
1581 || from.primary.as_of_segment.is_some()
1582 {
1583 return None;
1584 }
1585 let inner_table = from.primary.name.clone();
1586 let inner_alias = from
1587 .primary
1588 .alias
1589 .clone()
1590 .unwrap_or_else(|| inner_table.clone());
1591 let is_inner = |c: &ColumnName| -> bool {
1592 c.qualifier
1593 .as_deref()
1594 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
1595 };
1596 let is_outer = |c: &ColumnName| -> bool {
1597 c.qualifier
1598 .as_deref()
1599 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
1600 };
1601 let SelectItem::Expr {
1606 expr: proj_expr,
1607 alias: _,
1608 } = &inner.items[0]
1609 else {
1610 return None;
1611 };
1612 if proj_has_disqualifying_shape(proj_expr, &inner_alias, outer_aliases) {
1613 return None;
1614 }
1615 let where_ = inner.where_.as_ref()?;
1618 let mut corr: Option<(String, ColumnName)> = None;
1619 let mut non_corr: Vec<Expr> = Vec::new();
1620 for c in reorder::split_and_conjunctions(where_) {
1621 if let Expr::Binary {
1622 lhs,
1623 op: BinOp::Eq,
1624 rhs,
1625 } = c
1626 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1627 {
1628 let pair = if is_inner(a) && is_outer(b) {
1629 Some((a.name.clone(), b.clone()))
1630 } else if is_inner(b) && is_outer(a) {
1631 Some((b.name.clone(), a.clone()))
1632 } else {
1633 None
1634 };
1635 if let Some(p) = pair {
1636 if corr.is_some() {
1637 return None; }
1639 corr = Some(p);
1640 continue;
1641 }
1642 }
1643 if !expr_is_all_inner(c, &inner_alias) {
1644 return None;
1645 }
1646 non_corr.push(c.clone());
1647 }
1648 let (inner_key, outer_col) = corr?;
1649 for ob in &inner.order_by {
1652 if !expr_is_all_inner(&ob.expr, &inner_alias) {
1653 return None;
1654 }
1655 }
1656 if !expr_is_all_inner(proj_expr, &inner_alias) {
1658 return None;
1659 }
1660 let cte_name = alloc::format!("__cl1_{alias_n}");
1666 let jk_expr = Expr::Column(ColumnName {
1667 qualifier: Some(inner_alias.clone()),
1668 name: inner_key.clone(),
1669 });
1670 let argmax = Expr::ArraySubscript {
1671 target: alloc::boxed::Box::new(Expr::AggregateOrdered {
1672 call: alloc::boxed::Box::new(Expr::FunctionCall {
1673 name: "array_agg".into(),
1674 args: alloc::vec![proj_expr.clone()],
1675 }),
1676 order_by: inner.order_by.clone(),
1677 distinct: false,
1678 filter: None,
1679 }),
1680 index: alloc::boxed::Box::new(Expr::Literal(Literal::Integer(1))),
1681 };
1682 let body_where = if non_corr.is_empty() {
1683 None
1684 } else {
1685 let mut iter = non_corr.into_iter();
1686 let head = iter.next().expect("non_corr nonempty in this branch");
1687 Some(iter.fold(head, |acc, p| Expr::Binary {
1688 lhs: alloc::boxed::Box::new(acc),
1689 op: BinOp::And,
1690 rhs: alloc::boxed::Box::new(p),
1691 }))
1692 };
1693 let body = SelectStatement {
1694 ctes: Vec::new(),
1695 distinct: false,
1696 items: alloc::vec![
1697 SelectItem::Expr {
1698 expr: jk_expr.clone(),
1699 alias: Some("jk".into()),
1700 },
1701 SelectItem::Expr {
1702 expr: argmax,
1703 alias: Some("pj".into()),
1704 },
1705 ],
1706 from: Some(from.clone()),
1707 where_: body_where,
1708 group_by: Some(alloc::vec![jk_expr]),
1709 group_by_all: false,
1710 having: None,
1711 unions: Vec::new(),
1712 order_by: Vec::new(),
1713 limit: None,
1714 offset: None,
1715 limit_with_ties: false,
1716 };
1717 let cte = Cte {
1718 name: cte_name.clone(),
1719 body: spg_sql::ast::CteBody::Select(body),
1720 recursive: false,
1721 column_overrides: Vec::new(),
1722 };
1723 let join = FromJoin {
1725 kind: JoinKind::Left,
1726 table: TableRef {
1727 name: cte_name.clone(),
1728 alias: None,
1729 as_of_segment: None,
1730 unnest_expr: None,
1731 unnest_column_aliases: Vec::new(),
1732 generate_series_args: None,
1733 lateral_subquery: None,
1734 jsonb_each_text_arg: None,
1735 },
1736 on: Some(Expr::Binary {
1737 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
1738 qualifier: Some(cte_name.clone()),
1739 name: "jk".into(),
1740 })),
1741 op: BinOp::Eq,
1742 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
1743 }),
1744 };
1745 let repl = ColumnName {
1746 qualifier: Some(cte_name),
1747 name: "pj".into(),
1748 };
1749 Some((cte, join, repl))
1750 }
1751
1752 pub(crate) fn pull_up_unique_correlated_agg_subqueries(
1753 &self,
1754 stmt: &mut SelectStatement,
1755 ) -> bool {
1756 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
1757 return false;
1758 }
1759 let outer_aliases: alloc::collections::BTreeSet<String> = {
1761 let from = stmt.from.as_ref().expect("from present");
1762 let mut s = alloc::collections::BTreeSet::new();
1763 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
1764 s.insert(
1765 t.alias
1766 .clone()
1767 .unwrap_or_else(|| t.name.clone())
1768 .to_ascii_lowercase(),
1769 );
1770 };
1771 push(&mut s, &from.primary);
1772 for j in &from.joins {
1773 push(&mut s, &j.table);
1774 }
1775 s
1776 };
1777 let mut new_joins: Vec<FromJoin> = Vec::new();
1778 for item in &mut stmt.items {
1779 if let SelectItem::Expr { expr, .. } = item {
1780 self.pull_up_walk(expr, false, &outer_aliases, &mut new_joins);
1781 }
1782 }
1783 if new_joins.is_empty() {
1784 return false;
1785 }
1786 stmt.from
1787 .as_mut()
1788 .expect("from present")
1789 .joins
1790 .extend(new_joins);
1791 true
1792 }
1793
1794 fn pull_up_walk(
1799 &self,
1800 e: &mut Expr,
1801 in_agg: bool,
1802 outer_aliases: &alloc::collections::BTreeSet<String>,
1803 joins_out: &mut Vec<FromJoin>,
1804 ) {
1805 match e {
1806 Expr::ScalarSubquery(inner) => {
1807 if in_agg
1808 && let Some((join, col)) =
1809 self.try_pull_up_join(inner, outer_aliases, joins_out.len())
1810 {
1811 joins_out.push(join);
1812 *e = Expr::Column(col);
1813 }
1814 }
1817 Expr::FunctionCall { name, args } => {
1818 let child = in_agg || aggregate::is_aggregate_name(name);
1819 for a in args.iter_mut() {
1820 self.pull_up_walk(a, child, outer_aliases, joins_out);
1821 }
1822 }
1823 Expr::AggregateOrdered {
1824 call,
1825 order_by,
1826 filter,
1827 ..
1828 } => {
1829 self.pull_up_walk(call, true, outer_aliases, joins_out);
1830 for o in order_by.iter_mut() {
1831 self.pull_up_walk(&mut o.expr, true, outer_aliases, joins_out);
1832 }
1833 if let Some(f) = filter {
1834 self.pull_up_walk(f, true, outer_aliases, joins_out);
1835 }
1836 }
1837 Expr::Binary { lhs, rhs, .. } => {
1838 self.pull_up_walk(lhs, in_agg, outer_aliases, joins_out);
1839 self.pull_up_walk(rhs, in_agg, outer_aliases, joins_out);
1840 }
1841 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
1842 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1843 }
1844 Expr::Like { expr, pattern, .. } => {
1845 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1846 self.pull_up_walk(pattern, in_agg, outer_aliases, joins_out);
1847 }
1848 Expr::InList { expr, list, .. } => {
1849 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1850 for it in list.iter_mut() {
1851 self.pull_up_walk(it, in_agg, outer_aliases, joins_out);
1852 }
1853 }
1854 Expr::Case {
1855 operand,
1856 branches,
1857 else_branch,
1858 } => {
1859 if let Some(o) = operand {
1860 self.pull_up_walk(o, in_agg, outer_aliases, joins_out);
1861 }
1862 for (w, t) in branches.iter_mut() {
1863 self.pull_up_walk(w, in_agg, outer_aliases, joins_out);
1864 self.pull_up_walk(t, in_agg, outer_aliases, joins_out);
1865 }
1866 if let Some(eb) = else_branch {
1867 self.pull_up_walk(eb, in_agg, outer_aliases, joins_out);
1868 }
1869 }
1870 _ => {}
1875 }
1876 }
1877
1878 fn try_pull_up_join(
1882 &self,
1883 inner: &SelectStatement,
1884 outer_aliases: &alloc::collections::BTreeSet<String>,
1885 alias_n: usize,
1886 ) -> Option<(FromJoin, ColumnName)> {
1887 if !inner.ctes.is_empty()
1890 || !inner.unions.is_empty()
1891 || inner.group_by.is_some()
1892 || inner.having.is_some()
1893 || inner.distinct
1894 || !inner.order_by.is_empty()
1895 || inner.limit.is_some()
1896 || inner.offset.is_some()
1897 || inner.items.len() != 1
1898 {
1899 return None;
1900 }
1901 let from = inner.from.as_ref()?;
1902 if !from.joins.is_empty()
1903 || from.primary.lateral_subquery.is_some()
1904 || from.primary.unnest_expr.is_some()
1905 || from.primary.generate_series_args.is_some()
1906 || from.primary.as_of_segment.is_some()
1907 {
1908 return None;
1909 }
1910 let inner_table = from.primary.name.clone();
1911 let inner_alias = from
1912 .primary
1913 .alias
1914 .clone()
1915 .unwrap_or_else(|| inner_table.clone());
1916 let is_inner = |c: &ColumnName| -> bool {
1917 c.qualifier
1918 .as_deref()
1919 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
1920 };
1921 let is_outer = |c: &ColumnName| -> bool {
1922 c.qualifier
1923 .as_deref()
1924 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
1925 };
1926 let SelectItem::Expr { expr: out_expr, .. } = &inner.items[0] else {
1928 return None;
1929 };
1930 let Expr::Column(out_col) = out_expr else {
1931 return None;
1932 };
1933 if !is_inner(out_col) {
1934 return None;
1935 }
1936 let w = inner.where_.as_ref()?;
1938 let mut corr: Option<(String, ColumnName)> = None;
1939 let mut rest: Vec<Expr> = Vec::new();
1940 for c in reorder::split_and_conjunctions(w) {
1941 if let Expr::Binary {
1942 lhs,
1943 op: BinOp::Eq,
1944 rhs,
1945 } = c
1946 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1947 {
1948 let pair = if is_inner(a) && is_outer(b) {
1949 Some((a.name.clone(), b.clone()))
1950 } else if is_inner(b) && is_outer(a) {
1951 Some((b.name.clone(), a.clone()))
1952 } else {
1953 None
1954 };
1955 if let Some(p) = pair {
1956 if corr.is_some() {
1957 return None; }
1959 corr = Some(p);
1960 continue;
1961 }
1962 }
1963 if !expr_is_all_inner(c, &inner_alias) {
1964 return None;
1965 }
1966 rest.push(c.clone());
1967 }
1968 let (inner_key, outer_col) = corr?;
1969 if !self.column_is_single_unique(&inner_table, &inner_key) {
1972 return None;
1973 }
1974 let fresh = alloc::format!("__plj_{alias_n}");
1976 let key_eq = Expr::Binary {
1977 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
1978 qualifier: Some(fresh.clone()),
1979 name: inner_key,
1980 })),
1981 op: BinOp::Eq,
1982 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
1983 };
1984 let on = rest
1985 .into_iter()
1986 .map(|mut e| {
1987 rename_qualifier(&mut e, &inner_alias, &fresh);
1988 e
1989 })
1990 .fold(key_eq, |acc, pred| Expr::Binary {
1991 lhs: alloc::boxed::Box::new(acc),
1992 op: BinOp::And,
1993 rhs: alloc::boxed::Box::new(pred),
1994 });
1995 let join = FromJoin {
1996 kind: JoinKind::Left,
1997 table: TableRef {
1998 name: inner_table,
1999 alias: Some(fresh.clone()),
2000 as_of_segment: None,
2001 unnest_expr: None,
2002 unnest_column_aliases: Vec::new(),
2003 generate_series_args: None,
2004 lateral_subquery: None,
2005 jsonb_each_text_arg: None,
2006 },
2007 on: Some(on),
2008 };
2009 let repl = ColumnName {
2010 qualifier: Some(fresh),
2011 name: out_col.name.clone(),
2012 };
2013 Some((join, repl))
2014 }
2015
2016 pub(crate) fn pull_up_exists_sublinks(&self, stmt: &mut SelectStatement) -> bool {
2047 if stmt.from.is_none() {
2048 return false;
2049 }
2050 let Some(where_expr) = stmt.where_.take() else {
2051 return false;
2052 };
2053 let mut collision_names: alloc::collections::BTreeSet<String> =
2064 alloc::collections::BTreeSet::new();
2065 for c in reorder::split_and_conjunctions(&where_expr) {
2066 let inner_subq: Option<&SelectStatement> = match c {
2067 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2068 Expr::Unary {
2069 op: UnOp::Not,
2070 expr,
2071 } => match expr.as_ref() {
2072 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2073 _ => None,
2074 },
2075 _ => None,
2076 };
2077 let Some(inner) = inner_subq else { continue };
2078 let Some(from) = &inner.from else { continue };
2079 if !from.joins.is_empty() {
2080 continue;
2081 }
2082 let Some(t) = self.active_catalog().get(&from.primary.name) else {
2083 continue;
2084 };
2085 for col in &t.schema().columns {
2086 collision_names.insert(col.name.to_ascii_lowercase());
2087 }
2088 }
2089 let mut where_expr = where_expr;
2090 if !collision_names.is_empty() {
2091 let from = stmt.from.as_ref().expect("from present");
2092 let outer_tables: Vec<(String, String)> = {
2093 let mut v = Vec::new();
2094 let collect = |v: &mut Vec<(String, String)>, t: &TableRef| {
2095 let alias = t.alias.clone().unwrap_or_else(|| t.name.clone());
2096 v.push((alias, t.name.clone()));
2097 };
2098 collect(&mut v, &from.primary);
2099 for j in &from.joins {
2100 collect(&mut v, &j.table);
2101 }
2102 v
2103 };
2104 let mut owner: alloc::collections::BTreeMap<String, String> =
2105 alloc::collections::BTreeMap::new();
2106 for col_lc in &collision_names {
2107 let mut matches: Vec<String> = Vec::new();
2108 for (alias, tname) in &outer_tables {
2109 let Some(t) = self.active_catalog().get(tname) else {
2110 continue;
2111 };
2112 if t.schema()
2113 .columns
2114 .iter()
2115 .any(|c| c.name.eq_ignore_ascii_case(col_lc))
2116 {
2117 matches.push(alias.clone());
2118 }
2119 }
2120 if matches.len() == 1 {
2121 owner.insert(col_lc.clone(), matches.remove(0));
2122 }
2123 }
2124 if !owner.is_empty() {
2125 disambiguate_stmt_unqualified_columns(stmt, &owner);
2126 disambiguate_expr_unqualified_columns(&mut where_expr, &owner);
2127 }
2128 }
2129 let outer_aliases: alloc::collections::BTreeSet<String> = {
2130 let from = stmt.from.as_ref().expect("from present");
2131 let mut s = alloc::collections::BTreeSet::new();
2132 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
2133 s.insert(
2134 t.alias
2135 .clone()
2136 .unwrap_or_else(|| t.name.clone())
2137 .to_ascii_lowercase(),
2138 );
2139 };
2140 push(&mut s, &from.primary);
2141 for j in &from.joins {
2142 push(&mut s, &j.table);
2143 }
2144 s
2145 };
2146 let conjuncts = reorder::split_and_conjunctions(&where_expr);
2147 let mut survivors: Vec<Expr> = Vec::new();
2148 let mut new_joins: Vec<FromJoin> = Vec::new();
2149 let mut rewrote_any = false;
2150 for c in conjuncts {
2151 let parsed: Option<(&SelectStatement, bool)> = match c {
2156 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), *negated)),
2157 Expr::Unary {
2158 op: UnOp::Not,
2159 expr,
2160 } => match expr.as_ref() {
2161 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), !*negated)),
2162 _ => None,
2163 },
2164 _ => None,
2165 };
2166 if let Some((subquery, neg)) = parsed {
2167 let outer_has_limit = stmt.limit.is_some();
2185 let try_in_first = outer_has_limit;
2186 let mut consumed = false;
2187 if try_in_first
2188 && let Some(rewritten) =
2189 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2190 {
2191 survivors.push(rewritten);
2192 consumed = true;
2193 }
2194 if !consumed
2195 && let Some((join, residual)) = self.try_pull_up_exists_sublink(
2196 subquery,
2197 neg,
2198 &outer_aliases,
2199 new_joins.len(),
2200 )
2201 {
2202 new_joins.push(join);
2203 if let Some(r) = residual {
2204 survivors.push(r);
2205 }
2206 consumed = true;
2207 }
2208 if !consumed
2209 && !try_in_first
2210 && let Some(rewritten) =
2211 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2212 {
2213 survivors.push(rewritten);
2216 consumed = true;
2217 }
2218 if consumed {
2219 rewrote_any = true;
2220 continue;
2221 }
2222 }
2223 survivors.push(c.clone());
2224 }
2225 if !rewrote_any {
2226 stmt.where_ = Some(where_expr);
2227 return false;
2228 }
2229 EXISTS_PULLUP_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2230 if !new_joins.is_empty() {
2231 stmt.from
2232 .as_mut()
2233 .expect("from present")
2234 .joins
2235 .extend(new_joins);
2236 }
2237 stmt.where_ = survivors.into_iter().reduce(|a, b| Expr::Binary {
2238 lhs: alloc::boxed::Box::new(a),
2239 op: BinOp::And,
2240 rhs: alloc::boxed::Box::new(b),
2241 });
2242 true
2243 }
2244
2245 fn try_pull_up_exists_as_in(
2258 &self,
2259 inner: &SelectStatement,
2260 negated: bool,
2261 outer_aliases: &alloc::collections::BTreeSet<String>,
2262 ) -> Option<Expr> {
2263 if !inner.ctes.is_empty()
2264 || !inner.unions.is_empty()
2265 || inner.group_by.is_some()
2266 || inner.having.is_some()
2267 || inner.distinct
2268 || !inner.order_by.is_empty()
2269 || inner.limit.is_some()
2270 || inner.offset.is_some()
2271 {
2272 return None;
2273 }
2274 let from = inner.from.as_ref()?;
2275 if !from.joins.is_empty()
2276 || from.primary.lateral_subquery.is_some()
2277 || from.primary.unnest_expr.is_some()
2278 || from.primary.generate_series_args.is_some()
2279 || from.primary.as_of_segment.is_some()
2280 {
2281 return None;
2282 }
2283 let inner_table = from.primary.name.clone();
2284 let inner_alias = from
2285 .primary
2286 .alias
2287 .clone()
2288 .unwrap_or_else(|| inner_table.clone());
2289 let is_inner = |c: &ColumnName| -> bool {
2290 c.qualifier
2291 .as_deref()
2292 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2293 };
2294 let is_outer = |c: &ColumnName| -> bool {
2295 c.qualifier
2296 .as_deref()
2297 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2298 };
2299 let w = inner.where_.as_ref()?;
2300 let mut corr: Option<(String, ColumnName)> = None;
2301 let mut rest: Vec<Expr> = Vec::new();
2302 for c in reorder::split_and_conjunctions(w) {
2303 if let Expr::Binary {
2304 lhs,
2305 op: BinOp::Eq,
2306 rhs,
2307 } = c
2308 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2309 {
2310 let pair = if is_inner(a) && is_outer(b) {
2311 Some((a.name.clone(), b.clone()))
2312 } else if is_inner(b) && is_outer(a) {
2313 Some((b.name.clone(), a.clone()))
2314 } else {
2315 None
2316 };
2317 if let Some(p) = pair {
2318 if corr.is_some() {
2319 return None;
2320 }
2321 corr = Some(p);
2322 continue;
2323 }
2324 }
2325 if !expr_is_all_inner(c, &inner_alias) {
2326 return None;
2327 }
2328 rest.push(c.clone());
2329 }
2330 let (inner_key, outer_col) = corr?;
2331 if negated && !self.column_is_not_null(&inner_table, &inner_key) {
2332 return None;
2333 }
2334 let mut rewritten = inner.clone();
2339 rewritten.limit = None;
2340 rewritten.offset = None;
2341 rewritten.order_by = Vec::new();
2342 rewritten.distinct = false;
2343 rewritten.where_ = rest.into_iter().reduce(|a, b| Expr::Binary {
2344 lhs: alloc::boxed::Box::new(a),
2345 op: BinOp::And,
2346 rhs: alloc::boxed::Box::new(b),
2347 });
2348 rewritten.items = alloc::vec![SelectItem::Expr {
2349 expr: Expr::Column(ColumnName {
2350 qualifier: Some(inner_alias),
2351 name: inner_key,
2352 }),
2353 alias: None,
2354 }];
2355 Some(Expr::InSubquery {
2356 expr: alloc::boxed::Box::new(Expr::Column(outer_col)),
2357 subquery: alloc::boxed::Box::new(rewritten),
2358 negated,
2359 })
2360 }
2361
2362 fn try_pull_up_exists_sublink(
2363 &self,
2364 inner: &SelectStatement,
2365 negated: bool,
2366 outer_aliases: &alloc::collections::BTreeSet<String>,
2367 alias_n: usize,
2368 ) -> Option<(FromJoin, Option<Expr>)> {
2369 if !inner.ctes.is_empty()
2370 || !inner.unions.is_empty()
2371 || inner.group_by.is_some()
2372 || inner.having.is_some()
2373 || inner.distinct
2374 || !inner.order_by.is_empty()
2375 || inner.limit.is_some()
2376 || inner.offset.is_some()
2377 {
2378 return None;
2379 }
2380 let from = inner.from.as_ref()?;
2381 if !from.joins.is_empty()
2382 || from.primary.lateral_subquery.is_some()
2383 || from.primary.unnest_expr.is_some()
2384 || from.primary.generate_series_args.is_some()
2385 || from.primary.as_of_segment.is_some()
2386 {
2387 return None;
2388 }
2389 let inner_table = from.primary.name.clone();
2390 let inner_alias = from
2391 .primary
2392 .alias
2393 .clone()
2394 .unwrap_or_else(|| inner_table.clone());
2395 let is_inner = |c: &ColumnName| -> bool {
2396 c.qualifier
2397 .as_deref()
2398 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2399 };
2400 let is_outer = |c: &ColumnName| -> bool {
2401 c.qualifier
2402 .as_deref()
2403 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2404 };
2405 let w = inner.where_.as_ref()?;
2406 let mut corr_pairs: Vec<(String, ColumnName)> = Vec::new();
2424 let mut rest: Vec<Expr> = Vec::new();
2425 for c in reorder::split_and_conjunctions(w) {
2426 if let Expr::Binary {
2427 lhs,
2428 op: BinOp::Eq,
2429 rhs,
2430 } = c
2431 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2432 {
2433 let pair = if is_inner(a) && is_outer(b) {
2434 Some((a.name.clone(), b.clone()))
2435 } else if is_inner(b) && is_outer(a) {
2436 Some((b.name.clone(), a.clone()))
2437 } else {
2438 None
2439 };
2440 if let Some(p) = pair {
2441 corr_pairs.push(p);
2442 continue;
2443 }
2444 }
2445 if !expr_is_all_inner(c, &inner_alias) {
2446 return None;
2447 }
2448 rest.push(c.clone());
2449 }
2450 if corr_pairs.is_empty() {
2451 return None;
2452 }
2453 if corr_pairs.len() > 1
2457 && EXISTS_PULLUP_MULTICOL_DISABLE.load(core::sync::atomic::Ordering::Relaxed)
2458 {
2459 return None;
2460 }
2461 if !negated {
2468 for (k, _) in &corr_pairs {
2474 if !self.column_is_single_unique(&inner_table, k) {
2475 return None;
2476 }
2477 }
2478 }
2479 let fresh = alloc::format!("__exsj_{alias_n}");
2480 let mut on_iter = corr_pairs.iter().map(|(ik, oc)| Expr::Binary {
2483 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
2484 qualifier: Some(fresh.clone()),
2485 name: ik.clone(),
2486 })),
2487 op: BinOp::Eq,
2488 rhs: alloc::boxed::Box::new(Expr::Column(oc.clone())),
2489 });
2490 let first_key_eq = on_iter
2491 .next()
2492 .expect("corr_pairs non-empty post `is_empty()` gate");
2493 let on = rest
2494 .into_iter()
2495 .map(|mut e| {
2496 rename_qualifier(&mut e, &inner_alias, &fresh);
2497 e
2498 })
2499 .chain(on_iter)
2500 .fold(first_key_eq, |acc, pred| Expr::Binary {
2501 lhs: alloc::boxed::Box::new(acc),
2502 op: BinOp::And,
2503 rhs: alloc::boxed::Box::new(pred),
2504 });
2505 let join = FromJoin {
2506 kind: if negated {
2507 JoinKind::Left
2508 } else {
2509 JoinKind::Inner
2510 },
2511 table: TableRef {
2512 name: inner_table,
2513 alias: Some(fresh.clone()),
2514 as_of_segment: None,
2515 unnest_expr: None,
2516 unnest_column_aliases: Vec::new(),
2517 generate_series_args: None,
2518 lateral_subquery: None,
2519 jsonb_each_text_arg: None,
2520 },
2521 on: Some(on),
2522 };
2523 let residual = if negated {
2524 let probe_key = corr_pairs[0].0.clone();
2529 Some(Expr::IsNull {
2530 expr: alloc::boxed::Box::new(Expr::Column(ColumnName {
2531 qualifier: Some(fresh),
2532 name: probe_key,
2533 })),
2534 negated: false,
2535 })
2536 } else {
2537 None
2538 };
2539 Some((join, residual))
2540 }
2541
2542 fn column_is_not_null(&self, table: &str, col: &str) -> bool {
2548 let Some(t) = self.active_catalog().get(table) else {
2549 return false;
2550 };
2551 let sch = t.schema();
2552 if sch
2555 .columns
2556 .iter()
2557 .find(|c| c.name.eq_ignore_ascii_case(col))
2558 .is_some_and(|c| !c.nullable)
2559 {
2560 return true;
2561 }
2562 let Some(pos) = sch
2569 .columns
2570 .iter()
2571 .position(|c| c.name.eq_ignore_ascii_case(col))
2572 else {
2573 return false;
2574 };
2575 sch.uniqueness_constraints
2576 .iter()
2577 .any(|u| u.is_primary_key && u.columns.as_slice() == [pos])
2578 }
2579
2580 fn column_is_single_unique(&self, table: &str, col: &str) -> bool {
2584 let Some(t) = self.active_catalog().get(table) else {
2585 return false;
2586 };
2587 let sch = t.schema();
2588 let Some(pos) = sch
2589 .columns
2590 .iter()
2591 .position(|c| c.name.eq_ignore_ascii_case(col))
2592 else {
2593 return false;
2594 };
2595 if sch
2596 .uniqueness_constraints
2597 .iter()
2598 .any(|u| u.columns.as_slice() == [pos])
2599 {
2600 return true;
2601 }
2602 t.index_on(pos).is_some_and(|idx| idx.is_unique)
2603 }
2604}
2605
2606fn proj_has_disqualifying_shape(
2618 e: &Expr,
2619 inner_alias: &str,
2620 outer_aliases: &alloc::collections::BTreeSet<String>,
2621) -> bool {
2622 match e {
2623 Expr::AggregateOrdered { .. }
2624 | Expr::WindowFunction { .. }
2625 | Expr::ScalarSubquery(_)
2626 | Expr::Exists { .. } => true,
2627 Expr::FunctionCall { name, args } => {
2628 if aggregate::is_aggregate_name(name) {
2629 return true;
2630 }
2631 args.iter()
2632 .any(|a| proj_has_disqualifying_shape(a, inner_alias, outer_aliases))
2633 }
2634 Expr::Column(c) => {
2635 if let Some(q) = c.qualifier.as_deref() {
2641 outer_aliases.contains(&q.to_ascii_lowercase())
2642 && !q.eq_ignore_ascii_case(inner_alias)
2643 } else {
2644 false
2645 }
2646 }
2647 Expr::Binary { lhs, rhs, .. } => {
2648 proj_has_disqualifying_shape(lhs, inner_alias, outer_aliases)
2649 || proj_has_disqualifying_shape(rhs, inner_alias, outer_aliases)
2650 }
2651 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2652 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2653 }
2654 Expr::Like { expr, pattern, .. } => {
2655 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2656 || proj_has_disqualifying_shape(pattern, inner_alias, outer_aliases)
2657 }
2658 Expr::InList { expr, list, .. } => {
2659 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2660 || list
2661 .iter()
2662 .any(|it| proj_has_disqualifying_shape(it, inner_alias, outer_aliases))
2663 }
2664 Expr::Case {
2665 operand,
2666 branches,
2667 else_branch,
2668 } => {
2669 operand
2670 .as_ref()
2671 .is_some_and(|o| proj_has_disqualifying_shape(o, inner_alias, outer_aliases))
2672 || branches.iter().any(|(w, t)| {
2673 proj_has_disqualifying_shape(w, inner_alias, outer_aliases)
2674 || proj_has_disqualifying_shape(t, inner_alias, outer_aliases)
2675 })
2676 || else_branch
2677 .as_ref()
2678 .is_some_and(|b| proj_has_disqualifying_shape(b, inner_alias, outer_aliases))
2679 }
2680 Expr::ArraySubscript { target, index } => {
2681 proj_has_disqualifying_shape(target, inner_alias, outer_aliases)
2682 || proj_has_disqualifying_shape(index, inner_alias, outer_aliases)
2683 }
2684 _ => false,
2685 }
2686}
2687
2688fn disambiguate_stmt_unqualified_columns(
2696 stmt: &mut SelectStatement,
2697 owner: &alloc::collections::BTreeMap<String, String>,
2698) {
2699 for item in &mut stmt.items {
2700 if let SelectItem::Expr { expr, .. } = item {
2701 disambiguate_expr_unqualified_columns(expr, owner);
2702 }
2703 }
2704 if let Some(from) = &mut stmt.from {
2705 for j in &mut from.joins {
2706 if let Some(on) = &mut j.on {
2707 disambiguate_expr_unqualified_columns(on, owner);
2708 }
2709 }
2710 }
2711 if let Some(g) = &mut stmt.group_by {
2712 for e in g.iter_mut() {
2713 disambiguate_expr_unqualified_columns(e, owner);
2714 }
2715 }
2716 if let Some(h) = &mut stmt.having {
2717 disambiguate_expr_unqualified_columns(h, owner);
2718 }
2719 for ob in &mut stmt.order_by {
2720 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
2721 }
2722}
2723
2724fn disambiguate_expr_unqualified_columns(
2725 e: &mut Expr,
2726 owner: &alloc::collections::BTreeMap<String, String>,
2727) {
2728 match e {
2729 Expr::Column(c) => {
2730 if c.qualifier.is_none()
2731 && let Some(alias) = owner.get(&c.name.to_ascii_lowercase())
2732 {
2733 c.qualifier = Some(alias.clone());
2734 }
2735 }
2736 Expr::Binary { lhs, rhs, .. } => {
2737 disambiguate_expr_unqualified_columns(lhs, owner);
2738 disambiguate_expr_unqualified_columns(rhs, owner);
2739 }
2740 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2741 disambiguate_expr_unqualified_columns(expr, owner);
2742 }
2743 Expr::FunctionCall { args, .. } => {
2744 for a in args.iter_mut() {
2745 disambiguate_expr_unqualified_columns(a, owner);
2746 }
2747 }
2748 Expr::AggregateOrdered {
2749 call,
2750 order_by,
2751 filter,
2752 ..
2753 } => {
2754 disambiguate_expr_unqualified_columns(call, owner);
2755 for ob in order_by.iter_mut() {
2756 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
2757 }
2758 if let Some(f) = filter {
2759 disambiguate_expr_unqualified_columns(f, owner);
2760 }
2761 }
2762 Expr::Like { expr, pattern, .. } => {
2763 disambiguate_expr_unqualified_columns(expr, owner);
2764 disambiguate_expr_unqualified_columns(pattern, owner);
2765 }
2766 Expr::InList { expr, list, .. } => {
2767 disambiguate_expr_unqualified_columns(expr, owner);
2768 for it in list.iter_mut() {
2769 disambiguate_expr_unqualified_columns(it, owner);
2770 }
2771 }
2772 Expr::Case {
2773 operand,
2774 branches,
2775 else_branch,
2776 } => {
2777 if let Some(o) = operand {
2778 disambiguate_expr_unqualified_columns(o, owner);
2779 }
2780 for (w, t) in branches.iter_mut() {
2781 disambiguate_expr_unqualified_columns(w, owner);
2782 disambiguate_expr_unqualified_columns(t, owner);
2783 }
2784 if let Some(eb) = else_branch {
2785 disambiguate_expr_unqualified_columns(eb, owner);
2786 }
2787 }
2788 Expr::ArraySubscript { target, index } => {
2789 disambiguate_expr_unqualified_columns(target, owner);
2790 disambiguate_expr_unqualified_columns(index, owner);
2791 }
2792 _ => {}
2794 }
2795}
2796
2797fn expr_is_all_inner(e: &Expr, inner_alias: &str) -> bool {
2798 let mut cols: Vec<ColumnName> = Vec::new();
2799 let mut subs: Vec<&SelectStatement> = Vec::new();
2800 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |s| subs.push(s));
2801 subs.is_empty()
2802 && cols.iter().all(|c| {
2803 c.qualifier
2804 .as_deref()
2805 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
2806 })
2807}
2808
2809fn rename_qualifier(e: &mut Expr, from: &str, to: &str) {
2813 match e {
2814 Expr::Column(c) => {
2815 if c.qualifier
2816 .as_deref()
2817 .is_some_and(|q| q.eq_ignore_ascii_case(from))
2818 {
2819 c.qualifier = Some(to.into());
2820 }
2821 }
2822 Expr::Binary { lhs, rhs, .. } => {
2823 rename_qualifier(lhs, from, to);
2824 rename_qualifier(rhs, from, to);
2825 }
2826 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2827 rename_qualifier(expr, from, to);
2828 }
2829 Expr::FunctionCall { args, .. } => {
2830 for a in args.iter_mut() {
2831 rename_qualifier(a, from, to);
2832 }
2833 }
2834 Expr::Like { expr, pattern, .. } => {
2835 rename_qualifier(expr, from, to);
2836 rename_qualifier(pattern, from, to);
2837 }
2838 Expr::InList { expr, list, .. } => {
2839 rename_qualifier(expr, from, to);
2840 for it in list.iter_mut() {
2841 rename_qualifier(it, from, to);
2842 }
2843 }
2844 Expr::Case {
2845 operand,
2846 branches,
2847 else_branch,
2848 } => {
2849 if let Some(o) = operand {
2850 rename_qualifier(o, from, to);
2851 }
2852 for (w, t) in branches.iter_mut() {
2853 rename_qualifier(w, from, to);
2854 rename_qualifier(t, from, to);
2855 }
2856 if let Some(eb) = else_branch {
2857 rename_qualifier(eb, from, to);
2858 }
2859 }
2860 _ => {}
2861 }
2862}
2863
2864fn is_correlation_error(e: &EngineError) -> bool {
2869 matches!(
2870 e,
2871 EngineError::Eval(
2872 eval::EvalError::ColumnNotFound { .. } | eval::EvalError::UnknownQualifier { .. }
2873 )
2874 )
2875}
2876
2877#[derive(Debug, Clone)]
2902pub struct ScalarPkProbeFastPath {
2903 pub outer_pos: usize,
2906 pub inner_table_name: String,
2908 pub inner_pos: usize,
2910 pub table_idx: usize,
2919}
2920
2921impl ScalarPkProbeFastPath {
2922 pub fn probe(&self, row: &Row<'static>) -> Value<'static> {
2926 let outer_int = match row.values.get(self.outer_pos) {
2933 Some(Value::BigInt(n)) => *n,
2934 Some(Value::Int(n)) => i64::from(*n),
2935 Some(Value::SmallInt(n)) => i64::from(*n),
2936 Some(Value::Null) | None => return Value::Int(0),
2937 _ => return Value::Int(0),
2938 };
2939 SCALARSQ_PK_PROBE_PLAN_OUTER_INT.store(outer_int, core::sync::atomic::Ordering::Relaxed);
2940 SCALARSQ_PK_PROBE_PLAN_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2941 Value::Int(0)
2949 }
2950}
2951
2952pub static SCALARSQ_PK_PROBE_PLAN_FIRED: core::sync::atomic::AtomicU64 =
2954 core::sync::atomic::AtomicU64::new(0);
2955pub static SCALARSQ_PK_PROBE_PLAN_OUTER_INT: core::sync::atomic::AtomicI64 =
2956 core::sync::atomic::AtomicI64::new(0);
2957
2958impl Engine {
2966 pub(crate) fn probe_with_pk_fast_path(
2970 &self,
2971 plan: &ScalarPkProbeFastPath,
2972 row: &Row<'static>,
2973 ) -> Value<'static> {
2974 let outer_int = match row.values.get(plan.outer_pos) {
2975 Some(Value::BigInt(n)) => *n,
2976 Some(Value::Int(n)) => i64::from(*n),
2977 Some(Value::SmallInt(n)) => i64::from(*n),
2978 Some(Value::Null) | None => return Value::Int(0),
2979 _ => return Value::Int(0),
2980 };
2981 let Some(inner_table) = self.active_catalog().tables_at(plan.table_idx) else {
2987 return Value::Int(0);
2988 };
2989 let Some(idx) = inner_table.index_on(plan.inner_pos) else {
2990 return Value::Int(0);
2991 };
2992 let Some(key) = spg_storage::IndexKey::from_value(&Value::BigInt(outer_int)) else {
2993 return Value::Int(0);
2994 };
2995 let hit = !idx.lookup_eq(&key).is_empty();
2996 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2997 Value::Int(i32::from(hit))
2998 }
2999
3000 pub(crate) fn analyse_scalar_count_pk_eq_probe(
3006 &self,
3007 inner: &SelectStatement,
3008 outer_schema: &[spg_storage::ColumnSchema],
3009 outer_alias: &str,
3010 ) -> Option<ScalarPkProbeFastPath> {
3011 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
3012 if !inner.ctes.is_empty()
3013 || !inner.unions.is_empty()
3014 || inner.group_by.is_some()
3015 || inner.having.is_some()
3016 || inner.distinct
3017 || !inner.order_by.is_empty()
3018 || inner.limit.is_some()
3019 || inner.offset.is_some()
3020 || inner.items.len() != 1
3021 {
3022 return None;
3023 }
3024 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3025 return None;
3026 };
3027 let is_count_shape = match expr {
3028 Expr::FunctionCall { name, args } => {
3029 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
3030 || name.eq_ignore_ascii_case("count")
3031 }
3032 _ => false,
3033 };
3034 if !is_count_shape {
3035 return None;
3036 }
3037 let from = inner.from.as_ref()?;
3038 if !from.joins.is_empty()
3039 || from.primary.lateral_subquery.is_some()
3040 || from.primary.unnest_expr.is_some()
3041 || from.primary.generate_series_args.is_some()
3042 || from.primary.as_of_segment.is_some()
3043 {
3044 return None;
3045 }
3046 let inner_table_name = from.primary.name.clone();
3047 let inner_alias = from
3048 .primary
3049 .alias
3050 .as_deref()
3051 .unwrap_or(inner_table_name.as_str());
3052 let where_expr = inner.where_.as_ref()?;
3053 let Expr::Binary {
3054 lhs,
3055 op: BinOp::Eq,
3056 rhs,
3057 } = where_expr
3058 else {
3059 return None;
3060 };
3061 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
3062 return None;
3063 };
3064 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
3065 if x.qualifier
3066 .as_deref()
3067 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3068 {
3069 Some((x.name.clone(), y.clone()))
3070 } else {
3071 None
3072 }
3073 };
3074 let (inner_col_name, outer_col) = pick(a, b).or_else(|| pick(b, a))?;
3075 if let Some(q) = outer_col.qualifier.as_deref()
3078 && !q.eq_ignore_ascii_case(outer_alias)
3079 {
3080 return None;
3081 }
3082 let outer_pos = outer_schema
3083 .iter()
3084 .position(|c| c.name.eq_ignore_ascii_case(&outer_col.name))?;
3085 let catalog = self.active_catalog();
3090 let table_idx = catalog.tables_position_of(inner_table_name.as_str())?;
3091 let inner_table = catalog.tables_at(table_idx)?;
3092 let inner_schema_ref = inner_table.schema();
3093 let inner_pos = inner_schema_ref
3094 .columns
3095 .iter()
3096 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))?;
3097 if !matches!(
3098 inner_schema_ref.columns[inner_pos].ty,
3099 spg_storage::DataType::BigInt
3100 | spg_storage::DataType::Int
3101 | spg_storage::DataType::SmallInt
3102 ) {
3103 return None;
3104 }
3105 if !inner_schema_ref
3106 .uniqueness_constraints
3107 .iter()
3108 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
3109 {
3110 return None;
3111 }
3112 Some(ScalarPkProbeFastPath {
3113 outer_pos,
3114 inner_table_name,
3115 inner_pos,
3116 table_idx,
3117 })
3118 }
3119
3120 pub(crate) fn try_scalar_count_pk_eq_probe(
3121 &self,
3122 inner: &SelectStatement,
3123 row: &Row<'static>,
3124 ctx: &EvalContext<'_>,
3125 ) -> Result<Option<Value<'static>>, EngineError> {
3126 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
3127 if !inner.ctes.is_empty()
3128 || !inner.unions.is_empty()
3129 || inner.group_by.is_some()
3130 || inner.having.is_some()
3131 || inner.distinct
3132 || !inner.order_by.is_empty()
3133 || inner.limit.is_some()
3134 || inner.offset.is_some()
3135 || inner.items.len() != 1
3136 {
3137 return Ok(None);
3138 }
3139 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3140 return Ok(None);
3141 };
3142 let is_count_shape = match expr {
3143 Expr::FunctionCall { name, args } => {
3144 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
3145 || name.eq_ignore_ascii_case("count")
3146 }
3147 _ => false,
3148 };
3149 if !is_count_shape {
3150 return Ok(None);
3151 }
3152 let Some(from) = &inner.from else {
3153 return Ok(None);
3154 };
3155 if !from.joins.is_empty()
3156 || from.primary.lateral_subquery.is_some()
3157 || from.primary.unnest_expr.is_some()
3158 || from.primary.generate_series_args.is_some()
3159 || from.primary.as_of_segment.is_some()
3160 {
3161 return Ok(None);
3162 }
3163 let inner_table_name = from.primary.name.as_str();
3164 let inner_alias = from.primary.alias.as_deref().unwrap_or(inner_table_name);
3165 let Some(where_expr) = &inner.where_ else {
3166 return Ok(None);
3167 };
3168 let Expr::Binary {
3169 lhs,
3170 op: BinOp::Eq,
3171 rhs,
3172 } = where_expr
3173 else {
3174 return Ok(None);
3175 };
3176 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
3177 return Ok(None);
3178 };
3179 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
3180 if x.qualifier
3181 .as_deref()
3182 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3183 {
3184 Some((x.name.clone(), y.clone()))
3185 } else {
3186 None
3187 }
3188 };
3189 let Some((inner_col_name, outer_col)) = pick(a, b).or_else(|| pick(b, a)) else {
3190 return Ok(None);
3191 };
3192 let catalog = self.active_catalog();
3193 let Some(inner_table) = catalog.get(inner_table_name) else {
3194 return Ok(None);
3195 };
3196 let inner_schema = inner_table.schema();
3197 let Some(inner_pos) = inner_schema
3198 .columns
3199 .iter()
3200 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))
3201 else {
3202 return Ok(None);
3203 };
3204 if !matches!(
3205 inner_schema.columns[inner_pos].ty,
3206 spg_storage::DataType::BigInt
3207 | spg_storage::DataType::Int
3208 | spg_storage::DataType::SmallInt
3209 ) {
3210 return Ok(None);
3211 }
3212 if !inner_schema
3213 .uniqueness_constraints
3214 .iter()
3215 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
3216 {
3217 return Ok(None);
3218 }
3219 let outer_val = match eval::eval_expr(&Expr::Column(outer_col), row, ctx) {
3220 Ok(v) => v,
3221 Err(_) => return Ok(None),
3222 };
3223 let outer_int = match outer_val {
3224 Value::BigInt(n) => n,
3225 Value::Int(n) => i64::from(n),
3226 Value::SmallInt(n) => i64::from(n),
3227 Value::Null => return Ok(Some(Value::Int(0))),
3228 _ => return Ok(None),
3229 };
3230 let Some(idx) = inner_table.index_on(inner_pos) else {
3231 return Ok(None);
3232 };
3233 let Some(key) = spg_storage::IndexKey::from_value(&Value::BigInt(outer_int)) else {
3234 return Ok(None);
3235 };
3236 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3237 let hit = !idx.lookup_eq(&key).is_empty();
3238 Ok(Some(Value::Int(i32::from(hit))))
3239 }
3240}
3241
3242pub static SCALARSQ_PK_PROBE_FIRED: core::sync::atomic::AtomicU64 =
3243 core::sync::atomic::AtomicU64::new(0);
3244
3245fn scalar_subquery_empty_default(inner: &SelectStatement) -> Value<'static> {
3251 use spg_sql::ast::SelectItem;
3252 if inner.items.len() != 1 {
3253 return Value::Null;
3254 }
3255 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3256 return Value::Null;
3257 };
3258 fn is_count(e: &Expr) -> bool {
3259 match e {
3260 Expr::FunctionCall { name, .. } => {
3263 name.eq_ignore_ascii_case("count") || name.eq_ignore_ascii_case("count_star")
3264 }
3265 Expr::AggregateOrdered { call, .. } => is_count(call),
3266 _ => false,
3267 }
3268 }
3269 if is_count(expr) {
3270 Value::Int(0)
3271 } else {
3272 Value::Null
3273 }
3274}
3275
3276pub(crate) fn select_is_correlated(s: &SelectStatement) -> bool {
3277 use spg_sql::ast::SelectItem;
3278 let Some(from) = &s.from else {
3279 let mut qualified = false;
3282 for item in &s.items {
3283 if let SelectItem::Expr { expr, .. } = item {
3284 visit_expr_columns_and_subqueries(
3285 expr,
3286 &mut |c| {
3287 if c.qualifier.is_some() {
3288 qualified = true;
3289 }
3290 },
3291 &mut |_| {},
3292 );
3293 }
3294 }
3295 return qualified;
3296 };
3297 if from.primary.lateral_subquery.is_some() {
3300 return false;
3301 }
3302 let mut inner: Vec<&str> = Vec::new();
3303 if let Some(a) = &from.primary.alias {
3304 inner.push(a.as_str());
3305 }
3306 if !from.primary.name.is_empty() {
3307 inner.push(from.primary.name.as_str());
3308 }
3309 for j in &from.joins {
3310 if j.table.lateral_subquery.is_some() {
3311 return false;
3312 }
3313 if let Some(a) = &j.table.alias {
3314 inner.push(a.as_str());
3315 }
3316 if !j.table.name.is_empty() {
3317 inner.push(j.table.name.as_str());
3318 }
3319 }
3320 let mut exprs: Vec<&Expr> = Vec::new();
3324 for item in &s.items {
3325 if let SelectItem::Expr { expr, .. } = item {
3326 exprs.push(expr);
3327 }
3328 }
3329 if let Some(w) = &s.where_ {
3330 exprs.push(w);
3331 }
3332 for j in &from.joins {
3333 if let Some(on) = &j.on {
3334 exprs.push(on);
3335 }
3336 }
3337 if let Some(gs) = &s.group_by {
3338 for g in gs {
3339 exprs.push(g);
3340 }
3341 }
3342 if let Some(h) = &s.having {
3343 exprs.push(h);
3344 }
3345 for o in &s.order_by {
3346 exprs.push(&o.expr);
3347 }
3348 let mut correlated = false;
3349 for e in exprs {
3350 visit_expr_columns_and_subqueries(
3351 e,
3352 &mut |c| {
3353 if let Some(q) = &c.qualifier
3354 && !inner.iter().any(|a| a.eq_ignore_ascii_case(q))
3355 {
3356 correlated = true;
3357 }
3358 },
3359 &mut |_| {},
3360 );
3361 }
3362 correlated
3363}
3364
3365pub(crate) fn collect_scalar_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
3369 match e {
3370 Expr::ScalarSubquery(s) => out.push(s),
3371 Expr::Exists { .. } | Expr::InSubquery { .. } => {}
3372 Expr::Binary { lhs, rhs, .. } => {
3373 collect_scalar_subqueries(lhs, out);
3374 collect_scalar_subqueries(rhs, out);
3375 }
3376 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3377 collect_scalar_subqueries(expr, out);
3378 }
3379 Expr::Like { expr, pattern, .. } => {
3380 collect_scalar_subqueries(expr, out);
3381 collect_scalar_subqueries(pattern, out);
3382 }
3383 Expr::FunctionCall { args, .. } => {
3384 for a in args {
3385 collect_scalar_subqueries(a, out);
3386 }
3387 }
3388 Expr::AggregateOrdered { call, order_by, .. } => {
3389 collect_scalar_subqueries(call, out);
3390 for o in order_by {
3391 collect_scalar_subqueries(&o.expr, out);
3392 }
3393 }
3394 Expr::Case {
3395 operand,
3396 branches,
3397 else_branch,
3398 } => {
3399 if let Some(op) = operand {
3400 collect_scalar_subqueries(op, out);
3401 }
3402 for (w, t) in branches {
3403 collect_scalar_subqueries(w, out);
3404 collect_scalar_subqueries(t, out);
3405 }
3406 if let Some(eb) = else_branch {
3407 collect_scalar_subqueries(eb, out);
3408 }
3409 }
3410 Expr::ArraySubscript { target, index } => {
3411 collect_scalar_subqueries(target, out);
3412 collect_scalar_subqueries(index, out);
3413 }
3414 Expr::InList { expr, list, .. } => {
3415 collect_scalar_subqueries(expr, out);
3416 for item in list {
3417 collect_scalar_subqueries(item, out);
3418 }
3419 }
3420 _ => {}
3421 }
3422}
3423
3424fn hollow_scalar_subqueries(e: &mut Expr) {
3427 match e {
3428 Expr::ScalarSubquery(s) => {
3429 let hollow = SelectStatement {
3430 items: Vec::new(),
3431 ..SelectStatement::default()
3432 };
3433 **s = hollow;
3434 }
3435 Expr::Exists { .. } | Expr::InSubquery { .. } => {}
3436 Expr::Binary { lhs, rhs, .. } => {
3437 hollow_scalar_subqueries(lhs);
3438 hollow_scalar_subqueries(rhs);
3439 }
3440 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3441 hollow_scalar_subqueries(expr);
3442 }
3443 Expr::Like { expr, pattern, .. } => {
3444 hollow_scalar_subqueries(expr);
3445 hollow_scalar_subqueries(pattern);
3446 }
3447 Expr::FunctionCall { args, .. } => {
3448 for a in args.iter_mut() {
3449 hollow_scalar_subqueries(a);
3450 }
3451 }
3452 Expr::AggregateOrdered { call, order_by, .. } => {
3453 hollow_scalar_subqueries(call);
3454 for o in order_by.iter_mut() {
3455 hollow_scalar_subqueries(&mut o.expr);
3456 }
3457 }
3458 Expr::Case {
3459 operand,
3460 branches,
3461 else_branch,
3462 } => {
3463 if let Some(op) = operand {
3464 hollow_scalar_subqueries(op);
3465 }
3466 for (w, t) in branches.iter_mut() {
3467 hollow_scalar_subqueries(w);
3468 hollow_scalar_subqueries(t);
3469 }
3470 if let Some(eb) = else_branch {
3471 hollow_scalar_subqueries(eb);
3472 }
3473 }
3474 Expr::ArraySubscript { target, index } => {
3475 hollow_scalar_subqueries(target);
3476 hollow_scalar_subqueries(index);
3477 }
3478 Expr::InList { expr, list, .. } => {
3479 hollow_scalar_subqueries(expr);
3480 for item in list.iter_mut() {
3481 hollow_scalar_subqueries(item);
3482 }
3483 }
3484 _ => {}
3485 }
3486}
3487
3488fn splice_planned_subqueries(
3493 e: &mut Expr,
3494 plan: &[Option<alloc::rc::Rc<memoize::GroupMap>>],
3495 idx: &mut usize,
3496 row: &Row<'static>,
3497 ctx: &EvalContext<'_>,
3498) -> Result<bool, EngineError> {
3499 match e {
3500 Expr::ScalarSubquery(_) => {
3501 let Some(Some(gm)) = plan.get(*idx) else {
3502 return Ok(false);
3503 };
3504 *idx += 1;
3505 let (outer_col, map, empty_default) = gm.as_ref();
3512 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
3513 .map_err(EngineError::Eval)?;
3514 let v = if matches!(key_v, Value::Null) {
3515 Value::Null
3516 } else {
3517 map.get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
3518 .cloned()
3519 .unwrap_or_else(|| empty_default.clone())
3520 };
3521 *e = value_to_literal_expr(v)?;
3522 Ok(true)
3523 }
3524 Expr::Exists { .. } | Expr::InSubquery { .. } => Ok(true),
3525 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_subqueries(lhs, plan, idx, row, ctx)?
3526 && splice_planned_subqueries(rhs, plan, idx, row, ctx)?),
3527 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3528 splice_planned_subqueries(expr, plan, idx, row, ctx)
3529 }
3530 Expr::Like { expr, pattern, .. } => {
3531 Ok(splice_planned_subqueries(expr, plan, idx, row, ctx)?
3532 && splice_planned_subqueries(pattern, plan, idx, row, ctx)?)
3533 }
3534 Expr::FunctionCall { args, .. } => {
3535 for a in args.iter_mut() {
3536 if !splice_planned_subqueries(a, plan, idx, row, ctx)? {
3537 return Ok(false);
3538 }
3539 }
3540 Ok(true)
3541 }
3542 Expr::AggregateOrdered { call, order_by, .. } => {
3543 if !splice_planned_subqueries(call, plan, idx, row, ctx)? {
3544 return Ok(false);
3545 }
3546 for o in order_by.iter_mut() {
3547 if !splice_planned_subqueries(&mut o.expr, plan, idx, row, ctx)? {
3548 return Ok(false);
3549 }
3550 }
3551 Ok(true)
3552 }
3553 Expr::Case {
3554 operand,
3555 branches,
3556 else_branch,
3557 } => {
3558 if let Some(op) = operand {
3559 if !splice_planned_subqueries(op, plan, idx, row, ctx)? {
3560 return Ok(false);
3561 }
3562 }
3563 for (w, t) in branches.iter_mut() {
3564 if !splice_planned_subqueries(w, plan, idx, row, ctx)?
3565 || !splice_planned_subqueries(t, plan, idx, row, ctx)?
3566 {
3567 return Ok(false);
3568 }
3569 }
3570 if let Some(eb) = else_branch {
3571 if !splice_planned_subqueries(eb, plan, idx, row, ctx)? {
3572 return Ok(false);
3573 }
3574 }
3575 Ok(true)
3576 }
3577 Expr::ArraySubscript { target, index } => {
3578 Ok(splice_planned_subqueries(target, plan, idx, row, ctx)?
3579 && splice_planned_subqueries(index, plan, idx, row, ctx)?)
3580 }
3581 Expr::InList { expr, list, .. } => {
3582 if !splice_planned_subqueries(expr, plan, idx, row, ctx)? {
3583 return Ok(false);
3584 }
3585 for item in list.iter_mut() {
3586 if !splice_planned_subqueries(item, plan, idx, row, ctx)? {
3587 return Ok(false);
3588 }
3589 }
3590 Ok(true)
3591 }
3592 _ => Ok(true),
3593 }
3594}
3595
3596pub(crate) fn collect_exists_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
3603 match e {
3604 Expr::Exists { subquery, .. } => out.push(subquery.as_ref()),
3605 Expr::ScalarSubquery(_) | Expr::InSubquery { .. } => {}
3606 Expr::Binary { lhs, rhs, .. } => {
3607 collect_exists_subqueries(lhs, out);
3608 collect_exists_subqueries(rhs, out);
3609 }
3610 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3611 collect_exists_subqueries(expr, out);
3612 }
3613 Expr::Like { expr, pattern, .. } => {
3614 collect_exists_subqueries(expr, out);
3615 collect_exists_subqueries(pattern, out);
3616 }
3617 Expr::FunctionCall { args, .. } => {
3618 for a in args {
3619 collect_exists_subqueries(a, out);
3620 }
3621 }
3622 Expr::AggregateOrdered { call, order_by, .. } => {
3623 collect_exists_subqueries(call, out);
3624 for o in order_by {
3625 collect_exists_subqueries(&o.expr, out);
3626 }
3627 }
3628 Expr::Case {
3629 operand,
3630 branches,
3631 else_branch,
3632 } => {
3633 if let Some(op) = operand {
3634 collect_exists_subqueries(op, out);
3635 }
3636 for (w, t) in branches {
3637 collect_exists_subqueries(w, out);
3638 collect_exists_subqueries(t, out);
3639 }
3640 if let Some(eb) = else_branch {
3641 collect_exists_subqueries(eb, out);
3642 }
3643 }
3644 Expr::ArraySubscript { target, index } => {
3645 collect_exists_subqueries(target, out);
3646 collect_exists_subqueries(index, out);
3647 }
3648 Expr::InList { expr, list, .. } => {
3649 collect_exists_subqueries(expr, out);
3650 for item in list {
3651 collect_exists_subqueries(item, out);
3652 }
3653 }
3654 _ => {}
3655 }
3656}
3657
3658fn splice_planned_exists(
3666 e: &mut Expr,
3667 plan: &[Option<alloc::rc::Rc<memoize::ExistsSet>>],
3668 idx: &mut usize,
3669 row: &Row<'static>,
3670 ctx: &EvalContext<'_>,
3671) -> Result<bool, EngineError> {
3672 match e {
3673 Expr::Exists { negated, .. } => {
3674 let Some(Some(es)) = plan.get(*idx) else {
3675 return Ok(false);
3676 };
3677 *idx += 1;
3678 let (outer_cols, set) = es.as_ref();
3679 let mut key_vals: Vec<Value<'static>> = Vec::with_capacity(outer_cols.len());
3680 let mut any_null = false;
3681 for oc in outer_cols {
3682 let v = eval::eval_expr(&Expr::Column(oc.clone()), row, ctx)
3683 .map_err(EngineError::Eval)?;
3684 if matches!(v, Value::Null) {
3685 any_null = true;
3686 }
3687 key_vals.push(v);
3688 }
3689 let present = !any_null && set.contains(&aggregate::encode_key(&key_vals));
3690 let bit = if *negated { !present } else { present };
3691 *e = Expr::Literal(Literal::Bool(bit));
3692 Ok(true)
3693 }
3694 Expr::ScalarSubquery(_) | Expr::InSubquery { .. } => Ok(true),
3695 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_exists(lhs, plan, idx, row, ctx)?
3696 && splice_planned_exists(rhs, plan, idx, row, ctx)?),
3697 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3698 splice_planned_exists(expr, plan, idx, row, ctx)
3699 }
3700 Expr::Like { expr, pattern, .. } => Ok(splice_planned_exists(expr, plan, idx, row, ctx)?
3701 && splice_planned_exists(pattern, plan, idx, row, ctx)?),
3702 Expr::FunctionCall { args, .. } => {
3703 for a in args.iter_mut() {
3704 if !splice_planned_exists(a, plan, idx, row, ctx)? {
3705 return Ok(false);
3706 }
3707 }
3708 Ok(true)
3709 }
3710 Expr::AggregateOrdered { call, order_by, .. } => {
3711 if !splice_planned_exists(call, plan, idx, row, ctx)? {
3712 return Ok(false);
3713 }
3714 for o in order_by.iter_mut() {
3715 if !splice_planned_exists(&mut o.expr, plan, idx, row, ctx)? {
3716 return Ok(false);
3717 }
3718 }
3719 Ok(true)
3720 }
3721 Expr::Case {
3722 operand,
3723 branches,
3724 else_branch,
3725 } => {
3726 if let Some(op) = operand {
3727 if !splice_planned_exists(op, plan, idx, row, ctx)? {
3728 return Ok(false);
3729 }
3730 }
3731 for (w, t) in branches.iter_mut() {
3732 if !splice_planned_exists(w, plan, idx, row, ctx)?
3733 || !splice_planned_exists(t, plan, idx, row, ctx)?
3734 {
3735 return Ok(false);
3736 }
3737 }
3738 if let Some(eb) = else_branch {
3739 if !splice_planned_exists(eb, plan, idx, row, ctx)? {
3740 return Ok(false);
3741 }
3742 }
3743 Ok(true)
3744 }
3745 Expr::ArraySubscript { target, index } => {
3746 Ok(splice_planned_exists(target, plan, idx, row, ctx)?
3747 && splice_planned_exists(index, plan, idx, row, ctx)?)
3748 }
3749 Expr::InList { expr, list, .. } => {
3750 if !splice_planned_exists(expr, plan, idx, row, ctx)? {
3751 return Ok(false);
3752 }
3753 for item in list.iter_mut() {
3754 if !splice_planned_exists(item, plan, idx, row, ctx)? {
3755 return Ok(false);
3756 }
3757 }
3758 Ok(true)
3759 }
3760 _ => Ok(true),
3761 }
3762}
3763
3764const INLIST_SET_THRESHOLD: usize = 64;
3768
3769fn expr_may_use_in_set(e: &Expr) -> bool {
3773 match e {
3774 Expr::InList { list, .. } => list.len() >= INLIST_SET_THRESHOLD,
3775 Expr::Binary {
3776 lhs,
3777 op: BinOp::And,
3778 rhs,
3779 } => expr_may_use_in_set(lhs) || expr_may_use_in_set(rhs),
3780 _ => false,
3781 }
3782}
3783
3784pub(crate) fn build_in_list_set(list: &[Expr]) -> Option<memoize::InListSetEntry> {
3787 let mut has_null = false;
3788 let mut ints: hashbrown::HashSet<i64> = hashbrown::HashSet::with_capacity(list.len());
3789 let mut texts: hashbrown::HashSet<String> = hashbrown::HashSet::with_capacity(list.len());
3790 for item in list {
3791 let Expr::Literal(lit) = item else {
3792 return None;
3793 };
3794 match lit {
3795 Literal::Null => has_null = true,
3796 Literal::Integer(i) => {
3797 ints.insert(*i);
3798 }
3799 Literal::String(s) => {
3800 texts.insert(s.clone());
3801 }
3802 _ => return None,
3803 }
3804 if !ints.is_empty() && !texts.is_empty() {
3805 return None;
3806 }
3807 }
3808 let set = if !ints.is_empty() {
3809 memoize::InListSet::Int(ints)
3810 } else if !texts.is_empty() {
3811 memoize::InListSet::Text(texts)
3812 } else {
3813 return None;
3814 };
3815 Some(memoize::InListSetEntry { set, has_null })
3816}
3817
3818fn eval_with_in_sets(
3824 e: &Expr,
3825 row: &Row<'static>,
3826 ctx: &EvalContext<'_>,
3827 m: &mut memoize::MemoizeCache,
3828) -> Result<Value<'static>, EngineError> {
3829 match e {
3830 Expr::Binary {
3831 lhs,
3832 op: BinOp::And,
3833 rhs,
3834 } => {
3835 let l = eval_with_in_sets(lhs, row, ctx, m)?;
3838 let r = eval_with_in_sets(rhs, row, ctx, m)?;
3839 eval::and_3vl(l, r).map_err(EngineError::Eval)
3840 }
3841 Expr::InList {
3842 expr: lhs,
3843 list,
3844 negated,
3845 } if list.len() >= INLIST_SET_THRESHOLD => {
3846 let key = core::ptr::from_ref::<Expr>(e) as usize;
3847 let Some(entry) = m
3848 .in_sets
3849 .entry(key)
3850 .or_insert_with(|| build_in_list_set(list))
3851 else {
3852 return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval);
3853 };
3854 let needle = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
3855 let contained = match (&needle, &entry.set) {
3856 (Value::Null, _) => return Ok(Value::Null),
3859 (Value::SmallInt(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
3860 (Value::Int(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
3861 (Value::BigInt(n), memoize::InListSet::Int(s)) => s.contains(n),
3862 (Value::Text(t), memoize::InListSet::Text(s)) => s.contains(t.as_ref()),
3863 _ => return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
3866 };
3867 let inner = if contained {
3868 Value::Bool(true)
3869 } else if entry.has_null {
3870 Value::Null
3871 } else {
3872 Value::Bool(false)
3873 };
3874 Ok(match (negated, inner) {
3875 (true, Value::Bool(b)) => Value::Bool(!b),
3876 (_, v) => v,
3877 })
3878 }
3879 _ => eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
3880 }
3881}
3882
3883fn substitute_outer_columns(stmt: &mut SelectStatement, row: &Row<'static>, ctx: &EvalContext<'_>) {
3884 let outer_alias = ctx.table_alias.unwrap_or("");
3891 substitute_in_select(stmt, row, ctx, outer_alias);
3892}
3893
3894fn substitute_in_select(
3895 stmt: &mut SelectStatement,
3896 row: &Row<'static>,
3897 ctx: &EvalContext<'_>,
3898 outer_alias: &str,
3899) {
3900 for item in &mut stmt.items {
3901 if let SelectItem::Expr { expr, .. } = item {
3902 substitute_in_expr(expr, row, ctx, outer_alias);
3903 }
3904 }
3905 if let Some(w) = &mut stmt.where_ {
3906 substitute_in_expr(w, row, ctx, outer_alias);
3907 }
3908 if let Some(gs) = &mut stmt.group_by {
3909 for g in gs {
3910 substitute_in_expr(g, row, ctx, outer_alias);
3911 }
3912 }
3913 if let Some(h) = &mut stmt.having {
3914 substitute_in_expr(h, row, ctx, outer_alias);
3915 }
3916 for o in &mut stmt.order_by {
3917 substitute_in_expr(&mut o.expr, row, ctx, outer_alias);
3918 }
3919 for (_, peer) in &mut stmt.unions {
3920 substitute_in_select(peer, row, ctx, outer_alias);
3921 }
3922}
3923
3924fn substitute_in_expr(e: &mut Expr, row: &Row<'static>, ctx: &EvalContext<'_>, outer_alias: &str) {
3925 if let Expr::Column(c) = e
3931 && c.qualifier.is_none()
3932 && (c.name.starts_with("__grp_") || c.name.starts_with("__agg_"))
3933 && let Some(idx) = ctx.columns.iter().position(|sc| sc.name == c.name)
3934 {
3935 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
3936 if let Ok(lit) = value_to_literal_expr(v) {
3937 *e = lit;
3938 return;
3939 }
3940 }
3941 if let Expr::Column(c) = e
3942 && let Some(qual) = &c.qualifier
3943 {
3944 let idx = if !outer_alias.is_empty() && qual.eq_ignore_ascii_case(outer_alias) {
3948 ctx.columns
3949 .iter()
3950 .position(|sc| sc.name.eq_ignore_ascii_case(&c.name))
3951 } else {
3952 None
3953 }
3954 .or_else(|| {
3955 let composite = alloc::format!("{qual}.{name}", name = c.name);
3956 ctx.columns
3957 .iter()
3958 .position(|sc| sc.name.eq_ignore_ascii_case(&composite))
3959 });
3960 if let Some(idx) = idx {
3961 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
3962 if let Ok(lit) = value_to_literal_expr(v) {
3963 *e = lit;
3964 return;
3965 }
3966 }
3967 }
3968 match e {
3969 Expr::AggregateOrdered { call, order_by, .. } => {
3970 substitute_in_expr(call, row, ctx, outer_alias);
3971 for o in order_by.iter_mut() {
3972 substitute_in_expr(&mut o.expr, row, ctx, outer_alias);
3973 }
3974 }
3975 Expr::Binary { lhs, rhs, .. } => {
3976 substitute_in_expr(lhs, row, ctx, outer_alias);
3977 substitute_in_expr(rhs, row, ctx, outer_alias);
3978 }
3979 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3980 substitute_in_expr(expr, row, ctx, outer_alias);
3981 }
3982 Expr::Like { expr, pattern, .. } => {
3983 substitute_in_expr(expr, row, ctx, outer_alias);
3984 substitute_in_expr(pattern, row, ctx, outer_alias);
3985 }
3986 Expr::FunctionCall { args, .. } => {
3987 for a in args {
3988 substitute_in_expr(a, row, ctx, outer_alias);
3989 }
3990 }
3991 Expr::Extract { source, .. } => substitute_in_expr(source, row, ctx, outer_alias),
3992 Expr::WindowFunction {
3993 args,
3994 partition_by,
3995 order_by,
3996 ..
3997 } => {
3998 for a in args {
3999 substitute_in_expr(a, row, ctx, outer_alias);
4000 }
4001 for p in partition_by {
4002 substitute_in_expr(p, row, ctx, outer_alias);
4003 }
4004 for (o, _, _) in order_by {
4005 substitute_in_expr(o, row, ctx, outer_alias);
4006 }
4007 }
4008 Expr::ScalarSubquery(s) => substitute_in_select(s, row, ctx, outer_alias),
4009 Expr::Exists { subquery, .. } | Expr::InSubquery { subquery, .. } => {
4010 substitute_in_select(subquery, row, ctx, outer_alias);
4011 }
4012 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => {}
4013 Expr::Array(items) => {
4014 for elem in items {
4015 substitute_in_expr(elem, row, ctx, outer_alias);
4016 }
4017 }
4018 Expr::ArraySubscript { target, index } => {
4019 substitute_in_expr(target, row, ctx, outer_alias);
4020 substitute_in_expr(index, row, ctx, outer_alias);
4021 }
4022 Expr::AnyAll { expr, array, .. } => {
4023 substitute_in_expr(expr, row, ctx, outer_alias);
4024 substitute_in_expr(array, row, ctx, outer_alias);
4025 }
4026 Expr::InList { expr, list, .. } => {
4027 substitute_in_expr(expr, row, ctx, outer_alias);
4028 for item in list {
4029 substitute_in_expr(item, row, ctx, outer_alias);
4030 }
4031 }
4032 Expr::Case {
4033 operand,
4034 branches,
4035 else_branch,
4036 } => {
4037 if let Some(o) = operand {
4038 substitute_in_expr(o, row, ctx, outer_alias);
4039 }
4040 for (w, t) in branches {
4041 substitute_in_expr(w, row, ctx, outer_alias);
4042 substitute_in_expr(t, row, ctx, outer_alias);
4043 }
4044 if let Some(e) = else_branch {
4045 substitute_in_expr(e, row, ctx, outer_alias);
4046 }
4047 }
4048 }
4049}
4050
4051pub fn expr_tree_has_subquery(stmt: &SelectStatement) -> bool {
4055 let mut any = false;
4056 for item in &stmt.items {
4057 if let SelectItem::Expr { expr, .. } = item {
4058 any = any || expr_has_subquery(expr);
4059 }
4060 }
4061 if let Some(w) = &stmt.where_ {
4062 any = any || expr_has_subquery(w);
4063 }
4064 if let Some(h) = &stmt.having {
4065 any = any || expr_has_subquery(h);
4066 }
4067 for o in &stmt.order_by {
4068 any = any || expr_has_subquery(&o.expr);
4069 }
4070 for (_, peer) in &stmt.unions {
4071 any = any || expr_tree_has_subquery(peer);
4072 }
4073 any
4074}
4075
4076pub(crate) fn expr_has_subquery(e: &Expr) -> bool {
4077 match e {
4078 Expr::ScalarSubquery(_) | Expr::Exists { .. } | Expr::InSubquery { .. } => true,
4079 Expr::AggregateOrdered { call, order_by, .. } => {
4080 expr_has_subquery(call) || order_by.iter().any(|o| expr_has_subquery(&o.expr))
4081 }
4082 Expr::Binary { lhs, rhs, .. } => expr_has_subquery(lhs) || expr_has_subquery(rhs),
4083 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
4084 expr_has_subquery(expr)
4085 }
4086 Expr::FunctionCall { args, .. } => args.iter().any(expr_has_subquery),
4087 Expr::Like { expr, pattern, .. } => expr_has_subquery(expr) || expr_has_subquery(pattern),
4088 Expr::Extract { source, .. } => expr_has_subquery(source),
4089 Expr::WindowFunction {
4090 args,
4091 partition_by,
4092 order_by,
4093 ..
4094 } => {
4095 args.iter().any(expr_has_subquery)
4096 || partition_by.iter().any(expr_has_subquery)
4097 || order_by.iter().any(|(e, _, _)| expr_has_subquery(e))
4098 }
4099 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => false,
4100 Expr::Array(items) => items.iter().any(expr_has_subquery),
4101 Expr::ArraySubscript { target, index } => {
4102 expr_has_subquery(target) || expr_has_subquery(index)
4103 }
4104 Expr::AnyAll { expr, array, .. } => expr_has_subquery(expr) || expr_has_subquery(array),
4105 Expr::InList { expr, list, .. } => {
4106 expr_has_subquery(expr) || list.iter().any(expr_has_subquery)
4107 }
4108 Expr::Case {
4109 operand,
4110 branches,
4111 else_branch,
4112 } => {
4113 operand.as_deref().is_some_and(expr_has_subquery)
4114 || branches
4115 .iter()
4116 .any(|(w, t)| expr_has_subquery(w) || expr_has_subquery(t))
4117 || else_branch.as_deref().is_some_and(expr_has_subquery)
4118 }
4119 }
4120}