1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
11 query::plan::{
12 expr::{Expr, FieldId},
13 order_contract::DeterministicSecondaryOrderContract,
14 semantics::LogicalPushdownEligibility,
15 },
16 },
17 model::field::FieldKind,
18 value::Value,
19};
20
21#[derive(Clone, Copy, Debug, Eq, PartialEq)]
30pub enum QueryMode {
31 Load(LoadSpec),
32 Delete(DeleteSpec),
33}
34
35#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
42pub struct LoadSpec {
43 pub(crate) limit: Option<u32>,
44 pub(crate) offset: u32,
45}
46
47impl LoadSpec {
48 #[must_use]
50 pub const fn limit(&self) -> Option<u32> {
51 self.limit
52 }
53
54 #[must_use]
56 pub const fn offset(&self) -> u32 {
57 self.offset
58 }
59}
60
61#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
69pub struct DeleteSpec {
70 pub(crate) limit: Option<u32>,
71 pub(crate) offset: u32,
72}
73
74impl DeleteSpec {
75 #[must_use]
77 pub const fn limit(&self) -> Option<u32> {
78 self.limit
79 }
80
81 #[must_use]
83 pub const fn offset(&self) -> u32 {
84 self.offset
85 }
86}
87
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
93pub enum OrderDirection {
94 Asc,
95 Desc,
96}
97
98#[derive(Clone, Debug, Eq, PartialEq)]
112pub(crate) struct OrderTerm {
113 pub(crate) label: String,
114 pub(crate) expr: Expr,
115 pub(crate) direction: OrderDirection,
116}
117
118impl OrderTerm {
119 #[must_use]
121 pub(crate) const fn new(label: String, expr: Expr, direction: OrderDirection) -> Self {
122 Self {
123 label,
124 expr,
125 direction,
126 }
127 }
128
129 #[must_use]
131 pub(crate) fn field(field: impl Into<String>, direction: OrderDirection) -> Self {
132 let label = field.into();
133 let field_id = FieldId::new(label.clone());
134
135 Self::new(label, Expr::Field(field_id), direction)
136 }
137
138 #[must_use]
140 pub(crate) const fn label(&self) -> &str {
141 self.label.as_str()
142 }
143
144 #[must_use]
146 pub(crate) const fn expr(&self) -> &Expr {
147 &self.expr
148 }
149
150 #[must_use]
152 pub(crate) const fn direction(&self) -> OrderDirection {
153 self.direction
154 }
155}
156
157impl PartialEq<(String, OrderDirection)> for OrderTerm {
158 fn eq(&self, other: &(String, OrderDirection)) -> bool {
159 self.label == other.0 && self.direction == other.1
160 }
161}
162
163impl PartialEq<OrderTerm> for (String, OrderDirection) {
164 fn eq(&self, other: &OrderTerm) -> bool {
165 self.0 == other.label && self.1 == other.direction
166 }
167}
168
169#[derive(Clone, Debug, Eq, PartialEq)]
170pub(crate) struct OrderSpec {
171 pub(crate) fields: Vec<OrderTerm>,
172}
173
174#[derive(Clone, Copy, Debug, Eq, PartialEq)]
180pub(crate) struct DeleteLimitSpec {
181 pub(crate) limit: Option<u32>,
182 pub(crate) offset: u32,
183}
184
185#[derive(Clone, Copy, Debug, Eq, PartialEq)]
194pub(crate) enum DistinctExecutionStrategy {
195 None,
196 PreOrdered,
197 HashMaterialize,
198}
199
200impl DistinctExecutionStrategy {
201 #[must_use]
203 pub(crate) const fn is_enabled(self) -> bool {
204 !matches!(self, Self::None)
205 }
206}
207
208#[derive(Clone, Debug, Eq, PartialEq)]
217pub(in crate::db) struct PlannerRouteProfile {
218 continuation_policy: ContinuationPolicy,
219 logical_pushdown_eligibility: LogicalPushdownEligibility,
220 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
221}
222
223impl PlannerRouteProfile {
224 #[must_use]
226 pub(in crate::db) const fn new(
227 continuation_policy: ContinuationPolicy,
228 logical_pushdown_eligibility: LogicalPushdownEligibility,
229 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
230 ) -> Self {
231 Self {
232 continuation_policy,
233 logical_pushdown_eligibility,
234 secondary_order_contract,
235 }
236 }
237
238 #[must_use]
241 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
242 Self {
243 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
244 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
245 secondary_order_contract: None,
246 }
247 }
248
249 #[must_use]
251 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
252 &self.continuation_policy
253 }
254
255 #[must_use]
257 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
258 self.logical_pushdown_eligibility
259 }
260
261 #[must_use]
263 pub(in crate::db) const fn secondary_order_contract(
264 &self,
265 ) -> Option<&DeterministicSecondaryOrderContract> {
266 self.secondary_order_contract.as_ref()
267 }
268}
269
270#[derive(Clone, Copy, Debug, Eq, PartialEq)]
279pub(in crate::db) struct ContinuationPolicy {
280 requires_anchor: bool,
281 requires_strict_advance: bool,
282 is_grouped_safe: bool,
283}
284
285impl ContinuationPolicy {
286 #[must_use]
288 pub(in crate::db) const fn new(
289 requires_anchor: bool,
290 requires_strict_advance: bool,
291 is_grouped_safe: bool,
292 ) -> Self {
293 Self {
294 requires_anchor,
295 requires_strict_advance,
296 is_grouped_safe,
297 }
298 }
299
300 #[must_use]
302 pub(in crate::db) const fn requires_anchor(self) -> bool {
303 self.requires_anchor
304 }
305
306 #[must_use]
308 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
309 self.requires_strict_advance
310 }
311
312 #[must_use]
314 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
315 self.is_grouped_safe
316 }
317}
318
319#[derive(Clone, Copy, Debug, Eq, PartialEq)]
328pub(in crate::db) struct ExecutionShapeSignature {
329 continuation_signature: ContinuationSignature,
330}
331
332impl ExecutionShapeSignature {
333 #[must_use]
335 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
336 Self {
337 continuation_signature,
338 }
339 }
340
341 #[must_use]
343 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
344 self.continuation_signature
345 }
346}
347
348#[derive(Clone, Debug, Eq, PartialEq)]
354pub(crate) struct PageSpec {
355 pub(crate) limit: Option<u32>,
356 pub(crate) offset: u32,
357}
358
359#[derive(Clone, Copy, Debug, Eq, PartialEq)]
369pub enum AggregateKind {
370 Count,
371 Sum,
372 Avg,
373 Exists,
374 Min,
375 Max,
376 First,
377 Last,
378}
379
380impl AggregateKind {
381 #[must_use]
383 pub(in crate::db) const fn sql_label(self) -> &'static str {
384 match self {
385 Self::Count => "COUNT",
386 Self::Sum => "SUM",
387 Self::Avg => "AVG",
388 Self::Exists => "EXISTS",
389 Self::First => "FIRST",
390 Self::Last => "LAST",
391 Self::Min => "MIN",
392 Self::Max => "MAX",
393 }
394 }
395
396 #[must_use]
398 pub(crate) const fn is_count(self) -> bool {
399 matches!(self, Self::Count)
400 }
401
402 #[must_use]
404 pub(in crate::db) const fn is_sum(self) -> bool {
405 matches!(self, Self::Sum | Self::Avg)
406 }
407
408 #[must_use]
410 pub(in crate::db) const fn is_extrema(self) -> bool {
411 matches!(self, Self::Min | Self::Max)
412 }
413
414 #[must_use]
416 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
417 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
418 }
419
420 #[must_use]
422 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
423 matches!(
424 self,
425 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
426 )
427 }
428
429 #[must_use]
431 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
432 matches!(self, Self::Count | Self::Sum | Self::Avg)
433 }
434
435 #[must_use]
437 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
438 match self {
439 Self::Min => Some(Direction::Asc),
440 Self::Max => Some(Direction::Desc),
441 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
442 }
443 }
444
445 #[must_use]
447 pub(crate) const fn materialized_fold_direction(self) -> Direction {
448 match self {
449 Self::Min => Direction::Desc,
450 Self::Count
451 | Self::Sum
452 | Self::Avg
453 | Self::Exists
454 | Self::Max
455 | Self::First
456 | Self::Last => Direction::Asc,
457 }
458 }
459
460 #[must_use]
462 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
463 !self.is_count() && !self.is_sum()
464 }
465
466 #[must_use]
468 pub(crate) fn bounded_probe_fetch_hint(
469 self,
470 direction: Direction,
471 offset: usize,
472 page_limit: Option<usize>,
473 ) -> Option<usize> {
474 match self {
475 Self::Exists | Self::First => Some(offset.saturating_add(1)),
476 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
477 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
478 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
479 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
480 }
481 }
482
483 #[must_use]
485 pub(in crate::db) const fn explain_projection_mode_label(
486 self,
487 has_projected_field: bool,
488 covering_projection: bool,
489 ) -> &'static str {
490 if has_projected_field {
491 if covering_projection {
492 "field_idx"
493 } else {
494 "field_mat"
495 }
496 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
497 "entity_term"
498 } else {
499 "scalar_agg"
500 }
501 }
502
503 #[must_use]
505 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
506 matches!(self, Self::Count | Self::Exists)
507 }
508}
509
510#[derive(Clone, Debug)]
521pub(crate) struct GroupAggregateSpec {
522 pub(crate) kind: AggregateKind,
523 #[cfg(test)]
524 #[allow(dead_code)]
525 #[cfg(test)]
526 pub(crate) target_field: Option<String>,
527 pub(crate) input_expr: Option<Box<Expr>>,
528 pub(crate) filter_expr: Option<Box<Expr>>,
529 pub(crate) distinct: bool,
530}
531
532impl PartialEq for GroupAggregateSpec {
533 fn eq(&self, other: &Self) -> bool {
534 self.kind == other.kind
535 && self.input_expr == other.input_expr
536 && self.filter_expr == other.filter_expr
537 && self.distinct == other.distinct
538 }
539}
540
541impl Eq for GroupAggregateSpec {}
542
543#[derive(Clone, Debug)]
554pub(crate) struct FieldSlot {
555 pub(crate) index: usize,
556 pub(crate) field: String,
557 pub(crate) kind: Option<FieldKind>,
558}
559
560impl PartialEq for FieldSlot {
561 fn eq(&self, other: &Self) -> bool {
562 self.index == other.index && self.field == other.field
563 }
564}
565
566impl Eq for FieldSlot {}
567
568#[derive(Clone, Copy, Debug, Eq, PartialEq)]
577pub(crate) struct GroupedExecutionConfig {
578 pub(crate) max_groups: u64,
579 pub(crate) max_group_bytes: u64,
580}
581
582#[derive(Clone, Debug, Eq, PartialEq)]
591pub(crate) struct GroupSpec {
592 pub(crate) group_fields: Vec<FieldSlot>,
593 pub(crate) aggregates: Vec<GroupAggregateSpec>,
594 pub(crate) execution: GroupedExecutionConfig,
595}
596
597#[derive(Clone, Debug, Eq, PartialEq)]
606pub(crate) enum GroupHavingSymbol {
607 GroupField(FieldSlot),
608 AggregateIndex(usize),
609}
610
611#[derive(Clone, Debug, Eq, PartialEq)]
620pub(crate) struct GroupHavingClause {
621 pub(crate) symbol: GroupHavingSymbol,
622 pub(crate) op: CompareOp,
623 pub(crate) value: Value,
624}
625
626#[derive(Clone, Debug, Eq, PartialEq)]
647pub(crate) struct ScalarPlan {
648 pub(crate) mode: QueryMode,
650
651 pub(crate) predicate: Option<PredicateExecutionModel>,
653
654 pub(crate) order: Option<OrderSpec>,
656
657 pub(crate) distinct: bool,
659
660 pub(crate) delete_limit: Option<DeleteLimitSpec>,
662
663 pub(crate) page: Option<PageSpec>,
665
666 pub(crate) consistency: MissingRowPolicy,
668}
669
670#[derive(Clone, Debug, Eq, PartialEq)]
678pub(crate) struct GroupPlan {
679 pub(crate) scalar: ScalarPlan,
680 pub(crate) group: GroupSpec,
681 pub(crate) having_expr: Option<Expr>,
682}
683
684#[derive(Clone, Debug, Eq, PartialEq)]
694pub(crate) enum LogicalPlan {
695 Scalar(ScalarPlan),
696 Grouped(GroupPlan),
697}