1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
11 query::plan::{
12 expr::Expr, order_contract::DeterministicSecondaryOrderContract,
13 semantics::LogicalPushdownEligibility,
14 },
15 },
16 model::field::FieldKind,
17 value::Value,
18};
19
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum QueryMode {
30 Load(LoadSpec),
31 Delete(DeleteSpec),
32}
33
34#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
41pub struct LoadSpec {
42 pub(crate) limit: Option<u32>,
43 pub(crate) offset: u32,
44}
45
46impl LoadSpec {
47 #[must_use]
49 pub const fn limit(&self) -> Option<u32> {
50 self.limit
51 }
52
53 #[must_use]
55 pub const fn offset(&self) -> u32 {
56 self.offset
57 }
58}
59
60#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
68pub struct DeleteSpec {
69 pub(crate) limit: Option<u32>,
70 pub(crate) offset: u32,
71}
72
73impl DeleteSpec {
74 #[must_use]
76 pub const fn limit(&self) -> Option<u32> {
77 self.limit
78 }
79
80 #[must_use]
82 pub const fn offset(&self) -> u32 {
83 self.offset
84 }
85}
86
87#[derive(Clone, Copy, Debug, Eq, PartialEq)]
92pub enum OrderDirection {
93 Asc,
94 Desc,
95}
96
97#[derive(Clone, Debug, Eq, PartialEq)]
103pub(crate) struct OrderSpec {
104 pub(crate) fields: Vec<(String, OrderDirection)>,
105}
106
107#[derive(Clone, Copy, Debug, Eq, PartialEq)]
113pub(crate) struct DeleteLimitSpec {
114 pub(crate) limit: Option<u32>,
115 pub(crate) offset: u32,
116}
117
118#[derive(Clone, Copy, Debug, Eq, PartialEq)]
127pub(crate) enum DistinctExecutionStrategy {
128 None,
129 PreOrdered,
130 HashMaterialize,
131}
132
133impl DistinctExecutionStrategy {
134 #[must_use]
136 pub(crate) const fn is_enabled(self) -> bool {
137 !matches!(self, Self::None)
138 }
139}
140
141#[derive(Clone, Debug, Eq, PartialEq)]
150pub(in crate::db) struct PlannerRouteProfile {
151 continuation_policy: ContinuationPolicy,
152 logical_pushdown_eligibility: LogicalPushdownEligibility,
153 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
154}
155
156impl PlannerRouteProfile {
157 #[must_use]
159 pub(in crate::db) const fn new(
160 continuation_policy: ContinuationPolicy,
161 logical_pushdown_eligibility: LogicalPushdownEligibility,
162 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
163 ) -> Self {
164 Self {
165 continuation_policy,
166 logical_pushdown_eligibility,
167 secondary_order_contract,
168 }
169 }
170
171 #[must_use]
174 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
175 Self {
176 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
177 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
178 secondary_order_contract: None,
179 }
180 }
181
182 #[must_use]
184 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
185 &self.continuation_policy
186 }
187
188 #[must_use]
190 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
191 self.logical_pushdown_eligibility
192 }
193
194 #[must_use]
196 pub(in crate::db) const fn secondary_order_contract(
197 &self,
198 ) -> Option<&DeterministicSecondaryOrderContract> {
199 self.secondary_order_contract.as_ref()
200 }
201}
202
203#[derive(Clone, Copy, Debug, Eq, PartialEq)]
212pub(in crate::db) struct ContinuationPolicy {
213 requires_anchor: bool,
214 requires_strict_advance: bool,
215 is_grouped_safe: bool,
216}
217
218impl ContinuationPolicy {
219 #[must_use]
221 pub(in crate::db) const fn new(
222 requires_anchor: bool,
223 requires_strict_advance: bool,
224 is_grouped_safe: bool,
225 ) -> Self {
226 Self {
227 requires_anchor,
228 requires_strict_advance,
229 is_grouped_safe,
230 }
231 }
232
233 #[must_use]
235 pub(in crate::db) const fn requires_anchor(self) -> bool {
236 self.requires_anchor
237 }
238
239 #[must_use]
241 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
242 self.requires_strict_advance
243 }
244
245 #[must_use]
247 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
248 self.is_grouped_safe
249 }
250}
251
252#[derive(Clone, Copy, Debug, Eq, PartialEq)]
261pub(in crate::db) struct ExecutionShapeSignature {
262 continuation_signature: ContinuationSignature,
263}
264
265impl ExecutionShapeSignature {
266 #[must_use]
268 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
269 Self {
270 continuation_signature,
271 }
272 }
273
274 #[must_use]
276 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
277 self.continuation_signature
278 }
279}
280
281#[derive(Clone, Debug, Eq, PartialEq)]
287pub(crate) struct PageSpec {
288 pub(crate) limit: Option<u32>,
289 pub(crate) offset: u32,
290}
291
292#[derive(Clone, Copy, Debug, Eq, PartialEq)]
302pub enum AggregateKind {
303 Count,
304 Sum,
305 Avg,
306 Exists,
307 Min,
308 Max,
309 First,
310 Last,
311}
312
313impl AggregateKind {
314 #[must_use]
316 pub(in crate::db) const fn sql_label(self) -> &'static str {
317 match self {
318 Self::Count => "COUNT",
319 Self::Sum => "SUM",
320 Self::Avg => "AVG",
321 Self::Exists => "EXISTS",
322 Self::First => "FIRST",
323 Self::Last => "LAST",
324 Self::Min => "MIN",
325 Self::Max => "MAX",
326 }
327 }
328
329 #[must_use]
331 pub(crate) const fn is_count(self) -> bool {
332 matches!(self, Self::Count)
333 }
334
335 #[must_use]
337 pub(in crate::db) const fn is_sum(self) -> bool {
338 matches!(self, Self::Sum | Self::Avg)
339 }
340
341 #[must_use]
343 pub(in crate::db) const fn is_extrema(self) -> bool {
344 matches!(self, Self::Min | Self::Max)
345 }
346
347 #[must_use]
349 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
350 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
351 }
352
353 #[must_use]
355 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
356 matches!(
357 self,
358 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
359 )
360 }
361
362 #[must_use]
364 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
365 matches!(self, Self::Count | Self::Sum | Self::Avg)
366 }
367
368 #[must_use]
370 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
371 match self {
372 Self::Min => Some(Direction::Asc),
373 Self::Max => Some(Direction::Desc),
374 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
375 }
376 }
377
378 #[must_use]
380 pub(crate) const fn materialized_fold_direction(self) -> Direction {
381 match self {
382 Self::Min => Direction::Desc,
383 Self::Count
384 | Self::Sum
385 | Self::Avg
386 | Self::Exists
387 | Self::Max
388 | Self::First
389 | Self::Last => Direction::Asc,
390 }
391 }
392
393 #[must_use]
395 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
396 !self.is_count() && !self.is_sum()
397 }
398
399 #[must_use]
401 pub(crate) fn bounded_probe_fetch_hint(
402 self,
403 direction: Direction,
404 offset: usize,
405 page_limit: Option<usize>,
406 ) -> Option<usize> {
407 match self {
408 Self::Exists | Self::First => Some(offset.saturating_add(1)),
409 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
410 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
411 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
412 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
413 }
414 }
415
416 #[must_use]
418 pub(in crate::db) const fn explain_projection_mode_label(
419 self,
420 has_projected_field: bool,
421 covering_projection: bool,
422 ) -> &'static str {
423 if has_projected_field {
424 if covering_projection {
425 "field_idx"
426 } else {
427 "field_mat"
428 }
429 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
430 "entity_term"
431 } else {
432 "scalar_agg"
433 }
434 }
435
436 #[must_use]
438 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
439 matches!(self, Self::Count | Self::Exists)
440 }
441}
442
443#[derive(Clone, Debug)]
454pub(crate) struct GroupAggregateSpec {
455 pub(crate) kind: AggregateKind,
456 #[cfg(test)]
457 #[allow(dead_code)]
458 #[cfg(test)]
459 pub(crate) target_field: Option<String>,
460 pub(crate) input_expr: Option<Box<Expr>>,
461 pub(crate) distinct: bool,
462}
463
464impl PartialEq for GroupAggregateSpec {
465 fn eq(&self, other: &Self) -> bool {
466 self.kind == other.kind
467 && self.input_expr == other.input_expr
468 && self.distinct == other.distinct
469 }
470}
471
472impl Eq for GroupAggregateSpec {}
473
474#[derive(Clone, Debug)]
485pub(crate) struct FieldSlot {
486 pub(crate) index: usize,
487 pub(crate) field: String,
488 pub(crate) kind: Option<FieldKind>,
489}
490
491impl PartialEq for FieldSlot {
492 fn eq(&self, other: &Self) -> bool {
493 self.index == other.index && self.field == other.field
494 }
495}
496
497impl Eq for FieldSlot {}
498
499#[derive(Clone, Copy, Debug, Eq, PartialEq)]
508pub(crate) struct GroupedExecutionConfig {
509 pub(crate) max_groups: u64,
510 pub(crate) max_group_bytes: u64,
511}
512
513#[derive(Clone, Debug, Eq, PartialEq)]
522pub(crate) struct GroupSpec {
523 pub(crate) group_fields: Vec<FieldSlot>,
524 pub(crate) aggregates: Vec<GroupAggregateSpec>,
525 pub(crate) execution: GroupedExecutionConfig,
526}
527
528#[derive(Clone, Debug, Eq, PartialEq)]
537pub(crate) enum GroupHavingSymbol {
538 GroupField(FieldSlot),
539 AggregateIndex(usize),
540}
541
542#[derive(Clone, Debug, Eq, PartialEq)]
551pub(crate) struct GroupHavingClause {
552 pub(crate) symbol: GroupHavingSymbol,
553 pub(crate) op: CompareOp,
554 pub(crate) value: Value,
555}
556
557#[derive(Clone, Debug, Eq, PartialEq)]
578pub(crate) struct ScalarPlan {
579 pub(crate) mode: QueryMode,
581
582 pub(crate) predicate: Option<PredicateExecutionModel>,
584
585 pub(crate) order: Option<OrderSpec>,
587
588 pub(crate) distinct: bool,
590
591 pub(crate) delete_limit: Option<DeleteLimitSpec>,
593
594 pub(crate) page: Option<PageSpec>,
596
597 pub(crate) consistency: MissingRowPolicy,
599}
600
601#[derive(Clone, Debug, Eq, PartialEq)]
609pub(crate) struct GroupPlan {
610 pub(crate) scalar: ScalarPlan,
611 pub(crate) group: GroupSpec,
612 pub(crate) having_expr: Option<Expr>,
613}
614
615#[derive(Clone, Debug, Eq, PartialEq)]
625pub(crate) enum LogicalPlan {
626 Scalar(ScalarPlan),
627 Grouped(GroupPlan),
628}