1use crate::{
7 db::query::{
8 explain::{ExplainAccessPath, ExplainPlan, ExplainPredicate},
9 plan::AggregateKind,
10 },
11 value::Value,
12};
13use std::collections::BTreeMap;
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
23pub enum ExplainAggregateTerminalRoute {
24 Standard,
25 IndexSeekFirst { fetch: usize },
26 IndexSeekLast { fetch: usize },
27}
28
29#[derive(Clone, Debug, Eq, PartialEq)]
37pub struct ExplainAggregateTerminalPlan {
38 pub(crate) query: ExplainPlan,
39 pub(crate) terminal: AggregateKind,
40 pub(crate) route: ExplainAggregateTerminalRoute,
41 pub(crate) execution: ExplainExecutionDescriptor,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52pub enum ExplainExecutionOrderingSource {
53 AccessOrder,
54 Materialized,
55 IndexSeekFirst { fetch: usize },
56 IndexSeekLast { fetch: usize },
57}
58
59#[derive(Clone, Copy, Debug, Eq, PartialEq)]
66pub enum ExplainExecutionMode {
67 Streaming,
68 Materialized,
69}
70
71#[derive(Clone, Debug, Eq, PartialEq)]
80pub struct ExplainExecutionDescriptor {
81 pub(crate) access_strategy: ExplainAccessPath,
82 pub(crate) covering_projection: bool,
83 pub(crate) aggregation: AggregateKind,
84 pub(crate) execution_mode: ExplainExecutionMode,
85 pub(crate) ordering_source: ExplainExecutionOrderingSource,
86 pub(crate) limit: Option<u32>,
87 pub(crate) cursor: bool,
88 pub(crate) node_properties: BTreeMap<String, Value>,
89}
90
91#[derive(Clone, Copy, Debug, Eq, PartialEq)]
98pub enum ExplainExecutionNodeType {
99 ByKeyLookup,
100 ByKeysLookup,
101 PrimaryKeyRangeScan,
102 IndexPrefixScan,
103 IndexRangeScan,
104 IndexMultiLookup,
105 FullScan,
106 Union,
107 Intersection,
108 IndexPredicatePrefilter,
109 ResidualPredicateFilter,
110 OrderByAccessSatisfied,
111 OrderByMaterializedSort,
112 DistinctPreOrdered,
113 DistinctMaterialized,
114 ProjectionMaterialized,
115 ProjectionIndexOnly,
116 LimitOffset,
117 CursorResume,
118 IndexRangeLimitPushdown,
119 TopNSeek,
120 AggregateCount,
121 AggregateExists,
122 AggregateMin,
123 AggregateMax,
124 AggregateFirst,
125 AggregateLast,
126 AggregateSum,
127 AggregateSeekFirst,
128 AggregateSeekLast,
129 GroupedAggregateHashMaterialized,
130 GroupedAggregateOrderedMaterialized,
131 SecondaryOrderPushdown,
132}
133
134#[derive(Clone, Debug, Eq, PartialEq)]
142pub struct ExplainExecutionNodeDescriptor {
143 pub(crate) node_type: ExplainExecutionNodeType,
144 pub(crate) execution_mode: ExplainExecutionMode,
145 pub(crate) access_strategy: Option<ExplainAccessPath>,
146 pub(crate) predicate_pushdown: Option<String>,
147 pub(crate) residual_predicate: Option<ExplainPredicate>,
148 pub(crate) projection: Option<String>,
149 pub(crate) ordering_source: Option<ExplainExecutionOrderingSource>,
150 pub(crate) limit: Option<u32>,
151 pub(crate) cursor: Option<bool>,
152 pub(crate) covering_scan: Option<bool>,
153 pub(crate) rows_expected: Option<u64>,
154 pub(crate) children: Vec<Self>,
155 pub(crate) node_properties: BTreeMap<String, Value>,
156}
157
158impl ExplainAggregateTerminalPlan {
159 #[must_use]
161 pub const fn query(&self) -> &ExplainPlan {
162 &self.query
163 }
164
165 #[must_use]
167 pub const fn terminal(&self) -> AggregateKind {
168 self.terminal
169 }
170
171 #[must_use]
173 pub const fn route(&self) -> ExplainAggregateTerminalRoute {
174 self.route
175 }
176
177 #[must_use]
179 pub const fn execution(&self) -> &ExplainExecutionDescriptor {
180 &self.execution
181 }
182
183 #[must_use]
184 pub(in crate::db) const fn new(
185 query: ExplainPlan,
186 terminal: AggregateKind,
187 execution: ExplainExecutionDescriptor,
188 ) -> Self {
189 let route = execution.route();
190
191 Self {
192 query,
193 terminal,
194 route,
195 execution,
196 }
197 }
198}
199
200impl ExplainExecutionDescriptor {
201 #[must_use]
203 pub const fn access_strategy(&self) -> &ExplainAccessPath {
204 &self.access_strategy
205 }
206
207 #[must_use]
209 pub const fn covering_projection(&self) -> bool {
210 self.covering_projection
211 }
212
213 #[must_use]
215 pub const fn aggregation(&self) -> AggregateKind {
216 self.aggregation
217 }
218
219 #[must_use]
221 pub const fn execution_mode(&self) -> ExplainExecutionMode {
222 self.execution_mode
223 }
224
225 #[must_use]
227 pub const fn ordering_source(&self) -> ExplainExecutionOrderingSource {
228 self.ordering_source
229 }
230
231 #[must_use]
233 pub const fn limit(&self) -> Option<u32> {
234 self.limit
235 }
236
237 #[must_use]
239 pub const fn cursor(&self) -> bool {
240 self.cursor
241 }
242
243 #[must_use]
245 pub const fn node_properties(&self) -> &BTreeMap<String, Value> {
246 &self.node_properties
247 }
248
249 #[must_use]
250 pub(in crate::db) const fn route(&self) -> ExplainAggregateTerminalRoute {
251 match self.ordering_source {
252 ExplainExecutionOrderingSource::IndexSeekFirst { fetch } => {
253 ExplainAggregateTerminalRoute::IndexSeekFirst { fetch }
254 }
255 ExplainExecutionOrderingSource::IndexSeekLast { fetch } => {
256 ExplainAggregateTerminalRoute::IndexSeekLast { fetch }
257 }
258 ExplainExecutionOrderingSource::AccessOrder
259 | ExplainExecutionOrderingSource::Materialized => {
260 ExplainAggregateTerminalRoute::Standard
261 }
262 }
263 }
264}
265
266impl ExplainAggregateTerminalPlan {
267 #[must_use]
269 pub fn execution_node_descriptor(&self) -> ExplainExecutionNodeDescriptor {
270 ExplainExecutionNodeDescriptor {
271 node_type: aggregate_execution_node_type(self.terminal, self.execution.ordering_source),
272 execution_mode: self.execution.execution_mode,
273 access_strategy: Some(self.execution.access_strategy.clone()),
274 predicate_pushdown: None,
275 residual_predicate: None,
276 projection: None,
277 ordering_source: Some(self.execution.ordering_source),
278 limit: self.execution.limit,
279 cursor: Some(self.execution.cursor),
280 covering_scan: Some(self.execution.covering_projection),
281 rows_expected: None,
282 children: Vec::new(),
283 node_properties: self.execution.node_properties.clone(),
284 }
285 }
286}
287
288const fn aggregate_execution_node_type(
289 terminal: AggregateKind,
290 ordering_source: ExplainExecutionOrderingSource,
291) -> ExplainExecutionNodeType {
292 match ordering_source {
293 ExplainExecutionOrderingSource::IndexSeekFirst { .. } => {
294 ExplainExecutionNodeType::AggregateSeekFirst
295 }
296 ExplainExecutionOrderingSource::IndexSeekLast { .. } => {
297 ExplainExecutionNodeType::AggregateSeekLast
298 }
299 ExplainExecutionOrderingSource::AccessOrder
300 | ExplainExecutionOrderingSource::Materialized => match terminal {
301 AggregateKind::Count => ExplainExecutionNodeType::AggregateCount,
302 AggregateKind::Exists => ExplainExecutionNodeType::AggregateExists,
303 AggregateKind::Min => ExplainExecutionNodeType::AggregateMin,
304 AggregateKind::Max => ExplainExecutionNodeType::AggregateMax,
305 AggregateKind::First => ExplainExecutionNodeType::AggregateFirst,
306 AggregateKind::Last => ExplainExecutionNodeType::AggregateLast,
307 AggregateKind::Sum | AggregateKind::Avg => ExplainExecutionNodeType::AggregateSum,
308 },
309 }
310}
311
312impl ExplainExecutionNodeType {
313 #[must_use]
315 pub const fn as_str(self) -> &'static str {
316 match self {
317 Self::ByKeyLookup => "ByKeyLookup",
318 Self::ByKeysLookup => "ByKeysLookup",
319 Self::PrimaryKeyRangeScan => "PrimaryKeyRangeScan",
320 Self::IndexPrefixScan => "IndexPrefixScan",
321 Self::IndexRangeScan => "IndexRangeScan",
322 Self::IndexMultiLookup => "IndexMultiLookup",
323 Self::FullScan => "FullScan",
324 Self::Union => "Union",
325 Self::Intersection => "Intersection",
326 Self::IndexPredicatePrefilter => "IndexPredicatePrefilter",
327 Self::ResidualPredicateFilter => "ResidualPredicateFilter",
328 Self::OrderByAccessSatisfied => "OrderByAccessSatisfied",
329 Self::OrderByMaterializedSort => "OrderByMaterializedSort",
330 Self::DistinctPreOrdered => "DistinctPreOrdered",
331 Self::DistinctMaterialized => "DistinctMaterialized",
332 Self::ProjectionMaterialized => "ProjectionMaterialized",
333 Self::ProjectionIndexOnly => "ProjectionIndexOnly",
334 Self::LimitOffset => "LimitOffset",
335 Self::CursorResume => "CursorResume",
336 Self::IndexRangeLimitPushdown => "IndexRangeLimitPushdown",
337 Self::TopNSeek => "TopNSeek",
338 Self::AggregateCount => "AggregateCount",
339 Self::AggregateExists => "AggregateExists",
340 Self::AggregateMin => "AggregateMin",
341 Self::AggregateMax => "AggregateMax",
342 Self::AggregateFirst => "AggregateFirst",
343 Self::AggregateLast => "AggregateLast",
344 Self::AggregateSum => "AggregateSum",
345 Self::AggregateSeekFirst => "AggregateSeekFirst",
346 Self::AggregateSeekLast => "AggregateSeekLast",
347 Self::GroupedAggregateHashMaterialized => "GroupedAggregateHashMaterialized",
348 Self::GroupedAggregateOrderedMaterialized => "GroupedAggregateOrderedMaterialized",
349 Self::SecondaryOrderPushdown => "SecondaryOrderPushdown",
350 }
351 }
352
353 #[must_use]
355 pub const fn layer_label(self) -> &'static str {
356 crate::db::query::explain::nodes::layer_label(self)
357 }
358}
359
360impl ExplainExecutionNodeDescriptor {
361 #[must_use]
363 pub const fn node_type(&self) -> ExplainExecutionNodeType {
364 self.node_type
365 }
366
367 #[must_use]
369 pub const fn execution_mode(&self) -> ExplainExecutionMode {
370 self.execution_mode
371 }
372
373 #[must_use]
375 pub const fn access_strategy(&self) -> Option<&ExplainAccessPath> {
376 self.access_strategy.as_ref()
377 }
378
379 #[must_use]
381 pub fn predicate_pushdown(&self) -> Option<&str> {
382 self.predicate_pushdown.as_deref()
383 }
384
385 #[must_use]
387 pub const fn residual_predicate(&self) -> Option<&ExplainPredicate> {
388 self.residual_predicate.as_ref()
389 }
390
391 #[must_use]
393 pub fn projection(&self) -> Option<&str> {
394 self.projection.as_deref()
395 }
396
397 #[must_use]
399 pub const fn ordering_source(&self) -> Option<ExplainExecutionOrderingSource> {
400 self.ordering_source
401 }
402
403 #[must_use]
405 pub const fn limit(&self) -> Option<u32> {
406 self.limit
407 }
408
409 #[must_use]
411 pub const fn cursor(&self) -> Option<bool> {
412 self.cursor
413 }
414
415 #[must_use]
417 pub const fn covering_scan(&self) -> Option<bool> {
418 self.covering_scan
419 }
420
421 #[must_use]
423 pub const fn rows_expected(&self) -> Option<u64> {
424 self.rows_expected
425 }
426
427 #[must_use]
429 pub const fn children(&self) -> &[Self] {
430 self.children.as_slice()
431 }
432
433 #[must_use]
435 pub const fn node_properties(&self) -> &BTreeMap<String, Value> {
436 &self.node_properties
437 }
438}
439
440pub(in crate::db::query::explain) const fn execution_mode_label(
441 mode: ExplainExecutionMode,
442) -> &'static str {
443 match mode {
444 ExplainExecutionMode::Streaming => "Streaming",
445 ExplainExecutionMode::Materialized => "Materialized",
446 }
447}
448
449pub(in crate::db::query::explain) const fn ordering_source_label(
450 ordering_source: ExplainExecutionOrderingSource,
451) -> &'static str {
452 match ordering_source {
453 ExplainExecutionOrderingSource::AccessOrder => "AccessOrder",
454 ExplainExecutionOrderingSource::Materialized => "Materialized",
455 ExplainExecutionOrderingSource::IndexSeekFirst { .. } => "IndexSeekFirst",
456 ExplainExecutionOrderingSource::IndexSeekLast { .. } => "IndexSeekLast",
457 }
458}