1use crate::{
7 db::{
8 executor::{
9 BytesByProjectionMode, ExecutablePlan,
10 assemble_aggregate_terminal_execution_descriptor,
11 assemble_load_execution_node_descriptor, assemble_load_execution_verbose_diagnostics,
12 planning::route::AggregateRouteShape,
13 },
14 predicate::{CoercionId, CompareOp, MissingRowPolicy, Predicate},
15 query::{
16 builder::{
17 AggregateExpr, PreparedFluentAggregateExplainStrategy,
18 PreparedFluentProjectionStrategy,
19 },
20 explain::{
21 ExplainAccessPath, ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor,
22 ExplainExecutionNodeType, ExplainOrderPushdown, ExplainPlan, ExplainPredicate,
23 },
24 expr::{FilterExpr, SortExpr},
25 intent::{QueryError, model::QueryModel},
26 plan::{AccessPlannedQuery, LoadSpec, QueryMode, VisibleIndexes},
27 },
28 },
29 traits::{EntityKind, EntityValue, FieldValue, SingletonEntity},
30 value::Value,
31};
32use core::marker::PhantomData;
33
34#[derive(Clone, Debug)]
43pub(in crate::db) struct StructuralQuery {
44 intent: QueryModel<'static, Value>,
45}
46
47impl StructuralQuery {
48 #[must_use]
49 pub(in crate::db) const fn new(
50 model: &'static crate::model::entity::EntityModel,
51 consistency: MissingRowPolicy,
52 ) -> Self {
53 Self {
54 intent: QueryModel::new(model, consistency),
55 }
56 }
57
58 #[must_use]
59 const fn mode(&self) -> QueryMode {
60 self.intent.mode()
61 }
62
63 #[must_use]
64 fn has_explicit_order(&self) -> bool {
65 self.intent.has_explicit_order()
66 }
67
68 #[must_use]
69 const fn has_grouping(&self) -> bool {
70 self.intent.has_grouping()
71 }
72
73 #[must_use]
74 const fn load_spec(&self) -> Option<LoadSpec> {
75 match self.intent.mode() {
76 QueryMode::Load(spec) => Some(spec),
77 QueryMode::Delete(_) => None,
78 }
79 }
80
81 #[must_use]
82 pub(in crate::db) fn filter(mut self, predicate: Predicate) -> Self {
83 self.intent = self.intent.filter(predicate);
84 self
85 }
86
87 fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
88 let Self { intent } = self;
89 let intent = intent.filter_expr(expr)?;
90
91 Ok(Self { intent })
92 }
93
94 fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
95 let Self { intent } = self;
96 let intent = intent.sort_expr(expr)?;
97
98 Ok(Self { intent })
99 }
100
101 #[must_use]
102 pub(in crate::db) fn order_by(mut self, field: impl AsRef<str>) -> Self {
103 self.intent = self.intent.order_by(field);
104 self
105 }
106
107 #[must_use]
108 pub(in crate::db) fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
109 self.intent = self.intent.order_by_desc(field);
110 self
111 }
112
113 #[must_use]
114 pub(in crate::db) fn distinct(mut self) -> Self {
115 self.intent = self.intent.distinct();
116 self
117 }
118
119 #[cfg(feature = "sql")]
120 #[must_use]
121 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
122 where
123 I: IntoIterator<Item = S>,
124 S: Into<String>,
125 {
126 self.intent = self.intent.select_fields(fields);
127 self
128 }
129
130 pub(in crate::db) fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
131 let Self { intent } = self;
132 let intent = intent.push_group_field(field.as_ref())?;
133
134 Ok(Self { intent })
135 }
136
137 #[must_use]
138 pub(in crate::db) fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
139 self.intent = self.intent.push_group_aggregate(aggregate);
140 self
141 }
142
143 #[must_use]
144 fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
145 self.intent = self.intent.grouped_limits(max_groups, max_group_bytes);
146 self
147 }
148
149 pub(in crate::db) fn having_group(
150 self,
151 field: impl AsRef<str>,
152 op: CompareOp,
153 value: Value,
154 ) -> Result<Self, QueryError> {
155 let field = field.as_ref().to_owned();
156 let Self { intent } = self;
157 let intent = intent.push_having_group_clause(&field, op, value)?;
158
159 Ok(Self { intent })
160 }
161
162 pub(in crate::db) fn having_aggregate(
163 self,
164 aggregate_index: usize,
165 op: CompareOp,
166 value: Value,
167 ) -> Result<Self, QueryError> {
168 let Self { intent } = self;
169 let intent = intent.push_having_aggregate_clause(aggregate_index, op, value)?;
170
171 Ok(Self { intent })
172 }
173
174 #[must_use]
175 fn by_id(self, id: Value) -> Self {
176 let Self { intent } = self;
177 Self {
178 intent: intent.by_id(id),
179 }
180 }
181
182 #[must_use]
183 fn by_ids<I>(self, ids: I) -> Self
184 where
185 I: IntoIterator<Item = Value>,
186 {
187 let Self { intent } = self;
188 Self {
189 intent: intent.by_ids(ids),
190 }
191 }
192
193 #[must_use]
194 fn only(self, id: Value) -> Self {
195 let Self { intent } = self;
196
197 Self {
198 intent: intent.only(id),
199 }
200 }
201
202 #[must_use]
203 pub(in crate::db) fn delete(mut self) -> Self {
204 self.intent = self.intent.delete();
205 self
206 }
207
208 #[must_use]
209 pub(in crate::db) fn limit(mut self, limit: u32) -> Self {
210 self.intent = self.intent.limit(limit);
211 self
212 }
213
214 #[must_use]
215 pub(in crate::db) fn offset(mut self, offset: u32) -> Self {
216 self.intent = self.intent.offset(offset);
217 self
218 }
219
220 pub(in crate::db) fn build_plan(&self) -> Result<AccessPlannedQuery, QueryError> {
221 self.intent.build_plan_model()
222 }
223
224 pub(in crate::db) fn build_plan_with_visible_indexes(
225 &self,
226 visible_indexes: &VisibleIndexes<'_>,
227 ) -> Result<AccessPlannedQuery, QueryError> {
228 self.intent.build_plan_model_with_indexes(visible_indexes)
229 }
230
231 fn build_plan_for_visibility(
234 &self,
235 visible_indexes: Option<&VisibleIndexes<'_>>,
236 ) -> Result<AccessPlannedQuery, QueryError> {
237 match visible_indexes {
238 Some(visible_indexes) => self.build_plan_with_visible_indexes(visible_indexes),
239 None => self.build_plan(),
240 }
241 }
242
243 fn explain_execution_descriptor_from_plan(
246 &self,
247 plan: &AccessPlannedQuery,
248 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
249 assemble_load_execution_node_descriptor(
250 self.intent.model().fields(),
251 self.intent.model().primary_key().name(),
252 plan,
253 )
254 .map_err(QueryError::execute)
255 }
256
257 fn explain_execution_verbose_from_plan(
259 &self,
260 plan: &AccessPlannedQuery,
261 ) -> Result<String, QueryError> {
262 let descriptor = self.explain_execution_descriptor_from_plan(plan)?;
263 let route_diagnostics = assemble_load_execution_verbose_diagnostics(
264 self.intent.model().fields(),
265 self.intent.model().primary_key().name(),
266 plan,
267 )
268 .map_err(QueryError::execute)?;
269 let explain = plan.explain_with_model(self.intent.model());
270
271 let mut lines = vec![descriptor.render_text_tree_verbose()];
273 lines.extend(route_diagnostics);
274
275 lines.push(format!(
277 "diag.d.has_top_n_seek={}",
278 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
279 ));
280 lines.push(format!(
281 "diag.d.has_index_range_limit_pushdown={}",
282 contains_execution_node_type(
283 &descriptor,
284 ExplainExecutionNodeType::IndexRangeLimitPushdown,
285 )
286 ));
287 lines.push(format!(
288 "diag.d.has_index_predicate_prefilter={}",
289 contains_execution_node_type(
290 &descriptor,
291 ExplainExecutionNodeType::IndexPredicatePrefilter,
292 )
293 ));
294 lines.push(format!(
295 "diag.d.has_residual_predicate_filter={}",
296 contains_execution_node_type(
297 &descriptor,
298 ExplainExecutionNodeType::ResidualPredicateFilter,
299 )
300 ));
301
302 lines.push(format!("diag.p.mode={:?}", explain.mode()));
304 lines.push(format!(
305 "diag.p.order_pushdown={}",
306 plan_order_pushdown_label(explain.order_pushdown())
307 ));
308 lines.push(format!(
309 "diag.p.predicate_pushdown={}",
310 plan_predicate_pushdown_label(explain.predicate(), explain.access())
311 ));
312 lines.push(format!("diag.p.distinct={}", explain.distinct()));
313 lines.push(format!("diag.p.page={:?}", explain.page()));
314 lines.push(format!("diag.p.consistency={:?}", explain.consistency()));
315
316 Ok(lines.join("\n"))
317 }
318
319 #[cfg(feature = "sql")]
320 #[must_use]
321 pub(in crate::db) const fn model(&self) -> &'static crate::model::entity::EntityModel {
322 self.intent.model()
323 }
324
325 #[inline(never)]
326 pub(in crate::db) fn explain_execution_with_visible_indexes(
327 &self,
328 visible_indexes: &VisibleIndexes<'_>,
329 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
330 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
331
332 self.explain_execution_descriptor_from_plan(&plan)
333 }
334
335 #[inline(never)]
337 pub(in crate::db) fn explain_execution(
338 &self,
339 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
340 let plan = self.build_plan_for_visibility(None)?;
341
342 self.explain_execution_descriptor_from_plan(&plan)
343 }
344
345 pub(in crate::db) fn explain_execution_text(&self) -> Result<String, QueryError> {
348 Ok(self.explain_execution()?.render_text_tree())
349 }
350
351 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
352 &self,
353 visible_indexes: &VisibleIndexes<'_>,
354 ) -> Result<String, QueryError> {
355 Ok(self
356 .explain_execution_with_visible_indexes(visible_indexes)?
357 .render_text_tree())
358 }
359
360 pub(in crate::db) fn explain_execution_json(&self) -> Result<String, QueryError> {
363 Ok(self.explain_execution()?.render_json_canonical())
364 }
365
366 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
367 &self,
368 visible_indexes: &VisibleIndexes<'_>,
369 ) -> Result<String, QueryError> {
370 Ok(self
371 .explain_execution_with_visible_indexes(visible_indexes)?
372 .render_json_canonical())
373 }
374
375 #[inline(never)]
378 pub(in crate::db) fn explain_execution_verbose(&self) -> Result<String, QueryError> {
379 let plan = self.build_plan_for_visibility(None)?;
380
381 self.explain_execution_verbose_from_plan(&plan)
382 }
383
384 #[inline(never)]
385 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
386 &self,
387 visible_indexes: &VisibleIndexes<'_>,
388 ) -> Result<String, QueryError> {
389 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
390
391 self.explain_execution_verbose_from_plan(&plan)
392 }
393
394 #[inline(never)]
395 pub(in crate::db) fn explain_aggregate_terminal_with_visible_indexes(
396 &self,
397 visible_indexes: &VisibleIndexes<'_>,
398 aggregate: AggregateRouteShape<'_>,
399 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
400 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
401 let query_explain = plan.explain_with_model(self.intent.model());
402 let terminal = aggregate.kind();
403 let execution = assemble_aggregate_terminal_execution_descriptor(&plan, aggregate);
404
405 Ok(ExplainAggregateTerminalPlan::new(
406 query_explain,
407 terminal,
408 execution,
409 ))
410 }
411
412 #[inline(never)]
413 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
414 &self,
415 visible_indexes: &VisibleIndexes<'_>,
416 strategy: &S,
417 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
418 where
419 S: PreparedFluentAggregateExplainStrategy,
420 {
421 let Some(kind) = strategy.explain_aggregate_kind() else {
422 return Err(QueryError::invariant(
423 "prepared fluent aggregate explain requires an explain-visible aggregate kind",
424 ));
425 };
426 let aggregate = AggregateRouteShape::new_from_fields(
427 kind,
428 strategy.explain_projected_field(),
429 self.intent.model().fields(),
430 self.intent.model().primary_key().name(),
431 );
432
433 self.explain_aggregate_terminal_with_visible_indexes(visible_indexes, aggregate)
434 }
435}
436
437#[derive(Debug)]
446struct PlannedQueryCore {
447 model: &'static crate::model::entity::EntityModel,
448 plan: AccessPlannedQuery,
449}
450
451impl PlannedQueryCore {
452 #[must_use]
453 const fn new(
454 model: &'static crate::model::entity::EntityModel,
455 plan: AccessPlannedQuery,
456 ) -> Self {
457 Self { model, plan }
458 }
459
460 #[must_use]
461 fn explain(&self) -> ExplainPlan {
462 self.plan.explain_with_model(self.model)
463 }
464
465 #[must_use]
467 fn plan_hash_hex(&self) -> String {
468 self.plan.fingerprint().to_string()
469 }
470}
471
472#[derive(Debug)]
481pub struct PlannedQuery<E: EntityKind> {
482 inner: PlannedQueryCore,
483 _marker: PhantomData<E>,
484}
485
486impl<E: EntityKind> PlannedQuery<E> {
487 #[must_use]
488 const fn from_inner(inner: PlannedQueryCore) -> Self {
489 Self {
490 inner,
491 _marker: PhantomData,
492 }
493 }
494
495 #[must_use]
496 pub fn explain(&self) -> ExplainPlan {
497 self.inner.explain()
498 }
499
500 #[must_use]
502 pub fn plan_hash_hex(&self) -> String {
503 self.inner.plan_hash_hex()
504 }
505}
506
507#[derive(Clone, Debug)]
516struct CompiledQueryCore {
517 model: &'static crate::model::entity::EntityModel,
518 entity_path: &'static str,
519 plan: AccessPlannedQuery,
520}
521
522impl CompiledQueryCore {
523 #[must_use]
524 const fn new(
525 model: &'static crate::model::entity::EntityModel,
526 entity_path: &'static str,
527 plan: AccessPlannedQuery,
528 ) -> Self {
529 Self {
530 model,
531 entity_path,
532 plan,
533 }
534 }
535
536 #[must_use]
537 fn explain(&self) -> ExplainPlan {
538 self.plan.explain_with_model(self.model)
539 }
540
541 #[must_use]
543 fn plan_hash_hex(&self) -> String {
544 self.plan.fingerprint().to_string()
545 }
546
547 #[must_use]
548 #[cfg(test)]
549 fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
550 self.plan.projection_spec(self.model)
551 }
552
553 #[must_use]
554 fn into_inner(self) -> AccessPlannedQuery {
555 self.plan
556 }
557}
558
559#[derive(Clone, Debug)]
568pub struct CompiledQuery<E: EntityKind> {
569 inner: CompiledQueryCore,
570 _marker: PhantomData<E>,
571}
572
573impl<E: EntityKind> CompiledQuery<E> {
574 #[must_use]
575 const fn from_inner(inner: CompiledQueryCore) -> Self {
576 Self {
577 inner,
578 _marker: PhantomData,
579 }
580 }
581
582 #[must_use]
583 pub fn explain(&self) -> ExplainPlan {
584 self.inner.explain()
585 }
586
587 #[must_use]
589 pub fn plan_hash_hex(&self) -> String {
590 self.inner.plan_hash_hex()
591 }
592
593 #[must_use]
594 #[cfg(test)]
595 pub(in crate::db) fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
596 self.inner.projection_spec()
597 }
598
599 pub(in crate::db) fn into_executable(self) -> crate::db::executor::ExecutablePlan<E> {
601 assert!(
602 self.inner.entity_path == E::PATH,
603 "compiled query entity mismatch: compiled for '{}', requested '{}'",
604 self.inner.entity_path,
605 E::PATH,
606 );
607
608 crate::db::executor::ExecutablePlan::new(self.into_inner())
609 }
610
611 #[must_use]
612 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
613 self.inner.into_inner()
614 }
615}
616
617#[derive(Debug)]
629pub struct Query<E: EntityKind> {
630 inner: StructuralQuery,
631 _marker: PhantomData<E>,
632}
633
634impl<E: EntityKind> Query<E> {
635 pub(in crate::db) const fn from_inner(inner: StructuralQuery) -> Self {
637 Self {
638 inner,
639 _marker: PhantomData,
640 }
641 }
642
643 #[must_use]
647 pub const fn new(consistency: MissingRowPolicy) -> Self {
648 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
649 }
650
651 #[must_use]
653 pub const fn mode(&self) -> QueryMode {
654 self.inner.mode()
655 }
656
657 pub(in crate::db) fn explain_with_visible_indexes(
658 &self,
659 visible_indexes: &VisibleIndexes<'_>,
660 ) -> Result<ExplainPlan, QueryError> {
661 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
662
663 Ok(plan.explain_with_model(E::MODEL))
664 }
665
666 pub(in crate::db) fn plan_hash_hex_with_visible_indexes(
667 &self,
668 visible_indexes: &VisibleIndexes<'_>,
669 ) -> Result<String, QueryError> {
670 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
671
672 Ok(plan.fingerprint().to_string())
673 }
674
675 fn build_plan_for_visibility(
678 &self,
679 visible_indexes: Option<&VisibleIndexes<'_>>,
680 ) -> Result<AccessPlannedQuery, QueryError> {
681 self.inner.build_plan_for_visibility(visible_indexes)
682 }
683
684 fn planned_query_from_plan(plan: AccessPlannedQuery) -> PlannedQuery<E> {
686 let _projection = plan.projection_spec(E::MODEL);
687
688 PlannedQuery::from_inner(PlannedQueryCore::new(E::MODEL, plan))
689 }
690
691 fn compiled_query_from_plan(plan: AccessPlannedQuery) -> CompiledQuery<E> {
693 let _projection = plan.projection_spec(E::MODEL);
694
695 CompiledQuery::from_inner(CompiledQueryCore::new(E::MODEL, E::PATH, plan))
696 }
697
698 #[must_use]
699 pub(crate) fn has_explicit_order(&self) -> bool {
700 self.inner.has_explicit_order()
701 }
702
703 #[must_use]
704 pub(crate) const fn has_grouping(&self) -> bool {
705 self.inner.has_grouping()
706 }
707
708 #[must_use]
709 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
710 self.inner.load_spec()
711 }
712
713 #[must_use]
715 pub fn filter(mut self, predicate: Predicate) -> Self {
716 self.inner = self.inner.filter(predicate);
717 self
718 }
719
720 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
722 let Self { inner, .. } = self;
723 let inner = inner.filter_expr(expr)?;
724
725 Ok(Self::from_inner(inner))
726 }
727
728 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
730 let Self { inner, .. } = self;
731 let inner = inner.sort_expr(expr)?;
732
733 Ok(Self::from_inner(inner))
734 }
735
736 #[must_use]
738 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
739 self.inner = self.inner.order_by(field);
740 self
741 }
742
743 #[must_use]
745 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
746 self.inner = self.inner.order_by_desc(field);
747 self
748 }
749
750 #[must_use]
752 pub fn distinct(mut self) -> Self {
753 self.inner = self.inner.distinct();
754 self
755 }
756
757 #[cfg(all(test, feature = "sql"))]
760 #[must_use]
761 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
762 where
763 I: IntoIterator<Item = S>,
764 S: Into<String>,
765 {
766 self.inner = self.inner.select_fields(fields);
767 self
768 }
769
770 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
772 let Self { inner, .. } = self;
773 let inner = inner.group_by(field)?;
774
775 Ok(Self::from_inner(inner))
776 }
777
778 #[must_use]
780 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
781 self.inner = self.inner.aggregate(aggregate);
782 self
783 }
784
785 #[must_use]
787 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
788 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
789 self
790 }
791
792 pub fn having_group(
794 self,
795 field: impl AsRef<str>,
796 op: CompareOp,
797 value: Value,
798 ) -> Result<Self, QueryError> {
799 let Self { inner, .. } = self;
800 let inner = inner.having_group(field, op, value)?;
801
802 Ok(Self::from_inner(inner))
803 }
804
805 pub fn having_aggregate(
807 self,
808 aggregate_index: usize,
809 op: CompareOp,
810 value: Value,
811 ) -> Result<Self, QueryError> {
812 let Self { inner, .. } = self;
813 let inner = inner.having_aggregate(aggregate_index, op, value)?;
814
815 Ok(Self::from_inner(inner))
816 }
817
818 pub(crate) fn by_id(self, id: E::Key) -> Self {
820 let Self { inner, .. } = self;
821
822 Self::from_inner(inner.by_id(id.to_value()))
823 }
824
825 pub(crate) fn by_ids<I>(self, ids: I) -> Self
827 where
828 I: IntoIterator<Item = E::Key>,
829 {
830 let Self { inner, .. } = self;
831
832 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
833 }
834
835 #[must_use]
837 pub fn delete(mut self) -> Self {
838 self.inner = self.inner.delete();
839 self
840 }
841
842 #[must_use]
849 pub fn limit(mut self, limit: u32) -> Self {
850 self.inner = self.inner.limit(limit);
851 self
852 }
853
854 #[must_use]
860 pub fn offset(mut self, offset: u32) -> Self {
861 self.inner = self.inner.offset(offset);
862 self
863 }
864
865 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
867 let plan = self.planned()?;
868
869 Ok(plan.explain())
870 }
871
872 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
877 let plan = self.inner.build_plan()?;
878
879 Ok(plan.fingerprint().to_string())
880 }
881
882 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
884 where
885 E: EntityValue,
886 {
887 self.inner.explain_execution()
888 }
889
890 pub(in crate::db) fn explain_execution_with_visible_indexes(
891 &self,
892 visible_indexes: &VisibleIndexes<'_>,
893 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
894 where
895 E: EntityValue,
896 {
897 self.inner
898 .explain_execution_with_visible_indexes(visible_indexes)
899 }
900
901 pub fn explain_execution_text(&self) -> Result<String, QueryError>
903 where
904 E: EntityValue,
905 {
906 self.inner.explain_execution_text()
907 }
908
909 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
910 &self,
911 visible_indexes: &VisibleIndexes<'_>,
912 ) -> Result<String, QueryError>
913 where
914 E: EntityValue,
915 {
916 self.inner
917 .explain_execution_text_with_visible_indexes(visible_indexes)
918 }
919
920 pub fn explain_execution_json(&self) -> Result<String, QueryError>
922 where
923 E: EntityValue,
924 {
925 self.inner.explain_execution_json()
926 }
927
928 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
929 &self,
930 visible_indexes: &VisibleIndexes<'_>,
931 ) -> Result<String, QueryError>
932 where
933 E: EntityValue,
934 {
935 self.inner
936 .explain_execution_json_with_visible_indexes(visible_indexes)
937 }
938
939 #[inline(never)]
941 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
942 where
943 E: EntityValue,
944 {
945 self.inner.explain_execution_verbose()
946 }
947
948 #[cfg(test)]
950 #[inline(never)]
951 pub(in crate::db) fn explain_aggregate_terminal(
952 &self,
953 aggregate: AggregateExpr,
954 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
955 where
956 E: EntityValue,
957 {
958 self.inner.explain_aggregate_terminal_with_visible_indexes(
959 &VisibleIndexes::schema_owned(E::MODEL.indexes()),
960 AggregateRouteShape::new_from_fields(
961 aggregate.kind(),
962 aggregate.target_field(),
963 E::MODEL.fields(),
964 E::MODEL.primary_key().name(),
965 ),
966 )
967 }
968
969 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
970 &self,
971 visible_indexes: &VisibleIndexes<'_>,
972 ) -> Result<String, QueryError>
973 where
974 E: EntityValue,
975 {
976 self.inner
977 .explain_execution_verbose_with_visible_indexes(visible_indexes)
978 }
979
980 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
981 &self,
982 visible_indexes: &VisibleIndexes<'_>,
983 strategy: &S,
984 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
985 where
986 E: EntityValue,
987 S: PreparedFluentAggregateExplainStrategy,
988 {
989 self.inner
990 .explain_prepared_aggregate_terminal_with_visible_indexes(visible_indexes, strategy)
991 }
992
993 pub(in crate::db) fn explain_bytes_by_with_visible_indexes(
994 &self,
995 visible_indexes: &VisibleIndexes<'_>,
996 target_field: &str,
997 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
998 where
999 E: EntityValue,
1000 {
1001 let executable = self
1002 .plan_with_visible_indexes(visible_indexes)?
1003 .into_executable();
1004 let mut descriptor = executable
1005 .explain_load_execution_node_descriptor()
1006 .map_err(QueryError::execute)?;
1007 let projection_mode = executable.bytes_by_projection_mode(target_field);
1008 let projection_mode_label =
1009 ExecutablePlan::<E>::bytes_by_projection_mode_label(projection_mode);
1010
1011 descriptor
1012 .node_properties
1013 .insert("terminal", Value::from("bytes_by"));
1014 descriptor
1015 .node_properties
1016 .insert("terminal_field", Value::from(target_field.to_string()));
1017 descriptor.node_properties.insert(
1018 "terminal_projection_mode",
1019 Value::from(projection_mode_label),
1020 );
1021 descriptor.node_properties.insert(
1022 "terminal_index_only",
1023 Value::from(matches!(
1024 projection_mode,
1025 BytesByProjectionMode::CoveringIndex | BytesByProjectionMode::CoveringConstant
1026 )),
1027 );
1028
1029 Ok(descriptor)
1030 }
1031
1032 pub(in crate::db) fn explain_prepared_projection_terminal_with_visible_indexes(
1033 &self,
1034 visible_indexes: &VisibleIndexes<'_>,
1035 strategy: &PreparedFluentProjectionStrategy,
1036 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1037 where
1038 E: EntityValue,
1039 {
1040 let executable = self
1041 .plan_with_visible_indexes(visible_indexes)?
1042 .into_executable();
1043 let mut descriptor = executable
1044 .explain_load_execution_node_descriptor()
1045 .map_err(QueryError::execute)?;
1046 let projection_descriptor = strategy.explain_descriptor();
1047
1048 descriptor.node_properties.insert(
1049 "terminal",
1050 Value::from(projection_descriptor.terminal_label()),
1051 );
1052 descriptor.node_properties.insert(
1053 "terminal_field",
1054 Value::from(projection_descriptor.field_label().to_string()),
1055 );
1056 descriptor.node_properties.insert(
1057 "terminal_output",
1058 Value::from(projection_descriptor.output_label()),
1059 );
1060
1061 Ok(descriptor)
1062 }
1063
1064 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
1066 let plan = self.build_plan_for_visibility(None)?;
1067
1068 Ok(Self::planned_query_from_plan(plan))
1069 }
1070
1071 pub(in crate::db) fn planned_with_visible_indexes(
1072 &self,
1073 visible_indexes: &VisibleIndexes<'_>,
1074 ) -> Result<PlannedQuery<E>, QueryError> {
1075 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
1076
1077 Ok(Self::planned_query_from_plan(plan))
1078 }
1079
1080 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
1084 let plan = self.build_plan_for_visibility(None)?;
1085
1086 Ok(Self::compiled_query_from_plan(plan))
1087 }
1088
1089 pub(in crate::db) fn plan_with_visible_indexes(
1090 &self,
1091 visible_indexes: &VisibleIndexes<'_>,
1092 ) -> Result<CompiledQuery<E>, QueryError> {
1093 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
1094
1095 Ok(Self::compiled_query_from_plan(plan))
1096 }
1097}
1098
1099fn contains_execution_node_type(
1100 descriptor: &ExplainExecutionNodeDescriptor,
1101 target: ExplainExecutionNodeType,
1102) -> bool {
1103 descriptor.node_type() == target
1104 || descriptor
1105 .children()
1106 .iter()
1107 .any(|child| contains_execution_node_type(child, target))
1108}
1109
1110fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
1111 match order_pushdown {
1112 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
1113 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
1114 format!("eligible(index={index},prefix_len={prefix_len})",)
1115 }
1116 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
1117 }
1118}
1119
1120fn plan_predicate_pushdown_label(
1121 predicate: &ExplainPredicate,
1122 access: &ExplainAccessPath,
1123) -> String {
1124 let access_label = match access {
1125 ExplainAccessPath::ByKey { .. } => "by_key",
1126 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
1127 ExplainAccessPath::ByKeys { .. } => "by_keys",
1128 ExplainAccessPath::KeyRange { .. } => "key_range",
1129 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
1130 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
1131 ExplainAccessPath::IndexRange { .. } => "index_range",
1132 ExplainAccessPath::FullScan => "full_scan",
1133 ExplainAccessPath::Union(_) => "union",
1134 ExplainAccessPath::Intersection(_) => "intersection",
1135 };
1136 if matches!(predicate, ExplainPredicate::None) {
1137 return "none".to_string();
1138 }
1139 if matches!(access, ExplainAccessPath::FullScan) {
1140 if explain_predicate_contains_non_strict_compare(predicate) {
1141 return "fallback(non_strict_compare_coercion)".to_string();
1142 }
1143 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
1144 return "fallback(starts_with_empty_prefix)".to_string();
1145 }
1146 if explain_predicate_contains_is_null(predicate) {
1147 return "fallback(is_null_full_scan)".to_string();
1148 }
1149 if explain_predicate_contains_text_scan_operator(predicate) {
1150 return "fallback(text_operator_full_scan)".to_string();
1151 }
1152
1153 return format!("fallback({access_label})");
1154 }
1155
1156 format!("applied({access_label})")
1157}
1158
1159fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
1160 match predicate {
1161 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
1162 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1163 .iter()
1164 .any(explain_predicate_contains_non_strict_compare),
1165 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
1166 ExplainPredicate::None
1167 | ExplainPredicate::True
1168 | ExplainPredicate::False
1169 | ExplainPredicate::IsNull { .. }
1170 | ExplainPredicate::IsNotNull { .. }
1171 | ExplainPredicate::IsMissing { .. }
1172 | ExplainPredicate::IsEmpty { .. }
1173 | ExplainPredicate::IsNotEmpty { .. }
1174 | ExplainPredicate::TextContains { .. }
1175 | ExplainPredicate::TextContainsCi { .. } => false,
1176 }
1177}
1178
1179fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
1180 match predicate {
1181 ExplainPredicate::IsNull { .. } => true,
1182 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
1183 children.iter().any(explain_predicate_contains_is_null)
1184 }
1185 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
1186 ExplainPredicate::None
1187 | ExplainPredicate::True
1188 | ExplainPredicate::False
1189 | ExplainPredicate::Compare { .. }
1190 | ExplainPredicate::IsNotNull { .. }
1191 | ExplainPredicate::IsMissing { .. }
1192 | ExplainPredicate::IsEmpty { .. }
1193 | ExplainPredicate::IsNotEmpty { .. }
1194 | ExplainPredicate::TextContains { .. }
1195 | ExplainPredicate::TextContainsCi { .. } => false,
1196 }
1197}
1198
1199fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
1200 match predicate {
1201 ExplainPredicate::Compare {
1202 op: CompareOp::StartsWith,
1203 value: Value::Text(prefix),
1204 ..
1205 } => prefix.is_empty(),
1206 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1207 .iter()
1208 .any(explain_predicate_contains_empty_prefix_starts_with),
1209 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
1210 ExplainPredicate::None
1211 | ExplainPredicate::True
1212 | ExplainPredicate::False
1213 | ExplainPredicate::Compare { .. }
1214 | ExplainPredicate::IsNull { .. }
1215 | ExplainPredicate::IsNotNull { .. }
1216 | ExplainPredicate::IsMissing { .. }
1217 | ExplainPredicate::IsEmpty { .. }
1218 | ExplainPredicate::IsNotEmpty { .. }
1219 | ExplainPredicate::TextContains { .. }
1220 | ExplainPredicate::TextContainsCi { .. } => false,
1221 }
1222}
1223
1224fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
1225 match predicate {
1226 ExplainPredicate::Compare {
1227 op: CompareOp::EndsWith,
1228 ..
1229 }
1230 | ExplainPredicate::TextContains { .. }
1231 | ExplainPredicate::TextContainsCi { .. } => true,
1232 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1233 .iter()
1234 .any(explain_predicate_contains_text_scan_operator),
1235 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
1236 ExplainPredicate::Compare { .. }
1237 | ExplainPredicate::None
1238 | ExplainPredicate::True
1239 | ExplainPredicate::False
1240 | ExplainPredicate::IsNull { .. }
1241 | ExplainPredicate::IsNotNull { .. }
1242 | ExplainPredicate::IsMissing { .. }
1243 | ExplainPredicate::IsEmpty { .. }
1244 | ExplainPredicate::IsNotEmpty { .. } => false,
1245 }
1246}
1247
1248impl<E> Query<E>
1249where
1250 E: EntityKind + SingletonEntity,
1251 E::Key: Default,
1252{
1253 pub(crate) fn only(self) -> Self {
1255 let Self { inner, .. } = self;
1256
1257 Self::from_inner(inner.only(E::Key::default().to_value()))
1258 }
1259}