icydb_core/db/query/plan/semantics/
logical.rs1use crate::{
7 db::{
8 access::AccessPlan,
9 query::plan::{
10 AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
11 ExecutionShapeSignature, GroupPlan, LogicalPlan, PlannerRouteProfile, QueryMode,
12 ScalarPlan, derive_logical_pushdown_eligibility, expr::ProjectionSpec,
13 grouped_cursor_policy_violation, lower_projection_identity, lower_projection_intent,
14 },
15 },
16 model::entity::EntityModel,
17};
18
19impl QueryMode {
20 #[must_use]
22 pub const fn is_load(&self) -> bool {
23 match self {
24 Self::Load(_) => true,
25 Self::Delete(_) => false,
26 }
27 }
28
29 #[must_use]
31 pub const fn is_delete(&self) -> bool {
32 match self {
33 Self::Delete(_) => true,
34 Self::Load(_) => false,
35 }
36 }
37}
38
39impl LogicalPlan {
40 #[must_use]
42 pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
43 match self {
44 Self::Scalar(plan) => plan,
45 Self::Grouped(plan) => &plan.scalar,
46 }
47 }
48
49 #[must_use]
51 #[cfg(test)]
52 pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
53 match self {
54 Self::Scalar(plan) => plan,
55 Self::Grouped(plan) => &mut plan.scalar,
56 }
57 }
58
59 #[must_use]
61 #[cfg(test)]
62 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
63 self.scalar_semantics()
64 }
65
66 #[must_use]
68 #[cfg(test)]
69 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
70 self.scalar_semantics_mut()
71 }
72}
73
74impl AccessPlannedQuery {
75 #[must_use]
77 pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
78 self.logical.scalar_semantics()
79 }
80
81 #[must_use]
83 pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
84 match &self.logical {
85 LogicalPlan::Scalar(_) => None,
86 LogicalPlan::Grouped(plan) => Some(plan),
87 }
88 }
89
90 #[must_use]
92 pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
93 lower_projection_intent(model, &self.logical, &self.projection_selection)
94 }
95
96 #[must_use]
98 pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
99 lower_projection_identity(&self.logical)
100 }
101
102 #[must_use]
104 pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
105 if !self.scalar_plan().distinct {
106 return DistinctExecutionStrategy::None;
107 }
108
109 match distinct_runtime_dedup_strategy(&self.access) {
113 Some(strategy) => strategy,
114 None => DistinctExecutionStrategy::None,
115 }
116 }
117
118 #[must_use]
120 pub(in crate::db) fn planner_route_profile(&self, model: &EntityModel) -> PlannerRouteProfile {
121 PlannerRouteProfile::new(
122 derive_continuation_policy_validated(self),
123 derive_logical_pushdown_eligibility(model, self),
124 )
125 }
126
127 #[must_use]
129 pub(in crate::db) fn execution_shape_signature(
130 &self,
131 entity_path: &'static str,
132 ) -> ExecutionShapeSignature {
133 ExecutionShapeSignature::new(self.continuation_signature(entity_path))
134 }
135
136 #[must_use]
138 #[cfg(test)]
139 pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
140 self.logical.scalar_semantics_mut()
141 }
142
143 #[must_use]
145 #[cfg(test)]
146 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
147 self.scalar_plan()
148 }
149
150 #[must_use]
152 #[cfg(test)]
153 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
154 self.scalar_plan_mut()
155 }
156}
157
158fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
159 match access {
160 AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
161 Some(DistinctExecutionStrategy::PreOrdered)
162 }
163 AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
164 Some(DistinctExecutionStrategy::HashMaterialize)
165 }
166 AccessPlan::Path(_) => None,
167 }
168}
169
170fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
171 let is_grouped_safe = plan
172 .grouped_plan()
173 .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
174
175 ContinuationPolicy::new(
176 true, true, is_grouped_safe,
179 )
180}