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