1use crate::{
7 db::{
8 cursor::ContinuationSignature,
9 direction::Direction,
10 predicate::{CompareOp, MissingRowPolicy, PredicateExecutionModel},
11 query::plan::{
12 order_contract::DeterministicSecondaryOrderContract,
13 semantics::LogicalPushdownEligibility,
14 },
15 },
16 value::Value,
17};
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum QueryMode {
29 Load(LoadSpec),
30 Delete(DeleteSpec),
31}
32
33#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
40pub struct LoadSpec {
41 pub(crate) limit: Option<u32>,
42 pub(crate) offset: u32,
43}
44
45impl LoadSpec {
46 #[must_use]
48 pub const fn limit(&self) -> Option<u32> {
49 self.limit
50 }
51
52 #[must_use]
54 pub const fn offset(&self) -> u32 {
55 self.offset
56 }
57}
58
59#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
67pub struct DeleteSpec {
68 pub(crate) limit: Option<u32>,
69}
70
71impl DeleteSpec {
72 #[must_use]
74 pub const fn limit(&self) -> Option<u32> {
75 self.limit
76 }
77}
78
79#[derive(Clone, Copy, Debug, Eq, PartialEq)]
84pub enum OrderDirection {
85 Asc,
86 Desc,
87}
88
89#[derive(Clone, Debug, Eq, PartialEq)]
95pub(crate) struct OrderSpec {
96 pub(crate) fields: Vec<(String, OrderDirection)>,
97}
98
99#[derive(Clone, Copy, Debug, Eq, PartialEq)]
105pub(crate) struct DeleteLimitSpec {
106 pub(crate) max_rows: u32,
107}
108
109#[derive(Clone, Copy, Debug, Eq, PartialEq)]
118pub(crate) enum DistinctExecutionStrategy {
119 None,
120 PreOrdered,
121 HashMaterialize,
122}
123
124impl DistinctExecutionStrategy {
125 #[must_use]
127 pub(crate) const fn is_enabled(self) -> bool {
128 !matches!(self, Self::None)
129 }
130}
131
132#[derive(Clone, Debug, Eq, PartialEq)]
141pub(in crate::db) struct PlannerRouteProfile {
142 continuation_policy: ContinuationPolicy,
143 logical_pushdown_eligibility: LogicalPushdownEligibility,
144 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
145}
146
147impl PlannerRouteProfile {
148 #[must_use]
150 pub(in crate::db) const fn new(
151 continuation_policy: ContinuationPolicy,
152 logical_pushdown_eligibility: LogicalPushdownEligibility,
153 secondary_order_contract: Option<DeterministicSecondaryOrderContract>,
154 ) -> Self {
155 Self {
156 continuation_policy,
157 logical_pushdown_eligibility,
158 secondary_order_contract,
159 }
160 }
161
162 #[must_use]
165 pub(in crate::db) const fn seeded_unfinalized(is_grouped: bool) -> Self {
166 Self {
167 continuation_policy: ContinuationPolicy::new(true, true, !is_grouped),
168 logical_pushdown_eligibility: LogicalPushdownEligibility::new(false, is_grouped, false),
169 secondary_order_contract: None,
170 }
171 }
172
173 #[must_use]
175 pub(in crate::db) const fn continuation_policy(&self) -> &ContinuationPolicy {
176 &self.continuation_policy
177 }
178
179 #[must_use]
181 pub(in crate::db) const fn logical_pushdown_eligibility(&self) -> LogicalPushdownEligibility {
182 self.logical_pushdown_eligibility
183 }
184
185 #[must_use]
187 pub(in crate::db) const fn secondary_order_contract(
188 &self,
189 ) -> Option<&DeterministicSecondaryOrderContract> {
190 self.secondary_order_contract.as_ref()
191 }
192}
193
194#[derive(Clone, Copy, Debug, Eq, PartialEq)]
203pub(in crate::db) struct ContinuationPolicy {
204 requires_anchor: bool,
205 requires_strict_advance: bool,
206 is_grouped_safe: bool,
207}
208
209impl ContinuationPolicy {
210 #[must_use]
212 pub(in crate::db) const fn new(
213 requires_anchor: bool,
214 requires_strict_advance: bool,
215 is_grouped_safe: bool,
216 ) -> Self {
217 Self {
218 requires_anchor,
219 requires_strict_advance,
220 is_grouped_safe,
221 }
222 }
223
224 #[must_use]
226 pub(in crate::db) const fn requires_anchor(self) -> bool {
227 self.requires_anchor
228 }
229
230 #[must_use]
232 pub(in crate::db) const fn requires_strict_advance(self) -> bool {
233 self.requires_strict_advance
234 }
235
236 #[must_use]
238 pub(in crate::db) const fn is_grouped_safe(self) -> bool {
239 self.is_grouped_safe
240 }
241}
242
243#[derive(Clone, Copy, Debug, Eq, PartialEq)]
252pub(in crate::db) struct ExecutionShapeSignature {
253 continuation_signature: ContinuationSignature,
254}
255
256impl ExecutionShapeSignature {
257 #[must_use]
259 pub(in crate::db) const fn new(continuation_signature: ContinuationSignature) -> Self {
260 Self {
261 continuation_signature,
262 }
263 }
264
265 #[must_use]
267 pub(in crate::db) const fn continuation_signature(self) -> ContinuationSignature {
268 self.continuation_signature
269 }
270}
271
272#[derive(Clone, Debug, Eq, PartialEq)]
278pub(crate) struct PageSpec {
279 pub(crate) limit: Option<u32>,
280 pub(crate) offset: u32,
281}
282
283#[derive(Clone, Copy, Debug, Eq, PartialEq)]
293pub enum AggregateKind {
294 Count,
295 Sum,
296 Avg,
297 Exists,
298 Min,
299 Max,
300 First,
301 Last,
302}
303
304impl AggregateKind {
305 #[must_use]
307 pub(in crate::db) const fn sql_label(self) -> &'static str {
308 match self {
309 Self::Count => "COUNT",
310 Self::Sum => "SUM",
311 Self::Avg => "AVG",
312 Self::Exists => "EXISTS",
313 Self::First => "FIRST",
314 Self::Last => "LAST",
315 Self::Min => "MIN",
316 Self::Max => "MAX",
317 }
318 }
319
320 #[must_use]
322 pub(crate) const fn is_count(self) -> bool {
323 matches!(self, Self::Count)
324 }
325
326 #[must_use]
328 pub(in crate::db) const fn is_sum(self) -> bool {
329 matches!(self, Self::Sum | Self::Avg)
330 }
331
332 #[must_use]
334 pub(in crate::db) const fn is_extrema(self) -> bool {
335 matches!(self, Self::Min | Self::Max)
336 }
337
338 #[must_use]
340 pub(in crate::db) const fn requires_decoded_id(self) -> bool {
341 !matches!(self, Self::Count | Self::Sum | Self::Avg | Self::Exists)
342 }
343
344 #[must_use]
346 pub(in crate::db) const fn supports_grouped_distinct_v1(self) -> bool {
347 matches!(
348 self,
349 Self::Count | Self::Min | Self::Max | Self::Sum | Self::Avg
350 )
351 }
352
353 #[must_use]
355 pub(in crate::db) const fn supports_global_distinct_without_group_keys(self) -> bool {
356 matches!(self, Self::Count | Self::Sum | Self::Avg)
357 }
358
359 #[must_use]
361 pub(crate) const fn extrema_direction(self) -> Option<Direction> {
362 match self {
363 Self::Min => Some(Direction::Asc),
364 Self::Max => Some(Direction::Desc),
365 Self::Count | Self::Sum | Self::Avg | Self::Exists | Self::First | Self::Last => None,
366 }
367 }
368
369 #[must_use]
371 pub(crate) const fn materialized_fold_direction(self) -> Direction {
372 match self {
373 Self::Min => Direction::Desc,
374 Self::Count
375 | Self::Sum
376 | Self::Avg
377 | Self::Exists
378 | Self::Max
379 | Self::First
380 | Self::Last => Direction::Asc,
381 }
382 }
383
384 #[must_use]
386 pub(crate) const fn supports_bounded_probe_hint(self) -> bool {
387 !self.is_count() && !self.is_sum()
388 }
389
390 #[must_use]
392 pub(crate) fn bounded_probe_fetch_hint(
393 self,
394 direction: Direction,
395 offset: usize,
396 page_limit: Option<usize>,
397 ) -> Option<usize> {
398 match self {
399 Self::Exists | Self::First => Some(offset.saturating_add(1)),
400 Self::Min if direction == Direction::Asc => Some(offset.saturating_add(1)),
401 Self::Max if direction == Direction::Desc => Some(offset.saturating_add(1)),
402 Self::Last => page_limit.map(|limit| offset.saturating_add(limit)),
403 Self::Count | Self::Sum | Self::Avg | Self::Min | Self::Max => None,
404 }
405 }
406
407 #[must_use]
409 pub(in crate::db) const fn explain_projection_mode_label(
410 self,
411 has_projected_field: bool,
412 covering_projection: bool,
413 ) -> &'static str {
414 if has_projected_field {
415 if covering_projection {
416 "field_idx"
417 } else {
418 "field_mat"
419 }
420 } else if matches!(self, Self::Min | Self::Max | Self::First | Self::Last) {
421 "entity_term"
422 } else {
423 "scalar_agg"
424 }
425 }
426
427 #[must_use]
429 pub(in crate::db) const fn supports_covering_existing_rows_terminal(self) -> bool {
430 matches!(self, Self::Count | Self::Exists)
431 }
432}
433
434#[derive(Clone, Debug, Eq, PartialEq)]
443pub(crate) struct GroupAggregateSpec {
444 pub(crate) kind: AggregateKind,
445 pub(crate) target_field: Option<String>,
446 pub(crate) distinct: bool,
447}
448
449#[derive(Clone, Debug, Eq, PartialEq)]
458pub(crate) struct FieldSlot {
459 pub(crate) index: usize,
460 pub(crate) field: String,
461}
462
463#[derive(Clone, Copy, Debug, Eq, PartialEq)]
472pub(crate) struct GroupedExecutionConfig {
473 pub(crate) max_groups: u64,
474 pub(crate) max_group_bytes: u64,
475}
476
477#[derive(Clone, Debug, Eq, PartialEq)]
486pub(crate) struct GroupSpec {
487 pub(crate) group_fields: Vec<FieldSlot>,
488 pub(crate) aggregates: Vec<GroupAggregateSpec>,
489 pub(crate) execution: GroupedExecutionConfig,
490}
491
492#[derive(Clone, Debug, Eq, PartialEq)]
501pub(crate) enum GroupHavingSymbol {
502 GroupField(FieldSlot),
503 AggregateIndex(usize),
504}
505
506#[derive(Clone, Debug, Eq, PartialEq)]
515pub(crate) struct GroupHavingClause {
516 pub(crate) symbol: GroupHavingSymbol,
517 pub(crate) op: CompareOp,
518 pub(crate) value: Value,
519}
520
521#[derive(Clone, Debug, Eq, PartialEq)]
530pub(crate) struct GroupHavingSpec {
531 pub(crate) clauses: Vec<GroupHavingClause>,
532}
533
534#[derive(Clone, Debug, Eq, PartialEq)]
555pub(crate) struct ScalarPlan {
556 pub(crate) mode: QueryMode,
558
559 pub(crate) predicate: Option<PredicateExecutionModel>,
561
562 pub(crate) order: Option<OrderSpec>,
564
565 pub(crate) distinct: bool,
567
568 pub(crate) delete_limit: Option<DeleteLimitSpec>,
570
571 pub(crate) page: Option<PageSpec>,
573
574 pub(crate) consistency: MissingRowPolicy,
576}
577
578#[derive(Clone, Debug, Eq, PartialEq)]
586pub(crate) struct GroupPlan {
587 pub(crate) scalar: ScalarPlan,
588 pub(crate) group: GroupSpec,
589 pub(crate) having: Option<GroupHavingSpec>,
590}
591
592#[derive(Clone, Debug, Eq, PartialEq)]
600pub(crate) enum LogicalPlan {
601 Scalar(ScalarPlan),
602 Grouped(GroupPlan),
603}