use std::sync::Arc;
use crate::{
errors::ExecutorError,
select::columnar::{ColumnArray, ColumnarBatch},
};
pub struct JoinIndices {
pub left_indices: Vec<u32>,
pub right_indices: Vec<u32>,
}
pub struct LeftOuterJoinIndices {
pub left_indices: Vec<u32>,
pub right_indices: Vec<u32>,
}
pub struct RightOuterJoinIndices {
pub left_indices: Vec<u32>,
pub right_indices: Vec<u32>,
}
pub(crate) fn gather_join_result(
left_batch: &ColumnarBatch,
right_batch: &ColumnarBatch,
indices: &JoinIndices,
) -> Result<ColumnarBatch, ExecutorError> {
let _result_count = indices.left_indices.len();
let mut result_columns = Vec::new();
for col_idx in 0..left_batch.column_count() {
let column = left_batch.column(col_idx).unwrap();
let gathered = gather_column(column, &indices.left_indices)?;
result_columns.push(gathered);
}
for col_idx in 0..right_batch.column_count() {
let column = right_batch.column(col_idx).unwrap();
let gathered = gather_column(column, &indices.right_indices)?;
result_columns.push(gathered);
}
let column_names = match (left_batch.column_names(), right_batch.column_names()) {
(Some(left_names), Some(right_names)) => {
let mut names = left_names.to_vec();
names.extend(right_names.iter().cloned());
Some(names)
}
_ => None,
};
ColumnarBatch::from_columns(result_columns, column_names)
}
pub(crate) fn gather_left_outer_result(
left_batch: &ColumnarBatch,
right_batch: &ColumnarBatch,
indices: &LeftOuterJoinIndices,
) -> Result<ColumnarBatch, ExecutorError> {
let mut result_columns = Vec::new();
for col_idx in 0..left_batch.column_count() {
let column = left_batch.column(col_idx).unwrap();
let gathered = gather_column(column, &indices.left_indices)?;
result_columns.push(gathered);
}
for col_idx in 0..right_batch.column_count() {
let column = right_batch.column(col_idx).unwrap();
let gathered = gather_column_with_nulls(column, &indices.right_indices)?;
result_columns.push(gathered);
}
let column_names = match (left_batch.column_names(), right_batch.column_names()) {
(Some(left_names), Some(right_names)) => {
let mut names = left_names.to_vec();
names.extend(right_names.iter().cloned());
Some(names)
}
_ => None,
};
ColumnarBatch::from_columns(result_columns, column_names)
}
pub(crate) fn gather_right_outer_result(
left_batch: &ColumnarBatch,
right_batch: &ColumnarBatch,
indices: &RightOuterJoinIndices,
) -> Result<ColumnarBatch, ExecutorError> {
let mut result_columns = Vec::new();
for col_idx in 0..left_batch.column_count() {
let column = left_batch.column(col_idx).unwrap();
let gathered = gather_column_with_nulls(column, &indices.left_indices)?;
result_columns.push(gathered);
}
for col_idx in 0..right_batch.column_count() {
let column = right_batch.column(col_idx).unwrap();
let gathered = gather_column(column, &indices.right_indices)?;
result_columns.push(gathered);
}
let column_names = match (left_batch.column_names(), right_batch.column_names()) {
(Some(left_names), Some(right_names)) => {
let mut names = left_names.to_vec();
names.extend(right_names.iter().cloned());
Some(names)
}
_ => None,
};
ColumnarBatch::from_columns(result_columns, column_names)
}
pub(crate) fn gather_column(
column: &ColumnArray,
indices: &[u32],
) -> Result<ColumnArray, ExecutorError> {
match column {
ColumnArray::Int64(values, nulls) => {
let gathered: Vec<i64> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Int64(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Int32(values, nulls) => {
let gathered: Vec<i32> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Int32(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Float64(values, nulls) => {
let gathered: Vec<f64> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Float64(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Float32(values, nulls) => {
let gathered: Vec<f32> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Float32(Arc::new(gathered), gathered_nulls))
}
ColumnArray::String(values, nulls) => {
let gathered: Vec<std::sync::Arc<str>> =
indices.iter().map(|&idx| values[idx as usize].clone()).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::String(Arc::new(gathered), gathered_nulls))
}
ColumnArray::FixedString(values, nulls) => {
let gathered: Vec<std::sync::Arc<str>> =
indices.iter().map(|&idx| values[idx as usize].clone()).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::FixedString(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Date(values, nulls) => {
let gathered: Vec<i32> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Date(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Timestamp(values, nulls) => {
let gathered: Vec<i64> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Timestamp(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Boolean(values, nulls) => {
let gathered: Vec<u8> = indices.iter().map(|&idx| values[idx as usize]).collect();
let gathered_nulls = nulls
.as_ref()
.map(|n| Arc::new(indices.iter().map(|&idx| n[idx as usize]).collect()));
Ok(ColumnArray::Boolean(Arc::new(gathered), gathered_nulls))
}
ColumnArray::Mixed(values) => {
let gathered: Vec<vibesql_types::SqlValue> =
indices.iter().map(|&idx| values[idx as usize].clone()).collect();
Ok(ColumnArray::Mixed(Arc::new(gathered)))
}
}
}
pub(crate) fn gather_column_with_nulls(
column: &ColumnArray,
indices: &[u32],
) -> Result<ColumnArray, ExecutorError> {
match column {
ColumnArray::Int64(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0); nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Int64(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Int32(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Int32(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Float64(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0.0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Float64(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Float32(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0.0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Float32(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::String(values, existing_nulls) => {
let mut gathered: Vec<std::sync::Arc<str>> = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(Arc::from(""));
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i].clone());
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::String(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::FixedString(values, existing_nulls) => {
let mut gathered: Vec<std::sync::Arc<str>> = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(Arc::from(""));
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i].clone());
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::FixedString(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Date(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Date(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Timestamp(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Timestamp(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Boolean(values, existing_nulls) => {
let mut gathered = Vec::with_capacity(indices.len());
let mut nulls = Vec::with_capacity(indices.len());
for &idx in indices {
if idx == u32::MAX {
gathered.push(0);
nulls.push(true);
} else {
let i = idx as usize;
gathered.push(values[i]);
nulls.push(existing_nulls.as_ref().is_some_and(|n| n[i]));
}
}
Ok(ColumnArray::Boolean(Arc::new(gathered), Some(Arc::new(nulls))))
}
ColumnArray::Mixed(values) => {
let gathered: Vec<vibesql_types::SqlValue> = indices
.iter()
.map(|&idx| {
if idx == u32::MAX {
vibesql_types::SqlValue::Null
} else {
values[idx as usize].clone()
}
})
.collect();
Ok(ColumnArray::Mixed(Arc::new(gathered)))
}
}
}