use crate::db::{
query::plan::{
OrderSpec, OrderTerm,
expr::{ExprType, infer_expr_type},
validate::{OrderPlanError, PlanError},
},
schema::SchemaInfo,
};
pub(in crate::db::query::plan::validate) fn validate_order(
schema: &SchemaInfo,
order: &OrderSpec,
) -> Result<(), PlanError> {
for (term_index, term) in order.fields.iter().enumerate() {
validate_order_term(schema, term_index, term)?;
}
Ok(())
}
fn validate_order_term(
schema: &SchemaInfo,
term_index: usize,
term: &OrderTerm,
) -> Result<(), PlanError> {
if let Some(field) = term.direct_field() {
let Some(field_type) = schema.field(field) else {
return Err(PlanError::from(OrderPlanError::unknown_field(term_index)));
};
return field_type
.is_orderable()
.then_some(())
.ok_or_else(|| PlanError::from(OrderPlanError::unorderable_field(term_index)));
}
if matches!(
term.expr(),
crate::db::query::plan::expr::Expr::FieldPath(_)
) {
return validate_field_path_order_term(schema, term_index, term);
}
validate_expression_order_term(schema, term_index, term)
}
fn validate_field_path_order_term(
schema: &SchemaInfo,
term_index: usize,
term: &OrderTerm,
) -> Result<(), PlanError> {
let inferred = infer_expr_type(term.expr(), schema)?;
if matches!(
inferred,
ExprType::Bool | ExprType::Text | ExprType::Numeric(_) | ExprType::Unknown
) {
return Ok(());
}
Err(PlanError::from(OrderPlanError::unorderable_field(
term_index,
)))
}
fn validate_expression_order_term(
schema: &SchemaInfo,
term_index: usize,
term: &OrderTerm,
) -> Result<(), PlanError> {
let inferred = infer_expr_type(term.expr(), schema)?;
if !matches!(
inferred,
ExprType::Bool | ExprType::Text | ExprType::Numeric(_)
) {
return Err(PlanError::from(OrderPlanError::unorderable_field(
term_index,
)));
}
Ok(())
}
pub(in crate::db::query::plan::validate) fn validate_no_duplicate_non_pk_order_fields(
schema: &SchemaInfo,
order: &OrderSpec,
) -> Result<(), PlanError> {
let mut seen = Vec::with_capacity(order.fields.len());
let primary_key_names = schema.primary_key_names();
for (term_index, term) in order.fields.iter().enumerate() {
let field = term
.direct_field()
.map_or_else(|| term.rendered_label(), str::to_owned);
let non_pk_field = !primary_key_names.iter().any(|pk_field| pk_field == &field);
if !non_pk_field {
continue;
}
if let Some((first_term_index, _)) =
seen.iter().find(|(_, seen_field)| seen_field == &field)
{
return Err(PlanError::from(OrderPlanError::duplicate_order_field(
*first_term_index,
term_index,
)));
}
seen.push((term_index, field));
}
Ok(())
}
pub(in crate::db::query::plan::validate) fn validate_primary_key_tie_break(
schema: &SchemaInfo,
order: &OrderSpec,
) -> Result<(), PlanError> {
order.fields.is_empty().then_some(()).map_or_else(
|| {
let primary_key_names = schema.primary_key_names();
let primary_key_name_refs: Vec<&str> =
primary_key_names.iter().map(String::as_str).collect();
if let Some(primary_key_index) =
primary_key_name_refs.iter().copied().enumerate().find_map(
|(primary_key_index, primary_key_name)| {
let primary_key_present = order
.fields
.iter()
.any(|term| term.direct_field() == Some(primary_key_name));
(!primary_key_present).then_some(primary_key_index)
},
)
{
return Err(PlanError::from(
OrderPlanError::missing_primary_key_tie_break(primary_key_index),
));
}
Ok(())
},
|()| Ok(()),
)
}