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
222impl DistinctExecutionStrategy {
223 #[must_use]
225 pub(in crate::db) const fn is_enabled(self) -> bool {
226 !matches!(self, Self::None)
227 }
228}
229
230#[derive(Clone, Debug, Eq, PartialEq)]
239pub(in crate::db) struct PlannerRouteProfile {
240 continuation_policy: ContinuationPolicy,
241 logical_pushdown_eligibility: LogicalPushdownEligibility,
242 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
243}
244
245impl PlannerRouteProfile {
246 #[must_use]
248 pub(in crate::db) const fn new(
249 continuation_policy: ContinuationPolicy,
250 logical_pushdown_eligibility: LogicalPushdownEligibility,
251 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
252 ) -> Self {
253 Self {
254 continuation_policy,
255 logical_pushdown_eligibility,
256 secondary_order_contract,
257 }
258 }
259
260 #[must_use]
263 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
264 Self {
265 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
266 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
267 secondary_order_contract: None,
268 }
269 }
270
271 #[must_use]
273 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
274 &self.continuation_policy
275 }
276
277 #[must_use]
279 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
280 self.logical_pushdown_eligibility
281 }
282
283 #[must_use]
285 pub(in crate::db) const fn secondary_order_contract(
286 &self,
287 ) -> Option<&DeterministicSecondaryOrderContract> {
288 self.secondary_order_contract.as_ref()
289 }
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
301pub(in crate::db) struct ContinuationPolicy {
302 requires_anchor: bool,
303 requires_strict_advance: bool,
304 is_grouped_safe: bool,
305}
306
307impl ContinuationPolicy {
308 #[must_use]
310 pub(in crate::db) const fn new(
311 requires_anchor: bool,
312 requires_strict_advance: bool,
313 is_grouped_safe: bool,
314 ) -> Self {
315 Self {
316 requires_anchor,
317 requires_strict_advance,
318 is_grouped_safe,
319 }
320 }
321
322 #[must_use]
324 pub(in crate::db) const fn requires_anchor(self) -> bool {
325 self.requires_anchor
326 }
327
328 #[must_use]
330 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
331 self.requires_strict_advance
332 }
333
334 #[must_use]
336 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
337 self.is_grouped_safe
338 }
339}
340
341#[derive(Clone, Copy, Debug, Eq, PartialEq)]
350pub(in crate::db) struct ExecutionShapeSignature {
351 continuation_signature: ContinuationSignature,
352}
353
354impl ExecutionShapeSignature {
355 #[must_use]
357 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
358 Self {
359 continuation_signature,
360 }
361 }
362
363 #[must_use]
365 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
366 self.continuation_signature
367 }
368}
369
370#[derive(Clone, Debug, Eq, PartialEq)]
376pub(in crate::db) struct PageSpec {
377 pub(in crate::db) limit: Option<u32>,
378 pub(in crate::db) offset: u32,
379}
380
381#[derive(Clone, Copy, Debug, Eq, PartialEq)]
391pub enum AggregateKind {
392 Count,
393 Sum,
394 Avg,
395 Exists,
396 Min,
397 Max,
398 First,
399 Last,
400}
401
402#[derive(Clone, Copy, Debug, Eq, PartialEq)]
412pub(in crate::db) enum GlobalDistinctAggregateKind {
413 Count,
414 Sum,
415 Avg,
416}
417
418#[derive(Clone, Copy, Debug, Eq, PartialEq)]
429pub(in crate::db) enum GroupedPlanAggregateFamily {
430 CountRowsOnly,
431 FieldTargetRows,
432 GenericRows,
433}
434
435impl GroupedPlanAggregateFamily {
436 #[must_use]
438 pub(in crate::db) const fn code(self) -> &'static str {
439 match self {
440 Self::CountRowsOnly => "count_rows_only",
441 Self::FieldTargetRows => "field_target_rows",
442 Self::GenericRows => "generic_rows",
443 }
444 }
445}
446
447impl AggregateKind {
448 #[must_use]
450 pub(in crate::db) const fn canonical_label(self) -> &'static str {
451 match self {
452 Self::Count => "COUNT",
453 Self::Sum => "SUM",
454 Self::Avg => "AVG",
455 Self::Exists => "EXISTS",
456 Self::First => "FIRST",
457 Self::Last => "LAST",
458 Self::Min => "MIN",
459 Self::Max => "MAX",
460 }
461 }
462
463 #[must_use]
465 pub(in crate::db) const fn is_count(self) -> bool {
466 matches!(self, Self::Count)
467 }
468
469 #[must_use]
471 pub(in crate::db) const fn is_sum(self) -> bool {
472 matches!(self, Self::Sum | Self::Avg)
473 }
474
475 #[must_use]
477 pub(in crate::db) const fn is_extrema(self) -> bool {
478 matches!(self, Self::Min | Self::Max)
479 }
480
481 #[must_use]
483 pub(in crate::db) const fn supports_field_target(self) -> bool {
484 matches!(
485 self,
486 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max
487 )
488 }
489
490 #[must_use]
492 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
493 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
494 }
495
496 #[must_use]
498 pub(in crate::db) const fn supports_grouped_distinct(self) -> bool {
499 matches!(self, Self::Count | Self::Sum | Self::Avg)
500 }
501
502 #[must_use]
505 pub(in crate::db::query) const fn fingerprint_tag(self) -> u8 {
506 match self {
507 Self::Count => 0x01,
508 Self::Sum => 0x02,
509 Self::Exists => 0x03,
510 Self::Min => 0x04,
511 Self::Max => 0x05,
512 Self::First => 0x06,
513 Self::Last => 0x07,
514 Self::Avg => 0x08,
515 }
516 }
517
518 #[must_use]
520 pub(in crate::db) const fn global_distinct_kind(self) -> Option<GlobalDistinctAggregateKind> {
521 match self {
522 Self::Count => Some(GlobalDistinctAggregateKind::Count),
523 Self::Sum => Some(GlobalDistinctAggregateKind::Sum),
524 Self::Avg => Some(GlobalDistinctAggregateKind::Avg),
525 Self::Exists | Self::Min | Self::Max | Self::First | Self::Last => None,
526 }
527 }
528
529 #[must_use]
531 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
532 self.global_distinct_kind().is_some()
533 }
534
535 #[must_use]
537 pub(in crate::db) const fn grouped_plan_family(
538 self,
539 has_target_field: bool,
540 ) -> GroupedPlanAggregateFamily {
541 if has_target_field && self.supports_field_target() {
542 GroupedPlanAggregateFamily::FieldTargetRows
543 } else {
544 GroupedPlanAggregateFamily::GenericRows
545 }
546 }
547
548 #[must_use]
550 pub(in crate::db) const fn supports_grouped_streaming(
551 self,
552 has_target_field: bool,
553 distinct: bool,
554 ) -> bool {
555 if self.supports_field_target() {
556 return !distinct && (self.is_count() || has_target_field);
557 }
558
559 !has_target_field && (!distinct || self.supports_grouped_distinct())
560 }
561
562 #[must_use]
564 pub(in crate::db) const fn extrema_direction(self) -> Option<Direction> {
565 match self {
566 Self::Min => Some(Direction::Asc),
567 Self::Max => Some(Direction::Desc),
568 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
569 }
570 }
571
572 #[must_use]
574 pub(in crate::db) const fn materialized_fold_direction(self) -> Direction {
575 match self {
576 Self::Min => Direction::Desc,
577 Self::Count
578 | Self::Sum
579 | Self::Avg
580 | Self::Exists
581 | Self::Max
582 | Self::First
583 | Self::Last => Direction::Asc,
584 }
585 }
586
587 #[must_use]
589 pub(in crate::db) const fn supports_bounded_probe_hint(self) -> bool {
590 !self.is_count() && !self.is_sum()
591 }
592
593 #[must_use]
595 pub(in crate::db) fn bounded_probe_fetch_hint(
596 self,
597 direction: Direction,
598 offset: usize,
599 page_limit: Option<usize>,
600 ) -> Option<usize> {
601 match self {
602 Self::Exists | Self::First => Some(offset.saturating_add(1)),
603 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
604 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
605 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
606 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
607 }
608 }
609
610 #[must_use]
612 #[cfg(feature = "sql")]
613 pub(in crate::db) const fn explain_projection_mode_label(
614 self,
615 has_projected_field: bool,
616 covering_projection: bool,
617 ) -> &'static str {
618 if has_projected_field {
619 if covering_projection {
620 "field_idx"
621 } else {
622 "field_mat"
623 }
624 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
625 "entity_term"
626 } else {
627 "scalar_agg"
628 }
629 }
630
631 #[must_use]
633 #[cfg(feature = "sql")]
634 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
635 matches!(self, Self::Count | Self::Exists)
636 }
637}
638
639#[derive(Clone, Debug)]
650pub(in crate::db) struct GroupAggregateSpec {
651 shape: AggregateShape,
652}
653
654impl GroupAggregateSpec {
655 #[must_use]
657 pub(in crate::db) const fn from_shape(shape: AggregateShape) -> Self {
658 Self { shape }
659 }
660
661 #[must_use]
663 pub(in crate::db) const fn shape(&self) -> &AggregateShape {
664 &self.shape
665 }
666}
667
668impl PartialEq for GroupAggregateSpec {
669 fn eq(&self, other: &Self) -> bool {
670 self.semantic_key() == other.semantic_key()
671 }
672}
673
674impl Eq for GroupAggregateSpec {}
675
676impl GroupedPlanAggregateFamily {
677 #[must_use]
679 pub(in crate::db) fn from_grouped_aggregates(aggregates: &[GroupAggregateSpec]) -> Self {
680 if matches!(aggregates, [aggregate] if aggregate.identity().is_count_rows_only()) {
681 return Self::CountRowsOnly;
682 }
683
684 if aggregates.iter().all(|aggregate| {
685 aggregate
686 .kind()
687 .grouped_plan_family(aggregate.target_field().is_some())
688 == Self::FieldTargetRows
689 }) {
690 return Self::FieldTargetRows;
691 }
692
693 Self::GenericRows
694 }
695}
696
697#[derive(Clone, Debug)]
707pub(in crate::db::query::plan) enum FieldSlotAuthority {
708 Unresolved,
709 Accepted(AcceptedFieldKind),
710}
711
712#[derive(Clone, Debug)]
713pub(crate) struct FieldSlot {
714 pub(in crate::db) index: usize,
715 pub(in crate::db) field: String,
716 pub(in crate::db::query::plan) authority: FieldSlotAuthority,
717}
718
719impl PartialEq for FieldSlot {
720 fn eq(&self, other: &Self) -> bool {
721 self.index == other.index && self.field == other.field
722 }
723}
724
725impl Eq for FieldSlot {}
726
727#[derive(Clone, Copy, Debug, Eq, PartialEq)]
736pub(in crate::db) struct GroupedExecutionConfig {
737 pub(in crate::db) max_groups: u64,
738 pub(in crate::db) max_group_bytes: u64,
739}
740
741#[derive(Clone, Debug, Eq, PartialEq)]
750pub(in crate::db) struct GroupSpec {
751 pub(in crate::db) group_fields: Vec<FieldSlot>,
752 pub(in crate::db) aggregates: Vec<GroupAggregateSpec>,
753 pub(in crate::db) execution: GroupedExecutionConfig,
754}
755
756#[derive(Clone, Debug, Eq, PartialEq)]
777pub(in crate::db) struct ScalarPlan {
778 pub(in crate::db) mode: QueryMode,
780
781 pub(in crate::db) filter_expr: Option<Expr>,
783
784 pub(in crate::db) predicate_covers_filter_expr: bool,
786
787 pub(in crate::db) predicate: Option<Predicate>,
789
790 pub(in crate::db) order: Option<OrderSpec>,
792
793 pub(in crate::db) distinct: bool,
795
796 pub(in crate::db) delete_limit: Option<DeleteLimitSpec>,
798
799 pub(in crate::db) page: Option<PageSpec>,
801
802 pub(in crate::db) consistency: MissingRowPolicy,
804}
805
806#[derive(Clone, Debug, Eq, PartialEq)]
814pub(in crate::db) struct GroupPlan {
815 pub(in crate::db) scalar: ScalarPlan,
816 pub(in crate::db) group: GroupSpec,
817 pub(in crate::db) having_expr: Option<Expr>,
818}
819
820#[derive(Clone, Debug, Eq, PartialEq)]
830pub(in crate::db) enum LogicalPlan {
831 Scalar(ScalarPlan),
832 Grouped(GroupPlan),
833}