use crate::{
db::query::plan::{
OrderSpec, deterministic_secondary_index_order_satisfied, grouped_index_order_satisfied,
},
model::{entity::EntityModel, index::IndexModel},
};
use std::ops::Bound;
#[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) filtered: bool,
pub(in crate::db::query::plan) range_bound_count: u8,
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,
filtered: bool,
range_bound_count: u8,
order_compatible: bool,
) -> Self {
Self {
prefix_len,
exact,
filtered,
range_bound_count,
order_compatible,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(in crate::db::query::plan) enum AndFamilyPriorityClass {
#[default]
Ordinary,
SingletonPrimaryKey,
ConflictingPrimaryKeyChildren,
ExplicitEmpty,
}
impl AndFamilyPriorityClass {
#[must_use]
const fn rank(self) -> u8 {
match self {
Self::Ordinary => 0,
Self::SingletonPrimaryKey => 1,
Self::ConflictingPrimaryKeyChildren => 2,
Self::ExplicitEmpty => 3,
}
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub(in crate::db::query::plan) struct AndFamilyCandidateScore {
pub(in crate::db::query::plan) priority_class: AndFamilyPriorityClass,
pub(in crate::db::query::plan) preferred_on_required_order: bool,
pub(in crate::db::query::plan) family_rank: u8,
}
impl AndFamilyCandidateScore {
#[must_use]
pub(in crate::db::query::plan) const fn new(
priority_class: AndFamilyPriorityClass,
preferred_on_required_order: bool,
family_rank: u8,
) -> Self {
Self {
priority_class,
preferred_on_required_order,
family_rank,
}
}
}
#[must_use]
pub(in crate::db::query::plan) const fn and_family_candidate_score_outranks(
candidate: AndFamilyCandidateScore,
best: AndFamilyCandidateScore,
) -> bool {
if candidate.priority_class.rank() != best.priority_class.rank() {
return candidate.priority_class.rank() > best.priority_class.rank();
}
if candidate.preferred_on_required_order != best.preferred_on_required_order {
return candidate.preferred_on_required_order;
}
if candidate.family_rank != best.family_rank {
return candidate.family_rank > best.family_rank;
}
false
}
#[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.filtered != best.filtered {
return candidate.filtered;
}
if candidate.range_bound_count != best.range_bound_count {
return candidate.range_bound_count > best.range_bound_count;
}
if candidate.order_compatible != best.order_compatible {
return candidate.order_compatible;
}
false
}
#[must_use]
pub(in crate::db::query::plan) const fn range_bound_count<T>(
lower: &Bound<T>,
upper: &Bound<T>,
) -> u8 {
let mut count = 0u8;
if !matches!(lower, Bound::Unbounded) {
count = count.saturating_add(1);
}
if !matches!(upper, Bound::Unbounded) {
count = count.saturating_add(1);
}
count
}
#[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 {
let Some(order_contract) = order.and_then(OrderSpec::grouped_index_order_contract) else {
return false;
};
return grouped_index_order_satisfied(&order_contract, index, prefix_len);
}
let Some(order_contract) = order
.and_then(|order| order.deterministic_secondary_order_contract(model.primary_key.name))
else {
return false;
};
deterministic_secondary_index_order_satisfied(&order_contract, index, prefix_len)
}