1mod descriptor;
7
8#[cfg(test)]
9use crate::db::executor::planning::route::AggregateRouteShape;
10#[cfg(test)]
11use crate::db::query::builder::AggregateExpr;
12use crate::{
13 db::{
14 Query, TraceReuseEvent,
15 executor::{EntityAuthority, SharedPreparedExecutionPlan},
16 query::{
17 admission::{QueryAdmissionLane, QueryAdmissionPolicy, QueryAdmissionSummary},
18 builder::{AggregateExplain, ProjectionExplain},
19 explain::{
20 ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor,
21 ExplainExecutionNodeType, ExplainOrderPushdown, FinalizedQueryDiagnostics,
22 property_keys,
23 },
24 intent::{QueryError, StructuralQuery},
25 plan::{AccessPlannedQuery, VisibleIndexes},
26 },
27 },
28 model::entity::EntityModel,
29 traits::{EntityKind, EntityValue},
30 value::Value,
31};
32
33#[cfg(all(test, feature = "sql-explain"))]
34pub(in crate::db) use descriptor::assemble_load_execution_node_descriptor;
35use descriptor::assemble_load_execution_verbose_diagnostics_from_route_facts;
36#[cfg(feature = "sql-explain")]
37pub(in crate::db) use descriptor::assemble_scalar_aggregate_execution_descriptor_with_projection;
38pub(in crate::db) use descriptor::{
39 assemble_aggregate_terminal_execution_descriptor,
40 assemble_load_execution_node_descriptor_for_authority,
41 assemble_load_execution_node_descriptor_from_route_facts,
42 freeze_load_execution_route_facts_for_authority,
43 freeze_load_execution_route_facts_for_model_only,
44};
45
46struct DescriptorStagePresence {
47 present: [bool; Self::STAGE_COUNT],
48}
49
50impl DescriptorStagePresence {
51 const STAGE_COUNT: usize = 4;
52 const TOP_N_SEEK: usize = 0;
53 const INDEX_RANGE_LIMIT_PUSHDOWN: usize = 1;
54 const INDEX_PREDICATE_PREFILTER: usize = 2;
55 const RESIDUAL_FILTER: usize = 3;
56
57 fn from_descriptor(descriptor: &ExplainExecutionNodeDescriptor) -> Self {
58 let mut presence = Self {
59 present: [false; Self::STAGE_COUNT],
60 };
61
62 descriptor.for_each_preorder(&mut |node| match node.node_type() {
63 ExplainExecutionNodeType::TopNSeek => presence.present[Self::TOP_N_SEEK] = true,
64 ExplainExecutionNodeType::IndexRangeLimitPushdown => {
65 presence.present[Self::INDEX_RANGE_LIMIT_PUSHDOWN] = true;
66 }
67 ExplainExecutionNodeType::IndexPredicatePrefilter => {
68 presence.present[Self::INDEX_PREDICATE_PREFILTER] = true;
69 }
70 ExplainExecutionNodeType::ResidualFilter => {
71 presence.present[Self::RESIDUAL_FILTER] = true;
72 }
73 _ => {}
74 });
75
76 presence
77 }
78
79 const fn has_top_n_seek(&self) -> bool {
80 self.present[Self::TOP_N_SEEK]
81 }
82
83 const fn has_index_range_limit_pushdown(&self) -> bool {
84 self.present[Self::INDEX_RANGE_LIMIT_PUSHDOWN]
85 }
86
87 const fn has_index_predicate_prefilter(&self) -> bool {
88 self.present[Self::INDEX_PREDICATE_PREFILTER]
89 }
90
91 const fn has_residual_filter(&self) -> bool {
92 self.present[Self::RESIDUAL_FILTER]
93 }
94}
95
96impl StructuralQuery {
97 fn finalized_execution_diagnostics_from_route_facts(
100 plan: &AccessPlannedQuery,
101 route_facts: &crate::db::executor::explain::descriptor::LoadExecutionRouteFacts,
102 reuse: Option<TraceReuseEvent>,
103 ) -> Result<FinalizedQueryDiagnostics, QueryError> {
104 let descriptor =
105 assemble_load_execution_node_descriptor_from_route_facts(plan, route_facts)
106 .map_err(QueryError::execute)?;
107 let route_diagnostics =
108 assemble_load_execution_verbose_diagnostics_from_route_facts(plan, route_facts)
109 .map_err(QueryError::execute)?;
110 let explain = plan.explain();
111
112 let stage_presence = DescriptorStagePresence::from_descriptor(&descriptor);
114 let mut logical_diagnostics = Vec::new();
115 logical_diagnostics.push(format!(
116 "diag.d.has_top_n_seek={}",
117 stage_presence.has_top_n_seek()
118 ));
119 logical_diagnostics.push(format!(
120 "diag.d.has_index_range_limit_pushdown={}",
121 stage_presence.has_index_range_limit_pushdown()
122 ));
123 logical_diagnostics.push(format!(
124 "diag.d.has_index_predicate_prefilter={}",
125 stage_presence.has_index_predicate_prefilter()
126 ));
127 logical_diagnostics.push(format!(
128 "diag.d.has_residual_filter={}",
129 stage_presence.has_residual_filter()
130 ));
131
132 logical_diagnostics.push(format!("diag.p.mode={:?}", explain.mode()));
134 logical_diagnostics.push(format!(
135 "diag.p.order_pushdown={}",
136 plan_order_pushdown_label(explain.order_pushdown())
137 ));
138 logical_diagnostics.push(format!(
139 "diag.p.predicate_pushdown={}",
140 plan.predicate_pushdown_label()
141 ));
142 logical_diagnostics.push(format!(
143 "diag.p.predicate_pushdown_outcome={}",
144 plan.predicate_pushdown_outcome_label()
145 ));
146 logical_diagnostics.push(format!(
147 "diag.p.predicate_pushdown_reason={}",
148 plan.predicate_pushdown_reason_label()
149 ));
150 logical_diagnostics.push(format!("diag.p.distinct={}", explain.distinct()));
151 logical_diagnostics.push(format!("diag.p.page={:?}", explain.page()));
152 logical_diagnostics.push(format!("diag.p.consistency={:?}", explain.consistency()));
153
154 let admission = QueryAdmissionPolicy::diagnostic_explain().evaluate(
155 QueryAdmissionSummary::from_plan(QueryAdmissionLane::DiagnosticExplain, plan),
156 );
157
158 Ok(FinalizedQueryDiagnostics::new(
159 descriptor,
160 route_diagnostics,
161 logical_diagnostics,
162 reuse,
163 )
164 .with_admission(admission))
165 }
166
167 pub(in crate::db) fn explain_execution_descriptor_from_model_only_plan(
171 &self,
172 plan: &AccessPlannedQuery,
173 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
174 let primary_key_names = model_primary_key_names(self.model());
175 let route_facts = freeze_load_execution_route_facts_for_model_only(
176 self.model().fields(),
177 primary_key_names.as_slice(),
178 plan,
179 )
180 .map_err(QueryError::execute)?;
181
182 assemble_load_execution_node_descriptor_from_route_facts(plan, &route_facts)
183 .map_err(QueryError::execute)
184 }
185
186 pub(in crate::db) fn explain_execution_descriptor_from_plan_with_authority(
188 &self,
189 plan: &AccessPlannedQuery,
190 authority: &EntityAuthority,
191 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
192 debug_assert_eq!(self.model().path(), authority.entity_path());
193 let route_facts = freeze_load_execution_route_facts_for_authority(authority, plan)
194 .map_err(QueryError::execute)?;
195
196 assemble_load_execution_node_descriptor_from_route_facts(plan, &route_facts)
197 .map_err(QueryError::execute)
198 }
199
200 fn finalized_execution_diagnostics_from_model_only_plan(
205 &self,
206 plan: &AccessPlannedQuery,
207 reuse: Option<TraceReuseEvent>,
208 ) -> Result<FinalizedQueryDiagnostics, QueryError> {
209 let primary_key_names = model_primary_key_names(self.model());
210 let route_facts = freeze_load_execution_route_facts_for_model_only(
211 self.model().fields(),
212 primary_key_names.as_slice(),
213 plan,
214 )
215 .map_err(QueryError::execute)?;
216
217 Self::finalized_execution_diagnostics_from_route_facts(plan, &route_facts, reuse)
218 }
219
220 pub(in crate::db) fn finalized_execution_diagnostics_from_plan_with_authority_and_descriptor_mutator(
223 &self,
224 plan: &AccessPlannedQuery,
225 authority: &EntityAuthority,
226 reuse: Option<TraceReuseEvent>,
227 mutate_descriptor: impl FnOnce(&mut ExplainExecutionNodeDescriptor),
228 ) -> Result<FinalizedQueryDiagnostics, QueryError> {
229 debug_assert_eq!(self.model().path(), authority.entity_path());
230 let route_facts = freeze_load_execution_route_facts_for_authority(authority, plan)
231 .map_err(QueryError::execute)?;
232 let mut diagnostics =
233 Self::finalized_execution_diagnostics_from_route_facts(plan, &route_facts, reuse)?;
234 mutate_descriptor(&mut diagnostics.execution);
235
236 Ok(diagnostics)
237 }
238
239 fn explain_execution_verbose_from_plan(
242 &self,
243 plan: &AccessPlannedQuery,
244 ) -> Result<String, QueryError> {
245 self.finalized_execution_diagnostics_from_model_only_plan(plan, None)
246 .map(|diagnostics| diagnostics.render_text_verbose())
247 }
248
249 fn explain_execution_json_from_plan(
252 &self,
253 plan: &AccessPlannedQuery,
254 ) -> Result<String, QueryError> {
255 self.finalized_execution_diagnostics_from_model_only_plan(plan, None)
256 .map(|diagnostics| diagnostics.render_json_canonical())
257 }
258
259 fn finalize_explain_access_choice_for_visible_indexes(
262 &self,
263 plan: &mut AccessPlannedQuery,
264 visible_indexes: &VisibleIndexes<'_>,
265 ) {
266 if let Some(schema_info) = visible_indexes.accepted_schema_info() {
267 plan.finalize_access_choice_for_model_with_semantic_indexes_and_schema(
268 self.model(),
269 visible_indexes.accepted_semantic_index_contracts(),
270 schema_info,
271 );
272 return;
273 }
274
275 plan.finalize_access_choice_for_model_only_with_indexes(
276 self.model(),
277 visible_indexes.generated_model_only_indexes(),
278 );
279 }
280
281 fn finalize_explain_access_choice_for_model_only(&self, plan: &mut AccessPlannedQuery) {
285 plan.finalize_access_choice_for_model_only_with_indexes(
286 self.model(),
287 self.model().indexes(),
288 );
289 }
290
291 fn finalized_model_only_explain_plan(&self) -> Result<AccessPlannedQuery, QueryError> {
294 let mut plan = self.build_plan()?;
295 self.finalize_explain_access_choice_for_model_only(&mut plan);
296
297 Ok(plan)
298 }
299
300 fn finalized_visible_indexes_explain_plan(
303 &self,
304 visible_indexes: &VisibleIndexes<'_>,
305 ) -> Result<AccessPlannedQuery, QueryError> {
306 let mut plan = self.build_plan_with_visible_indexes(visible_indexes)?;
307 self.finalize_explain_access_choice_for_visible_indexes(&mut plan, visible_indexes);
308
309 Ok(plan)
310 }
311
312 fn explain_execution_descriptor_for_model_only(
315 &self,
316 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
317 let plan = self.finalized_model_only_explain_plan()?;
318
319 self.explain_execution_descriptor_from_model_only_plan(&plan)
320 }
321
322 fn explain_execution_descriptor_for_visible_indexes(
325 &self,
326 visible_indexes: &VisibleIndexes<'_>,
327 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
328 let plan = self.finalized_visible_indexes_explain_plan(visible_indexes)?;
329
330 self.explain_execution_descriptor_from_model_only_plan(&plan)
331 }
332
333 fn render_execution_verbose_for_model_only(&self) -> Result<String, QueryError> {
336 let plan = self.finalized_model_only_explain_plan()?;
337
338 self.explain_execution_verbose_from_plan(&plan)
339 }
340
341 fn render_execution_json_for_model_only(&self) -> Result<String, QueryError> {
344 let plan = self.finalized_model_only_explain_plan()?;
345
346 self.explain_execution_json_from_plan(&plan)
347 }
348
349 fn explain_execution_verbose_for_visible_indexes(
352 &self,
353 visible_indexes: &VisibleIndexes<'_>,
354 ) -> Result<String, QueryError> {
355 let plan = self.finalized_visible_indexes_explain_plan(visible_indexes)?;
356
357 self.explain_execution_verbose_from_plan(&plan)
358 }
359
360 fn explain_execution_json_for_visible_indexes(
363 &self,
364 visible_indexes: &VisibleIndexes<'_>,
365 ) -> Result<String, QueryError> {
366 let plan = self.finalized_visible_indexes_explain_plan(visible_indexes)?;
367
368 self.explain_execution_json_from_plan(&plan)
369 }
370
371 #[inline(never)]
373 pub(in crate::db) fn explain_execution_for_model_only(
374 &self,
375 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
376 self.explain_execution_descriptor_for_model_only()
377 }
378
379 #[inline(never)]
381 pub(in crate::db) fn explain_execution_with_visible_indexes(
382 &self,
383 visible_indexes: &VisibleIndexes<'_>,
384 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
385 self.explain_execution_descriptor_for_visible_indexes(visible_indexes)
386 }
387
388 #[inline(never)]
391 pub(in crate::db) fn explain_execution_verbose_for_model_only(
392 &self,
393 ) -> Result<String, QueryError> {
394 self.render_execution_verbose_for_model_only()
395 }
396
397 #[inline(never)]
399 pub(in crate::db) fn explain_execution_json_for_model_only(
400 &self,
401 ) -> Result<String, QueryError> {
402 self.render_execution_json_for_model_only()
403 }
404
405 #[inline(never)]
407 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
408 &self,
409 visible_indexes: &VisibleIndexes<'_>,
410 ) -> Result<String, QueryError> {
411 self.explain_execution_verbose_for_visible_indexes(visible_indexes)
412 }
413
414 #[inline(never)]
416 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
417 &self,
418 visible_indexes: &VisibleIndexes<'_>,
419 ) -> Result<String, QueryError> {
420 self.explain_execution_json_for_visible_indexes(visible_indexes)
421 }
422
423 #[inline(never)]
425 #[cfg(test)]
426 pub(in crate::db) fn explain_aggregate_terminal_with_visible_indexes(
427 &self,
428 visible_indexes: &VisibleIndexes<'_>,
429 aggregate: AggregateRouteShape<'_>,
430 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
431 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
432 let query_explain = plan.explain();
433 let terminal = aggregate.kind();
434 let execution = assemble_aggregate_terminal_execution_descriptor(&plan, aggregate)
435 .map_err(QueryError::execute)?;
436
437 Ok(ExplainAggregateTerminalPlan::new(
438 query_explain,
439 terminal,
440 execution,
441 ))
442 }
443}
444
445impl SharedPreparedExecutionPlan {
446 pub(in crate::db) fn explain_prepared_aggregate_terminal<S>(
448 &self,
449 strategy: &S,
450 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
451 where
452 S: AggregateExplain,
453 {
454 let Some(kind) = strategy.explain_aggregate_kind() else {
455 return Err(QueryError::invariant());
456 };
457 let aggregate = self
458 .authority_ref()
459 .aggregate_route_shape(kind, strategy.explain_projected_field())
460 .map_err(QueryError::execute)?;
461 let execution =
462 assemble_aggregate_terminal_execution_descriptor(self.logical_plan(), aggregate)
463 .map_err(QueryError::execute)?;
464
465 Ok(ExplainAggregateTerminalPlan::new(
466 self.logical_plan().explain(),
467 kind,
468 execution,
469 ))
470 }
471
472 fn explain_prepared_terminal_load_descriptor(
473 &self,
474 terminal_label: &str,
475 field_label: &str,
476 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
477 let mut descriptor = assemble_load_execution_node_descriptor_for_authority(
478 self.authority_ref(),
479 self.logical_plan(),
480 )
481 .map_err(QueryError::execute)?;
482
483 descriptor
484 .node_properties
485 .insert(property_keys::TERMINAL, Value::from(terminal_label));
486 descriptor.node_properties.insert(
487 property_keys::TERMINAL_FIELD,
488 Value::from(field_label.to_string()),
489 );
490
491 Ok(descriptor)
492 }
493
494 pub(in crate::db) fn explain_bytes_by_terminal(
496 &self,
497 target_field: &str,
498 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
499 let mut descriptor =
500 self.explain_prepared_terminal_load_descriptor("bytes_by", target_field)?;
501 let projection_mode = self.bytes_by_projection_mode(target_field);
502
503 descriptor.node_properties.insert(
504 property_keys::TERMINAL_PROJECTION_MODE,
505 Value::from(projection_mode.label()),
506 );
507 descriptor.node_properties.insert(
508 property_keys::TERMINAL_INDEX_ONLY,
509 Value::from(projection_mode.is_index_only()),
510 );
511
512 Ok(descriptor)
513 }
514
515 pub(in crate::db) fn explain_prepared_projection_terminal<S>(
517 &self,
518 strategy: &S,
519 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
520 where
521 S: ProjectionExplain,
522 {
523 let projection_descriptor = strategy.explain_projection_descriptor();
524 let mut descriptor = self.explain_prepared_terminal_load_descriptor(
525 projection_descriptor.terminal_label(),
526 projection_descriptor.field_label(),
527 )?;
528 descriptor.node_properties.insert(
529 property_keys::TERMINAL_OUTPUT,
530 Value::from(projection_descriptor.output_label()),
531 );
532
533 Ok(descriptor)
534 }
535}
536
537impl<E> Query<E>
538where
539 E: EntityValue + EntityKind,
540{
541 fn explain_execution_descriptor_for_model_only_or_visible_indexes(
544 &self,
545 visible_indexes: Option<&VisibleIndexes<'_>>,
546 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
547 match visible_indexes {
548 Some(visible_indexes) => self
549 .structural()
550 .explain_execution_with_visible_indexes(visible_indexes),
551 None => self.structural().explain_execution_for_model_only(),
552 }
553 }
554
555 fn render_execution_descriptor_for_visibility(
558 &self,
559 visible_indexes: Option<&VisibleIndexes<'_>>,
560 render: impl FnOnce(ExplainExecutionNodeDescriptor) -> String,
561 ) -> Result<String, QueryError> {
562 let descriptor =
563 self.explain_execution_descriptor_for_model_only_or_visible_indexes(visible_indexes)?;
564
565 Ok(render(descriptor))
566 }
567
568 fn explain_execution_verbose_for_model_only_or_visible_indexes(
571 &self,
572 visible_indexes: Option<&VisibleIndexes<'_>>,
573 ) -> Result<String, QueryError> {
574 match visible_indexes {
575 Some(visible_indexes) => self
576 .structural()
577 .explain_execution_verbose_with_visible_indexes(visible_indexes),
578 None => self.structural().explain_execution_verbose_for_model_only(),
579 }
580 }
581
582 fn explain_execution_json_for_model_only_or_visible_indexes(
585 &self,
586 visible_indexes: Option<&VisibleIndexes<'_>>,
587 ) -> Result<String, QueryError> {
588 match visible_indexes {
589 Some(visible_indexes) => self
590 .structural()
591 .explain_execution_json_with_visible_indexes(visible_indexes),
592 None => self.structural().explain_execution_json_for_model_only(),
593 }
594 }
595
596 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
598 self.explain_execution_descriptor_for_model_only_or_visible_indexes(None)
599 }
600
601 #[cfg(test)]
603 pub(in crate::db) fn explain_execution_with_visible_indexes(
604 &self,
605 visible_indexes: &VisibleIndexes<'_>,
606 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
607 self.explain_execution_descriptor_for_model_only_or_visible_indexes(Some(visible_indexes))
608 }
609
610 pub fn explain_execution_text(&self) -> Result<String, QueryError> {
612 self.render_execution_descriptor_for_visibility(None, |descriptor| {
613 descriptor.render_text_tree()
614 })
615 }
616
617 pub fn explain_execution_json(&self) -> Result<String, QueryError> {
619 self.explain_execution_json_for_model_only_or_visible_indexes(None)
620 }
621
622 #[inline(never)]
624 pub fn explain_execution_verbose(&self) -> Result<String, QueryError> {
625 self.explain_execution_verbose_for_model_only_or_visible_indexes(None)
626 }
627
628 #[cfg(test)]
630 #[inline(never)]
631 pub(in crate::db) fn explain_aggregate_terminal(
632 &self,
633 aggregate: AggregateExpr,
634 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
635 self.structural()
636 .explain_aggregate_terminal_with_visible_indexes(
637 &VisibleIndexes::generated_model_only(E::MODEL.indexes()),
638 AggregateRouteShape::new_from_fields(
639 aggregate.kind(),
640 aggregate.target_field(),
641 E::MODEL.fields(),
642 model_primary_key_names(E::MODEL).as_slice(),
643 ),
644 )
645 }
646}
647
648fn model_primary_key_names(model: &EntityModel) -> Vec<&'static str> {
649 model
650 .primary_key_model()
651 .fields()
652 .iter()
653 .map(crate::model::field::FieldModel::name)
654 .collect()
655}
656
657fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
659 match order_pushdown {
660 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
661 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
662 format!("eligible(index={index},prefix_len={prefix_len})")
663 }
664 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
665 }
666}