use crate::{
db::query::{
builder::aggregate::AggregateExpr,
plan::{
FieldSlot, GroupAggregateSpec, LogicalPlan,
expr::{
Expr, FieldId, ProjectionField, ProjectionSelection, ProjectionSpec,
collect_unique_direct_projection_slots, projection_field_direct_field_name,
},
semantics::group_aggregate_spec_expr,
},
},
model::entity::EntityModel,
};
#[must_use]
pub(crate) fn lower_projection_intent(
model: &EntityModel,
logical: &LogicalPlan,
selection: &ProjectionSelection,
) -> ProjectionSpec {
match logical {
LogicalPlan::Scalar(_) => lower_scalar_projection(model, selection),
LogicalPlan::Grouped(grouped) => match selection {
ProjectionSelection::Exprs(fields) => ProjectionSpec::new(fields.clone()),
ProjectionSelection::All | ProjectionSelection::Fields(_) => lower_grouped_projection(
grouped.group.group_fields.as_slice(),
grouped.group.aggregates.as_slice(),
),
},
}
}
#[must_use]
pub(crate) const fn lower_global_aggregate_projection(
fields: Vec<ProjectionField>,
) -> ProjectionSpec {
ProjectionSpec::new(fields)
}
fn lower_scalar_projection(model: &EntityModel, selection: &ProjectionSelection) -> ProjectionSpec {
let fields = match selection {
ProjectionSelection::All => model
.fields
.iter()
.map(|field| direct_field_projection(FieldId::new(field.name)))
.collect(),
ProjectionSelection::Fields(field_ids) => field_ids
.iter()
.cloned()
.map(direct_field_projection)
.collect(),
ProjectionSelection::Exprs(fields) => fields.clone(),
};
ProjectionSpec::new(fields)
}
#[must_use]
pub(crate) fn lower_direct_projection_slots(
model: &EntityModel,
logical: &LogicalPlan,
selection: &ProjectionSelection,
) -> Option<Vec<usize>> {
match logical {
LogicalPlan::Scalar(_) => match selection {
ProjectionSelection::All => Some((0..model.fields.len()).collect()),
ProjectionSelection::Fields(field_ids) => {
collect_unique_direct_projection_slots(model, field_ids.iter().map(FieldId::as_str))
}
ProjectionSelection::Exprs(fields) => collect_unique_direct_projection_slots(
model,
fields
.iter()
.map(projection_field_direct_field_name)
.collect::<Option<Vec<_>>>()?,
),
},
LogicalPlan::Grouped(_) => None,
}
}
#[must_use]
pub(crate) fn lower_projection_identity(logical: &LogicalPlan) -> ProjectionSpec {
match logical {
LogicalPlan::Scalar(_) => ProjectionSpec::new(vec![direct_field_projection(FieldId::new(
"__icydb_scalar_projection_default_v1__",
))]),
LogicalPlan::Grouped(grouped) => lower_grouped_projection(
grouped.group.group_fields.as_slice(),
grouped.group.aggregates.as_slice(),
),
}
}
fn lower_grouped_projection(
group_fields: &[FieldSlot],
aggregates: &[GroupAggregateSpec],
) -> ProjectionSpec {
let mut fields = Vec::with_capacity(group_fields.len().saturating_add(aggregates.len()));
for group_field in group_fields {
fields.push(direct_field_projection(FieldId::new(group_field.field())));
}
for aggregate in aggregates {
fields.push(aggregate_projection(group_aggregate_spec_expr(aggregate)));
}
ProjectionSpec::new(fields)
}
const fn direct_field_projection(field_id: FieldId) -> ProjectionField {
ProjectionField::Scalar {
expr: Expr::Field(field_id),
alias: None,
}
}
const fn aggregate_projection(aggregate_expr: AggregateExpr) -> ProjectionField {
ProjectionField::Scalar {
expr: Expr::Aggregate(aggregate_expr),
alias: None,
}
}