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_CANDIDATE_COUNT: core::sync::atomic::AtomicU64 =
52 core::sync::atomic::AtomicU64::new(0);
53pub static EXISTS_PULLUP_BAIL_INNER_SHAPE: core::sync::atomic::AtomicU64 =
56 core::sync::atomic::AtomicU64::new(0);
57pub static EXISTS_PULLUP_BAIL_INNER_FROM: core::sync::atomic::AtomicU64 =
59 core::sync::atomic::AtomicU64::new(0);
60pub static EXISTS_PULLUP_BAIL_NO_WHERE: core::sync::atomic::AtomicU64 =
62 core::sync::atomic::AtomicU64::new(0);
63pub static EXISTS_PULLUP_BAIL_RESIDUAL_NOT_INNER: core::sync::atomic::AtomicU64 =
65 core::sync::atomic::AtomicU64::new(0);
66pub static EXISTS_PULLUP_BAIL_NO_CORR: core::sync::atomic::AtomicU64 =
68 core::sync::atomic::AtomicU64::new(0);
69pub static EXISTS_PULLUP_BAIL_MULTICOL_DISABLED: core::sync::atomic::AtomicU64 =
71 core::sync::atomic::AtomicU64::new(0);
72pub static EXISTS_PULLUP_BAIL_UNIQUE_KEY_MISSING: core::sync::atomic::AtomicU64 =
74 core::sync::atomic::AtomicU64::new(0);
75pub static EXISTS_PULLUP_FIRE_COUNT: core::sync::atomic::AtomicU64 =
76 core::sync::atomic::AtomicU64::new(0);
77pub static EXISTS_BATCH_FIRE_COUNT: core::sync::atomic::AtomicU64 =
78 core::sync::atomic::AtomicU64::new(0);
79pub static EXISTS_BATCH_FALL_THROUGH_COUNT: core::sync::atomic::AtomicU64 =
80 core::sync::atomic::AtomicU64::new(0);
81
82pub static EXISTS_PULLUP_MULTICOL_DISABLE: core::sync::atomic::AtomicBool =
89 core::sync::atomic::AtomicBool::new(false);
90
91use spg_storage::{Row, Value};
92
93use crate::eval::{self, EvalContext};
94use crate::substitute::value_to_literal_expr;
95use crate::{
96 CancelToken, Engine, EngineError, QueryResult, aggregate, memoize, order_by_value_cmp, reorder,
97 value_cmp, visit_expr_columns_and_subqueries,
98};
99
100impl Engine {
101 pub(crate) fn eval_expr_with_correlated(
109 &self,
110 expr: &Expr,
111 row: &Row<'static>,
112 ctx: &EvalContext<'_>,
113 cancel: CancelToken<'_>,
114 mut memo: Option<&mut memoize::MemoizeCache>,
115 ) -> Result<Value<'static>, EngineError> {
116 let has_subq = if let Some(m) = memo.as_deref_mut() {
121 let key = core::ptr::from_ref::<Expr>(expr) as usize;
122 match m.has_subquery.get(&key) {
123 Some(b) => *b,
124 None => {
125 let b = expr_has_subquery(expr);
126 m.has_subquery.insert(key, b);
127 b
128 }
129 }
130 } else {
131 expr_has_subquery(expr)
132 };
133 if !has_subq {
134 if let Some(m) = memo.as_deref_mut()
139 && expr_may_use_in_set(expr)
140 {
141 return eval_with_in_sets(expr, row, ctx, m);
142 }
143 return eval::eval_expr(expr, row, ctx).map_err(EngineError::Eval);
144 }
145 if let Some(m) = memo.as_deref_mut() {
151 let key = core::ptr::from_ref::<Expr>(expr) as usize;
152 let plan_hit = m.expr_plans.contains_key(&key);
157 let exists_plan_hit = m.exists_plans.contains_key(&key);
158 let mut subs: Vec<&SelectStatement> = Vec::new();
159 let mut exists_subs: Vec<&SelectStatement> = Vec::new();
160 if !plan_hit {
161 collect_scalar_subqueries(expr, &mut subs);
162 }
163 if !exists_plan_hit {
164 collect_exists_subqueries(expr, &mut exists_subs);
165 }
166 if !plan_hit && !subs.is_empty() {
167 let mut plan: Vec<Option<alloc::rc::Rc<memoize::GroupMap>>> =
168 Vec::with_capacity(subs.len());
169 for sub in &subs {
170 let repr = alloc::format!("{sub}");
171 if !m.group_maps.contains_key(&repr) {
172 let built = self
173 .try_batch_correlated_scalar(sub, None, cancel)?
174 .map(alloc::rc::Rc::new);
175 m.group_maps.insert(repr.clone(), built);
176 }
177 plan.push(m.group_maps.get(&repr).cloned().flatten());
178 }
179 let mut template = expr.clone();
180 hollow_scalar_subqueries(&mut template);
181 m.expr_plans.insert(key, (subs.len(), plan, template));
182 }
183 if !exists_plan_hit && !exists_subs.is_empty() {
189 let mut eplan: Vec<Option<alloc::rc::Rc<memoize::ExistsSet>>> =
190 Vec::with_capacity(exists_subs.len());
191 for sub in &exists_subs {
192 let built = self
193 .try_batch_correlated_exists(sub, cancel)?
194 .map(alloc::rc::Rc::new);
195 if built.is_some() {
196 EXISTS_BATCH_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
197 } else {
198 EXISTS_BATCH_FALL_THROUGH_COUNT
199 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
200 }
201 eplan.push(built);
202 }
203 m.exists_plans.insert(key, eplan);
204 }
205 let scalar_ready = m
210 .expr_plans
211 .get(&key)
212 .map(|(_, plan, _)| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
213 .unwrap_or(false);
214 let exists_ready = m
215 .exists_plans
216 .get(&key)
217 .map(|plan| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
218 .unwrap_or(false);
219 if scalar_ready || exists_ready {
220 let scalar_plan = m
228 .expr_plans
229 .get(&key)
230 .map(|(_, plan, template)| (plan.clone(), template.clone()));
231 let exists_plan = m.exists_plans.get(&key).cloned();
232 let mut e = match &scalar_plan {
233 Some((_, template)) => template.clone(),
234 None => expr.clone(),
235 };
236 let mut all_ok = true;
237 if let Some((plan, _)) = &scalar_plan {
238 let mut idx = 0usize;
239 all_ok &= splice_planned_subqueries(&mut e, plan, &mut idx, row, ctx)?;
240 }
241 if all_ok && let Some(plan) = &exists_plan {
242 let mut idx = 0usize;
243 all_ok &= splice_planned_exists(&mut e, plan, &mut idx, row, ctx)?;
244 }
245 if all_ok {
246 if expr_has_subquery(&e) {
247 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
248 }
249 return eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval);
250 }
251 }
252 }
253 let mut e = expr.clone();
254 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
255 eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval)
256 }
257
258 fn resolve_correlated_in_expr(
259 &self,
260 e: &mut Expr,
261 row: &Row<'static>,
262 ctx: &EvalContext<'_>,
263 cancel: CancelToken<'_>,
264 mut memo: Option<&mut memoize::MemoizeCache>,
265 ) -> Result<(), EngineError> {
266 match e {
267 Expr::AggregateOrdered { call, order_by, .. } => {
268 self.resolve_correlated_in_expr(call, row, ctx, cancel, memo.as_deref_mut())?;
269 for o in order_by.iter_mut() {
270 self.resolve_correlated_in_expr(
271 &mut o.expr,
272 row,
273 ctx,
274 cancel,
275 memo.as_deref_mut(),
276 )?;
277 }
278 }
279 Expr::ScalarSubquery(inner) => {
280 if memo.is_some() {
293 let ptr_key = core::ptr::from_ref::<SelectStatement>(&**inner) as usize;
294 let entry_known = memo
295 .as_ref()
296 .is_some_and(|m| m.group_maps_by_ptr.contains_key(&ptr_key));
297 if !entry_known {
298 let built = self
299 .try_batch_correlated_scalar(inner, None, cancel)?
300 .map(alloc::rc::Rc::new);
301 if let Some(m) = memo.as_deref_mut() {
302 m.group_maps_by_ptr.insert(ptr_key, built);
303 }
304 }
305 if let Some(m) = memo.as_deref_mut()
306 && let Some(Some(gm)) = m.group_maps_by_ptr.get(&ptr_key)
307 {
308 let (outer_col, map, empty_default) = gm.as_ref();
309 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
310 .map_err(EngineError::Eval)?;
311 let v = if matches!(key_v, Value::Null) {
318 Value::Null
319 } else {
320 map.get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
321 .cloned()
322 .unwrap_or_else(|| empty_default.clone())
323 };
324 *e = value_to_literal_expr(v)?;
325 return Ok(());
326 }
327 }
328 let cache_key = memo.as_ref().map(|_| memoize::CacheKey {
333 subquery_repr: alloc::format!("{}", **inner),
334 outer_values: row.values.iter().cloned().map(Value::into_owned).collect(),
335 });
336 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key.as_ref())
337 && let Some(cached) = cache.get(k)
338 {
339 *e = value_to_literal_expr(cached)?;
340 return Ok(());
341 }
342 if let Some(v) = self.try_scalar_count_pk_eq_probe(inner, row, ctx)? {
354 *e = value_to_literal_expr(v)?;
355 return Ok(());
356 }
357 let mut s = (**inner).clone();
358 substitute_outer_columns(&mut s, row, ctx);
359 let r = self.exec_select_cancel(&s, cancel)?;
360 let QueryResult::Rows { rows, .. } = r else {
361 return Err(EngineError::Unsupported(
362 "scalar subquery: inner did not return rows".into(),
363 ));
364 };
365 let value = match rows.as_slice() {
366 [] => Value::Null,
367 [r0] => r0.values.first().cloned().unwrap_or(Value::Null),
368 _ => {
369 return Err(EngineError::Unsupported(alloc::format!(
370 "scalar subquery returned {} rows; expected 0 or 1",
371 rows.len()
372 )));
373 }
374 };
375 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key) {
376 cache.insert(k, value.clone());
377 }
378 *e = value_to_literal_expr(value)?;
379 }
380 Expr::Exists { subquery, negated } => {
381 if memo.is_some() {
388 let repr = alloc::format!("{}", **subquery);
389 let known = memo
390 .as_ref()
391 .is_some_and(|m| m.exists_sets.contains_key(&repr));
392 if !known {
393 let built = self
394 .try_batch_correlated_exists(subquery, cancel)?
395 .map(alloc::rc::Rc::new);
396 if let Some(m) = memo.as_deref_mut() {
397 m.exists_sets.insert(repr.clone(), built);
398 }
399 }
400 if let Some(m) = memo.as_deref_mut()
401 && let Some(Some(es)) = m.exists_sets.get(&repr)
402 {
403 let (outer_cols, set) = es.as_ref();
404 let mut key_vals: Vec<Value<'static>> =
405 Vec::with_capacity(outer_cols.len());
406 let mut any_null = false;
407 for oc in outer_cols {
408 let v = eval::eval_expr(&Expr::Column(oc.clone()), row, ctx)
409 .map_err(EngineError::Eval)?;
410 if matches!(v, Value::Null) {
411 any_null = true;
412 }
413 key_vals.push(v);
414 }
415 let present = !any_null && set.contains(&aggregate::encode_key(&key_vals));
417 let bit = if *negated { !present } else { present };
418 *e = Expr::Literal(Literal::Bool(bit));
419 return Ok(());
420 }
421 }
422 let mut s = (**subquery).clone();
423 substitute_outer_columns(&mut s, row, ctx);
424 let r = self.exec_select_cancel(&s, cancel)?;
425 let exists = matches!(r, QueryResult::Rows { rows, .. } if !rows.is_empty());
426 let bit = if *negated { !exists } else { exists };
427 *e = Expr::Literal(Literal::Bool(bit));
428 }
429 Expr::InSubquery {
430 expr: lhs,
431 subquery,
432 negated,
433 } => {
434 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
435 let lhs_val = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
436 let mut s = (**subquery).clone();
437 substitute_outer_columns(&mut s, row, ctx);
438 let r = self.exec_select_cancel(&s, cancel)?;
439 let QueryResult::Rows { columns, rows, .. } = r else {
440 return Err(EngineError::Unsupported(
441 "IN-subquery: inner did not return rows".into(),
442 ));
443 };
444 if columns.len() != 1 {
445 return Err(EngineError::Unsupported(alloc::format!(
446 "IN-subquery must project exactly one column; got {}",
447 columns.len()
448 )));
449 }
450 let mut found = false;
451 let mut any_null = false;
452 for r0 in rows {
453 let v = r0.values.into_iter().next().unwrap_or(Value::Null);
454 if v.is_null() {
455 any_null = true;
456 continue;
457 }
458 if value_cmp(&v, &lhs_val) == core::cmp::Ordering::Equal {
459 found = true;
460 break;
461 }
462 }
463 let bit = if found {
464 !*negated
465 } else if any_null {
466 return Err(EngineError::Unsupported(
467 "IN-subquery with NULL in result and no match: NULL semantics not yet implemented".into(),
468 ));
469 } else {
470 *negated
471 };
472 *e = Expr::Literal(Literal::Bool(bit));
473 }
474 Expr::Binary { lhs, rhs, .. } => {
475 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
476 self.resolve_correlated_in_expr(rhs, row, ctx, cancel, memo.as_deref_mut())?;
477 }
478 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
479 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
480 }
481 Expr::Like { expr, pattern, .. } => {
482 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
483 self.resolve_correlated_in_expr(pattern, row, ctx, cancel, memo.as_deref_mut())?;
484 }
485 Expr::FunctionCall { args, .. } => {
486 for a in args {
487 self.resolve_correlated_in_expr(a, row, ctx, cancel, memo.as_deref_mut())?;
488 }
489 }
490 Expr::Extract { source, .. } => {
491 self.resolve_correlated_in_expr(source, row, ctx, cancel, memo.as_deref_mut())?;
492 }
493 Expr::WindowFunction { .. }
494 | Expr::Literal(_)
495 | Expr::Placeholder(_)
496 | Expr::Column(_) => {}
497 Expr::Array(items) => {
499 for elem in items {
500 self.resolve_correlated_in_expr(elem, row, ctx, cancel, memo.as_deref_mut())?;
501 }
502 }
503 Expr::ArraySubscript { target, index } => {
504 self.resolve_correlated_in_expr(target, row, ctx, cancel, memo.as_deref_mut())?;
505 self.resolve_correlated_in_expr(index, row, ctx, cancel, memo.as_deref_mut())?;
506 }
507 Expr::AnyAll { expr, array, .. } => {
508 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
509 self.resolve_correlated_in_expr(array, row, ctx, cancel, memo.as_deref_mut())?;
510 }
511 Expr::InList { expr, list, .. } => {
512 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
513 for item in list {
514 self.resolve_correlated_in_expr(item, row, ctx, cancel, memo.as_deref_mut())?;
515 }
516 }
517 Expr::Case {
518 operand,
519 branches,
520 else_branch,
521 } => {
522 if let Some(o) = operand {
523 self.resolve_correlated_in_expr(o, row, ctx, cancel, memo.as_deref_mut())?;
524 }
525 for (w, t) in branches {
526 self.resolve_correlated_in_expr(w, row, ctx, cancel, memo.as_deref_mut())?;
527 self.resolve_correlated_in_expr(t, row, ctx, cancel, memo.as_deref_mut())?;
528 }
529 if let Some(e) = else_branch {
530 self.resolve_correlated_in_expr(e, row, ctx, cancel, memo.as_deref_mut())?;
531 }
532 }
533 }
534 Ok(())
535 }
536
537 pub(crate) fn subquery_replacement(
547 &self,
548 e: &Expr,
549 cancel: CancelToken<'_>,
550 ) -> Result<Option<Expr>, EngineError> {
551 match e {
552 Expr::ScalarSubquery(inner) => {
553 if select_is_correlated(inner) {
558 return Ok(None);
559 }
560 let mut s = (**inner).clone();
561 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 QueryResult::Rows { rows, .. } = r else {
570 return Err(EngineError::Unsupported(
571 "scalar subquery: inner statement did not return rows".into(),
572 ));
573 };
574 let value = match rows.as_slice() {
575 [] => Value::Null,
576 [row] => row.values.first().cloned().unwrap_or(Value::Null),
577 _ => {
578 return Err(EngineError::Unsupported(alloc::format!(
579 "scalar subquery returned {} rows; expected 0 or 1",
580 rows.len()
581 )));
582 }
583 };
584 Ok(Some(value_to_literal_expr(value)?))
585 }
586 Expr::Exists { subquery, negated } => {
587 if select_is_correlated(subquery) {
588 return Ok(None);
589 }
590 let mut s = (**subquery).clone();
591 self.resolve_select_subqueries(&mut s, cancel)?;
592 let r = match self.exec_bare_select_cancel(&s, cancel) {
593 Ok(r) => r,
594 Err(e) if is_correlation_error(&e) => return Ok(None),
595 Err(e) => return Err(e),
596 };
597 let exists = match r {
598 QueryResult::Rows { rows, .. } => !rows.is_empty(),
599 QueryResult::CommandOk { .. } => false,
600 };
601 let bit = if *negated { !exists } else { exists };
602 Ok(Some(Expr::Literal(Literal::Bool(bit))))
603 }
604 Expr::InSubquery {
605 expr,
606 subquery,
607 negated,
608 } => {
609 if select_is_correlated(subquery) {
610 return Ok(None);
611 }
612 let mut s = (**subquery).clone();
613 self.resolve_select_subqueries(&mut s, cancel)?;
614 let r = match self.exec_bare_select_cancel(&s, cancel) {
615 Ok(r) => r,
616 Err(e) if is_correlation_error(&e) => return Ok(None),
617 Err(e) => return Err(e),
618 };
619 let QueryResult::Rows { columns, rows, .. } = r else {
620 return Err(EngineError::Unsupported(
621 "IN-subquery: inner statement did not return rows".into(),
622 ));
623 };
624 if columns.len() != 1 {
625 return Err(EngineError::Unsupported(alloc::format!(
626 "IN-subquery must project exactly one column; got {}",
627 columns.len()
628 )));
629 }
630 let mut list: Vec<Expr> = Vec::with_capacity(rows.len());
636 for row in rows {
637 let v = row.values.into_iter().next().unwrap_or(Value::Null);
638 list.push(value_to_literal_expr(v)?);
639 }
640 Ok(Some(Expr::InList {
641 expr: expr.clone(),
642 list,
643 negated: *negated,
644 }))
645 }
646 _ => Ok(None),
647 }
648 }
649}
650
651impl Engine {
652 pub(crate) fn try_batch_correlated_scalar(
661 &self,
662 inner: &SelectStatement,
663 restrict: Option<(&[Row<'static>], &EvalContext<'_>)>,
664 cancel: CancelToken<'_>,
665 ) -> Result<Option<memoize::GroupMap>, EngineError> {
666 use spg_sql::ast::{BinOp, SelectItem as SI};
667 if !inner.ctes.is_empty()
668 || !inner.unions.is_empty()
669 || inner.group_by.is_some()
670 || inner.having.is_some()
671 || inner.distinct
672 || inner.items.len() != 1
673 || inner.order_by.len() > 1
674 || inner.offset.is_some()
675 {
676 return Ok(None);
677 }
678 if let Some(le) = inner.limit
680 && le.as_literal() != Some(1)
681 {
682 return Ok(None);
683 }
684 let Some(from) = &inner.from else {
685 return Ok(None);
686 };
687 if from.primary.lateral_subquery.is_some() || from.primary.unnest_expr.is_some() {
688 return Ok(None);
689 }
690 let mut inner_aliases: Vec<String> = Vec::new();
692 inner_aliases.push(
693 from.primary
694 .alias
695 .clone()
696 .unwrap_or_else(|| from.primary.name.clone()),
697 );
698 for j in &from.joins {
699 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
700 return Ok(None);
701 }
702 inner_aliases.push(
703 j.table
704 .alias
705 .clone()
706 .unwrap_or_else(|| j.table.name.clone()),
707 );
708 }
709 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
710 match &c.qualifier {
711 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
712 None => false,
713 }
714 };
715 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
716 match &c.qualifier {
717 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
718 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
721 }
722 };
723 let all_inner = |e: &Expr| -> bool {
726 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
727 let mut subs: Vec<&SelectStatement> = Vec::new();
728 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
729 subs.push(sub)
730 });
731 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
732 };
733 let Some(w) = &inner.where_ else {
734 return Ok(None);
735 };
736 let conjuncts = reorder::split_and_conjunctions(w);
737 let mut corr: Option<(spg_sql::ast::ColumnName, spg_sql::ast::ColumnName)> = None; let mut rest: Vec<&Expr> = Vec::new();
739 for c in conjuncts {
740 if let Expr::Binary {
741 lhs,
742 op: BinOp::Eq,
743 rhs,
744 } = c
745 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
746 {
747 let pair = if is_inner(a) && is_outer(b) {
748 Some((a.clone(), b.clone()))
749 } else if is_inner(b) && is_outer(a) {
750 Some((b.clone(), a.clone()))
751 } else {
752 None
753 };
754 if let Some(p) = pair {
755 if corr.is_some() {
756 return Ok(None); }
758 corr = Some(p);
759 continue;
760 }
761 }
762 if !all_inner(c) {
763 return Ok(None);
764 }
765 rest.push(c);
766 }
767 let Some((inner_col, outer_col)) = corr else {
768 return Ok(None);
769 };
770 let SI::Expr { expr: out_expr, .. } = &inner.items[0] else {
771 return Ok(None);
772 };
773 if !all_inner(out_expr) {
774 return Ok(None);
775 }
776 let order = inner.order_by.first();
777 if let Some(o) = order
778 && !all_inner(&o.expr)
779 {
780 return Ok(None);
781 }
782 let mut batch = inner.clone();
785 batch.limit = None;
786 batch.offset = None;
787 batch.order_by = Vec::new();
788 batch.where_ = rest
789 .iter()
790 .map(|e| (*e).clone())
791 .reduce(|a, b| Expr::Binary {
792 lhs: alloc::boxed::Box::new(a),
793 op: BinOp::And,
794 rhs: alloc::boxed::Box::new(b),
795 });
796 let mut items: Vec<SI> = alloc::vec![SI::Expr {
797 expr: Expr::Column(inner_col.clone()),
798 alias: None,
799 }];
800 if let Some(o) = order {
801 items.push(SI::Expr {
802 expr: o.expr.clone(),
803 alias: None,
804 });
805 }
806 items.push(SI::Expr {
807 expr: out_expr.clone(),
808 alias: None,
809 });
810 batch.items = items;
811 if aggregate::contains_aggregate(out_expr) {
823 batch.group_by = Some(alloc::vec![Expr::Column(inner_col.clone())]);
824 }
825 let keyed: Option<(&[Row<'static>], &EvalContext<'_>)> =
848 restrict.and_then(|(rows, rctx)| {
849 let driver_name: &str = if from.joins.is_empty() {
851 from.primary.name.as_str()
852 } else {
853 let q = inner_col.qualifier.as_deref()?;
854 let primary_alias = from
855 .primary
856 .alias
857 .as_deref()
858 .unwrap_or(from.primary.name.as_str());
859 if primary_alias.eq_ignore_ascii_case(q) {
860 from.primary.name.as_str()
861 } else {
862 from.joins
863 .iter()
864 .find(|j| {
865 j.table
866 .alias
867 .as_deref()
868 .unwrap_or(j.table.name.as_str())
869 .eq_ignore_ascii_case(q)
870 })
871 .map(|j| j.table.name.as_str())?
872 }
873 };
874 let table = self.active_catalog().get(driver_name)?;
875 let pos = table
876 .schema()
877 .columns
878 .iter()
879 .position(|c| c.name.eq_ignore_ascii_case(&inner_col.name))?;
880 table.index_on(pos)?;
881 if rows.len().saturating_mul(4) >= table.row_count() {
896 return None;
897 }
898 if !from.joins.is_empty() {
904 let driver_alias = inner_col.qualifier.as_deref()?;
905 if !reorder::drive_from(&mut batch, driver_alias) {
906 return None;
907 }
908 }
909 Some((rows, rctx))
910 });
911 let rows = if let Some((restrict_rows, rctx)) = keyed {
912 BATCHED_SCALAR_KEYED_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
913 let mut seen: alloc::collections::BTreeSet<String> =
928 alloc::collections::BTreeSet::new();
929 let mut key_lits: Vec<Expr> = Vec::new();
930 for srow in restrict_rows {
931 cancel.check()?;
932 let kv = eval::eval_expr(&Expr::Column(outer_col.clone()), srow, rctx)
933 .map_err(EngineError::Eval)?;
934 if matches!(kv, Value::Null) {
935 continue;
936 }
937 if !seen.insert(aggregate::encode_key(core::slice::from_ref(&kv))) {
938 continue;
939 }
940 key_lits.push(value_to_literal_expr(kv)?);
941 }
942 if key_lits.is_empty() {
943 Vec::new()
944 } else {
945 let in_pred = Expr::InList {
946 expr: alloc::boxed::Box::new(Expr::Column(inner_col.clone())),
947 list: key_lits,
948 negated: false,
949 };
950 let mut probe = batch.clone();
951 probe.where_ = Some(match probe.where_.take() {
952 Some(w) => Expr::Binary {
953 lhs: alloc::boxed::Box::new(w),
954 op: BinOp::And,
955 rhs: alloc::boxed::Box::new(in_pred),
956 },
957 None => in_pred,
958 });
959 BATCHED_SCALAR_KEYED_PROBE_COUNT
960 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
961 if let QueryResult::Rows { rows, .. } = self.exec_select_cancel(&probe, cancel)? {
962 rows
963 } else {
964 Vec::new()
965 }
966 }
967 } else {
968 BATCHED_SCALAR_FALL_THROUGH_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
969 let r = self.exec_select_cancel(&batch, cancel)?;
970 let QueryResult::Rows { rows, .. } = r else {
971 return Ok(None);
972 };
973 rows
974 };
975 let has_order = order.is_some();
976 let (desc, nf) = order
977 .map(|o| (o.desc, o.nulls_first))
978 .unwrap_or((false, None));
979 let mut best: alloc::collections::BTreeMap<String, (Option<Value>, Value)> =
980 alloc::collections::BTreeMap::new();
981 for row in rows {
982 let key_v = row.values.first().cloned().unwrap_or(Value::Null);
983 if matches!(key_v, Value::Null) {
984 continue;
985 }
986 let key = aggregate::encode_key(core::slice::from_ref(&key_v));
987 let (ord_v, out_v) = if has_order {
988 (
989 Some(row.values.get(1).cloned().unwrap_or(Value::Null)),
990 row.values.get(2).cloned().unwrap_or(Value::Null),
991 )
992 } else {
993 (None, row.values.get(1).cloned().unwrap_or(Value::Null))
994 };
995 match best.get(&key) {
996 None => {
997 best.insert(key, (ord_v, out_v));
998 }
999 Some((cur_ord, _)) if has_order => {
1000 let cand = ord_v.clone().unwrap_or(Value::Null);
1004 let cur = cur_ord.clone().unwrap_or(Value::Null);
1005 if order_by_value_cmp(desc, nf, &cand, &cur) == core::cmp::Ordering::Less {
1006 best.insert(key, (ord_v, out_v));
1007 }
1008 }
1009 Some(_) => {} }
1011 }
1012 let map = best.into_iter().map(|(k, (_, v))| (k, v)).collect();
1013 let empty_default = scalar_subquery_empty_default(inner);
1018 Ok(Some((outer_col, map, empty_default)))
1019 }
1020}
1021
1022impl Engine {
1023 pub(crate) fn try_batch_correlated_exists(
1042 &self,
1043 inner: &SelectStatement,
1044 cancel: CancelToken<'_>,
1045 ) -> Result<Option<memoize::ExistsSet>, EngineError> {
1046 use spg_sql::ast::SelectItem as SI;
1047 if !inner.ctes.is_empty()
1048 || !inner.unions.is_empty()
1049 || inner.group_by.is_some()
1050 || inner.having.is_some()
1051 || inner.distinct
1052 {
1053 return Ok(None);
1054 }
1055 let Some(from) = &inner.from else {
1056 return Ok(None);
1057 };
1058 if from.primary.lateral_subquery.is_some()
1059 || from.primary.unnest_expr.is_some()
1060 || from.primary.generate_series_args.is_some()
1061 || from.primary.as_of_segment.is_some()
1062 {
1063 return Ok(None);
1064 }
1065 let mut inner_aliases: Vec<String> = Vec::new();
1066 inner_aliases.push(
1067 from.primary
1068 .alias
1069 .clone()
1070 .unwrap_or_else(|| from.primary.name.clone()),
1071 );
1072 for j in &from.joins {
1073 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
1074 return Ok(None);
1075 }
1076 inner_aliases.push(
1077 j.table
1078 .alias
1079 .clone()
1080 .unwrap_or_else(|| j.table.name.clone()),
1081 );
1082 }
1083 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
1084 match &c.qualifier {
1085 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1086 None => false,
1087 }
1088 };
1089 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
1090 match &c.qualifier {
1091 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1092 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
1093 }
1094 };
1095 let all_inner = |e: &Expr| -> bool {
1096 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1097 let mut subs: Vec<&SelectStatement> = Vec::new();
1098 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
1099 subs.push(sub)
1100 });
1101 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
1102 };
1103 let Some(w) = &inner.where_ else {
1104 return Ok(None);
1105 };
1106 let conjuncts = reorder::split_and_conjunctions(w);
1107 let mut inner_keys: Vec<spg_sql::ast::ColumnName> = Vec::new();
1108 let mut outer_cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1109 let mut rest: Vec<&Expr> = Vec::new();
1110 for c in conjuncts {
1111 if let Expr::Binary {
1112 lhs,
1113 op: BinOp::Eq,
1114 rhs,
1115 } = c
1116 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1117 {
1118 let pair = if is_inner(a) && is_outer(b) {
1119 Some((a.clone(), b.clone()))
1120 } else if is_inner(b) && is_outer(a) {
1121 Some((b.clone(), a.clone()))
1122 } else {
1123 None
1124 };
1125 if let Some((ic, oc)) = pair {
1126 inner_keys.push(ic);
1127 outer_cols.push(oc);
1128 continue;
1129 }
1130 }
1131 if !all_inner(c) {
1135 return Ok(None);
1136 }
1137 rest.push(c);
1138 }
1139 if inner_keys.is_empty() {
1140 return Ok(None); }
1142 let mut batch = inner.clone();
1146 batch.limit = None;
1147 batch.offset = None;
1148 batch.order_by = Vec::new();
1149 batch.distinct = false;
1150 batch.where_ = rest
1151 .iter()
1152 .map(|e| (*e).clone())
1153 .reduce(|a, b| Expr::Binary {
1154 lhs: alloc::boxed::Box::new(a),
1155 op: BinOp::And,
1156 rhs: alloc::boxed::Box::new(b),
1157 });
1158 batch.items = inner_keys
1159 .iter()
1160 .map(|c| SI::Expr {
1161 expr: Expr::Column(c.clone()),
1162 alias: None,
1163 })
1164 .collect();
1165 let r = self.exec_select_cancel(&batch, cancel)?;
1166 let QueryResult::Rows { rows, .. } = r else {
1167 return Ok(None);
1168 };
1169 let n = inner_keys.len();
1170 let mut set: alloc::collections::BTreeSet<String> = alloc::collections::BTreeSet::new();
1171 for row in rows {
1172 let keys = row.values.get(..n).unwrap_or(&row.values);
1173 if keys.iter().any(|v| matches!(v, Value::Null)) {
1176 continue;
1177 }
1178 set.insert(aggregate::encode_key(keys));
1179 }
1180 Ok(Some((outer_cols, set)))
1181 }
1182}
1183
1184impl Engine {
1185 pub(crate) fn pull_up_correlated_limit_one_subqueries(
1259 &self,
1260 stmt: &mut SelectStatement,
1261 ) -> bool {
1262 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
1270 return false;
1271 }
1272 let outer_aliases: alloc::collections::BTreeSet<String> = {
1275 let from = stmt.from.as_ref().expect("from present");
1276 let mut s = alloc::collections::BTreeSet::new();
1277 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
1278 s.insert(
1279 t.alias
1280 .clone()
1281 .unwrap_or_else(|| t.name.clone())
1282 .to_ascii_lowercase(),
1283 );
1284 };
1285 push(&mut s, &from.primary);
1286 for j in &from.joins {
1287 push(&mut s, &j.table);
1288 }
1289 s
1290 };
1291 let outer_has_group_by = stmt.group_by.is_some() || stmt.group_by_all;
1292 let mut new_ctes: Vec<Cte> = Vec::new();
1293 let mut new_joins: Vec<FromJoin> = Vec::new();
1294 let cte_seed = stmt.ctes.len();
1295 for item in &mut stmt.items {
1296 if let SelectItem::Expr { expr, .. } = item {
1297 self.pull_up_walk_limit_one(
1298 expr,
1299 false,
1300 &outer_aliases,
1301 outer_has_group_by,
1302 cte_seed,
1303 &mut new_ctes,
1304 &mut new_joins,
1305 );
1306 }
1307 }
1308 if new_ctes.is_empty() {
1309 return false;
1310 }
1311 PULLUP_LIMIT1_FIRE_COUNT
1312 .fetch_add(new_ctes.len() as u64, core::sync::atomic::Ordering::Relaxed);
1313 stmt.ctes.extend(new_ctes);
1314 stmt.from
1315 .as_mut()
1316 .expect("from present")
1317 .joins
1318 .extend(new_joins);
1319 true
1320 }
1321
1322 #[allow(clippy::too_many_arguments)]
1327 fn pull_up_walk_limit_one(
1328 &self,
1329 e: &mut Expr,
1330 in_agg: bool,
1331 outer_aliases: &alloc::collections::BTreeSet<String>,
1332 outer_has_group_by: bool,
1333 cte_seed: usize,
1334 ctes_out: &mut Vec<Cte>,
1335 joins_out: &mut Vec<FromJoin>,
1336 ) {
1337 match e {
1338 Expr::ScalarSubquery(inner) => {
1339 if let Some((cte, join, cte_col)) =
1340 self.try_pull_up_limit_one(inner, outer_aliases, cte_seed + ctes_out.len())
1341 {
1342 ctes_out.push(cte);
1343 joins_out.push(join);
1344 let col_expr = Expr::Column(cte_col);
1354 *e = if outer_has_group_by && !in_agg {
1355 Expr::FunctionCall {
1356 name: "max".into(),
1357 args: alloc::vec![col_expr],
1358 }
1359 } else {
1360 col_expr
1361 };
1362 }
1363 }
1366 Expr::FunctionCall { name, args } => {
1367 let child = in_agg || aggregate::is_aggregate_name(name);
1368 for a in args.iter_mut() {
1369 self.pull_up_walk_limit_one(
1370 a,
1371 child,
1372 outer_aliases,
1373 outer_has_group_by,
1374 cte_seed,
1375 ctes_out,
1376 joins_out,
1377 );
1378 }
1379 }
1380 Expr::AggregateOrdered {
1381 call,
1382 order_by,
1383 filter,
1384 ..
1385 } => {
1386 self.pull_up_walk_limit_one(
1387 call,
1388 true,
1389 outer_aliases,
1390 outer_has_group_by,
1391 cte_seed,
1392 ctes_out,
1393 joins_out,
1394 );
1395 for o in order_by.iter_mut() {
1396 self.pull_up_walk_limit_one(
1397 &mut o.expr,
1398 true,
1399 outer_aliases,
1400 outer_has_group_by,
1401 cte_seed,
1402 ctes_out,
1403 joins_out,
1404 );
1405 }
1406 if let Some(f) = filter {
1407 self.pull_up_walk_limit_one(
1408 f,
1409 true,
1410 outer_aliases,
1411 outer_has_group_by,
1412 cte_seed,
1413 ctes_out,
1414 joins_out,
1415 );
1416 }
1417 }
1418 Expr::Binary { lhs, rhs, .. } => {
1419 self.pull_up_walk_limit_one(
1420 lhs,
1421 in_agg,
1422 outer_aliases,
1423 outer_has_group_by,
1424 cte_seed,
1425 ctes_out,
1426 joins_out,
1427 );
1428 self.pull_up_walk_limit_one(
1429 rhs,
1430 in_agg,
1431 outer_aliases,
1432 outer_has_group_by,
1433 cte_seed,
1434 ctes_out,
1435 joins_out,
1436 );
1437 }
1438 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
1439 self.pull_up_walk_limit_one(
1440 expr,
1441 in_agg,
1442 outer_aliases,
1443 outer_has_group_by,
1444 cte_seed,
1445 ctes_out,
1446 joins_out,
1447 );
1448 }
1449 Expr::Like { expr, pattern, .. } => {
1450 self.pull_up_walk_limit_one(
1451 expr,
1452 in_agg,
1453 outer_aliases,
1454 outer_has_group_by,
1455 cte_seed,
1456 ctes_out,
1457 joins_out,
1458 );
1459 self.pull_up_walk_limit_one(
1460 pattern,
1461 in_agg,
1462 outer_aliases,
1463 outer_has_group_by,
1464 cte_seed,
1465 ctes_out,
1466 joins_out,
1467 );
1468 }
1469 Expr::InList { expr, list, .. } => {
1470 self.pull_up_walk_limit_one(
1471 expr,
1472 in_agg,
1473 outer_aliases,
1474 outer_has_group_by,
1475 cte_seed,
1476 ctes_out,
1477 joins_out,
1478 );
1479 for it in list.iter_mut() {
1480 self.pull_up_walk_limit_one(
1481 it,
1482 in_agg,
1483 outer_aliases,
1484 outer_has_group_by,
1485 cte_seed,
1486 ctes_out,
1487 joins_out,
1488 );
1489 }
1490 }
1491 Expr::Case {
1492 operand,
1493 branches,
1494 else_branch,
1495 } => {
1496 if let Some(o) = operand {
1497 self.pull_up_walk_limit_one(
1498 o,
1499 in_agg,
1500 outer_aliases,
1501 outer_has_group_by,
1502 cte_seed,
1503 ctes_out,
1504 joins_out,
1505 );
1506 }
1507 for (w, t) in branches.iter_mut() {
1508 self.pull_up_walk_limit_one(
1509 w,
1510 in_agg,
1511 outer_aliases,
1512 outer_has_group_by,
1513 cte_seed,
1514 ctes_out,
1515 joins_out,
1516 );
1517 self.pull_up_walk_limit_one(
1518 t,
1519 in_agg,
1520 outer_aliases,
1521 outer_has_group_by,
1522 cte_seed,
1523 ctes_out,
1524 joins_out,
1525 );
1526 }
1527 if let Some(eb) = else_branch {
1528 self.pull_up_walk_limit_one(
1529 eb,
1530 in_agg,
1531 outer_aliases,
1532 outer_has_group_by,
1533 cte_seed,
1534 ctes_out,
1535 joins_out,
1536 );
1537 }
1538 }
1539 _ => {}
1542 }
1543 }
1544
1545 fn try_pull_up_limit_one(
1551 &self,
1552 inner: &SelectStatement,
1553 outer_aliases: &alloc::collections::BTreeSet<String>,
1554 alias_n: usize,
1555 ) -> Option<(Cte, FromJoin, ColumnName)> {
1556 return None;
1582 #[allow(unreachable_code)]
1583 if !inner.ctes.is_empty()
1585 || !inner.unions.is_empty()
1586 || inner.group_by.is_some()
1587 || inner.group_by_all
1588 || inner.having.is_some()
1589 || inner.distinct
1590 || inner.offset.is_some()
1591 || inner.items.len() != 1
1592 || inner.order_by.is_empty()
1593 {
1594 return None;
1595 }
1596 match inner.limit {
1599 Some(LimitExpr::Literal(1)) => {}
1600 _ => return None,
1601 }
1602 let from = inner.from.as_ref()?;
1603 if !from.joins.is_empty()
1606 || from.primary.lateral_subquery.is_some()
1607 || from.primary.unnest_expr.is_some()
1608 || from.primary.generate_series_args.is_some()
1609 || from.primary.as_of_segment.is_some()
1610 {
1611 return None;
1612 }
1613 let inner_table = from.primary.name.clone();
1614 let inner_alias = from
1615 .primary
1616 .alias
1617 .clone()
1618 .unwrap_or_else(|| inner_table.clone());
1619 let is_inner = |c: &ColumnName| -> bool {
1620 c.qualifier
1621 .as_deref()
1622 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
1623 };
1624 let is_outer = |c: &ColumnName| -> bool {
1625 c.qualifier
1626 .as_deref()
1627 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
1628 };
1629 let SelectItem::Expr {
1634 expr: proj_expr,
1635 alias: _,
1636 } = &inner.items[0]
1637 else {
1638 return None;
1639 };
1640 if proj_has_disqualifying_shape(proj_expr, &inner_alias, outer_aliases) {
1641 return None;
1642 }
1643 let where_ = inner.where_.as_ref()?;
1646 let mut corr: Option<(String, ColumnName)> = None;
1647 let mut non_corr: Vec<Expr> = Vec::new();
1648 for c in reorder::split_and_conjunctions(where_) {
1649 if let Expr::Binary {
1650 lhs,
1651 op: BinOp::Eq,
1652 rhs,
1653 } = c
1654 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1655 {
1656 let pair = if is_inner(a) && is_outer(b) {
1657 Some((a.name.clone(), b.clone()))
1658 } else if is_inner(b) && is_outer(a) {
1659 Some((b.name.clone(), a.clone()))
1660 } else {
1661 None
1662 };
1663 if let Some(p) = pair {
1664 if corr.is_some() {
1665 return None; }
1667 corr = Some(p);
1668 continue;
1669 }
1670 }
1671 if !expr_is_all_inner(c, &inner_alias) {
1672 return None;
1673 }
1674 non_corr.push(c.clone());
1675 }
1676 let (inner_key, outer_col) = corr?;
1677 for ob in &inner.order_by {
1680 if !expr_is_all_inner(&ob.expr, &inner_alias) {
1681 return None;
1682 }
1683 }
1684 if !expr_is_all_inner(proj_expr, &inner_alias) {
1686 return None;
1687 }
1688 let cte_name = alloc::format!("__cl1_{alias_n}");
1694 let jk_expr = Expr::Column(ColumnName {
1695 qualifier: Some(inner_alias.clone()),
1696 name: inner_key.clone(),
1697 });
1698 let argmax = Expr::ArraySubscript {
1699 target: alloc::boxed::Box::new(Expr::AggregateOrdered {
1700 call: alloc::boxed::Box::new(Expr::FunctionCall {
1701 name: "array_agg".into(),
1702 args: alloc::vec![proj_expr.clone()],
1703 }),
1704 order_by: inner.order_by.clone(),
1705 distinct: false,
1706 filter: None,
1707 }),
1708 index: alloc::boxed::Box::new(Expr::Literal(Literal::Integer(1))),
1709 };
1710 let body_where = if non_corr.is_empty() {
1711 None
1712 } else {
1713 let mut iter = non_corr.into_iter();
1714 let head = iter.next().expect("non_corr nonempty in this branch");
1715 Some(iter.fold(head, |acc, p| Expr::Binary {
1716 lhs: alloc::boxed::Box::new(acc),
1717 op: BinOp::And,
1718 rhs: alloc::boxed::Box::new(p),
1719 }))
1720 };
1721 let body = SelectStatement {
1722 ctes: Vec::new(),
1723 distinct: false,
1724 items: alloc::vec![
1725 SelectItem::Expr {
1726 expr: jk_expr.clone(),
1727 alias: Some("jk".into()),
1728 },
1729 SelectItem::Expr {
1730 expr: argmax,
1731 alias: Some("pj".into()),
1732 },
1733 ],
1734 from: Some(from.clone()),
1735 where_: body_where,
1736 group_by: Some(alloc::vec![jk_expr]),
1737 group_by_all: false,
1738 having: None,
1739 unions: Vec::new(),
1740 order_by: Vec::new(),
1741 limit: None,
1742 offset: None,
1743 limit_with_ties: false,
1744 };
1745 let cte = Cte {
1746 name: cte_name.clone(),
1747 body: spg_sql::ast::CteBody::Select(body),
1748 recursive: false,
1749 column_overrides: Vec::new(),
1750 };
1751 let join = FromJoin {
1753 kind: JoinKind::Left,
1754 table: TableRef {
1755 name: cte_name.clone(),
1756 alias: None,
1757 as_of_segment: None,
1758 unnest_expr: None,
1759 unnest_column_aliases: Vec::new(),
1760 generate_series_args: None,
1761 lateral_subquery: None,
1762 jsonb_each_text_arg: None,
1763 },
1764 on: Some(Expr::Binary {
1765 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
1766 qualifier: Some(cte_name.clone()),
1767 name: "jk".into(),
1768 })),
1769 op: BinOp::Eq,
1770 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
1771 }),
1772 };
1773 let repl = ColumnName {
1774 qualifier: Some(cte_name),
1775 name: "pj".into(),
1776 };
1777 Some((cte, join, repl))
1778 }
1779
1780 pub(crate) fn pull_up_unique_correlated_agg_subqueries(
1781 &self,
1782 stmt: &mut SelectStatement,
1783 ) -> bool {
1784 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
1785 return false;
1786 }
1787 let outer_aliases: alloc::collections::BTreeSet<String> = {
1789 let from = stmt.from.as_ref().expect("from present");
1790 let mut s = alloc::collections::BTreeSet::new();
1791 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
1792 s.insert(
1793 t.alias
1794 .clone()
1795 .unwrap_or_else(|| t.name.clone())
1796 .to_ascii_lowercase(),
1797 );
1798 };
1799 push(&mut s, &from.primary);
1800 for j in &from.joins {
1801 push(&mut s, &j.table);
1802 }
1803 s
1804 };
1805 let mut new_joins: Vec<FromJoin> = Vec::new();
1806 for item in &mut stmt.items {
1807 if let SelectItem::Expr { expr, .. } = item {
1808 self.pull_up_walk(expr, false, &outer_aliases, &mut new_joins);
1809 }
1810 }
1811 if new_joins.is_empty() {
1812 return false;
1813 }
1814 stmt.from
1815 .as_mut()
1816 .expect("from present")
1817 .joins
1818 .extend(new_joins);
1819 true
1820 }
1821
1822 fn pull_up_walk(
1827 &self,
1828 e: &mut Expr,
1829 in_agg: bool,
1830 outer_aliases: &alloc::collections::BTreeSet<String>,
1831 joins_out: &mut Vec<FromJoin>,
1832 ) {
1833 match e {
1834 Expr::ScalarSubquery(inner) => {
1835 if in_agg
1836 && let Some((join, col)) =
1837 self.try_pull_up_join(inner, outer_aliases, joins_out.len())
1838 {
1839 joins_out.push(join);
1840 *e = Expr::Column(col);
1841 }
1842 }
1845 Expr::FunctionCall { name, args } => {
1846 let child = in_agg || aggregate::is_aggregate_name(name);
1847 for a in args.iter_mut() {
1848 self.pull_up_walk(a, child, outer_aliases, joins_out);
1849 }
1850 }
1851 Expr::AggregateOrdered {
1852 call,
1853 order_by,
1854 filter,
1855 ..
1856 } => {
1857 self.pull_up_walk(call, true, outer_aliases, joins_out);
1858 for o in order_by.iter_mut() {
1859 self.pull_up_walk(&mut o.expr, true, outer_aliases, joins_out);
1860 }
1861 if let Some(f) = filter {
1862 self.pull_up_walk(f, true, outer_aliases, joins_out);
1863 }
1864 }
1865 Expr::Binary { lhs, rhs, .. } => {
1866 self.pull_up_walk(lhs, in_agg, outer_aliases, joins_out);
1867 self.pull_up_walk(rhs, in_agg, outer_aliases, joins_out);
1868 }
1869 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
1870 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1871 }
1872 Expr::Like { expr, pattern, .. } => {
1873 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1874 self.pull_up_walk(pattern, in_agg, outer_aliases, joins_out);
1875 }
1876 Expr::InList { expr, list, .. } => {
1877 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
1878 for it in list.iter_mut() {
1879 self.pull_up_walk(it, in_agg, outer_aliases, joins_out);
1880 }
1881 }
1882 Expr::Case {
1883 operand,
1884 branches,
1885 else_branch,
1886 } => {
1887 if let Some(o) = operand {
1888 self.pull_up_walk(o, in_agg, outer_aliases, joins_out);
1889 }
1890 for (w, t) in branches.iter_mut() {
1891 self.pull_up_walk(w, in_agg, outer_aliases, joins_out);
1892 self.pull_up_walk(t, in_agg, outer_aliases, joins_out);
1893 }
1894 if let Some(eb) = else_branch {
1895 self.pull_up_walk(eb, in_agg, outer_aliases, joins_out);
1896 }
1897 }
1898 _ => {}
1903 }
1904 }
1905
1906 fn try_pull_up_join(
1910 &self,
1911 inner: &SelectStatement,
1912 outer_aliases: &alloc::collections::BTreeSet<String>,
1913 alias_n: usize,
1914 ) -> Option<(FromJoin, ColumnName)> {
1915 if !inner.ctes.is_empty()
1918 || !inner.unions.is_empty()
1919 || inner.group_by.is_some()
1920 || inner.having.is_some()
1921 || inner.distinct
1922 || !inner.order_by.is_empty()
1923 || inner.limit.is_some()
1924 || inner.offset.is_some()
1925 || inner.items.len() != 1
1926 {
1927 return None;
1928 }
1929 let from = inner.from.as_ref()?;
1930 if !from.joins.is_empty()
1931 || from.primary.lateral_subquery.is_some()
1932 || from.primary.unnest_expr.is_some()
1933 || from.primary.generate_series_args.is_some()
1934 || from.primary.as_of_segment.is_some()
1935 {
1936 return None;
1937 }
1938 let inner_table = from.primary.name.clone();
1939 let inner_alias = from
1940 .primary
1941 .alias
1942 .clone()
1943 .unwrap_or_else(|| inner_table.clone());
1944 let is_inner = |c: &ColumnName| -> bool {
1945 c.qualifier
1946 .as_deref()
1947 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
1948 };
1949 let is_outer = |c: &ColumnName| -> bool {
1950 c.qualifier
1951 .as_deref()
1952 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
1953 };
1954 let SelectItem::Expr { expr: out_expr, .. } = &inner.items[0] else {
1956 return None;
1957 };
1958 let Expr::Column(out_col) = out_expr else {
1959 return None;
1960 };
1961 if !is_inner(out_col) {
1962 return None;
1963 }
1964 let w = inner.where_.as_ref()?;
1966 let mut corr: Option<(String, ColumnName)> = None;
1967 let mut rest: Vec<Expr> = Vec::new();
1968 for c in reorder::split_and_conjunctions(w) {
1969 if let Expr::Binary {
1970 lhs,
1971 op: BinOp::Eq,
1972 rhs,
1973 } = c
1974 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1975 {
1976 let pair = if is_inner(a) && is_outer(b) {
1977 Some((a.name.clone(), b.clone()))
1978 } else if is_inner(b) && is_outer(a) {
1979 Some((b.name.clone(), a.clone()))
1980 } else {
1981 None
1982 };
1983 if let Some(p) = pair {
1984 if corr.is_some() {
1985 return None; }
1987 corr = Some(p);
1988 continue;
1989 }
1990 }
1991 if !expr_is_all_inner(c, &inner_alias) {
1992 return None;
1993 }
1994 rest.push(c.clone());
1995 }
1996 let (inner_key, outer_col) = corr?;
1997 if !self.column_is_single_unique(&inner_table, &inner_key) {
2000 return None;
2001 }
2002 let fresh = alloc::format!("__plj_{alias_n}");
2004 let key_eq = Expr::Binary {
2005 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
2006 qualifier: Some(fresh.clone()),
2007 name: inner_key,
2008 })),
2009 op: BinOp::Eq,
2010 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
2011 };
2012 let on = rest
2013 .into_iter()
2014 .map(|mut e| {
2015 rename_qualifier(&mut e, &inner_alias, &fresh);
2016 e
2017 })
2018 .fold(key_eq, |acc, pred| Expr::Binary {
2019 lhs: alloc::boxed::Box::new(acc),
2020 op: BinOp::And,
2021 rhs: alloc::boxed::Box::new(pred),
2022 });
2023 let join = FromJoin {
2024 kind: JoinKind::Left,
2025 table: TableRef {
2026 name: inner_table,
2027 alias: Some(fresh.clone()),
2028 as_of_segment: None,
2029 unnest_expr: None,
2030 unnest_column_aliases: Vec::new(),
2031 generate_series_args: None,
2032 lateral_subquery: None,
2033 jsonb_each_text_arg: None,
2034 },
2035 on: Some(on),
2036 };
2037 let repl = ColumnName {
2038 qualifier: Some(fresh),
2039 name: out_col.name.clone(),
2040 };
2041 Some((join, repl))
2042 }
2043
2044 pub(crate) fn pull_up_exists_sublinks(&self, stmt: &mut SelectStatement) -> bool {
2075 if stmt.from.is_none() {
2076 return false;
2077 }
2078 let Some(where_expr) = stmt.where_.take() else {
2079 return false;
2080 };
2081 let mut collision_names: alloc::collections::BTreeSet<String> =
2092 alloc::collections::BTreeSet::new();
2093 for c in reorder::split_and_conjunctions(&where_expr) {
2094 let inner_subq: Option<&SelectStatement> = match c {
2095 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2096 Expr::Unary {
2097 op: UnOp::Not,
2098 expr,
2099 } => match expr.as_ref() {
2100 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2101 _ => None,
2102 },
2103 _ => None,
2104 };
2105 let Some(inner) = inner_subq else { continue };
2106 let Some(from) = &inner.from else { continue };
2107 if !from.joins.is_empty() {
2108 continue;
2109 }
2110 let Some(t) = self.active_catalog().get(&from.primary.name) else {
2111 continue;
2112 };
2113 for col in &t.schema().columns {
2114 collision_names.insert(col.name.to_ascii_lowercase());
2115 }
2116 }
2117 let mut where_expr = where_expr;
2118 if !collision_names.is_empty() {
2119 let from = stmt.from.as_ref().expect("from present");
2120 let outer_tables: Vec<(String, String)> = {
2121 let mut v = Vec::new();
2122 let collect = |v: &mut Vec<(String, String)>, t: &TableRef| {
2123 let alias = t.alias.clone().unwrap_or_else(|| t.name.clone());
2124 v.push((alias, t.name.clone()));
2125 };
2126 collect(&mut v, &from.primary);
2127 for j in &from.joins {
2128 collect(&mut v, &j.table);
2129 }
2130 v
2131 };
2132 let mut owner: alloc::collections::BTreeMap<String, String> =
2133 alloc::collections::BTreeMap::new();
2134 for col_lc in &collision_names {
2135 let mut matches: Vec<String> = Vec::new();
2136 for (alias, tname) in &outer_tables {
2137 let Some(t) = self.active_catalog().get(tname) else {
2138 continue;
2139 };
2140 if t.schema()
2141 .columns
2142 .iter()
2143 .any(|c| c.name.eq_ignore_ascii_case(col_lc))
2144 {
2145 matches.push(alias.clone());
2146 }
2147 }
2148 if matches.len() == 1 {
2149 owner.insert(col_lc.clone(), matches.remove(0));
2150 }
2151 }
2152 if !owner.is_empty() {
2153 disambiguate_stmt_unqualified_columns(stmt, &owner);
2154 disambiguate_expr_unqualified_columns(&mut where_expr, &owner);
2155 }
2156 }
2157 let outer_aliases: alloc::collections::BTreeSet<String> = {
2158 let from = stmt.from.as_ref().expect("from present");
2159 let mut s = alloc::collections::BTreeSet::new();
2160 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
2161 s.insert(
2162 t.alias
2163 .clone()
2164 .unwrap_or_else(|| t.name.clone())
2165 .to_ascii_lowercase(),
2166 );
2167 };
2168 push(&mut s, &from.primary);
2169 for j in &from.joins {
2170 push(&mut s, &j.table);
2171 }
2172 s
2173 };
2174 let conjuncts = reorder::split_and_conjunctions(&where_expr);
2175 let mut survivors: Vec<Expr> = Vec::new();
2176 let mut new_joins: Vec<FromJoin> = Vec::new();
2177 let mut rewrote_any = false;
2178 for c in conjuncts {
2179 let parsed: Option<(&SelectStatement, bool)> = match c {
2184 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), *negated)),
2185 Expr::Unary {
2186 op: UnOp::Not,
2187 expr,
2188 } => match expr.as_ref() {
2189 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), !*negated)),
2190 _ => None,
2191 },
2192 _ => None,
2193 };
2194 if let Some((subquery, neg)) = parsed {
2195 let outer_has_limit = stmt.limit.is_some();
2213 let try_in_first = outer_has_limit;
2214 let mut consumed = false;
2215 if try_in_first
2216 && let Some(rewritten) =
2217 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2218 {
2219 survivors.push(rewritten);
2220 consumed = true;
2221 }
2222 if !consumed
2223 && let Some((join, residual)) = self.try_pull_up_exists_sublink(
2224 subquery,
2225 neg,
2226 &outer_aliases,
2227 new_joins.len(),
2228 )
2229 {
2230 new_joins.push(join);
2231 if let Some(r) = residual {
2232 survivors.push(r);
2233 }
2234 consumed = true;
2235 }
2236 if !consumed
2237 && !try_in_first
2238 && let Some(rewritten) =
2239 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2240 {
2241 survivors.push(rewritten);
2244 consumed = true;
2245 }
2246 if consumed {
2247 rewrote_any = true;
2248 continue;
2249 }
2250 }
2251 survivors.push(c.clone());
2252 }
2253 if !rewrote_any {
2254 stmt.where_ = Some(where_expr);
2255 return false;
2256 }
2257 EXISTS_PULLUP_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2258 if !new_joins.is_empty() {
2259 stmt.from
2260 .as_mut()
2261 .expect("from present")
2262 .joins
2263 .extend(new_joins);
2264 }
2265 stmt.where_ = survivors.into_iter().reduce(|a, b| Expr::Binary {
2266 lhs: alloc::boxed::Box::new(a),
2267 op: BinOp::And,
2268 rhs: alloc::boxed::Box::new(b),
2269 });
2270 true
2271 }
2272
2273 fn try_pull_up_exists_as_in(
2286 &self,
2287 inner: &SelectStatement,
2288 negated: bool,
2289 outer_aliases: &alloc::collections::BTreeSet<String>,
2290 ) -> Option<Expr> {
2291 if !inner.ctes.is_empty()
2292 || !inner.unions.is_empty()
2293 || inner.group_by.is_some()
2294 || inner.having.is_some()
2295 || inner.distinct
2296 || !inner.order_by.is_empty()
2297 || inner.limit.is_some()
2298 || inner.offset.is_some()
2299 {
2300 return None;
2301 }
2302 let from = inner.from.as_ref()?;
2303 if !from.joins.is_empty()
2304 || from.primary.lateral_subquery.is_some()
2305 || from.primary.unnest_expr.is_some()
2306 || from.primary.generate_series_args.is_some()
2307 || from.primary.as_of_segment.is_some()
2308 {
2309 return None;
2310 }
2311 let inner_table = from.primary.name.clone();
2312 let inner_alias = from
2313 .primary
2314 .alias
2315 .clone()
2316 .unwrap_or_else(|| inner_table.clone());
2317 let is_inner = |c: &ColumnName| -> bool {
2318 c.qualifier
2319 .as_deref()
2320 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2321 };
2322 let is_outer = |c: &ColumnName| -> bool {
2323 c.qualifier
2324 .as_deref()
2325 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2326 };
2327 let w = inner.where_.as_ref()?;
2328 let mut corr: Option<(String, ColumnName)> = None;
2329 let mut rest: Vec<Expr> = Vec::new();
2330 for c in reorder::split_and_conjunctions(w) {
2331 if let Expr::Binary {
2332 lhs,
2333 op: BinOp::Eq,
2334 rhs,
2335 } = c
2336 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2337 {
2338 let pair = if is_inner(a) && is_outer(b) {
2339 Some((a.name.clone(), b.clone()))
2340 } else if is_inner(b) && is_outer(a) {
2341 Some((b.name.clone(), a.clone()))
2342 } else {
2343 None
2344 };
2345 if let Some(p) = pair {
2346 if corr.is_some() {
2347 return None;
2348 }
2349 corr = Some(p);
2350 continue;
2351 }
2352 }
2353 if !expr_is_all_inner(c, &inner_alias) {
2354 return None;
2355 }
2356 rest.push(c.clone());
2357 }
2358 let (inner_key, outer_col) = corr?;
2359 if negated && !self.column_is_not_null(&inner_table, &inner_key) {
2360 return None;
2361 }
2362 let mut rewritten = inner.clone();
2367 rewritten.limit = None;
2368 rewritten.offset = None;
2369 rewritten.order_by = Vec::new();
2370 rewritten.distinct = false;
2371 rewritten.where_ = rest.into_iter().reduce(|a, b| Expr::Binary {
2372 lhs: alloc::boxed::Box::new(a),
2373 op: BinOp::And,
2374 rhs: alloc::boxed::Box::new(b),
2375 });
2376 rewritten.items = alloc::vec![SelectItem::Expr {
2377 expr: Expr::Column(ColumnName {
2378 qualifier: Some(inner_alias),
2379 name: inner_key,
2380 }),
2381 alias: None,
2382 }];
2383 Some(Expr::InSubquery {
2384 expr: alloc::boxed::Box::new(Expr::Column(outer_col)),
2385 subquery: alloc::boxed::Box::new(rewritten),
2386 negated,
2387 })
2388 }
2389
2390 fn try_pull_up_exists_sublink(
2391 &self,
2392 inner: &SelectStatement,
2393 negated: bool,
2394 outer_aliases: &alloc::collections::BTreeSet<String>,
2395 alias_n: usize,
2396 ) -> Option<(FromJoin, Option<Expr>)> {
2397 EXISTS_PULLUP_CANDIDATE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2398 if !inner.ctes.is_empty()
2399 || !inner.unions.is_empty()
2400 || inner.group_by.is_some()
2401 || inner.having.is_some()
2402 || inner.distinct
2403 || !inner.order_by.is_empty()
2404 || inner.limit.is_some()
2405 || inner.offset.is_some()
2406 {
2407 EXISTS_PULLUP_BAIL_INNER_SHAPE.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2408 return None;
2409 }
2410 let from = inner.from.as_ref()?;
2411 if !from.joins.is_empty()
2412 || from.primary.lateral_subquery.is_some()
2413 || from.primary.unnest_expr.is_some()
2414 || from.primary.generate_series_args.is_some()
2415 || from.primary.as_of_segment.is_some()
2416 {
2417 EXISTS_PULLUP_BAIL_INNER_FROM.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2418 return None;
2419 }
2420 let inner_table = from.primary.name.clone();
2421 let inner_alias = from
2422 .primary
2423 .alias
2424 .clone()
2425 .unwrap_or_else(|| inner_table.clone());
2426 let is_inner = |c: &ColumnName| -> bool {
2427 c.qualifier
2428 .as_deref()
2429 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2430 };
2431 let is_outer = |c: &ColumnName| -> bool {
2432 c.qualifier
2433 .as_deref()
2434 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2435 };
2436 let Some(w) = inner.where_.as_ref() else {
2437 EXISTS_PULLUP_BAIL_NO_WHERE.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2438 return None;
2439 };
2440 let mut corr_pairs: Vec<(String, ColumnName)> = Vec::new();
2458 let mut rest: Vec<Expr> = Vec::new();
2459 for c in reorder::split_and_conjunctions(w) {
2460 if let Expr::Binary {
2461 lhs,
2462 op: BinOp::Eq,
2463 rhs,
2464 } = c
2465 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2466 {
2467 let pair = if is_inner(a) && is_outer(b) {
2468 Some((a.name.clone(), b.clone()))
2469 } else if is_inner(b) && is_outer(a) {
2470 Some((b.name.clone(), a.clone()))
2471 } else {
2472 None
2473 };
2474 if let Some(p) = pair {
2475 corr_pairs.push(p);
2476 continue;
2477 }
2478 }
2479 if !expr_is_all_inner(c, &inner_alias) {
2480 EXISTS_PULLUP_BAIL_RESIDUAL_NOT_INNER
2481 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2482 return None;
2483 }
2484 rest.push(c.clone());
2485 }
2486 if corr_pairs.is_empty() {
2487 EXISTS_PULLUP_BAIL_NO_CORR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2488 return None;
2489 }
2490 if corr_pairs.len() > 1
2494 && EXISTS_PULLUP_MULTICOL_DISABLE.load(core::sync::atomic::Ordering::Relaxed)
2495 {
2496 EXISTS_PULLUP_BAIL_MULTICOL_DISABLED
2497 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2498 return None;
2499 }
2500 if !negated {
2507 for (k, _) in &corr_pairs {
2513 if !self.column_is_single_unique(&inner_table, k) {
2514 EXISTS_PULLUP_BAIL_UNIQUE_KEY_MISSING
2515 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2516 return None;
2517 }
2518 }
2519 }
2520 let fresh = alloc::format!("__exsj_{alias_n}");
2521 let mut on_iter = corr_pairs.iter().map(|(ik, oc)| Expr::Binary {
2524 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
2525 qualifier: Some(fresh.clone()),
2526 name: ik.clone(),
2527 })),
2528 op: BinOp::Eq,
2529 rhs: alloc::boxed::Box::new(Expr::Column(oc.clone())),
2530 });
2531 let first_key_eq = on_iter
2532 .next()
2533 .expect("corr_pairs non-empty post `is_empty()` gate");
2534 let on = rest
2535 .into_iter()
2536 .map(|mut e| {
2537 rename_qualifier(&mut e, &inner_alias, &fresh);
2538 e
2539 })
2540 .chain(on_iter)
2541 .fold(first_key_eq, |acc, pred| Expr::Binary {
2542 lhs: alloc::boxed::Box::new(acc),
2543 op: BinOp::And,
2544 rhs: alloc::boxed::Box::new(pred),
2545 });
2546 let join = FromJoin {
2547 kind: if negated {
2548 JoinKind::Left
2549 } else {
2550 JoinKind::Inner
2551 },
2552 table: TableRef {
2553 name: inner_table,
2554 alias: Some(fresh.clone()),
2555 as_of_segment: None,
2556 unnest_expr: None,
2557 unnest_column_aliases: Vec::new(),
2558 generate_series_args: None,
2559 lateral_subquery: None,
2560 jsonb_each_text_arg: None,
2561 },
2562 on: Some(on),
2563 };
2564 let residual = if negated {
2565 let probe_key = corr_pairs[0].0.clone();
2570 Some(Expr::IsNull {
2571 expr: alloc::boxed::Box::new(Expr::Column(ColumnName {
2572 qualifier: Some(fresh),
2573 name: probe_key,
2574 })),
2575 negated: false,
2576 })
2577 } else {
2578 None
2579 };
2580 Some((join, residual))
2581 }
2582
2583 fn column_is_not_null(&self, table: &str, col: &str) -> bool {
2589 let Some(t) = self.active_catalog().get(table) else {
2590 return false;
2591 };
2592 let sch = t.schema();
2593 if sch
2596 .columns
2597 .iter()
2598 .find(|c| c.name.eq_ignore_ascii_case(col))
2599 .is_some_and(|c| !c.nullable)
2600 {
2601 return true;
2602 }
2603 let Some(pos) = sch
2610 .columns
2611 .iter()
2612 .position(|c| c.name.eq_ignore_ascii_case(col))
2613 else {
2614 return false;
2615 };
2616 sch.uniqueness_constraints
2617 .iter()
2618 .any(|u| u.is_primary_key && u.columns.as_slice() == [pos])
2619 }
2620
2621 fn column_is_single_unique(&self, table: &str, col: &str) -> bool {
2625 let Some(t) = self.active_catalog().get(table) else {
2626 return false;
2627 };
2628 let sch = t.schema();
2629 let Some(pos) = sch
2630 .columns
2631 .iter()
2632 .position(|c| c.name.eq_ignore_ascii_case(col))
2633 else {
2634 return false;
2635 };
2636 if sch
2637 .uniqueness_constraints
2638 .iter()
2639 .any(|u| u.columns.as_slice() == [pos])
2640 {
2641 return true;
2642 }
2643 t.index_on(pos).is_some_and(|idx| idx.is_unique)
2644 }
2645}
2646
2647fn proj_has_disqualifying_shape(
2659 e: &Expr,
2660 inner_alias: &str,
2661 outer_aliases: &alloc::collections::BTreeSet<String>,
2662) -> bool {
2663 match e {
2664 Expr::AggregateOrdered { .. }
2665 | Expr::WindowFunction { .. }
2666 | Expr::ScalarSubquery(_)
2667 | Expr::Exists { .. } => true,
2668 Expr::FunctionCall { name, args } => {
2669 if aggregate::is_aggregate_name(name) {
2670 return true;
2671 }
2672 args.iter()
2673 .any(|a| proj_has_disqualifying_shape(a, inner_alias, outer_aliases))
2674 }
2675 Expr::Column(c) => {
2676 if let Some(q) = c.qualifier.as_deref() {
2682 outer_aliases.contains(&q.to_ascii_lowercase())
2683 && !q.eq_ignore_ascii_case(inner_alias)
2684 } else {
2685 false
2686 }
2687 }
2688 Expr::Binary { lhs, rhs, .. } => {
2689 proj_has_disqualifying_shape(lhs, inner_alias, outer_aliases)
2690 || proj_has_disqualifying_shape(rhs, inner_alias, outer_aliases)
2691 }
2692 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2693 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2694 }
2695 Expr::Like { expr, pattern, .. } => {
2696 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2697 || proj_has_disqualifying_shape(pattern, inner_alias, outer_aliases)
2698 }
2699 Expr::InList { expr, list, .. } => {
2700 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
2701 || list
2702 .iter()
2703 .any(|it| proj_has_disqualifying_shape(it, inner_alias, outer_aliases))
2704 }
2705 Expr::Case {
2706 operand,
2707 branches,
2708 else_branch,
2709 } => {
2710 operand
2711 .as_ref()
2712 .is_some_and(|o| proj_has_disqualifying_shape(o, inner_alias, outer_aliases))
2713 || branches.iter().any(|(w, t)| {
2714 proj_has_disqualifying_shape(w, inner_alias, outer_aliases)
2715 || proj_has_disqualifying_shape(t, inner_alias, outer_aliases)
2716 })
2717 || else_branch
2718 .as_ref()
2719 .is_some_and(|b| proj_has_disqualifying_shape(b, inner_alias, outer_aliases))
2720 }
2721 Expr::ArraySubscript { target, index } => {
2722 proj_has_disqualifying_shape(target, inner_alias, outer_aliases)
2723 || proj_has_disqualifying_shape(index, inner_alias, outer_aliases)
2724 }
2725 _ => false,
2726 }
2727}
2728
2729fn disambiguate_stmt_unqualified_columns(
2737 stmt: &mut SelectStatement,
2738 owner: &alloc::collections::BTreeMap<String, String>,
2739) {
2740 for item in &mut stmt.items {
2741 if let SelectItem::Expr { expr, .. } = item {
2742 disambiguate_expr_unqualified_columns(expr, owner);
2743 }
2744 }
2745 if let Some(from) = &mut stmt.from {
2746 for j in &mut from.joins {
2747 if let Some(on) = &mut j.on {
2748 disambiguate_expr_unqualified_columns(on, owner);
2749 }
2750 }
2751 }
2752 if let Some(g) = &mut stmt.group_by {
2753 for e in g.iter_mut() {
2754 disambiguate_expr_unqualified_columns(e, owner);
2755 }
2756 }
2757 if let Some(h) = &mut stmt.having {
2758 disambiguate_expr_unqualified_columns(h, owner);
2759 }
2760 for ob in &mut stmt.order_by {
2761 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
2762 }
2763}
2764
2765fn disambiguate_expr_unqualified_columns(
2766 e: &mut Expr,
2767 owner: &alloc::collections::BTreeMap<String, String>,
2768) {
2769 match e {
2770 Expr::Column(c) => {
2771 if c.qualifier.is_none()
2772 && let Some(alias) = owner.get(&c.name.to_ascii_lowercase())
2773 {
2774 c.qualifier = Some(alias.clone());
2775 }
2776 }
2777 Expr::Binary { lhs, rhs, .. } => {
2778 disambiguate_expr_unqualified_columns(lhs, owner);
2779 disambiguate_expr_unqualified_columns(rhs, owner);
2780 }
2781 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2782 disambiguate_expr_unqualified_columns(expr, owner);
2783 }
2784 Expr::FunctionCall { args, .. } => {
2785 for a in args.iter_mut() {
2786 disambiguate_expr_unqualified_columns(a, owner);
2787 }
2788 }
2789 Expr::AggregateOrdered {
2790 call,
2791 order_by,
2792 filter,
2793 ..
2794 } => {
2795 disambiguate_expr_unqualified_columns(call, owner);
2796 for ob in order_by.iter_mut() {
2797 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
2798 }
2799 if let Some(f) = filter {
2800 disambiguate_expr_unqualified_columns(f, owner);
2801 }
2802 }
2803 Expr::Like { expr, pattern, .. } => {
2804 disambiguate_expr_unqualified_columns(expr, owner);
2805 disambiguate_expr_unqualified_columns(pattern, owner);
2806 }
2807 Expr::InList { expr, list, .. } => {
2808 disambiguate_expr_unqualified_columns(expr, owner);
2809 for it in list.iter_mut() {
2810 disambiguate_expr_unqualified_columns(it, owner);
2811 }
2812 }
2813 Expr::Case {
2814 operand,
2815 branches,
2816 else_branch,
2817 } => {
2818 if let Some(o) = operand {
2819 disambiguate_expr_unqualified_columns(o, owner);
2820 }
2821 for (w, t) in branches.iter_mut() {
2822 disambiguate_expr_unqualified_columns(w, owner);
2823 disambiguate_expr_unqualified_columns(t, owner);
2824 }
2825 if let Some(eb) = else_branch {
2826 disambiguate_expr_unqualified_columns(eb, owner);
2827 }
2828 }
2829 Expr::ArraySubscript { target, index } => {
2830 disambiguate_expr_unqualified_columns(target, owner);
2831 disambiguate_expr_unqualified_columns(index, owner);
2832 }
2833 _ => {}
2835 }
2836}
2837
2838fn expr_is_all_inner(e: &Expr, inner_alias: &str) -> bool {
2839 let mut cols: Vec<ColumnName> = Vec::new();
2840 let mut subs: Vec<&SelectStatement> = Vec::new();
2841 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |s| subs.push(s));
2842 subs.is_empty()
2843 && cols.iter().all(|c| {
2844 c.qualifier
2845 .as_deref()
2846 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
2847 })
2848}
2849
2850fn rename_qualifier(e: &mut Expr, from: &str, to: &str) {
2854 match e {
2855 Expr::Column(c) => {
2856 if c.qualifier
2857 .as_deref()
2858 .is_some_and(|q| q.eq_ignore_ascii_case(from))
2859 {
2860 c.qualifier = Some(to.into());
2861 }
2862 }
2863 Expr::Binary { lhs, rhs, .. } => {
2864 rename_qualifier(lhs, from, to);
2865 rename_qualifier(rhs, from, to);
2866 }
2867 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
2868 rename_qualifier(expr, from, to);
2869 }
2870 Expr::FunctionCall { args, .. } => {
2871 for a in args.iter_mut() {
2872 rename_qualifier(a, from, to);
2873 }
2874 }
2875 Expr::Like { expr, pattern, .. } => {
2876 rename_qualifier(expr, from, to);
2877 rename_qualifier(pattern, from, to);
2878 }
2879 Expr::InList { expr, list, .. } => {
2880 rename_qualifier(expr, from, to);
2881 for it in list.iter_mut() {
2882 rename_qualifier(it, from, to);
2883 }
2884 }
2885 Expr::Case {
2886 operand,
2887 branches,
2888 else_branch,
2889 } => {
2890 if let Some(o) = operand {
2891 rename_qualifier(o, from, to);
2892 }
2893 for (w, t) in branches.iter_mut() {
2894 rename_qualifier(w, from, to);
2895 rename_qualifier(t, from, to);
2896 }
2897 if let Some(eb) = else_branch {
2898 rename_qualifier(eb, from, to);
2899 }
2900 }
2901 _ => {}
2902 }
2903}
2904
2905fn is_correlation_error(e: &EngineError) -> bool {
2910 matches!(
2911 e,
2912 EngineError::Eval(
2913 eval::EvalError::ColumnNotFound { .. } | eval::EvalError::UnknownQualifier { .. }
2914 )
2915 )
2916}
2917
2918#[derive(Debug, Clone)]
2943pub struct ScalarPkProbeFastPath {
2944 pub outer_pos: usize,
2947 pub inner_table_name: String,
2949 pub inner_pos: usize,
2951 pub table_idx: usize,
2960}
2961
2962impl ScalarPkProbeFastPath {
2963 pub fn probe(&self, row: &Row<'static>) -> Value<'static> {
2967 let outer_int = match row.values.get(self.outer_pos) {
2974 Some(Value::BigInt(n)) => *n,
2975 Some(Value::Int(n)) => i64::from(*n),
2976 Some(Value::SmallInt(n)) => i64::from(*n),
2977 Some(Value::Null) | None => return Value::Int(0),
2978 _ => return Value::Int(0),
2979 };
2980 SCALARSQ_PK_PROBE_PLAN_OUTER_INT.store(outer_int, core::sync::atomic::Ordering::Relaxed);
2981 SCALARSQ_PK_PROBE_PLAN_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2982 Value::Int(0)
2990 }
2991}
2992
2993pub static SCALARSQ_PK_PROBE_PLAN_FIRED: core::sync::atomic::AtomicU64 =
2995 core::sync::atomic::AtomicU64::new(0);
2996pub static SCALARSQ_PK_PROBE_PLAN_OUTER_INT: core::sync::atomic::AtomicI64 =
2997 core::sync::atomic::AtomicI64::new(0);
2998
2999impl Engine {
3007 pub(crate) fn probe_with_pk_fast_path(
3011 &self,
3012 plan: &ScalarPkProbeFastPath,
3013 row: &Row<'static>,
3014 ) -> Value<'static> {
3015 let outer_int = match row.values.get(plan.outer_pos) {
3016 Some(Value::BigInt(n)) => *n,
3017 Some(Value::Int(n)) => i64::from(*n),
3018 Some(Value::SmallInt(n)) => i64::from(*n),
3019 Some(Value::Null) | None => return Value::Int(0),
3020 _ => return Value::Int(0),
3021 };
3022 let Some(inner_table) = self.active_catalog().tables_at(plan.table_idx) else {
3028 return Value::Int(0);
3029 };
3030 let Some(idx) = inner_table.index_on(plan.inner_pos) else {
3031 return Value::Int(0);
3032 };
3033 let Some(key) = spg_storage::IndexKey::from_value(&Value::BigInt(outer_int)) else {
3034 return Value::Int(0);
3035 };
3036 let hit = !idx.lookup_eq(&key).is_empty();
3037 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3038 Value::Int(i32::from(hit))
3039 }
3040
3041 pub(crate) fn analyse_scalar_count_pk_eq_probe(
3047 &self,
3048 inner: &SelectStatement,
3049 outer_schema: &[spg_storage::ColumnSchema],
3050 outer_alias: &str,
3051 ) -> Option<ScalarPkProbeFastPath> {
3052 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
3053 if !inner.ctes.is_empty()
3054 || !inner.unions.is_empty()
3055 || inner.group_by.is_some()
3056 || inner.having.is_some()
3057 || inner.distinct
3058 || !inner.order_by.is_empty()
3059 || inner.limit.is_some()
3060 || inner.offset.is_some()
3061 || inner.items.len() != 1
3062 {
3063 return None;
3064 }
3065 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3066 return None;
3067 };
3068 let is_count_shape = match expr {
3069 Expr::FunctionCall { name, args } => {
3070 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
3071 || name.eq_ignore_ascii_case("count")
3072 }
3073 _ => false,
3074 };
3075 if !is_count_shape {
3076 return None;
3077 }
3078 let from = inner.from.as_ref()?;
3079 if !from.joins.is_empty()
3080 || from.primary.lateral_subquery.is_some()
3081 || from.primary.unnest_expr.is_some()
3082 || from.primary.generate_series_args.is_some()
3083 || from.primary.as_of_segment.is_some()
3084 {
3085 return None;
3086 }
3087 let inner_table_name = from.primary.name.clone();
3088 let inner_alias = from
3089 .primary
3090 .alias
3091 .as_deref()
3092 .unwrap_or(inner_table_name.as_str());
3093 let where_expr = inner.where_.as_ref()?;
3094 let Expr::Binary {
3095 lhs,
3096 op: BinOp::Eq,
3097 rhs,
3098 } = where_expr
3099 else {
3100 return None;
3101 };
3102 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
3103 return None;
3104 };
3105 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
3106 if x.qualifier
3107 .as_deref()
3108 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3109 {
3110 Some((x.name.clone(), y.clone()))
3111 } else {
3112 None
3113 }
3114 };
3115 let (inner_col_name, outer_col) = pick(a, b).or_else(|| pick(b, a))?;
3116 if let Some(q) = outer_col.qualifier.as_deref()
3119 && !q.eq_ignore_ascii_case(outer_alias)
3120 {
3121 return None;
3122 }
3123 let outer_pos = outer_schema
3124 .iter()
3125 .position(|c| c.name.eq_ignore_ascii_case(&outer_col.name))?;
3126 let catalog = self.active_catalog();
3131 let table_idx = catalog.tables_position_of(inner_table_name.as_str())?;
3132 let inner_table = catalog.tables_at(table_idx)?;
3133 let inner_schema_ref = inner_table.schema();
3134 let inner_pos = inner_schema_ref
3135 .columns
3136 .iter()
3137 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))?;
3138 if !matches!(
3139 inner_schema_ref.columns[inner_pos].ty,
3140 spg_storage::DataType::BigInt
3141 | spg_storage::DataType::Int
3142 | spg_storage::DataType::SmallInt
3143 ) {
3144 return None;
3145 }
3146 if !inner_schema_ref
3147 .uniqueness_constraints
3148 .iter()
3149 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
3150 {
3151 return None;
3152 }
3153 Some(ScalarPkProbeFastPath {
3154 outer_pos,
3155 inner_table_name,
3156 inner_pos,
3157 table_idx,
3158 })
3159 }
3160
3161 pub(crate) fn try_scalar_count_pk_eq_probe(
3162 &self,
3163 inner: &SelectStatement,
3164 row: &Row<'static>,
3165 ctx: &EvalContext<'_>,
3166 ) -> Result<Option<Value<'static>>, EngineError> {
3167 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
3168 if !inner.ctes.is_empty()
3169 || !inner.unions.is_empty()
3170 || inner.group_by.is_some()
3171 || inner.having.is_some()
3172 || inner.distinct
3173 || !inner.order_by.is_empty()
3174 || inner.limit.is_some()
3175 || inner.offset.is_some()
3176 || inner.items.len() != 1
3177 {
3178 return Ok(None);
3179 }
3180 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3181 return Ok(None);
3182 };
3183 let is_count_shape = match expr {
3184 Expr::FunctionCall { name, args } => {
3185 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
3186 || name.eq_ignore_ascii_case("count")
3187 }
3188 _ => false,
3189 };
3190 if !is_count_shape {
3191 return Ok(None);
3192 }
3193 let Some(from) = &inner.from else {
3194 return Ok(None);
3195 };
3196 if !from.joins.is_empty()
3197 || from.primary.lateral_subquery.is_some()
3198 || from.primary.unnest_expr.is_some()
3199 || from.primary.generate_series_args.is_some()
3200 || from.primary.as_of_segment.is_some()
3201 {
3202 return Ok(None);
3203 }
3204 let inner_table_name = from.primary.name.as_str();
3205 let inner_alias = from.primary.alias.as_deref().unwrap_or(inner_table_name);
3206 let Some(where_expr) = &inner.where_ else {
3207 return Ok(None);
3208 };
3209 let Expr::Binary {
3210 lhs,
3211 op: BinOp::Eq,
3212 rhs,
3213 } = where_expr
3214 else {
3215 return Ok(None);
3216 };
3217 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
3218 return Ok(None);
3219 };
3220 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
3221 if x.qualifier
3222 .as_deref()
3223 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3224 {
3225 Some((x.name.clone(), y.clone()))
3226 } else {
3227 None
3228 }
3229 };
3230 let Some((inner_col_name, outer_col)) = pick(a, b).or_else(|| pick(b, a)) else {
3231 return Ok(None);
3232 };
3233 let catalog = self.active_catalog();
3234 let Some(inner_table) = catalog.get(inner_table_name) else {
3235 return Ok(None);
3236 };
3237 let inner_schema = inner_table.schema();
3238 let Some(inner_pos) = inner_schema
3239 .columns
3240 .iter()
3241 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))
3242 else {
3243 return Ok(None);
3244 };
3245 if !matches!(
3246 inner_schema.columns[inner_pos].ty,
3247 spg_storage::DataType::BigInt
3248 | spg_storage::DataType::Int
3249 | spg_storage::DataType::SmallInt
3250 ) {
3251 return Ok(None);
3252 }
3253 if !inner_schema
3254 .uniqueness_constraints
3255 .iter()
3256 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
3257 {
3258 return Ok(None);
3259 }
3260 let outer_val = match eval::eval_expr(&Expr::Column(outer_col), row, ctx) {
3261 Ok(v) => v,
3262 Err(_) => return Ok(None),
3263 };
3264 let outer_int = match outer_val {
3265 Value::BigInt(n) => n,
3266 Value::Int(n) => i64::from(n),
3267 Value::SmallInt(n) => i64::from(n),
3268 Value::Null => return Ok(Some(Value::Int(0))),
3269 _ => return Ok(None),
3270 };
3271 let Some(idx) = inner_table.index_on(inner_pos) else {
3272 return Ok(None);
3273 };
3274 let Some(key) = spg_storage::IndexKey::from_value(&Value::BigInt(outer_int)) else {
3275 return Ok(None);
3276 };
3277 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3278 let hit = !idx.lookup_eq(&key).is_empty();
3279 Ok(Some(Value::Int(i32::from(hit))))
3280 }
3281}
3282
3283pub static SCALARSQ_PK_PROBE_FIRED: core::sync::atomic::AtomicU64 =
3284 core::sync::atomic::AtomicU64::new(0);
3285
3286fn scalar_subquery_empty_default(inner: &SelectStatement) -> Value<'static> {
3292 use spg_sql::ast::SelectItem;
3293 if inner.items.len() != 1 {
3294 return Value::Null;
3295 }
3296 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3297 return Value::Null;
3298 };
3299 fn is_count(e: &Expr) -> bool {
3300 match e {
3301 Expr::FunctionCall { name, .. } => {
3304 name.eq_ignore_ascii_case("count") || name.eq_ignore_ascii_case("count_star")
3305 }
3306 Expr::AggregateOrdered { call, .. } => is_count(call),
3307 _ => false,
3308 }
3309 }
3310 if is_count(expr) {
3311 Value::Int(0)
3312 } else {
3313 Value::Null
3314 }
3315}
3316
3317pub(crate) fn select_is_correlated(s: &SelectStatement) -> bool {
3318 use spg_sql::ast::SelectItem;
3319 let Some(from) = &s.from else {
3320 let mut qualified = false;
3323 for item in &s.items {
3324 if let SelectItem::Expr { expr, .. } = item {
3325 visit_expr_columns_and_subqueries(
3326 expr,
3327 &mut |c| {
3328 if c.qualifier.is_some() {
3329 qualified = true;
3330 }
3331 },
3332 &mut |_| {},
3333 );
3334 }
3335 }
3336 return qualified;
3337 };
3338 if from.primary.lateral_subquery.is_some() {
3341 return false;
3342 }
3343 let mut inner: Vec<&str> = Vec::new();
3344 if let Some(a) = &from.primary.alias {
3345 inner.push(a.as_str());
3346 }
3347 if !from.primary.name.is_empty() {
3348 inner.push(from.primary.name.as_str());
3349 }
3350 for j in &from.joins {
3351 if j.table.lateral_subquery.is_some() {
3352 return false;
3353 }
3354 if let Some(a) = &j.table.alias {
3355 inner.push(a.as_str());
3356 }
3357 if !j.table.name.is_empty() {
3358 inner.push(j.table.name.as_str());
3359 }
3360 }
3361 let mut exprs: Vec<&Expr> = Vec::new();
3365 for item in &s.items {
3366 if let SelectItem::Expr { expr, .. } = item {
3367 exprs.push(expr);
3368 }
3369 }
3370 if let Some(w) = &s.where_ {
3371 exprs.push(w);
3372 }
3373 for j in &from.joins {
3374 if let Some(on) = &j.on {
3375 exprs.push(on);
3376 }
3377 }
3378 if let Some(gs) = &s.group_by {
3379 for g in gs {
3380 exprs.push(g);
3381 }
3382 }
3383 if let Some(h) = &s.having {
3384 exprs.push(h);
3385 }
3386 for o in &s.order_by {
3387 exprs.push(&o.expr);
3388 }
3389 let mut correlated = false;
3390 for e in exprs {
3391 visit_expr_columns_and_subqueries(
3392 e,
3393 &mut |c| {
3394 if let Some(q) = &c.qualifier
3395 && !inner.iter().any(|a| a.eq_ignore_ascii_case(q))
3396 {
3397 correlated = true;
3398 }
3399 },
3400 &mut |_| {},
3401 );
3402 }
3403 correlated
3404}
3405
3406pub(crate) fn collect_scalar_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
3410 match e {
3411 Expr::ScalarSubquery(s) => out.push(s),
3412 Expr::Exists { .. } | Expr::InSubquery { .. } => {}
3413 Expr::Binary { lhs, rhs, .. } => {
3414 collect_scalar_subqueries(lhs, out);
3415 collect_scalar_subqueries(rhs, out);
3416 }
3417 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3418 collect_scalar_subqueries(expr, out);
3419 }
3420 Expr::Like { expr, pattern, .. } => {
3421 collect_scalar_subqueries(expr, out);
3422 collect_scalar_subqueries(pattern, out);
3423 }
3424 Expr::FunctionCall { args, .. } => {
3425 for a in args {
3426 collect_scalar_subqueries(a, out);
3427 }
3428 }
3429 Expr::AggregateOrdered { call, order_by, .. } => {
3430 collect_scalar_subqueries(call, out);
3431 for o in order_by {
3432 collect_scalar_subqueries(&o.expr, out);
3433 }
3434 }
3435 Expr::Case {
3436 operand,
3437 branches,
3438 else_branch,
3439 } => {
3440 if let Some(op) = operand {
3441 collect_scalar_subqueries(op, out);
3442 }
3443 for (w, t) in branches {
3444 collect_scalar_subqueries(w, out);
3445 collect_scalar_subqueries(t, out);
3446 }
3447 if let Some(eb) = else_branch {
3448 collect_scalar_subqueries(eb, out);
3449 }
3450 }
3451 Expr::ArraySubscript { target, index } => {
3452 collect_scalar_subqueries(target, out);
3453 collect_scalar_subqueries(index, out);
3454 }
3455 Expr::InList { expr, list, .. } => {
3456 collect_scalar_subqueries(expr, out);
3457 for item in list {
3458 collect_scalar_subqueries(item, out);
3459 }
3460 }
3461 _ => {}
3462 }
3463}
3464
3465fn hollow_scalar_subqueries(e: &mut Expr) {
3468 match e {
3469 Expr::ScalarSubquery(s) => {
3470 let hollow = SelectStatement {
3471 items: Vec::new(),
3472 ..SelectStatement::default()
3473 };
3474 **s = hollow;
3475 }
3476 Expr::Exists { .. } | Expr::InSubquery { .. } => {}
3477 Expr::Binary { lhs, rhs, .. } => {
3478 hollow_scalar_subqueries(lhs);
3479 hollow_scalar_subqueries(rhs);
3480 }
3481 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3482 hollow_scalar_subqueries(expr);
3483 }
3484 Expr::Like { expr, pattern, .. } => {
3485 hollow_scalar_subqueries(expr);
3486 hollow_scalar_subqueries(pattern);
3487 }
3488 Expr::FunctionCall { args, .. } => {
3489 for a in args.iter_mut() {
3490 hollow_scalar_subqueries(a);
3491 }
3492 }
3493 Expr::AggregateOrdered { call, order_by, .. } => {
3494 hollow_scalar_subqueries(call);
3495 for o in order_by.iter_mut() {
3496 hollow_scalar_subqueries(&mut o.expr);
3497 }
3498 }
3499 Expr::Case {
3500 operand,
3501 branches,
3502 else_branch,
3503 } => {
3504 if let Some(op) = operand {
3505 hollow_scalar_subqueries(op);
3506 }
3507 for (w, t) in branches.iter_mut() {
3508 hollow_scalar_subqueries(w);
3509 hollow_scalar_subqueries(t);
3510 }
3511 if let Some(eb) = else_branch {
3512 hollow_scalar_subqueries(eb);
3513 }
3514 }
3515 Expr::ArraySubscript { target, index } => {
3516 hollow_scalar_subqueries(target);
3517 hollow_scalar_subqueries(index);
3518 }
3519 Expr::InList { expr, list, .. } => {
3520 hollow_scalar_subqueries(expr);
3521 for item in list.iter_mut() {
3522 hollow_scalar_subqueries(item);
3523 }
3524 }
3525 _ => {}
3526 }
3527}
3528
3529fn splice_planned_subqueries(
3534 e: &mut Expr,
3535 plan: &[Option<alloc::rc::Rc<memoize::GroupMap>>],
3536 idx: &mut usize,
3537 row: &Row<'static>,
3538 ctx: &EvalContext<'_>,
3539) -> Result<bool, EngineError> {
3540 match e {
3541 Expr::ScalarSubquery(_) => {
3542 let Some(Some(gm)) = plan.get(*idx) else {
3543 return Ok(false);
3544 };
3545 *idx += 1;
3546 let (outer_col, map, empty_default) = gm.as_ref();
3553 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
3554 .map_err(EngineError::Eval)?;
3555 let v = if matches!(key_v, Value::Null) {
3556 Value::Null
3557 } else {
3558 map.get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
3559 .cloned()
3560 .unwrap_or_else(|| empty_default.clone())
3561 };
3562 *e = value_to_literal_expr(v)?;
3563 Ok(true)
3564 }
3565 Expr::Exists { .. } | Expr::InSubquery { .. } => Ok(true),
3566 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_subqueries(lhs, plan, idx, row, ctx)?
3567 && splice_planned_subqueries(rhs, plan, idx, row, ctx)?),
3568 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3569 splice_planned_subqueries(expr, plan, idx, row, ctx)
3570 }
3571 Expr::Like { expr, pattern, .. } => {
3572 Ok(splice_planned_subqueries(expr, plan, idx, row, ctx)?
3573 && splice_planned_subqueries(pattern, plan, idx, row, ctx)?)
3574 }
3575 Expr::FunctionCall { args, .. } => {
3576 for a in args.iter_mut() {
3577 if !splice_planned_subqueries(a, plan, idx, row, ctx)? {
3578 return Ok(false);
3579 }
3580 }
3581 Ok(true)
3582 }
3583 Expr::AggregateOrdered { call, order_by, .. } => {
3584 if !splice_planned_subqueries(call, plan, idx, row, ctx)? {
3585 return Ok(false);
3586 }
3587 for o in order_by.iter_mut() {
3588 if !splice_planned_subqueries(&mut o.expr, plan, idx, row, ctx)? {
3589 return Ok(false);
3590 }
3591 }
3592 Ok(true)
3593 }
3594 Expr::Case {
3595 operand,
3596 branches,
3597 else_branch,
3598 } => {
3599 if let Some(op) = operand {
3600 if !splice_planned_subqueries(op, plan, idx, row, ctx)? {
3601 return Ok(false);
3602 }
3603 }
3604 for (w, t) in branches.iter_mut() {
3605 if !splice_planned_subqueries(w, plan, idx, row, ctx)?
3606 || !splice_planned_subqueries(t, plan, idx, row, ctx)?
3607 {
3608 return Ok(false);
3609 }
3610 }
3611 if let Some(eb) = else_branch {
3612 if !splice_planned_subqueries(eb, plan, idx, row, ctx)? {
3613 return Ok(false);
3614 }
3615 }
3616 Ok(true)
3617 }
3618 Expr::ArraySubscript { target, index } => {
3619 Ok(splice_planned_subqueries(target, plan, idx, row, ctx)?
3620 && splice_planned_subqueries(index, plan, idx, row, ctx)?)
3621 }
3622 Expr::InList { expr, list, .. } => {
3623 if !splice_planned_subqueries(expr, plan, idx, row, ctx)? {
3624 return Ok(false);
3625 }
3626 for item in list.iter_mut() {
3627 if !splice_planned_subqueries(item, plan, idx, row, ctx)? {
3628 return Ok(false);
3629 }
3630 }
3631 Ok(true)
3632 }
3633 _ => Ok(true),
3634 }
3635}
3636
3637pub(crate) fn collect_exists_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
3644 match e {
3645 Expr::Exists { subquery, .. } => out.push(subquery.as_ref()),
3646 Expr::ScalarSubquery(_) | Expr::InSubquery { .. } => {}
3647 Expr::Binary { lhs, rhs, .. } => {
3648 collect_exists_subqueries(lhs, out);
3649 collect_exists_subqueries(rhs, out);
3650 }
3651 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3652 collect_exists_subqueries(expr, out);
3653 }
3654 Expr::Like { expr, pattern, .. } => {
3655 collect_exists_subqueries(expr, out);
3656 collect_exists_subqueries(pattern, out);
3657 }
3658 Expr::FunctionCall { args, .. } => {
3659 for a in args {
3660 collect_exists_subqueries(a, out);
3661 }
3662 }
3663 Expr::AggregateOrdered { call, order_by, .. } => {
3664 collect_exists_subqueries(call, out);
3665 for o in order_by {
3666 collect_exists_subqueries(&o.expr, out);
3667 }
3668 }
3669 Expr::Case {
3670 operand,
3671 branches,
3672 else_branch,
3673 } => {
3674 if let Some(op) = operand {
3675 collect_exists_subqueries(op, out);
3676 }
3677 for (w, t) in branches {
3678 collect_exists_subqueries(w, out);
3679 collect_exists_subqueries(t, out);
3680 }
3681 if let Some(eb) = else_branch {
3682 collect_exists_subqueries(eb, out);
3683 }
3684 }
3685 Expr::ArraySubscript { target, index } => {
3686 collect_exists_subqueries(target, out);
3687 collect_exists_subqueries(index, out);
3688 }
3689 Expr::InList { expr, list, .. } => {
3690 collect_exists_subqueries(expr, out);
3691 for item in list {
3692 collect_exists_subqueries(item, out);
3693 }
3694 }
3695 _ => {}
3696 }
3697}
3698
3699fn splice_planned_exists(
3707 e: &mut Expr,
3708 plan: &[Option<alloc::rc::Rc<memoize::ExistsSet>>],
3709 idx: &mut usize,
3710 row: &Row<'static>,
3711 ctx: &EvalContext<'_>,
3712) -> Result<bool, EngineError> {
3713 match e {
3714 Expr::Exists { negated, .. } => {
3715 let Some(Some(es)) = plan.get(*idx) else {
3716 return Ok(false);
3717 };
3718 *idx += 1;
3719 let (outer_cols, set) = es.as_ref();
3720 let mut key_vals: Vec<Value<'static>> = Vec::with_capacity(outer_cols.len());
3721 let mut any_null = false;
3722 for oc in outer_cols {
3723 let v = eval::eval_expr(&Expr::Column(oc.clone()), row, ctx)
3724 .map_err(EngineError::Eval)?;
3725 if matches!(v, Value::Null) {
3726 any_null = true;
3727 }
3728 key_vals.push(v);
3729 }
3730 let present = !any_null && set.contains(&aggregate::encode_key(&key_vals));
3731 let bit = if *negated { !present } else { present };
3732 *e = Expr::Literal(Literal::Bool(bit));
3733 Ok(true)
3734 }
3735 Expr::ScalarSubquery(_) | Expr::InSubquery { .. } => Ok(true),
3736 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_exists(lhs, plan, idx, row, ctx)?
3737 && splice_planned_exists(rhs, plan, idx, row, ctx)?),
3738 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
3739 splice_planned_exists(expr, plan, idx, row, ctx)
3740 }
3741 Expr::Like { expr, pattern, .. } => Ok(splice_planned_exists(expr, plan, idx, row, ctx)?
3742 && splice_planned_exists(pattern, plan, idx, row, ctx)?),
3743 Expr::FunctionCall { args, .. } => {
3744 for a in args.iter_mut() {
3745 if !splice_planned_exists(a, plan, idx, row, ctx)? {
3746 return Ok(false);
3747 }
3748 }
3749 Ok(true)
3750 }
3751 Expr::AggregateOrdered { call, order_by, .. } => {
3752 if !splice_planned_exists(call, plan, idx, row, ctx)? {
3753 return Ok(false);
3754 }
3755 for o in order_by.iter_mut() {
3756 if !splice_planned_exists(&mut o.expr, plan, idx, row, ctx)? {
3757 return Ok(false);
3758 }
3759 }
3760 Ok(true)
3761 }
3762 Expr::Case {
3763 operand,
3764 branches,
3765 else_branch,
3766 } => {
3767 if let Some(op) = operand {
3768 if !splice_planned_exists(op, plan, idx, row, ctx)? {
3769 return Ok(false);
3770 }
3771 }
3772 for (w, t) in branches.iter_mut() {
3773 if !splice_planned_exists(w, plan, idx, row, ctx)?
3774 || !splice_planned_exists(t, plan, idx, row, ctx)?
3775 {
3776 return Ok(false);
3777 }
3778 }
3779 if let Some(eb) = else_branch {
3780 if !splice_planned_exists(eb, plan, idx, row, ctx)? {
3781 return Ok(false);
3782 }
3783 }
3784 Ok(true)
3785 }
3786 Expr::ArraySubscript { target, index } => {
3787 Ok(splice_planned_exists(target, plan, idx, row, ctx)?
3788 && splice_planned_exists(index, plan, idx, row, ctx)?)
3789 }
3790 Expr::InList { expr, list, .. } => {
3791 if !splice_planned_exists(expr, plan, idx, row, ctx)? {
3792 return Ok(false);
3793 }
3794 for item in list.iter_mut() {
3795 if !splice_planned_exists(item, plan, idx, row, ctx)? {
3796 return Ok(false);
3797 }
3798 }
3799 Ok(true)
3800 }
3801 _ => Ok(true),
3802 }
3803}
3804
3805const INLIST_SET_THRESHOLD: usize = 64;
3809
3810fn expr_may_use_in_set(e: &Expr) -> bool {
3814 match e {
3815 Expr::InList { list, .. } => list.len() >= INLIST_SET_THRESHOLD,
3816 Expr::Binary {
3817 lhs,
3818 op: BinOp::And,
3819 rhs,
3820 } => expr_may_use_in_set(lhs) || expr_may_use_in_set(rhs),
3821 _ => false,
3822 }
3823}
3824
3825pub(crate) fn build_in_list_set(list: &[Expr]) -> Option<memoize::InListSetEntry> {
3828 let mut has_null = false;
3829 let mut ints: hashbrown::HashSet<i64> = hashbrown::HashSet::with_capacity(list.len());
3830 let mut texts: hashbrown::HashSet<String> = hashbrown::HashSet::with_capacity(list.len());
3831 for item in list {
3832 let Expr::Literal(lit) = item else {
3833 return None;
3834 };
3835 match lit {
3836 Literal::Null => has_null = true,
3837 Literal::Integer(i) => {
3838 ints.insert(*i);
3839 }
3840 Literal::String(s) => {
3841 texts.insert(s.clone());
3842 }
3843 _ => return None,
3844 }
3845 if !ints.is_empty() && !texts.is_empty() {
3846 return None;
3847 }
3848 }
3849 let set = if !ints.is_empty() {
3850 memoize::InListSet::Int(ints)
3851 } else if !texts.is_empty() {
3852 memoize::InListSet::Text(texts)
3853 } else {
3854 return None;
3855 };
3856 Some(memoize::InListSetEntry { set, has_null })
3857}
3858
3859fn eval_with_in_sets(
3865 e: &Expr,
3866 row: &Row<'static>,
3867 ctx: &EvalContext<'_>,
3868 m: &mut memoize::MemoizeCache,
3869) -> Result<Value<'static>, EngineError> {
3870 match e {
3871 Expr::Binary {
3872 lhs,
3873 op: BinOp::And,
3874 rhs,
3875 } => {
3876 let l = eval_with_in_sets(lhs, row, ctx, m)?;
3879 let r = eval_with_in_sets(rhs, row, ctx, m)?;
3880 eval::and_3vl(l, r).map_err(EngineError::Eval)
3881 }
3882 Expr::InList {
3883 expr: lhs,
3884 list,
3885 negated,
3886 } if list.len() >= INLIST_SET_THRESHOLD => {
3887 let key = core::ptr::from_ref::<Expr>(e) as usize;
3888 let Some(entry) = m
3889 .in_sets
3890 .entry(key)
3891 .or_insert_with(|| build_in_list_set(list))
3892 else {
3893 return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval);
3894 };
3895 let needle = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
3896 let contained = match (&needle, &entry.set) {
3897 (Value::Null, _) => return Ok(Value::Null),
3900 (Value::SmallInt(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
3901 (Value::Int(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
3902 (Value::BigInt(n), memoize::InListSet::Int(s)) => s.contains(n),
3903 (Value::Text(t), memoize::InListSet::Text(s)) => s.contains(t.as_ref()),
3904 _ => return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
3907 };
3908 let inner = if contained {
3909 Value::Bool(true)
3910 } else if entry.has_null {
3911 Value::Null
3912 } else {
3913 Value::Bool(false)
3914 };
3915 Ok(match (negated, inner) {
3916 (true, Value::Bool(b)) => Value::Bool(!b),
3917 (_, v) => v,
3918 })
3919 }
3920 _ => eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
3921 }
3922}
3923
3924fn substitute_outer_columns(stmt: &mut SelectStatement, row: &Row<'static>, ctx: &EvalContext<'_>) {
3925 let outer_alias = ctx.table_alias.unwrap_or("");
3932 substitute_in_select(stmt, row, ctx, outer_alias);
3933}
3934
3935fn substitute_in_select(
3936 stmt: &mut SelectStatement,
3937 row: &Row<'static>,
3938 ctx: &EvalContext<'_>,
3939 outer_alias: &str,
3940) {
3941 for item in &mut stmt.items {
3942 if let SelectItem::Expr { expr, .. } = item {
3943 substitute_in_expr(expr, row, ctx, outer_alias);
3944 }
3945 }
3946 if let Some(w) = &mut stmt.where_ {
3947 substitute_in_expr(w, row, ctx, outer_alias);
3948 }
3949 if let Some(gs) = &mut stmt.group_by {
3950 for g in gs {
3951 substitute_in_expr(g, row, ctx, outer_alias);
3952 }
3953 }
3954 if let Some(h) = &mut stmt.having {
3955 substitute_in_expr(h, row, ctx, outer_alias);
3956 }
3957 for o in &mut stmt.order_by {
3958 substitute_in_expr(&mut o.expr, row, ctx, outer_alias);
3959 }
3960 for (_, peer) in &mut stmt.unions {
3961 substitute_in_select(peer, row, ctx, outer_alias);
3962 }
3963}
3964
3965fn substitute_in_expr(e: &mut Expr, row: &Row<'static>, ctx: &EvalContext<'_>, outer_alias: &str) {
3966 if let Expr::Column(c) = e
3972 && c.qualifier.is_none()
3973 && (c.name.starts_with("__grp_") || c.name.starts_with("__agg_"))
3974 && let Some(idx) = ctx.columns.iter().position(|sc| sc.name == c.name)
3975 {
3976 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
3977 if let Ok(lit) = value_to_literal_expr(v) {
3978 *e = lit;
3979 return;
3980 }
3981 }
3982 if let Expr::Column(c) = e
3983 && let Some(qual) = &c.qualifier
3984 {
3985 let idx = if !outer_alias.is_empty() && qual.eq_ignore_ascii_case(outer_alias) {
3989 ctx.columns
3990 .iter()
3991 .position(|sc| sc.name.eq_ignore_ascii_case(&c.name))
3992 } else {
3993 None
3994 }
3995 .or_else(|| {
3996 let composite = alloc::format!("{qual}.{name}", name = c.name);
3997 ctx.columns
3998 .iter()
3999 .position(|sc| sc.name.eq_ignore_ascii_case(&composite))
4000 });
4001 if let Some(idx) = idx {
4002 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
4003 if let Ok(lit) = value_to_literal_expr(v) {
4004 *e = lit;
4005 return;
4006 }
4007 }
4008 }
4009 match e {
4010 Expr::AggregateOrdered { call, order_by, .. } => {
4011 substitute_in_expr(call, row, ctx, outer_alias);
4012 for o in order_by.iter_mut() {
4013 substitute_in_expr(&mut o.expr, row, ctx, outer_alias);
4014 }
4015 }
4016 Expr::Binary { lhs, rhs, .. } => {
4017 substitute_in_expr(lhs, row, ctx, outer_alias);
4018 substitute_in_expr(rhs, row, ctx, outer_alias);
4019 }
4020 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
4021 substitute_in_expr(expr, row, ctx, outer_alias);
4022 }
4023 Expr::Like { expr, pattern, .. } => {
4024 substitute_in_expr(expr, row, ctx, outer_alias);
4025 substitute_in_expr(pattern, row, ctx, outer_alias);
4026 }
4027 Expr::FunctionCall { args, .. } => {
4028 for a in args {
4029 substitute_in_expr(a, row, ctx, outer_alias);
4030 }
4031 }
4032 Expr::Extract { source, .. } => substitute_in_expr(source, row, ctx, outer_alias),
4033 Expr::WindowFunction {
4034 args,
4035 partition_by,
4036 order_by,
4037 ..
4038 } => {
4039 for a in args {
4040 substitute_in_expr(a, row, ctx, outer_alias);
4041 }
4042 for p in partition_by {
4043 substitute_in_expr(p, row, ctx, outer_alias);
4044 }
4045 for (o, _, _) in order_by {
4046 substitute_in_expr(o, row, ctx, outer_alias);
4047 }
4048 }
4049 Expr::ScalarSubquery(s) => substitute_in_select(s, row, ctx, outer_alias),
4050 Expr::Exists { subquery, .. } | Expr::InSubquery { subquery, .. } => {
4051 substitute_in_select(subquery, row, ctx, outer_alias);
4052 }
4053 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => {}
4054 Expr::Array(items) => {
4055 for elem in items {
4056 substitute_in_expr(elem, row, ctx, outer_alias);
4057 }
4058 }
4059 Expr::ArraySubscript { target, index } => {
4060 substitute_in_expr(target, row, ctx, outer_alias);
4061 substitute_in_expr(index, row, ctx, outer_alias);
4062 }
4063 Expr::AnyAll { expr, array, .. } => {
4064 substitute_in_expr(expr, row, ctx, outer_alias);
4065 substitute_in_expr(array, row, ctx, outer_alias);
4066 }
4067 Expr::InList { expr, list, .. } => {
4068 substitute_in_expr(expr, row, ctx, outer_alias);
4069 for item in list {
4070 substitute_in_expr(item, row, ctx, outer_alias);
4071 }
4072 }
4073 Expr::Case {
4074 operand,
4075 branches,
4076 else_branch,
4077 } => {
4078 if let Some(o) = operand {
4079 substitute_in_expr(o, row, ctx, outer_alias);
4080 }
4081 for (w, t) in branches {
4082 substitute_in_expr(w, row, ctx, outer_alias);
4083 substitute_in_expr(t, row, ctx, outer_alias);
4084 }
4085 if let Some(e) = else_branch {
4086 substitute_in_expr(e, row, ctx, outer_alias);
4087 }
4088 }
4089 }
4090}
4091
4092pub fn expr_tree_has_subquery(stmt: &SelectStatement) -> bool {
4096 let mut any = false;
4097 for item in &stmt.items {
4098 if let SelectItem::Expr { expr, .. } = item {
4099 any = any || expr_has_subquery(expr);
4100 }
4101 }
4102 if let Some(w) = &stmt.where_ {
4103 any = any || expr_has_subquery(w);
4104 }
4105 if let Some(h) = &stmt.having {
4106 any = any || expr_has_subquery(h);
4107 }
4108 for o in &stmt.order_by {
4109 any = any || expr_has_subquery(&o.expr);
4110 }
4111 for (_, peer) in &stmt.unions {
4112 any = any || expr_tree_has_subquery(peer);
4113 }
4114 any
4115}
4116
4117pub(crate) fn expr_has_subquery(e: &Expr) -> bool {
4118 match e {
4119 Expr::ScalarSubquery(_) | Expr::Exists { .. } | Expr::InSubquery { .. } => true,
4120 Expr::AggregateOrdered { call, order_by, .. } => {
4121 expr_has_subquery(call) || order_by.iter().any(|o| expr_has_subquery(&o.expr))
4122 }
4123 Expr::Binary { lhs, rhs, .. } => expr_has_subquery(lhs) || expr_has_subquery(rhs),
4124 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } | Expr::IsNull { expr, .. } => {
4125 expr_has_subquery(expr)
4126 }
4127 Expr::FunctionCall { args, .. } => args.iter().any(expr_has_subquery),
4128 Expr::Like { expr, pattern, .. } => expr_has_subquery(expr) || expr_has_subquery(pattern),
4129 Expr::Extract { source, .. } => expr_has_subquery(source),
4130 Expr::WindowFunction {
4131 args,
4132 partition_by,
4133 order_by,
4134 ..
4135 } => {
4136 args.iter().any(expr_has_subquery)
4137 || partition_by.iter().any(expr_has_subquery)
4138 || order_by.iter().any(|(e, _, _)| expr_has_subquery(e))
4139 }
4140 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => false,
4141 Expr::Array(items) => items.iter().any(expr_has_subquery),
4142 Expr::ArraySubscript { target, index } => {
4143 expr_has_subquery(target) || expr_has_subquery(index)
4144 }
4145 Expr::AnyAll { expr, array, .. } => expr_has_subquery(expr) || expr_has_subquery(array),
4146 Expr::InList { expr, list, .. } => {
4147 expr_has_subquery(expr) || list.iter().any(expr_has_subquery)
4148 }
4149 Expr::Case {
4150 operand,
4151 branches,
4152 else_branch,
4153 } => {
4154 operand.as_deref().is_some_and(expr_has_subquery)
4155 || branches
4156 .iter()
4157 .any(|(w, t)| expr_has_subquery(w) || expr_has_subquery(t))
4158 || else_branch.as_deref().is_some_and(expr_has_subquery)
4159 }
4160 }
4161}