use crate::{
db::{
query::plan::{
ExpressionOrderTerm, OrderSpec,
validate::{OrderPlanError, PlanError},
},
schema::SchemaInfo,
},
model::entity::EntityModel,
};
pub(crate) fn validate_order(schema: &SchemaInfo, order: &OrderSpec) -> Result<(), PlanError> {
for (field, _) in &order.fields {
if let Some(field_type) = schema.field(field) {
field_type
.is_orderable()
.then_some(())
.ok_or_else(|| PlanError::from(OrderPlanError::unorderable_field(field)))?;
continue;
}
validate_expression_order_term(schema, field)?;
}
Ok(())
}
fn validate_expression_order_term(schema: &SchemaInfo, field: &str) -> Result<(), PlanError> {
let Some(expression) = ExpressionOrderTerm::parse(field) else {
return Err(PlanError::from(OrderPlanError::unknown_field(field)));
};
let field_type = schema
.field(expression.field())
.ok_or_else(|| PlanError::from(OrderPlanError::unknown_field(field)))?;
if !field_type.is_text() {
return Err(PlanError::from(OrderPlanError::unorderable_field(field)));
}
Ok(())
}
pub(crate) fn validate_no_duplicate_non_pk_order_fields(
model: &EntityModel,
order: &OrderSpec,
) -> Result<(), PlanError> {
let mut seen = Vec::with_capacity(order.fields.len());
let pk_field = model.primary_key.name;
for (field, _) in &order.fields {
let non_pk_field = field != pk_field;
if !non_pk_field {
continue;
}
if seen.contains(&field.as_str()) {
return Err(PlanError::from(OrderPlanError::duplicate_order_field(
field,
)));
}
seen.push(field.as_str());
}
Ok(())
}
pub(crate) fn validate_primary_key_tie_break(
model: &EntityModel,
order: &OrderSpec,
) -> Result<(), PlanError> {
order.fields.is_empty().then_some(()).map_or_else(
|| {
let pk_field = model.primary_key.name;
order
.has_exact_primary_key_tie_break(pk_field)
.then_some(())
.ok_or_else(|| {
PlanError::from(OrderPlanError::missing_primary_key_tie_break(pk_field))
})
},
|()| Ok(()),
)
}