use crate::{
db::query::plan::{OrderSpec, index_order_terms},
model::{entity::EntityModel, index::IndexModel},
};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(in crate::db::query::plan) struct AccessCandidateScore {
pub(in crate::db::query::plan) prefix_len: usize,
pub(in crate::db::query::plan) exact: bool,
pub(in crate::db::query::plan) order_compatible: bool,
}
impl AccessCandidateScore {
#[must_use]
pub(in crate::db::query::plan) const fn new(
prefix_len: usize,
exact: bool,
order_compatible: bool,
) -> Self {
Self {
prefix_len,
exact,
order_compatible,
}
}
}
#[must_use]
pub(in crate::db::query::plan) const fn access_candidate_score_outranks(
candidate: AccessCandidateScore,
best: AccessCandidateScore,
exact_priority: bool,
) -> bool {
if candidate.prefix_len != best.prefix_len {
return candidate.prefix_len > best.prefix_len;
}
if exact_priority && candidate.exact != best.exact {
return candidate.exact;
}
if candidate.order_compatible != best.order_compatible {
return candidate.order_compatible;
}
false
}
#[must_use]
pub(in crate::db::query::plan) fn candidate_satisfies_secondary_order(
model: &EntityModel,
order: Option<&OrderSpec>,
index: &IndexModel,
prefix_len: usize,
grouped: bool,
) -> bool {
if grouped {
return grouped_order_matches_index(order, index, prefix_len);
}
let Some(order_contract) = order
.and_then(|order| order.deterministic_secondary_order_contract(model.primary_key.name))
else {
return false;
};
let index_terms = index_order_terms(index);
order_contract.matches_index_suffix(&index_terms, prefix_len)
|| order_contract.matches_index_full(&index_terms)
}
fn grouped_order_matches_index(
order: Option<&OrderSpec>,
index: &IndexModel,
prefix_len: usize,
) -> bool {
let Some(order) = order else {
return false;
};
let Some(direction) = order
.fields
.first()
.map(crate::db::query::plan::OrderTerm::direction)
else {
return false;
};
if order
.fields
.iter()
.any(|term| term.direction() != direction)
{
return false;
}
let order_terms = order
.fields
.iter()
.map(crate::db::query::plan::OrderTerm::rendered_label)
.collect::<Vec<_>>();
let index_terms = index_order_terms(index);
order_terms == index_terms
|| (prefix_len <= index_terms.len() && order_terms == index_terms[prefix_len..])
}