1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{MissingRowPolicy, PredicateExecutionModel},
11 query::{
12 builder::scalar_projection::render_scalar_projection_expr_sql_label,
13 plan::{
14 expr::{Expr, FieldId},
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#[derive(Clone, Debug, Eq, PartialEq)]
186pub(crate) struct OrderSpec {
187 pub(crate) fields: Vec<OrderTerm>,
188}
189
190#[derive(Clone, Copy, Debug, Eq, PartialEq)]
196pub(crate) struct DeleteLimitSpec {
197 pub(crate) limit: Option<u32>,
198 pub(crate) offset: u32,
199}
200
201#[derive(Clone, Copy, Debug, Eq, PartialEq)]
210pub(crate) enum DistinctExecutionStrategy {
211 None,
212 PreOrdered,
213 HashMaterialize,
214}
215
216impl DistinctExecutionStrategy {
217 #[must_use]
219 pub(crate) const fn is_enabled(self) -> bool {
220 !matches!(self, Self::None)
221 }
222}
223
224#[derive(Clone, Debug, Eq, PartialEq)]
233pub(in crate::db) struct PlannerRouteProfile {
234 continuation_policy: ContinuationPolicy,
235 logical_pushdown_eligibility: LogicalPushdownEligibility,
236 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
237}
238
239impl PlannerRouteProfile {
240 #[must_use]
242 pub(in crate::db) const fn new(
243 continuation_policy: ContinuationPolicy,
244 logical_pushdown_eligibility: LogicalPushdownEligibility,
245 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
246 ) -> Self {
247 Self {
248 continuation_policy,
249 logical_pushdown_eligibility,
250 secondary_order_contract,
251 }
252 }
253
254 #[must_use]
257 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
258 Self {
259 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
260 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
261 secondary_order_contract: None,
262 }
263 }
264
265 #[must_use]
267 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
268 &self.continuation_policy
269 }
270
271 #[must_use]
273 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
274 self.logical_pushdown_eligibility
275 }
276
277 #[must_use]
279 pub(in crate::db) const fn secondary_order_contract(
280 &self,
281 ) -> Option<&DeterministicSecondaryOrderContract> {
282 self.secondary_order_contract.as_ref()
283 }
284}
285
286#[derive(Clone, Copy, Debug, Eq, PartialEq)]
295pub(in crate::db) struct ContinuationPolicy {
296 requires_anchor: bool,
297 requires_strict_advance: bool,
298 is_grouped_safe: bool,
299}
300
301impl ContinuationPolicy {
302 #[must_use]
304 pub(in crate::db) const fn new(
305 requires_anchor: bool,
306 requires_strict_advance: bool,
307 is_grouped_safe: bool,
308 ) -> Self {
309 Self {
310 requires_anchor,
311 requires_strict_advance,
312 is_grouped_safe,
313 }
314 }
315
316 #[must_use]
318 pub(in crate::db) const fn requires_anchor(self) -> bool {
319 self.requires_anchor
320 }
321
322 #[must_use]
324 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
325 self.requires_strict_advance
326 }
327
328 #[must_use]
330 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
331 self.is_grouped_safe
332 }
333}
334
335#[derive(Clone, Copy, Debug, Eq, PartialEq)]
344pub(in crate::db) struct ExecutionShapeSignature {
345 continuation_signature: ContinuationSignature,
346}
347
348impl ExecutionShapeSignature {
349 #[must_use]
351 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
352 Self {
353 continuation_signature,
354 }
355 }
356
357 #[must_use]
359 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
360 self.continuation_signature
361 }
362}
363
364#[derive(Clone, Debug, Eq, PartialEq)]
370pub(crate) struct PageSpec {
371 pub(crate) limit: Option<u32>,
372 pub(crate) offset: u32,
373}
374
375#[derive(Clone, Copy, Debug, Eq, PartialEq)]
385pub enum AggregateKind {
386 Count,
387 Sum,
388 Avg,
389 Exists,
390 Min,
391 Max,
392 First,
393 Last,
394}
395
396impl AggregateKind {
397 #[must_use]
399 pub(in crate::db) const fn sql_label(self) -> &'static str {
400 match self {
401 Self::Count => "COUNT",
402 Self::Sum => "SUM",
403 Self::Avg => "AVG",
404 Self::Exists => "EXISTS",
405 Self::First => "FIRST",
406 Self::Last => "LAST",
407 Self::Min => "MIN",
408 Self::Max => "MAX",
409 }
410 }
411
412 #[must_use]
414 pub(crate) const fn is_count(self) -> bool {
415 matches!(self, Self::Count)
416 }
417
418 #[must_use]
420 pub(in crate::db) const fn is_sum(self) -> bool {
421 matches!(self, Self::Sum | Self::Avg)
422 }
423
424 #[must_use]
426 pub(in crate::db) const fn is_extrema(self) -> bool {
427 matches!(self, Self::Min | Self::Max)
428 }
429
430 #[must_use]
432 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
433 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
434 }
435
436 #[must_use]
438 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
439 matches!(
440 self,
441 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
442 )
443 }
444
445 #[must_use]
447 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
448 matches!(self, Self::Count | Self::Sum | Self::Avg)
449 }
450
451 #[must_use]
453 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
454 match self {
455 Self::Min => Some(Direction::Asc),
456 Self::Max => Some(Direction::Desc),
457 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
458 }
459 }
460
461 #[must_use]
463 pub(crate) const fn materialized_fold_direction(self) -> Direction {
464 match self {
465 Self::Min => Direction::Desc,
466 Self::Count
467 | Self::Sum
468 | Self::Avg
469 | Self::Exists
470 | Self::Max
471 | Self::First
472 | Self::Last => Direction::Asc,
473 }
474 }
475
476 #[must_use]
478 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
479 !self.is_count() && !self.is_sum()
480 }
481
482 #[must_use]
484 pub(crate) fn bounded_probe_fetch_hint(
485 self,
486 direction: Direction,
487 offset: usize,
488 page_limit: Option<usize>,
489 ) -> Option<usize> {
490 match self {
491 Self::Exists | Self::First => Some(offset.saturating_add(1)),
492 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
493 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
494 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
495 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
496 }
497 }
498
499 #[must_use]
501 pub(in crate::db) const fn explain_projection_mode_label(
502 self,
503 has_projected_field: bool,
504 covering_projection: bool,
505 ) -> &'static str {
506 if has_projected_field {
507 if covering_projection {
508 "field_idx"
509 } else {
510 "field_mat"
511 }
512 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
513 "entity_term"
514 } else {
515 "scalar_agg"
516 }
517 }
518
519 #[must_use]
521 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
522 matches!(self, Self::Count | Self::Exists)
523 }
524}
525
526#[derive(Clone, Debug)]
537pub(crate) struct GroupAggregateSpec {
538 pub(crate) kind: AggregateKind,
539 #[cfg(test)]
540 #[allow(dead_code)]
541 #[cfg(test)]
542 pub(crate) target_field: Option<String>,
543 pub(crate) input_expr: Option<Box<Expr>>,
544 pub(crate) filter_expr: Option<Box<Expr>>,
545 pub(crate) distinct: bool,
546}
547
548impl PartialEq for GroupAggregateSpec {
549 fn eq(&self, other: &Self) -> bool {
550 self.kind == other.kind
551 && self.input_expr == other.input_expr
552 && self.filter_expr == other.filter_expr
553 && self.distinct == other.distinct
554 }
555}
556
557impl Eq for GroupAggregateSpec {}
558
559#[derive(Clone, Debug)]
570pub(crate) struct FieldSlot {
571 pub(crate) index: usize,
572 pub(crate) field: String,
573 pub(crate) kind: Option<FieldKind>,
574}
575
576impl PartialEq for FieldSlot {
577 fn eq(&self, other: &Self) -> bool {
578 self.index == other.index && self.field == other.field
579 }
580}
581
582impl Eq for FieldSlot {}
583
584#[derive(Clone, Copy, Debug, Eq, PartialEq)]
593pub(crate) struct GroupedExecutionConfig {
594 pub(crate) max_groups: u64,
595 pub(crate) max_group_bytes: u64,
596}
597
598#[derive(Clone, Debug, Eq, PartialEq)]
607pub(crate) struct GroupSpec {
608 pub(crate) group_fields: Vec<FieldSlot>,
609 pub(crate) aggregates: Vec<GroupAggregateSpec>,
610 pub(crate) execution: GroupedExecutionConfig,
611}
612
613#[derive(Clone, Debug, Eq, PartialEq)]
634pub(crate) struct ScalarPlan {
635 pub(crate) mode: QueryMode,
637
638 pub(crate) predicate: Option<PredicateExecutionModel>,
640
641 pub(crate) order: Option<OrderSpec>,
643
644 pub(crate) distinct: bool,
646
647 pub(crate) delete_limit: Option<DeleteLimitSpec>,
649
650 pub(crate) page: Option<PageSpec>,
652
653 pub(crate) consistency: MissingRowPolicy,
655}
656
657#[derive(Clone, Debug, Eq, PartialEq)]
665pub(crate) struct GroupPlan {
666 pub(crate) scalar: ScalarPlan,
667 pub(crate) group: GroupSpec,
668 pub(crate) having_expr: Option<Expr>,
669}
670
671#[derive(Clone, Debug, Eq, PartialEq)]
681pub(crate) enum LogicalPlan {
682 Scalar(ScalarPlan),
683 Grouped(GroupPlan),
684}