use crate::{
db::{
access::AccessPlan,
predicate::PredicateExecutionModel,
query::plan::{
AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
ExecutionShapeSignature, GroupPlan, LogicalPlan, PlannerRouteProfile, QueryMode,
ScalarPlan, derive_logical_pushdown_eligibility, expr::ProjectionSpec,
grouped_cursor_policy_violation, lower_projection_identity, lower_projection_intent,
residual_query_predicate_after_access_path_bounds,
residual_query_predicate_after_filtered_access,
},
},
model::entity::EntityModel,
};
impl QueryMode {
#[must_use]
pub const fn is_load(&self) -> bool {
match self {
Self::Load(_) => true,
Self::Delete(_) => false,
}
}
#[must_use]
pub const fn is_delete(&self) -> bool {
match self {
Self::Delete(_) => true,
Self::Load(_) => false,
}
}
}
impl LogicalPlan {
#[must_use]
pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
match self {
Self::Scalar(plan) => plan,
Self::Grouped(plan) => &plan.scalar,
}
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
match self {
Self::Scalar(plan) => plan,
Self::Grouped(plan) => &mut plan.scalar,
}
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
self.scalar_semantics()
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
self.scalar_semantics_mut()
}
}
impl AccessPlannedQuery {
#[must_use]
pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
self.logical.scalar_semantics()
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
self.logical.scalar_semantics_mut()
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
self.scalar_plan()
}
#[must_use]
#[cfg(test)]
pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
self.scalar_plan_mut()
}
#[must_use]
pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
match &self.logical {
LogicalPlan::Scalar(_) => None,
LogicalPlan::Grouped(plan) => Some(plan),
}
}
#[must_use]
pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
lower_projection_intent(model, &self.logical, &self.projection_selection)
}
#[must_use]
pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
lower_projection_identity(&self.logical)
}
#[must_use]
pub(in crate::db) fn execution_preparation_predicate(&self) -> Option<PredicateExecutionModel> {
let query_predicate = self.scalar_plan().predicate.as_ref()?;
match self.access.selected_index_model() {
Some(index) => residual_query_predicate_after_filtered_access(index, query_predicate),
None => Some(query_predicate.clone()),
}
}
#[must_use]
pub(in crate::db) fn effective_execution_predicate(&self) -> Option<PredicateExecutionModel> {
let filtered_residual = self.execution_preparation_predicate();
let filtered_residual = filtered_residual.as_ref()?;
residual_query_predicate_after_access_path_bounds(self.access.as_path(), filtered_residual)
}
#[must_use]
pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
if !self.scalar_plan().distinct {
return DistinctExecutionStrategy::None;
}
match distinct_runtime_dedup_strategy(&self.access) {
Some(strategy) => strategy,
None => DistinctExecutionStrategy::None,
}
}
pub(in crate::db) fn finalize_planner_route_profile_for_model(&mut self, model: &EntityModel) {
self.set_planner_route_profile(project_planner_route_profile_for_model(model, self));
}
#[must_use]
pub(in crate::db) fn execution_shape_signature(
&self,
entity_path: &'static str,
) -> ExecutionShapeSignature {
ExecutionShapeSignature::new(self.continuation_signature(entity_path))
}
#[must_use]
pub(in crate::db) fn predicate_fully_satisfied_by_access_contract(&self) -> bool {
self.scalar_plan().predicate.is_some() && self.effective_execution_predicate().is_none()
}
#[must_use]
pub(in crate::db) fn has_residual_predicate(&self) -> bool {
self.scalar_plan().predicate.is_some()
&& !self.predicate_fully_satisfied_by_access_contract()
}
}
fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
match access {
AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
Some(DistinctExecutionStrategy::PreOrdered)
}
AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
Some(DistinctExecutionStrategy::HashMaterialize)
}
AccessPlan::Path(_) => None,
}
}
fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
let is_grouped_safe = plan
.grouped_plan()
.is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
ContinuationPolicy::new(
true, true, is_grouped_safe,
)
}
#[must_use]
pub(in crate::db) fn project_planner_route_profile_for_model(
model: &EntityModel,
plan: &AccessPlannedQuery,
) -> PlannerRouteProfile {
let secondary_order_contract = plan
.scalar_plan()
.order
.as_ref()
.and_then(|order| order.deterministic_secondary_order_contract(model.primary_key.name));
PlannerRouteProfile::new(
derive_continuation_policy_validated(plan),
derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
secondary_order_contract,
)
}