1use crate::db::{
7 cursor::ContinuationSignature,
8 direction::Direction,
9 predicate::{MissingRowPolicy, Predicate},
10 query::{
11 builder::scalar_projection::render_scalar_projection_expr_plan_label,
12 plan::{
13 aggregate_shape::AggregateShape,
14 expr::{Expr, FieldId, normalize_bool_expr},
15 order_contract::DeterministicSecondaryOrderContract,
16 semantics::LogicalPushdownEligibility,
17 },
18 },
19 schema::AcceptedFieldKind,
20};
21
22#[derive(Clone, Copy, Debug, Eq, PartialEq)]
31pub enum QueryMode {
32 Load(LoadSpec),
33 Delete(DeleteSpec),
34}
35
36#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
43pub struct LoadSpec {
44 pub(in crate::db) limit: Option<u32>,
45 pub(in crate::db) offset: u32,
46}
47
48impl LoadSpec {
49 #[must_use]
51 pub const fn limit(&self) -> Option<u32> {
52 self.limit
53 }
54
55 #[must_use]
57 pub const fn offset(&self) -> u32 {
58 self.offset
59 }
60}
61
62#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
70pub struct DeleteSpec {
71 pub(in crate::db) limit: Option<u32>,
72 pub(in crate::db) offset: u32,
73}
74
75impl DeleteSpec {
76 #[must_use]
78 pub const fn limit(&self) -> Option<u32> {
79 self.limit
80 }
81
82 #[must_use]
84 pub const fn offset(&self) -> u32 {
85 self.offset
86 }
87}
88
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
94pub enum OrderDirection {
95 Asc,
96 Desc,
97}
98
99#[derive(Clone, Eq, PartialEq)]
109pub(in crate::db) struct OrderTerm {
110 pub(in crate::db) expr: Expr,
111 pub(in crate::db) direction: OrderDirection,
112}
113
114impl OrderTerm {
115 #[must_use]
117 pub(in crate::db) const fn new(expr: Expr, direction: OrderDirection) -> Self {
118 Self { expr, direction }
119 }
120
121 #[must_use]
123 pub(in crate::db) fn field(field: impl Into<String>, direction: OrderDirection) -> Self {
124 Self::new(Expr::Field(FieldId::new(field.into())), direction)
125 }
126
127 #[must_use]
129 pub(in crate::db) const fn expr(&self) -> &Expr {
130 &self.expr
131 }
132
133 #[must_use]
135 pub(in crate::db) const fn direct_field(&self) -> Option<&str> {
136 let Expr::Field(field) = &self.expr else {
137 return None;
138 };
139
140 Some(field.as_str())
141 }
142
143 #[must_use]
145 pub(in crate::db) fn rendered_label(&self) -> String {
146 render_scalar_projection_expr_plan_label(&self.expr)
147 }
148
149 #[must_use]
151 pub(in crate::db) const fn direction(&self) -> OrderDirection {
152 self.direction
153 }
154}
155
156impl std::fmt::Debug for OrderTerm {
157 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158 f.debug_struct("OrderTerm")
159 .field("label", &self.rendered_label())
160 .field("expr", &self.expr)
161 .field("direction", &self.direction)
162 .finish()
163 }
164}
165
166impl PartialEq<(String, OrderDirection)> for OrderTerm {
167 fn eq(&self, other: &(String, OrderDirection)) -> bool {
168 self.rendered_label() == other.0 && self.direction == other.1
169 }
170}
171
172impl PartialEq<OrderTerm> for (String, OrderDirection) {
173 fn eq(&self, other: &OrderTerm) -> bool {
174 self.0 == other.rendered_label() && self.1 == other.direction
175 }
176}
177
178#[must_use]
181pub(in crate::db) fn render_scalar_filter_expr_plan_label(expr: &Expr) -> String {
182 render_scalar_projection_expr_plan_label(&normalize_bool_expr(expr.clone()))
183}
184
185#[derive(Clone, Debug, Eq, PartialEq)]
192pub(in crate::db) struct OrderSpec {
193 pub(in crate::db) fields: Vec<OrderTerm>,
194}
195
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
202pub(in crate::db) struct DeleteLimitSpec {
203 pub(in crate::db) limit: Option<u32>,
204 pub(in crate::db) offset: u32,
205}
206
207#[derive(Clone, Copy, Debug, Eq, PartialEq)]
216pub(in crate::db) enum DistinctExecutionStrategy {
217 None,
218 PreOrdered,
219 HashMaterialize,
220}
221
222#[derive(Clone, Debug, Eq, PartialEq)]
231pub(in crate::db) struct PlannerRouteProfile {
232 continuation_policy: ContinuationPolicy,
233 logical_pushdown_eligibility: LogicalPushdownEligibility,
234 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
235}
236
237impl PlannerRouteProfile {
238 #[must_use]
240 pub(in crate::db) const fn new(
241 continuation_policy: ContinuationPolicy,
242 logical_pushdown_eligibility: LogicalPushdownEligibility,
243 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
244 ) -> Self {
245 Self {
246 continuation_policy,
247 logical_pushdown_eligibility,
248 secondary_order_contract,
249 }
250 }
251
252 #[must_use]
255 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
256 Self {
257 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
258 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
259 secondary_order_contract: None,
260 }
261 }
262
263 #[must_use]
265 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
266 &self.continuation_policy
267 }
268
269 #[must_use]
271 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
272 self.logical_pushdown_eligibility
273 }
274
275 #[must_use]
277 pub(in crate::db) const fn secondary_order_contract(
278 &self,
279 ) -> Option<&DeterministicSecondaryOrderContract> {
280 self.secondary_order_contract.as_ref()
281 }
282}
283
284#[derive(Clone, Copy, Debug, Eq, PartialEq)]
293pub(in crate::db) struct ContinuationPolicy {
294 requires_anchor: bool,
295 requires_strict_advance: bool,
296 is_grouped_safe: bool,
297}
298
299impl ContinuationPolicy {
300 #[must_use]
302 pub(in crate::db) const fn new(
303 requires_anchor: bool,
304 requires_strict_advance: bool,
305 is_grouped_safe: bool,
306 ) -> Self {
307 Self {
308 requires_anchor,
309 requires_strict_advance,
310 is_grouped_safe,
311 }
312 }
313
314 #[must_use]
316 pub(in crate::db) const fn requires_anchor(self) -> bool {
317 self.requires_anchor
318 }
319
320 #[must_use]
322 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
323 self.requires_strict_advance
324 }
325
326 #[must_use]
328 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
329 self.is_grouped_safe
330 }
331}
332
333#[derive(Clone, Copy, Debug, Eq, PartialEq)]
342pub(in crate::db) struct ExecutionShapeSignature {
343 continuation_signature: ContinuationSignature,
344}
345
346impl ExecutionShapeSignature {
347 #[must_use]
349 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
350 Self {
351 continuation_signature,
352 }
353 }
354
355 #[must_use]
357 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
358 self.continuation_signature
359 }
360}
361
362#[derive(Clone, Debug, Eq, PartialEq)]
368pub(in crate::db) struct PageSpec {
369 pub(in crate::db) limit: Option<u32>,
370 pub(in crate::db) offset: u32,
371}
372
373#[derive(Clone, Copy, Debug, Eq, PartialEq)]
383pub enum AggregateKind {
384 Count,
385 Sum,
386 Avg,
387 Exists,
388 Min,
389 Max,
390 First,
391 Last,
392}
393
394#[derive(Clone, Copy, Debug, Eq, PartialEq)]
404pub(in crate::db) enum GlobalDistinctAggregateKind {
405 Count,
406 Sum,
407 Avg,
408}
409
410#[derive(Clone, Copy, Debug, Eq, PartialEq)]
421pub(in crate::db) enum GroupedPlanAggregateFamily {
422 CountRowsOnly,
423 FieldTargetRows,
424 GenericRows,
425}
426
427impl GroupedPlanAggregateFamily {
428 #[must_use]
430 pub(in crate::db) const fn code(self) -> &'static str {
431 match self {
432 Self::CountRowsOnly => "count_rows_only",
433 Self::FieldTargetRows => "field_target_rows",
434 Self::GenericRows => "generic_rows",
435 }
436 }
437}
438
439impl AggregateKind {
440 #[must_use]
442 pub(in crate::db) const fn canonical_label(self) -> &'static str {
443 match self {
444 Self::Count => "COUNT",
445 Self::Sum => "SUM",
446 Self::Avg => "AVG",
447 Self::Exists => "EXISTS",
448 Self::First => "FIRST",
449 Self::Last => "LAST",
450 Self::Min => "MIN",
451 Self::Max => "MAX",
452 }
453 }
454
455 #[must_use]
457 pub(in crate::db) const fn is_count(self) -> bool {
458 matches!(self, Self::Count)
459 }
460
461 #[must_use]
463 pub(in crate::db) const fn is_sum(self) -> bool {
464 matches!(self, Self::Sum | Self::Avg)
465 }
466
467 #[must_use]
469 pub(in crate::db) const fn is_extrema(self) -> bool {
470 matches!(self, Self::Min | Self::Max)
471 }
472
473 #[must_use]
475 pub(in crate::db) const fn supports_field_target(self) -> bool {
476 matches!(
477 self,
478 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max
479 )
480 }
481
482 #[must_use]
484 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
485 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
486 }
487
488 #[must_use]
490 pub(in crate::db) const fn supports_grouped_distinct(self) -> bool {
491 matches!(self, Self::Count | Self::Sum | Self::Avg)
492 }
493
494 #[must_use]
497 pub(in crate::db::query) const fn fingerprint_tag(self) -> u8 {
498 match self {
499 Self::Count => 0x01,
500 Self::Sum => 0x02,
501 Self::Exists => 0x03,
502 Self::Min => 0x04,
503 Self::Max => 0x05,
504 Self::First => 0x06,
505 Self::Last => 0x07,
506 Self::Avg => 0x08,
507 }
508 }
509
510 #[must_use]
512 pub(in crate::db) const fn global_distinct_kind(self) -> Option<GlobalDistinctAggregateKind> {
513 match self {
514 Self::Count => Some(GlobalDistinctAggregateKind::Count),
515 Self::Sum => Some(GlobalDistinctAggregateKind::Sum),
516 Self::Avg => Some(GlobalDistinctAggregateKind::Avg),
517 Self::Exists | Self::Min | Self::Max | Self::First | Self::Last => None,
518 }
519 }
520
521 #[must_use]
523 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
524 self.global_distinct_kind().is_some()
525 }
526
527 #[must_use]
529 pub(in crate::db) const fn grouped_plan_family(
530 self,
531 has_target_field: bool,
532 ) -> GroupedPlanAggregateFamily {
533 if has_target_field && self.supports_field_target() {
534 GroupedPlanAggregateFamily::FieldTargetRows
535 } else {
536 GroupedPlanAggregateFamily::GenericRows
537 }
538 }
539
540 #[must_use]
542 pub(in crate::db) const fn supports_grouped_streaming(
543 self,
544 has_target_field: bool,
545 distinct: bool,
546 ) -> bool {
547 if self.supports_field_target() {
548 return !distinct && (self.is_count() || has_target_field);
549 }
550
551 !has_target_field && (!distinct || self.supports_grouped_distinct())
552 }
553
554 #[must_use]
556 pub(in crate::db) const fn extrema_direction(self) -> Option<Direction> {
557 match self {
558 Self::Min => Some(Direction::Asc),
559 Self::Max => Some(Direction::Desc),
560 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
561 }
562 }
563
564 #[must_use]
566 pub(in crate::db) const fn materialized_fold_direction(self) -> Direction {
567 match self {
568 Self::Min => Direction::Desc,
569 Self::Count
570 | Self::Sum
571 | Self::Avg
572 | Self::Exists
573 | Self::Max
574 | Self::First
575 | Self::Last => Direction::Asc,
576 }
577 }
578
579 #[must_use]
581 pub(in crate::db) const fn supports_bounded_probe_hint(self) -> bool {
582 !self.is_count() && !self.is_sum()
583 }
584
585 #[must_use]
587 pub(in crate::db) fn bounded_probe_fetch_hint(
588 self,
589 direction: Direction,
590 offset: usize,
591 page_limit: Option<usize>,
592 ) -> Option<usize> {
593 match self {
594 Self::Exists | Self::First => Some(offset.saturating_add(1)),
595 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
596 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
597 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
598 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
599 }
600 }
601
602 #[must_use]
604 #[cfg(feature = "sql")]
605 pub(in crate::db) const fn explain_projection_mode_label(
606 self,
607 has_projected_field: bool,
608 covering_projection: bool,
609 ) -> &'static str {
610 if has_projected_field {
611 if covering_projection {
612 "field_idx"
613 } else {
614 "field_mat"
615 }
616 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
617 "entity_term"
618 } else {
619 "scalar_agg"
620 }
621 }
622
623 #[must_use]
625 #[cfg(feature = "sql")]
626 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
627 matches!(self, Self::Count | Self::Exists)
628 }
629}
630
631#[derive(Clone, Debug)]
642pub(in crate::db) struct GroupAggregateSpec {
643 shape: AggregateShape,
644}
645
646impl GroupAggregateSpec {
647 #[must_use]
649 pub(in crate::db) const fn from_shape(shape: AggregateShape) -> Self {
650 Self { shape }
651 }
652
653 #[must_use]
655 pub(in crate::db) const fn shape(&self) -> &AggregateShape {
656 &self.shape
657 }
658}
659
660impl PartialEq for GroupAggregateSpec {
661 fn eq(&self, other: &Self) -> bool {
662 self.semantic_key() == other.semantic_key()
663 }
664}
665
666impl Eq for GroupAggregateSpec {}
667
668impl GroupedPlanAggregateFamily {
669 #[must_use]
671 pub(in crate::db) fn from_grouped_aggregates(aggregates: &[GroupAggregateSpec]) -> Self {
672 if matches!(aggregates, [aggregate] if aggregate.identity().is_count_rows_only()) {
673 return Self::CountRowsOnly;
674 }
675
676 if aggregates.iter().all(|aggregate| {
677 aggregate
678 .kind()
679 .grouped_plan_family(aggregate.target_field().is_some())
680 == Self::FieldTargetRows
681 }) {
682 return Self::FieldTargetRows;
683 }
684
685 Self::GenericRows
686 }
687}
688
689#[derive(Clone, Debug)]
699pub(in crate::db::query::plan) enum FieldSlotAuthority {
700 Unresolved,
701 Accepted(AcceptedFieldKind),
702}
703
704#[derive(Clone, Debug)]
705pub(crate) struct FieldSlot {
706 pub(in crate::db) index: usize,
707 pub(in crate::db) field: String,
708 pub(in crate::db::query::plan) authority: FieldSlotAuthority,
709}
710
711impl PartialEq for FieldSlot {
712 fn eq(&self, other: &Self) -> bool {
713 self.index == other.index && self.field == other.field
714 }
715}
716
717impl Eq for FieldSlot {}
718
719#[derive(Clone, Copy, Debug, Eq, PartialEq)]
728pub(in crate::db) struct GroupedExecutionConfig {
729 pub(in crate::db) max_groups: u64,
730 pub(in crate::db) max_group_bytes: u64,
731}
732
733#[derive(Clone, Debug, Eq, PartialEq)]
742pub(in crate::db) struct GroupSpec {
743 pub(in crate::db) group_fields: crate::db::query::plan::GroupFieldSet,
744 pub(in crate::db) aggregates: Vec<GroupAggregateSpec>,
745 pub(in crate::db) execution: GroupedExecutionConfig,
746}
747
748#[derive(Clone, Debug, Eq, PartialEq)]
769pub(in crate::db) struct ScalarPlan {
770 pub(in crate::db) mode: QueryMode,
772
773 pub(in crate::db) filter_expr: Option<Expr>,
775
776 pub(in crate::db) predicate_covers_filter_expr: bool,
778
779 pub(in crate::db) predicate: Option<Predicate>,
781
782 pub(in crate::db) order: Option<OrderSpec>,
784
785 pub(in crate::db) distinct: bool,
787
788 pub(in crate::db) delete_limit: Option<DeleteLimitSpec>,
790
791 pub(in crate::db) page: Option<PageSpec>,
793
794 pub(in crate::db) consistency: MissingRowPolicy,
796}
797
798#[derive(Clone, Debug, Eq, PartialEq)]
806pub(in crate::db) struct GroupPlan {
807 pub(in crate::db) scalar: ScalarPlan,
808 pub(in crate::db) group: GroupSpec,
809 pub(in crate::db) having_expr: Option<Expr>,
810}
811
812#[derive(Clone, Debug, Eq, PartialEq)]
822pub(in crate::db) enum LogicalPlan {
823 Scalar(ScalarPlan),
824 Grouped(GroupPlan),
825}