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