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 traits::FieldValue,
18};
19
20impl QueryMode {
21 #[must_use]
23 pub const fn is_load(&self) -> bool {
24 match self {
25 Self::Load(_) => true,
26 Self::Delete(_) => false,
27 }
28 }
29
30 #[must_use]
32 pub const fn is_delete(&self) -> bool {
33 match self {
34 Self::Delete(_) => true,
35 Self::Load(_) => false,
36 }
37 }
38}
39
40impl LogicalPlan {
41 #[must_use]
43 pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
44 match self {
45 Self::Scalar(plan) => plan,
46 Self::Grouped(plan) => &plan.scalar,
47 }
48 }
49
50 #[must_use]
52 #[cfg(test)]
53 pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
54 match self {
55 Self::Scalar(plan) => plan,
56 Self::Grouped(plan) => &mut plan.scalar,
57 }
58 }
59
60 #[must_use]
62 #[cfg(test)]
63 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
64 self.scalar_semantics()
65 }
66
67 #[must_use]
69 #[cfg(test)]
70 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
71 self.scalar_semantics_mut()
72 }
73}
74
75impl<K> AccessPlannedQuery<K> {
76 #[must_use]
78 pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
79 self.logical.scalar_semantics()
80 }
81
82 #[must_use]
84 pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
85 match &self.logical {
86 LogicalPlan::Scalar(_) => None,
87 LogicalPlan::Grouped(plan) => Some(plan),
88 }
89 }
90
91 #[must_use]
93 pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
94 lower_projection_intent(model, &self.logical)
95 }
96
97 #[must_use]
99 pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
100 lower_projection_identity(&self.logical)
101 }
102
103 #[must_use]
105 pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
106 if !self.scalar_plan().distinct {
107 return DistinctExecutionStrategy::None;
108 }
109
110 match distinct_runtime_dedup_strategy(&self.access) {
114 Some(strategy) => strategy,
115 None => DistinctExecutionStrategy::None,
116 }
117 }
118
119 #[must_use]
121 pub(in crate::db) fn planner_route_profile(&self, model: &EntityModel) -> PlannerRouteProfile {
122 PlannerRouteProfile::new(
123 derive_continuation_policy_validated(self),
124 derive_logical_pushdown_eligibility(model, self),
125 )
126 }
127
128 #[must_use]
130 pub(in crate::db) fn execution_shape_signature(
131 &self,
132 entity_path: &'static str,
133 ) -> ExecutionShapeSignature
134 where
135 K: FieldValue,
136 {
137 ExecutionShapeSignature::new(self.continuation_signature(entity_path))
138 }
139
140 #[must_use]
142 #[cfg(test)]
143 pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
144 self.logical.scalar_semantics_mut()
145 }
146
147 #[must_use]
149 #[cfg(test)]
150 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
151 self.scalar_plan()
152 }
153
154 #[must_use]
156 #[cfg(test)]
157 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
158 self.scalar_plan_mut()
159 }
160}
161
162fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
163 match access {
164 AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
165 Some(DistinctExecutionStrategy::PreOrdered)
166 }
167 AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
168 Some(DistinctExecutionStrategy::HashMaterialize)
169 }
170 AccessPlan::Path(_) => None,
171 }
172}
173
174fn derive_continuation_policy_validated<K>(plan: &AccessPlannedQuery<K>) -> ContinuationPolicy {
175 let is_grouped_safe = plan
176 .grouped_plan()
177 .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
178
179 ContinuationPolicy::new(
180 true, true, is_grouped_safe,
183 )
184}