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 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 #[cfg(feature = "sql")]
232 #[must_use]
233 pub(in crate::db) const fn model(&self) -> &'static crate::model::entity::EntityModel {
234 self.intent.model()
235 }
236
237 #[inline(never)]
238 pub(in crate::db) fn explain_execution_with_visible_indexes(
239 &self,
240 visible_indexes: &VisibleIndexes<'_>,
241 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
242 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
243
244 assemble_load_execution_node_descriptor(
245 self.intent.model().fields(),
246 self.intent.model().primary_key().name(),
247 &plan,
248 )
249 .map_err(QueryError::execute)
250 }
251
252 #[inline(never)]
254 pub(in crate::db) fn explain_execution(
255 &self,
256 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
257 let plan = self.build_plan()?;
258
259 assemble_load_execution_node_descriptor(
260 self.intent.model().fields(),
261 self.intent.model().primary_key().name(),
262 &plan,
263 )
264 .map_err(QueryError::execute)
265 }
266
267 pub(in crate::db) fn explain_execution_text(&self) -> Result<String, QueryError> {
270 Ok(self.explain_execution()?.render_text_tree())
271 }
272
273 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
274 &self,
275 visible_indexes: &VisibleIndexes<'_>,
276 ) -> Result<String, QueryError> {
277 Ok(self
278 .explain_execution_with_visible_indexes(visible_indexes)?
279 .render_text_tree())
280 }
281
282 pub(in crate::db) fn explain_execution_json(&self) -> Result<String, QueryError> {
285 Ok(self.explain_execution()?.render_json_canonical())
286 }
287
288 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
289 &self,
290 visible_indexes: &VisibleIndexes<'_>,
291 ) -> Result<String, QueryError> {
292 Ok(self
293 .explain_execution_with_visible_indexes(visible_indexes)?
294 .render_json_canonical())
295 }
296
297 #[inline(never)]
300 pub(in crate::db) fn explain_execution_verbose(&self) -> Result<String, QueryError> {
301 let plan = self.build_plan()?;
302 let descriptor = assemble_load_execution_node_descriptor(
303 self.intent.model().fields(),
304 self.intent.model().primary_key().name(),
305 &plan,
306 )
307 .map_err(QueryError::execute)?;
308 let route_diagnostics = assemble_load_execution_verbose_diagnostics(
309 self.intent.model().fields(),
310 self.intent.model().primary_key().name(),
311 &plan,
312 )
313 .map_err(QueryError::execute)?;
314 let explain = plan.explain_with_model(self.intent.model());
315
316 let mut lines = vec![descriptor.render_text_tree_verbose()];
318 lines.extend(route_diagnostics);
319
320 lines.push(format!(
322 "diag.d.has_top_n_seek={}",
323 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
324 ));
325 lines.push(format!(
326 "diag.d.has_index_range_limit_pushdown={}",
327 contains_execution_node_type(
328 &descriptor,
329 ExplainExecutionNodeType::IndexRangeLimitPushdown,
330 )
331 ));
332 lines.push(format!(
333 "diag.d.has_index_predicate_prefilter={}",
334 contains_execution_node_type(
335 &descriptor,
336 ExplainExecutionNodeType::IndexPredicatePrefilter,
337 )
338 ));
339 lines.push(format!(
340 "diag.d.has_residual_predicate_filter={}",
341 contains_execution_node_type(
342 &descriptor,
343 ExplainExecutionNodeType::ResidualPredicateFilter,
344 )
345 ));
346
347 lines.push(format!("diag.p.mode={:?}", explain.mode()));
349 lines.push(format!(
350 "diag.p.order_pushdown={}",
351 plan_order_pushdown_label(explain.order_pushdown())
352 ));
353 lines.push(format!(
354 "diag.p.predicate_pushdown={}",
355 plan_predicate_pushdown_label(explain.predicate(), explain.access())
356 ));
357 lines.push(format!("diag.p.distinct={}", explain.distinct()));
358 lines.push(format!("diag.p.page={:?}", explain.page()));
359 lines.push(format!("diag.p.consistency={:?}", explain.consistency()));
360
361 Ok(lines.join("\n"))
362 }
363
364 #[inline(never)]
365 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
366 &self,
367 visible_indexes: &VisibleIndexes<'_>,
368 ) -> Result<String, QueryError> {
369 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
370 let descriptor = assemble_load_execution_node_descriptor(
371 self.intent.model().fields(),
372 self.intent.model().primary_key().name(),
373 &plan,
374 )
375 .map_err(QueryError::execute)?;
376 let route_diagnostics = assemble_load_execution_verbose_diagnostics(
377 self.intent.model().fields(),
378 self.intent.model().primary_key().name(),
379 &plan,
380 )
381 .map_err(QueryError::execute)?;
382 let explain = plan.explain_with_model(self.intent.model());
383
384 let mut lines = vec![descriptor.render_text_tree_verbose()];
386 lines.extend(route_diagnostics);
387
388 lines.push(format!(
390 "diag.d.has_top_n_seek={}",
391 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
392 ));
393 lines.push(format!(
394 "diag.d.has_index_range_limit_pushdown={}",
395 contains_execution_node_type(
396 &descriptor,
397 ExplainExecutionNodeType::IndexRangeLimitPushdown,
398 )
399 ));
400 lines.push(format!(
401 "diag.d.has_index_predicate_prefilter={}",
402 contains_execution_node_type(
403 &descriptor,
404 ExplainExecutionNodeType::IndexPredicatePrefilter,
405 )
406 ));
407 lines.push(format!(
408 "diag.d.has_residual_predicate_filter={}",
409 contains_execution_node_type(
410 &descriptor,
411 ExplainExecutionNodeType::ResidualPredicateFilter,
412 )
413 ));
414
415 lines.push(format!("diag.p.mode={:?}", explain.mode()));
417 lines.push(format!(
418 "diag.p.order_pushdown={}",
419 plan_order_pushdown_label(explain.order_pushdown())
420 ));
421 lines.push(format!(
422 "diag.p.predicate_pushdown={}",
423 plan_predicate_pushdown_label(explain.predicate(), explain.access())
424 ));
425 lines.push(format!("diag.p.distinct={}", explain.distinct()));
426 lines.push(format!("diag.p.page={:?}", explain.page()));
427 lines.push(format!("diag.p.consistency={:?}", explain.consistency()));
428
429 Ok(lines.join("\n"))
430 }
431
432 #[inline(never)]
433 pub(in crate::db) fn explain_aggregate_terminal_with_visible_indexes(
434 &self,
435 visible_indexes: &VisibleIndexes<'_>,
436 aggregate: AggregateRouteShape<'_>,
437 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
438 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
439 let query_explain = plan.explain_with_model(self.intent.model());
440 let terminal = aggregate.kind();
441 let execution = assemble_aggregate_terminal_execution_descriptor(&plan, aggregate);
442
443 Ok(ExplainAggregateTerminalPlan::new(
444 query_explain,
445 terminal,
446 execution,
447 ))
448 }
449
450 #[inline(never)]
451 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
452 &self,
453 visible_indexes: &VisibleIndexes<'_>,
454 strategy: &S,
455 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
456 where
457 S: PreparedFluentAggregateExplainStrategy,
458 {
459 let Some(kind) = strategy.explain_aggregate_kind() else {
460 return Err(QueryError::invariant(
461 "prepared fluent aggregate explain requires an explain-visible aggregate kind",
462 ));
463 };
464 let aggregate = AggregateRouteShape::new_from_fields(
465 kind,
466 strategy.explain_projected_field(),
467 self.intent.model().fields(),
468 self.intent.model().primary_key().name(),
469 );
470
471 self.explain_aggregate_terminal_with_visible_indexes(visible_indexes, aggregate)
472 }
473}
474
475#[derive(Debug)]
484struct PlannedQueryCore {
485 model: &'static crate::model::entity::EntityModel,
486 plan: AccessPlannedQuery,
487}
488
489impl PlannedQueryCore {
490 #[must_use]
491 const fn new(
492 model: &'static crate::model::entity::EntityModel,
493 plan: AccessPlannedQuery,
494 ) -> Self {
495 Self { model, plan }
496 }
497
498 #[must_use]
499 fn explain(&self) -> ExplainPlan {
500 self.plan.explain_with_model(self.model)
501 }
502
503 #[must_use]
505 fn plan_hash_hex(&self) -> String {
506 self.plan.fingerprint().to_string()
507 }
508}
509
510#[derive(Debug)]
519pub struct PlannedQuery<E: EntityKind> {
520 inner: PlannedQueryCore,
521 _marker: PhantomData<E>,
522}
523
524impl<E: EntityKind> PlannedQuery<E> {
525 #[must_use]
526 const fn from_inner(inner: PlannedQueryCore) -> Self {
527 Self {
528 inner,
529 _marker: PhantomData,
530 }
531 }
532
533 #[must_use]
534 pub fn explain(&self) -> ExplainPlan {
535 self.inner.explain()
536 }
537
538 #[must_use]
540 pub fn plan_hash_hex(&self) -> String {
541 self.inner.plan_hash_hex()
542 }
543}
544
545#[derive(Clone, Debug)]
554struct CompiledQueryCore {
555 model: &'static crate::model::entity::EntityModel,
556 entity_path: &'static str,
557 plan: AccessPlannedQuery,
558}
559
560impl CompiledQueryCore {
561 #[must_use]
562 const fn new(
563 model: &'static crate::model::entity::EntityModel,
564 entity_path: &'static str,
565 plan: AccessPlannedQuery,
566 ) -> Self {
567 Self {
568 model,
569 entity_path,
570 plan,
571 }
572 }
573
574 #[must_use]
575 fn explain(&self) -> ExplainPlan {
576 self.plan.explain_with_model(self.model)
577 }
578
579 #[must_use]
581 fn plan_hash_hex(&self) -> String {
582 self.plan.fingerprint().to_string()
583 }
584
585 #[must_use]
586 #[cfg(test)]
587 fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
588 self.plan.projection_spec(self.model)
589 }
590
591 #[must_use]
592 fn into_inner(self) -> AccessPlannedQuery {
593 self.plan
594 }
595}
596
597#[derive(Clone, Debug)]
606pub struct CompiledQuery<E: EntityKind> {
607 inner: CompiledQueryCore,
608 _marker: PhantomData<E>,
609}
610
611impl<E: EntityKind> CompiledQuery<E> {
612 #[must_use]
613 const fn from_inner(inner: CompiledQueryCore) -> Self {
614 Self {
615 inner,
616 _marker: PhantomData,
617 }
618 }
619
620 #[must_use]
621 pub fn explain(&self) -> ExplainPlan {
622 self.inner.explain()
623 }
624
625 #[must_use]
627 pub fn plan_hash_hex(&self) -> String {
628 self.inner.plan_hash_hex()
629 }
630
631 #[must_use]
632 #[cfg(test)]
633 pub(in crate::db) fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
634 self.inner.projection_spec()
635 }
636
637 pub(in crate::db) fn into_executable(self) -> crate::db::executor::ExecutablePlan<E> {
639 assert!(
640 self.inner.entity_path == E::PATH,
641 "compiled query entity mismatch: compiled for '{}', requested '{}'",
642 self.inner.entity_path,
643 E::PATH,
644 );
645
646 crate::db::executor::ExecutablePlan::new(self.into_inner())
647 }
648
649 #[must_use]
650 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
651 self.inner.into_inner()
652 }
653}
654
655#[derive(Debug)]
667pub struct Query<E: EntityKind> {
668 inner: StructuralQuery,
669 _marker: PhantomData<E>,
670}
671
672impl<E: EntityKind> Query<E> {
673 pub(in crate::db) const fn from_inner(inner: StructuralQuery) -> Self {
675 Self {
676 inner,
677 _marker: PhantomData,
678 }
679 }
680
681 #[must_use]
685 pub const fn new(consistency: MissingRowPolicy) -> Self {
686 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
687 }
688
689 #[must_use]
691 pub const fn mode(&self) -> QueryMode {
692 self.inner.mode()
693 }
694
695 pub(in crate::db) fn explain_with_visible_indexes(
696 &self,
697 visible_indexes: &VisibleIndexes<'_>,
698 ) -> Result<ExplainPlan, QueryError> {
699 let plan = self
700 .inner
701 .build_plan_with_visible_indexes(visible_indexes)?;
702
703 Ok(plan.explain_with_model(E::MODEL))
704 }
705
706 pub(in crate::db) fn plan_hash_hex_with_visible_indexes(
707 &self,
708 visible_indexes: &VisibleIndexes<'_>,
709 ) -> Result<String, QueryError> {
710 let plan = self
711 .inner
712 .build_plan_with_visible_indexes(visible_indexes)?;
713
714 Ok(plan.fingerprint().to_string())
715 }
716
717 #[must_use]
718 pub(crate) fn has_explicit_order(&self) -> bool {
719 self.inner.has_explicit_order()
720 }
721
722 #[must_use]
723 pub(crate) const fn has_grouping(&self) -> bool {
724 self.inner.has_grouping()
725 }
726
727 #[must_use]
728 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
729 self.inner.load_spec()
730 }
731
732 #[must_use]
734 pub fn filter(mut self, predicate: Predicate) -> Self {
735 self.inner = self.inner.filter(predicate);
736 self
737 }
738
739 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
741 let Self { inner, .. } = self;
742 let inner = inner.filter_expr(expr)?;
743
744 Ok(Self::from_inner(inner))
745 }
746
747 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
749 let Self { inner, .. } = self;
750 let inner = inner.sort_expr(expr)?;
751
752 Ok(Self::from_inner(inner))
753 }
754
755 #[must_use]
757 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
758 self.inner = self.inner.order_by(field);
759 self
760 }
761
762 #[must_use]
764 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
765 self.inner = self.inner.order_by_desc(field);
766 self
767 }
768
769 #[must_use]
771 pub fn distinct(mut self) -> Self {
772 self.inner = self.inner.distinct();
773 self
774 }
775
776 #[cfg(all(test, feature = "sql"))]
779 #[must_use]
780 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
781 where
782 I: IntoIterator<Item = S>,
783 S: Into<String>,
784 {
785 self.inner = self.inner.select_fields(fields);
786 self
787 }
788
789 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
791 let Self { inner, .. } = self;
792 let inner = inner.group_by(field)?;
793
794 Ok(Self::from_inner(inner))
795 }
796
797 #[must_use]
799 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
800 self.inner = self.inner.aggregate(aggregate);
801 self
802 }
803
804 #[must_use]
806 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
807 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
808 self
809 }
810
811 pub fn having_group(
813 self,
814 field: impl AsRef<str>,
815 op: CompareOp,
816 value: Value,
817 ) -> Result<Self, QueryError> {
818 let Self { inner, .. } = self;
819 let inner = inner.having_group(field, op, value)?;
820
821 Ok(Self::from_inner(inner))
822 }
823
824 pub fn having_aggregate(
826 self,
827 aggregate_index: usize,
828 op: CompareOp,
829 value: Value,
830 ) -> Result<Self, QueryError> {
831 let Self { inner, .. } = self;
832 let inner = inner.having_aggregate(aggregate_index, op, value)?;
833
834 Ok(Self::from_inner(inner))
835 }
836
837 pub(crate) fn by_id(self, id: E::Key) -> Self {
839 let Self { inner, .. } = self;
840
841 Self::from_inner(inner.by_id(id.to_value()))
842 }
843
844 pub(crate) fn by_ids<I>(self, ids: I) -> Self
846 where
847 I: IntoIterator<Item = E::Key>,
848 {
849 let Self { inner, .. } = self;
850
851 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
852 }
853
854 #[must_use]
856 pub fn delete(mut self) -> Self {
857 self.inner = self.inner.delete();
858 self
859 }
860
861 #[must_use]
868 pub fn limit(mut self, limit: u32) -> Self {
869 self.inner = self.inner.limit(limit);
870 self
871 }
872
873 #[must_use]
879 pub fn offset(mut self, offset: u32) -> Self {
880 self.inner = self.inner.offset(offset);
881 self
882 }
883
884 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
886 let plan = self.planned()?;
887
888 Ok(plan.explain())
889 }
890
891 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
896 let plan = self.inner.build_plan()?;
897
898 Ok(plan.fingerprint().to_string())
899 }
900
901 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
903 where
904 E: EntityValue,
905 {
906 self.inner.explain_execution()
907 }
908
909 pub(in crate::db) fn explain_execution_with_visible_indexes(
910 &self,
911 visible_indexes: &VisibleIndexes<'_>,
912 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
913 where
914 E: EntityValue,
915 {
916 self.inner
917 .explain_execution_with_visible_indexes(visible_indexes)
918 }
919
920 pub fn explain_execution_text(&self) -> Result<String, QueryError>
922 where
923 E: EntityValue,
924 {
925 self.inner.explain_execution_text()
926 }
927
928 pub(in crate::db) fn explain_execution_text_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_text_with_visible_indexes(visible_indexes)
937 }
938
939 pub fn explain_execution_json(&self) -> Result<String, QueryError>
941 where
942 E: EntityValue,
943 {
944 self.inner.explain_execution_json()
945 }
946
947 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
948 &self,
949 visible_indexes: &VisibleIndexes<'_>,
950 ) -> Result<String, QueryError>
951 where
952 E: EntityValue,
953 {
954 self.inner
955 .explain_execution_json_with_visible_indexes(visible_indexes)
956 }
957
958 #[inline(never)]
960 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
961 where
962 E: EntityValue,
963 {
964 self.inner.explain_execution_verbose()
965 }
966
967 #[cfg(test)]
969 #[inline(never)]
970 pub(in crate::db) fn explain_aggregate_terminal(
971 &self,
972 aggregate: AggregateExpr,
973 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
974 where
975 E: EntityValue,
976 {
977 self.inner.explain_aggregate_terminal_with_visible_indexes(
978 &VisibleIndexes::schema_owned(E::MODEL.indexes()),
979 AggregateRouteShape::new_from_fields(
980 aggregate.kind(),
981 aggregate.target_field(),
982 E::MODEL.fields(),
983 E::MODEL.primary_key().name(),
984 ),
985 )
986 }
987
988 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
989 &self,
990 visible_indexes: &VisibleIndexes<'_>,
991 ) -> Result<String, QueryError>
992 where
993 E: EntityValue,
994 {
995 self.inner
996 .explain_execution_verbose_with_visible_indexes(visible_indexes)
997 }
998
999 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
1000 &self,
1001 visible_indexes: &VisibleIndexes<'_>,
1002 strategy: &S,
1003 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1004 where
1005 E: EntityValue,
1006 S: PreparedFluentAggregateExplainStrategy,
1007 {
1008 self.inner
1009 .explain_prepared_aggregate_terminal_with_visible_indexes(visible_indexes, strategy)
1010 }
1011
1012 pub(in crate::db) fn explain_bytes_by_with_visible_indexes(
1013 &self,
1014 visible_indexes: &VisibleIndexes<'_>,
1015 target_field: &str,
1016 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1017 where
1018 E: EntityValue,
1019 {
1020 let executable = self
1021 .plan_with_visible_indexes(visible_indexes)?
1022 .into_executable();
1023 let mut descriptor = executable
1024 .explain_load_execution_node_descriptor()
1025 .map_err(QueryError::execute)?;
1026 let projection_mode = executable.bytes_by_projection_mode(target_field);
1027 let projection_mode_label =
1028 ExecutablePlan::<E>::bytes_by_projection_mode_label(projection_mode);
1029
1030 descriptor
1031 .node_properties
1032 .insert("terminal", Value::from("bytes_by"));
1033 descriptor
1034 .node_properties
1035 .insert("terminal_field", Value::from(target_field.to_string()));
1036 descriptor.node_properties.insert(
1037 "terminal_projection_mode",
1038 Value::from(projection_mode_label),
1039 );
1040 descriptor.node_properties.insert(
1041 "terminal_index_only",
1042 Value::from(matches!(
1043 projection_mode,
1044 BytesByProjectionMode::CoveringIndex | BytesByProjectionMode::CoveringConstant
1045 )),
1046 );
1047
1048 Ok(descriptor)
1049 }
1050
1051 pub(in crate::db) fn explain_prepared_projection_terminal_with_visible_indexes(
1052 &self,
1053 visible_indexes: &VisibleIndexes<'_>,
1054 strategy: &PreparedFluentProjectionStrategy,
1055 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1056 where
1057 E: EntityValue,
1058 {
1059 let executable = self
1060 .plan_with_visible_indexes(visible_indexes)?
1061 .into_executable();
1062 let mut descriptor = executable
1063 .explain_load_execution_node_descriptor()
1064 .map_err(QueryError::execute)?;
1065 let projection_descriptor = strategy.explain_descriptor();
1066
1067 descriptor.node_properties.insert(
1068 "terminal",
1069 Value::from(projection_descriptor.terminal_label()),
1070 );
1071 descriptor.node_properties.insert(
1072 "terminal_field",
1073 Value::from(projection_descriptor.field_label().to_string()),
1074 );
1075 descriptor.node_properties.insert(
1076 "terminal_output",
1077 Value::from(projection_descriptor.output_label()),
1078 );
1079
1080 Ok(descriptor)
1081 }
1082
1083 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
1085 let plan = self.inner.build_plan()?;
1086 let _projection = plan.projection_spec(E::MODEL);
1087
1088 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
1089 E::MODEL,
1090 plan,
1091 )))
1092 }
1093
1094 pub(in crate::db) fn planned_with_visible_indexes(
1095 &self,
1096 visible_indexes: &VisibleIndexes<'_>,
1097 ) -> Result<PlannedQuery<E>, QueryError> {
1098 let plan = self
1099 .inner
1100 .build_plan_with_visible_indexes(visible_indexes)?;
1101 let _projection = plan.projection_spec(E::MODEL);
1102
1103 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
1104 E::MODEL,
1105 plan,
1106 )))
1107 }
1108
1109 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
1113 let plan = self.inner.build_plan()?;
1114 let _projection = plan.projection_spec(E::MODEL);
1115
1116 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
1117 E::MODEL,
1118 E::PATH,
1119 plan,
1120 )))
1121 }
1122
1123 pub(in crate::db) fn plan_with_visible_indexes(
1124 &self,
1125 visible_indexes: &VisibleIndexes<'_>,
1126 ) -> Result<CompiledQuery<E>, QueryError> {
1127 let plan = self
1128 .inner
1129 .build_plan_with_visible_indexes(visible_indexes)?;
1130 let _projection = plan.projection_spec(E::MODEL);
1131
1132 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
1133 E::MODEL,
1134 E::PATH,
1135 plan,
1136 )))
1137 }
1138}
1139
1140fn contains_execution_node_type(
1141 descriptor: &ExplainExecutionNodeDescriptor,
1142 target: ExplainExecutionNodeType,
1143) -> bool {
1144 descriptor.node_type() == target
1145 || descriptor
1146 .children()
1147 .iter()
1148 .any(|child| contains_execution_node_type(child, target))
1149}
1150
1151fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
1152 match order_pushdown {
1153 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
1154 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
1155 format!("eligible(index={index},prefix_len={prefix_len})",)
1156 }
1157 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
1158 }
1159}
1160
1161fn plan_predicate_pushdown_label(
1162 predicate: &ExplainPredicate,
1163 access: &ExplainAccessPath,
1164) -> String {
1165 let access_label = match access {
1166 ExplainAccessPath::ByKey { .. } => "by_key",
1167 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
1168 ExplainAccessPath::ByKeys { .. } => "by_keys",
1169 ExplainAccessPath::KeyRange { .. } => "key_range",
1170 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
1171 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
1172 ExplainAccessPath::IndexRange { .. } => "index_range",
1173 ExplainAccessPath::FullScan => "full_scan",
1174 ExplainAccessPath::Union(_) => "union",
1175 ExplainAccessPath::Intersection(_) => "intersection",
1176 };
1177 if matches!(predicate, ExplainPredicate::None) {
1178 return "none".to_string();
1179 }
1180 if matches!(access, ExplainAccessPath::FullScan) {
1181 if explain_predicate_contains_non_strict_compare(predicate) {
1182 return "fallback(non_strict_compare_coercion)".to_string();
1183 }
1184 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
1185 return "fallback(starts_with_empty_prefix)".to_string();
1186 }
1187 if explain_predicate_contains_is_null(predicate) {
1188 return "fallback(is_null_full_scan)".to_string();
1189 }
1190 if explain_predicate_contains_text_scan_operator(predicate) {
1191 return "fallback(text_operator_full_scan)".to_string();
1192 }
1193
1194 return format!("fallback({access_label})");
1195 }
1196
1197 format!("applied({access_label})")
1198}
1199
1200fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
1201 match predicate {
1202 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
1203 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1204 .iter()
1205 .any(explain_predicate_contains_non_strict_compare),
1206 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
1207 ExplainPredicate::None
1208 | ExplainPredicate::True
1209 | ExplainPredicate::False
1210 | ExplainPredicate::IsNull { .. }
1211 | ExplainPredicate::IsNotNull { .. }
1212 | ExplainPredicate::IsMissing { .. }
1213 | ExplainPredicate::IsEmpty { .. }
1214 | ExplainPredicate::IsNotEmpty { .. }
1215 | ExplainPredicate::TextContains { .. }
1216 | ExplainPredicate::TextContainsCi { .. } => false,
1217 }
1218}
1219
1220fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
1221 match predicate {
1222 ExplainPredicate::IsNull { .. } => true,
1223 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
1224 children.iter().any(explain_predicate_contains_is_null)
1225 }
1226 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
1227 ExplainPredicate::None
1228 | ExplainPredicate::True
1229 | ExplainPredicate::False
1230 | ExplainPredicate::Compare { .. }
1231 | ExplainPredicate::IsNotNull { .. }
1232 | ExplainPredicate::IsMissing { .. }
1233 | ExplainPredicate::IsEmpty { .. }
1234 | ExplainPredicate::IsNotEmpty { .. }
1235 | ExplainPredicate::TextContains { .. }
1236 | ExplainPredicate::TextContainsCi { .. } => false,
1237 }
1238}
1239
1240fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
1241 match predicate {
1242 ExplainPredicate::Compare {
1243 op: CompareOp::StartsWith,
1244 value: Value::Text(prefix),
1245 ..
1246 } => prefix.is_empty(),
1247 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1248 .iter()
1249 .any(explain_predicate_contains_empty_prefix_starts_with),
1250 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
1251 ExplainPredicate::None
1252 | ExplainPredicate::True
1253 | ExplainPredicate::False
1254 | ExplainPredicate::Compare { .. }
1255 | ExplainPredicate::IsNull { .. }
1256 | ExplainPredicate::IsNotNull { .. }
1257 | ExplainPredicate::IsMissing { .. }
1258 | ExplainPredicate::IsEmpty { .. }
1259 | ExplainPredicate::IsNotEmpty { .. }
1260 | ExplainPredicate::TextContains { .. }
1261 | ExplainPredicate::TextContainsCi { .. } => false,
1262 }
1263}
1264
1265fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
1266 match predicate {
1267 ExplainPredicate::Compare {
1268 op: CompareOp::EndsWith,
1269 ..
1270 }
1271 | ExplainPredicate::TextContains { .. }
1272 | ExplainPredicate::TextContainsCi { .. } => true,
1273 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1274 .iter()
1275 .any(explain_predicate_contains_text_scan_operator),
1276 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
1277 ExplainPredicate::Compare { .. }
1278 | ExplainPredicate::None
1279 | ExplainPredicate::True
1280 | ExplainPredicate::False
1281 | ExplainPredicate::IsNull { .. }
1282 | ExplainPredicate::IsNotNull { .. }
1283 | ExplainPredicate::IsMissing { .. }
1284 | ExplainPredicate::IsEmpty { .. }
1285 | ExplainPredicate::IsNotEmpty { .. } => false,
1286 }
1287}
1288
1289impl<E> Query<E>
1290where
1291 E: EntityKind + SingletonEntity,
1292 E::Key: Default,
1293{
1294 pub(crate) fn only(self) -> Self {
1296 let Self { inner, .. } = self;
1297
1298 Self::from_inner(inner.only(E::Key::default().to_value()))
1299 }
1300}