use super::condition::{eval_join_condition_to_bool, EquijoinEvalStrategy};
use crate::{
errors::ExecutorError,
evaluator::CombinedExpressionEvaluator,
schema::CombinedSchema,
timeout::{TimeoutContext, CHECK_INTERVAL},
};
#[allow(clippy::too_many_arguments)]
pub fn execute_optimized_equijoin(
left_rows: &[vibesql_storage::Row],
right_rows: &[vibesql_storage::Row],
left_col_idx: usize,
right_col_idx: usize,
remaining_condition: Option<&vibesql_ast::Expression>,
schema: &CombinedSchema,
database: &vibesql_storage::Database,
timeout_ctx: &TimeoutContext,
left_table_names: &[String],
right_table_names: &[String],
outer_row: Option<&vibesql_storage::Row>,
outer_schema: Option<&CombinedSchema>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
let mut result_rows = Vec::new();
let mut iterations = 0;
let evaluator = if remaining_condition.is_some() {
Some(match (outer_row, outer_schema) {
(Some(outer_row), Some(outer_schema)) => {
CombinedExpressionEvaluator::with_database_and_outer_context(
schema,
database,
outer_row,
outer_schema,
)
}
_ => CombinedExpressionEvaluator::with_database(schema, database),
})
} else {
None
};
for left_row in left_rows {
let left_value = &left_row.values[left_col_idx];
for right_row in right_rows {
iterations += 1;
if iterations % CHECK_INTERVAL == 0 {
timeout_ctx.check()?;
}
let right_value = &right_row.values[right_col_idx];
if left_value != right_value {
continue; }
if let Some(remaining_cond) = remaining_condition {
let combined_row = vibesql_storage::Row::combine_for_join(
left_row,
right_row,
left_table_names,
right_table_names,
);
evaluator.as_ref().unwrap().clear_cse_cache();
let matches = eval_join_condition_to_bool(
evaluator.as_ref().unwrap().eval(remaining_cond, &combined_row)?,
)?;
if matches {
result_rows.push(combined_row);
}
} else {
result_rows.push(vibesql_storage::Row::combine_for_join(
left_row,
right_row,
left_table_names,
right_table_names,
));
}
}
}
Ok(result_rows)
}
#[allow(clippy::too_many_arguments)]
pub fn execute_nested_loop_classic(
left_rows: &[vibesql_storage::Row],
right_rows: &[vibesql_storage::Row],
condition: &Option<vibesql_ast::Expression>,
schema: &CombinedSchema,
database: &vibesql_storage::Database,
timeout_ctx: &TimeoutContext,
left_table_names: &[String],
right_table_names: &[String],
outer_row: Option<&vibesql_storage::Row>,
outer_schema: Option<&CombinedSchema>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
if condition.is_none() {
return execute_cross_product_fast(
left_rows,
right_rows,
timeout_ctx,
left_table_names,
right_table_names,
);
}
#[cfg(feature = "parallel")]
{
use crate::select::parallel::ParallelConfig;
let config = ParallelConfig::global();
if config.should_parallelize_join(left_rows.len()) {
return super::parallel::execute_nested_loop_parallel(
left_rows,
right_rows,
condition,
schema,
database,
timeout_ctx,
);
}
}
execute_nested_loop_sequential(
left_rows,
right_rows,
condition,
schema,
database,
timeout_ctx,
left_table_names,
right_table_names,
outer_row,
outer_schema,
)
}
#[allow(clippy::too_many_arguments)]
pub fn execute_nested_loop_sequential(
left_rows: &[vibesql_storage::Row],
right_rows: &[vibesql_storage::Row],
condition: &Option<vibesql_ast::Expression>,
schema: &CombinedSchema,
database: &vibesql_storage::Database,
timeout_ctx: &TimeoutContext,
left_table_names: &[String],
right_table_names: &[String],
outer_row: Option<&vibesql_storage::Row>,
outer_schema: Option<&CombinedSchema>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
let evaluator = match (outer_row, outer_schema) {
(Some(outer_row), Some(outer_schema)) => {
CombinedExpressionEvaluator::with_database_and_outer_context(
schema,
database,
outer_row,
outer_schema,
)
}
_ => CombinedExpressionEvaluator::with_database(schema, database),
};
let mut result_rows = Vec::new();
let mut iterations = 0;
for left_row in left_rows {
for right_row in right_rows {
iterations += 1;
if iterations % CHECK_INTERVAL == 0 {
timeout_ctx.check()?;
}
let combined_row = vibesql_storage::Row::combine_for_join(
left_row,
right_row,
left_table_names,
right_table_names,
);
evaluator.clear_cse_cache();
let matches = eval_join_condition_to_bool(
evaluator.eval(condition.as_ref().unwrap(), &combined_row)?,
)?;
if matches {
result_rows.push(combined_row);
}
}
}
Ok(result_rows)
}
#[inline]
pub fn execute_cross_product_fast(
left_rows: &[vibesql_storage::Row],
right_rows: &[vibesql_storage::Row],
timeout_ctx: &TimeoutContext,
left_table_names: &[String],
right_table_names: &[String],
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
let result_size = left_rows.len() * right_rows.len();
let mut result_rows = Vec::with_capacity(result_size);
const CROSS_CHECK_INTERVAL: usize = 100_000;
let mut iterations = 0;
for left_row in left_rows {
for right_row in right_rows {
iterations += 1;
if iterations % CROSS_CHECK_INTERVAL == 0 {
timeout_ctx.check()?;
}
result_rows.push(vibesql_storage::Row::combine_for_join(
left_row,
right_row,
left_table_names,
right_table_names,
));
}
}
Ok(result_rows)
}
#[allow(clippy::too_many_arguments)]
pub fn execute_with_strategy(
left_rows: &[vibesql_storage::Row],
right_rows: &[vibesql_storage::Row],
condition: &Option<vibesql_ast::Expression>,
eval_strategy: EquijoinEvalStrategy,
schema: &CombinedSchema,
database: &vibesql_storage::Database,
timeout_ctx: &TimeoutContext,
left_table_names: &[String],
right_table_names: &[String],
outer_row: Option<&vibesql_storage::Row>,
outer_schema: Option<&CombinedSchema>,
) -> Result<Vec<vibesql_storage::Row>, ExecutorError> {
match eval_strategy {
EquijoinEvalStrategy::Simple { left_col_idx, right_col_idx, remaining_condition } => {
execute_optimized_equijoin(
left_rows,
right_rows,
left_col_idx,
right_col_idx,
remaining_condition.as_deref(),
schema,
database,
timeout_ctx,
left_table_names,
right_table_names,
outer_row,
outer_schema,
)
}
EquijoinEvalStrategy::Complex => {
execute_nested_loop_classic(
left_rows,
right_rows,
condition,
schema,
database,
timeout_ctx,
left_table_names,
right_table_names,
outer_row,
outer_schema,
)
}
}
}