#[cfg(feature = "parallel")]
use rayon::prelude::*;
use super::{
build::{build_hash_table_composite_parallel, build_hash_table_parallel, CompositeKey},
FromResult,
};
#[cfg(feature = "parallel")]
use crate::select::parallel::ParallelConfig;
use crate::{
errors::ExecutorError, evaluator::CombinedExpressionEvaluator, schema::CombinedSchema,
select::join::combine_rows,
};
#[allow(dead_code)]
pub(crate) fn create_null_row(col_count: usize) -> vibesql_storage::Row {
vibesql_storage::Row::new(vec![vibesql_types::SqlValue::Null; col_count])
}
pub(in crate::select::join) fn hash_join_left_outer(
left: FromResult,
right: FromResult,
left_col_idx: usize,
right_col_idx: usize,
) -> Result<FromResult, ExecutorError> {
let right_col_count = right.schema.total_columns;
let left_col_count = left.schema.total_columns;
let combined_schema = CombinedSchema::merge(left.schema.clone(), right.schema.clone());
let left_slice = left.as_slice();
let right_slice = right.as_slice();
let hash_table = build_hash_table_parallel(right_slice, right_col_idx);
let combined_size = left_col_count + right_col_count;
#[cfg(feature = "parallel")]
{
let config = ParallelConfig::global();
if config.should_parallelize_join(left_slice.len()) {
let null_values = vec![vibesql_types::SqlValue::Null; right_col_count];
let result_rows: Vec<vibesql_storage::Row> = left_slice
.par_iter()
.flat_map(|left_row| {
let key = &left_row.values[left_col_idx];
if key == &vibesql_types::SqlValue::Null {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
return vec![vibesql_storage::Row::new(combined)];
}
if let Some(right_indices) = hash_table.get(key) {
right_indices
.iter()
.map(|&right_idx| {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&right_slice[right_idx].values);
vibesql_storage::Row::new(combined)
})
.collect()
} else {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
vec![vibesql_storage::Row::new(combined)]
}
})
.collect();
return Ok(FromResult::from_rows(combined_schema, result_rows));
}
}
let mut match_count = 0usize;
let mut unmatched_count = 0usize;
for left_row in left_slice {
let key = &left_row.values[left_col_idx];
if key == &vibesql_types::SqlValue::Null {
unmatched_count += 1;
} else if let Some(right_indices) = hash_table.get(key) {
match_count += right_indices.len();
} else {
unmatched_count += 1;
}
}
let mut result_rows = Vec::with_capacity(match_count + unmatched_count);
let null_values = vec![vibesql_types::SqlValue::Null; right_col_count];
for left_row in left_slice {
let key = &left_row.values[left_col_idx];
if key == &vibesql_types::SqlValue::Null {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
continue;
}
if let Some(right_indices) = hash_table.get(key) {
for &right_idx in right_indices {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&right_slice[right_idx].values);
result_rows.push(vibesql_storage::Row::new(combined));
}
} else {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
}
}
Ok(FromResult::from_rows(combined_schema, result_rows))
}
pub(in crate::select::join) fn hash_join_left_outer_multi(
left: FromResult,
right: FromResult,
left_col_indices: &[usize],
right_col_indices: &[usize],
) -> Result<FromResult, ExecutorError> {
let right_col_count = right.schema.total_columns;
let left_col_count = left.schema.total_columns;
let combined_schema = CombinedSchema::merge(left.schema.clone(), right.schema.clone());
let left_slice = left.as_slice();
let right_slice = right.as_slice();
let hash_table = build_hash_table_composite_parallel(right_slice, right_col_indices);
let combined_size = left_col_count + right_col_count;
#[cfg(feature = "parallel")]
{
let config = ParallelConfig::global();
if config.should_parallelize_join(left_slice.len()) {
let null_values = vec![vibesql_types::SqlValue::Null; right_col_count];
let result_rows: Vec<vibesql_storage::Row> = left_slice
.par_iter()
.flat_map(|left_row| {
let probe_key = CompositeKey::from_row(left_row, left_col_indices);
if probe_key.has_null() {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
return vec![vibesql_storage::Row::new(combined)];
}
if let Some(right_indices) = hash_table.get(&probe_key) {
right_indices
.iter()
.map(|&right_idx| {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&right_slice[right_idx].values);
vibesql_storage::Row::new(combined)
})
.collect()
} else {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
vec![vibesql_storage::Row::new(combined)]
}
})
.collect();
return Ok(FromResult::from_rows(combined_schema, result_rows));
}
}
let null_values = vec![vibesql_types::SqlValue::Null; right_col_count];
let mut result_rows = Vec::with_capacity(left_slice.len());
for left_row in left_slice {
let probe_key = CompositeKey::from_row(left_row, left_col_indices);
if probe_key.has_null() {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
continue;
}
if let Some(right_indices) = hash_table.get(&probe_key) {
for &right_idx in right_indices {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&right_slice[right_idx].values);
result_rows.push(vibesql_storage::Row::new(combined));
}
} else {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
}
}
Ok(FromResult::from_rows(combined_schema, result_rows))
}
pub(in crate::select::join) fn hash_join_left_outer_with_filter(
left: FromResult,
right: FromResult,
left_col_idx: usize,
right_col_idx: usize,
additional_filter: &vibesql_ast::Expression,
combined_schema: &CombinedSchema,
database: &vibesql_storage::Database,
) -> Result<FromResult, ExecutorError> {
let right_col_count = right.schema.total_columns;
let left_col_count = left.schema.total_columns;
let left_slice = left.as_slice();
let right_slice = right.as_slice();
let hash_table = build_hash_table_parallel(right_slice, right_col_idx);
let combined_size = left_col_count + right_col_count;
let evaluator = CombinedExpressionEvaluator::with_database(combined_schema, database);
let null_values = vec![vibesql_types::SqlValue::Null; right_col_count];
let mut result_rows = Vec::with_capacity(left_slice.len());
for left_row in left_slice {
let key = &left_row.values[left_col_idx];
if key == &vibesql_types::SqlValue::Null {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
continue;
}
if let Some(right_indices) = hash_table.get(key) {
let mut any_match_passed = false;
for &right_idx in right_indices {
let right_row = &right_slice[right_idx];
let combined_row = combine_rows(left_row, right_row);
evaluator.clear_cse_cache();
match evaluator.eval(additional_filter, &combined_row) {
Ok(vibesql_types::SqlValue::Boolean(true)) => {
any_match_passed = true;
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&right_row.values);
result_rows.push(vibesql_storage::Row::new(combined));
}
Ok(vibesql_types::SqlValue::Boolean(false))
| Ok(vibesql_types::SqlValue::Null) => {
continue;
}
Ok(_) | Err(_) => {
continue;
}
}
}
if !any_match_passed {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
}
} else {
let mut combined = Vec::with_capacity(combined_size);
combined.extend_from_slice(&left_row.values);
combined.extend_from_slice(&null_values);
result_rows.push(vibesql_storage::Row::new(combined));
}
}
let result_schema = combined_schema.clone();
Ok(FromResult::from_rows(result_schema, result_rows))
}