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