use crate::{
db::{
access::AccessPlan,
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,
},
},
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 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,
}
}
#[must_use]
pub(in crate::db) fn planner_route_profile(&self, model: &EntityModel) -> PlannerRouteProfile {
PlannerRouteProfile::new(
derive_continuation_policy_validated(self),
derive_logical_pushdown_eligibility(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))
}
}
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,
)
}