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