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 },
16 predicate::{CoercionId, CompareOp, MissingRowPolicy, Predicate},
17 query::{
18 builder::aggregate::{
19 AggregateExpr, PreparedFluentOrderSensitiveTerminalStrategy,
20 PreparedFluentScalarTerminalStrategy,
21 },
22 explain::{
23 ExplainAccessPath, ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor,
24 ExplainExecutionNodeType, ExplainOrderPushdown, ExplainPlan, ExplainPredicate,
25 },
26 expr::{FilterExpr, SortExpr},
27 intent::{QueryError, model::QueryModel},
28 plan::{AccessPlannedQuery, LoadSpec, QueryMode, VisibleIndexes},
29 },
30 },
31 traits::{EntityKind, EntityValue, FieldValue, SingletonEntity},
32 value::Value,
33};
34use core::marker::PhantomData;
35
36#[cfg(test)]
37use crate::db::query::builder::aggregate::PreparedFluentNumericFieldStrategy;
38
39#[derive(Debug)]
48pub(in crate::db) struct StructuralQuery {
49 intent: QueryModel<'static, Value>,
50}
51
52impl StructuralQuery {
53 #[must_use]
54 pub(in crate::db) const fn new(
55 model: &'static crate::model::entity::EntityModel,
56 consistency: MissingRowPolicy,
57 ) -> Self {
58 Self {
59 intent: QueryModel::new(model, consistency),
60 }
61 }
62
63 #[must_use]
64 const fn mode(&self) -> QueryMode {
65 self.intent.mode()
66 }
67
68 #[must_use]
69 fn has_explicit_order(&self) -> bool {
70 self.intent.has_explicit_order()
71 }
72
73 #[must_use]
74 const fn has_grouping(&self) -> bool {
75 self.intent.has_grouping()
76 }
77
78 #[must_use]
79 const fn load_spec(&self) -> Option<LoadSpec> {
80 match self.intent.mode() {
81 QueryMode::Load(spec) => Some(spec),
82 QueryMode::Delete(_) => None,
83 }
84 }
85
86 #[must_use]
87 pub(in crate::db) fn filter(mut self, predicate: Predicate) -> Self {
88 self.intent = self.intent.filter(predicate);
89 self
90 }
91
92 fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
93 let Self { intent } = self;
94 let intent = intent.filter_expr(expr)?;
95
96 Ok(Self { intent })
97 }
98
99 fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
100 let Self { intent } = self;
101 let intent = intent.sort_expr(expr)?;
102
103 Ok(Self { intent })
104 }
105
106 #[must_use]
107 pub(in crate::db) fn order_by(mut self, field: impl AsRef<str>) -> Self {
108 self.intent = self.intent.order_by(field);
109 self
110 }
111
112 #[must_use]
113 pub(in crate::db) fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
114 self.intent = self.intent.order_by_desc(field);
115 self
116 }
117
118 #[must_use]
119 pub(in crate::db) fn distinct(mut self) -> Self {
120 self.intent = self.intent.distinct();
121 self
122 }
123
124 #[cfg(feature = "sql")]
125 #[must_use]
126 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
127 where
128 I: IntoIterator<Item = S>,
129 S: Into<String>,
130 {
131 self.intent = self.intent.select_fields(fields);
132 self
133 }
134
135 pub(in crate::db) fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
136 let Self { intent } = self;
137 let intent = intent.push_group_field(field.as_ref())?;
138
139 Ok(Self { intent })
140 }
141
142 #[must_use]
143 pub(in crate::db) fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
144 self.intent = self.intent.push_group_aggregate(aggregate);
145 self
146 }
147
148 #[must_use]
149 fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
150 self.intent = self.intent.grouped_limits(max_groups, max_group_bytes);
151 self
152 }
153
154 pub(in crate::db) fn having_group(
155 self,
156 field: impl AsRef<str>,
157 op: CompareOp,
158 value: Value,
159 ) -> Result<Self, QueryError> {
160 let field = field.as_ref().to_owned();
161 let Self { intent } = self;
162 let intent = intent.push_having_group_clause(&field, op, value)?;
163
164 Ok(Self { intent })
165 }
166
167 pub(in crate::db) fn having_aggregate(
168 self,
169 aggregate_index: usize,
170 op: CompareOp,
171 value: Value,
172 ) -> Result<Self, QueryError> {
173 let Self { intent } = self;
174 let intent = intent.push_having_aggregate_clause(aggregate_index, op, value)?;
175
176 Ok(Self { intent })
177 }
178
179 #[must_use]
180 fn by_id(self, id: Value) -> Self {
181 let Self { intent } = self;
182 Self {
183 intent: intent.by_id(id),
184 }
185 }
186
187 #[must_use]
188 fn by_ids<I>(self, ids: I) -> Self
189 where
190 I: IntoIterator<Item = Value>,
191 {
192 let Self { intent } = self;
193 Self {
194 intent: intent.by_ids(ids),
195 }
196 }
197
198 #[must_use]
199 fn only(self, id: Value) -> Self {
200 let Self { intent } = self;
201
202 Self {
203 intent: intent.only(id),
204 }
205 }
206
207 #[must_use]
208 pub(in crate::db) fn delete(mut self) -> Self {
209 self.intent = self.intent.delete();
210 self
211 }
212
213 #[must_use]
214 pub(in crate::db) fn limit(mut self, limit: u32) -> Self {
215 self.intent = self.intent.limit(limit);
216 self
217 }
218
219 #[must_use]
220 pub(in crate::db) fn offset(mut self, offset: u32) -> Self {
221 self.intent = self.intent.offset(offset);
222 self
223 }
224
225 pub(in crate::db) fn build_plan(&self) -> Result<AccessPlannedQuery, QueryError> {
226 self.intent.build_plan_model()
227 }
228
229 pub(in crate::db) fn build_plan_with_visible_indexes(
230 &self,
231 visible_indexes: &VisibleIndexes<'_>,
232 ) -> Result<AccessPlannedQuery, QueryError> {
233 self.intent.build_plan_model_with_indexes(visible_indexes)
234 }
235
236 #[cfg(feature = "sql")]
237 #[must_use]
238 pub(in crate::db) const fn model(&self) -> &'static crate::model::entity::EntityModel {
239 self.intent.model()
240 }
241
242 #[inline(never)]
243 pub(in crate::db) fn explain_execution_with_visible_indexes(
244 &self,
245 visible_indexes: &VisibleIndexes<'_>,
246 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
247 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
248
249 assemble_load_execution_node_descriptor_with_model_and_visible_indexes(
250 self.intent.model(),
251 visible_indexes,
252 &plan,
253 )
254 .map_err(QueryError::execute)
255 }
256
257 #[inline(never)]
259 pub(in crate::db) fn explain_execution(
260 &self,
261 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
262 let plan = self.build_plan()?;
263
264 assemble_load_execution_node_descriptor_with_model(self.intent.model(), &plan)
265 .map_err(QueryError::execute)
266 }
267
268 pub(in crate::db) fn explain_execution_text(&self) -> Result<String, QueryError> {
271 Ok(self.explain_execution()?.render_text_tree())
272 }
273
274 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
275 &self,
276 visible_indexes: &VisibleIndexes<'_>,
277 ) -> Result<String, QueryError> {
278 Ok(self
279 .explain_execution_with_visible_indexes(visible_indexes)?
280 .render_text_tree())
281 }
282
283 pub(in crate::db) fn explain_execution_json(&self) -> Result<String, QueryError> {
286 Ok(self.explain_execution()?.render_json_canonical())
287 }
288
289 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
290 &self,
291 visible_indexes: &VisibleIndexes<'_>,
292 ) -> Result<String, QueryError> {
293 Ok(self
294 .explain_execution_with_visible_indexes(visible_indexes)?
295 .render_json_canonical())
296 }
297
298 #[inline(never)]
301 pub(in crate::db) fn explain_execution_verbose(&self) -> Result<String, QueryError> {
302 let plan = self.build_plan()?;
303 let descriptor =
304 assemble_load_execution_node_descriptor_with_model(self.intent.model(), &plan)
305 .map_err(QueryError::execute)?;
306 let route_diagnostics =
307 assemble_load_execution_verbose_diagnostics_with_model(self.intent.model(), &plan)
308 .map_err(QueryError::execute)?;
309 let explain = plan.explain_with_model(self.intent.model());
310
311 let mut lines = vec![descriptor.render_text_tree_verbose()];
313 lines.extend(route_diagnostics);
314
315 lines.push(format!(
317 "diag.d.has_top_n_seek={}",
318 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
319 ));
320 lines.push(format!(
321 "diag.d.has_index_range_limit_pushdown={}",
322 contains_execution_node_type(
323 &descriptor,
324 ExplainExecutionNodeType::IndexRangeLimitPushdown,
325 )
326 ));
327 lines.push(format!(
328 "diag.d.has_index_predicate_prefilter={}",
329 contains_execution_node_type(
330 &descriptor,
331 ExplainExecutionNodeType::IndexPredicatePrefilter,
332 )
333 ));
334 lines.push(format!(
335 "diag.d.has_residual_predicate_filter={}",
336 contains_execution_node_type(
337 &descriptor,
338 ExplainExecutionNodeType::ResidualPredicateFilter,
339 )
340 ));
341
342 lines.push(format!("diag.p.mode={:?}", explain.mode()));
344 lines.push(format!(
345 "diag.p.order_pushdown={}",
346 plan_order_pushdown_label(explain.order_pushdown())
347 ));
348 lines.push(format!(
349 "diag.p.predicate_pushdown={}",
350 plan_predicate_pushdown_label(explain.predicate(), explain.access())
351 ));
352 lines.push(format!("diag.p.distinct={}", explain.distinct()));
353 lines.push(format!("diag.p.page={:?}", explain.page()));
354 lines.push(format!("diag.p.consistency={:?}", explain.consistency()));
355
356 Ok(lines.join("\n"))
357 }
358
359 #[inline(never)]
360 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
361 &self,
362 visible_indexes: &VisibleIndexes<'_>,
363 ) -> Result<String, QueryError> {
364 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
365 let descriptor = assemble_load_execution_node_descriptor_with_model_and_visible_indexes(
366 self.intent.model(),
367 visible_indexes,
368 &plan,
369 )
370 .map_err(QueryError::execute)?;
371 let route_diagnostics =
372 assemble_load_execution_verbose_diagnostics_with_model_and_visible_indexes(
373 self.intent.model(),
374 visible_indexes,
375 &plan,
376 )
377 .map_err(QueryError::execute)?;
378 let explain = plan.explain_with_model(self.intent.model());
379
380 let mut lines = vec![descriptor.render_text_tree_verbose()];
382 lines.extend(route_diagnostics);
383
384 lines.push(format!(
386 "diag.d.has_top_n_seek={}",
387 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
388 ));
389 lines.push(format!(
390 "diag.d.has_index_range_limit_pushdown={}",
391 contains_execution_node_type(
392 &descriptor,
393 ExplainExecutionNodeType::IndexRangeLimitPushdown,
394 )
395 ));
396 lines.push(format!(
397 "diag.d.has_index_predicate_prefilter={}",
398 contains_execution_node_type(
399 &descriptor,
400 ExplainExecutionNodeType::IndexPredicatePrefilter,
401 )
402 ));
403 lines.push(format!(
404 "diag.d.has_residual_predicate_filter={}",
405 contains_execution_node_type(
406 &descriptor,
407 ExplainExecutionNodeType::ResidualPredicateFilter,
408 )
409 ));
410
411 lines.push(format!("diag.p.mode={:?}", explain.mode()));
413 lines.push(format!(
414 "diag.p.order_pushdown={}",
415 plan_order_pushdown_label(explain.order_pushdown())
416 ));
417 lines.push(format!(
418 "diag.p.predicate_pushdown={}",
419 plan_predicate_pushdown_label(explain.predicate(), explain.access())
420 ));
421 lines.push(format!("diag.p.distinct={}", explain.distinct()));
422 lines.push(format!("diag.p.page={:?}", explain.page()));
423 lines.push(format!("diag.p.consistency={:?}", explain.consistency()));
424
425 Ok(lines.join("\n"))
426 }
427
428 #[inline(never)]
429 pub(in crate::db) fn explain_aggregate_terminal_with_visible_indexes(
430 &self,
431 visible_indexes: &VisibleIndexes<'_>,
432 aggregate: AggregateExpr,
433 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
434 let plan = self.build_plan_with_visible_indexes(visible_indexes)?;
435 let query_explain = plan.explain_with_model(self.intent.model());
436 let terminal = aggregate.kind();
437 let execution = assemble_aggregate_terminal_execution_descriptor_with_model(
438 self.intent.model(),
439 &plan,
440 aggregate,
441 );
442
443 Ok(ExplainAggregateTerminalPlan::new(
444 query_explain,
445 terminal,
446 execution,
447 ))
448 }
449
450 #[inline(never)]
451 pub(in crate::db) fn explain_prepared_scalar_terminal_with_visible_indexes(
452 &self,
453 visible_indexes: &VisibleIndexes<'_>,
454 strategy: &PreparedFluentScalarTerminalStrategy,
455 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
456 self.explain_aggregate_terminal_with_visible_indexes(
457 visible_indexes,
458 strategy.aggregate().clone(),
459 )
460 }
461
462 #[inline(never)]
463 pub(in crate::db) fn explain_prepared_order_sensitive_terminal_with_visible_indexes(
464 &self,
465 visible_indexes: &VisibleIndexes<'_>,
466 strategy: &PreparedFluentOrderSensitiveTerminalStrategy,
467 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
468 let Some(aggregate) = strategy.explain_aggregate() else {
469 return Err(QueryError::invariant(
470 "prepared fluent order-sensitive explain requires an explain-visible aggregate kind",
471 ));
472 };
473
474 self.explain_aggregate_terminal_with_visible_indexes(visible_indexes, aggregate.clone())
475 }
476
477 #[cfg(test)]
478 #[inline(never)]
479 pub(in crate::db) fn explain_prepared_numeric_field_with_visible_indexes(
480 &self,
481 visible_indexes: &VisibleIndexes<'_>,
482 strategy: &PreparedFluentNumericFieldStrategy,
483 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
484 self.explain_aggregate_terminal_with_visible_indexes(
485 visible_indexes,
486 strategy.aggregate().clone(),
487 )
488 }
489}
490
491#[derive(Debug)]
500struct PlannedQueryCore {
501 model: &'static crate::model::entity::EntityModel,
502 plan: AccessPlannedQuery,
503}
504
505impl PlannedQueryCore {
506 #[must_use]
507 const fn new(
508 model: &'static crate::model::entity::EntityModel,
509 plan: AccessPlannedQuery,
510 ) -> Self {
511 Self { model, plan }
512 }
513
514 #[must_use]
515 fn explain(&self) -> ExplainPlan {
516 self.plan.explain_with_model(self.model)
517 }
518
519 #[must_use]
521 fn plan_hash_hex(&self) -> String {
522 self.plan.fingerprint().to_string()
523 }
524}
525
526#[derive(Debug)]
535pub struct PlannedQuery<E: EntityKind> {
536 inner: PlannedQueryCore,
537 _marker: PhantomData<E>,
538}
539
540impl<E: EntityKind> PlannedQuery<E> {
541 #[must_use]
542 const fn from_inner(inner: PlannedQueryCore) -> Self {
543 Self {
544 inner,
545 _marker: PhantomData,
546 }
547 }
548
549 #[must_use]
550 pub fn explain(&self) -> ExplainPlan {
551 self.inner.explain()
552 }
553
554 #[must_use]
556 pub fn plan_hash_hex(&self) -> String {
557 self.inner.plan_hash_hex()
558 }
559}
560
561#[derive(Clone, Debug)]
570struct CompiledQueryCore {
571 model: &'static crate::model::entity::EntityModel,
572 entity_path: &'static str,
573 plan: AccessPlannedQuery,
574}
575
576impl CompiledQueryCore {
577 #[must_use]
578 const fn new(
579 model: &'static crate::model::entity::EntityModel,
580 entity_path: &'static str,
581 plan: AccessPlannedQuery,
582 ) -> Self {
583 Self {
584 model,
585 entity_path,
586 plan,
587 }
588 }
589
590 #[must_use]
591 fn explain(&self) -> ExplainPlan {
592 self.plan.explain_with_model(self.model)
593 }
594
595 #[must_use]
597 fn plan_hash_hex(&self) -> String {
598 self.plan.fingerprint().to_string()
599 }
600
601 #[must_use]
602 #[cfg(test)]
603 fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
604 self.plan.projection_spec(self.model)
605 }
606
607 #[must_use]
608 fn into_inner(self) -> AccessPlannedQuery {
609 self.plan
610 }
611}
612
613#[derive(Clone, Debug)]
622pub struct CompiledQuery<E: EntityKind> {
623 inner: CompiledQueryCore,
624 _marker: PhantomData<E>,
625}
626
627impl<E: EntityKind> CompiledQuery<E> {
628 #[must_use]
629 const fn from_inner(inner: CompiledQueryCore) -> Self {
630 Self {
631 inner,
632 _marker: PhantomData,
633 }
634 }
635
636 #[must_use]
637 pub fn explain(&self) -> ExplainPlan {
638 self.inner.explain()
639 }
640
641 #[must_use]
643 pub fn plan_hash_hex(&self) -> String {
644 self.inner.plan_hash_hex()
645 }
646
647 #[must_use]
648 #[cfg(test)]
649 pub(in crate::db) fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
650 self.inner.projection_spec()
651 }
652
653 pub(in crate::db) fn into_executable(self) -> crate::db::executor::ExecutablePlan<E> {
655 assert!(
656 self.inner.entity_path == E::PATH,
657 "compiled query entity mismatch: compiled for '{}', requested '{}'",
658 self.inner.entity_path,
659 E::PATH,
660 );
661
662 crate::db::executor::ExecutablePlan::new(self.into_inner())
663 }
664
665 #[must_use]
666 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
667 self.inner.into_inner()
668 }
669}
670
671#[derive(Debug)]
683pub struct Query<E: EntityKind> {
684 inner: StructuralQuery,
685 _marker: PhantomData<E>,
686}
687
688impl<E: EntityKind> Query<E> {
689 pub(in crate::db) const fn from_inner(inner: StructuralQuery) -> Self {
691 Self {
692 inner,
693 _marker: PhantomData,
694 }
695 }
696
697 #[must_use]
701 pub const fn new(consistency: MissingRowPolicy) -> Self {
702 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
703 }
704
705 #[must_use]
707 pub const fn mode(&self) -> QueryMode {
708 self.inner.mode()
709 }
710
711 pub(in crate::db) fn explain_with_visible_indexes(
712 &self,
713 visible_indexes: &VisibleIndexes<'_>,
714 ) -> Result<ExplainPlan, QueryError> {
715 let plan = self
716 .inner
717 .build_plan_with_visible_indexes(visible_indexes)?;
718
719 Ok(plan.explain_with_model(E::MODEL))
720 }
721
722 pub(in crate::db) fn plan_hash_hex_with_visible_indexes(
723 &self,
724 visible_indexes: &VisibleIndexes<'_>,
725 ) -> Result<String, QueryError> {
726 let plan = self
727 .inner
728 .build_plan_with_visible_indexes(visible_indexes)?;
729
730 Ok(plan.fingerprint().to_string())
731 }
732
733 #[must_use]
734 pub(crate) fn has_explicit_order(&self) -> bool {
735 self.inner.has_explicit_order()
736 }
737
738 #[must_use]
739 pub(crate) const fn has_grouping(&self) -> bool {
740 self.inner.has_grouping()
741 }
742
743 #[must_use]
744 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
745 self.inner.load_spec()
746 }
747
748 #[must_use]
750 pub fn filter(mut self, predicate: Predicate) -> Self {
751 self.inner = self.inner.filter(predicate);
752 self
753 }
754
755 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
757 let Self { inner, .. } = self;
758 let inner = inner.filter_expr(expr)?;
759
760 Ok(Self::from_inner(inner))
761 }
762
763 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
765 let Self { inner, .. } = self;
766 let inner = inner.sort_expr(expr)?;
767
768 Ok(Self::from_inner(inner))
769 }
770
771 #[must_use]
773 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
774 self.inner = self.inner.order_by(field);
775 self
776 }
777
778 #[must_use]
780 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
781 self.inner = self.inner.order_by_desc(field);
782 self
783 }
784
785 #[must_use]
787 pub fn distinct(mut self) -> Self {
788 self.inner = self.inner.distinct();
789 self
790 }
791
792 #[cfg(feature = "sql")]
795 #[cfg_attr(not(test), allow(dead_code))]
796 #[must_use]
797 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
798 where
799 I: IntoIterator<Item = S>,
800 S: Into<String>,
801 {
802 self.inner = self.inner.select_fields(fields);
803 self
804 }
805
806 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
808 let Self { inner, .. } = self;
809 let inner = inner.group_by(field)?;
810
811 Ok(Self::from_inner(inner))
812 }
813
814 #[must_use]
816 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
817 self.inner = self.inner.aggregate(aggregate);
818 self
819 }
820
821 #[must_use]
823 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
824 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
825 self
826 }
827
828 pub fn having_group(
830 self,
831 field: impl AsRef<str>,
832 op: CompareOp,
833 value: Value,
834 ) -> Result<Self, QueryError> {
835 let Self { inner, .. } = self;
836 let inner = inner.having_group(field, op, value)?;
837
838 Ok(Self::from_inner(inner))
839 }
840
841 pub fn having_aggregate(
843 self,
844 aggregate_index: usize,
845 op: CompareOp,
846 value: Value,
847 ) -> Result<Self, QueryError> {
848 let Self { inner, .. } = self;
849 let inner = inner.having_aggregate(aggregate_index, op, value)?;
850
851 Ok(Self::from_inner(inner))
852 }
853
854 pub(crate) fn by_id(self, id: E::Key) -> Self {
856 let Self { inner, .. } = self;
857
858 Self::from_inner(inner.by_id(id.to_value()))
859 }
860
861 pub(crate) fn by_ids<I>(self, ids: I) -> Self
863 where
864 I: IntoIterator<Item = E::Key>,
865 {
866 let Self { inner, .. } = self;
867
868 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
869 }
870
871 #[must_use]
873 pub fn delete(mut self) -> Self {
874 self.inner = self.inner.delete();
875 self
876 }
877
878 #[must_use]
885 pub fn limit(mut self, limit: u32) -> Self {
886 self.inner = self.inner.limit(limit);
887 self
888 }
889
890 #[must_use]
896 pub fn offset(mut self, offset: u32) -> Self {
897 self.inner = self.inner.offset(offset);
898 self
899 }
900
901 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
903 let plan = self.planned()?;
904
905 Ok(plan.explain())
906 }
907
908 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
913 let plan = self.inner.build_plan()?;
914
915 Ok(plan.fingerprint().to_string())
916 }
917
918 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
920 where
921 E: EntityValue,
922 {
923 self.inner.explain_execution()
924 }
925
926 pub(in crate::db) fn explain_execution_with_visible_indexes(
927 &self,
928 visible_indexes: &VisibleIndexes<'_>,
929 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
930 where
931 E: EntityValue,
932 {
933 self.inner
934 .explain_execution_with_visible_indexes(visible_indexes)
935 }
936
937 pub fn explain_execution_text(&self) -> Result<String, QueryError>
939 where
940 E: EntityValue,
941 {
942 self.inner.explain_execution_text()
943 }
944
945 pub(in crate::db) fn explain_execution_text_with_visible_indexes(
946 &self,
947 visible_indexes: &VisibleIndexes<'_>,
948 ) -> Result<String, QueryError>
949 where
950 E: EntityValue,
951 {
952 self.inner
953 .explain_execution_text_with_visible_indexes(visible_indexes)
954 }
955
956 pub fn explain_execution_json(&self) -> Result<String, QueryError>
958 where
959 E: EntityValue,
960 {
961 self.inner.explain_execution_json()
962 }
963
964 pub(in crate::db) fn explain_execution_json_with_visible_indexes(
965 &self,
966 visible_indexes: &VisibleIndexes<'_>,
967 ) -> Result<String, QueryError>
968 where
969 E: EntityValue,
970 {
971 self.inner
972 .explain_execution_json_with_visible_indexes(visible_indexes)
973 }
974
975 #[inline(never)]
977 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
978 where
979 E: EntityValue,
980 {
981 self.inner.explain_execution_verbose()
982 }
983
984 #[cfg(test)]
986 #[inline(never)]
987 pub(in crate::db) fn explain_aggregate_terminal(
988 &self,
989 aggregate: AggregateExpr,
990 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
991 where
992 E: EntityValue,
993 {
994 self.inner.explain_aggregate_terminal_with_visible_indexes(
995 &VisibleIndexes::schema_owned(E::MODEL.indexes()),
996 aggregate,
997 )
998 }
999
1000 pub(in crate::db) fn explain_execution_verbose_with_visible_indexes(
1001 &self,
1002 visible_indexes: &VisibleIndexes<'_>,
1003 ) -> Result<String, QueryError>
1004 where
1005 E: EntityValue,
1006 {
1007 self.inner
1008 .explain_execution_verbose_with_visible_indexes(visible_indexes)
1009 }
1010
1011 pub(in crate::db) fn explain_prepared_scalar_terminal_with_visible_indexes(
1012 &self,
1013 visible_indexes: &VisibleIndexes<'_>,
1014 strategy: &PreparedFluentScalarTerminalStrategy,
1015 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1016 where
1017 E: EntityValue,
1018 {
1019 self.inner
1020 .explain_prepared_scalar_terminal_with_visible_indexes(visible_indexes, strategy)
1021 }
1022
1023 pub(in crate::db) fn explain_prepared_order_sensitive_terminal_with_visible_indexes(
1024 &self,
1025 visible_indexes: &VisibleIndexes<'_>,
1026 strategy: &PreparedFluentOrderSensitiveTerminalStrategy,
1027 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1028 where
1029 E: EntityValue,
1030 {
1031 self.inner
1032 .explain_prepared_order_sensitive_terminal_with_visible_indexes(
1033 visible_indexes,
1034 strategy,
1035 )
1036 }
1037
1038 #[cfg(test)]
1039 pub(in crate::db) fn explain_prepared_numeric_field_with_visible_indexes(
1040 &self,
1041 visible_indexes: &VisibleIndexes<'_>,
1042 strategy: &PreparedFluentNumericFieldStrategy,
1043 ) -> Result<ExplainAggregateTerminalPlan, QueryError>
1044 where
1045 E: EntityValue,
1046 {
1047 self.inner
1048 .explain_prepared_numeric_field_with_visible_indexes(visible_indexes, strategy)
1049 }
1050
1051 pub(in crate::db) fn explain_bytes_by_with_visible_indexes(
1052 &self,
1053 visible_indexes: &VisibleIndexes<'_>,
1054 target_field: &str,
1055 ) -> Result<ExplainExecutionNodeDescriptor, QueryError>
1056 where
1057 E: EntityValue,
1058 {
1059 let executable = self
1060 .plan_with_visible_indexes(visible_indexes)?
1061 .into_executable();
1062 let mut descriptor = executable
1063 .explain_load_execution_node_descriptor()
1064 .map_err(QueryError::execute)?;
1065 let projection_mode = executable.bytes_by_projection_mode(target_field);
1066 let projection_mode_label =
1067 ExecutablePlan::<E>::bytes_by_projection_mode_label(projection_mode);
1068
1069 descriptor
1070 .node_properties
1071 .insert("terminal", Value::from("bytes_by"));
1072 descriptor
1073 .node_properties
1074 .insert("terminal_field", Value::from(target_field.to_string()));
1075 descriptor.node_properties.insert(
1076 "terminal_projection_mode",
1077 Value::from(projection_mode_label),
1078 );
1079 descriptor.node_properties.insert(
1080 "terminal_index_only",
1081 Value::from(matches!(
1082 projection_mode,
1083 BytesByProjectionMode::CoveringIndex | BytesByProjectionMode::CoveringConstant
1084 )),
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}