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(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(feature = "sql")]
785 #[cfg_attr(not(test), allow(dead_code))]
786 #[must_use]
787 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
788 where
789 I: IntoIterator<Item = S>,
790 S: Into<String>,
791 {
792 self.inner = self.inner.select_fields(fields);
793 self
794 }
795
796 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
798 let Self { inner, .. } = self;
799 let inner = inner.group_by(field)?;
800
801 Ok(Self::from_inner(inner))
802 }
803
804 #[must_use]
806 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
807 self.inner = self.inner.aggregate(aggregate);
808 self
809 }
810
811 #[must_use]
813 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
814 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
815 self
816 }
817
818 pub fn having_group(
820 self,
821 field: impl AsRef<str>,
822 op: CompareOp,
823 value: Value,
824 ) -> Result<Self, QueryError> {
825 let Self { inner, .. } = self;
826 let inner = inner.having_group(field, op, value)?;
827
828 Ok(Self::from_inner(inner))
829 }
830
831 pub fn having_aggregate(
833 self,
834 aggregate_index: usize,
835 op: CompareOp,
836 value: Value,
837 ) -> Result<Self, QueryError> {
838 let Self { inner, .. } = self;
839 let inner = inner.having_aggregate(aggregate_index, op, value)?;
840
841 Ok(Self::from_inner(inner))
842 }
843
844 pub(crate) fn by_id(self, id: E::Key) -> Self {
846 let Self { inner, .. } = self;
847
848 Self::from_inner(inner.by_id(id.to_value()))
849 }
850
851 pub(crate) fn by_ids<I>(self, ids: I) -> Self
853 where
854 I: IntoIterator<Item = E::Key>,
855 {
856 let Self { inner, .. } = self;
857
858 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
859 }
860
861 #[must_use]
863 pub fn delete(mut self) -> Self {
864 self.inner = self.inner.delete();
865 self
866 }
867
868 #[must_use]
875 pub fn limit(mut self, limit: u32) -> Self {
876 self.inner = self.inner.limit(limit);
877 self
878 }
879
880 #[must_use]
886 pub fn offset(mut self, offset: u32) -> Self {
887 self.inner = self.inner.offset(offset);
888 self
889 }
890
891 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
893 let plan = self.planned()?;
894
895 Ok(plan.explain())
896 }
897
898 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
903 let plan = self.inner.build_plan()?;
904
905 Ok(plan.fingerprint().to_string())
906 }
907
908 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
910 where
911 E: EntityValue,
912 {
913 self.inner.explain_execution()
914 }
915
916 pub(in crate::db) fn explain_execution_with_visible_indexes(
917 &self,
918 visible_indexes: &VisibleIndexes<'_>,
919 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
920 where
921 E: EntityValue,
922 {
923 self.inner
924 .explain_execution_with_visible_indexes(visible_indexes)
925 }
926
927 pub fn explain_execution_text(&self) -> Result<String, QueryError>
929 where
930 E: EntityValue,
931 {
932 self.inner.explain_execution_text()
933 }
934
935 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
936 &self,
937 visible_indexes: &VisibleIndexes<'_>,
938 ) -> Result<String, QueryError>
939 where
940 E: EntityValue,
941 {
942 self.inner
943 .explain_execution_text_with_visible_indexes(visible_indexes)
944 }
945
946 pub fn explain_execution_json(&self) -> Result<String, QueryError>
948 where
949 E: EntityValue,
950 {
951 self.inner.explain_execution_json()
952 }
953
954 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
955 &self,
956 visible_indexes: &VisibleIndexes<'_>,
957 ) -> Result<String, QueryError>
958 where
959 E: EntityValue,
960 {
961 self.inner
962 .explain_execution_json_with_visible_indexes(visible_indexes)
963 }
964
965 #[inline(never)]
967 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
968 where
969 E: EntityValue,
970 {
971 self.inner.explain_execution_verbose()
972 }
973
974 #[cfg(test)]
976 #[inline(never)]
977 pub(in crate::db) fn explain_aggregate_terminal(
978 &self,
979 aggregate: AggregateExpr,
980 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
981 where
982 E: EntityValue,
983 {
984 self.inner.explain_aggregate_terminal_with_visible_indexes(
985 &VisibleIndexes::schema_owned(E::MODEL.indexes()),
986 AggregateRouteShape::new_from_fields(
987 aggregate.kind(),
988 aggregate.target_field(),
989 E::MODEL.fields(),
990 E::MODEL.primary_key().name(),
991 ),
992 )
993 }
994
995 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
996 &self,
997 visible_indexes: &VisibleIndexes<'_>,
998 ) -> Result<String, QueryError>
999 where
1000 E: EntityValue,
1001 {
1002 self.inner
1003 .explain_execution_verbose_with_visible_indexes(visible_indexes)
1004 }
1005
1006 pub(in crate::db) fn explain_prepared_aggregate_terminal_with_visible_indexes<S>(
1007 &self,
1008 visible_indexes: &VisibleIndexes<'_>,
1009 strategy: &S,
1010 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1011 where
1012 E: EntityValue,
1013 S: PreparedFluentAggregateExplainStrategy,
1014 {
1015 self.inner
1016 .explain_prepared_aggregate_terminal_with_visible_indexes(visible_indexes, strategy)
1017 }
1018
1019 pub(in crate::db) fn explain_bytes_by_with_visible_indexes(
1020 &self,
1021 visible_indexes: &VisibleIndexes<'_>,
1022 target_field: &str,
1023 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1024 where
1025 E: EntityValue,
1026 {
1027 let executable = self
1028 .plan_with_visible_indexes(visible_indexes)?
1029 .into_executable();
1030 let mut descriptor = executable
1031 .explain_load_execution_node_descriptor()
1032 .map_err(QueryError::execute)?;
1033 let projection_mode = executable.bytes_by_projection_mode(target_field);
1034 let projection_mode_label =
1035 ExecutablePlan::<E>::bytes_by_projection_mode_label(projection_mode);
1036
1037 descriptor
1038 .node_properties
1039 .insert("terminal", Value::from("bytes_by"));
1040 descriptor
1041 .node_properties
1042 .insert("terminal_field", Value::from(target_field.to_string()));
1043 descriptor.node_properties.insert(
1044 "terminal_projection_mode",
1045 Value::from(projection_mode_label),
1046 );
1047 descriptor.node_properties.insert(
1048 "terminal_index_only",
1049 Value::from(matches!(
1050 projection_mode,
1051 BytesByProjectionMode::CoveringIndex | BytesByProjectionMode::CoveringConstant
1052 )),
1053 );
1054
1055 Ok(descriptor)
1056 }
1057
1058 pub(in crate::db) fn explain_prepared_projection_terminal_with_visible_indexes(
1059 &self,
1060 visible_indexes: &VisibleIndexes<'_>,
1061 strategy: &PreparedFluentProjectionStrategy,
1062 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1063 where
1064 E: EntityValue,
1065 {
1066 let executable = self
1067 .plan_with_visible_indexes(visible_indexes)?
1068 .into_executable();
1069 let mut descriptor = executable
1070 .explain_load_execution_node_descriptor()
1071 .map_err(QueryError::execute)?;
1072 let projection_descriptor = strategy.explain_descriptor();
1073
1074 descriptor.node_properties.insert(
1075 "terminal",
1076 Value::from(projection_descriptor.terminal_label()),
1077 );
1078 descriptor.node_properties.insert(
1079 "terminal_field",
1080 Value::from(projection_descriptor.field_label().to_string()),
1081 );
1082 descriptor.node_properties.insert(
1083 "terminal_output",
1084 Value::from(projection_descriptor.output_label()),
1085 );
1086
1087 Ok(descriptor)
1088 }
1089
1090 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
1092 let plan = self.inner.build_plan()?;
1093 let _projection = plan.projection_spec(E::MODEL);
1094
1095 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
1096 E::MODEL,
1097 plan,
1098 )))
1099 }
1100
1101 pub(in crate::db) fn planned_with_visible_indexes(
1102 &self,
1103 visible_indexes: &VisibleIndexes<'_>,
1104 ) -> Result<PlannedQuery<E>, QueryError> {
1105 let plan = self
1106 .inner
1107 .build_plan_with_visible_indexes(visible_indexes)?;
1108 let _projection = plan.projection_spec(E::MODEL);
1109
1110 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
1111 E::MODEL,
1112 plan,
1113 )))
1114 }
1115
1116 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
1120 let plan = self.inner.build_plan()?;
1121 let _projection = plan.projection_spec(E::MODEL);
1122
1123 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
1124 E::MODEL,
1125 E::PATH,
1126 plan,
1127 )))
1128 }
1129
1130 pub(in crate::db) fn plan_with_visible_indexes(
1131 &self,
1132 visible_indexes: &VisibleIndexes<'_>,
1133 ) -> Result<CompiledQuery<E>, QueryError> {
1134 let plan = self
1135 .inner
1136 .build_plan_with_visible_indexes(visible_indexes)?;
1137 let _projection = plan.projection_spec(E::MODEL);
1138
1139 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
1140 E::MODEL,
1141 E::PATH,
1142 plan,
1143 )))
1144 }
1145}
1146
1147fn contains_execution_node_type(
1148 descriptor: &ExplainExecutionNodeDescriptor,
1149 target: ExplainExecutionNodeType,
1150) -> bool {
1151 descriptor.node_type() == target
1152 || descriptor
1153 .children()
1154 .iter()
1155 .any(|child| contains_execution_node_type(child, target))
1156}
1157
1158fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
1159 match order_pushdown {
1160 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
1161 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
1162 format!("eligible(index={index},prefix_len={prefix_len})",)
1163 }
1164 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
1165 }
1166}
1167
1168fn plan_predicate_pushdown_label(
1169 predicate: &ExplainPredicate,
1170 access: &ExplainAccessPath,
1171) -> String {
1172 let access_label = match access {
1173 ExplainAccessPath::ByKey { .. } => "by_key",
1174 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
1175 ExplainAccessPath::ByKeys { .. } => "by_keys",
1176 ExplainAccessPath::KeyRange { .. } => "key_range",
1177 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
1178 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
1179 ExplainAccessPath::IndexRange { .. } => "index_range",
1180 ExplainAccessPath::FullScan => "full_scan",
1181 ExplainAccessPath::Union(_) => "union",
1182 ExplainAccessPath::Intersection(_) => "intersection",
1183 };
1184 if matches!(predicate, ExplainPredicate::None) {
1185 return "none".to_string();
1186 }
1187 if matches!(access, ExplainAccessPath::FullScan) {
1188 if explain_predicate_contains_non_strict_compare(predicate) {
1189 return "fallback(non_strict_compare_coercion)".to_string();
1190 }
1191 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
1192 return "fallback(starts_with_empty_prefix)".to_string();
1193 }
1194 if explain_predicate_contains_is_null(predicate) {
1195 return "fallback(is_null_full_scan)".to_string();
1196 }
1197 if explain_predicate_contains_text_scan_operator(predicate) {
1198 return "fallback(text_operator_full_scan)".to_string();
1199 }
1200
1201 return format!("fallback({access_label})");
1202 }
1203
1204 format!("applied({access_label})")
1205}
1206
1207fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
1208 match predicate {
1209 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
1210 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1211 .iter()
1212 .any(explain_predicate_contains_non_strict_compare),
1213 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
1214 ExplainPredicate::None
1215 | ExplainPredicate::True
1216 | ExplainPredicate::False
1217 | ExplainPredicate::IsNull { .. }
1218 | ExplainPredicate::IsNotNull { .. }
1219 | ExplainPredicate::IsMissing { .. }
1220 | ExplainPredicate::IsEmpty { .. }
1221 | ExplainPredicate::IsNotEmpty { .. }
1222 | ExplainPredicate::TextContains { .. }
1223 | ExplainPredicate::TextContainsCi { .. } => false,
1224 }
1225}
1226
1227fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
1228 match predicate {
1229 ExplainPredicate::IsNull { .. } => true,
1230 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
1231 children.iter().any(explain_predicate_contains_is_null)
1232 }
1233 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
1234 ExplainPredicate::None
1235 | ExplainPredicate::True
1236 | ExplainPredicate::False
1237 | ExplainPredicate::Compare { .. }
1238 | ExplainPredicate::IsNotNull { .. }
1239 | ExplainPredicate::IsMissing { .. }
1240 | ExplainPredicate::IsEmpty { .. }
1241 | ExplainPredicate::IsNotEmpty { .. }
1242 | ExplainPredicate::TextContains { .. }
1243 | ExplainPredicate::TextContainsCi { .. } => false,
1244 }
1245}
1246
1247fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
1248 match predicate {
1249 ExplainPredicate::Compare {
1250 op: CompareOp::StartsWith,
1251 value: Value::Text(prefix),
1252 ..
1253 } => prefix.is_empty(),
1254 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1255 .iter()
1256 .any(explain_predicate_contains_empty_prefix_starts_with),
1257 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
1258 ExplainPredicate::None
1259 | ExplainPredicate::True
1260 | ExplainPredicate::False
1261 | ExplainPredicate::Compare { .. }
1262 | ExplainPredicate::IsNull { .. }
1263 | ExplainPredicate::IsNotNull { .. }
1264 | ExplainPredicate::IsMissing { .. }
1265 | ExplainPredicate::IsEmpty { .. }
1266 | ExplainPredicate::IsNotEmpty { .. }
1267 | ExplainPredicate::TextContains { .. }
1268 | ExplainPredicate::TextContainsCi { .. } => false,
1269 }
1270}
1271
1272fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
1273 match predicate {
1274 ExplainPredicate::Compare {
1275 op: CompareOp::EndsWith,
1276 ..
1277 }
1278 | ExplainPredicate::TextContains { .. }
1279 | ExplainPredicate::TextContainsCi { .. } => true,
1280 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
1281 .iter()
1282 .any(explain_predicate_contains_text_scan_operator),
1283 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
1284 ExplainPredicate::Compare { .. }
1285 | ExplainPredicate::None
1286 | ExplainPredicate::True
1287 | ExplainPredicate::False
1288 | ExplainPredicate::IsNull { .. }
1289 | ExplainPredicate::IsNotNull { .. }
1290 | ExplainPredicate::IsMissing { .. }
1291 | ExplainPredicate::IsEmpty { .. }
1292 | ExplainPredicate::IsNotEmpty { .. } => false,
1293 }
1294}
1295
1296impl<E> Query<E>
1297where
1298 E: EntityKind + SingletonEntity,
1299 E::Key: Default,
1300{
1301 pub(crate) fn only(self) -> Self {
1303 let Self { inner, .. } = self;
1304
1305 Self::from_inner(inner.only(E::Key::default().to_value()))
1306 }
1307}