1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
11 query::plan::{
12 order_contract::DeterministicSecondaryOrderContract,
13 semantics::LogicalPushdownEligibility,
14 },
15 },
16 model::field::FieldKind,
17 value::Value,
18};
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum QueryMode {
30 Load(LoadSpec),
31 Delete(DeleteSpec),
32}
33
34#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
41pub struct LoadSpec {
42 pub(crate) limit: Option<u32>,
43 pub(crate) offset: u32,
44}
45
46impl LoadSpec {
47 #[must_use]
49 pub const fn limit(&self) -> Option<u32> {
50 self.limit
51 }
52
53 #[must_use]
55 pub const fn offset(&self) -> u32 {
56 self.offset
57 }
58}
59
60#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
68pub struct DeleteSpec {
69 pub(crate) limit: Option<u32>,
70}
71
72impl DeleteSpec {
73 #[must_use]
75 pub const fn limit(&self) -> Option<u32> {
76 self.limit
77 }
78}
79
80#[derive(Clone, Copy, Debug, Eq, PartialEq)]
85pub enum OrderDirection {
86 Asc,
87 Desc,
88}
89
90#[derive(Clone, Debug, Eq, PartialEq)]
96pub(crate) struct OrderSpec {
97 pub(crate) fields: Vec<(String, OrderDirection)>,
98}
99
100#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub(crate) struct DeleteLimitSpec {
107 pub(crate) max_rows: u32,
108}
109
110#[derive(Clone, Copy, Debug, Eq, PartialEq)]
119pub(crate) enum DistinctExecutionStrategy {
120 None,
121 PreOrdered,
122 HashMaterialize,
123}
124
125impl DistinctExecutionStrategy {
126 #[must_use]
128 pub(crate) const fn is_enabled(self) -> bool {
129 !matches!(self, Self::None)
130 }
131}
132
133#[derive(Clone, Debug, Eq, PartialEq)]
142pub(in crate::db) struct PlannerRouteProfile {
143 continuation_policy: ContinuationPolicy,
144 logical_pushdown_eligibility: LogicalPushdownEligibility,
145 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
146}
147
148impl PlannerRouteProfile {
149 #[must_use]
151 pub(in crate::db) const fn new(
152 continuation_policy: ContinuationPolicy,
153 logical_pushdown_eligibility: LogicalPushdownEligibility,
154 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
155 ) -> Self {
156 Self {
157 continuation_policy,
158 logical_pushdown_eligibility,
159 secondary_order_contract,
160 }
161 }
162
163 #[must_use]
166 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
167 Self {
168 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
169 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
170 secondary_order_contract: None,
171 }
172 }
173
174 #[must_use]
176 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
177 &self.continuation_policy
178 }
179
180 #[must_use]
182 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
183 self.logical_pushdown_eligibility
184 }
185
186 #[must_use]
188 pub(in crate::db) const fn secondary_order_contract(
189 &self,
190 ) -> Option<&DeterministicSecondaryOrderContract> {
191 self.secondary_order_contract.as_ref()
192 }
193}
194
195#[derive(Clone, Copy, Debug, Eq, PartialEq)]
204pub(in crate::db) struct ContinuationPolicy {
205 requires_anchor: bool,
206 requires_strict_advance: bool,
207 is_grouped_safe: bool,
208}
209
210impl ContinuationPolicy {
211 #[must_use]
213 pub(in crate::db) const fn new(
214 requires_anchor: bool,
215 requires_strict_advance: bool,
216 is_grouped_safe: bool,
217 ) -> Self {
218 Self {
219 requires_anchor,
220 requires_strict_advance,
221 is_grouped_safe,
222 }
223 }
224
225 #[must_use]
227 pub(in crate::db) const fn requires_anchor(self) -> bool {
228 self.requires_anchor
229 }
230
231 #[must_use]
233 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
234 self.requires_strict_advance
235 }
236
237 #[must_use]
239 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
240 self.is_grouped_safe
241 }
242}
243
244#[derive(Clone, Copy, Debug, Eq, PartialEq)]
253pub(in crate::db) struct ExecutionShapeSignature {
254 continuation_signature: ContinuationSignature,
255}
256
257impl ExecutionShapeSignature {
258 #[must_use]
260 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
261 Self {
262 continuation_signature,
263 }
264 }
265
266 #[must_use]
268 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
269 self.continuation_signature
270 }
271}
272
273#[derive(Clone, Debug, Eq, PartialEq)]
279pub(crate) struct PageSpec {
280 pub(crate) limit: Option<u32>,
281 pub(crate) offset: u32,
282}
283
284#[derive(Clone, Copy, Debug, Eq, PartialEq)]
294pub enum AggregateKind {
295 Count,
296 Sum,
297 Avg,
298 Exists,
299 Min,
300 Max,
301 First,
302 Last,
303}
304
305impl AggregateKind {
306 #[must_use]
308 pub(in crate::db) const fn sql_label(self) -> &'static str {
309 match self {
310 Self::Count => "COUNT",
311 Self::Sum => "SUM",
312 Self::Avg => "AVG",
313 Self::Exists => "EXISTS",
314 Self::First => "FIRST",
315 Self::Last => "LAST",
316 Self::Min => "MIN",
317 Self::Max => "MAX",
318 }
319 }
320
321 #[must_use]
323 pub(crate) const fn is_count(self) -> bool {
324 matches!(self, Self::Count)
325 }
326
327 #[must_use]
329 pub(in crate::db) const fn is_sum(self) -> bool {
330 matches!(self, Self::Sum | Self::Avg)
331 }
332
333 #[must_use]
335 pub(in crate::db) const fn is_extrema(self) -> bool {
336 matches!(self, Self::Min | Self::Max)
337 }
338
339 #[must_use]
341 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
342 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
343 }
344
345 #[must_use]
347 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
348 matches!(
349 self,
350 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
351 )
352 }
353
354 #[must_use]
356 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
357 matches!(self, Self::Count | Self::Sum | Self::Avg)
358 }
359
360 #[must_use]
362 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
363 match self {
364 Self::Min => Some(Direction::Asc),
365 Self::Max => Some(Direction::Desc),
366 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
367 }
368 }
369
370 #[must_use]
372 pub(crate) const fn materialized_fold_direction(self) -> Direction {
373 match self {
374 Self::Min => Direction::Desc,
375 Self::Count
376 | Self::Sum
377 | Self::Avg
378 | Self::Exists
379 | Self::Max
380 | Self::First
381 | Self::Last => Direction::Asc,
382 }
383 }
384
385 #[must_use]
387 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
388 !self.is_count() && !self.is_sum()
389 }
390
391 #[must_use]
393 pub(crate) fn bounded_probe_fetch_hint(
394 self,
395 direction: Direction,
396 offset: usize,
397 page_limit: Option<usize>,
398 ) -> Option<usize> {
399 match self {
400 Self::Exists | Self::First => Some(offset.saturating_add(1)),
401 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
402 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
403 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
404 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
405 }
406 }
407
408 #[must_use]
410 pub(in crate::db) const fn explain_projection_mode_label(
411 self,
412 has_projected_field: bool,
413 covering_projection: bool,
414 ) -> &'static str {
415 if has_projected_field {
416 if covering_projection {
417 "field_idx"
418 } else {
419 "field_mat"
420 }
421 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
422 "entity_term"
423 } else {
424 "scalar_agg"
425 }
426 }
427
428 #[must_use]
430 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
431 matches!(self, Self::Count | Self::Exists)
432 }
433}
434
435#[derive(Clone, Debug, Eq, PartialEq)]
444pub(crate) struct GroupAggregateSpec {
445 pub(crate) kind: AggregateKind,
446 pub(crate) target_field: Option<String>,
447 pub(crate) distinct: bool,
448}
449
450#[derive(Clone, Debug)]
461pub(crate) struct FieldSlot {
462 pub(crate) index: usize,
463 pub(crate) field: String,
464 pub(crate) kind: Option<FieldKind>,
465}
466
467impl PartialEq for FieldSlot {
468 fn eq(&self, other: &Self) -> bool {
469 self.index == other.index && self.field == other.field
470 }
471}
472
473impl Eq for FieldSlot {}
474
475#[derive(Clone, Copy, Debug, Eq, PartialEq)]
484pub(crate) struct GroupedExecutionConfig {
485 pub(crate) max_groups: u64,
486 pub(crate) max_group_bytes: u64,
487}
488
489#[derive(Clone, Debug, Eq, PartialEq)]
498pub(crate) struct GroupSpec {
499 pub(crate) group_fields: Vec<FieldSlot>,
500 pub(crate) aggregates: Vec<GroupAggregateSpec>,
501 pub(crate) execution: GroupedExecutionConfig,
502}
503
504#[derive(Clone, Debug, Eq, PartialEq)]
513pub(crate) enum GroupHavingSymbol {
514 GroupField(FieldSlot),
515 AggregateIndex(usize),
516}
517
518#[derive(Clone, Debug, Eq, PartialEq)]
527pub(crate) struct GroupHavingClause {
528 pub(crate) symbol: GroupHavingSymbol,
529 pub(crate) op: CompareOp,
530 pub(crate) value: Value,
531}
532
533#[derive(Clone, Debug, Eq, PartialEq)]
542pub(crate) struct GroupHavingSpec {
543 pub(crate) clauses: Vec<GroupHavingClause>,
544}
545
546#[derive(Clone, Debug, Eq, PartialEq)]
567pub(crate) struct ScalarPlan {
568 pub(crate) mode: QueryMode,
570
571 pub(crate) predicate: Option<PredicateExecutionModel>,
573
574 pub(crate) order: Option<OrderSpec>,
576
577 pub(crate) distinct: bool,
579
580 pub(crate) delete_limit: Option<DeleteLimitSpec>,
582
583 pub(crate) page: Option<PageSpec>,
585
586 pub(crate) consistency: MissingRowPolicy,
588}
589
590#[derive(Clone, Debug, Eq, PartialEq)]
598pub(crate) struct GroupPlan {
599 pub(crate) scalar: ScalarPlan,
600 pub(crate) group: GroupSpec,
601 pub(crate) having: Option<GroupHavingSpec>,
602}
603
604#[derive(Clone, Debug, Eq, PartialEq)]
612pub(crate) enum LogicalPlan {
613 Scalar(ScalarPlan),
614 Grouped(GroupPlan),
615}