mod operators;
mod scalar;
#[cfg(test)]
use crate::db::executor::projection::grouped::{
GroupedRowView, compile_grouped_projection_expr, eval_grouped_projection_expr,
};
use crate::{
db::query::plan::expr::Expr,
error::InternalError,
model::entity::{EntityModel, resolve_field_slot},
value::Value,
};
#[cfg(any(test, feature = "sql"))]
use std::borrow::Cow;
use thiserror::Error as ThisError;
pub(in crate::db::executor) use operators::{eval_binary_expr, eval_unary_expr};
#[cfg(test)]
pub(in crate::db::executor) use scalar::eval_canonical_scalar_projection_expr;
#[cfg(any(test, feature = "sql"))]
pub(in crate::db::executor) use scalar::eval_canonical_scalar_projection_expr_with_required_value_reader_cow;
#[cfg(test)]
pub(in crate::db::executor) use scalar::{ScalarProjectionEvalError, eval_scalar_projection_expr};
pub(in crate::db::executor) use scalar::{
ScalarProjectionExpr, compile_scalar_projection_expr,
eval_scalar_projection_expr_with_value_reader,
};
#[derive(Clone, Debug, Eq, PartialEq, ThisError)]
pub(in crate::db::executor) enum ProjectionEvalError {
#[error("projection expression references unknown field '{field}'")]
UnknownField { field: String },
#[error("projection expression could not read field '{field}' at index={index}")]
MissingFieldValue { field: String, index: usize },
#[error("projection expression cannot evaluate aggregate '{kind}' in scalar row context")]
AggregateNotEvaluable { kind: String },
#[error("projection unary operator '{op}' is incompatible with operand value {found:?}")]
InvalidUnaryOperand { op: String, found: Box<Value> },
#[error(
"projection binary operator '{op}' is incompatible with operand values ({left:?}, {right:?})"
)]
InvalidBinaryOperands {
op: String,
left: Box<Value>,
right: Box<Value>,
},
#[error(
"grouped projection expression references unknown aggregate expression kind={kind} target_field={target_field:?} distinct={distinct}"
)]
UnknownGroupedAggregateExpression {
kind: String,
target_field: Option<String>,
distinct: bool,
},
#[error(
"grouped projection expression references aggregate output index={aggregate_index} but only {aggregate_count} outputs are available"
)]
MissingGroupedAggregateValue {
aggregate_index: usize,
aggregate_count: usize,
},
}
impl ProjectionEvalError {
pub(in crate::db::executor) fn into_invalid_logical_plan_internal_error(self) -> InternalError {
InternalError::query_invalid_logical_plan(self.to_string())
}
pub(in crate::db::executor) fn into_grouped_projection_internal_error(self) -> InternalError {
InternalError::query_invalid_logical_plan(format!(
"grouped projection evaluation failed: {self}",
))
}
}
pub(in crate::db::executor) fn eval_expr_with_slot_reader(
expr: &Expr,
model: &EntityModel,
read_slot: &mut dyn FnMut(usize) -> Option<Value>,
) -> Result<Value, ProjectionEvalError> {
match expr {
Expr::Field(field_id) => {
let field_name = field_id.as_str();
let Some(field_index) = resolve_field_slot(model, field_name) else {
return Err(ProjectionEvalError::UnknownField {
field: field_name.to_string(),
});
};
let Some(value) = read_slot(field_index) else {
return Err(ProjectionEvalError::MissingFieldValue {
field: field_name.to_string(),
index: field_index,
});
};
Ok(value)
}
Expr::Literal(value) => Ok(value.clone()),
Expr::Unary { op, expr } => {
let operand = eval_expr_with_slot_reader(expr.as_ref(), model, read_slot)?;
operators::eval_unary_expr(*op, operand)
}
Expr::Binary { op, left, right } => {
let left_value = eval_expr_with_slot_reader(left.as_ref(), model, read_slot)?;
let right_value = eval_expr_with_slot_reader(right.as_ref(), model, read_slot)?;
operators::eval_binary_expr(*op, left_value, right_value)
}
Expr::Aggregate(aggregate) => Err(ProjectionEvalError::AggregateNotEvaluable {
kind: format!("{:?}", aggregate.kind()),
}),
Expr::Alias { expr, .. } => eval_expr_with_slot_reader(expr.as_ref(), model, read_slot),
}
}
#[cfg(test)]
pub(in crate::db::executor) fn eval_expr_with_required_value_reader(
expr: &Expr,
model: &EntityModel,
read_slot: &mut dyn FnMut(usize) -> Result<Value, InternalError>,
) -> Result<Value, InternalError> {
match expr {
Expr::Field(field_id) => {
let field_name = field_id.as_str();
let Some(field_index) = resolve_field_slot(model, field_name) else {
return Err(ProjectionEvalError::UnknownField {
field: field_name.to_string(),
}
.into_invalid_logical_plan_internal_error());
};
read_slot(field_index)
}
Expr::Literal(value) => Ok(value.clone()),
Expr::Unary { op, expr } => {
let operand = eval_expr_with_required_value_reader(expr.as_ref(), model, read_slot)?;
operators::eval_unary_expr(*op, operand)
.map_err(ProjectionEvalError::into_invalid_logical_plan_internal_error)
}
Expr::Binary { op, left, right } => {
let left_value = eval_expr_with_required_value_reader(left.as_ref(), model, read_slot)?;
let right_value =
eval_expr_with_required_value_reader(right.as_ref(), model, read_slot)?;
operators::eval_binary_expr(*op, left_value, right_value)
.map_err(ProjectionEvalError::into_invalid_logical_plan_internal_error)
}
Expr::Aggregate(aggregate) => Err(ProjectionEvalError::AggregateNotEvaluable {
kind: format!("{:?}", aggregate.kind()),
}
.into_invalid_logical_plan_internal_error()),
Expr::Alias { expr, .. } => {
eval_expr_with_required_value_reader(expr.as_ref(), model, read_slot)
}
}
}
#[cfg(any(test, feature = "sql"))]
pub(in crate::db::executor) fn eval_expr_with_required_value_reader_cow<'a>(
expr: &Expr,
model: &EntityModel,
read_slot: &mut dyn FnMut(usize) -> Result<Cow<'a, Value>, InternalError>,
) -> Result<Cow<'a, Value>, InternalError> {
match expr {
Expr::Field(field_id) => {
let field_name = field_id.as_str();
let Some(field_index) = resolve_field_slot(model, field_name) else {
return Err(ProjectionEvalError::UnknownField {
field: field_name.to_string(),
}
.into_invalid_logical_plan_internal_error());
};
read_slot(field_index)
}
Expr::Literal(value) => Ok(Cow::Owned(value.clone())),
Expr::Unary { op, expr } => {
let operand =
eval_expr_with_required_value_reader_cow(expr.as_ref(), model, read_slot)?
.into_owned();
operators::eval_unary_expr(*op, operand)
.map(Cow::Owned)
.map_err(ProjectionEvalError::into_invalid_logical_plan_internal_error)
}
Expr::Binary { op, left, right } => {
let left_value =
eval_expr_with_required_value_reader_cow(left.as_ref(), model, read_slot)?
.into_owned();
let right_value =
eval_expr_with_required_value_reader_cow(right.as_ref(), model, read_slot)?
.into_owned();
operators::eval_binary_expr(*op, left_value, right_value)
.map(Cow::Owned)
.map_err(ProjectionEvalError::into_invalid_logical_plan_internal_error)
}
Expr::Aggregate(aggregate) => Err(ProjectionEvalError::AggregateNotEvaluable {
kind: format!("{:?}", aggregate.kind()),
}
.into_invalid_logical_plan_internal_error()),
Expr::Alias { expr, .. } => {
eval_expr_with_required_value_reader_cow(expr.as_ref(), model, read_slot)
}
}
}
#[cfg(test)]
pub(in crate::db::executor) fn eval_expr_grouped(
expr: &Expr,
grouped_row: &GroupedRowView<'_>,
) -> Result<Value, ProjectionEvalError> {
let compiled = compile_grouped_projection_expr(
expr,
grouped_row.group_fields,
grouped_row.aggregate_execution_specs,
)?;
eval_grouped_projection_expr(&compiled, grouped_row)
}