use vibesql_ast::{BinaryOperator, Expression};
use vibesql_types::SqlValue;
use super::super::{
aggregate::AggregateOp,
batch::{ColumnArray, ColumnarBatch},
simd_ops,
};
use crate::errors::ExecutorError;
pub fn compute_expression_aggregate_batch(
batch: &ColumnarBatch,
expr: &Expression,
op: AggregateOp,
) -> Result<SqlValue, ExecutorError> {
if let Expression::BinaryOp { left, op: bin_op, right } = expr {
if *bin_op == BinaryOperator::Multiply {
if let (Expression::ColumnRef(col_id1), Expression::ColumnRef(col_id2)) =
(left.as_ref(), right.as_ref())
{
if col_id1.schema_canonical().is_none() && col_id2.schema_canonical().is_none() {
let left_idx = batch.column_index_by_name(col_id1.column_canonical());
let right_idx = batch.column_index_by_name(col_id2.column_canonical());
if let (Some(l_idx), Some(r_idx)) = (left_idx, right_idx) {
return compute_multiply_aggregate(batch, l_idx, r_idx, op);
}
}
}
}
}
let row_count = batch.row_count();
let mut sum = 0.0f64;
let mut count = 0i64;
for row_idx in 0..row_count {
if let Ok(value) = eval_expr_on_batch(batch, expr, row_idx) {
match value {
SqlValue::Integer(v) => {
sum += v as f64;
count += 1;
}
SqlValue::Double(v) => {
sum += v;
count += 1;
}
SqlValue::Float(v) => {
sum += v as f64;
count += 1;
}
SqlValue::Bigint(v) => {
sum += v as f64;
count += 1;
}
SqlValue::Numeric(v) => {
sum += v;
count += 1;
}
SqlValue::Null => {}
_ => {}
}
}
}
match op {
AggregateOp::Sum => Ok(if count > 0 { SqlValue::Double(sum) } else { SqlValue::Null }),
AggregateOp::Count => Ok(SqlValue::Integer(count)),
AggregateOp::Avg => {
Ok(if count > 0 { SqlValue::Double(sum / count as f64) } else { SqlValue::Null })
}
_ => Err(ExecutorError::UnsupportedExpression(
"MIN/MAX not supported for expression aggregates".to_string(),
)),
}
}
pub fn compute_multiply_aggregate(
batch: &ColumnarBatch,
left_idx: usize,
right_idx: usize,
op: AggregateOp,
) -> Result<SqlValue, ExecutorError> {
let left_col = batch.column(left_idx).ok_or_else(|| ExecutorError::ColumnarColumnNotFound {
column_index: left_idx,
batch_columns: batch.column_count(),
})?;
let right_col =
batch.column(right_idx).ok_or_else(|| ExecutorError::ColumnarColumnNotFound {
column_index: right_idx,
batch_columns: batch.column_count(),
})?;
if let (
ColumnArray::Float64(left_values, left_nulls),
ColumnArray::Float64(right_values, right_nulls),
) = (left_col, right_col)
{
if left_values.len() != right_values.len() {
return Err(ExecutorError::ColumnarLengthMismatch {
context: "multiply_aggregate".to_string(),
expected: left_values.len(),
actual: right_values.len(),
});
}
let (sum, count) = simd_ops::sum_product_f64_masked(
left_values,
right_values,
left_nulls.as_ref().map(|v| v.as_slice()),
right_nulls.as_ref().map(|v| v.as_slice()),
);
return match op {
AggregateOp::Sum => Ok(if count > 0 { SqlValue::Double(sum) } else { SqlValue::Null }),
AggregateOp::Count => Ok(SqlValue::Integer(count)),
AggregateOp::Avg => {
Ok(if count > 0 { SqlValue::Double(sum / count as f64) } else { SqlValue::Null })
}
_ => Err(ExecutorError::UnsupportedExpression(
"MIN/MAX not supported for expression aggregates".to_string(),
)),
};
}
if let (
ColumnArray::Int64(left_values, left_nulls),
ColumnArray::Int64(right_values, right_nulls),
) = (left_col, right_col)
{
if left_values.len() != right_values.len() {
return Err(ExecutorError::ColumnarLengthMismatch {
context: "multiply_aggregate".to_string(),
expected: left_values.len(),
actual: right_values.len(),
});
}
let has_left_nulls = left_nulls.as_ref().map_or(false, |n| n.iter().any(|&x| x));
let has_right_nulls = right_nulls.as_ref().map_or(false, |n| n.iter().any(|&x| x));
if !has_left_nulls && !has_right_nulls {
let len = left_values.len();
let (mut s0, mut s1, mut s2, mut s3) = (0i64, 0i64, 0i64, 0i64);
let chunks = len / 4;
for i in 0..chunks {
let off = i * 4;
s0 = s0.wrapping_add(left_values[off].wrapping_mul(right_values[off]));
s1 = s1.wrapping_add(left_values[off + 1].wrapping_mul(right_values[off + 1]));
s2 = s2.wrapping_add(left_values[off + 2].wrapping_mul(right_values[off + 2]));
s3 = s3.wrapping_add(left_values[off + 3].wrapping_mul(right_values[off + 3]));
}
let mut sum = s0.wrapping_add(s1).wrapping_add(s2).wrapping_add(s3);
for i in (chunks * 4)..len {
sum = sum.wrapping_add(left_values[i].wrapping_mul(right_values[i]));
}
return match op {
AggregateOp::Sum => {
Ok(SqlValue::Integer(sum))
}
AggregateOp::Count => Ok(SqlValue::Integer(len as i64)),
AggregateOp::Avg => Ok(if len > 0 {
SqlValue::Double(sum as f64 / len as f64)
} else {
SqlValue::Null
}),
_ => Err(ExecutorError::UnsupportedExpression(
"MIN/MAX not supported for expression aggregates".to_string(),
)),
};
}
let null_a = left_nulls.as_ref().map(|v| v.as_slice());
let null_b = right_nulls.as_ref().map(|v| v.as_slice());
let mut sum = 0i64;
let mut count = 0i64;
for i in 0..left_values.len() {
let is_null_a = null_a.map_or(false, |n| n.get(i).copied().unwrap_or(false));
let is_null_b = null_b.map_or(false, |n| n.get(i).copied().unwrap_or(false));
if !is_null_a && !is_null_b {
sum = sum.wrapping_add(left_values[i].wrapping_mul(right_values[i]));
count += 1;
}
}
return match op {
AggregateOp::Sum => {
Ok(if count > 0 { SqlValue::Integer(sum) } else { SqlValue::Null })
}
AggregateOp::Count => Ok(SqlValue::Integer(count)),
AggregateOp::Avg => Ok(if count > 0 {
SqlValue::Double(sum as f64 / count as f64)
} else {
SqlValue::Null
}),
_ => Err(ExecutorError::UnsupportedExpression(
"MIN/MAX not supported for expression aggregates".to_string(),
)),
};
}
let left_f64 = column_to_f64_vec(left_col)?;
let right_f64 = column_to_f64_vec(right_col)?;
if left_f64.len() != right_f64.len() {
return Err(ExecutorError::ColumnarLengthMismatch {
context: "multiply_aggregate".to_string(),
expected: left_f64.len(),
actual: right_f64.len(),
});
}
let mut sum = 0.0f64;
let mut count = 0i64;
for i in 0..left_f64.len() {
if let (Some(l), Some(r)) = (left_f64[i], right_f64[i]) {
sum += l * r;
count += 1;
}
}
match op {
AggregateOp::Sum => Ok(if count > 0 { SqlValue::Double(sum) } else { SqlValue::Null }),
AggregateOp::Count => Ok(SqlValue::Integer(count)),
AggregateOp::Avg => {
Ok(if count > 0 { SqlValue::Double(sum / count as f64) } else { SqlValue::Null })
}
_ => Err(ExecutorError::UnsupportedExpression(
"MIN/MAX not supported for expression aggregates".to_string(),
)),
}
}
pub fn column_to_f64_vec(column: &ColumnArray) -> Result<Vec<Option<f64>>, ExecutorError> {
match column {
ColumnArray::Int64(values, nulls) => Ok(values
.iter()
.enumerate()
.map(
|(i, &v)| {
if nulls.as_ref().map_or(false, |n| n[i]) {
None
} else {
Some(v as f64)
}
},
)
.collect()),
ColumnArray::Float64(values, nulls) => Ok(values
.iter()
.enumerate()
.map(|(i, &v)| if nulls.as_ref().map_or(false, |n| n[i]) { None } else { Some(v) })
.collect()),
ColumnArray::Mixed(values) => Ok(values
.iter()
.map(|v| match v {
SqlValue::Integer(n) => Some(*n as f64),
SqlValue::Bigint(n) => Some(*n as f64),
SqlValue::Float(n) => Some(*n as f64),
SqlValue::Double(n) => Some(*n),
SqlValue::Numeric(n) => Some(*n),
SqlValue::Null => None,
_ => None,
})
.collect()),
_ => Err(ExecutorError::UnsupportedArrayType {
operation: "column_to_f64".to_string(),
array_type: format!("{:?}", std::mem::discriminant(column)),
}),
}
}
pub fn eval_expr_on_batch(
batch: &ColumnarBatch,
expr: &Expression,
row_idx: usize,
) -> Result<SqlValue, ExecutorError> {
match expr {
Expression::ColumnRef(col_id) => {
if let Some(col_idx) = batch.column_index_by_name(col_id.column_canonical()) {
batch.get_value(row_idx, col_idx)
} else {
Ok(SqlValue::Null)
}
}
Expression::Literal(val) => Ok(val.clone()),
Expression::BinaryOp { left, op, right } => {
let left_val = eval_expr_on_batch(batch, left, row_idx)?;
let right_val = eval_expr_on_batch(batch, right, row_idx)?;
match (left_val, right_val) {
(SqlValue::Null, _) | (_, SqlValue::Null) => Ok(SqlValue::Null),
(l, r) => {
let l_f64 = sql_value_to_f64(&l).ok_or_else(|| {
ExecutorError::ColumnarTypeMismatch {
operation: "binary_op".to_string(),
left_type: format!("{:?}", l),
right_type: None,
}
})?;
let r_f64 = sql_value_to_f64(&r).ok_or_else(|| {
ExecutorError::ColumnarTypeMismatch {
operation: "binary_op".to_string(),
left_type: format!("{:?}", r),
right_type: None,
}
})?;
let result = match op {
BinaryOperator::Plus => l_f64 + r_f64,
BinaryOperator::Minus => l_f64 - r_f64,
BinaryOperator::Multiply => l_f64 * r_f64,
BinaryOperator::Divide => l_f64 / r_f64,
_ => {
return Err(ExecutorError::UnsupportedExpression(format!(
"Unsupported binary operator: {:?}",
op
)))
}
};
Ok(SqlValue::Double(result))
}
}
}
_ => Err(ExecutorError::UnsupportedExpression(
"Complex expression not supported".to_string(),
)),
}
}
pub fn sql_value_to_f64(val: &SqlValue) -> Option<f64> {
match val {
SqlValue::Integer(v) => Some(*v as f64),
SqlValue::Bigint(v) => Some(*v as f64),
SqlValue::Float(v) => Some(*v as f64),
SqlValue::Double(v) => Some(*v),
SqlValue::Numeric(v) => Some(*v),
SqlValue::Smallint(v) => Some(*v as f64),
_ => None,
}
}