Skip to main content

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}