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