use crate::{
db::{
access::single_path_capabilities,
direction::Direction,
executor::{ExecutionPreparation, preparation::slot_map_for_model_plan},
predicate::IndexPredicateCapability,
query::plan::{
AccessPlannedQuery, CoveringReadExecutionPlan, covering_read_execution_plan,
index_covering_existing_rows_terminal_eligible,
},
},
model::entity::EntityModel,
};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::executor) enum BytesTerminalFastPathContract {
PrimaryKeyWindow(Direction),
OrderedKeyStreamWindow(Direction),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::executor) enum CountTerminalFastPathContract {
PrimaryKeyCardinality,
PrimaryKeyExistingRows(Direction),
IndexCoveringExistingRows(Direction),
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(in crate::db::executor) enum ExistsTerminalFastPathContract {
IndexCoveringExistingRows(Direction),
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub(in crate::db::executor) enum LoadTerminalFastPathContract {
CoveringRead(CoveringReadExecutionPlan),
}
fn plan_has_predicate(plan: &AccessPlannedQuery) -> bool {
plan.has_residual_predicate()
}
fn plan_has_no_predicate_or_distinct(plan: &AccessPlannedQuery) -> bool {
!plan_has_predicate(plan) && !plan.scalar_plan().distinct
}
fn unordered_or_primary_key_order_direction_for_model(
model: &EntityModel,
plan: &AccessPlannedQuery,
) -> Option<Direction> {
let Some(order) = plan.scalar_plan().order.as_ref() else {
return Some(Direction::Asc);
};
order
.primary_key_only_direction(model.primary_key().name)
.map(|direction| match direction {
crate::db::query::plan::OrderDirection::Asc => Direction::Asc,
crate::db::query::plan::OrderDirection::Desc => Direction::Desc,
})
}
pub(in crate::db::executor) fn derive_count_terminal_fast_path_contract_for_model(
model: &EntityModel,
plan: &AccessPlannedQuery,
strict_predicate_compatible: bool,
) -> Option<CountTerminalFastPathContract> {
let access_strategy = plan.access.resolve_strategy();
let capabilities = access_strategy.as_path().map(single_path_capabilities)?;
(plan_has_no_predicate_or_distinct(plan)
&& capabilities.supports_count_terminal_primary_key_cardinality())
.then_some(CountTerminalFastPathContract::PrimaryKeyCardinality)
.or_else(|| {
let direction = unordered_or_primary_key_order_direction_for_model(model, plan)?;
(!plan_has_predicate(plan)
&& capabilities.supports_count_terminal_primary_key_existing_rows())
.then_some(CountTerminalFastPathContract::PrimaryKeyExistingRows(
direction,
))
})
.or_else(|| {
index_covering_existing_rows_terminal_eligible(plan, strict_predicate_compatible).then_some(
CountTerminalFastPathContract::IndexCoveringExistingRows(Direction::Asc),
)
})
}
pub(in crate::db::executor) fn derive_exists_terminal_fast_path_contract_for_model(
plan: &AccessPlannedQuery,
strict_predicate_compatible: bool,
) -> Option<ExistsTerminalFastPathContract> {
index_covering_existing_rows_terminal_eligible(plan, strict_predicate_compatible).then_some(
ExistsTerminalFastPathContract::IndexCoveringExistingRows(Direction::Asc),
)
}
pub(in crate::db::executor) fn derive_load_terminal_fast_path_contract_for_model(
model: &EntityModel,
plan: &AccessPlannedQuery,
strict_predicate_compatible: bool,
) -> Option<LoadTerminalFastPathContract> {
covering_read_execution_plan(
model,
plan,
model.primary_key.name,
strict_predicate_compatible,
)
.map(LoadTerminalFastPathContract::CoveringRead)
}
pub(in crate::db::executor) fn derive_load_terminal_fast_path_contract_for_model_plan(
model: &'static EntityModel,
plan: &AccessPlannedQuery,
) -> Option<LoadTerminalFastPathContract> {
if !plan.scalar_plan().mode.is_load() {
return None;
}
let execution_preparation =
ExecutionPreparation::from_plan(model, plan, slot_map_for_model_plan(model, plan));
let strict_predicate_compatible = !plan.has_residual_predicate()
|| execution_preparation
.predicate_capability_profile()
.is_some_and(|profile| profile.index() == IndexPredicateCapability::FullyIndexable);
derive_load_terminal_fast_path_contract_for_model(model, plan, strict_predicate_compatible)
}