use crate::{
db::query::plan::expr::ast::{Alias, Expr, FieldId},
model::entity::{EntityModel, resolve_field_slot},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum ProjectionSelection {
All,
Fields(Vec<FieldId>),
Exprs(Vec<ProjectionField>),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(crate) enum ProjectionField {
Scalar { expr: Expr, alias: Option<Alias> },
}
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub(crate) struct ProjectionSpec {
fields: Vec<ProjectionField>,
}
impl ProjectionSpec {
#[must_use]
pub(in crate::db::query::plan) const fn new(fields: Vec<ProjectionField>) -> Self {
Self { fields }
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn from_fields_for_test(fields: Vec<ProjectionField>) -> Self {
Self::new(fields)
}
#[must_use]
pub(crate) const fn len(&self) -> usize {
self.fields.len()
}
pub(crate) fn fields(&self) -> std::slice::Iter<'_, ProjectionField> {
self.fields.iter()
}
}
#[must_use]
#[cfg(not(test))]
pub(in crate::db) const fn projection_field_direct_field_name(
field: &ProjectionField,
) -> Option<&str> {
match field {
ProjectionField::Scalar { expr, .. } => direct_projection_expr_field_name(expr),
}
}
#[must_use]
#[cfg(test)]
pub(in crate::db) fn projection_field_direct_field_name(field: &ProjectionField) -> Option<&str> {
match field {
ProjectionField::Scalar { expr, .. } => direct_projection_expr_field_name(expr),
}
}
#[must_use]
#[cfg(not(test))]
pub(in crate::db) const fn direct_projection_expr_field_name(expr: &Expr) -> Option<&str> {
match expr {
Expr::Field(field) => Some(field.as_str()),
Expr::Literal(_) | Expr::FunctionCall { .. } | Expr::Aggregate(_) => None,
}
}
#[must_use]
#[cfg(test)]
pub(in crate::db) fn direct_projection_expr_field_name(expr: &Expr) -> Option<&str> {
match expr {
Expr::Field(field) => Some(field.as_str()),
Expr::Alias { expr, .. } => direct_projection_expr_field_name(expr.as_ref()),
Expr::Literal(_)
| Expr::FunctionCall { .. }
| Expr::Aggregate(_)
| Expr::Unary { .. }
| Expr::Binary { .. } => None,
}
}
#[must_use]
pub(crate) fn collect_unique_direct_projection_slots<'a>(
model: &EntityModel,
field_names: impl IntoIterator<Item = &'a str>,
) -> Option<Vec<usize>> {
let mut field_slots = Vec::new();
for field_name in field_names {
let slot = resolve_field_slot(model, field_name)?;
if field_slots.contains(&slot) {
return None;
}
field_slots.push(slot);
}
Some(field_slots)
}
#[must_use]
pub(crate) fn expr_references_only_fields(expr: &Expr, allowed: &[&str]) -> bool {
match expr {
Expr::Field(field) => allowed.iter().any(|allowed| *allowed == field.as_str()),
Expr::Aggregate(_) => true,
Expr::Literal(_) => true,
Expr::FunctionCall { args, .. } => args
.iter()
.all(|arg| expr_references_only_fields(arg, allowed)),
#[cfg(test)]
Expr::Alias { expr, .. } => expr_references_only_fields(expr.as_ref(), allowed),
#[cfg(test)]
Expr::Unary { expr, .. } => expr_references_only_fields(expr.as_ref(), allowed),
#[cfg(test)]
Expr::Binary { left, right, .. } => {
expr_references_only_fields(left.as_ref(), allowed)
&& expr_references_only_fields(right.as_ref(), allowed)
}
}
}