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