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
100fn scalar_subquery_arity(ncols: usize) -> Result<(), EngineError> {
114 if ncols == 1 {
115 Ok(())
116 } else {
117 Err(EngineError::Unsupported(
118 "subquery must return only one column".into(),
119 ))
120 }
121}
122
123fn build_row_comparison(row: &[Expr], op: spg_sql::ast::BinOp, rhs: &[Expr]) -> Expr {
124 use alloc::boxed::Box;
125 use spg_sql::ast::{BinOp, UnOp};
126 fn row_eq(lhs: &[Expr], rhs: &[Expr]) -> Expr {
127 let mut it = lhs.iter().zip(rhs.iter()).map(|(l, r)| Expr::Binary {
128 lhs: Box::new(l.clone()),
129 op: BinOp::Eq,
130 rhs: Box::new(r.clone()),
131 });
132 let first = it.next().expect("row has >= 1 element");
133 it.fold(first, |acc, e| Expr::Binary {
134 lhs: Box::new(acc),
135 op: BinOp::And,
136 rhs: Box::new(e),
137 })
138 }
139 fn row_lex(lhs: &[Expr], rhs: &[Expr], strict: BinOp, last: BinOp) -> Expr {
140 if lhs.len() == 1 {
141 return Expr::Binary {
142 lhs: Box::new(lhs[0].clone()),
143 op: last,
144 rhs: Box::new(rhs[0].clone()),
145 };
146 }
147 let head_strict = Expr::Binary {
148 lhs: Box::new(lhs[0].clone()),
149 op: strict,
150 rhs: Box::new(rhs[0].clone()),
151 };
152 let head_eq = Expr::Binary {
153 lhs: Box::new(lhs[0].clone()),
154 op: BinOp::Eq,
155 rhs: Box::new(rhs[0].clone()),
156 };
157 Expr::Binary {
158 lhs: Box::new(head_strict),
159 op: BinOp::Or,
160 rhs: Box::new(Expr::Binary {
161 lhs: Box::new(head_eq),
162 op: BinOp::And,
163 rhs: Box::new(row_lex(&lhs[1..], &rhs[1..], strict, last)),
164 }),
165 }
166 }
167 match op {
168 BinOp::Eq => row_eq(row, rhs),
169 BinOp::NotEq => Expr::Unary {
170 op: UnOp::Not,
171 expr: Box::new(row_eq(row, rhs)),
172 },
173 BinOp::Lt => row_lex(row, rhs, BinOp::Lt, BinOp::Lt),
174 BinOp::LtEq => row_lex(row, rhs, BinOp::Lt, BinOp::LtEq),
175 BinOp::Gt => row_lex(row, rhs, BinOp::Gt, BinOp::Gt),
176 BinOp::GtEq => row_lex(row, rhs, BinOp::Gt, BinOp::GtEq),
177 _ => Expr::Literal(Literal::Bool(false)), }
179}
180
181impl Engine {
182 pub(crate) fn order_by_resolved_for_row(
217 &self,
218 order_by: &[spg_sql::ast::OrderBy],
219 row: &Row<'static>,
220 ctx: &EvalContext<'_>,
221 cancel: CancelToken<'_>,
222 ) -> Result<Option<alloc::vec::Vec<spg_sql::ast::OrderBy>>, EngineError> {
223 if !order_by.iter().any(|o| expr_has_subquery(&o.expr)) {
224 return Ok(None);
225 }
226 let mut out = alloc::vec::Vec::with_capacity(order_by.len());
227 for o in order_by {
228 if !expr_has_subquery(&o.expr) {
229 out.push(o.clone());
230 continue;
231 }
232 let v = self.eval_expr_with_correlated(&o.expr, row, ctx, cancel, None)?;
233 let mut o2 = o.clone();
234 o2.expr = crate::substitute::value_to_literal_expr(v)?;
238 out.push(o2);
239 }
240 Ok(Some(out))
241 }
242
243 pub(crate) fn eval_expr_with_correlated(
244 &self,
245 expr: &Expr,
246 row: &Row<'static>,
247 ctx: &EvalContext<'_>,
248 cancel: CancelToken<'_>,
249 mut memo: Option<&mut memoize::MemoizeCache>,
250 ) -> Result<Value<'static>, EngineError> {
251 let has_subq = if let Some(m) = memo.as_deref_mut() {
256 let key = core::ptr::from_ref::<Expr>(expr) as usize;
257 match m.has_subquery.get(&key) {
258 Some(b) => *b,
259 None => {
260 let b = expr_has_subquery(expr);
261 m.has_subquery.insert(key, b);
262 b
263 }
264 }
265 } else {
266 expr_has_subquery(expr)
267 };
268 if !has_subq {
269 if let Some(m) = memo.as_deref_mut()
274 && expr_may_use_in_set(expr)
275 {
276 return eval_with_in_sets(expr, row, ctx, m);
277 }
278 return eval::eval_expr(expr, row, ctx).map_err(EngineError::Eval);
279 }
280 if let Some(m) = memo.as_deref_mut() {
286 let key = core::ptr::from_ref::<Expr>(expr) as usize;
287 let plan_hit = m.expr_plans.contains_key(&key);
292 let exists_plan_hit = m.exists_plans.contains_key(&key);
293 let mut subs: Vec<&SelectStatement> = Vec::new();
294 let mut exists_subs: Vec<&SelectStatement> = Vec::new();
295 if !plan_hit {
296 collect_scalar_subqueries(expr, &mut subs);
297 }
298 if !exists_plan_hit {
299 collect_exists_subqueries(expr, &mut exists_subs);
300 }
301 if !plan_hit && !subs.is_empty() {
302 let mut plan: Vec<Option<alloc::rc::Rc<memoize::GroupMap>>> =
303 Vec::with_capacity(subs.len());
304 for sub in &subs {
305 let repr = alloc::format!("{sub}");
306 if !m.group_maps.contains_key(&repr) {
307 let built = self
308 .try_batch_correlated_scalar(sub, None, cancel)?
309 .map(alloc::rc::Rc::new);
310 m.group_maps.insert(repr.clone(), built);
311 }
312 plan.push(m.group_maps.get(&repr).cloned().flatten());
313 }
314 let mut template = expr.clone();
315 hollow_scalar_subqueries(&mut template);
316 m.expr_plans.insert(key, (subs.len(), plan, template));
317 }
318 if !exists_plan_hit && !exists_subs.is_empty() {
324 let mut eplan: Vec<Option<alloc::rc::Rc<memoize::ExistsSet>>> =
325 Vec::with_capacity(exists_subs.len());
326 for sub in &exists_subs {
327 let built = self
328 .try_batch_correlated_exists(sub, cancel)?
329 .map(alloc::rc::Rc::new);
330 if built.is_some() {
331 EXISTS_BATCH_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
332 } else {
333 EXISTS_BATCH_FALL_THROUGH_COUNT
334 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
335 }
336 eplan.push(built);
337 }
338 m.exists_plans.insert(key, eplan);
339 }
340 if !m.expr_plans.contains_key(&key)
359 && let Some((negated, wrapped_in_not)) = bare_exists_shape(expr)
360 && let Some(plan) = m.exists_plans.get(&key)
361 && plan.len() == 1
362 && let Some(Some(es)) = plan.first()
363 {
364 let bit = planned_exists_bit(es, negated, row, ctx)?;
365 return Ok(Value::Bool(if wrapped_in_not { !bit } else { bit }));
366 }
367 let scalar_ready = m
368 .expr_plans
369 .get(&key)
370 .map(|(_, plan, _)| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
371 .unwrap_or(false);
372 let exists_ready = m
373 .exists_plans
374 .get(&key)
375 .map(|plan| !plan.is_empty() && plan.iter().all(|p| p.is_some()))
376 .unwrap_or(false);
377 if scalar_ready || exists_ready {
378 let scalar_plan = m
386 .expr_plans
387 .get(&key)
388 .map(|(_, plan, template)| (plan.clone(), template.clone()));
389 let exists_plan = m.exists_plans.get(&key).cloned();
390 let mut e = match &scalar_plan {
391 Some((_, template)) => template.clone(),
392 None => expr.clone(),
393 };
394 let mut all_ok = true;
395 if let Some((plan, _)) = &scalar_plan {
396 let mut idx = 0usize;
397 all_ok &= splice_planned_subqueries(&mut e, plan, &mut idx, row, ctx)?;
398 }
399 if all_ok && let Some(plan) = &exists_plan {
400 let mut idx = 0usize;
401 all_ok &= splice_planned_exists(&mut e, plan, &mut idx, row, ctx)?;
402 }
403 if all_ok {
404 if expr_has_subquery(&e) {
405 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
406 }
407 return eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval);
408 }
409 }
410 }
411 let mut e = expr.clone();
412 self.resolve_correlated_in_expr(&mut e, row, ctx, cancel, memo)?;
413 eval::eval_expr(&e, row, ctx).map_err(EngineError::Eval)
414 }
415
416 pub(crate) fn materialize_quantified_rows(
421 &self,
422 inner: &SelectStatement,
423 cancel: CancelToken<'_>,
424 ) -> Result<Expr, EngineError> {
425 let r = self.exec_select_cancel(inner, cancel)?;
426 let QueryResult::Rows { rows, .. } = r else {
427 return Err(EngineError::Unsupported(
428 "ANY/ALL subquery: inner did not return rows".into(),
429 ));
430 };
431 let mut items = alloc::vec::Vec::with_capacity(rows.len());
432 for r0 in rows {
433 let v = r0.values.into_iter().next().unwrap_or(Value::Null);
434 items.push(value_to_literal_expr(v)?);
435 }
436 Ok(Expr::Array(items))
437 }
438
439 fn resolve_correlated_in_expr(
440 &self,
441 e: &mut Expr,
442 row: &Row<'static>,
443 ctx: &EvalContext<'_>,
444 cancel: CancelToken<'_>,
445 mut memo: Option<&mut memoize::MemoizeCache>,
446 ) -> Result<(), EngineError> {
447 match e {
448 Expr::Collate { expr, .. } | Expr::NamedArg { expr, .. } | Expr::Variadic(expr) => {
449 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
450 }
451 Expr::AggregateOrdered { call, order_by, .. } => {
452 self.resolve_correlated_in_expr(call, row, ctx, cancel, memo.as_deref_mut())?;
453 for o in order_by.iter_mut() {
454 self.resolve_correlated_in_expr(
455 &mut o.expr,
456 row,
457 ctx,
458 cancel,
459 memo.as_deref_mut(),
460 )?;
461 }
462 }
463 Expr::ScalarSubquery(inner) => {
464 if memo.is_some() {
477 let ptr_key = core::ptr::from_ref::<SelectStatement>(&**inner) as usize;
478 let entry_known = memo
479 .as_ref()
480 .is_some_and(|m| m.group_maps_by_ptr.contains_key(&ptr_key));
481 if !entry_known {
482 let built = self
483 .try_batch_correlated_scalar(inner, None, cancel)?
484 .map(alloc::rc::Rc::new);
485 if let Some(m) = memo.as_deref_mut() {
486 m.group_maps_by_ptr.insert(ptr_key, built);
487 }
488 }
489 if let Some(m) = memo.as_deref_mut()
490 && let Some(Some(gm)) = m.group_maps_by_ptr.get(&ptr_key)
491 {
492 let (outer_col, map, empty_default) = gm.as_ref();
493 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
494 .map_err(EngineError::Eval)?;
495 let v = map
511 .get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
512 .cloned()
513 .unwrap_or_else(|| empty_default.clone());
514 *e = value_to_literal_expr(v)?;
515 return Ok(());
516 }
517 }
518 let cache_key = memo.as_ref().map(|_| memoize::CacheKey {
523 subquery_repr: alloc::format!("{}", **inner),
524 outer_values: row.values.iter().cloned().map(Value::into_owned).collect(),
525 });
526 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key.as_ref())
527 && let Some(cached) = cache.get(k)
528 {
529 *e = value_to_literal_expr(cached)?;
530 return Ok(());
531 }
532 if let Some(v) = self.try_scalar_count_pk_eq_probe(inner, row, ctx)? {
544 *e = value_to_literal_expr(v)?;
545 return Ok(());
546 }
547 let mut s = (**inner).clone();
548 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
549 let r = self.exec_select_cancel(&s, cancel)?;
550 let QueryResult::Rows { columns, rows, .. } = r else {
551 return Err(EngineError::Unsupported(
552 "scalar subquery: inner did not return rows".into(),
553 ));
554 };
555 scalar_subquery_arity(columns.len())?;
556 let value = match rows.as_slice() {
557 [] => Value::Null,
558 [r0] => r0.values.first().cloned().unwrap_or(Value::Null),
559 _ => {
560 return Err(EngineError::CardinalityViolation);
561 }
562 };
563 if let (Some(cache), Some(k)) = (memo.as_deref_mut(), cache_key) {
564 cache.insert(k, value.clone());
565 }
566 *e = value_to_literal_expr(value)?;
567 }
568 Expr::Exists { subquery, negated } => {
569 if memo.is_some() {
576 let repr = alloc::format!("{}", **subquery);
577 let known = memo
578 .as_ref()
579 .is_some_and(|m| m.exists_sets.contains_key(&repr));
580 if !known {
581 let built = self
582 .try_batch_correlated_exists(subquery, cancel)?
583 .map(alloc::rc::Rc::new);
584 if let Some(m) = memo.as_deref_mut() {
585 m.exists_sets.insert(repr.clone(), built);
586 }
587 }
588 if let Some(m) = memo.as_deref_mut()
589 && let Some(Some(es)) = m.exists_sets.get(&repr)
590 {
591 let (outer_cols, set) = es.as_ref();
592 let mut key_vals: Vec<Value<'static>> =
593 Vec::with_capacity(outer_cols.len());
594 let mut any_null = false;
595 for oc in outer_cols {
596 let v = eval::eval_expr(oc, row, ctx).map_err(EngineError::Eval)?;
599 if matches!(v, Value::Null) {
600 any_null = true;
601 }
602 key_vals.push(v);
603 }
604 let present =
606 !any_null && set.contains(&aggregate::encode_canonical_key(&key_vals));
607 let bit = if *negated { !present } else { present };
608 *e = Expr::Literal(Literal::Bool(bit));
609 return Ok(());
610 }
611 }
612 let mut s = (**subquery).clone();
613 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
614 let r = self.exec_select_cancel(&s, cancel)?;
615 let exists = matches!(r, QueryResult::Rows { rows, .. } if !rows.is_empty());
616 let bit = if *negated { !exists } else { exists };
617 *e = Expr::Literal(Literal::Bool(bit));
618 }
619 Expr::InSubquery {
620 expr: lhs,
621 subquery,
622 negated,
623 } => {
624 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
625 let lhs_val = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
626 let mut s = (**subquery).clone();
627 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
628 let r = self.exec_select_cancel(&s, cancel)?;
629 let QueryResult::Rows { columns, rows, .. } = r else {
630 return Err(EngineError::Unsupported(
631 "IN-subquery: inner did not return rows".into(),
632 ));
633 };
634 if columns.len() != 1 {
635 return Err(EngineError::Unsupported(
641 if columns.is_empty() {
642 "subquery has too few columns"
643 } else {
644 "subquery has too many columns"
645 }
646 .into(),
647 ));
648 }
649 let mut found = false;
650 let mut any_null = false;
651 for r0 in rows {
652 let v = r0.values.into_iter().next().unwrap_or(Value::Null);
653 if v.is_null() {
654 any_null = true;
655 continue;
656 }
657 if value_cmp(&v, &lhs_val) == core::cmp::Ordering::Equal {
658 found = true;
659 break;
660 }
661 }
662 if !found && any_null {
663 *e = Expr::Literal(Literal::Null);
668 return Ok(());
669 }
670 let bit = if found { !*negated } else { *negated };
671 *e = Expr::Literal(Literal::Bool(bit));
672 }
673 Expr::RowInSubquery {
674 row: row_exprs,
675 subquery,
676 negated,
677 } => {
678 for el in row_exprs.iter_mut() {
687 self.resolve_correlated_in_expr(el, row, ctx, cancel, memo.as_deref_mut())?;
688 }
689 let lhs_vals: Vec<Value> = row_exprs
690 .iter()
691 .map(|el| eval::eval_expr(el, row, ctx).map_err(EngineError::Eval))
692 .collect::<Result<_, _>>()?;
693 let mut s = (**subquery).clone();
694 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
695 let r = self.exec_select_cancel(&s, cancel)?;
696 let QueryResult::Rows { columns, rows, .. } = r else {
697 return Err(EngineError::Unsupported(
698 "row IN-subquery: inner did not return rows".into(),
699 ));
700 };
701 if columns.len() != lhs_vals.len() {
702 return Err(EngineError::Unsupported(alloc::format!(
703 "row IN-subquery: left side has {} column(s), subquery returns {}",
704 lhs_vals.len(),
705 columns.len()
706 )));
707 }
708 let mut found = false;
709 let mut any_null = false;
710 'rows: for r0 in rows {
711 let mut has_null = false;
712 for (j, sub_v) in r0.values.iter().enumerate() {
713 let lv = &lhs_vals[j];
714 if lv.is_null() || sub_v.is_null() {
715 has_null = true;
716 } else if value_cmp(lv, sub_v) != core::cmp::Ordering::Equal {
717 continue 'rows; }
719 }
720 if has_null {
721 any_null = true; } else {
723 found = true; break;
725 }
726 }
727 if !found && any_null {
728 *e = Expr::Literal(Literal::Null);
729 return Ok(());
730 }
731 let bit = if found { !*negated } else { *negated };
732 *e = Expr::Literal(Literal::Bool(bit));
733 }
734 Expr::RowCmpSubquery {
735 row: row_exprs,
736 op,
737 subquery,
738 } => {
739 for el in row_exprs.iter_mut() {
743 self.resolve_correlated_in_expr(el, row, ctx, cancel, memo.as_deref_mut())?;
744 }
745 let mut s = (**subquery).clone();
746 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
747 let r = self.exec_select_cancel(&s, cancel)?;
748 let QueryResult::Rows {
749 columns, mut rows, ..
750 } = r
751 else {
752 return Err(EngineError::Unsupported(
753 "row comparison subquery: inner did not return rows".into(),
754 ));
755 };
756 if rows.is_empty() {
757 *e = Expr::Literal(Literal::Null);
758 return Ok(());
759 }
760 if rows.len() > 1 {
761 return Err(EngineError::CardinalityViolation);
762 }
763 if columns.len() != row_exprs.len() {
764 return Err(EngineError::Unsupported(alloc::format!(
765 "row comparison: left side has {} column(s), subquery returns {}",
766 row_exprs.len(),
767 columns.len()
768 )));
769 }
770 let rhs: Vec<Expr> = rows
771 .remove(0)
772 .values
773 .into_iter()
774 .map(value_to_literal_expr)
775 .collect::<Result<_, _>>()?;
776 let cmp = build_row_comparison(row_exprs, *op, &rhs);
777 let v = eval::eval_expr(&cmp, row, ctx).map_err(EngineError::Eval)?;
778 *e = value_to_literal_expr(v)?;
779 }
780 Expr::Binary { lhs, rhs, .. } => {
781 self.resolve_correlated_in_expr(lhs, row, ctx, cancel, memo.as_deref_mut())?;
782 self.resolve_correlated_in_expr(rhs, row, ctx, cancel, memo.as_deref_mut())?;
783 }
784 Expr::Unary { expr, .. }
785 | Expr::Cast { expr, .. }
786 | Expr::IsNull { expr, .. }
787 | Expr::BoolTest { expr, .. }
788 | Expr::FieldAccess { base: expr, .. } => {
789 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
790 }
791 Expr::Like { expr, pattern, .. } => {
792 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
793 self.resolve_correlated_in_expr(pattern, row, ctx, cancel, memo.as_deref_mut())?;
794 }
795 Expr::FunctionCall { args, .. } => {
796 for a in args {
797 self.resolve_correlated_in_expr(a, row, ctx, cancel, memo.as_deref_mut())?;
798 }
799 }
800 Expr::Extract { source, .. } => {
801 self.resolve_correlated_in_expr(source, row, ctx, cancel, memo.as_deref_mut())?;
802 }
803 Expr::WindowFunction { .. }
804 | Expr::Literal(_)
805 | Expr::Placeholder(_)
806 | Expr::Column(_) => {}
807 Expr::Array(items) => {
809 for elem in items {
810 self.resolve_correlated_in_expr(elem, row, ctx, cancel, memo.as_deref_mut())?;
811 }
812 }
813 Expr::ArraySubscript { target, index } => {
814 self.resolve_correlated_in_expr(target, row, ctx, cancel, memo.as_deref_mut())?;
815 self.resolve_correlated_in_expr(index, row, ctx, cancel, memo.as_deref_mut())?;
816 }
817 Expr::ArraySlice { target, lo, hi } => {
818 self.resolve_correlated_in_expr(target, row, ctx, cancel, memo.as_deref_mut())?;
819 if let Some(l) = lo {
820 self.resolve_correlated_in_expr(l, row, ctx, cancel, memo.as_deref_mut())?;
821 }
822 if let Some(h) = hi {
823 self.resolve_correlated_in_expr(h, row, ctx, cancel, memo.as_deref_mut())?;
824 }
825 }
826 Expr::AnyAll { expr, array, .. } => {
827 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
828 if let Expr::ScalarSubquery(inner) = array.as_mut() {
831 let mut s = (**inner).clone();
832 substitute_outer_columns(&mut s, row, ctx, self.active_catalog());
833 **array = self.materialize_quantified_rows(&s, cancel)?;
834 } else {
835 self.resolve_correlated_in_expr(array, row, ctx, cancel, memo.as_deref_mut())?;
836 }
837 }
838 Expr::InList { expr, list, .. } => {
839 self.resolve_correlated_in_expr(expr, row, ctx, cancel, memo.as_deref_mut())?;
840 for item in list {
841 self.resolve_correlated_in_expr(item, row, ctx, cancel, memo.as_deref_mut())?;
842 }
843 }
844 Expr::Case {
845 operand,
846 branches,
847 else_branch,
848 } => {
849 if let Some(o) = operand {
850 self.resolve_correlated_in_expr(o, row, ctx, cancel, memo.as_deref_mut())?;
851 }
852 for (w, t) in branches {
853 self.resolve_correlated_in_expr(w, row, ctx, cancel, memo.as_deref_mut())?;
854 self.resolve_correlated_in_expr(t, row, ctx, cancel, memo.as_deref_mut())?;
855 }
856 if let Some(e) = else_branch {
857 self.resolve_correlated_in_expr(e, row, ctx, cancel, memo.as_deref_mut())?;
858 }
859 }
860 }
861 Ok(())
862 }
863
864 #[inline(never)]
885 fn arm_row_cmp_subquery(
886 &self,
887 e: &Expr,
888 cancel: CancelToken<'_>,
889 ) -> Result<Option<Expr>, EngineError> {
890 let Expr::RowCmpSubquery { row, op, subquery } = e else {
891 return Ok(None);
892 };
893
894 if select_is_correlated(subquery) {
895 return Ok(None);
896 }
897 let mut s = (**subquery).clone();
898 self.resolve_select_subqueries(&mut s, cancel)?;
899 let r = match self.exec_select_cancel(&s, cancel) {
900 Ok(r) => r,
901 Err(e) if is_correlation_error(&e) => return Ok(None),
902 Err(e) => return Err(e),
903 };
904 let QueryResult::Rows {
905 columns, mut rows, ..
906 } = r
907 else {
908 return Err(EngineError::Unsupported(
909 "row comparison subquery: inner statement did not return rows".into(),
910 ));
911 };
912 if rows.is_empty() {
914 return Ok(Some(Expr::Literal(Literal::Null)));
915 }
916 if rows.len() > 1 {
917 return Err(EngineError::CardinalityViolation);
918 }
919 if columns.len() != row.len() {
920 return Err(EngineError::Unsupported(alloc::format!(
921 "row comparison: left side has {} column(s), subquery returns {}",
922 row.len(),
923 columns.len()
924 )));
925 }
926 let rhs: Vec<Expr> = rows
927 .remove(0)
928 .values
929 .into_iter()
930 .map(value_to_literal_expr)
931 .collect::<Result<_, _>>()?;
932 Ok(Some(build_row_comparison(row, *op, &rhs)))
935 }
936
937 #[inline(never)]
949 fn arm_row_in_subquery(
950 &self,
951 e: &Expr,
952 cancel: CancelToken<'_>,
953 ) -> Result<Option<Expr>, EngineError> {
954 let Expr::RowInSubquery {
955 row,
956 subquery,
957 negated,
958 } = e
959 else {
960 return Ok(None);
961 };
962
963 use alloc::boxed::Box;
964 if select_is_correlated(subquery) {
967 return Ok(None);
968 }
969 let mut s = (**subquery).clone();
970 self.resolve_select_subqueries(&mut s, cancel)?;
971 let r = match self.exec_select_cancel(&s, cancel) {
972 Ok(r) => r,
973 Err(e) if is_correlation_error(&e) => return Ok(None),
974 Err(e) => return Err(e),
975 };
976 let QueryResult::Rows { columns, rows, .. } = r else {
977 return Err(EngineError::Unsupported(
978 "row IN-subquery: inner statement did not return rows".into(),
979 ));
980 };
981 if columns.len() != row.len() {
982 return Err(EngineError::Unsupported(alloc::format!(
983 "row IN-subquery: left side has {} column(s), subquery returns {}",
984 row.len(),
985 columns.len()
986 )));
987 }
988 let mut alts: Vec<Expr> = Vec::with_capacity(rows.len());
994 for r0 in rows {
995 let mut conj: Option<Expr> = None;
996 for (lhs_el, v) in row.iter().zip(r0.values) {
997 let eq = Expr::Binary {
998 lhs: Box::new(lhs_el.clone()),
999 op: BinOp::Eq,
1000 rhs: Box::new(value_to_literal_expr(v)?),
1001 };
1002 conj = Some(match conj {
1003 None => eq,
1004 Some(prev) => Expr::Binary {
1005 lhs: Box::new(prev),
1006 op: BinOp::And,
1007 rhs: Box::new(eq),
1008 },
1009 });
1010 }
1011 if let Some(c) = conj {
1012 alts.push(c);
1013 }
1014 }
1015 let combined = match alts.into_iter().reduce(|acc, e| Expr::Binary {
1016 lhs: Box::new(acc),
1017 op: BinOp::Or,
1018 rhs: Box::new(e),
1019 }) {
1020 Some(c) => c,
1021 None => Expr::Literal(Literal::Bool(false)),
1022 };
1023 let result = if *negated {
1024 Expr::Unary {
1025 op: UnOp::Not,
1026 expr: Box::new(combined),
1027 }
1028 } else {
1029 combined
1030 };
1031 Ok(Some(result))
1032 }
1033
1034 #[inline(never)]
1046 fn arm_in_subquery(
1047 &self,
1048 e: &Expr,
1049 cancel: CancelToken<'_>,
1050 ) -> Result<Option<Expr>, EngineError> {
1051 let Expr::InSubquery {
1052 expr,
1053 subquery,
1054 negated,
1055 } = e
1056 else {
1057 return Ok(None);
1058 };
1059
1060 if select_is_correlated(subquery) {
1061 return Ok(None);
1062 }
1063 let mut s = (**subquery).clone();
1064 self.resolve_select_subqueries(&mut s, cancel)?;
1065 let r = match self.exec_select_cancel(&s, cancel) {
1066 Ok(r) => r,
1067 Err(e) if is_correlation_error(&e) => return Ok(None),
1068 Err(e) => return Err(e),
1069 };
1070 let QueryResult::Rows { columns, rows, .. } = r else {
1071 return Err(EngineError::Unsupported(
1072 "IN-subquery: inner statement did not return rows".into(),
1073 ));
1074 };
1075 if columns.len() != 1 {
1076 return Err(EngineError::Unsupported(
1082 if columns.is_empty() {
1083 "subquery has too few columns"
1084 } else {
1085 "subquery has too many columns"
1086 }
1087 .into(),
1088 ));
1089 }
1090 let mut list: Vec<Expr> = Vec::with_capacity(rows.len());
1096 for row in rows {
1097 let v = row.values.into_iter().next().unwrap_or(Value::Null);
1098 list.push(value_to_literal_expr(v)?);
1099 }
1100 Ok(Some(Expr::InList {
1101 expr: expr.clone(),
1102 list,
1103 negated: *negated,
1104 }))
1105 }
1106
1107 #[inline(never)]
1110 fn arm_exists(&self, e: &Expr, cancel: CancelToken<'_>) -> Result<Option<Expr>, EngineError> {
1111 let Expr::Exists { subquery, negated } = e else {
1112 return Ok(None);
1113 };
1114
1115 if select_is_correlated(subquery) {
1116 return Ok(None);
1117 }
1118 let mut s = (**subquery).clone();
1119 self.resolve_select_subqueries(&mut s, cancel)?;
1120 let r = match self.exec_select_cancel(&s, cancel) {
1121 Ok(r) => r,
1122 Err(e) if is_correlation_error(&e) => return Ok(None),
1123 Err(e) => return Err(e),
1124 };
1125 let exists = match r {
1126 QueryResult::Rows { rows, .. } => !rows.is_empty(),
1127 QueryResult::CommandOk { .. } => false,
1128 };
1129 let bit = if *negated { !exists } else { exists };
1130 Ok(Some(Expr::Literal(Literal::Bool(bit))))
1131 }
1132
1133 #[inline(never)]
1136 fn arm_scalar_subquery(
1137 &self,
1138 e: &Expr,
1139 cancel: CancelToken<'_>,
1140 ) -> Result<Option<Expr>, EngineError> {
1141 let Expr::ScalarSubquery(inner) = e else {
1142 return Ok(None);
1143 };
1144
1145 if select_is_correlated(inner) {
1150 return Ok(None);
1151 }
1152 let mut s = (**inner).clone();
1153 self.resolve_select_subqueries(&mut s, cancel)?;
1156 let r = match self.exec_select_cancel(&s, cancel) {
1157 Ok(r) => r,
1158 Err(e) if is_correlation_error(&e) => return Ok(None),
1159 Err(e) => return Err(e),
1160 };
1161 let QueryResult::Rows { columns, rows, .. } = r else {
1162 return Err(EngineError::Unsupported(
1163 "scalar subquery: inner statement did not return rows".into(),
1164 ));
1165 };
1166 scalar_subquery_arity(columns.len())?;
1167 let value = match rows.as_slice() {
1168 [] => Value::Null,
1169 [row] => row.values.first().cloned().unwrap_or(Value::Null),
1170 _ => {
1171 return Err(EngineError::CardinalityViolation);
1172 }
1173 };
1174 Ok(Some(crate::substitute::value_to_literal_expr_typed(
1177 value,
1178 columns.first().map(|c| c.ty),
1179 )?))
1180 }
1181
1182 pub(crate) fn subquery_replacement(
1183 &self,
1184 e: &Expr,
1185 cancel: CancelToken<'_>,
1186 ) -> Result<Option<Expr>, EngineError> {
1187 match e {
1188 Expr::ScalarSubquery(..) => self.arm_scalar_subquery(e, cancel),
1189 Expr::Exists { .. } => self.arm_exists(e, cancel),
1190 Expr::InSubquery { .. } => self.arm_in_subquery(e, cancel),
1191 Expr::RowInSubquery { .. } => self.arm_row_in_subquery(e, cancel),
1192 Expr::RowCmpSubquery { .. } => self.arm_row_cmp_subquery(e, cancel),
1193 _ => Ok(None),
1194 }
1195 }
1196}
1197
1198impl Engine {
1199 pub(crate) fn try_batch_correlated_scalar(
1208 &self,
1209 inner: &SelectStatement,
1210 restrict: Option<(&[Row<'static>], &EvalContext<'_>)>,
1211 cancel: CancelToken<'_>,
1212 ) -> Result<Option<memoize::GroupMap>, EngineError> {
1213 use spg_sql::ast::{BinOp, SelectItem as SI};
1214 if !inner.ctes.is_empty()
1215 || !inner.unions.is_empty()
1216 || inner.group_by.is_some()
1217 || inner.having.is_some()
1218 || inner.distinct
1219 || inner.items.len() != 1
1220 || inner.order_by.len() > 1
1221 || inner.offset.is_some()
1222 {
1223 return Ok(None);
1224 }
1225 if let Some(le) = &inner.limit
1227 && le.as_literal() != Some(1)
1228 {
1229 return Ok(None);
1230 }
1231 let Some(from) = &inner.from else {
1232 return Ok(None);
1233 };
1234 if from.primary.lateral_subquery.is_some() || from.primary.unnest_expr.is_some() {
1235 return Ok(None);
1236 }
1237 let mut inner_aliases: Vec<String> = Vec::new();
1239 inner_aliases.push(
1240 from.primary
1241 .alias
1242 .clone()
1243 .unwrap_or_else(|| from.primary.name.clone()),
1244 );
1245 for j in &from.joins {
1246 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
1247 return Ok(None);
1248 }
1249 inner_aliases.push(
1250 j.table
1251 .alias
1252 .clone()
1253 .unwrap_or_else(|| j.table.name.clone()),
1254 );
1255 }
1256 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
1257 match &c.qualifier {
1258 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1259 None => false,
1260 }
1261 };
1262 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
1263 match &c.qualifier {
1264 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1265 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
1268 }
1269 };
1270 let all_inner = |e: &Expr| -> bool {
1273 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1274 let mut subs: Vec<&SelectStatement> = Vec::new();
1275 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
1276 subs.push(sub)
1277 });
1278 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
1279 };
1280 let Some(w) = &inner.where_ else {
1281 return Ok(None);
1282 };
1283 let conjuncts = reorder::split_and_conjunctions(w);
1284 let mut corr: Option<(spg_sql::ast::ColumnName, spg_sql::ast::ColumnName)> = None; let mut rest: Vec<&Expr> = Vec::new();
1286 for c in conjuncts {
1287 if let Expr::Binary {
1288 lhs,
1289 op: BinOp::Eq,
1290 rhs,
1291 } = c
1292 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
1293 {
1294 let pair = if is_inner(a) && is_outer(b) {
1295 Some((a.clone(), b.clone()))
1296 } else if is_inner(b) && is_outer(a) {
1297 Some((b.clone(), a.clone()))
1298 } else {
1299 None
1300 };
1301 if let Some(p) = pair {
1302 if corr.is_some() {
1303 return Ok(None); }
1305 corr = Some(p);
1306 continue;
1307 }
1308 }
1309 if !all_inner(c) {
1310 return Ok(None);
1311 }
1312 rest.push(c);
1313 }
1314 let Some((inner_col, outer_col)) = corr else {
1315 return Ok(None);
1316 };
1317 let SI::Expr { expr: out_expr, .. } = &inner.items[0] else {
1318 return Ok(None);
1319 };
1320 if !all_inner(out_expr) {
1321 return Ok(None);
1322 }
1323 let order = inner.order_by.first();
1324 if let Some(o) = order
1325 && !all_inner(&o.expr)
1326 {
1327 return Ok(None);
1328 }
1329 let mut batch = inner.clone();
1332 batch.limit = None;
1333 batch.offset = None;
1334 batch.order_by = Vec::new();
1335 batch.where_ = rest
1336 .iter()
1337 .map(|e| (*e).clone())
1338 .reduce(|a, b| Expr::Binary {
1339 lhs: alloc::boxed::Box::new(a),
1340 op: BinOp::And,
1341 rhs: alloc::boxed::Box::new(b),
1342 });
1343 let mut items: Vec<SI> = alloc::vec![SI::Expr {
1344 expr: Expr::Column(inner_col.clone()),
1345 alias: None,
1346 }];
1347 if let Some(o) = order {
1348 items.push(SI::Expr {
1349 expr: o.expr.clone(),
1350 alias: None,
1351 });
1352 }
1353 items.push(SI::Expr {
1354 expr: out_expr.clone(),
1355 alias: None,
1356 });
1357 batch.items = items;
1358 if aggregate::contains_aggregate(out_expr) {
1370 batch.group_by = Some(alloc::vec![Expr::Column(inner_col.clone())]);
1371 }
1372 let keyed: Option<(&[Row<'static>], &EvalContext<'_>)> =
1395 restrict.and_then(|(rows, rctx)| {
1396 let driver_name: &str = if from.joins.is_empty() {
1398 from.primary.name.as_str()
1399 } else {
1400 let q = inner_col.qualifier.as_deref()?;
1401 let primary_alias = from
1402 .primary
1403 .alias
1404 .as_deref()
1405 .unwrap_or(from.primary.name.as_str());
1406 if primary_alias.eq_ignore_ascii_case(q) {
1407 from.primary.name.as_str()
1408 } else {
1409 from.joins
1410 .iter()
1411 .find(|j| {
1412 j.table
1413 .alias
1414 .as_deref()
1415 .unwrap_or(j.table.name.as_str())
1416 .eq_ignore_ascii_case(q)
1417 })
1418 .map(|j| j.table.name.as_str())?
1419 }
1420 };
1421 let table = self.active_catalog().get(driver_name)?;
1422 let pos = table
1423 .schema()
1424 .columns
1425 .iter()
1426 .position(|c| c.name.eq_ignore_ascii_case(&inner_col.name))?;
1427 table.index_on(pos)?;
1428 if rows.len().saturating_mul(4) >= table.row_count() {
1443 return None;
1444 }
1445 if !from.joins.is_empty() {
1451 let driver_alias = inner_col.qualifier.as_deref()?;
1452 if !reorder::drive_from(&mut batch, driver_alias) {
1453 return None;
1454 }
1455 }
1456 Some((rows, rctx))
1457 });
1458 let rows = if let Some((restrict_rows, rctx)) = keyed {
1459 BATCHED_SCALAR_KEYED_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
1460 let mut seen: alloc::collections::BTreeSet<String> =
1475 alloc::collections::BTreeSet::new();
1476 let mut key_lits: Vec<Expr> = Vec::new();
1477 for srow in restrict_rows {
1478 cancel.check()?;
1479 let kv = eval::eval_expr(&Expr::Column(outer_col.clone()), srow, rctx)
1480 .map_err(EngineError::Eval)?;
1481 if matches!(kv, Value::Null) {
1482 continue;
1483 }
1484 if !seen.insert(aggregate::encode_key(core::slice::from_ref(&kv))) {
1485 continue;
1486 }
1487 key_lits.push(value_to_literal_expr(kv)?);
1488 }
1489 if key_lits.is_empty() {
1490 Vec::new()
1491 } else {
1492 let in_pred = Expr::InList {
1493 expr: alloc::boxed::Box::new(Expr::Column(inner_col.clone())),
1494 list: key_lits,
1495 negated: false,
1496 };
1497 let mut probe = batch.clone();
1498 probe.where_ = Some(match probe.where_.take() {
1499 Some(w) => Expr::Binary {
1500 lhs: alloc::boxed::Box::new(w),
1501 op: BinOp::And,
1502 rhs: alloc::boxed::Box::new(in_pred),
1503 },
1504 None => in_pred,
1505 });
1506 BATCHED_SCALAR_KEYED_PROBE_COUNT
1507 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
1508 if let QueryResult::Rows { rows, .. } = self.exec_select_cancel(&probe, cancel)? {
1509 rows
1510 } else {
1511 Vec::new()
1512 }
1513 }
1514 } else {
1515 BATCHED_SCALAR_FALL_THROUGH_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
1516 let r = self.exec_select_cancel(&batch, cancel)?;
1517 let QueryResult::Rows { rows, .. } = r else {
1518 return Ok(None);
1519 };
1520 rows
1521 };
1522 let has_order = order.is_some();
1523 let (desc, nf) = order
1524 .map(|o| (o.desc, o.nulls_first))
1525 .unwrap_or((false, None));
1526 let mut best: alloc::collections::BTreeMap<String, (Option<Value>, Value)> =
1527 alloc::collections::BTreeMap::new();
1528 for row in rows {
1529 let key_v = row.values.first().cloned().unwrap_or(Value::Null);
1530 if matches!(key_v, Value::Null) {
1531 continue;
1532 }
1533 let key = aggregate::encode_key(core::slice::from_ref(&key_v));
1534 let (ord_v, out_v) = if has_order {
1535 (
1536 Some(row.values.get(1).cloned().unwrap_or(Value::Null)),
1537 row.values.get(2).cloned().unwrap_or(Value::Null),
1538 )
1539 } else {
1540 (None, row.values.get(1).cloned().unwrap_or(Value::Null))
1541 };
1542 match best.get(&key) {
1543 None => {
1544 best.insert(key, (ord_v, out_v));
1545 }
1546 Some((cur_ord, _)) if has_order => {
1547 let cand = ord_v.clone().unwrap_or(Value::Null);
1551 let cur = cur_ord.clone().unwrap_or(Value::Null);
1552 if order_by_value_cmp(desc, nf, &cand, &cur) == core::cmp::Ordering::Less {
1553 best.insert(key, (ord_v, out_v));
1554 }
1555 }
1556 Some(_) => {} }
1558 }
1559 let map = best.into_iter().map(|(k, (_, v))| (k, v)).collect();
1560 let empty_default = scalar_subquery_empty_default(inner);
1565 Ok(Some((outer_col, map, empty_default)))
1566 }
1567}
1568
1569impl Engine {
1570 pub(crate) fn try_batch_correlated_exists(
1589 &self,
1590 inner: &SelectStatement,
1591 cancel: CancelToken<'_>,
1592 ) -> Result<Option<memoize::ExistsSet>, EngineError> {
1593 use spg_sql::ast::SelectItem as SI;
1594 if !inner.ctes.is_empty()
1595 || !inner.unions.is_empty()
1596 || inner.group_by.is_some()
1597 || inner.having.is_some()
1598 || inner.distinct
1599 {
1600 return Ok(None);
1601 }
1602 let Some(from) = &inner.from else {
1603 return Ok(None);
1604 };
1605 if from.primary.lateral_subquery.is_some()
1606 || from.primary.unnest_expr.is_some()
1607 || from.primary.generate_series_args.is_some()
1608 || from.primary.as_of_segment.is_some()
1609 {
1610 return Ok(None);
1611 }
1612 let mut inner_aliases: Vec<String> = Vec::new();
1613 inner_aliases.push(
1614 from.primary
1615 .alias
1616 .clone()
1617 .unwrap_or_else(|| from.primary.name.clone()),
1618 );
1619 for j in &from.joins {
1620 if j.table.lateral_subquery.is_some() || j.table.unnest_expr.is_some() {
1621 return Ok(None);
1622 }
1623 inner_aliases.push(
1624 j.table
1625 .alias
1626 .clone()
1627 .unwrap_or_else(|| j.table.name.clone()),
1628 );
1629 }
1630 let is_inner = |c: &spg_sql::ast::ColumnName| -> bool {
1631 match &c.qualifier {
1632 Some(q) => inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1633 None => false,
1634 }
1635 };
1636 let is_outer = |c: &spg_sql::ast::ColumnName| -> bool {
1637 match &c.qualifier {
1638 Some(q) => !inner_aliases.iter().any(|a| a.eq_ignore_ascii_case(q)),
1639 None => c.name.starts_with("__grp_") || c.name.starts_with("__agg_"),
1640 }
1641 };
1642 let all_inner = |e: &Expr| -> bool {
1643 let mut cols: Vec<spg_sql::ast::ColumnName> = Vec::new();
1644 let mut subs: Vec<&SelectStatement> = Vec::new();
1645 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |sub| {
1646 subs.push(sub)
1647 });
1648 subs.is_empty() && cols.iter().all(|c| is_inner(c) && !c.name.is_empty())
1649 };
1650 let Some(w) = &inner.where_ else {
1651 return Ok(None);
1652 };
1653 let conjuncts = reorder::split_and_conjunctions(w);
1654 let outer_only_key = |e: &Expr| -> bool {
1662 fn shape(e: &Expr, is_outer: &dyn Fn(&spg_sql::ast::ColumnName) -> bool) -> bool {
1663 use spg_sql::ast::BinOp as B;
1664 match e {
1665 Expr::Column(c) => is_outer(c),
1666 Expr::Literal(_) => true,
1667 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } => shape(expr, is_outer),
1668 Expr::Binary { lhs, op, rhs } => {
1669 matches!(op, B::Add | B::Sub | B::Mul | B::Div | B::IntDiv | B::Mod)
1670 && shape(lhs, is_outer)
1671 && shape(rhs, is_outer)
1672 }
1673 _ => false,
1674 }
1675 }
1676 fn mentions_column(e: &Expr) -> bool {
1677 match e {
1678 Expr::Column(_) => true,
1679 Expr::Unary { expr, .. } | Expr::Cast { expr, .. } => mentions_column(expr),
1680 Expr::Binary { lhs, rhs, .. } => mentions_column(lhs) || mentions_column(rhs),
1681 _ => false,
1682 }
1683 }
1684 shape(e, &is_outer) && mentions_column(e)
1685 };
1686 let mut inner_keys: Vec<spg_sql::ast::ColumnName> = Vec::new();
1687 let mut outer_cols: Vec<Expr> = Vec::new();
1688 let mut rest: Vec<&Expr> = Vec::new();
1689 for c in conjuncts {
1690 if let Expr::Binary {
1691 lhs,
1692 op: BinOp::Eq,
1693 rhs,
1694 } = c
1695 {
1696 let pair = match (lhs.as_ref(), rhs.as_ref()) {
1697 (Expr::Column(a), other) if is_inner(a) && outer_only_key(other) => {
1698 Some((a.clone(), other.clone()))
1699 }
1700 (other, Expr::Column(b)) if is_inner(b) && outer_only_key(other) => {
1701 Some((b.clone(), other.clone()))
1702 }
1703 _ => None,
1704 };
1705 if let Some((ic, oc)) = pair {
1706 inner_keys.push(ic);
1707 outer_cols.push(oc);
1708 continue;
1709 }
1710 }
1711 if !all_inner(c) {
1715 return Ok(None);
1716 }
1717 rest.push(c);
1718 }
1719 if inner_keys.is_empty() {
1720 return Ok(None); }
1722 let mut batch = inner.clone();
1726 batch.limit = None;
1727 batch.offset = None;
1728 batch.order_by = Vec::new();
1729 batch.distinct = false;
1730 batch.where_ = rest
1731 .iter()
1732 .map(|e| (*e).clone())
1733 .reduce(|a, b| Expr::Binary {
1734 lhs: alloc::boxed::Box::new(a),
1735 op: BinOp::And,
1736 rhs: alloc::boxed::Box::new(b),
1737 });
1738 batch.items = inner_keys
1739 .iter()
1740 .map(|c| SI::Expr {
1741 expr: Expr::Column(c.clone()),
1742 alias: None,
1743 })
1744 .collect();
1745 let r = self.exec_select_cancel(&batch, cancel)?;
1746 let QueryResult::Rows { rows, .. } = r else {
1747 return Ok(None);
1748 };
1749 let n = inner_keys.len();
1750 let mut set: alloc::collections::BTreeSet<String> = alloc::collections::BTreeSet::new();
1751 for row in rows {
1752 let keys = row.values.get(..n).unwrap_or(&row.values);
1753 if keys.iter().any(|v| matches!(v, Value::Null)) {
1756 continue;
1757 }
1758 set.insert(aggregate::encode_canonical_key(keys));
1759 }
1760 Ok(Some((outer_cols, set)))
1761 }
1762}
1763
1764impl Engine {
1765 pub(crate) fn pull_up_correlated_limit_one_subqueries(
1839 &self,
1840 stmt: &mut SelectStatement,
1841 ) -> bool {
1842 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
1850 return false;
1851 }
1852 let outer_aliases: alloc::collections::BTreeSet<String> = {
1855 let from = stmt.from.as_ref().expect("from present");
1856 let mut s = alloc::collections::BTreeSet::new();
1857 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
1858 s.insert(
1859 t.alias
1860 .clone()
1861 .unwrap_or_else(|| t.name.clone())
1862 .to_ascii_lowercase(),
1863 );
1864 };
1865 push(&mut s, &from.primary);
1866 for j in &from.joins {
1867 push(&mut s, &j.table);
1868 }
1869 s
1870 };
1871 let outer_has_group_by = stmt.group_by.is_some() || stmt.group_by_all;
1872 let mut new_ctes: Vec<Cte> = Vec::new();
1873 let mut new_joins: Vec<FromJoin> = Vec::new();
1874 let cte_seed = stmt.ctes.len();
1875 for item in &mut stmt.items {
1876 if let SelectItem::Expr { expr, .. } = item {
1877 self.pull_up_walk_limit_one(
1878 expr,
1879 false,
1880 &outer_aliases,
1881 outer_has_group_by,
1882 cte_seed,
1883 &mut new_ctes,
1884 &mut new_joins,
1885 );
1886 }
1887 }
1888 if new_ctes.is_empty() {
1889 return false;
1890 }
1891 PULLUP_LIMIT1_FIRE_COUNT
1892 .fetch_add(new_ctes.len() as u64, core::sync::atomic::Ordering::Relaxed);
1893 stmt.ctes.extend(new_ctes);
1894 stmt.from
1895 .as_mut()
1896 .expect("from present")
1897 .joins
1898 .extend(new_joins);
1899 true
1900 }
1901
1902 #[allow(clippy::too_many_arguments)]
1907 fn pull_up_walk_limit_one(
1908 &self,
1909 e: &mut Expr,
1910 in_agg: bool,
1911 outer_aliases: &alloc::collections::BTreeSet<String>,
1912 outer_has_group_by: bool,
1913 cte_seed: usize,
1914 ctes_out: &mut Vec<Cte>,
1915 joins_out: &mut Vec<FromJoin>,
1916 ) {
1917 match e {
1918 Expr::ScalarSubquery(inner) => {
1919 if let Some((cte, join, cte_col)) =
1920 self.try_pull_up_limit_one(inner, outer_aliases, cte_seed + ctes_out.len())
1921 {
1922 ctes_out.push(cte);
1923 joins_out.push(join);
1924 let col_expr = Expr::Column(cte_col);
1934 *e = if outer_has_group_by && !in_agg {
1935 Expr::FunctionCall {
1936 name: "max".into(),
1937 args: alloc::vec![col_expr],
1938 }
1939 } else {
1940 col_expr
1941 };
1942 }
1943 }
1946 Expr::FunctionCall { name, args } => {
1947 let child = in_agg || aggregate::is_aggregate_name(name);
1948 for a in args.iter_mut() {
1949 self.pull_up_walk_limit_one(
1950 a,
1951 child,
1952 outer_aliases,
1953 outer_has_group_by,
1954 cte_seed,
1955 ctes_out,
1956 joins_out,
1957 );
1958 }
1959 }
1960 Expr::AggregateOrdered {
1961 call,
1962 order_by,
1963 filter,
1964 ..
1965 } => {
1966 self.pull_up_walk_limit_one(
1967 call,
1968 true,
1969 outer_aliases,
1970 outer_has_group_by,
1971 cte_seed,
1972 ctes_out,
1973 joins_out,
1974 );
1975 for o in order_by.iter_mut() {
1976 self.pull_up_walk_limit_one(
1977 &mut o.expr,
1978 true,
1979 outer_aliases,
1980 outer_has_group_by,
1981 cte_seed,
1982 ctes_out,
1983 joins_out,
1984 );
1985 }
1986 if let Some(f) = filter {
1987 self.pull_up_walk_limit_one(
1988 f,
1989 true,
1990 outer_aliases,
1991 outer_has_group_by,
1992 cte_seed,
1993 ctes_out,
1994 joins_out,
1995 );
1996 }
1997 }
1998 Expr::Binary { lhs, rhs, .. } => {
1999 self.pull_up_walk_limit_one(
2000 lhs,
2001 in_agg,
2002 outer_aliases,
2003 outer_has_group_by,
2004 cte_seed,
2005 ctes_out,
2006 joins_out,
2007 );
2008 self.pull_up_walk_limit_one(
2009 rhs,
2010 in_agg,
2011 outer_aliases,
2012 outer_has_group_by,
2013 cte_seed,
2014 ctes_out,
2015 joins_out,
2016 );
2017 }
2018 Expr::Unary { expr, .. }
2019 | Expr::Cast { expr, .. }
2020 | Expr::IsNull { expr, .. }
2021 | Expr::BoolTest { expr, .. }
2022 | Expr::FieldAccess { base: expr, .. } => {
2023 self.pull_up_walk_limit_one(
2024 expr,
2025 in_agg,
2026 outer_aliases,
2027 outer_has_group_by,
2028 cte_seed,
2029 ctes_out,
2030 joins_out,
2031 );
2032 }
2033 Expr::Like { expr, pattern, .. } => {
2034 self.pull_up_walk_limit_one(
2035 expr,
2036 in_agg,
2037 outer_aliases,
2038 outer_has_group_by,
2039 cte_seed,
2040 ctes_out,
2041 joins_out,
2042 );
2043 self.pull_up_walk_limit_one(
2044 pattern,
2045 in_agg,
2046 outer_aliases,
2047 outer_has_group_by,
2048 cte_seed,
2049 ctes_out,
2050 joins_out,
2051 );
2052 }
2053 Expr::InList { expr, list, .. } => {
2054 self.pull_up_walk_limit_one(
2055 expr,
2056 in_agg,
2057 outer_aliases,
2058 outer_has_group_by,
2059 cte_seed,
2060 ctes_out,
2061 joins_out,
2062 );
2063 for it in list.iter_mut() {
2064 self.pull_up_walk_limit_one(
2065 it,
2066 in_agg,
2067 outer_aliases,
2068 outer_has_group_by,
2069 cte_seed,
2070 ctes_out,
2071 joins_out,
2072 );
2073 }
2074 }
2075 Expr::Case {
2076 operand,
2077 branches,
2078 else_branch,
2079 } => {
2080 if let Some(o) = operand {
2081 self.pull_up_walk_limit_one(
2082 o,
2083 in_agg,
2084 outer_aliases,
2085 outer_has_group_by,
2086 cte_seed,
2087 ctes_out,
2088 joins_out,
2089 );
2090 }
2091 for (w, t) in branches.iter_mut() {
2092 self.pull_up_walk_limit_one(
2093 w,
2094 in_agg,
2095 outer_aliases,
2096 outer_has_group_by,
2097 cte_seed,
2098 ctes_out,
2099 joins_out,
2100 );
2101 self.pull_up_walk_limit_one(
2102 t,
2103 in_agg,
2104 outer_aliases,
2105 outer_has_group_by,
2106 cte_seed,
2107 ctes_out,
2108 joins_out,
2109 );
2110 }
2111 if let Some(eb) = else_branch {
2112 self.pull_up_walk_limit_one(
2113 eb,
2114 in_agg,
2115 outer_aliases,
2116 outer_has_group_by,
2117 cte_seed,
2118 ctes_out,
2119 joins_out,
2120 );
2121 }
2122 }
2123 _ => {}
2126 }
2127 }
2128
2129 fn try_pull_up_limit_one(
2135 &self,
2136 inner: &SelectStatement,
2137 outer_aliases: &alloc::collections::BTreeSet<String>,
2138 alias_n: usize,
2139 ) -> Option<(Cte, FromJoin, ColumnName)> {
2140 return None;
2166 #[allow(unreachable_code)]
2167 if !inner.ctes.is_empty()
2169 || !inner.unions.is_empty()
2170 || inner.group_by.is_some()
2171 || inner.group_by_all
2172 || inner.having.is_some()
2173 || inner.distinct
2174 || inner.offset.is_some()
2175 || inner.items.len() != 1
2176 || inner.order_by.is_empty()
2177 {
2178 return None;
2179 }
2180 match inner.limit {
2183 Some(LimitExpr::Literal(1)) => {}
2184 _ => return None,
2185 }
2186 let from = inner.from.as_ref()?;
2187 if !from.joins.is_empty()
2190 || from.primary.lateral_subquery.is_some()
2191 || from.primary.unnest_expr.is_some()
2192 || from.primary.generate_series_args.is_some()
2193 || from.primary.as_of_segment.is_some()
2194 {
2195 return None;
2196 }
2197 let inner_table = from.primary.name.clone();
2198 let inner_alias = from
2199 .primary
2200 .alias
2201 .clone()
2202 .unwrap_or_else(|| inner_table.clone());
2203 let is_inner = |c: &ColumnName| -> bool {
2204 c.qualifier
2205 .as_deref()
2206 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2207 };
2208 let is_outer = |c: &ColumnName| -> bool {
2209 c.qualifier
2210 .as_deref()
2211 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2212 };
2213 let SelectItem::Expr {
2218 expr: proj_expr,
2219 alias: _,
2220 } = &inner.items[0]
2221 else {
2222 return None;
2223 };
2224 if proj_has_disqualifying_shape(proj_expr, &inner_alias, outer_aliases) {
2225 return None;
2226 }
2227 let where_ = inner.where_.as_ref()?;
2230 let mut corr: Option<(String, ColumnName)> = None;
2231 let mut non_corr: Vec<Expr> = Vec::new();
2232 for c in reorder::split_and_conjunctions(where_) {
2233 if let Expr::Binary {
2234 lhs,
2235 op: BinOp::Eq,
2236 rhs,
2237 } = c
2238 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2239 {
2240 let pair = if is_inner(a) && is_outer(b) {
2241 Some((a.name.clone(), b.clone()))
2242 } else if is_inner(b) && is_outer(a) {
2243 Some((b.name.clone(), a.clone()))
2244 } else {
2245 None
2246 };
2247 if let Some(p) = pair {
2248 if corr.is_some() {
2249 return None; }
2251 corr = Some(p);
2252 continue;
2253 }
2254 }
2255 if !expr_is_all_inner(c, &inner_alias) {
2256 return None;
2257 }
2258 non_corr.push(c.clone());
2259 }
2260 let (inner_key, outer_col) = corr?;
2261 for ob in &inner.order_by {
2264 if !expr_is_all_inner(&ob.expr, &inner_alias) {
2265 return None;
2266 }
2267 }
2268 if !expr_is_all_inner(proj_expr, &inner_alias) {
2270 return None;
2271 }
2272 let cte_name = alloc::format!("__cl1_{alias_n}");
2278 let jk_expr = Expr::Column(ColumnName {
2279 qualifier: Some(inner_alias.clone()),
2280 name: inner_key.clone(),
2281 });
2282 let argmax = Expr::ArraySubscript {
2283 target: alloc::boxed::Box::new(Expr::AggregateOrdered {
2284 call: alloc::boxed::Box::new(Expr::FunctionCall {
2285 name: "array_agg".into(),
2286 args: alloc::vec![proj_expr.clone()],
2287 }),
2288 order_by: inner.order_by.clone(),
2289 distinct: false,
2290 filter: None,
2291 }),
2292 index: alloc::boxed::Box::new(Expr::Literal(Literal::Integer(1))),
2293 };
2294 let body_where = if non_corr.is_empty() {
2295 None
2296 } else {
2297 let mut iter = non_corr.into_iter();
2298 let head = iter.next().expect("non_corr nonempty in this branch");
2299 Some(iter.fold(head, |acc, p| Expr::Binary {
2300 lhs: alloc::boxed::Box::new(acc),
2301 op: BinOp::And,
2302 rhs: alloc::boxed::Box::new(p),
2303 }))
2304 };
2305 let body = SelectStatement {
2306 locking: None,
2307 ctes: Vec::new(),
2308 distinct: false,
2309 distinct_on: Vec::new(),
2310 items: alloc::vec![
2311 SelectItem::Expr {
2312 expr: jk_expr.clone(),
2313 alias: Some("jk".into()),
2314 },
2315 SelectItem::Expr {
2316 expr: argmax,
2317 alias: Some("pj".into()),
2318 },
2319 ],
2320 from: Some(from.clone()),
2321 where_: body_where,
2322 group_by: Some(alloc::vec![jk_expr]),
2323 group_by_all: false,
2324 having: None,
2325 unions: Vec::new(),
2326 order_by: Vec::new(),
2327 limit: None,
2328 offset: None,
2329 limit_with_ties: false,
2330 window_check_exprs: Vec::new(),
2331 };
2332 let cte = Cte {
2333 name: cte_name.clone(),
2334 body: spg_sql::ast::CteBody::Select(body),
2335 recursive: false,
2336 column_overrides: Vec::new(),
2337 search: None,
2338 cycle: None,
2339 };
2340 let join = FromJoin {
2342 kind: JoinKind::Left,
2343 table: TableRef {
2344 name: cte_name.clone(),
2345 alias: None,
2346 only: false,
2347 as_of_segment: None,
2348 unnest_expr: None,
2349 unnest_column_aliases: Vec::new(),
2350 with_ordinality: false,
2351 generate_series_args: None,
2352 lateral_subquery: None,
2353 jsonb_each_text_arg: None,
2354 table_fn_call: None,
2355 rows_from: None,
2356 json_table: None,
2357 scalar_fn_item: false,
2358 },
2359 on: Some(Expr::Binary {
2360 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
2361 qualifier: Some(cte_name.clone()),
2362 name: "jk".into(),
2363 })),
2364 op: BinOp::Eq,
2365 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
2366 }),
2367 using_cols: None,
2368 natural: false,
2369 };
2370 let repl = ColumnName {
2371 qualifier: Some(cte_name),
2372 name: "pj".into(),
2373 };
2374 Some((cte, join, repl))
2375 }
2376
2377 pub(crate) fn pull_up_unique_correlated_agg_subqueries(
2378 &self,
2379 stmt: &mut SelectStatement,
2380 ) -> bool {
2381 if stmt.from.is_none() || stmt.items.iter().any(|i| matches!(i, SelectItem::Wildcard)) {
2382 return false;
2383 }
2384 let outer_aliases: alloc::collections::BTreeSet<String> = {
2386 let from = stmt.from.as_ref().expect("from present");
2387 let mut s = alloc::collections::BTreeSet::new();
2388 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
2389 s.insert(
2390 t.alias
2391 .clone()
2392 .unwrap_or_else(|| t.name.clone())
2393 .to_ascii_lowercase(),
2394 );
2395 };
2396 push(&mut s, &from.primary);
2397 for j in &from.joins {
2398 push(&mut s, &j.table);
2399 }
2400 s
2401 };
2402 let mut new_joins: Vec<FromJoin> = Vec::new();
2403 for item in &mut stmt.items {
2404 if let SelectItem::Expr { expr, .. } = item {
2405 self.pull_up_walk(expr, false, &outer_aliases, &mut new_joins);
2406 }
2407 }
2408 if new_joins.is_empty() {
2409 return false;
2410 }
2411 stmt.from
2412 .as_mut()
2413 .expect("from present")
2414 .joins
2415 .extend(new_joins);
2416 true
2417 }
2418
2419 fn pull_up_walk(
2424 &self,
2425 e: &mut Expr,
2426 in_agg: bool,
2427 outer_aliases: &alloc::collections::BTreeSet<String>,
2428 joins_out: &mut Vec<FromJoin>,
2429 ) {
2430 match e {
2431 Expr::ScalarSubquery(inner) => {
2432 if in_agg
2433 && let Some((join, col)) =
2434 self.try_pull_up_join(inner, outer_aliases, joins_out.len())
2435 {
2436 joins_out.push(join);
2437 *e = Expr::Column(col);
2438 }
2439 }
2442 Expr::FunctionCall { name, args } => {
2443 let child = in_agg || aggregate::is_aggregate_name(name);
2444 for a in args.iter_mut() {
2445 self.pull_up_walk(a, child, outer_aliases, joins_out);
2446 }
2447 }
2448 Expr::AggregateOrdered {
2449 call,
2450 order_by,
2451 filter,
2452 ..
2453 } => {
2454 self.pull_up_walk(call, true, outer_aliases, joins_out);
2455 for o in order_by.iter_mut() {
2456 self.pull_up_walk(&mut o.expr, true, outer_aliases, joins_out);
2457 }
2458 if let Some(f) = filter {
2459 self.pull_up_walk(f, true, outer_aliases, joins_out);
2460 }
2461 }
2462 Expr::Binary { lhs, rhs, .. } => {
2463 self.pull_up_walk(lhs, in_agg, outer_aliases, joins_out);
2464 self.pull_up_walk(rhs, in_agg, outer_aliases, joins_out);
2465 }
2466 Expr::Unary { expr, .. }
2467 | Expr::Cast { expr, .. }
2468 | Expr::IsNull { expr, .. }
2469 | Expr::BoolTest { expr, .. }
2470 | Expr::FieldAccess { base: expr, .. } => {
2471 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
2472 }
2473 Expr::Like { expr, pattern, .. } => {
2474 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
2475 self.pull_up_walk(pattern, in_agg, outer_aliases, joins_out);
2476 }
2477 Expr::InList { expr, list, .. } => {
2478 self.pull_up_walk(expr, in_agg, outer_aliases, joins_out);
2479 for it in list.iter_mut() {
2480 self.pull_up_walk(it, in_agg, outer_aliases, joins_out);
2481 }
2482 }
2483 Expr::Case {
2484 operand,
2485 branches,
2486 else_branch,
2487 } => {
2488 if let Some(o) = operand {
2489 self.pull_up_walk(o, in_agg, outer_aliases, joins_out);
2490 }
2491 for (w, t) in branches.iter_mut() {
2492 self.pull_up_walk(w, in_agg, outer_aliases, joins_out);
2493 self.pull_up_walk(t, in_agg, outer_aliases, joins_out);
2494 }
2495 if let Some(eb) = else_branch {
2496 self.pull_up_walk(eb, in_agg, outer_aliases, joins_out);
2497 }
2498 }
2499 _ => {}
2504 }
2505 }
2506
2507 fn try_pull_up_join(
2511 &self,
2512 inner: &SelectStatement,
2513 outer_aliases: &alloc::collections::BTreeSet<String>,
2514 alias_n: usize,
2515 ) -> Option<(FromJoin, ColumnName)> {
2516 if !inner.ctes.is_empty()
2519 || !inner.unions.is_empty()
2520 || inner.group_by.is_some()
2521 || inner.having.is_some()
2522 || inner.distinct
2523 || !inner.order_by.is_empty()
2524 || inner.limit.is_some()
2525 || inner.offset.is_some()
2526 || inner.items.len() != 1
2527 {
2528 return None;
2529 }
2530 let from = inner.from.as_ref()?;
2531 if !from.joins.is_empty()
2532 || from.primary.lateral_subquery.is_some()
2533 || from.primary.unnest_expr.is_some()
2534 || from.primary.generate_series_args.is_some()
2535 || from.primary.as_of_segment.is_some()
2536 {
2537 return None;
2538 }
2539 let inner_table = from.primary.name.clone();
2540 let inner_alias = from
2541 .primary
2542 .alias
2543 .clone()
2544 .unwrap_or_else(|| inner_table.clone());
2545 let is_inner = |c: &ColumnName| -> bool {
2546 c.qualifier
2547 .as_deref()
2548 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2549 };
2550 let is_outer = |c: &ColumnName| -> bool {
2551 c.qualifier
2552 .as_deref()
2553 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2554 };
2555 let SelectItem::Expr { expr: out_expr, .. } = &inner.items[0] else {
2557 return None;
2558 };
2559 let Expr::Column(out_col) = out_expr else {
2560 return None;
2561 };
2562 if !is_inner(out_col) {
2563 return None;
2564 }
2565 let w = inner.where_.as_ref()?;
2567 let mut corr: Option<(String, ColumnName)> = None;
2568 let mut rest: Vec<Expr> = Vec::new();
2569 for c in reorder::split_and_conjunctions(w) {
2570 if let Expr::Binary {
2571 lhs,
2572 op: BinOp::Eq,
2573 rhs,
2574 } = c
2575 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2576 {
2577 let pair = if is_inner(a) && is_outer(b) {
2578 Some((a.name.clone(), b.clone()))
2579 } else if is_inner(b) && is_outer(a) {
2580 Some((b.name.clone(), a.clone()))
2581 } else {
2582 None
2583 };
2584 if let Some(p) = pair {
2585 if corr.is_some() {
2586 return None; }
2588 corr = Some(p);
2589 continue;
2590 }
2591 }
2592 if !expr_is_all_inner(c, &inner_alias) {
2593 return None;
2594 }
2595 rest.push(c.clone());
2596 }
2597 let (inner_key, outer_col) = corr?;
2598 if !self.column_is_single_unique(&inner_table, &inner_key) {
2601 return None;
2602 }
2603 let fresh = alloc::format!("__plj_{alias_n}");
2605 let key_eq = Expr::Binary {
2606 lhs: alloc::boxed::Box::new(Expr::Column(ColumnName {
2607 qualifier: Some(fresh.clone()),
2608 name: inner_key,
2609 })),
2610 op: BinOp::Eq,
2611 rhs: alloc::boxed::Box::new(Expr::Column(outer_col)),
2612 };
2613 let on = rest
2614 .into_iter()
2615 .map(|mut e| {
2616 rename_qualifier(&mut e, &inner_alias, &fresh);
2617 e
2618 })
2619 .fold(key_eq, |acc, pred| Expr::Binary {
2620 lhs: alloc::boxed::Box::new(acc),
2621 op: BinOp::And,
2622 rhs: alloc::boxed::Box::new(pred),
2623 });
2624 let join = FromJoin {
2625 kind: JoinKind::Left,
2626 table: TableRef {
2627 name: inner_table,
2628 alias: Some(fresh.clone()),
2629 only: false,
2630 as_of_segment: None,
2631 unnest_expr: None,
2632 unnest_column_aliases: Vec::new(),
2633 with_ordinality: false,
2634 generate_series_args: None,
2635 lateral_subquery: None,
2636 jsonb_each_text_arg: None,
2637 table_fn_call: None,
2638 rows_from: None,
2639 json_table: None,
2640 scalar_fn_item: false,
2641 },
2642 on: Some(on),
2643 using_cols: None,
2644 natural: false,
2645 };
2646 let repl = ColumnName {
2647 qualifier: Some(fresh),
2648 name: out_col.name.clone(),
2649 };
2650 Some((join, repl))
2651 }
2652
2653 pub(crate) fn pull_up_exists_sublinks(&self, stmt: &mut SelectStatement) -> bool {
2684 if stmt.from.is_none() {
2685 return false;
2686 }
2687 let Some(where_expr) = stmt.where_.take() else {
2688 return false;
2689 };
2690 let mut collision_names: alloc::collections::BTreeSet<String> =
2701 alloc::collections::BTreeSet::new();
2702 for c in reorder::split_and_conjunctions(&where_expr) {
2703 let inner_subq: Option<&SelectStatement> = match c {
2704 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2705 Expr::Unary {
2706 op: UnOp::Not,
2707 expr,
2708 } => match expr.as_ref() {
2709 Expr::Exists { subquery, .. } => Some(subquery.as_ref()),
2710 _ => None,
2711 },
2712 _ => None,
2713 };
2714 let Some(inner) = inner_subq else { continue };
2715 let Some(from) = &inner.from else { continue };
2716 if !from.joins.is_empty() {
2717 continue;
2718 }
2719 let Some(t) = self.active_catalog().get(&from.primary.name) else {
2720 continue;
2721 };
2722 for col in &t.schema().columns {
2723 collision_names.insert(col.name.to_ascii_lowercase());
2724 }
2725 }
2726 let mut where_expr = where_expr;
2727 if !collision_names.is_empty() {
2728 let from = stmt.from.as_ref().expect("from present");
2729 let outer_tables: Vec<(String, String)> = {
2730 let mut v = Vec::new();
2731 let collect = |v: &mut Vec<(String, String)>, t: &TableRef| {
2732 let alias = t.alias.clone().unwrap_or_else(|| t.name.clone());
2733 v.push((alias, t.name.clone()));
2734 };
2735 collect(&mut v, &from.primary);
2736 for j in &from.joins {
2737 collect(&mut v, &j.table);
2738 }
2739 v
2740 };
2741 let mut owner: alloc::collections::BTreeMap<String, String> =
2742 alloc::collections::BTreeMap::new();
2743 for col_lc in &collision_names {
2744 let mut matches: Vec<String> = Vec::new();
2745 for (alias, tname) in &outer_tables {
2746 let Some(t) = self.active_catalog().get(tname) else {
2747 continue;
2748 };
2749 if t.schema()
2750 .columns
2751 .iter()
2752 .any(|c| c.name.eq_ignore_ascii_case(col_lc))
2753 {
2754 matches.push(alias.clone());
2755 }
2756 }
2757 if matches.len() == 1 {
2758 owner.insert(col_lc.clone(), matches.remove(0));
2759 }
2760 }
2761 if !owner.is_empty() {
2762 disambiguate_stmt_unqualified_columns(stmt, &owner);
2763 disambiguate_expr_unqualified_columns(&mut where_expr, &owner);
2764 }
2765 }
2766 let outer_aliases: alloc::collections::BTreeSet<String> = {
2767 let from = stmt.from.as_ref().expect("from present");
2768 let mut s = alloc::collections::BTreeSet::new();
2769 let push = |s: &mut alloc::collections::BTreeSet<String>, t: &TableRef| {
2770 s.insert(
2771 t.alias
2772 .clone()
2773 .unwrap_or_else(|| t.name.clone())
2774 .to_ascii_lowercase(),
2775 );
2776 };
2777 push(&mut s, &from.primary);
2778 for j in &from.joins {
2779 push(&mut s, &j.table);
2780 }
2781 s
2782 };
2783 let outer_tables: alloc::collections::BTreeMap<String, String> = {
2787 let from = stmt.from.as_ref().expect("from present");
2788 let mut m = alloc::collections::BTreeMap::new();
2789 let push = |m: &mut alloc::collections::BTreeMap<String, String>, t: &TableRef| {
2790 m.insert(
2791 t.alias
2792 .clone()
2793 .unwrap_or_else(|| t.name.clone())
2794 .to_ascii_lowercase(),
2795 t.name.clone(),
2796 );
2797 };
2798 push(&mut m, &from.primary);
2799 for j in &from.joins {
2800 push(&mut m, &j.table);
2801 }
2802 m
2803 };
2804 let conjuncts = reorder::split_and_conjunctions(&where_expr);
2805 let mut survivors: Vec<Expr> = Vec::new();
2806 let mut new_joins: Vec<FromJoin> = Vec::new();
2807 let mut rewrote_any = false;
2808 for c in conjuncts {
2809 let parsed: Option<(&SelectStatement, bool)> = match c {
2814 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), *negated)),
2815 Expr::Unary {
2816 op: UnOp::Not,
2817 expr,
2818 } => match expr.as_ref() {
2819 Expr::Exists { subquery, negated } => Some((subquery.as_ref(), !*negated)),
2820 _ => None,
2821 },
2822 _ => None,
2823 };
2824 if let Some((subquery, neg)) = parsed {
2825 let outer_has_limit = stmt.limit.is_some();
2843 let try_in_first = outer_has_limit;
2844 let mut consumed = false;
2845 if try_in_first
2846 && let Some(rewritten) =
2847 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2848 {
2849 survivors.push(rewritten);
2850 consumed = true;
2851 }
2852 if !consumed
2853 && let Some((join, residual)) = self.try_pull_up_exists_sublink(
2854 subquery,
2855 neg,
2856 &outer_aliases,
2857 &outer_tables,
2858 new_joins.len(),
2859 )
2860 {
2861 new_joins.push(join);
2862 if let Some(r) = residual {
2863 survivors.push(r);
2864 }
2865 consumed = true;
2866 }
2867 if !consumed
2868 && !try_in_first
2869 && let Some(rewritten) =
2870 self.try_pull_up_exists_as_in(subquery, neg, &outer_aliases)
2871 {
2872 survivors.push(rewritten);
2875 consumed = true;
2876 }
2877 if consumed {
2878 rewrote_any = true;
2879 continue;
2880 }
2881 }
2882 survivors.push(c.clone());
2883 }
2884 if !rewrote_any {
2885 stmt.where_ = Some(where_expr);
2886 return false;
2887 }
2888 EXISTS_PULLUP_FIRE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
2889 if !new_joins.is_empty() {
2890 stmt.from
2891 .as_mut()
2892 .expect("from present")
2893 .joins
2894 .extend(new_joins);
2895 }
2896 stmt.where_ = survivors.into_iter().reduce(|a, b| Expr::Binary {
2897 lhs: alloc::boxed::Box::new(a),
2898 op: BinOp::And,
2899 rhs: alloc::boxed::Box::new(b),
2900 });
2901 true
2902 }
2903
2904 fn try_pull_up_exists_as_in(
2917 &self,
2918 inner: &SelectStatement,
2919 negated: bool,
2920 outer_aliases: &alloc::collections::BTreeSet<String>,
2921 ) -> Option<Expr> {
2922 if !inner.ctes.is_empty()
2923 || !inner.unions.is_empty()
2924 || inner.group_by.is_some()
2925 || inner.having.is_some()
2926 || inner.distinct
2927 || !inner.order_by.is_empty()
2928 || inner.limit.is_some()
2929 || inner.offset.is_some()
2930 {
2931 return None;
2932 }
2933 let from = inner.from.as_ref()?;
2934 if !from.joins.is_empty()
2935 || from.primary.lateral_subquery.is_some()
2936 || from.primary.unnest_expr.is_some()
2937 || from.primary.generate_series_args.is_some()
2938 || from.primary.as_of_segment.is_some()
2939 {
2940 return None;
2941 }
2942 let inner_table = from.primary.name.clone();
2943 let inner_alias = from
2944 .primary
2945 .alias
2946 .clone()
2947 .unwrap_or_else(|| inner_table.clone());
2948 let is_inner = |c: &ColumnName| -> bool {
2949 c.qualifier
2950 .as_deref()
2951 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
2952 };
2953 let is_outer = |c: &ColumnName| -> bool {
2954 c.qualifier
2955 .as_deref()
2956 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
2957 };
2958 let w = inner.where_.as_ref()?;
2959 let mut corr: Option<(String, ColumnName)> = None;
2960 let mut rest: Vec<Expr> = Vec::new();
2961 for c in reorder::split_and_conjunctions(w) {
2962 if let Expr::Binary {
2963 lhs,
2964 op: BinOp::Eq,
2965 rhs,
2966 } = c
2967 && let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref())
2968 {
2969 let pair = if is_inner(a) && is_outer(b) {
2970 Some((a.name.clone(), b.clone()))
2971 } else if is_inner(b) && is_outer(a) {
2972 Some((b.name.clone(), a.clone()))
2973 } else {
2974 None
2975 };
2976 if let Some(p) = pair {
2977 if corr.is_some() {
2978 return None;
2979 }
2980 corr = Some(p);
2981 continue;
2982 }
2983 }
2984 if !expr_is_all_inner(c, &inner_alias) {
2985 return None;
2986 }
2987 rest.push(c.clone());
2988 }
2989 let (inner_key, outer_col) = corr?;
2990 if negated && !self.column_is_not_null(&inner_table, &inner_key) {
2991 return None;
2992 }
2993 let mut rewritten = inner.clone();
2998 rewritten.limit = None;
2999 rewritten.offset = None;
3000 rewritten.order_by = Vec::new();
3001 rewritten.distinct = false;
3002 rewritten.where_ = rest.into_iter().reduce(|a, b| Expr::Binary {
3003 lhs: alloc::boxed::Box::new(a),
3004 op: BinOp::And,
3005 rhs: alloc::boxed::Box::new(b),
3006 });
3007 rewritten.items = alloc::vec![SelectItem::Expr {
3008 expr: Expr::Column(ColumnName {
3009 qualifier: Some(inner_alias),
3010 name: inner_key,
3011 }),
3012 alias: None,
3013 }];
3014 Some(Expr::InSubquery {
3015 expr: alloc::boxed::Box::new(Expr::Column(outer_col)),
3016 subquery: alloc::boxed::Box::new(rewritten),
3017 negated,
3018 })
3019 }
3020
3021 fn try_pull_up_exists_sublink(
3022 &self,
3023 inner: &SelectStatement,
3024 negated: bool,
3025 outer_aliases: &alloc::collections::BTreeSet<String>,
3026 outer_tables: &alloc::collections::BTreeMap<String, String>,
3027 alias_n: usize,
3028 ) -> Option<(FromJoin, Option<Expr>)> {
3029 EXISTS_PULLUP_CANDIDATE_COUNT.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3030 if !inner.ctes.is_empty()
3031 || !inner.unions.is_empty()
3032 || inner.group_by.is_some()
3033 || inner.having.is_some()
3034 || inner.distinct
3035 || !inner.order_by.is_empty()
3036 || inner.limit.is_some()
3037 || inner.offset.is_some()
3038 {
3039 EXISTS_PULLUP_BAIL_INNER_SHAPE.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3040 return None;
3041 }
3042 let from = inner.from.as_ref()?;
3043 if !from.joins.is_empty()
3044 || from.primary.lateral_subquery.is_some()
3045 || from.primary.unnest_expr.is_some()
3046 || from.primary.generate_series_args.is_some()
3047 || from.primary.as_of_segment.is_some()
3048 {
3049 EXISTS_PULLUP_BAIL_INNER_FROM.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3050 return None;
3051 }
3052 let inner_table = from.primary.name.clone();
3053 let inner_alias = from
3054 .primary
3055 .alias
3056 .clone()
3057 .unwrap_or_else(|| inner_table.clone());
3058 let is_inner = |c: &ColumnName| -> bool {
3059 c.qualifier
3060 .as_deref()
3061 .is_some_and(|q| q.eq_ignore_ascii_case(&inner_alias))
3062 };
3063 let is_outer = |c: &ColumnName| -> bool {
3064 c.qualifier
3065 .as_deref()
3066 .is_some_and(|q| outer_aliases.contains(&q.to_ascii_lowercase()))
3067 };
3068 let Some(w) = inner.where_.as_ref() else {
3069 EXISTS_PULLUP_BAIL_NO_WHERE.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3070 return None;
3071 };
3072 let inner_col_is_int = |name: &str| -> bool {
3100 self.active_catalog().get(&inner_table).is_some_and(|t| {
3101 t.schema().columns.iter().any(|cs| {
3102 cs.name.eq_ignore_ascii_case(name)
3103 && matches!(
3104 cs.ty,
3105 spg_storage::DataType::Int
3106 | spg_storage::DataType::BigInt
3107 | spg_storage::DataType::SmallInt
3108 )
3109 })
3110 })
3111 };
3112 enum InnerHalf {
3120 Col(String),
3121 IntExpr(Expr),
3122 }
3123 fn inner_int_only_expr(
3129 e: &Expr,
3130 is_inner: &dyn Fn(&ColumnName) -> bool,
3131 inner_col_is_int: &dyn Fn(&str) -> bool,
3132 ) -> bool {
3133 match e {
3134 Expr::Column(c) => is_inner(c) && inner_col_is_int(&c.name),
3135 Expr::Literal(spg_sql::ast::Literal::Integer(_)) => true,
3136 Expr::Binary { lhs, op, rhs } => {
3137 matches!(op, BinOp::Add | BinOp::Sub | BinOp::Mul)
3138 && inner_int_only_expr(lhs, is_inner, inner_col_is_int)
3139 && inner_int_only_expr(rhs, is_inner, inner_col_is_int)
3140 }
3141 _ => false,
3142 }
3143 }
3144 fn first_inner_col(e: &Expr) -> Option<String> {
3145 match e {
3146 Expr::Column(c) => Some(c.name.clone()),
3147 Expr::Binary { lhs, rhs, .. } => {
3148 first_inner_col(lhs).or_else(|| first_inner_col(rhs))
3149 }
3150 _ => None,
3151 }
3152 }
3153 let mut corr_pairs: Vec<(InnerHalf, Expr)> = Vec::new();
3154 let mut rest: Vec<Expr> = Vec::new();
3155 for c in reorder::split_and_conjunctions(w) {
3156 if let Expr::Binary {
3157 lhs,
3158 op: BinOp::Eq,
3159 rhs,
3160 } = c
3161 {
3162 let pair = match (lhs.as_ref(), rhs.as_ref()) {
3163 (Expr::Column(a), Expr::Column(b)) if is_inner(a) && is_outer(b) => {
3164 Some((InnerHalf::Col(a.name.clone()), Expr::Column(b.clone())))
3165 }
3166 (Expr::Column(a), Expr::Column(b)) if is_inner(b) && is_outer(a) => {
3167 Some((InnerHalf::Col(b.name.clone()), Expr::Column(a.clone())))
3168 }
3169 (Expr::Column(a), e)
3173 if is_inner(a)
3174 && !matches!(e, Expr::Column(_))
3175 && inner_col_is_int(&a.name)
3176 && outer_int_only_expr(e, outer_aliases, outer_tables, self) =>
3177 {
3178 Some((InnerHalf::Col(a.name.clone()), e.clone()))
3179 }
3180 (e, Expr::Column(a))
3181 if is_inner(a)
3182 && !matches!(e, Expr::Column(_))
3183 && inner_col_is_int(&a.name)
3184 && outer_int_only_expr(e, outer_aliases, outer_tables, self) =>
3185 {
3186 Some((InnerHalf::Col(a.name.clone()), e.clone()))
3187 }
3188 (Expr::Column(o), e)
3196 if is_outer(o)
3197 && !matches!(e, Expr::Column(_))
3198 && outer_int_only_expr(
3199 &Expr::Column(o.clone()),
3200 outer_aliases,
3201 outer_tables,
3202 self,
3203 )
3204 && inner_int_only_expr(e, &is_inner, &inner_col_is_int)
3205 && first_inner_col(e).is_some() =>
3206 {
3207 Some((InnerHalf::IntExpr(e.clone()), Expr::Column(o.clone())))
3208 }
3209 (e, Expr::Column(o))
3210 if is_outer(o)
3211 && !matches!(e, Expr::Column(_))
3212 && outer_int_only_expr(
3213 &Expr::Column(o.clone()),
3214 outer_aliases,
3215 outer_tables,
3216 self,
3217 )
3218 && inner_int_only_expr(e, &is_inner, &inner_col_is_int)
3219 && first_inner_col(e).is_some() =>
3220 {
3221 Some((InnerHalf::IntExpr(e.clone()), Expr::Column(o.clone())))
3222 }
3223 _ => None,
3224 };
3225 if let Some(p) = pair {
3226 corr_pairs.push(p);
3227 continue;
3228 }
3229 }
3230 if !expr_is_all_inner(c, &inner_alias) {
3231 EXISTS_PULLUP_BAIL_RESIDUAL_NOT_INNER
3232 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3233 return None;
3234 }
3235 rest.push(c.clone());
3236 }
3237 if corr_pairs.is_empty() {
3238 EXISTS_PULLUP_BAIL_NO_CORR.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3239 return None;
3240 }
3241 if corr_pairs.len() > 1
3245 && EXISTS_PULLUP_MULTICOL_DISABLE.load(core::sync::atomic::Ordering::Relaxed)
3246 {
3247 EXISTS_PULLUP_BAIL_MULTICOL_DISABLED
3248 .fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3249 return None;
3250 }
3251 let fresh = alloc::format!("__exsj_{alias_n}");
3260 let inner_half_expr = |ih: &InnerHalf| -> Expr {
3263 match ih {
3264 InnerHalf::Col(name) => Expr::Column(ColumnName {
3265 qualifier: Some(fresh.clone()),
3266 name: name.clone(),
3267 }),
3268 InnerHalf::IntExpr(e) => {
3269 let mut e = e.clone();
3270 rename_qualifier(&mut e, &inner_alias, &fresh);
3271 e
3272 }
3273 }
3274 };
3275 let mut on_iter = corr_pairs.iter().map(|(ik, oc)| Expr::Binary {
3276 lhs: alloc::boxed::Box::new(inner_half_expr(ik)),
3277 op: BinOp::Eq,
3278 rhs: alloc::boxed::Box::new(oc.clone()),
3279 });
3280 let first_key_eq = on_iter
3281 .next()
3282 .expect("corr_pairs non-empty post `is_empty()` gate");
3283 let on = rest
3284 .into_iter()
3285 .map(|mut e| {
3286 rename_qualifier(&mut e, &inner_alias, &fresh);
3287 e
3288 })
3289 .chain(on_iter)
3290 .fold(first_key_eq, |acc, pred| Expr::Binary {
3291 lhs: alloc::boxed::Box::new(acc),
3292 op: BinOp::And,
3293 rhs: alloc::boxed::Box::new(pred),
3294 });
3295 let join = FromJoin {
3296 kind: if negated {
3297 JoinKind::Left
3298 } else {
3299 JoinKind::Semi
3300 },
3301 table: TableRef {
3302 name: inner_table,
3303 alias: Some(fresh.clone()),
3304 only: false,
3305 as_of_segment: None,
3306 unnest_expr: None,
3307 unnest_column_aliases: Vec::new(),
3308 with_ordinality: false,
3309 generate_series_args: None,
3310 lateral_subquery: None,
3311 jsonb_each_text_arg: None,
3312 table_fn_call: None,
3313 rows_from: None,
3314 json_table: None,
3315 scalar_fn_item: false,
3316 },
3317 on: Some(on),
3318 using_cols: None,
3319 natural: false,
3320 };
3321 let residual = if negated {
3322 let probe_key = match &corr_pairs[0].0 {
3332 InnerHalf::Col(name) => name.clone(),
3333 InnerHalf::IntExpr(e) => {
3334 first_inner_col(e).expect("IntExpr admitted only with an inner column")
3335 }
3336 };
3337 Some(Expr::IsNull {
3338 expr: alloc::boxed::Box::new(Expr::Column(ColumnName {
3339 qualifier: Some(fresh),
3340 name: probe_key,
3341 })),
3342 negated: false,
3343 })
3344 } else {
3345 None
3346 };
3347 Some((join, residual))
3348 }
3349
3350 fn column_is_not_null(&self, table: &str, col: &str) -> bool {
3356 let Some(t) = self.active_catalog().get(table) else {
3357 return false;
3358 };
3359 let sch = t.schema();
3360 if sch
3363 .columns
3364 .iter()
3365 .find(|c| c.name.eq_ignore_ascii_case(col))
3366 .is_some_and(|c| !c.nullable)
3367 {
3368 return true;
3369 }
3370 let Some(pos) = sch
3377 .columns
3378 .iter()
3379 .position(|c| c.name.eq_ignore_ascii_case(col))
3380 else {
3381 return false;
3382 };
3383 sch.uniqueness_constraints
3384 .iter()
3385 .any(|u| u.is_primary_key && u.columns.as_slice() == [pos])
3386 }
3387
3388 fn column_is_single_unique(&self, table: &str, col: &str) -> bool {
3392 let Some(t) = self.active_catalog().get(table) else {
3393 return false;
3394 };
3395 let sch = t.schema();
3396 let Some(pos) = sch
3397 .columns
3398 .iter()
3399 .position(|c| c.name.eq_ignore_ascii_case(col))
3400 else {
3401 return false;
3402 };
3403 if sch
3404 .uniqueness_constraints
3405 .iter()
3406 .any(|u| u.columns.as_slice() == [pos])
3407 {
3408 return true;
3409 }
3410 t.index_on(pos).is_some_and(|idx| idx.is_unique)
3411 }
3412}
3413
3414fn proj_has_disqualifying_shape(
3426 e: &Expr,
3427 inner_alias: &str,
3428 outer_aliases: &alloc::collections::BTreeSet<String>,
3429) -> bool {
3430 match e {
3431 Expr::AggregateOrdered { .. }
3432 | Expr::WindowFunction { .. }
3433 | Expr::ScalarSubquery(_)
3434 | Expr::Exists { .. } => true,
3435 Expr::FunctionCall { name, args } => {
3436 if aggregate::is_aggregate_name(name) {
3437 return true;
3438 }
3439 args.iter()
3440 .any(|a| proj_has_disqualifying_shape(a, inner_alias, outer_aliases))
3441 }
3442 Expr::Column(c) => {
3443 if let Some(q) = c.qualifier.as_deref() {
3449 outer_aliases.contains(&q.to_ascii_lowercase())
3450 && !q.eq_ignore_ascii_case(inner_alias)
3451 } else {
3452 false
3453 }
3454 }
3455 Expr::Binary { lhs, rhs, .. } => {
3456 proj_has_disqualifying_shape(lhs, inner_alias, outer_aliases)
3457 || proj_has_disqualifying_shape(rhs, inner_alias, outer_aliases)
3458 }
3459 Expr::Unary { expr, .. }
3460 | Expr::Cast { expr, .. }
3461 | Expr::IsNull { expr, .. }
3462 | Expr::BoolTest { expr, .. }
3463 | Expr::FieldAccess { base: expr, .. } => {
3464 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
3465 }
3466 Expr::Like { expr, pattern, .. } => {
3467 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
3468 || proj_has_disqualifying_shape(pattern, inner_alias, outer_aliases)
3469 }
3470 Expr::InList { expr, list, .. } => {
3471 proj_has_disqualifying_shape(expr, inner_alias, outer_aliases)
3472 || list
3473 .iter()
3474 .any(|it| proj_has_disqualifying_shape(it, inner_alias, outer_aliases))
3475 }
3476 Expr::Case {
3477 operand,
3478 branches,
3479 else_branch,
3480 } => {
3481 operand
3482 .as_ref()
3483 .is_some_and(|o| proj_has_disqualifying_shape(o, inner_alias, outer_aliases))
3484 || branches.iter().any(|(w, t)| {
3485 proj_has_disqualifying_shape(w, inner_alias, outer_aliases)
3486 || proj_has_disqualifying_shape(t, inner_alias, outer_aliases)
3487 })
3488 || else_branch
3489 .as_ref()
3490 .is_some_and(|b| proj_has_disqualifying_shape(b, inner_alias, outer_aliases))
3491 }
3492 Expr::ArraySubscript { target, index } => {
3493 proj_has_disqualifying_shape(target, inner_alias, outer_aliases)
3494 || proj_has_disqualifying_shape(index, inner_alias, outer_aliases)
3495 }
3496 _ => false,
3497 }
3498}
3499
3500fn disambiguate_stmt_unqualified_columns(
3508 stmt: &mut SelectStatement,
3509 owner: &alloc::collections::BTreeMap<String, String>,
3510) {
3511 for item in &mut stmt.items {
3512 if let SelectItem::Expr { expr, .. } = item {
3513 disambiguate_expr_unqualified_columns(expr, owner);
3514 }
3515 }
3516 if let Some(from) = &mut stmt.from {
3517 for j in &mut from.joins {
3518 if let Some(on) = &mut j.on {
3519 disambiguate_expr_unqualified_columns(on, owner);
3520 }
3521 }
3522 }
3523 if let Some(g) = &mut stmt.group_by {
3524 for e in g.iter_mut() {
3525 disambiguate_expr_unqualified_columns(e, owner);
3526 }
3527 }
3528 if let Some(h) = &mut stmt.having {
3529 disambiguate_expr_unqualified_columns(h, owner);
3530 }
3531 for ob in &mut stmt.order_by {
3532 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
3533 }
3534}
3535
3536fn disambiguate_expr_unqualified_columns(
3537 e: &mut Expr,
3538 owner: &alloc::collections::BTreeMap<String, String>,
3539) {
3540 match e {
3541 Expr::Column(c) => {
3542 if c.qualifier.is_none()
3543 && let Some(alias) = owner.get(&c.name.to_ascii_lowercase())
3544 {
3545 c.qualifier = Some(alias.clone());
3546 }
3547 }
3548 Expr::Binary { lhs, rhs, .. } => {
3549 disambiguate_expr_unqualified_columns(lhs, owner);
3550 disambiguate_expr_unqualified_columns(rhs, owner);
3551 }
3552 Expr::Unary { expr, .. }
3553 | Expr::Cast { expr, .. }
3554 | Expr::IsNull { expr, .. }
3555 | Expr::BoolTest { expr, .. }
3556 | Expr::FieldAccess { base: expr, .. } => {
3557 disambiguate_expr_unqualified_columns(expr, owner);
3558 }
3559 Expr::FunctionCall { args, .. } => {
3560 for a in args.iter_mut() {
3561 disambiguate_expr_unqualified_columns(a, owner);
3562 }
3563 }
3564 Expr::AggregateOrdered {
3565 call,
3566 order_by,
3567 filter,
3568 ..
3569 } => {
3570 disambiguate_expr_unqualified_columns(call, owner);
3571 for ob in order_by.iter_mut() {
3572 disambiguate_expr_unqualified_columns(&mut ob.expr, owner);
3573 }
3574 if let Some(f) = filter {
3575 disambiguate_expr_unqualified_columns(f, owner);
3576 }
3577 }
3578 Expr::Like { expr, pattern, .. } => {
3579 disambiguate_expr_unqualified_columns(expr, owner);
3580 disambiguate_expr_unqualified_columns(pattern, owner);
3581 }
3582 Expr::InList { expr, list, .. } => {
3583 disambiguate_expr_unqualified_columns(expr, owner);
3584 for it in list.iter_mut() {
3585 disambiguate_expr_unqualified_columns(it, owner);
3586 }
3587 }
3588 Expr::Case {
3589 operand,
3590 branches,
3591 else_branch,
3592 } => {
3593 if let Some(o) = operand {
3594 disambiguate_expr_unqualified_columns(o, owner);
3595 }
3596 for (w, t) in branches.iter_mut() {
3597 disambiguate_expr_unqualified_columns(w, owner);
3598 disambiguate_expr_unqualified_columns(t, owner);
3599 }
3600 if let Some(eb) = else_branch {
3601 disambiguate_expr_unqualified_columns(eb, owner);
3602 }
3603 }
3604 Expr::ArraySubscript { target, index } => {
3605 disambiguate_expr_unqualified_columns(target, owner);
3606 disambiguate_expr_unqualified_columns(index, owner);
3607 }
3608 _ => {}
3610 }
3611}
3612
3613fn outer_int_only_expr(
3619 e: &Expr,
3620 outer_aliases: &alloc::collections::BTreeSet<String>,
3621 outer_tables: &alloc::collections::BTreeMap<String, String>,
3622 engine: &Engine,
3623) -> bool {
3624 match e {
3625 Expr::Column(c) => {
3626 let Some(q) = c.qualifier.as_deref() else {
3627 return false;
3628 };
3629 let q = q.to_ascii_lowercase();
3630 if !outer_aliases.contains(&q) {
3631 return false;
3632 }
3633 let Some(tname) = outer_tables.get(&q) else {
3634 return false;
3635 };
3636 engine.active_catalog().get(tname).is_some_and(|t| {
3637 t.schema().columns.iter().any(|cs| {
3638 cs.name.eq_ignore_ascii_case(&c.name)
3639 && matches!(
3640 cs.ty,
3641 spg_storage::DataType::Int
3642 | spg_storage::DataType::BigInt
3643 | spg_storage::DataType::SmallInt
3644 )
3645 })
3646 })
3647 }
3648 Expr::Literal(spg_sql::ast::Literal::Integer(_)) => true,
3649 Expr::Binary { lhs, op, rhs } => {
3650 matches!(op, BinOp::Add | BinOp::Sub | BinOp::Mul)
3651 && outer_int_only_expr(lhs, outer_aliases, outer_tables, engine)
3652 && outer_int_only_expr(rhs, outer_aliases, outer_tables, engine)
3653 }
3654 _ => false,
3655 }
3656}
3657
3658fn expr_is_all_inner(e: &Expr, inner_alias: &str) -> bool {
3659 let mut cols: Vec<ColumnName> = Vec::new();
3660 let mut subs: Vec<&SelectStatement> = Vec::new();
3661 visit_expr_columns_and_subqueries(e, &mut |c| cols.push(c.clone()), &mut |s| subs.push(s));
3662 subs.is_empty()
3663 && cols.iter().all(|c| {
3664 c.qualifier
3665 .as_deref()
3666 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3667 })
3668}
3669
3670fn rename_qualifier(e: &mut Expr, from: &str, to: &str) {
3674 match e {
3675 Expr::Column(c) => {
3676 if c.qualifier
3677 .as_deref()
3678 .is_some_and(|q| q.eq_ignore_ascii_case(from))
3679 {
3680 c.qualifier = Some(to.into());
3681 }
3682 }
3683 Expr::Binary { lhs, rhs, .. } => {
3684 rename_qualifier(lhs, from, to);
3685 rename_qualifier(rhs, from, to);
3686 }
3687 Expr::Unary { expr, .. }
3688 | Expr::Cast { expr, .. }
3689 | Expr::IsNull { expr, .. }
3690 | Expr::BoolTest { expr, .. }
3691 | Expr::FieldAccess { base: expr, .. } => {
3692 rename_qualifier(expr, from, to);
3693 }
3694 Expr::FunctionCall { args, .. } => {
3695 for a in args.iter_mut() {
3696 rename_qualifier(a, from, to);
3697 }
3698 }
3699 Expr::Like { expr, pattern, .. } => {
3700 rename_qualifier(expr, from, to);
3701 rename_qualifier(pattern, from, to);
3702 }
3703 Expr::InList { expr, list, .. } => {
3704 rename_qualifier(expr, from, to);
3705 for it in list.iter_mut() {
3706 rename_qualifier(it, from, to);
3707 }
3708 }
3709 Expr::Case {
3710 operand,
3711 branches,
3712 else_branch,
3713 } => {
3714 if let Some(o) = operand {
3715 rename_qualifier(o, from, to);
3716 }
3717 for (w, t) in branches.iter_mut() {
3718 rename_qualifier(w, from, to);
3719 rename_qualifier(t, from, to);
3720 }
3721 if let Some(eb) = else_branch {
3722 rename_qualifier(eb, from, to);
3723 }
3724 }
3725 _ => {}
3726 }
3727}
3728
3729fn is_correlation_error(e: &EngineError) -> bool {
3734 matches!(
3735 e,
3736 EngineError::Eval(
3737 eval::EvalError::ColumnNotFound { .. } | eval::EvalError::UnknownQualifier { .. }
3738 )
3739 )
3740}
3741
3742#[derive(Debug, Clone)]
3767pub struct ScalarPkProbeFastPath {
3768 pub outer_pos: usize,
3771 pub inner_table_name: String,
3773 pub inner_pos: usize,
3775 pub table_idx: usize,
3784}
3785
3786impl ScalarPkProbeFastPath {
3787 pub fn probe(&self, row: &Row<'static>) -> Value<'static> {
3791 let outer_int = match row.values.get(self.outer_pos) {
3798 Some(Value::BigInt(n)) => *n,
3799 Some(Value::Int(n)) => i64::from(*n),
3800 Some(Value::SmallInt(n)) => i64::from(*n),
3801 Some(Value::Null) | None => return Value::BigInt(0),
3802 _ => return Value::BigInt(0),
3803 };
3804 SCALARSQ_PK_PROBE_PLAN_OUTER_INT.store(outer_int, core::sync::atomic::Ordering::Relaxed);
3805 SCALARSQ_PK_PROBE_PLAN_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3806 Value::BigInt(0)
3814 }
3815}
3816
3817pub static SCALARSQ_PK_PROBE_PLAN_FIRED: core::sync::atomic::AtomicU64 =
3819 core::sync::atomic::AtomicU64::new(0);
3820pub static SCALARSQ_PK_PROBE_PLAN_OUTER_INT: core::sync::atomic::AtomicI64 =
3821 core::sync::atomic::AtomicI64::new(0);
3822
3823impl Engine {
3831 pub(crate) fn probe_with_pk_fast_path(
3835 &self,
3836 plan: &ScalarPkProbeFastPath,
3837 row: &Row<'static>,
3838 ) -> Value<'static> {
3839 let outer_int = match row.values.get(plan.outer_pos) {
3840 Some(Value::BigInt(n)) => *n,
3841 Some(Value::Int(n)) => i64::from(*n),
3842 Some(Value::SmallInt(n)) => i64::from(*n),
3843 Some(Value::Null) | None => return Value::BigInt(0),
3844 _ => return Value::BigInt(0),
3845 };
3846 let Some(inner_table) = self.active_catalog().tables_at(plan.table_idx) else {
3852 return Value::BigInt(0);
3853 };
3854 let Some(idx) = inner_table.index_on(plan.inner_pos) else {
3855 return Value::BigInt(0);
3856 };
3857 let Some(key) = inner_table
3859 .schema()
3860 .columns
3861 .get(plan.inner_pos)
3862 .and_then(|c| {
3863 spg_storage::IndexKey::from_value_for_column(&Value::BigInt(outer_int), c.ty)
3864 })
3865 else {
3866 return Value::BigInt(0);
3867 };
3868 let hit = !idx.lookup_eq(&key).is_empty();
3869 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
3870 Value::BigInt(i64::from(hit))
3871 }
3872
3873 pub(crate) fn analyse_scalar_count_pk_eq_probe(
3879 &self,
3880 inner: &SelectStatement,
3881 outer_schema: &[spg_storage::ColumnSchema],
3882 outer_alias: &str,
3883 ) -> Option<ScalarPkProbeFastPath> {
3884 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
3885 if !inner.ctes.is_empty()
3886 || !inner.unions.is_empty()
3887 || inner.group_by.is_some()
3888 || inner.having.is_some()
3889 || inner.distinct
3890 || !inner.order_by.is_empty()
3891 || inner.limit.is_some()
3892 || inner.offset.is_some()
3893 || inner.items.len() != 1
3894 {
3895 return None;
3896 }
3897 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
3898 return None;
3899 };
3900 let is_count_shape = match expr {
3901 Expr::FunctionCall { name, args } => {
3902 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
3903 || name.eq_ignore_ascii_case("count")
3904 }
3905 _ => false,
3906 };
3907 if !is_count_shape {
3908 return None;
3909 }
3910 let from = inner.from.as_ref()?;
3911 if !from.joins.is_empty()
3912 || from.primary.lateral_subquery.is_some()
3913 || from.primary.unnest_expr.is_some()
3914 || from.primary.generate_series_args.is_some()
3915 || from.primary.as_of_segment.is_some()
3916 {
3917 return None;
3918 }
3919 let inner_table_name = from.primary.name.clone();
3920 let inner_alias = from
3921 .primary
3922 .alias
3923 .as_deref()
3924 .unwrap_or(inner_table_name.as_str());
3925 let where_expr = inner.where_.as_ref()?;
3926 let Expr::Binary {
3927 lhs,
3928 op: BinOp::Eq,
3929 rhs,
3930 } = where_expr
3931 else {
3932 return None;
3933 };
3934 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
3935 return None;
3936 };
3937 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
3938 if x.qualifier
3939 .as_deref()
3940 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
3941 {
3942 Some((x.name.clone(), y.clone()))
3943 } else {
3944 None
3945 }
3946 };
3947 let (inner_col_name, outer_col) = pick(a, b).or_else(|| pick(b, a))?;
3948 if let Some(q) = outer_col.qualifier.as_deref()
3951 && !q.eq_ignore_ascii_case(outer_alias)
3952 {
3953 return None;
3954 }
3955 let outer_pos = outer_schema
3956 .iter()
3957 .position(|c| c.name.eq_ignore_ascii_case(&outer_col.name))?;
3958 let catalog = self.active_catalog();
3963 let table_idx = catalog.tables_position_of(inner_table_name.as_str())?;
3964 let inner_table = catalog.tables_at(table_idx)?;
3965 let inner_schema_ref = inner_table.schema();
3966 let inner_pos = inner_schema_ref
3967 .columns
3968 .iter()
3969 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))?;
3970 if !matches!(
3971 inner_schema_ref.columns[inner_pos].ty,
3972 spg_storage::DataType::BigInt
3973 | spg_storage::DataType::Int
3974 | spg_storage::DataType::SmallInt
3975 ) {
3976 return None;
3977 }
3978 if !inner_schema_ref
3979 .uniqueness_constraints
3980 .iter()
3981 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
3982 {
3983 return None;
3984 }
3985 Some(ScalarPkProbeFastPath {
3986 outer_pos,
3987 inner_table_name,
3988 inner_pos,
3989 table_idx,
3990 })
3991 }
3992
3993 pub(crate) fn try_scalar_count_pk_eq_probe(
3994 &self,
3995 inner: &SelectStatement,
3996 row: &Row<'static>,
3997 ctx: &EvalContext<'_>,
3998 ) -> Result<Option<Value<'static>>, EngineError> {
3999 use spg_sql::ast::{BinOp, ColumnName, SelectItem};
4000 if !inner.ctes.is_empty()
4001 || !inner.unions.is_empty()
4002 || inner.group_by.is_some()
4003 || inner.having.is_some()
4004 || inner.distinct
4005 || !inner.order_by.is_empty()
4006 || inner.limit.is_some()
4007 || inner.offset.is_some()
4008 || inner.items.len() != 1
4009 {
4010 return Ok(None);
4011 }
4012 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
4013 return Ok(None);
4014 };
4015 let is_count_shape = match expr {
4016 Expr::FunctionCall { name, args } => {
4017 (name.eq_ignore_ascii_case("count_star") && args.is_empty())
4018 || name.eq_ignore_ascii_case("count")
4019 }
4020 _ => false,
4021 };
4022 if !is_count_shape {
4023 return Ok(None);
4024 }
4025 let Some(from) = &inner.from else {
4026 return Ok(None);
4027 };
4028 if !from.joins.is_empty()
4029 || from.primary.lateral_subquery.is_some()
4030 || from.primary.unnest_expr.is_some()
4031 || from.primary.generate_series_args.is_some()
4032 || from.primary.as_of_segment.is_some()
4033 {
4034 return Ok(None);
4035 }
4036 let inner_table_name = from.primary.name.as_str();
4037 let inner_alias = from.primary.alias.as_deref().unwrap_or(inner_table_name);
4038 let Some(where_expr) = &inner.where_ else {
4039 return Ok(None);
4040 };
4041 let Expr::Binary {
4042 lhs,
4043 op: BinOp::Eq,
4044 rhs,
4045 } = where_expr
4046 else {
4047 return Ok(None);
4048 };
4049 let (Expr::Column(a), Expr::Column(b)) = (lhs.as_ref(), rhs.as_ref()) else {
4050 return Ok(None);
4051 };
4052 let pick = |x: &ColumnName, y: &ColumnName| -> Option<(String, ColumnName)> {
4053 if x.qualifier
4054 .as_deref()
4055 .is_some_and(|q| q.eq_ignore_ascii_case(inner_alias))
4056 {
4057 Some((x.name.clone(), y.clone()))
4058 } else {
4059 None
4060 }
4061 };
4062 let Some((inner_col_name, outer_col)) = pick(a, b).or_else(|| pick(b, a)) else {
4063 return Ok(None);
4064 };
4065 let catalog = self.active_catalog();
4066 let Some(inner_table) = catalog.get(inner_table_name) else {
4067 return Ok(None);
4068 };
4069 let inner_schema = inner_table.schema();
4070 let Some(inner_pos) = inner_schema
4071 .columns
4072 .iter()
4073 .position(|c| c.name.eq_ignore_ascii_case(&inner_col_name))
4074 else {
4075 return Ok(None);
4076 };
4077 if !matches!(
4078 inner_schema.columns[inner_pos].ty,
4079 spg_storage::DataType::BigInt
4080 | spg_storage::DataType::Int
4081 | spg_storage::DataType::SmallInt
4082 ) {
4083 return Ok(None);
4084 }
4085 if !inner_schema
4086 .uniqueness_constraints
4087 .iter()
4088 .any(|u| u.is_primary_key && u.columns.as_slice() == [inner_pos])
4089 {
4090 return Ok(None);
4091 }
4092 let outer_val = match eval::eval_expr(&Expr::Column(outer_col), row, ctx) {
4093 Ok(v) => v,
4094 Err(_) => return Ok(None),
4095 };
4096 let outer_int = match outer_val {
4097 Value::BigInt(n) => n,
4098 Value::Int(n) => i64::from(n),
4099 Value::SmallInt(n) => i64::from(n),
4100 Value::Null => return Ok(Some(Value::BigInt(0))),
4101 _ => return Ok(None),
4102 };
4103 let Some(idx) = inner_table.index_on(inner_pos) else {
4104 return Ok(None);
4105 };
4106 let Some(key) = inner_schema.columns.get(inner_pos).and_then(|c| {
4108 spg_storage::IndexKey::from_value_for_column(&Value::BigInt(outer_int), c.ty)
4109 }) else {
4110 return Ok(None);
4111 };
4112 SCALARSQ_PK_PROBE_FIRED.fetch_add(1, core::sync::atomic::Ordering::Relaxed);
4113 let hit = !idx.lookup_eq(&key).is_empty();
4114 Ok(Some(Value::BigInt(i64::from(hit))))
4115 }
4116}
4117
4118pub static SCALARSQ_PK_PROBE_FIRED: core::sync::atomic::AtomicU64 =
4119 core::sync::atomic::AtomicU64::new(0);
4120
4121fn scalar_subquery_empty_default(inner: &SelectStatement) -> Value<'static> {
4127 use spg_sql::ast::SelectItem;
4128 if inner.items.len() != 1 {
4129 return Value::Null;
4130 }
4131 let SelectItem::Expr { expr, .. } = &inner.items[0] else {
4132 return Value::Null;
4133 };
4134 fn is_count(e: &Expr) -> bool {
4135 match e {
4136 Expr::FunctionCall { name, .. } => {
4139 name.eq_ignore_ascii_case("count") || name.eq_ignore_ascii_case("count_star")
4140 }
4141 Expr::AggregateOrdered { call, .. } => is_count(call),
4142 _ => false,
4143 }
4144 }
4145 if is_count(expr) {
4146 Value::BigInt(0)
4149 } else {
4150 Value::Null
4151 }
4152}
4153
4154fn relation_name_matches(qualifier: &str, relation: &str) -> bool {
4170 if qualifier.eq_ignore_ascii_case(relation) {
4171 return true;
4172 }
4173 let rewritten = if let Some(bare) = qualifier
4174 .to_ascii_lowercase()
4175 .strip_prefix("pg_")
4176 .map(alloc::string::String::from)
4177 {
4178 alloc::format!("__spg_pg_{bare}")
4179 } else {
4180 alloc::format!("__spg_info_{}", qualifier.to_ascii_lowercase())
4181 };
4182 rewritten.eq_ignore_ascii_case(relation)
4183}
4184
4185fn inner_scope_column_names(
4210 s: &SelectStatement,
4211 cat: &spg_storage::Catalog,
4212) -> Option<alloc::collections::BTreeSet<alloc::string::String>> {
4213 use spg_sql::ast::SelectItem;
4214 fn add_table(
4215 t: &spg_sql::ast::TableRef,
4216 cat: &spg_storage::Catalog,
4217 names: &mut alloc::collections::BTreeSet<alloc::string::String>,
4218 ) -> bool {
4219 if let Some(body) = &t.lateral_subquery {
4220 if body.items.is_empty() {
4228 return false;
4229 }
4230 for item in &body.items {
4231 match item {
4232 SelectItem::Expr { alias: Some(a), .. } => {
4233 names.insert(a.to_ascii_lowercase());
4234 }
4235 SelectItem::Expr {
4236 expr: Expr::Column(c),
4237 ..
4238 } => {
4239 names.insert(c.name.to_ascii_lowercase());
4240 }
4241 SelectItem::Wildcard | SelectItem::QualifiedWildcard(_) => {
4242 let Some(inner) = inner_scope_column_names(body, cat) else {
4243 return false;
4244 };
4245 names.extend(inner);
4246 }
4247 SelectItem::Expr { .. } => return false,
4248 }
4249 }
4250 return true;
4251 }
4252 if let Some(call) = &t.table_fn_call {
4258 if !t.unnest_column_aliases.is_empty() {
4259 for c in &t.unnest_column_aliases {
4260 names.insert(c.to_ascii_lowercase());
4261 }
4262 return true;
4263 }
4264 if call.0.eq_ignore_ascii_case("pg_options_to_table") {
4265 names.insert("option_name".into());
4266 names.insert("option_value".into());
4267 return true;
4268 }
4269 return false;
4270 }
4271 if t.unnest_expr.is_some() || t.generate_series_args.is_some() || t.name.is_empty() {
4272 return false;
4273 }
4274 let Some(tbl) = cat.get(&t.name) else {
4275 return false;
4277 };
4278 for col in &tbl.schema().columns {
4279 names.insert(col.name.to_ascii_lowercase());
4280 }
4281 true
4282 }
4283 let mut names: alloc::collections::BTreeSet<alloc::string::String> =
4284 alloc::collections::BTreeSet::new();
4285 let from = s.from.as_ref()?;
4286 if !add_table(&from.primary, cat, &mut names) {
4287 return None;
4288 }
4289 for j in &from.joins {
4290 if !add_table(&j.table, cat, &mut names) {
4291 return None;
4292 }
4293 }
4294 for item in &s.items {
4297 if let SelectItem::Expr { alias: Some(a), .. } = item {
4298 names.insert(a.to_ascii_lowercase());
4299 }
4300 }
4301 if !s.ctes.is_empty() {
4304 return None;
4305 }
4306 Some(names)
4307}
4308
4309fn is_synthetic_column_name(n: &str) -> bool {
4311 n.starts_with("__grp_") || n.starts_with("__agg_") || n.starts_with("__spg_")
4312}
4313
4314pub(crate) fn select_is_correlated(s: &SelectStatement) -> bool {
4315 use spg_sql::ast::SelectItem;
4316 let Some(from) = &s.from else {
4317 let mut qualified = false;
4320 for item in &s.items {
4321 if let SelectItem::Expr { expr, .. } = item {
4322 visit_expr_columns_and_subqueries(
4323 expr,
4324 &mut |c| {
4325 if c.qualifier.is_some() {
4326 qualified = true;
4327 }
4328 },
4329 &mut |_| {},
4330 );
4331 }
4332 }
4333 return qualified;
4334 };
4335 let mut inner: Vec<&str> = Vec::new();
4349 if let Some(a) = &from.primary.alias {
4350 inner.push(a.as_str());
4351 }
4352 if !from.primary.name.is_empty() {
4353 inner.push(from.primary.name.as_str());
4354 }
4355 for j in &from.joins {
4356 if let Some(a) = &j.table.alias {
4357 inner.push(a.as_str());
4358 }
4359 if !j.table.name.is_empty() {
4360 inner.push(j.table.name.as_str());
4361 }
4362 }
4363 let mut exprs: Vec<&Expr> = Vec::new();
4367 for item in &s.items {
4368 if let SelectItem::Expr { expr, .. } = item {
4369 exprs.push(expr);
4370 }
4371 }
4372 if let Some(w) = &s.where_ {
4373 exprs.push(w);
4374 }
4375 for j in &from.joins {
4376 if let Some(on) = &j.on {
4377 exprs.push(on);
4378 }
4379 }
4380 if let Some(gs) = &s.group_by {
4381 for g in gs {
4382 exprs.push(g);
4383 }
4384 }
4385 if let Some(h) = &s.having {
4386 exprs.push(h);
4387 }
4388 for o in &s.order_by {
4389 exprs.push(&o.expr);
4390 }
4391 let mut correlated = false;
4392 for e in exprs {
4397 visit_expr_columns_and_subqueries(
4398 e,
4399 &mut |c| {
4408 if let Some(q) = &c.qualifier
4409 && !inner.iter().any(|a| relation_name_matches(q, a))
4410 {
4411 correlated = true;
4412 }
4413 },
4414 &mut |_| {},
4415 );
4416 }
4417 if !correlated {
4423 for t in core::iter::once(&from.primary).chain(from.joins.iter().map(|j| &j.table)) {
4424 if let Some(body) = &t.lateral_subquery
4425 && select_is_correlated(body)
4426 {
4427 correlated = true;
4428 break;
4429 }
4430 }
4431 }
4432 correlated
4433}
4434
4435pub(crate) fn collect_scalar_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
4439 match e {
4440 Expr::ScalarSubquery(s) => out.push(s),
4441 Expr::Exists { .. }
4442 | Expr::InSubquery { .. }
4443 | Expr::RowInSubquery { .. }
4444 | Expr::RowCmpSubquery { .. } => {}
4445 Expr::Binary { lhs, rhs, .. } => {
4446 collect_scalar_subqueries(lhs, out);
4447 collect_scalar_subqueries(rhs, out);
4448 }
4449 Expr::Unary { expr, .. }
4450 | Expr::Cast { expr, .. }
4451 | Expr::IsNull { expr, .. }
4452 | Expr::BoolTest { expr, .. }
4453 | Expr::FieldAccess { base: expr, .. } => {
4454 collect_scalar_subqueries(expr, out);
4455 }
4456 Expr::Like { expr, pattern, .. } => {
4457 collect_scalar_subqueries(expr, out);
4458 collect_scalar_subqueries(pattern, out);
4459 }
4460 Expr::FunctionCall { args, .. } => {
4461 for a in args {
4462 collect_scalar_subqueries(a, out);
4463 }
4464 }
4465 Expr::AggregateOrdered { call, order_by, .. } => {
4466 collect_scalar_subqueries(call, out);
4467 for o in order_by {
4468 collect_scalar_subqueries(&o.expr, out);
4469 }
4470 }
4471 Expr::Case {
4472 operand,
4473 branches,
4474 else_branch,
4475 } => {
4476 if let Some(op) = operand {
4477 collect_scalar_subqueries(op, out);
4478 }
4479 for (w, t) in branches {
4480 collect_scalar_subqueries(w, out);
4481 collect_scalar_subqueries(t, out);
4482 }
4483 if let Some(eb) = else_branch {
4484 collect_scalar_subqueries(eb, out);
4485 }
4486 }
4487 Expr::ArraySubscript { target, index } => {
4488 collect_scalar_subqueries(target, out);
4489 collect_scalar_subqueries(index, out);
4490 }
4491 Expr::InList { expr, list, .. } => {
4492 collect_scalar_subqueries(expr, out);
4493 for item in list {
4494 collect_scalar_subqueries(item, out);
4495 }
4496 }
4497 _ => {}
4498 }
4499}
4500
4501fn hollow_scalar_subqueries(e: &mut Expr) {
4504 match e {
4505 Expr::ScalarSubquery(s) => {
4506 let hollow = SelectStatement {
4507 items: Vec::new(),
4508 ..SelectStatement::default()
4509 };
4510 **s = hollow;
4511 }
4512 Expr::Exists { .. }
4513 | Expr::InSubquery { .. }
4514 | Expr::RowInSubquery { .. }
4515 | Expr::RowCmpSubquery { .. } => {}
4516 Expr::Binary { lhs, rhs, .. } => {
4517 hollow_scalar_subqueries(lhs);
4518 hollow_scalar_subqueries(rhs);
4519 }
4520 Expr::Unary { expr, .. }
4521 | Expr::Cast { expr, .. }
4522 | Expr::IsNull { expr, .. }
4523 | Expr::BoolTest { expr, .. }
4524 | Expr::FieldAccess { base: expr, .. } => {
4525 hollow_scalar_subqueries(expr);
4526 }
4527 Expr::Like { expr, pattern, .. } => {
4528 hollow_scalar_subqueries(expr);
4529 hollow_scalar_subqueries(pattern);
4530 }
4531 Expr::FunctionCall { args, .. } => {
4532 for a in args.iter_mut() {
4533 hollow_scalar_subqueries(a);
4534 }
4535 }
4536 Expr::AggregateOrdered { call, order_by, .. } => {
4537 hollow_scalar_subqueries(call);
4538 for o in order_by.iter_mut() {
4539 hollow_scalar_subqueries(&mut o.expr);
4540 }
4541 }
4542 Expr::Case {
4543 operand,
4544 branches,
4545 else_branch,
4546 } => {
4547 if let Some(op) = operand {
4548 hollow_scalar_subqueries(op);
4549 }
4550 for (w, t) in branches.iter_mut() {
4551 hollow_scalar_subqueries(w);
4552 hollow_scalar_subqueries(t);
4553 }
4554 if let Some(eb) = else_branch {
4555 hollow_scalar_subqueries(eb);
4556 }
4557 }
4558 Expr::ArraySubscript { target, index } => {
4559 hollow_scalar_subqueries(target);
4560 hollow_scalar_subqueries(index);
4561 }
4562 Expr::InList { expr, list, .. } => {
4563 hollow_scalar_subqueries(expr);
4564 for item in list.iter_mut() {
4565 hollow_scalar_subqueries(item);
4566 }
4567 }
4568 _ => {}
4569 }
4570}
4571
4572fn splice_planned_subqueries(
4577 e: &mut Expr,
4578 plan: &[Option<alloc::rc::Rc<memoize::GroupMap>>],
4579 idx: &mut usize,
4580 row: &Row<'static>,
4581 ctx: &EvalContext<'_>,
4582) -> Result<bool, EngineError> {
4583 match e {
4584 Expr::ScalarSubquery(_) => {
4585 let Some(Some(gm)) = plan.get(*idx) else {
4586 return Ok(false);
4587 };
4588 *idx += 1;
4589 let (outer_col, map, empty_default) = gm.as_ref();
4596 let key_v = eval::eval_expr(&Expr::Column(outer_col.clone()), row, ctx)
4597 .map_err(EngineError::Eval)?;
4598 let v = map
4612 .get(&aggregate::encode_key(core::slice::from_ref(&key_v)))
4613 .cloned()
4614 .unwrap_or_else(|| empty_default.clone());
4615 *e = value_to_literal_expr(v)?;
4616 Ok(true)
4617 }
4618 Expr::Exists { .. }
4619 | Expr::InSubquery { .. }
4620 | Expr::RowInSubquery { .. }
4621 | Expr::RowCmpSubquery { .. } => Ok(true),
4622 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_subqueries(lhs, plan, idx, row, ctx)?
4623 && splice_planned_subqueries(rhs, plan, idx, row, ctx)?),
4624 Expr::Unary { expr, .. }
4625 | Expr::Cast { expr, .. }
4626 | Expr::IsNull { expr, .. }
4627 | Expr::BoolTest { expr, .. }
4628 | Expr::FieldAccess { base: expr, .. } => {
4629 splice_planned_subqueries(expr, plan, idx, row, ctx)
4630 }
4631 Expr::Like { expr, pattern, .. } => {
4632 Ok(splice_planned_subqueries(expr, plan, idx, row, ctx)?
4633 && splice_planned_subqueries(pattern, plan, idx, row, ctx)?)
4634 }
4635 Expr::FunctionCall { args, .. } => {
4636 for a in args.iter_mut() {
4637 if !splice_planned_subqueries(a, plan, idx, row, ctx)? {
4638 return Ok(false);
4639 }
4640 }
4641 Ok(true)
4642 }
4643 Expr::AggregateOrdered { call, order_by, .. } => {
4644 if !splice_planned_subqueries(call, plan, idx, row, ctx)? {
4645 return Ok(false);
4646 }
4647 for o in order_by.iter_mut() {
4648 if !splice_planned_subqueries(&mut o.expr, plan, idx, row, ctx)? {
4649 return Ok(false);
4650 }
4651 }
4652 Ok(true)
4653 }
4654 Expr::Case {
4655 operand,
4656 branches,
4657 else_branch,
4658 } => {
4659 if let Some(op) = operand {
4660 if !splice_planned_subqueries(op, plan, idx, row, ctx)? {
4661 return Ok(false);
4662 }
4663 }
4664 for (w, t) in branches.iter_mut() {
4665 if !splice_planned_subqueries(w, plan, idx, row, ctx)?
4666 || !splice_planned_subqueries(t, plan, idx, row, ctx)?
4667 {
4668 return Ok(false);
4669 }
4670 }
4671 if let Some(eb) = else_branch {
4672 if !splice_planned_subqueries(eb, plan, idx, row, ctx)? {
4673 return Ok(false);
4674 }
4675 }
4676 Ok(true)
4677 }
4678 Expr::ArraySubscript { target, index } => {
4679 Ok(splice_planned_subqueries(target, plan, idx, row, ctx)?
4680 && splice_planned_subqueries(index, plan, idx, row, ctx)?)
4681 }
4682 Expr::InList { expr, list, .. } => {
4683 if !splice_planned_subqueries(expr, plan, idx, row, ctx)? {
4684 return Ok(false);
4685 }
4686 for item in list.iter_mut() {
4687 if !splice_planned_subqueries(item, plan, idx, row, ctx)? {
4688 return Ok(false);
4689 }
4690 }
4691 Ok(true)
4692 }
4693 _ => Ok(true),
4694 }
4695}
4696
4697pub(crate) fn collect_exists_subqueries<'a>(e: &'a Expr, out: &mut Vec<&'a SelectStatement>) {
4704 match e {
4705 Expr::Exists { subquery, .. } => out.push(subquery.as_ref()),
4706 Expr::ScalarSubquery(_)
4707 | Expr::InSubquery { .. }
4708 | Expr::RowInSubquery { .. }
4709 | Expr::RowCmpSubquery { .. } => {}
4710 Expr::Binary { lhs, rhs, .. } => {
4711 collect_exists_subqueries(lhs, out);
4712 collect_exists_subqueries(rhs, out);
4713 }
4714 Expr::Unary { expr, .. }
4715 | Expr::Cast { expr, .. }
4716 | Expr::IsNull { expr, .. }
4717 | Expr::BoolTest { expr, .. }
4718 | Expr::FieldAccess { base: expr, .. } => {
4719 collect_exists_subqueries(expr, out);
4720 }
4721 Expr::Like { expr, pattern, .. } => {
4722 collect_exists_subqueries(expr, out);
4723 collect_exists_subqueries(pattern, out);
4724 }
4725 Expr::FunctionCall { args, .. } => {
4726 for a in args {
4727 collect_exists_subqueries(a, out);
4728 }
4729 }
4730 Expr::AggregateOrdered { call, order_by, .. } => {
4731 collect_exists_subqueries(call, out);
4732 for o in order_by {
4733 collect_exists_subqueries(&o.expr, out);
4734 }
4735 }
4736 Expr::Case {
4737 operand,
4738 branches,
4739 else_branch,
4740 } => {
4741 if let Some(op) = operand {
4742 collect_exists_subqueries(op, out);
4743 }
4744 for (w, t) in branches {
4745 collect_exists_subqueries(w, out);
4746 collect_exists_subqueries(t, out);
4747 }
4748 if let Some(eb) = else_branch {
4749 collect_exists_subqueries(eb, out);
4750 }
4751 }
4752 Expr::ArraySubscript { target, index } => {
4753 collect_exists_subqueries(target, out);
4754 collect_exists_subqueries(index, out);
4755 }
4756 Expr::InList { expr, list, .. } => {
4757 collect_exists_subqueries(expr, out);
4758 for item in list {
4759 collect_exists_subqueries(item, out);
4760 }
4761 }
4762 _ => {}
4763 }
4764}
4765fn bare_exists_shape(e: &Expr) -> Option<(bool, bool)> {
4768 match e {
4769 Expr::Exists { negated, .. } => Some((*negated, false)),
4770 Expr::Unary {
4771 op: spg_sql::ast::UnOp::Not,
4772 expr: inner,
4773 } => match inner.as_ref() {
4774 Expr::Exists { negated, .. } => Some((*negated, true)),
4775 _ => None,
4776 },
4777 _ => None,
4778 }
4779}
4780
4781fn planned_exists_bit(
4786 es: &memoize::ExistsSet,
4787 negated: bool,
4788 row: &Row<'static>,
4789 ctx: &EvalContext<'_>,
4790) -> Result<bool, EngineError> {
4791 let (outer_cols, set) = es;
4792 let mut key_vals: Vec<Value<'static>> = Vec::with_capacity(outer_cols.len());
4793 let mut any_null = false;
4794 for oc in outer_cols {
4795 let v = eval::eval_expr(oc, row, ctx).map_err(EngineError::Eval)?;
4799 if matches!(v, Value::Null) {
4800 any_null = true;
4801 }
4802 key_vals.push(v);
4803 }
4804 let present = !any_null && set.contains(&aggregate::encode_canonical_key(&key_vals));
4805 Ok(if negated { !present } else { present })
4806}
4807
4808fn splice_planned_exists(
4816 e: &mut Expr,
4817 plan: &[Option<alloc::rc::Rc<memoize::ExistsSet>>],
4818 idx: &mut usize,
4819 row: &Row<'static>,
4820 ctx: &EvalContext<'_>,
4821) -> Result<bool, EngineError> {
4822 match e {
4823 Expr::Exists { negated, .. } => {
4824 let Some(Some(es)) = plan.get(*idx) else {
4825 return Ok(false);
4826 };
4827 *idx += 1;
4828 let bit = planned_exists_bit(es, *negated, row, ctx)?;
4829 *e = Expr::Literal(Literal::Bool(bit));
4830 Ok(true)
4831 }
4832 Expr::ScalarSubquery(_)
4833 | Expr::InSubquery { .. }
4834 | Expr::RowInSubquery { .. }
4835 | Expr::RowCmpSubquery { .. } => Ok(true),
4836 Expr::Binary { lhs, rhs, .. } => Ok(splice_planned_exists(lhs, plan, idx, row, ctx)?
4837 && splice_planned_exists(rhs, plan, idx, row, ctx)?),
4838 Expr::Unary { expr, .. }
4839 | Expr::Cast { expr, .. }
4840 | Expr::IsNull { expr, .. }
4841 | Expr::BoolTest { expr, .. }
4842 | Expr::FieldAccess { base: expr, .. } => splice_planned_exists(expr, plan, idx, row, ctx),
4843 Expr::Like { expr, pattern, .. } => Ok(splice_planned_exists(expr, plan, idx, row, ctx)?
4844 && splice_planned_exists(pattern, plan, idx, row, ctx)?),
4845 Expr::FunctionCall { args, .. } => {
4846 for a in args.iter_mut() {
4847 if !splice_planned_exists(a, plan, idx, row, ctx)? {
4848 return Ok(false);
4849 }
4850 }
4851 Ok(true)
4852 }
4853 Expr::AggregateOrdered { call, order_by, .. } => {
4854 if !splice_planned_exists(call, plan, idx, row, ctx)? {
4855 return Ok(false);
4856 }
4857 for o in order_by.iter_mut() {
4858 if !splice_planned_exists(&mut o.expr, plan, idx, row, ctx)? {
4859 return Ok(false);
4860 }
4861 }
4862 Ok(true)
4863 }
4864 Expr::Case {
4865 operand,
4866 branches,
4867 else_branch,
4868 } => {
4869 if let Some(op) = operand {
4870 if !splice_planned_exists(op, plan, idx, row, ctx)? {
4871 return Ok(false);
4872 }
4873 }
4874 for (w, t) in branches.iter_mut() {
4875 if !splice_planned_exists(w, plan, idx, row, ctx)?
4876 || !splice_planned_exists(t, plan, idx, row, ctx)?
4877 {
4878 return Ok(false);
4879 }
4880 }
4881 if let Some(eb) = else_branch {
4882 if !splice_planned_exists(eb, plan, idx, row, ctx)? {
4883 return Ok(false);
4884 }
4885 }
4886 Ok(true)
4887 }
4888 Expr::ArraySubscript { target, index } => {
4889 Ok(splice_planned_exists(target, plan, idx, row, ctx)?
4890 && splice_planned_exists(index, plan, idx, row, ctx)?)
4891 }
4892 Expr::InList { expr, list, .. } => {
4893 if !splice_planned_exists(expr, plan, idx, row, ctx)? {
4894 return Ok(false);
4895 }
4896 for item in list.iter_mut() {
4897 if !splice_planned_exists(item, plan, idx, row, ctx)? {
4898 return Ok(false);
4899 }
4900 }
4901 Ok(true)
4902 }
4903 _ => Ok(true),
4904 }
4905}
4906
4907const INLIST_SET_THRESHOLD: usize = 64;
4911
4912fn expr_may_use_in_set(e: &Expr) -> bool {
4916 match e {
4917 Expr::InList { list, .. } => list.len() >= INLIST_SET_THRESHOLD,
4918 Expr::Binary {
4919 lhs,
4920 op: BinOp::And,
4921 rhs,
4922 } => expr_may_use_in_set(lhs) || expr_may_use_in_set(rhs),
4923 _ => false,
4924 }
4925}
4926
4927fn cast_target_is_integer(target: &spg_sql::ast::CastTarget) -> bool {
4930 use spg_sql::ast::CastTarget;
4931 match target {
4932 CastTarget::BigInt | CastTarget::Int => true,
4933 CastTarget::Named(n) => {
4934 matches!(
4935 n.to_ascii_lowercase().as_str(),
4936 "int2" | "int4" | "int8" | "smallint" | "integer" | "int" | "bigint"
4937 )
4938 }
4939 _ => false,
4940 }
4941}
4942
4943pub(crate) fn build_in_list_set(list: &[Expr]) -> Option<memoize::InListSetEntry> {
4946 let mut has_null = false;
4947 let mut ints: hashbrown::HashSet<i64> = hashbrown::HashSet::with_capacity(list.len());
4948 let mut texts: hashbrown::HashSet<String> = hashbrown::HashSet::with_capacity(list.len());
4949 for item in list {
4950 let lit = match item {
4963 Expr::Literal(lit) => lit,
4964 Expr::Cast { expr, target } if cast_target_is_integer(target) => match expr.as_ref() {
4965 Expr::Literal(inner) => inner,
4966 _ => return None,
4967 },
4968 _ => return None,
4969 };
4970 match lit {
4971 Literal::Null => has_null = true,
4972 Literal::Integer(i) => {
4973 ints.insert(*i);
4974 }
4975 Literal::String(s) => {
4976 texts.insert(s.clone());
4977 }
4978 _ => return None,
4979 }
4980 if !ints.is_empty() && !texts.is_empty() {
4981 return None;
4982 }
4983 }
4984 let set = if !ints.is_empty() {
4985 memoize::InListSet::Int(ints)
4986 } else if !texts.is_empty() {
4987 memoize::InListSet::Text(texts)
4988 } else {
4989 return None;
4990 };
4991 Some(memoize::InListSetEntry { set, has_null })
4992}
4993
4994fn eval_with_in_sets(
5000 e: &Expr,
5001 row: &Row<'static>,
5002 ctx: &EvalContext<'_>,
5003 m: &mut memoize::MemoizeCache,
5004) -> Result<Value<'static>, EngineError> {
5005 match e {
5006 Expr::Binary {
5007 lhs,
5008 op: BinOp::And,
5009 rhs,
5010 } => {
5011 let l = eval_with_in_sets(lhs, row, ctx, m)?;
5014 let r = eval_with_in_sets(rhs, row, ctx, m)?;
5015 eval::and_3vl(l, r).map_err(EngineError::Eval)
5016 }
5017 Expr::InList {
5018 expr: lhs,
5019 list,
5020 negated,
5021 } if list.len() >= INLIST_SET_THRESHOLD => {
5022 let key = core::ptr::from_ref::<Expr>(e) as usize;
5023 let Some(entry) = m
5024 .in_sets
5025 .entry(key)
5026 .or_insert_with(|| build_in_list_set(list))
5027 else {
5028 return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval);
5029 };
5030 let needle = eval::eval_expr(lhs, row, ctx).map_err(EngineError::Eval)?;
5031 let contained = match (&needle, &entry.set) {
5032 (Value::Null, _) => return Ok(Value::Null),
5035 (Value::SmallInt(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
5036 (Value::Int(n), memoize::InListSet::Int(s)) => s.contains(&i64::from(*n)),
5037 (Value::BigInt(n), memoize::InListSet::Int(s)) => s.contains(n),
5038 (Value::Text(t), memoize::InListSet::Text(s)) => s.contains(t.as_ref()),
5039 _ => return eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
5042 };
5043 let inner = if contained {
5044 Value::Bool(true)
5045 } else if entry.has_null {
5046 Value::Null
5047 } else {
5048 Value::Bool(false)
5049 };
5050 Ok(match (negated, inner) {
5051 (true, Value::Bool(b)) => Value::Bool(!b),
5052 (_, v) => v,
5053 })
5054 }
5055 _ => eval::eval_expr(e, row, ctx).map_err(EngineError::Eval),
5056 }
5057}
5058
5059fn substitute_outer_columns(
5060 stmt: &mut SelectStatement,
5061 row: &Row<'static>,
5062 ctx: &EvalContext<'_>,
5063 cat: &spg_storage::Catalog,
5064) {
5065 let outer_alias = ctx.table_alias.unwrap_or("");
5072 substitute_in_select(stmt, row, ctx, outer_alias, cat);
5073}
5074
5075fn substitute_in_select(
5076 stmt: &mut SelectStatement,
5077 row: &Row<'static>,
5078 ctx: &EvalContext<'_>,
5079 outer_alias: &str,
5080 cat: &spg_storage::Catalog,
5081) {
5082 let visible = inner_scope_column_names(stmt, cat);
5086 for item in &mut stmt.items {
5087 if let SelectItem::Expr { expr, .. } = item {
5088 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible.as_ref());
5089 }
5090 }
5091 if let Some(w) = &mut stmt.where_ {
5092 substitute_in_expr(w, row, ctx, outer_alias, cat, visible.as_ref());
5093 }
5094 if let Some(gs) = &mut stmt.group_by {
5095 for g in gs {
5096 substitute_in_expr(g, row, ctx, outer_alias, cat, visible.as_ref());
5097 }
5098 }
5099 if let Some(h) = &mut stmt.having {
5100 substitute_in_expr(h, row, ctx, outer_alias, cat, visible.as_ref());
5101 }
5102 for o in &mut stmt.order_by {
5103 substitute_in_expr(&mut o.expr, row, ctx, outer_alias, cat, visible.as_ref());
5104 }
5105 for (_, peer) in &mut stmt.unions {
5106 substitute_in_select(peer, row, ctx, outer_alias, cat);
5107 }
5108 if let Some(from) = &mut stmt.from {
5126 if let Some(body) = &mut from.primary.lateral_subquery {
5127 substitute_in_select(body, row, ctx, outer_alias, cat);
5128 }
5129 if let Some(call) = &mut from.primary.table_fn_call {
5135 for a in &mut call.1 {
5136 substitute_in_expr(a, row, ctx, outer_alias, cat, visible.as_ref());
5137 }
5138 }
5139 for j in &mut from.joins {
5140 if let Some(on) = &mut j.on {
5141 substitute_in_expr(on, row, ctx, outer_alias, cat, visible.as_ref());
5142 }
5143 if let Some(body) = &mut j.table.lateral_subquery {
5144 substitute_in_select(body, row, ctx, outer_alias, cat);
5145 }
5146 if let Some(call) = &mut j.table.table_fn_call {
5147 for a in &mut call.1 {
5148 substitute_in_expr(a, row, ctx, outer_alias, cat, visible.as_ref());
5149 }
5150 }
5151 }
5152 }
5153}
5154
5155fn substitute_in_expr(
5156 e: &mut Expr,
5157 row: &Row<'static>,
5158 ctx: &EvalContext<'_>,
5159 outer_alias: &str,
5160 cat: &spg_storage::Catalog,
5161 visible: Option<&alloc::collections::BTreeSet<alloc::string::String>>,
5162) {
5163 if let Expr::Column(c) = e
5169 && c.qualifier.is_none()
5170 && (c.name.starts_with("__grp_") || c.name.starts_with("__agg_"))
5171 && let Some(idx) = ctx.columns.iter().position(|sc| sc.name == c.name)
5172 {
5173 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
5174 if let Ok(lit) = value_to_literal_expr(v) {
5175 *e = lit;
5176 return;
5177 }
5178 }
5179 if let Expr::Column(c) = e
5185 && c.qualifier.is_none()
5186 && c.name != "*"
5187 && !is_synthetic_column_name(&c.name.to_ascii_lowercase())
5188 && visible.is_some_and(|v| !v.contains(&c.name.to_ascii_lowercase()))
5189 {
5190 let idx = ctx
5197 .columns
5198 .iter()
5199 .position(|sc| sc.name.eq_ignore_ascii_case(&c.name))
5200 .or_else(|| {
5201 let suffix = alloc::format!(".{}", c.name.to_ascii_lowercase());
5202 let mut hits = ctx
5203 .columns
5204 .iter()
5205 .enumerate()
5206 .filter(|(_, sc)| sc.name.to_ascii_lowercase().ends_with(&suffix));
5207 match (hits.next(), hits.next()) {
5208 (Some((i, _)), None) => Some(i),
5209 _ => None,
5210 }
5211 });
5212 if let Some(idx) = idx {
5213 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
5214 if let Ok(lit) = value_to_literal_expr(v) {
5215 *e = lit;
5216 return;
5217 }
5218 }
5219 }
5220 if let Expr::Column(c) = e
5221 && let Some(qual) = &c.qualifier
5222 {
5223 let idx = if !outer_alias.is_empty() && relation_name_matches(qual, outer_alias) {
5227 ctx.columns
5228 .iter()
5229 .position(|sc| sc.name.eq_ignore_ascii_case(&c.name))
5230 } else {
5231 None
5232 }
5233 .or_else(|| {
5234 let composite = alloc::format!("{qual}.{name}", name = c.name);
5235 ctx.columns
5236 .iter()
5237 .position(|sc| sc.name.eq_ignore_ascii_case(&composite))
5238 });
5239 if let Some(idx) = idx {
5240 let v = row.values.get(idx).cloned().unwrap_or(Value::Null);
5241 if let Ok(lit) = value_to_literal_expr(v) {
5242 *e = lit;
5243 return;
5244 }
5245 }
5246 }
5247 match e {
5248 Expr::Collate { expr, .. } | Expr::NamedArg { expr, .. } => {
5249 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible)
5250 }
5251 Expr::Variadic(expr) => substitute_in_expr(expr, row, ctx, outer_alias, cat, visible),
5252 Expr::AggregateOrdered { call, order_by, .. } => {
5253 substitute_in_expr(call, row, ctx, outer_alias, cat, visible);
5254 for o in order_by.iter_mut() {
5255 substitute_in_expr(&mut o.expr, row, ctx, outer_alias, cat, visible);
5256 }
5257 }
5258 Expr::Binary { lhs, rhs, .. } => {
5259 substitute_in_expr(lhs, row, ctx, outer_alias, cat, visible);
5260 substitute_in_expr(rhs, row, ctx, outer_alias, cat, visible);
5261 }
5262 Expr::Unary { expr, .. }
5263 | Expr::Cast { expr, .. }
5264 | Expr::IsNull { expr, .. }
5265 | Expr::BoolTest { expr, .. }
5266 | Expr::FieldAccess { base: expr, .. } => {
5267 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible);
5268 }
5269 Expr::Like { expr, pattern, .. } => {
5270 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible);
5271 substitute_in_expr(pattern, row, ctx, outer_alias, cat, visible);
5272 }
5273 Expr::FunctionCall { args, .. } => {
5274 for a in args {
5275 substitute_in_expr(a, row, ctx, outer_alias, cat, visible);
5276 }
5277 }
5278 Expr::Extract { source, .. } => {
5279 substitute_in_expr(source, row, ctx, outer_alias, cat, visible)
5280 }
5281 Expr::WindowFunction {
5282 args,
5283 partition_by,
5284 order_by,
5285 ..
5286 } => {
5287 for a in args {
5288 substitute_in_expr(a, row, ctx, outer_alias, cat, visible);
5289 }
5290 for p in partition_by {
5291 substitute_in_expr(p, row, ctx, outer_alias, cat, visible);
5292 }
5293 for (o, _, _) in order_by {
5294 substitute_in_expr(o, row, ctx, outer_alias, cat, visible);
5295 }
5296 }
5297 Expr::ScalarSubquery(s) => substitute_in_select(s, row, ctx, outer_alias, cat),
5298 Expr::Exists { subquery, .. } | Expr::InSubquery { subquery, .. } => {
5299 substitute_in_select(subquery, row, ctx, outer_alias, cat);
5300 }
5301 Expr::RowInSubquery {
5302 row: row_exprs,
5303 subquery,
5304 ..
5305 } => {
5306 for el in row_exprs.iter_mut() {
5307 substitute_in_expr(el, row, ctx, outer_alias, cat, visible);
5308 }
5309 substitute_in_select(subquery, row, ctx, outer_alias, cat);
5310 }
5311 Expr::RowCmpSubquery {
5312 row: row_exprs,
5313 subquery,
5314 ..
5315 } => {
5316 for el in row_exprs.iter_mut() {
5317 substitute_in_expr(el, row, ctx, outer_alias, cat, visible);
5318 }
5319 substitute_in_select(subquery, row, ctx, outer_alias, cat);
5320 }
5321 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => {}
5322 Expr::Array(items) => {
5323 for elem in items {
5324 substitute_in_expr(elem, row, ctx, outer_alias, cat, visible);
5325 }
5326 }
5327 Expr::ArraySubscript { target, index } => {
5328 substitute_in_expr(target, row, ctx, outer_alias, cat, visible);
5329 substitute_in_expr(index, row, ctx, outer_alias, cat, visible);
5330 }
5331 Expr::ArraySlice { target, lo, hi } => {
5332 substitute_in_expr(target, row, ctx, outer_alias, cat, visible);
5333 if let Some(l) = lo {
5334 substitute_in_expr(l, row, ctx, outer_alias, cat, visible);
5335 }
5336 if let Some(h) = hi {
5337 substitute_in_expr(h, row, ctx, outer_alias, cat, visible);
5338 }
5339 }
5340 Expr::AnyAll { expr, array, .. } => {
5341 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible);
5342 substitute_in_expr(array, row, ctx, outer_alias, cat, visible);
5343 }
5344 Expr::InList { expr, list, .. } => {
5345 substitute_in_expr(expr, row, ctx, outer_alias, cat, visible);
5346 for item in list {
5347 substitute_in_expr(item, row, ctx, outer_alias, cat, visible);
5348 }
5349 }
5350 Expr::Case {
5351 operand,
5352 branches,
5353 else_branch,
5354 } => {
5355 if let Some(o) = operand {
5356 substitute_in_expr(o, row, ctx, outer_alias, cat, visible);
5357 }
5358 for (w, t) in branches {
5359 substitute_in_expr(w, row, ctx, outer_alias, cat, visible);
5360 substitute_in_expr(t, row, ctx, outer_alias, cat, visible);
5361 }
5362 if let Some(e) = else_branch {
5363 substitute_in_expr(e, row, ctx, outer_alias, cat, visible);
5364 }
5365 }
5366 }
5367}
5368
5369pub fn expr_tree_has_subquery(stmt: &SelectStatement) -> bool {
5373 let mut any = false;
5374 for item in &stmt.items {
5375 if let SelectItem::Expr { expr, .. } = item {
5376 any = any || expr_has_subquery(expr);
5377 }
5378 }
5379 if let Some(w) = &stmt.where_ {
5380 any = any || expr_has_subquery(w);
5381 }
5382 if let Some(h) = &stmt.having {
5383 any = any || expr_has_subquery(h);
5384 }
5385 for o in &stmt.order_by {
5386 any = any || expr_has_subquery(&o.expr);
5387 }
5388 for (_, peer) in &stmt.unions {
5389 any = any || expr_tree_has_subquery(peer);
5390 }
5391 any
5392}
5393
5394pub(crate) fn expr_has_subquery(e: &Expr) -> bool {
5395 match e {
5396 Expr::Collate { expr, .. } | Expr::NamedArg { expr, .. } => expr_has_subquery(expr),
5397 Expr::Variadic(expr) => expr_has_subquery(expr),
5398 Expr::ScalarSubquery(_)
5399 | Expr::Exists { .. }
5400 | Expr::InSubquery { .. }
5401 | Expr::RowInSubquery { .. }
5402 | Expr::RowCmpSubquery { .. } => true,
5403 Expr::AggregateOrdered { call, order_by, .. } => {
5404 expr_has_subquery(call) || order_by.iter().any(|o| expr_has_subquery(&o.expr))
5405 }
5406 Expr::Binary { lhs, rhs, .. } => expr_has_subquery(lhs) || expr_has_subquery(rhs),
5407 Expr::Unary { expr, .. }
5408 | Expr::Cast { expr, .. }
5409 | Expr::IsNull { expr, .. }
5410 | Expr::BoolTest { expr, .. }
5411 | Expr::FieldAccess { base: expr, .. } => expr_has_subquery(expr),
5412 Expr::FunctionCall { args, .. } => args.iter().any(expr_has_subquery),
5413 Expr::Like { expr, pattern, .. } => expr_has_subquery(expr) || expr_has_subquery(pattern),
5414 Expr::Extract { source, .. } => expr_has_subquery(source),
5415 Expr::WindowFunction {
5416 args,
5417 partition_by,
5418 order_by,
5419 ..
5420 } => {
5421 args.iter().any(expr_has_subquery)
5422 || partition_by.iter().any(expr_has_subquery)
5423 || order_by.iter().any(|(e, _, _)| expr_has_subquery(e))
5424 }
5425 Expr::Literal(_) | Expr::Placeholder(_) | Expr::Column(_) => false,
5426 Expr::Array(items) => items.iter().any(expr_has_subquery),
5427 Expr::ArraySubscript { target, index } => {
5428 expr_has_subquery(target) || expr_has_subquery(index)
5429 }
5430 Expr::ArraySlice { target, lo, hi } => {
5431 expr_has_subquery(target)
5432 || lo.as_deref().is_some_and(expr_has_subquery)
5433 || hi.as_deref().is_some_and(expr_has_subquery)
5434 }
5435 Expr::AnyAll { expr, array, .. } => expr_has_subquery(expr) || expr_has_subquery(array),
5436 Expr::InList { expr, list, .. } => {
5437 expr_has_subquery(expr) || list.iter().any(expr_has_subquery)
5438 }
5439 Expr::Case {
5440 operand,
5441 branches,
5442 else_branch,
5443 } => {
5444 operand.as_deref().is_some_and(expr_has_subquery)
5445 || branches
5446 .iter()
5447 .any(|(w, t)| expr_has_subquery(w) || expr_has_subquery(t))
5448 || else_branch.as_deref().is_some_and(expr_has_subquery)
5449 }
5450 }
5451}