#[cfg(test)]
use crate::db::query::plan::expr::UnaryOp;
#[cfg(test)]
use crate::db::scalar_expr::{ScalarValueProgram, compile_scalar_field_program};
#[cfg(test)]
use crate::db::scalar_expr::{compile_scalar_literal_expr_value, scalar_expr_value_into_value};
use crate::value::Value;
use crate::{
db::query::plan::expr::{BinaryOp, Expr, ProjectionField, ProjectionSpec},
model::entity::{EntityModel, resolve_field_slot},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) enum ScalarProjectionExpr {
Field(ScalarProjectionField),
Literal(Value),
FunctionCall {
function: crate::db::query::plan::expr::Function,
args: Vec<Self>,
},
#[cfg(test)]
Unary {
op: UnaryOp,
expr: Box<Self>,
},
Binary {
op: BinaryOp,
left: Box<Self>,
right: Box<Self>,
},
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db) struct ScalarProjectionField {
field: String,
slot: usize,
#[cfg(test)]
program: Option<ScalarValueProgram>,
}
impl ScalarProjectionField {
#[must_use]
pub(in crate::db) const fn field(&self) -> &str {
self.field.as_str()
}
#[must_use]
pub(in crate::db) const fn slot(&self) -> usize {
self.slot
}
#[cfg(test)]
#[must_use]
pub(in crate::db) const fn program(&self) -> Option<&ScalarValueProgram> {
self.program.as_ref()
}
}
pub(in crate::db) fn extend_scalar_projection_referenced_slots(
expr: &ScalarProjectionExpr,
referenced: &mut Vec<usize>,
) {
match expr {
ScalarProjectionExpr::Field(field) => {
if !referenced.contains(&field.slot()) {
referenced.push(field.slot());
}
}
ScalarProjectionExpr::Literal(_) => {}
ScalarProjectionExpr::FunctionCall { args, .. } => {
for arg in args {
extend_scalar_projection_referenced_slots(arg, referenced);
}
}
#[cfg(test)]
ScalarProjectionExpr::Unary { expr, .. } => {
extend_scalar_projection_referenced_slots(expr.as_ref(), referenced);
}
ScalarProjectionExpr::Binary { left, right, .. } => {
extend_scalar_projection_referenced_slots(left.as_ref(), referenced);
extend_scalar_projection_referenced_slots(right.as_ref(), referenced);
}
}
}
#[must_use]
pub(in crate::db) fn compile_scalar_projection_expr(
model: &EntityModel,
expr: &Expr,
) -> Option<ScalarProjectionExpr> {
match expr {
Expr::Field(field_id) => {
let slot = resolve_field_slot(model, field_id.as_str())?;
#[cfg(test)]
let program = compile_scalar_field_program(model, field_id.as_str());
Some(ScalarProjectionExpr::Field(ScalarProjectionField {
field: field_id.as_str().to_string(),
slot,
#[cfg(test)]
program,
}))
}
Expr::Literal(value) => {
#[cfg(test)]
{
compile_scalar_literal_expr_value(value)
.map(scalar_expr_value_into_value)
.map(ScalarProjectionExpr::Literal)
}
#[cfg(not(test))]
{
Some(ScalarProjectionExpr::Literal(value.clone()))
}
}
Expr::FunctionCall { function, args } => {
let args = args
.iter()
.map(|arg| compile_scalar_projection_expr(model, arg))
.collect::<Option<Vec<_>>>()?;
Some(ScalarProjectionExpr::FunctionCall {
function: *function,
args,
})
}
#[cfg(test)]
Expr::Unary { op, expr } => {
compile_scalar_projection_expr(model, expr.as_ref()).map(|expr| {
ScalarProjectionExpr::Unary {
op: *op,
expr: Box::new(expr),
}
})
}
Expr::Binary { op, left, right } => {
let left = compile_scalar_projection_expr(model, left.as_ref())?;
let right = compile_scalar_projection_expr(model, right.as_ref())?;
Some(ScalarProjectionExpr::Binary {
op: *op,
left: Box::new(left),
right: Box::new(right),
})
}
Expr::Aggregate(_) => None,
#[cfg(test)]
Expr::Alias { expr, .. } => compile_scalar_projection_expr(model, expr.as_ref()),
}
}
#[must_use]
pub(in crate::db) fn compile_scalar_projection_plan(
model: &EntityModel,
projection: &ProjectionSpec,
) -> Option<Vec<ScalarProjectionExpr>> {
let mut compiled_fields = Vec::with_capacity(projection.len());
for field in projection.fields() {
match field {
ProjectionField::Scalar { expr, .. } => {
compiled_fields.push(compile_scalar_projection_expr(model, expr)?);
}
}
}
Some(compiled_fields)
}