mod batch;
mod evaluator;
mod simd;
mod vectorized;
#[cfg(test)]
mod tests;
pub(super) use batch::compute_batch_expression_aggregate;
pub use batch::{evaluate_expression_to_column, evaluate_expression_with_cached_column};
pub(super) use vectorized::compute_expression_aggregate;
use vibesql_ast::Expression;
use super::{AggregateOp, AggregateSource, AggregateSpec};
use crate::schema::CombinedSchema;
pub fn extract_aggregates(
exprs: &[Expression],
schema: &CombinedSchema,
) -> Option<Vec<AggregateSpec>> {
let mut aggregates = Vec::new();
for expr in exprs.iter() {
match expr {
Expression::AggregateFunction { name, distinct, args, .. } => {
if *distinct {
return None;
}
let op = match name.to_uppercase().as_str() {
"SUM" => AggregateOp::Sum,
"COUNT" => AggregateOp::Count,
"AVG" => AggregateOp::Avg,
"MIN" => AggregateOp::Min,
"MAX" => AggregateOp::Max,
_ => return None, };
if op == AggregateOp::Count && args.is_empty() {
aggregates.push(AggregateSpec { op, source: AggregateSource::Column(0) });
continue;
}
if op == AggregateOp::Count && args.len() == 1 {
match &args[0] {
Expression::Wildcard => {
aggregates
.push(AggregateSpec { op, source: AggregateSource::CountStar });
continue;
}
Expression::ColumnRef(col_id) if col_id.column_canonical() == "*" => {
aggregates
.push(AggregateSpec { op, source: AggregateSource::CountStar });
continue;
}
_ => {}
}
}
if args.len() != 1 {
return None; }
let source = match &args[0] {
Expression::ColumnRef(col_id) => {
let column_idx = schema.get_column_index(
col_id.table_canonical(),
col_id.column_canonical(),
)?;
AggregateSource::Column(column_idx)
}
Expression::BinaryOp { .. } => {
if is_simple_arithmetic_expr(&args[0], schema).is_some() {
AggregateSource::Expression(args[0].clone())
} else {
return None; }
}
_ => return None, };
aggregates.push(AggregateSpec { op, source });
}
_ => {
return None; }
}
}
Some(aggregates)
}
fn is_simple_arithmetic_expr(expr: &Expression, schema: &CombinedSchema) -> Option<()> {
match expr {
Expression::ColumnRef(col_id) => {
schema.get_column_index(col_id.table_canonical(), col_id.column_canonical())?;
Some(())
}
Expression::Literal(_) => Some(()),
Expression::BinaryOp { left, op, right } => {
use vibesql_ast::BinaryOperator::*;
match op {
Plus | Minus | Multiply | Divide => {
is_simple_arithmetic_expr(left, schema)?;
is_simple_arithmetic_expr(right, schema)?;
Some(())
}
_ => None, }
}
_ => None, }
}