1use crate::{
7 db::{
8 executor::{
9 BytesByProjectionMode, PreparedExecutionPlan,
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_prepared_execution_plan(
601 self,
602 ) -> crate::db::executor::PreparedExecutionPlan<E> {
603 assert!(
604 self.inner.entity_path == E::PATH,
605 "compiled query entity mismatch: compiled for '{}', requested '{}'",
606 self.inner.entity_path,
607 E::PATH,
608 );
609
610 crate::db::executor::PreparedExecutionPlan::new(self.into_inner())
611 }
612
613 #[must_use]
614 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
615 self.inner.into_inner()
616 }
617}
618
619#[derive(Debug)]
631pub struct Query<E: EntityKind> {
632 inner: StructuralQuery,
633 _marker: PhantomData<E>,
634}
635
636impl<E: EntityKind> Query<E> {
637 pub(in crate::db) const fn from_inner(inner: StructuralQuery) -> Self {
639 Self {
640 inner,
641 _marker: PhantomData,
642 }
643 }
644
645 #[must_use]
649 pub const fn new(consistency: MissingRowPolicy) -> Self {
650 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
651 }
652
653 #[must_use]
655 pub const fn mode(&self) -> QueryMode {
656 self.inner.mode()
657 }
658
659 pub(in crate::db) fn explain_with_visible_indexes(
660 &self,
661 visible_indexes: &VisibleIndexes<'_>,
662 ) -> Result<ExplainPlan, QueryError> {
663 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
664
665 Ok(plan.explain_with_model(E::MODEL))
666 }
667
668 pub(in crate::db) fn plan_hash_hex_with_visible_indexes(
669 &self,
670 visible_indexes: &VisibleIndexes<'_>,
671 ) -> Result<String, QueryError> {
672 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
673
674 Ok(plan.fingerprint().to_string())
675 }
676
677 fn build_plan_for_visibility(
680 &self,
681 visible_indexes: Option<&VisibleIndexes<'_>>,
682 ) -> Result<AccessPlannedQuery, QueryError> {
683 self.inner.build_plan_for_visibility(visible_indexes)
684 }
685
686 fn planned_query_from_plan(plan: AccessPlannedQuery) -> PlannedQuery<E> {
688 let _projection = plan.projection_spec(E::MODEL);
689
690 PlannedQuery::from_inner(PlannedQueryCore::new(E::MODEL, plan))
691 }
692
693 fn compiled_query_from_plan(plan: AccessPlannedQuery) -> CompiledQuery<E> {
695 let _projection = plan.projection_spec(E::MODEL);
696
697 CompiledQuery::from_inner(CompiledQueryCore::new(E::MODEL, E::PATH, plan))
698 }
699
700 #[must_use]
701 pub(crate) fn has_explicit_order(&self) -> bool {
702 self.inner.has_explicit_order()
703 }
704
705 #[must_use]
706 pub(crate) const fn has_grouping(&self) -> bool {
707 self.inner.has_grouping()
708 }
709
710 #[must_use]
711 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
712 self.inner.load_spec()
713 }
714
715 #[must_use]
717 pub fn filter(mut self, predicate: Predicate) -> Self {
718 self.inner = self.inner.filter(predicate);
719 self
720 }
721
722 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
724 let Self { inner, .. } = self;
725 let inner = inner.filter_expr(expr)?;
726
727 Ok(Self::from_inner(inner))
728 }
729
730 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
732 let Self { inner, .. } = self;
733 let inner = inner.sort_expr(expr)?;
734
735 Ok(Self::from_inner(inner))
736 }
737
738 #[must_use]
740 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
741 self.inner = self.inner.order_by(field);
742 self
743 }
744
745 #[must_use]
747 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
748 self.inner = self.inner.order_by_desc(field);
749 self
750 }
751
752 #[must_use]
754 pub fn distinct(mut self) -> Self {
755 self.inner = self.inner.distinct();
756 self
757 }
758
759 #[cfg(all(test, feature = "sql"))]
762 #[must_use]
763 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
764 where
765 I: IntoIterator<Item = S>,
766 S: Into<String>,
767 {
768 self.inner = self.inner.select_fields(fields);
769 self
770 }
771
772 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
774 let Self { inner, .. } = self;
775 let inner = inner.group_by(field)?;
776
777 Ok(Self::from_inner(inner))
778 }
779
780 #[must_use]
782 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
783 self.inner = self.inner.aggregate(aggregate);
784 self
785 }
786
787 #[must_use]
789 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
790 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
791 self
792 }
793
794 pub fn having_group(
796 self,
797 field: impl AsRef<str>,
798 op: CompareOp,
799 value: Value,
800 ) -> Result<Self, QueryError> {
801 let Self { inner, .. } = self;
802 let inner = inner.having_group(field, op, value)?;
803
804 Ok(Self::from_inner(inner))
805 }
806
807 pub fn having_aggregate(
809 self,
810 aggregate_index: usize,
811 op: CompareOp,
812 value: Value,
813 ) -> Result<Self, QueryError> {
814 let Self { inner, .. } = self;
815 let inner = inner.having_aggregate(aggregate_index, op, value)?;
816
817 Ok(Self::from_inner(inner))
818 }
819
820 pub(crate) fn by_id(self, id: E::Key) -> Self {
822 let Self { inner, .. } = self;
823
824 Self::from_inner(inner.by_id(id.to_value()))
825 }
826
827 pub(crate) fn by_ids<I>(self, ids: I) -> Self
829 where
830 I: IntoIterator<Item = E::Key>,
831 {
832 let Self { inner, .. } = self;
833
834 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
835 }
836
837 #[must_use]
839 pub fn delete(mut self) -> Self {
840 self.inner = self.inner.delete();
841 self
842 }
843
844 #[must_use]
851 pub fn limit(mut self, limit: u32) -> Self {
852 self.inner = self.inner.limit(limit);
853 self
854 }
855
856 #[must_use]
862 pub fn offset(mut self, offset: u32) -> Self {
863 self.inner = self.inner.offset(offset);
864 self
865 }
866
867 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
869 let plan = self.planned()?;
870
871 Ok(plan.explain())
872 }
873
874 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
879 let plan = self.inner.build_plan()?;
880
881 Ok(plan.fingerprint().to_string())
882 }
883
884 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
886 where
887 E: EntityValue,
888 {
889 self.inner.explain_execution()
890 }
891
892 pub(in crate::db) fn explain_execution_with_visible_indexes(
893 &self,
894 visible_indexes: &VisibleIndexes<'_>,
895 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
896 where
897 E: EntityValue,
898 {
899 self.inner
900 .explain_execution_with_visible_indexes(visible_indexes)
901 }
902
903 pub fn explain_execution_text(&self) -> Result<String, QueryError>
905 where
906 E: EntityValue,
907 {
908 self.inner.explain_execution_text()
909 }
910
911 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
912 &self,
913 visible_indexes: &VisibleIndexes<'_>,
914 ) -> Result<String, QueryError>
915 where
916 E: EntityValue,
917 {
918 self.inner
919 .explain_execution_text_with_visible_indexes(visible_indexes)
920 }
921
922 pub fn explain_execution_json(&self) -> Result<String, QueryError>
924 where
925 E: EntityValue,
926 {
927 self.inner.explain_execution_json()
928 }
929
930 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
931 &self,
932 visible_indexes: &VisibleIndexes<'_>,
933 ) -> Result<String, QueryError>
934 where
935 E: EntityValue,
936 {
937 self.inner
938 .explain_execution_json_with_visible_indexes(visible_indexes)
939 }
940
941 #[inline(never)]
943 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
944 where
945 E: EntityValue,
946 {
947 self.inner.explain_execution_verbose()
948 }
949
950 #[cfg(test)]
952 #[inline(never)]
953 pub(in crate::db) fn explain_aggregate_terminal(
954 &self,
955 aggregate: AggregateExpr,
956 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
957 where
958 E: EntityValue,
959 {
960 self.inner.explain_aggregate_terminal_with_visible_indexes(
961 &VisibleIndexes::schema_owned(E::MODEL.indexes()),
962 AggregateRouteShape::new_from_fields(
963 aggregate.kind(),
964 aggregate.target_field(),
965 E::MODEL.fields(),
966 E::MODEL.primary_key().name(),
967 ),
968 )
969 }
970
971 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
972 &self,
973 visible_indexes: &VisibleIndexes<'_>,
974 ) -> Result<String, QueryError>
975 where
976 E: EntityValue,
977 {
978 self.inner
979 .explain_execution_verbose_with_visible_indexes(visible_indexes)
980 }
981
982 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
983 &self,
984 visible_indexes: &VisibleIndexes<'_>,
985 strategy: &S,
986 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
987 where
988 E: EntityValue,
989 S: PreparedFluentAggregateExplainStrategy,
990 {
991 self.inner
992 .explain_prepared_aggregate_terminal_with_visible_indexes(visible_indexes, strategy)
993 }
994
995 pub(in crate::db) fn explain_bytes_by_with_visible_indexes(
996 &self,
997 visible_indexes: &VisibleIndexes<'_>,
998 target_field: &str,
999 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1000 where
1001 E: EntityValue,
1002 {
1003 let executable = self
1004 .plan_with_visible_indexes(visible_indexes)?
1005 .into_prepared_execution_plan();
1006 let mut descriptor = executable
1007 .explain_load_execution_node_descriptor()
1008 .map_err(QueryError::execute)?;
1009 let projection_mode = executable.bytes_by_projection_mode(target_field);
1010 let projection_mode_label =
1011 PreparedExecutionPlan::<E>::bytes_by_projection_mode_label(projection_mode);
1012
1013 descriptor
1014 .node_properties
1015 .insert("terminal", Value::from("bytes_by"));
1016 descriptor
1017 .node_properties
1018 .insert("terminal_field", Value::from(target_field.to_string()));
1019 descriptor.node_properties.insert(
1020 "terminal_projection_mode",
1021 Value::from(projection_mode_label),
1022 );
1023 descriptor.node_properties.insert(
1024 "terminal_index_only",
1025 Value::from(matches!(
1026 projection_mode,
1027 BytesByProjectionMode::CoveringIndex | BytesByProjectionMode::CoveringConstant
1028 )),
1029 );
1030
1031 Ok(descriptor)
1032 }
1033
1034 pub(in crate::db) fn explain_prepared_projection_terminal_with_visible_indexes(
1035 &self,
1036 visible_indexes: &VisibleIndexes<'_>,
1037 strategy: &PreparedFluentProjectionStrategy,
1038 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1039 where
1040 E: EntityValue,
1041 {
1042 let executable = self
1043 .plan_with_visible_indexes(visible_indexes)?
1044 .into_prepared_execution_plan();
1045 let mut descriptor = executable
1046 .explain_load_execution_node_descriptor()
1047 .map_err(QueryError::execute)?;
1048 let projection_descriptor = strategy.explain_descriptor();
1049
1050 descriptor.node_properties.insert(
1051 "terminal",
1052 Value::from(projection_descriptor.terminal_label()),
1053 );
1054 descriptor.node_properties.insert(
1055 "terminal_field",
1056 Value::from(projection_descriptor.field_label().to_string()),
1057 );
1058 descriptor.node_properties.insert(
1059 "terminal_output",
1060 Value::from(projection_descriptor.output_label()),
1061 );
1062
1063 Ok(descriptor)
1064 }
1065
1066 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
1068 let plan = self.build_plan_for_visibility(None)?;
1069
1070 Ok(Self::planned_query_from_plan(plan))
1071 }
1072
1073 pub(in crate::db) fn planned_with_visible_indexes(
1074 &self,
1075 visible_indexes: &VisibleIndexes<'_>,
1076 ) -> Result<PlannedQuery<E>, QueryError> {
1077 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
1078
1079 Ok(Self::planned_query_from_plan(plan))
1080 }
1081
1082 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
1086 let plan = self.build_plan_for_visibility(None)?;
1087
1088 Ok(Self::compiled_query_from_plan(plan))
1089 }
1090
1091 pub(in crate::db) fn plan_with_visible_indexes(
1092 &self,
1093 visible_indexes: &VisibleIndexes<'_>,
1094 ) -> Result<CompiledQuery<E>, QueryError> {
1095 let plan = self.build_plan_for_visibility(Some(visible_indexes))?;
1096
1097 Ok(Self::compiled_query_from_plan(plan))
1098 }
1099}
1100
1101fn contains_execution_node_type(
1102 descriptor: &ExplainExecutionNodeDescriptor,
1103 target: ExplainExecutionNodeType,
1104) -> bool {
1105 descriptor.node_type() == target
1106 || descriptor
1107 .children()
1108 .iter()
1109 .any(|child| contains_execution_node_type(child, target))
1110}
1111
1112fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
1113 match order_pushdown {
1114 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
1115 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
1116 format!("eligible(index={index},prefix_len={prefix_len})",)
1117 }
1118 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
1119 }
1120}
1121
1122fn plan_predicate_pushdown_label(
1123 predicate: &ExplainPredicate,
1124 access: &ExplainAccessPath,
1125) -> String {
1126 let access_label = match access {
1127 ExplainAccessPath::ByKey { .. } => "by_key",
1128 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
1129 ExplainAccessPath::ByKeys { .. } => "by_keys",
1130 ExplainAccessPath::KeyRange { .. } => "key_range",
1131 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
1132 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
1133 ExplainAccessPath::IndexRange { .. } => "index_range",
1134 ExplainAccessPath::FullScan => "full_scan",
1135 ExplainAccessPath::Union(_) => "union",
1136 ExplainAccessPath::Intersection(_) => "intersection",
1137 };
1138 if matches!(predicate, ExplainPredicate::None) {
1139 return "none".to_string();
1140 }
1141 if matches!(access, ExplainAccessPath::FullScan) {
1142 if explain_predicate_contains_non_strict_compare(predicate) {
1143 return "fallback(non_strict_compare_coercion)".to_string();
1144 }
1145 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
1146 return "fallback(starts_with_empty_prefix)".to_string();
1147 }
1148 if explain_predicate_contains_is_null(predicate) {
1149 return "fallback(is_null_full_scan)".to_string();
1150 }
1151 if explain_predicate_contains_text_scan_operator(predicate) {
1152 return "fallback(text_operator_full_scan)".to_string();
1153 }
1154
1155 return format!("fallback({access_label})");
1156 }
1157
1158 format!("applied({access_label})")
1159}
1160
1161fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
1162 match predicate {
1163 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
1164 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1165 .iter()
1166 .any(explain_predicate_contains_non_strict_compare),
1167 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
1168 ExplainPredicate::None
1169 | ExplainPredicate::True
1170 | ExplainPredicate::False
1171 | ExplainPredicate::IsNull { .. }
1172 | ExplainPredicate::IsNotNull { .. }
1173 | ExplainPredicate::IsMissing { .. }
1174 | ExplainPredicate::IsEmpty { .. }
1175 | ExplainPredicate::IsNotEmpty { .. }
1176 | ExplainPredicate::TextContains { .. }
1177 | ExplainPredicate::TextContainsCi { .. } => false,
1178 }
1179}
1180
1181fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
1182 match predicate {
1183 ExplainPredicate::IsNull { .. } => true,
1184 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
1185 children.iter().any(explain_predicate_contains_is_null)
1186 }
1187 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
1188 ExplainPredicate::None
1189 | ExplainPredicate::True
1190 | ExplainPredicate::False
1191 | ExplainPredicate::Compare { .. }
1192 | ExplainPredicate::IsNotNull { .. }
1193 | ExplainPredicate::IsMissing { .. }
1194 | ExplainPredicate::IsEmpty { .. }
1195 | ExplainPredicate::IsNotEmpty { .. }
1196 | ExplainPredicate::TextContains { .. }
1197 | ExplainPredicate::TextContainsCi { .. } => false,
1198 }
1199}
1200
1201fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
1202 match predicate {
1203 ExplainPredicate::Compare {
1204 op: CompareOp::StartsWith,
1205 value: Value::Text(prefix),
1206 ..
1207 } => prefix.is_empty(),
1208 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1209 .iter()
1210 .any(explain_predicate_contains_empty_prefix_starts_with),
1211 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
1212 ExplainPredicate::None
1213 | ExplainPredicate::True
1214 | ExplainPredicate::False
1215 | ExplainPredicate::Compare { .. }
1216 | ExplainPredicate::IsNull { .. }
1217 | ExplainPredicate::IsNotNull { .. }
1218 | ExplainPredicate::IsMissing { .. }
1219 | ExplainPredicate::IsEmpty { .. }
1220 | ExplainPredicate::IsNotEmpty { .. }
1221 | ExplainPredicate::TextContains { .. }
1222 | ExplainPredicate::TextContainsCi { .. } => false,
1223 }
1224}
1225
1226fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
1227 match predicate {
1228 ExplainPredicate::Compare {
1229 op: CompareOp::EndsWith,
1230 ..
1231 }
1232 | ExplainPredicate::TextContains { .. }
1233 | ExplainPredicate::TextContainsCi { .. } => true,
1234 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1235 .iter()
1236 .any(explain_predicate_contains_text_scan_operator),
1237 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
1238 ExplainPredicate::Compare { .. }
1239 | ExplainPredicate::None
1240 | ExplainPredicate::True
1241 | ExplainPredicate::False
1242 | ExplainPredicate::IsNull { .. }
1243 | ExplainPredicate::IsNotNull { .. }
1244 | ExplainPredicate::IsMissing { .. }
1245 | ExplainPredicate::IsEmpty { .. }
1246 | ExplainPredicate::IsNotEmpty { .. } => false,
1247 }
1248}
1249
1250impl<E> Query<E>
1251where
1252 E: EntityKind + SingletonEntity,
1253 E::Key: Default,
1254{
1255 pub(crate) fn only(self) -> Self {
1257 let Self { inner, .. } = self;
1258
1259 Self::from_inner(inner.only(E::Key::default().to_value()))
1260 }
1261}