pub struct SubqueryExecutor<'a, H: SubqueryHost + ?Sized> { /* private fields */ }Expand description
Single owner for subquery rewriting, correlation analysis, cache use and semi/anti-join execution.
Implementations§
Source§impl<'host, H: SubqueryHost + ?Sized> SubqueryExecutor<'host, H>
impl<'host, H: SubqueryHost + ?Sized> SubqueryExecutor<'host, H>
Sourcepub fn try_extract_semi_join_info(
exists: &ExistsExpression,
is_negated: bool,
outer_tables: &[String],
) -> Option<SemiJoinInfo>
pub fn try_extract_semi_join_info( exists: &ExistsExpression, is_negated: bool, outer_tables: &[String], ) -> Option<SemiJoinInfo>
Try to extract semi-join information from a correlated EXISTS subquery.
For semi-join optimization, we need:
- A simple table source (no joins in subquery)
- A WHERE clause with
inner.col = outer.colequality - Optional additional non-correlated predicates
Returns None if the subquery cannot be optimized as a semi-join.
Source§impl<'host, H: SubqueryHost + ?Sized> SubqueryExecutor<'host, H>
impl<'host, H: SubqueryHost + ?Sized> SubqueryExecutor<'host, H>
Sourcepub fn should_use_index_nested_loop_for_anti_join(
&self,
_info: &SemiJoinInfo,
outer_limit: Option<i64>,
) -> bool
pub fn should_use_index_nested_loop_for_anti_join( &self, _info: &SemiJoinInfo, outer_limit: Option<i64>, ) -> bool
Check if index-nested-loop should be preferred over anti-join for NOT EXISTS.
For NOT EXISTS, anti-join using HashJoinOperator is almost always more efficient than both index-nested-loop and InHashSet because:
- HashJoinOperator does bulk hash table build/probe (cache-efficient)
- No per-row expression evaluation overhead
- Even with LIMIT, the bulk operation is faster than per-row checking
The only case where we might prefer index-nested-loop is for VERY small LIMIT (e.g., LIMIT 10) with a highly selective index, but benchmarks show hash join is still faster in most cases.
Sourcepub fn execute_semi_join_optimization(
&self,
info: &SemiJoinInfo,
ctx: &ExecutionContext,
) -> Result<CompactArc<ValueSet>>
pub fn execute_semi_join_optimization( &self, info: &SemiJoinInfo, ctx: &ExecutionContext, ) -> Result<CompactArc<ValueSet>>
Execute the semi-join optimization for an EXISTS subquery.
Instead of executing the subquery for each outer row, we:
- Execute the inner query once with non-correlated predicates
- Collect all distinct values of the inner correlation column
- Return an FxHashSet for fast O(1) lookups
Results are cached to avoid re-execution for the same query within a single top-level query execution.
Sourcepub fn execute_anti_join(
&self,
info: &SemiJoinInfo,
outer_rows: CompactArc<Vec<Row>>,
outer_columns: &[String],
_ctx: &ExecutionContext,
) -> Result<RowVec>
pub fn execute_anti_join( &self, info: &SemiJoinInfo, outer_rows: CompactArc<Vec<Row>>, outer_columns: &[String], _ctx: &ExecutionContext, ) -> Result<RowVec>
Execute NOT EXISTS as a true anti-join using HashJoinOperator.
This is more efficient than the InHashSet approach because:
- HashJoinOperator builds hash table once and probes in bulk
- No per-row expression evaluation overhead
- Better cache efficiency due to batch processing
- Direct table access without going through full query pipeline
§Arguments
info- SemiJoinInfo extracted from the NOT EXISTS subqueryouter_rows- Pre-materialized outer table rowsouter_columns- Column names for outer table_ctx- Execution context (not used but kept for API consistency)
§Returns
Rows from outer table that have NO match in inner table (anti-join result)
Sourcepub fn try_extract_not_exists_info(
expr: &Expression,
outer_tables: &[String],
) -> Option<SemiJoinInfo>
pub fn try_extract_not_exists_info( expr: &Expression, outer_tables: &[String], ) -> Option<SemiJoinInfo>
Try to extract SemiJoinInfo from a NOT EXISTS expression. Returns None if the expression is not a valid NOT EXISTS pattern.
Sourcepub fn transform_exists_to_in_list(
info: &SemiJoinInfo,
hash_set: CompactArc<ValueSet>,
) -> Expression
pub fn transform_exists_to_in_list( info: &SemiJoinInfo, hash_set: CompactArc<ValueSet>, ) -> Expression
Transform a WHERE clause with EXISTS into one using a pre-computed hash set.
Replaces: EXISTS (SELECT …) with: outer_col IN (hash_set_values)
Sourcepub fn try_optimize_exists_to_semi_join(
&self,
expr: &Expression,
ctx: &ExecutionContext,
outer_tables: &[String],
outer_limit: Option<i64>,
) -> Result<Option<Expression>>
pub fn try_optimize_exists_to_semi_join( &self, expr: &Expression, ctx: &ExecutionContext, outer_tables: &[String], outer_limit: Option<i64>, ) -> Result<Option<Expression>>
Try to optimize correlated EXISTS subqueries to semi-join. Returns Some(optimized_expression) if successful, None if not applicable.
Note: This function now checks if index-nested-loop would be more efficient and skips the semi-join transformation in that case, allowing per-row index probing.
The outer_limit parameter helps decide between strategies:
- With small LIMIT + index: prefer index-nested-loop (per-row probing with early termination)
- Without LIMIT: prefer semi-join (scan inner once, hash lookup per outer row)
Sourcepub fn try_optimize_in_to_semi_join(
&self,
expr: &Expression,
ctx: &ExecutionContext,
outer_tables: &[String],
) -> Result<Option<Expression>>
pub fn try_optimize_in_to_semi_join( &self, expr: &Expression, ctx: &ExecutionContext, outer_tables: &[String], ) -> Result<Option<Expression>>
Try to optimize IN subqueries to semi-join (execute once, hash lookup per row).
This transforms:
WHERE outer.col IN (SELECT inner_col FROM t WHERE non_correlated_pred)Into:
WHERE outer.col IN (hash_set_of_inner_col_values)§Optimization Criteria
- IN right side must be a scalar subquery
- Subquery must SELECT exactly one column
- Subquery must have a simple table source (no joins)
- Subquery WHERE clause must NOT reference outer tables (non-correlated)
§Performance Impact
- Before: O(N×M) - executes subquery for each outer row
- After: O(N+M) - executes subquery once, O(1) hash lookup per row
Sourcepub fn collect_outer_table_names(
table_expr: &Option<Box<Expression>>,
) -> Vec<String>
pub fn collect_outer_table_names( table_expr: &Option<Box<Expression>>, ) -> Vec<String>
Get outer table names from a table expression (for semi-join optimization).