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/// PageSpec
81/// Executor-facing pagination specification.
82///
83
84#[derive(Clone, Debug, Eq, PartialEq)]
85pub(crate) struct PageSpec {
86 pub limit: Option<u32>,
87 pub offset: u32,
88}
89
90///
91/// AggregateKind
92///
93/// Canonical aggregate terminal taxonomy owned by query planning.
94/// All layers (query, explain, fingerprint, executor) must interpret aggregate
95/// terminal semantics through this single enum authority.
96/// Executor must derive traversal and fold direction exclusively from this enum.
97///
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
100pub enum AggregateKind {
101 Count,
102 Sum,
103 Exists,
104 Min,
105 Max,
106 First,
107 Last,
108}
109
110/// Compatibility alias for grouped planning callsites.
111pub(crate) type GroupAggregateKind = AggregateKind;
112
113///
114/// GroupAggregateSpec
115///
116/// One grouped aggregate terminal specification declared at query-plan time.
117/// `target_field` remains optional so future field-target grouped terminals can
118/// reuse this contract without mutating the wrapper shape.
119///
120
121#[derive(Clone, Debug, Eq, PartialEq)]
122pub(crate) struct GroupAggregateSpec {
123 pub(crate) kind: AggregateKind,
124 pub(crate) target_field: Option<String>,
125 pub(crate) distinct: bool,
126}
127
128///
129/// FieldSlot
130///
131/// Canonical resolved field reference used by logical planning.
132/// `index` is the stable slot in `EntityModel::fields`; `field` is retained
133/// for diagnostics and explain surfaces.
134///
135
136#[derive(Clone, Debug, Eq, PartialEq)]
137pub(crate) struct FieldSlot {
138 pub(crate) index: usize,
139 pub(crate) field: String,
140}
141
142///
143/// GroupedExecutionConfig
144///
145/// Declarative grouped-execution budget policy selected by query planning.
146/// This remains planner-owned input; executor policy bridges may still apply
147/// defaults and enforcement strategy at runtime boundaries.
148///
149
150#[derive(Clone, Copy, Debug, Eq, PartialEq)]
151pub(crate) struct GroupedExecutionConfig {
152 pub(crate) max_groups: u64,
153 pub(crate) max_group_bytes: u64,
154}
155
156///
157/// GroupSpec
158///
159/// Declarative GROUP BY stage contract attached to a validated base plan.
160/// This wrapper is intentionally semantic-only; field-slot resolution and
161/// execution-mode derivation remain executor-owned boundaries.
162///
163
164#[derive(Clone, Debug, Eq, PartialEq)]
165pub(crate) struct GroupSpec {
166 pub(crate) group_fields: Vec<FieldSlot>,
167 pub(crate) aggregates: Vec<GroupAggregateSpec>,
168 pub(crate) execution: GroupedExecutionConfig,
169}
170
171///
172/// GroupHavingSymbol
173///
174/// Reference to one grouped HAVING input symbol.
175/// Group-field symbols reference resolved grouped key slots.
176/// Aggregate symbols reference grouped aggregate outputs by declaration index.
177///
178
179#[derive(Clone, Debug, Eq, PartialEq)]
180pub(crate) enum GroupHavingSymbol {
181 GroupField(FieldSlot),
182 AggregateIndex(usize),
183}
184
185///
186/// GroupHavingClause
187///
188/// One conservative grouped HAVING clause.
189/// This clause model intentionally supports one symbol-to-literal comparison
190/// and excludes arbitrary expression trees in grouped v1.
191///
192
193#[derive(Clone, Debug, Eq, PartialEq)]
194pub(crate) struct GroupHavingClause {
195 pub(crate) symbol: GroupHavingSymbol,
196 pub(crate) op: CompareOp,
197 pub(crate) value: Value,
198}
199
200///
201/// GroupHavingSpec
202///
203/// Declarative grouped HAVING specification evaluated after grouped
204/// aggregate finalization and before grouped pagination emission.
205/// Clauses are AND-composed in declaration order.
206///
207
208#[derive(Clone, Debug, Eq, PartialEq)]
209pub(crate) struct GroupHavingSpec {
210 pub(crate) clauses: Vec<GroupHavingClause>,
211}
212
213///
214/// ScalarPlan
215///
216/// Pure scalar logical query intent produced by the planner.
217///
218/// A `ScalarPlan` represents the access-independent query semantics:
219/// predicate/filter, ordering, distinct behavior, pagination/delete windows,
220/// and read-consistency mode.
221///
222/// Design notes:
223/// - Predicates are applied *after* data access
224/// - Ordering is applied after filtering
225/// - Pagination is applied after ordering (load only)
226/// - Delete limits are applied after ordering (delete only)
227/// - Missing-row policy is explicit and must not depend on access strategy
228///
229/// This struct is the logical compiler stage output and intentionally excludes
230/// access-path details.
231///
232
233#[derive(Clone, Debug, Eq, PartialEq)]
234pub(crate) struct ScalarPlan {
235 /// Load vs delete intent.
236 pub(crate) mode: QueryMode,
237
238 /// Optional residual predicate applied after access.
239 pub(crate) predicate: Option<PredicateExecutionModel>,
240
241 /// Optional ordering specification.
242 pub(crate) order: Option<OrderSpec>,
243
244 /// Optional distinct semantics over ordered rows.
245 pub(crate) distinct: bool,
246
247 /// Optional delete bound (delete intents only).
248 pub(crate) delete_limit: Option<DeleteLimitSpec>,
249
250 /// Optional pagination specification.
251 pub(crate) page: Option<PageSpec>,
252
253 /// Missing-row policy for execution.
254 pub(crate) consistency: MissingRowPolicy,
255}
256
257///
258/// GroupPlan
259///
260/// Pure grouped logical intent emitted by grouped planning.
261/// Group metadata is carried through one canonical `GroupSpec` contract.
262///
263
264#[derive(Clone, Debug, Eq, PartialEq)]
265pub(crate) struct GroupPlan {
266 pub(crate) scalar: ScalarPlan,
267 pub(crate) group: GroupSpec,
268 pub(crate) having: Option<GroupHavingSpec>,
269}
270
271///
272/// LogicalPlan
273///
274/// Exclusive logical query intent emitted by planning.
275/// Scalar and grouped semantics are distinct variants by construction.
276///
277
278#[derive(Clone, Debug, Eq, PartialEq)]
279pub(crate) enum LogicalPlan {
280 Scalar(ScalarPlan),
281 Grouped(GroupPlan),
282}