1use crate::{
7 db::query::{
8 explain::{ExplainAccessPath, ExplainPlan, ExplainPredicate},
9 plan::AggregateKind,
10 },
11 value::Value,
12};
13use std::fmt::{self, Debug};
14
15#[derive(Clone, Default, Eq, PartialEq)]
24pub struct ExplainPropertyMap {
25 entries: Vec<(&'static str, Value)>,
26}
27
28impl ExplainPropertyMap {
29 #[must_use]
31 pub const fn new() -> Self {
32 Self {
33 entries: Vec::new(),
34 }
35 }
36
37 pub fn insert(&mut self, key: &'static str, value: Value) -> Option<Value> {
39 match self
40 .entries
41 .binary_search_by_key(&key, |(existing_key, _)| *existing_key)
42 {
43 Ok(index) => Some(std::mem::replace(&mut self.entries[index].1, value)),
44 Err(index) => {
45 self.entries.insert(index, (key, value));
46 None
47 }
48 }
49 }
50
51 #[must_use]
53 pub fn get(&self, key: &str) -> Option<&Value> {
54 self.entries
55 .binary_search_by_key(&key, |(existing_key, _)| *existing_key)
56 .ok()
57 .map(|index| &self.entries[index].1)
58 }
59
60 #[must_use]
62 pub fn contains_key(&self, key: &str) -> bool {
63 self.get(key).is_some()
64 }
65
66 #[must_use]
68 pub const fn is_empty(&self) -> bool {
69 self.entries.is_empty()
70 }
71
72 pub fn iter(&self) -> impl Iterator<Item = (&'static str, &Value)> {
74 self.entries.iter().map(|(key, value)| (*key, value))
75 }
76}
77
78impl Debug for ExplainPropertyMap {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 let mut map = f.debug_map();
81 for (key, value) in self.iter() {
82 map.entry(&key, value);
83 }
84 map.finish()
85 }
86}
87
88#[derive(Clone, Copy, Debug, Eq, PartialEq)]
96pub enum ExplainAggregateTerminalRoute {
97 Standard,
98 IndexSeekFirst { fetch: usize },
99 IndexSeekLast { fetch: usize },
100}
101
102#[derive(Clone, Debug, Eq, PartialEq)]
110pub struct ExplainAggregateTerminalPlan {
111 pub(crate) query: ExplainPlan,
112 pub(crate) terminal: AggregateKind,
113 pub(crate) route: ExplainAggregateTerminalRoute,
114 pub(crate) execution: ExplainExecutionDescriptor,
115}
116
117#[derive(Clone, Copy, Debug, Eq, PartialEq)]
125pub enum ExplainExecutionOrderingSource {
126 AccessOrder,
127 Materialized,
128 IndexSeekFirst { fetch: usize },
129 IndexSeekLast { fetch: usize },
130}
131
132#[derive(Clone, Copy, Debug, Eq, PartialEq)]
139pub enum ExplainExecutionMode {
140 Streaming,
141 Materialized,
142}
143
144#[derive(Clone, Debug, Eq, PartialEq)]
153pub struct ExplainExecutionDescriptor {
154 pub(crate) access_strategy: ExplainAccessPath,
155 pub(crate) covering_projection: bool,
156 pub(crate) aggregation: AggregateKind,
157 pub(crate) execution_mode: ExplainExecutionMode,
158 pub(crate) ordering_source: ExplainExecutionOrderingSource,
159 pub(crate) limit: Option<u32>,
160 pub(crate) cursor: bool,
161 pub(crate) node_properties: ExplainPropertyMap,
162}
163
164#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub enum ExplainExecutionNodeType {
172 ByKeyLookup,
173 ByKeysLookup,
174 PrimaryKeyRangeScan,
175 IndexPrefixScan,
176 IndexRangeScan,
177 IndexMultiLookup,
178 FullScan,
179 Union,
180 Intersection,
181 IndexPredicatePrefilter,
182 ResidualPredicateFilter,
183 OrderByAccessSatisfied,
184 OrderByMaterializedSort,
185 DistinctPreOrdered,
186 DistinctMaterialized,
187 ProjectionMaterialized,
188 ProjectionIndexOnly,
189 LimitOffset,
190 CursorResume,
191 IndexRangeLimitPushdown,
192 TopNSeek,
193 AggregateCount,
194 AggregateExists,
195 AggregateMin,
196 AggregateMax,
197 AggregateFirst,
198 AggregateLast,
199 AggregateSum,
200 AggregateSeekFirst,
201 AggregateSeekLast,
202 GroupedAggregateHashMaterialized,
203 GroupedAggregateOrderedMaterialized,
204 SecondaryOrderPushdown,
205}
206
207#[derive(Clone, Debug, Eq, PartialEq)]
215pub struct ExplainExecutionNodeDescriptor {
216 pub(crate) node_type: ExplainExecutionNodeType,
217 pub(crate) execution_mode: ExplainExecutionMode,
218 pub(crate) access_strategy: Option<ExplainAccessPath>,
219 pub(crate) predicate_pushdown: Option<String>,
220 pub(crate) residual_predicate: Option<ExplainPredicate>,
221 pub(crate) projection: Option<String>,
222 pub(crate) ordering_source: Option<ExplainExecutionOrderingSource>,
223 pub(crate) limit: Option<u32>,
224 pub(crate) cursor: Option<bool>,
225 pub(crate) covering_scan: Option<bool>,
226 pub(crate) rows_expected: Option<u64>,
227 pub(crate) children: Vec<Self>,
228 pub(crate) node_properties: ExplainPropertyMap,
229}
230
231impl ExplainAggregateTerminalPlan {
232 #[must_use]
234 pub const fn query(&self) -> &ExplainPlan {
235 &self.query
236 }
237
238 #[must_use]
240 pub const fn terminal(&self) -> AggregateKind {
241 self.terminal
242 }
243
244 #[must_use]
246 pub const fn route(&self) -> ExplainAggregateTerminalRoute {
247 self.route
248 }
249
250 #[must_use]
252 pub const fn execution(&self) -> &ExplainExecutionDescriptor {
253 &self.execution
254 }
255
256 #[must_use]
257 pub(in crate::db) const fn new(
258 query: ExplainPlan,
259 terminal: AggregateKind,
260 execution: ExplainExecutionDescriptor,
261 ) -> Self {
262 let route = execution.route();
263
264 Self {
265 query,
266 terminal,
267 route,
268 execution,
269 }
270 }
271}
272
273impl ExplainExecutionDescriptor {
274 #[must_use]
276 pub const fn access_strategy(&self) -> &ExplainAccessPath {
277 &self.access_strategy
278 }
279
280 #[must_use]
282 pub const fn covering_projection(&self) -> bool {
283 self.covering_projection
284 }
285
286 #[must_use]
288 pub const fn aggregation(&self) -> AggregateKind {
289 self.aggregation
290 }
291
292 #[must_use]
294 pub const fn execution_mode(&self) -> ExplainExecutionMode {
295 self.execution_mode
296 }
297
298 #[must_use]
300 pub const fn ordering_source(&self) -> ExplainExecutionOrderingSource {
301 self.ordering_source
302 }
303
304 #[must_use]
306 pub const fn limit(&self) -> Option<u32> {
307 self.limit
308 }
309
310 #[must_use]
312 pub const fn cursor(&self) -> bool {
313 self.cursor
314 }
315
316 #[must_use]
318 pub const fn node_properties(&self) -> &ExplainPropertyMap {
319 &self.node_properties
320 }
321
322 #[must_use]
323 pub(in crate::db) const fn route(&self) -> ExplainAggregateTerminalRoute {
324 match self.ordering_source {
325 ExplainExecutionOrderingSource::IndexSeekFirst { fetch } => {
326 ExplainAggregateTerminalRoute::IndexSeekFirst { fetch }
327 }
328 ExplainExecutionOrderingSource::IndexSeekLast { fetch } => {
329 ExplainAggregateTerminalRoute::IndexSeekLast { fetch }
330 }
331 ExplainExecutionOrderingSource::AccessOrder
332 | ExplainExecutionOrderingSource::Materialized => {
333 ExplainAggregateTerminalRoute::Standard
334 }
335 }
336 }
337}
338
339impl ExplainAggregateTerminalPlan {
340 #[must_use]
342 pub fn execution_node_descriptor(&self) -> ExplainExecutionNodeDescriptor {
343 ExplainExecutionNodeDescriptor {
344 node_type: aggregate_execution_node_type(self.terminal, self.execution.ordering_source),
345 execution_mode: self.execution.execution_mode,
346 access_strategy: Some(self.execution.access_strategy.clone()),
347 predicate_pushdown: None,
348 residual_predicate: None,
349 projection: None,
350 ordering_source: Some(self.execution.ordering_source),
351 limit: self.execution.limit,
352 cursor: Some(self.execution.cursor),
353 covering_scan: Some(self.execution.covering_projection),
354 rows_expected: None,
355 children: Vec::new(),
356 node_properties: self.execution.node_properties.clone(),
357 }
358 }
359}
360
361const fn aggregate_execution_node_type(
362 terminal: AggregateKind,
363 ordering_source: ExplainExecutionOrderingSource,
364) -> ExplainExecutionNodeType {
365 match ordering_source {
366 ExplainExecutionOrderingSource::IndexSeekFirst { .. } => {
367 ExplainExecutionNodeType::AggregateSeekFirst
368 }
369 ExplainExecutionOrderingSource::IndexSeekLast { .. } => {
370 ExplainExecutionNodeType::AggregateSeekLast
371 }
372 ExplainExecutionOrderingSource::AccessOrder
373 | ExplainExecutionOrderingSource::Materialized => match terminal {
374 AggregateKind::Count => ExplainExecutionNodeType::AggregateCount,
375 AggregateKind::Exists => ExplainExecutionNodeType::AggregateExists,
376 AggregateKind::Min => ExplainExecutionNodeType::AggregateMin,
377 AggregateKind::Max => ExplainExecutionNodeType::AggregateMax,
378 AggregateKind::First => ExplainExecutionNodeType::AggregateFirst,
379 AggregateKind::Last => ExplainExecutionNodeType::AggregateLast,
380 AggregateKind::Sum | AggregateKind::Avg => ExplainExecutionNodeType::AggregateSum,
381 },
382 }
383}
384
385impl ExplainExecutionNodeType {
386 #[must_use]
388 pub const fn as_str(self) -> &'static str {
389 match self {
390 Self::ByKeyLookup => "ByKeyLookup",
391 Self::ByKeysLookup => "ByKeysLookup",
392 Self::PrimaryKeyRangeScan => "PrimaryKeyRangeScan",
393 Self::IndexPrefixScan => "IndexPrefixScan",
394 Self::IndexRangeScan => "IndexRangeScan",
395 Self::IndexMultiLookup => "IndexMultiLookup",
396 Self::FullScan => "FullScan",
397 Self::Union => "Union",
398 Self::Intersection => "Intersection",
399 Self::IndexPredicatePrefilter => "IndexPredicatePrefilter",
400 Self::ResidualPredicateFilter => "ResidualPredicateFilter",
401 Self::OrderByAccessSatisfied => "OrderByAccessSatisfied",
402 Self::OrderByMaterializedSort => "OrderByMaterializedSort",
403 Self::DistinctPreOrdered => "DistinctPreOrdered",
404 Self::DistinctMaterialized => "DistinctMaterialized",
405 Self::ProjectionMaterialized => "ProjectionMaterialized",
406 Self::ProjectionIndexOnly => "ProjectionIndexOnly",
407 Self::LimitOffset => "LimitOffset",
408 Self::CursorResume => "CursorResume",
409 Self::IndexRangeLimitPushdown => "IndexRangeLimitPushdown",
410 Self::TopNSeek => "TopNSeek",
411 Self::AggregateCount => "AggregateCount",
412 Self::AggregateExists => "AggregateExists",
413 Self::AggregateMin => "AggregateMin",
414 Self::AggregateMax => "AggregateMax",
415 Self::AggregateFirst => "AggregateFirst",
416 Self::AggregateLast => "AggregateLast",
417 Self::AggregateSum => "AggregateSum",
418 Self::AggregateSeekFirst => "AggregateSeekFirst",
419 Self::AggregateSeekLast => "AggregateSeekLast",
420 Self::GroupedAggregateHashMaterialized => "GroupedAggregateHashMaterialized",
421 Self::GroupedAggregateOrderedMaterialized => "GroupedAggregateOrderedMaterialized",
422 Self::SecondaryOrderPushdown => "SecondaryOrderPushdown",
423 }
424 }
425
426 #[must_use]
428 pub const fn layer_label(self) -> &'static str {
429 crate::db::query::explain::nodes::layer_label(self)
430 }
431}
432
433impl ExplainExecutionNodeDescriptor {
434 #[must_use]
436 pub const fn node_type(&self) -> ExplainExecutionNodeType {
437 self.node_type
438 }
439
440 #[must_use]
442 pub const fn execution_mode(&self) -> ExplainExecutionMode {
443 self.execution_mode
444 }
445
446 #[must_use]
448 pub const fn access_strategy(&self) -> Option<&ExplainAccessPath> {
449 self.access_strategy.as_ref()
450 }
451
452 #[must_use]
454 pub fn predicate_pushdown(&self) -> Option<&str> {
455 self.predicate_pushdown.as_deref()
456 }
457
458 #[must_use]
460 pub const fn residual_predicate(&self) -> Option<&ExplainPredicate> {
461 self.residual_predicate.as_ref()
462 }
463
464 #[must_use]
466 pub fn projection(&self) -> Option<&str> {
467 self.projection.as_deref()
468 }
469
470 #[must_use]
472 pub const fn ordering_source(&self) -> Option<ExplainExecutionOrderingSource> {
473 self.ordering_source
474 }
475
476 #[must_use]
478 pub const fn limit(&self) -> Option<u32> {
479 self.limit
480 }
481
482 #[must_use]
484 pub const fn cursor(&self) -> Option<bool> {
485 self.cursor
486 }
487
488 #[must_use]
490 pub const fn covering_scan(&self) -> Option<bool> {
491 self.covering_scan
492 }
493
494 #[must_use]
496 pub const fn rows_expected(&self) -> Option<u64> {
497 self.rows_expected
498 }
499
500 #[must_use]
502 pub const fn children(&self) -> &[Self] {
503 self.children.as_slice()
504 }
505
506 #[must_use]
508 pub const fn node_properties(&self) -> &ExplainPropertyMap {
509 &self.node_properties
510 }
511}
512
513pub(in crate::db::query::explain) const fn execution_mode_label(
514 mode: ExplainExecutionMode,
515) -> &'static str {
516 match mode {
517 ExplainExecutionMode::Streaming => "Streaming",
518 ExplainExecutionMode::Materialized => "Materialized",
519 }
520}
521
522pub(in crate::db::query::explain) const fn ordering_source_label(
523 ordering_source: ExplainExecutionOrderingSource,
524) -> &'static str {
525 match ordering_source {
526 ExplainExecutionOrderingSource::AccessOrder => "AccessOrder",
527 ExplainExecutionOrderingSource::Materialized => "Materialized",
528 ExplainExecutionOrderingSource::IndexSeekFirst { .. } => "IndexSeekFirst",
529 ExplainExecutionOrderingSource::IndexSeekLast { .. } => "IndexSeekLast",
530 }
531}