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