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