1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{MissingRowPolicy, Predicate},
11 query::{
12 builder::scalar_projection::render_scalar_projection_expr_sql_label,
13 plan::{
14 expr::{Expr, FieldId, normalize_bool_expr},
15 order_contract::DeterministicSecondaryOrderContract,
16 semantics::LogicalPushdownEligibility,
17 },
18 },
19 },
20 model::field::FieldKind,
21};
22
23#[derive(Clone, Copy, Debug, Eq, PartialEq)]
32pub enum QueryMode {
33 Load(LoadSpec),
34 Delete(DeleteSpec),
35}
36
37#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
44pub struct LoadSpec {
45 pub(crate) limit: Option<u32>,
46 pub(crate) offset: u32,
47}
48
49impl LoadSpec {
50 #[must_use]
52 pub const fn limit(&self) -> Option<u32> {
53 self.limit
54 }
55
56 #[must_use]
58 pub const fn offset(&self) -> u32 {
59 self.offset
60 }
61}
62
63#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
71pub struct DeleteSpec {
72 pub(crate) limit: Option<u32>,
73 pub(crate) offset: u32,
74}
75
76impl DeleteSpec {
77 #[must_use]
79 pub const fn limit(&self) -> Option<u32> {
80 self.limit
81 }
82
83 #[must_use]
85 pub const fn offset(&self) -> u32 {
86 self.offset
87 }
88}
89
90#[derive(Clone, Copy, Debug, Eq, PartialEq)]
95pub enum OrderDirection {
96 Asc,
97 Desc,
98}
99
100#[derive(Clone, Eq, PartialEq)]
110pub(crate) struct OrderTerm {
111 pub(crate) expr: Expr,
112 pub(crate) direction: OrderDirection,
113}
114
115impl OrderTerm {
116 #[must_use]
118 pub(crate) const fn new(expr: Expr, direction: OrderDirection) -> Self {
119 Self { expr, direction }
120 }
121
122 #[must_use]
124 pub(crate) fn field(field: impl Into<String>, direction: OrderDirection) -> Self {
125 Self::new(Expr::Field(FieldId::new(field.into())), direction)
126 }
127
128 #[must_use]
130 pub(crate) const fn expr(&self) -> &Expr {
131 &self.expr
132 }
133
134 #[must_use]
136 pub(crate) const fn direct_field(&self) -> Option<&str> {
137 let Expr::Field(field) = &self.expr else {
138 return None;
139 };
140
141 Some(field.as_str())
142 }
143
144 #[must_use]
146 pub(crate) fn rendered_label(&self) -> String {
147 render_scalar_projection_expr_sql_label(&self.expr)
148 }
149
150 #[must_use]
152 pub(crate) const fn direction(&self) -> OrderDirection {
153 self.direction
154 }
155}
156
157impl std::fmt::Debug for OrderTerm {
158 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
159 f.debug_struct("OrderTerm")
160 .field("label", &self.rendered_label())
161 .field("expr", &self.expr)
162 .field("direction", &self.direction)
163 .finish()
164 }
165}
166
167impl PartialEq<(String, OrderDirection)> for OrderTerm {
168 fn eq(&self, other: &(String, OrderDirection)) -> bool {
169 self.rendered_label() == other.0 && self.direction == other.1
170 }
171}
172
173impl PartialEq<OrderTerm> for (String, OrderDirection) {
174 fn eq(&self, other: &OrderTerm) -> bool {
175 self.0 == other.rendered_label() && self.1 == other.direction
176 }
177}
178
179#[must_use]
182pub(in crate::db) fn render_scalar_filter_expr_sql_label(expr: &Expr) -> String {
183 render_scalar_projection_expr_sql_label(&normalize_bool_expr(expr.clone()))
184}
185
186#[derive(Clone, Debug, Eq, PartialEq)]
193pub(crate) struct OrderSpec {
194 pub(crate) fields: Vec<OrderTerm>,
195}
196
197#[derive(Clone, Copy, Debug, Eq, PartialEq)]
203pub(crate) struct DeleteLimitSpec {
204 pub(crate) limit: Option<u32>,
205 pub(crate) offset: u32,
206}
207
208#[derive(Clone, Copy, Debug, Eq, PartialEq)]
217pub(crate) enum DistinctExecutionStrategy {
218 None,
219 PreOrdered,
220 HashMaterialize,
221}
222
223impl DistinctExecutionStrategy {
224 #[must_use]
226 pub(crate) const fn is_enabled(self) -> bool {
227 !matches!(self, Self::None)
228 }
229}
230
231#[derive(Clone, Debug, Eq, PartialEq)]
240pub(in crate::db) struct PlannerRouteProfile {
241 continuation_policy: ContinuationPolicy,
242 logical_pushdown_eligibility: LogicalPushdownEligibility,
243 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
244}
245
246impl PlannerRouteProfile {
247 #[must_use]
249 pub(in crate::db) const fn new(
250 continuation_policy: ContinuationPolicy,
251 logical_pushdown_eligibility: LogicalPushdownEligibility,
252 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
253 ) -> Self {
254 Self {
255 continuation_policy,
256 logical_pushdown_eligibility,
257 secondary_order_contract,
258 }
259 }
260
261 #[must_use]
264 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
265 Self {
266 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
267 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
268 secondary_order_contract: None,
269 }
270 }
271
272 #[must_use]
274 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
275 &self.continuation_policy
276 }
277
278 #[must_use]
280 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
281 self.logical_pushdown_eligibility
282 }
283
284 #[must_use]
286 pub(in crate::db) const fn secondary_order_contract(
287 &self,
288 ) -> Option<&DeterministicSecondaryOrderContract> {
289 self.secondary_order_contract.as_ref()
290 }
291}
292
293#[derive(Clone, Copy, Debug, Eq, PartialEq)]
302pub(in crate::db) struct ContinuationPolicy {
303 requires_anchor: bool,
304 requires_strict_advance: bool,
305 is_grouped_safe: bool,
306}
307
308impl ContinuationPolicy {
309 #[must_use]
311 pub(in crate::db) const fn new(
312 requires_anchor: bool,
313 requires_strict_advance: bool,
314 is_grouped_safe: bool,
315 ) -> Self {
316 Self {
317 requires_anchor,
318 requires_strict_advance,
319 is_grouped_safe,
320 }
321 }
322
323 #[must_use]
325 pub(in crate::db) const fn requires_anchor(self) -> bool {
326 self.requires_anchor
327 }
328
329 #[must_use]
331 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
332 self.requires_strict_advance
333 }
334
335 #[must_use]
337 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
338 self.is_grouped_safe
339 }
340}
341
342#[derive(Clone, Copy, Debug, Eq, PartialEq)]
351pub(in crate::db) struct ExecutionShapeSignature {
352 continuation_signature: ContinuationSignature,
353}
354
355impl ExecutionShapeSignature {
356 #[must_use]
358 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
359 Self {
360 continuation_signature,
361 }
362 }
363
364 #[must_use]
366 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
367 self.continuation_signature
368 }
369}
370
371#[derive(Clone, Debug, Eq, PartialEq)]
377pub(crate) struct PageSpec {
378 pub(crate) limit: Option<u32>,
379 pub(crate) offset: u32,
380}
381
382#[derive(Clone, Copy, Debug, Eq, PartialEq)]
392pub enum AggregateKind {
393 Count,
394 Sum,
395 Avg,
396 Exists,
397 Min,
398 Max,
399 First,
400 Last,
401}
402
403#[derive(Clone, Copy, Debug, Eq, PartialEq)]
413pub(crate) enum GlobalDistinctAggregateKind {
414 Count,
415 Sum,
416 Avg,
417}
418
419impl GlobalDistinctAggregateKind {}
420
421#[derive(Clone, Copy, Debug, Eq, PartialEq)]
432pub(crate) enum GroupedPlanAggregateFamily {
433 CountRowsOnly,
434 FieldTargetRows,
435 GenericRows,
436}
437
438impl GroupedPlanAggregateFamily {
439 #[must_use]
441 pub(crate) const fn code(self) -> &'static str {
442 match self {
443 Self::CountRowsOnly => "count_rows_only",
444 Self::FieldTargetRows => "field_target_rows",
445 Self::GenericRows => "generic_rows",
446 }
447 }
448}
449
450impl AggregateKind {
451 #[must_use]
453 pub(in crate::db) const fn sql_label(self) -> &'static str {
454 match self {
455 Self::Count => "COUNT",
456 Self::Sum => "SUM",
457 Self::Avg => "AVG",
458 Self::Exists => "EXISTS",
459 Self::First => "FIRST",
460 Self::Last => "LAST",
461 Self::Min => "MIN",
462 Self::Max => "MAX",
463 }
464 }
465
466 #[must_use]
468 pub(crate) const fn is_count(self) -> bool {
469 matches!(self, Self::Count)
470 }
471
472 #[must_use]
474 pub(in crate::db) const fn is_sum(self) -> bool {
475 matches!(self, Self::Sum | Self::Avg)
476 }
477
478 #[must_use]
480 pub(in crate::db) const fn is_extrema(self) -> bool {
481 matches!(self, Self::Min | Self::Max)
482 }
483
484 #[must_use]
486 pub(in crate::db) const fn supports_field_target_v1(self) -> bool {
487 matches!(
488 self,
489 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max
490 )
491 }
492
493 #[must_use]
495 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
496 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
497 }
498
499 #[must_use]
501 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
502 matches!(
503 self,
504 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
505 )
506 }
507
508 #[must_use]
511 pub(in crate::db) const fn uses_grouped_distinct_value_dedup_v1(self) -> bool {
512 matches!(self, Self::Count | Self::Sum | Self::Avg)
513 }
514
515 #[must_use]
518 pub(in crate::db::query) const fn fingerprint_tag(self) -> u8 {
519 match self {
520 Self::Count => 0x01,
521 Self::Sum => 0x02,
522 Self::Exists => 0x03,
523 Self::Min => 0x04,
524 Self::Max => 0x05,
525 Self::First => 0x06,
526 Self::Last => 0x07,
527 Self::Avg => 0x08,
528 }
529 }
530
531 #[must_use]
533 pub(in crate::db) const fn global_distinct_kind(self) -> Option<GlobalDistinctAggregateKind> {
534 match self {
535 Self::Count => Some(GlobalDistinctAggregateKind::Count),
536 Self::Sum => Some(GlobalDistinctAggregateKind::Sum),
537 Self::Avg => Some(GlobalDistinctAggregateKind::Avg),
538 Self::Exists | Self::Min | Self::Max | Self::First | Self::Last => None,
539 }
540 }
541
542 #[must_use]
544 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
545 self.global_distinct_kind().is_some()
546 }
547
548 #[must_use]
550 pub(in crate::db) const fn is_count_rows_only_group_terminal(
551 self,
552 has_target_field: bool,
553 distinct: bool,
554 ) -> bool {
555 matches!(self, Self::Count) && !has_target_field && !distinct
556 }
557
558 #[must_use]
560 pub(in crate::db) const fn grouped_plan_family(
561 self,
562 has_target_field: bool,
563 ) -> GroupedPlanAggregateFamily {
564 if has_target_field && self.supports_field_target_v1() {
565 GroupedPlanAggregateFamily::FieldTargetRows
566 } else {
567 GroupedPlanAggregateFamily::GenericRows
568 }
569 }
570
571 #[must_use]
573 pub(in crate::db) const fn supports_grouped_streaming_v1(
574 self,
575 has_target_field: bool,
576 distinct: bool,
577 ) -> bool {
578 if self.supports_field_target_v1() {
579 return !distinct && (self.is_count() || has_target_field);
580 }
581
582 !has_target_field && (!distinct || self.supports_grouped_distinct_v1())
583 }
584
585 #[must_use]
587 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
588 match self {
589 Self::Min => Some(Direction::Asc),
590 Self::Max => Some(Direction::Desc),
591 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
592 }
593 }
594
595 #[must_use]
597 pub(crate) const fn materialized_fold_direction(self) -> Direction {
598 match self {
599 Self::Min => Direction::Desc,
600 Self::Count
601 | Self::Sum
602 | Self::Avg
603 | Self::Exists
604 | Self::Max
605 | Self::First
606 | Self::Last => Direction::Asc,
607 }
608 }
609
610 #[must_use]
612 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
613 !self.is_count() && !self.is_sum()
614 }
615
616 #[must_use]
618 pub(crate) fn bounded_probe_fetch_hint(
619 self,
620 direction: Direction,
621 offset: usize,
622 page_limit: Option<usize>,
623 ) -> Option<usize> {
624 match self {
625 Self::Exists | Self::First => Some(offset.saturating_add(1)),
626 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
627 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
628 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
629 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
630 }
631 }
632
633 #[must_use]
635 pub(in crate::db) const fn explain_projection_mode_label(
636 self,
637 has_projected_field: bool,
638 covering_projection: bool,
639 ) -> &'static str {
640 if has_projected_field {
641 if covering_projection {
642 "field_idx"
643 } else {
644 "field_mat"
645 }
646 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
647 "entity_term"
648 } else {
649 "scalar_agg"
650 }
651 }
652
653 #[must_use]
655 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
656 matches!(self, Self::Count | Self::Exists)
657 }
658}
659
660#[derive(Clone, Debug)]
671pub(crate) struct GroupAggregateSpec {
672 pub(crate) kind: AggregateKind,
673 #[cfg(test)]
674 #[cfg(test)]
675 pub(crate) target_field: Option<String>,
676 pub(crate) input_expr: Option<Box<Expr>>,
677 pub(crate) filter_expr: Option<Box<Expr>>,
678 pub(crate) distinct: bool,
679}
680
681impl PartialEq for GroupAggregateSpec {
682 fn eq(&self, other: &Self) -> bool {
683 self.kind == other.kind
684 && self.input_expr == other.input_expr
685 && self.filter_expr == other.filter_expr
686 && self.distinct == other.distinct
687 }
688}
689
690impl Eq for GroupAggregateSpec {}
691
692impl GroupedPlanAggregateFamily {
693 #[must_use]
695 pub(in crate::db) fn from_grouped_aggregates(aggregates: &[GroupAggregateSpec]) -> Self {
696 if matches!(aggregates, [aggregate] if aggregate.kind().is_count_rows_only_group_terminal(
697 aggregate.target_field().is_some(),
698 aggregate.distinct(),
699 )) {
700 return Self::CountRowsOnly;
701 }
702
703 if aggregates.iter().all(|aggregate| {
704 aggregate
705 .kind()
706 .grouped_plan_family(aggregate.target_field().is_some())
707 == Self::FieldTargetRows
708 }) {
709 return Self::FieldTargetRows;
710 }
711
712 Self::GenericRows
713 }
714}
715
716#[derive(Clone, Debug)]
727pub(crate) struct FieldSlot {
728 pub(crate) index: usize,
729 pub(crate) field: String,
730 pub(crate) kind: Option<FieldKind>,
731}
732
733impl PartialEq for FieldSlot {
734 fn eq(&self, other: &Self) -> bool {
735 self.index == other.index && self.field == other.field
736 }
737}
738
739impl Eq for FieldSlot {}
740
741#[derive(Clone, Copy, Debug, Eq, PartialEq)]
750pub(crate) struct GroupedExecutionConfig {
751 pub(crate) max_groups: u64,
752 pub(crate) max_group_bytes: u64,
753}
754
755#[derive(Clone, Debug, Eq, PartialEq)]
764pub(crate) struct GroupSpec {
765 pub(crate) group_fields: Vec<FieldSlot>,
766 pub(crate) aggregates: Vec<GroupAggregateSpec>,
767 pub(crate) execution: GroupedExecutionConfig,
768}
769
770#[derive(Clone, Debug, Eq, PartialEq)]
791pub(crate) struct ScalarPlan {
792 pub(crate) mode: QueryMode,
794
795 pub(crate) filter_expr: Option<Expr>,
797
798 pub(crate) predicate: Option<Predicate>,
800
801 pub(crate) order: Option<OrderSpec>,
803
804 pub(crate) distinct: bool,
806
807 pub(crate) delete_limit: Option<DeleteLimitSpec>,
809
810 pub(crate) page: Option<PageSpec>,
812
813 pub(crate) consistency: MissingRowPolicy,
815}
816
817#[derive(Clone, Debug, Eq, PartialEq)]
825pub(crate) struct GroupPlan {
826 pub(crate) scalar: ScalarPlan,
827 pub(crate) group: GroupSpec,
828 pub(crate) having_expr: Option<Expr>,
829}
830
831#[derive(Clone, Debug, Eq, PartialEq)]
841pub(crate) enum LogicalPlan {
842 Scalar(ScalarPlan),
843 Grouped(GroupPlan),
844}