icydb_core/db/query/plan/model.rs
1//! Module: query::plan::model
2//! Responsibility: pure logical query-plan data contracts.
3//! Does not own: constructors, plan assembly, or semantic interpretation.
4//! Boundary: data-only types shared by plan builder/semantics/validation layers.
5
6use crate::{
7 db::predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
8 value::Value,
9};
10
11///
12/// QueryMode
13///
14/// Discriminates load vs delete intent at planning time.
15/// Encodes mode-specific fields so invalid states are unrepresentable.
16/// Mode checks are explicit and stable at execution time.
17///
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub enum QueryMode {
21 Load(LoadSpec),
22 Delete(DeleteSpec),
23}
24
25///
26/// LoadSpec
27///
28/// Mode-specific fields for load intents.
29/// Encodes pagination without leaking into delete intents.
30///
31#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
32pub struct LoadSpec {
33 pub limit: Option<u32>,
34 pub offset: u32,
35}
36
37///
38/// DeleteSpec
39///
40/// Mode-specific fields for delete intents.
41/// Encodes delete limits without leaking into load intents.
42///
43
44#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
45pub struct DeleteSpec {
46 pub limit: Option<u32>,
47}
48
49///
50/// OrderDirection
51/// Executor-facing ordering direction (applied after filtering).
52///
53#[derive(Clone, Copy, Debug, Eq, PartialEq)]
54pub enum OrderDirection {
55 Asc,
56 Desc,
57}
58
59///
60/// OrderSpec
61/// Executor-facing ordering specification.
62///
63
64#[derive(Clone, Debug, Eq, PartialEq)]
65pub(crate) struct OrderSpec {
66 pub(crate) fields: Vec<(String, OrderDirection)>,
67}
68
69///
70/// DeleteLimitSpec
71/// Executor-facing delete bound with no offsets.
72///
73
74#[derive(Clone, Copy, Debug, Eq, PartialEq)]
75pub(crate) struct DeleteLimitSpec {
76 pub max_rows: u32,
77}
78
79///
80/// DistinctExecutionStrategy
81///
82/// Planner-owned scalar DISTINCT execution strategy.
83/// This is execution-mechanics only and must not be used for semantic
84/// admissibility decisions.
85///
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
88pub(crate) enum DistinctExecutionStrategy {
89 None,
90 PreOrdered,
91 HashMaterialize,
92}
93
94impl DistinctExecutionStrategy {
95 /// Return true when scalar DISTINCT execution is enabled.
96 #[must_use]
97 pub(crate) const fn is_enabled(self) -> bool {
98 !matches!(self, Self::None)
99 }
100}
101
102///
103/// PlannerRouteProfile
104///
105/// Planner-projected route profile consumed by executor route planning.
106/// Carries planner-owned continuation policy that route/load layers must honor.
107///
108
109#[derive(Clone, Debug, Eq, PartialEq)]
110pub(in crate::db) struct PlannerRouteProfile {
111 continuation_policy: ContinuationPolicy,
112}
113
114impl PlannerRouteProfile {
115 /// Construct one planner-projected route profile.
116 #[must_use]
117 pub(in crate::db) const fn new(continuation_policy: ContinuationPolicy) -> Self {
118 Self {
119 continuation_policy,
120 }
121 }
122
123 /// Borrow planner-projected continuation policy contract.
124 #[must_use]
125 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
126 &self.continuation_policy
127 }
128}
129
130///
131/// ContinuationPolicy
132///
133/// Planner-projected continuation contract carried into route/executor layers.
134/// This contract captures static continuation invariants and must not be
135/// rederived by route/load orchestration code.
136///
137
138#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub(in crate::db) struct ContinuationPolicy {
140 requires_anchor: bool,
141 requires_strict_advance: bool,
142 is_grouped_safe: bool,
143}
144
145impl ContinuationPolicy {
146 /// Construct one planner-projected continuation policy contract.
147 #[must_use]
148 pub(in crate::db) const fn new(
149 requires_anchor: bool,
150 requires_strict_advance: bool,
151 is_grouped_safe: bool,
152 ) -> Self {
153 Self {
154 requires_anchor,
155 requires_strict_advance,
156 is_grouped_safe,
157 }
158 }
159
160 /// Return true when continuation resume paths require an anchor boundary.
161 #[must_use]
162 pub(in crate::db) const fn requires_anchor(self) -> bool {
163 self.requires_anchor
164 }
165
166 /// Return true when continuation resume paths require strict advancement.
167 #[must_use]
168 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
169 self.requires_strict_advance
170 }
171
172 /// Return true when grouped continuation usage is semantically safe.
173 #[must_use]
174 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
175 self.is_grouped_safe
176 }
177}
178
179///
180/// PageSpec
181/// Executor-facing pagination specification.
182///
183
184#[derive(Clone, Debug, Eq, PartialEq)]
185pub(crate) struct PageSpec {
186 pub limit: Option<u32>,
187 pub offset: u32,
188}
189
190///
191/// AggregateKind
192///
193/// Canonical aggregate terminal taxonomy owned by query planning.
194/// All layers (query, explain, fingerprint, executor) must interpret aggregate
195/// terminal semantics through this single enum authority.
196/// Executor must derive traversal and fold direction exclusively from this enum.
197///
198
199#[derive(Clone, Copy, Debug, Eq, PartialEq)]
200pub enum AggregateKind {
201 Count,
202 Sum,
203 Exists,
204 Min,
205 Max,
206 First,
207 Last,
208}
209
210/// Compatibility alias for grouped planning callsites.
211pub(crate) type GroupAggregateKind = AggregateKind;
212
213///
214/// GroupAggregateSpec
215///
216/// One grouped aggregate terminal specification declared at query-plan time.
217/// `target_field` remains optional so future field-target grouped terminals can
218/// reuse this contract without mutating the wrapper shape.
219///
220
221#[derive(Clone, Debug, Eq, PartialEq)]
222pub(crate) struct GroupAggregateSpec {
223 pub(crate) kind: AggregateKind,
224 pub(crate) target_field: Option<String>,
225 pub(crate) distinct: bool,
226}
227
228///
229/// FieldSlot
230///
231/// Canonical resolved field reference used by logical planning.
232/// `index` is the stable slot in `EntityModel::fields`; `field` is retained
233/// for diagnostics and explain surfaces.
234///
235
236#[derive(Clone, Debug, Eq, PartialEq)]
237pub(crate) struct FieldSlot {
238 pub(crate) index: usize,
239 pub(crate) field: String,
240}
241
242///
243/// GroupedExecutionConfig
244///
245/// Declarative grouped-execution budget policy selected by query planning.
246/// This remains planner-owned input; executor policy bridges may still apply
247/// defaults and enforcement strategy at runtime boundaries.
248///
249
250#[derive(Clone, Copy, Debug, Eq, PartialEq)]
251pub(crate) struct GroupedExecutionConfig {
252 pub(crate) max_groups: u64,
253 pub(crate) max_group_bytes: u64,
254}
255
256///
257/// GroupSpec
258///
259/// Declarative GROUP BY stage contract attached to a validated base plan.
260/// This wrapper is intentionally semantic-only; field-slot resolution and
261/// execution-mode derivation remain executor-owned boundaries.
262///
263
264#[derive(Clone, Debug, Eq, PartialEq)]
265pub(crate) struct GroupSpec {
266 pub(crate) group_fields: Vec<FieldSlot>,
267 pub(crate) aggregates: Vec<GroupAggregateSpec>,
268 pub(crate) execution: GroupedExecutionConfig,
269}
270
271///
272/// GroupHavingSymbol
273///
274/// Reference to one grouped HAVING input symbol.
275/// Group-field symbols reference resolved grouped key slots.
276/// Aggregate symbols reference grouped aggregate outputs by declaration index.
277///
278
279#[derive(Clone, Debug, Eq, PartialEq)]
280pub(crate) enum GroupHavingSymbol {
281 GroupField(FieldSlot),
282 AggregateIndex(usize),
283}
284
285///
286/// GroupHavingClause
287///
288/// One conservative grouped HAVING clause.
289/// This clause model intentionally supports one symbol-to-literal comparison
290/// and excludes arbitrary expression trees in grouped v1.
291///
292
293#[derive(Clone, Debug, Eq, PartialEq)]
294pub(crate) struct GroupHavingClause {
295 pub(crate) symbol: GroupHavingSymbol,
296 pub(crate) op: CompareOp,
297 pub(crate) value: Value,
298}
299
300///
301/// GroupHavingSpec
302///
303/// Declarative grouped HAVING specification evaluated after grouped
304/// aggregate finalization and before grouped pagination emission.
305/// Clauses are AND-composed in declaration order.
306///
307
308#[derive(Clone, Debug, Eq, PartialEq)]
309pub(crate) struct GroupHavingSpec {
310 pub(crate) clauses: Vec<GroupHavingClause>,
311}
312
313///
314/// ScalarPlan
315///
316/// Pure scalar logical query intent produced by the planner.
317///
318/// A `ScalarPlan` represents the access-independent query semantics:
319/// predicate/filter, ordering, distinct behavior, pagination/delete windows,
320/// and read-consistency mode.
321///
322/// Design notes:
323/// - Predicates are applied *after* data access
324/// - Ordering is applied after filtering
325/// - Pagination is applied after ordering (load only)
326/// - Delete limits are applied after ordering (delete only)
327/// - Missing-row policy is explicit and must not depend on access strategy
328///
329/// This struct is the logical compiler stage output and intentionally excludes
330/// access-path details.
331///
332
333#[derive(Clone, Debug, Eq, PartialEq)]
334pub(crate) struct ScalarPlan {
335 /// Load vs delete intent.
336 pub(crate) mode: QueryMode,
337
338 /// Optional residual predicate applied after access.
339 pub(crate) predicate: Option<PredicateExecutionModel>,
340
341 /// Optional ordering specification.
342 pub(crate) order: Option<OrderSpec>,
343
344 /// Optional distinct semantics over ordered rows.
345 pub(crate) distinct: bool,
346
347 /// Optional delete bound (delete intents only).
348 pub(crate) delete_limit: Option<DeleteLimitSpec>,
349
350 /// Optional pagination specification.
351 pub(crate) page: Option<PageSpec>,
352
353 /// Missing-row policy for execution.
354 pub(crate) consistency: MissingRowPolicy,
355}
356
357///
358/// GroupPlan
359///
360/// Pure grouped logical intent emitted by grouped planning.
361/// Group metadata is carried through one canonical `GroupSpec` contract.
362///
363
364#[derive(Clone, Debug, Eq, PartialEq)]
365pub(crate) struct GroupPlan {
366 pub(crate) scalar: ScalarPlan,
367 pub(crate) group: GroupSpec,
368 pub(crate) having: Option<GroupHavingSpec>,
369}
370
371///
372/// LogicalPlan
373///
374/// Exclusive logical query intent emitted by planning.
375/// Scalar and grouped semantics are distinct variants by construction.
376///
377
378#[derive(Clone, Debug, Eq, PartialEq)]
379pub(crate) enum LogicalPlan {
380 Scalar(ScalarPlan),
381 Grouped(GroupPlan),
382}