1use crate::{
7 db::{
8 executor::{
9 assemble_aggregate_terminal_execution_descriptor_with_model,
10 assemble_load_execution_node_descriptor_with_model,
11 assemble_load_execution_verbose_diagnostics_with_model,
12 },
13 predicate::{CoercionId, CompareOp, MissingRowPolicy, Predicate},
14 query::{
15 builder::aggregate::AggregateExpr,
16 explain::{
17 ExplainAccessPath, ExplainAggregateTerminalPlan, ExplainExecutionNodeDescriptor,
18 ExplainExecutionNodeType, ExplainOrderPushdown, ExplainPlan, ExplainPredicate,
19 },
20 expr::{FilterExpr, SortExpr},
21 intent::{QueryError, model::QueryModel},
22 plan::{AccessPlannedQuery, LoadSpec, QueryMode},
23 },
24 },
25 traits::{EntityKind, EntityValue, FieldValue, SingletonEntity},
26 value::Value,
27};
28use core::marker::PhantomData;
29
30#[derive(Debug)]
39pub(in crate::db) struct StructuralQuery {
40 intent: QueryModel<'static, Value>,
41}
42
43impl StructuralQuery {
44 #[must_use]
45 pub(in crate::db) const fn new(
46 model: &'static crate::model::entity::EntityModel,
47 consistency: MissingRowPolicy,
48 ) -> Self {
49 Self {
50 intent: QueryModel::new(model, consistency),
51 }
52 }
53
54 #[must_use]
55 const fn mode(&self) -> QueryMode {
56 self.intent.mode()
57 }
58
59 #[must_use]
60 fn has_explicit_order(&self) -> bool {
61 self.intent.has_explicit_order()
62 }
63
64 #[must_use]
65 const fn has_grouping(&self) -> bool {
66 self.intent.has_grouping()
67 }
68
69 #[must_use]
70 const fn load_spec(&self) -> Option<LoadSpec> {
71 match self.intent.mode() {
72 QueryMode::Load(spec) => Some(spec),
73 QueryMode::Delete(_) => None,
74 }
75 }
76
77 #[must_use]
78 pub(in crate::db) fn filter(mut self, predicate: Predicate) -> Self {
79 self.intent = self.intent.filter(predicate);
80 self
81 }
82
83 fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
84 let Self { intent } = self;
85 let intent = intent.filter_expr(expr)?;
86
87 Ok(Self { intent })
88 }
89
90 fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
91 let Self { intent } = self;
92 let intent = intent.sort_expr(expr)?;
93
94 Ok(Self { intent })
95 }
96
97 #[must_use]
98 pub(in crate::db) fn order_by(mut self, field: impl AsRef<str>) -> Self {
99 self.intent = self.intent.order_by(field);
100 self
101 }
102
103 #[must_use]
104 pub(in crate::db) fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
105 self.intent = self.intent.order_by_desc(field);
106 self
107 }
108
109 #[must_use]
110 pub(in crate::db) fn distinct(mut self) -> Self {
111 self.intent = self.intent.distinct();
112 self
113 }
114
115 #[cfg(feature = "sql")]
116 #[must_use]
117 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
118 where
119 I: IntoIterator<Item = S>,
120 S: Into<String>,
121 {
122 self.intent = self.intent.select_fields(fields);
123 self
124 }
125
126 pub(in crate::db) fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
127 let Self { intent } = self;
128 let intent = intent.push_group_field(field.as_ref())?;
129
130 Ok(Self { intent })
131 }
132
133 #[must_use]
134 pub(in crate::db) fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
135 self.intent = self.intent.push_group_aggregate(aggregate);
136 self
137 }
138
139 #[must_use]
140 fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
141 self.intent = self.intent.grouped_limits(max_groups, max_group_bytes);
142 self
143 }
144
145 pub(in crate::db) fn having_group(
146 self,
147 field: impl AsRef<str>,
148 op: CompareOp,
149 value: Value,
150 ) -> Result<Self, QueryError> {
151 let field = field.as_ref().to_owned();
152 let Self { intent } = self;
153 let intent = intent.push_having_group_clause(&field, op, value)?;
154
155 Ok(Self { intent })
156 }
157
158 pub(in crate::db) fn having_aggregate(
159 self,
160 aggregate_index: usize,
161 op: CompareOp,
162 value: Value,
163 ) -> Result<Self, QueryError> {
164 let Self { intent } = self;
165 let intent = intent.push_having_aggregate_clause(aggregate_index, op, value)?;
166
167 Ok(Self { intent })
168 }
169
170 #[must_use]
171 fn by_id(self, id: Value) -> Self {
172 let Self { intent } = self;
173 Self {
174 intent: intent.by_id(id),
175 }
176 }
177
178 #[must_use]
179 fn by_ids<I>(self, ids: I) -> Self
180 where
181 I: IntoIterator<Item = Value>,
182 {
183 let Self { intent } = self;
184 Self {
185 intent: intent.by_ids(ids),
186 }
187 }
188
189 #[must_use]
190 fn only(self, id: Value) -> Self {
191 let Self { intent } = self;
192
193 Self {
194 intent: intent.only(id),
195 }
196 }
197
198 #[must_use]
199 pub(in crate::db) fn delete(mut self) -> Self {
200 self.intent = self.intent.delete();
201 self
202 }
203
204 #[must_use]
205 pub(in crate::db) fn limit(mut self, limit: u32) -> Self {
206 self.intent = self.intent.limit(limit);
207 self
208 }
209
210 #[must_use]
211 pub(in crate::db) fn offset(mut self, offset: u32) -> Self {
212 self.intent = self.intent.offset(offset);
213 self
214 }
215
216 pub(in crate::db) fn build_plan(&self) -> Result<AccessPlannedQuery, QueryError> {
217 self.intent.build_plan_model()
218 }
219
220 #[must_use]
221 pub(in crate::db) const fn model(&self) -> &'static crate::model::entity::EntityModel {
222 self.intent.model()
223 }
224
225 pub(in crate::db) fn explain_execution(
227 &self,
228 ) -> Result<ExplainExecutionNodeDescriptor, QueryError> {
229 let plan = self.build_plan()?;
230
231 assemble_load_execution_node_descriptor_with_model(self.intent.model(), &plan)
232 .map_err(QueryError::execute)
233 }
234
235 pub(in crate::db) fn explain_execution_text(&self) -> Result<String, QueryError> {
238 Ok(self.explain_execution()?.render_text_tree())
239 }
240
241 pub(in crate::db) fn explain_execution_json(&self) -> Result<String, QueryError> {
244 Ok(self.explain_execution()?.render_json_canonical())
245 }
246
247 pub(in crate::db) fn explain_execution_verbose(&self) -> Result<String, QueryError> {
250 let plan = self.build_plan()?;
251 let descriptor =
252 assemble_load_execution_node_descriptor_with_model(self.intent.model(), &plan)
253 .map_err(QueryError::execute)?;
254 let route_diagnostics =
255 assemble_load_execution_verbose_diagnostics_with_model(self.intent.model(), &plan)
256 .map_err(QueryError::execute)?;
257 let explain = plan.explain_with_model(self.intent.model());
258
259 let mut lines = vec![descriptor.render_text_tree_verbose()];
261 lines.extend(route_diagnostics);
262
263 lines.push(format!(
265 "diagnostic.descriptor.has_top_n_seek={}",
266 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
267 ));
268 lines.push(format!(
269 "diagnostic.descriptor.has_index_range_limit_pushdown={}",
270 contains_execution_node_type(
271 &descriptor,
272 ExplainExecutionNodeType::IndexRangeLimitPushdown,
273 )
274 ));
275 lines.push(format!(
276 "diagnostic.descriptor.has_index_predicate_prefilter={}",
277 contains_execution_node_type(
278 &descriptor,
279 ExplainExecutionNodeType::IndexPredicatePrefilter,
280 )
281 ));
282 lines.push(format!(
283 "diagnostic.descriptor.has_residual_predicate_filter={}",
284 contains_execution_node_type(
285 &descriptor,
286 ExplainExecutionNodeType::ResidualPredicateFilter,
287 )
288 ));
289
290 lines.push(format!("diagnostic.plan.mode={:?}", explain.mode()));
292 lines.push(format!(
293 "diagnostic.plan.order_pushdown={}",
294 plan_order_pushdown_label(explain.order_pushdown())
295 ));
296 lines.push(format!(
297 "diagnostic.plan.predicate_pushdown={}",
298 plan_predicate_pushdown_label(explain.predicate(), explain.access())
299 ));
300 lines.push(format!("diagnostic.plan.distinct={}", explain.distinct()));
301 lines.push(format!("diagnostic.plan.page={:?}", explain.page()));
302 lines.push(format!(
303 "diagnostic.plan.consistency={:?}",
304 explain.consistency()
305 ));
306
307 Ok(lines.join("\n"))
308 }
309
310 pub(in crate::db) fn explain_aggregate_terminal(
313 &self,
314 aggregate: AggregateExpr,
315 ) -> Result<ExplainAggregateTerminalPlan, QueryError> {
316 let plan = self.build_plan()?;
317 let query_explain = plan.explain_with_model(self.intent.model());
318 let terminal = aggregate.kind();
319 let execution = assemble_aggregate_terminal_execution_descriptor_with_model(
320 self.intent.model(),
321 &plan,
322 aggregate,
323 );
324
325 Ok(ExplainAggregateTerminalPlan::new(
326 query_explain,
327 terminal,
328 execution,
329 ))
330 }
331}
332
333#[derive(Debug)]
342struct PlannedQueryCore {
343 model: &'static crate::model::entity::EntityModel,
344 plan: AccessPlannedQuery,
345}
346
347impl PlannedQueryCore {
348 #[must_use]
349 const fn new(
350 model: &'static crate::model::entity::EntityModel,
351 plan: AccessPlannedQuery,
352 ) -> Self {
353 Self { model, plan }
354 }
355
356 #[must_use]
357 fn explain(&self) -> ExplainPlan {
358 self.plan.explain_with_model(self.model)
359 }
360
361 #[must_use]
363 fn plan_hash_hex(&self) -> String {
364 self.plan.fingerprint().to_string()
365 }
366}
367
368#[derive(Debug)]
377pub struct PlannedQuery<E: EntityKind> {
378 inner: PlannedQueryCore,
379 _marker: PhantomData<E>,
380}
381
382impl<E: EntityKind> PlannedQuery<E> {
383 #[must_use]
384 const fn from_inner(inner: PlannedQueryCore) -> Self {
385 Self {
386 inner,
387 _marker: PhantomData,
388 }
389 }
390
391 #[must_use]
392 pub fn explain(&self) -> ExplainPlan {
393 self.inner.explain()
394 }
395
396 #[must_use]
398 pub fn plan_hash_hex(&self) -> String {
399 self.inner.plan_hash_hex()
400 }
401}
402
403#[derive(Clone, Debug)]
412struct CompiledQueryCore {
413 model: &'static crate::model::entity::EntityModel,
414 entity_path: &'static str,
415 plan: AccessPlannedQuery,
416}
417
418impl CompiledQueryCore {
419 #[must_use]
420 const fn new(
421 model: &'static crate::model::entity::EntityModel,
422 entity_path: &'static str,
423 plan: AccessPlannedQuery,
424 ) -> Self {
425 Self {
426 model,
427 entity_path,
428 plan,
429 }
430 }
431
432 #[must_use]
433 fn explain(&self) -> ExplainPlan {
434 self.plan.explain_with_model(self.model)
435 }
436
437 #[must_use]
439 fn plan_hash_hex(&self) -> String {
440 self.plan.fingerprint().to_string()
441 }
442
443 #[must_use]
444 #[cfg(feature = "sql")]
445 fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
446 self.plan.projection_spec(self.model)
447 }
448
449 #[must_use]
450 fn into_inner(self) -> AccessPlannedQuery {
451 self.plan
452 }
453}
454
455#[derive(Clone, Debug)]
464pub struct CompiledQuery<E: EntityKind> {
465 inner: CompiledQueryCore,
466 _marker: PhantomData<E>,
467}
468
469impl<E: EntityKind> CompiledQuery<E> {
470 #[must_use]
471 const fn from_inner(inner: CompiledQueryCore) -> Self {
472 Self {
473 inner,
474 _marker: PhantomData,
475 }
476 }
477
478 #[must_use]
479 pub fn explain(&self) -> ExplainPlan {
480 self.inner.explain()
481 }
482
483 #[must_use]
485 pub fn plan_hash_hex(&self) -> String {
486 self.inner.plan_hash_hex()
487 }
488
489 #[must_use]
490 #[cfg(feature = "sql")]
491 pub(in crate::db) fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
492 self.inner.projection_spec()
493 }
494
495 pub(in crate::db) fn into_executable(self) -> crate::db::executor::ExecutablePlan<E> {
497 assert!(
498 self.inner.entity_path == E::PATH,
499 "compiled query entity mismatch: compiled for '{}', requested '{}'",
500 self.inner.entity_path,
501 E::PATH,
502 );
503
504 crate::db::executor::ExecutablePlan::new(self.into_inner())
505 }
506
507 #[must_use]
508 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
509 self.inner.into_inner()
510 }
511}
512
513#[derive(Debug)]
525pub struct Query<E: EntityKind> {
526 inner: StructuralQuery,
527 _marker: PhantomData<E>,
528}
529
530impl<E: EntityKind> Query<E> {
531 pub(in crate::db) const fn from_inner(inner: StructuralQuery) -> Self {
533 Self {
534 inner,
535 _marker: PhantomData,
536 }
537 }
538
539 #[must_use]
543 pub const fn new(consistency: MissingRowPolicy) -> Self {
544 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
545 }
546
547 #[must_use]
549 pub const fn mode(&self) -> QueryMode {
550 self.inner.mode()
551 }
552
553 #[must_use]
554 pub(crate) fn has_explicit_order(&self) -> bool {
555 self.inner.has_explicit_order()
556 }
557
558 #[must_use]
559 pub(crate) const fn has_grouping(&self) -> bool {
560 self.inner.has_grouping()
561 }
562
563 #[must_use]
564 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
565 self.inner.load_spec()
566 }
567
568 #[must_use]
570 pub fn filter(mut self, predicate: Predicate) -> Self {
571 self.inner = self.inner.filter(predicate);
572 self
573 }
574
575 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
577 let Self { inner, .. } = self;
578 let inner = inner.filter_expr(expr)?;
579
580 Ok(Self::from_inner(inner))
581 }
582
583 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
585 let Self { inner, .. } = self;
586 let inner = inner.sort_expr(expr)?;
587
588 Ok(Self::from_inner(inner))
589 }
590
591 #[must_use]
593 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
594 self.inner = self.inner.order_by(field);
595 self
596 }
597
598 #[must_use]
600 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
601 self.inner = self.inner.order_by_desc(field);
602 self
603 }
604
605 #[must_use]
607 pub fn distinct(mut self) -> Self {
608 self.inner = self.inner.distinct();
609 self
610 }
611
612 #[cfg(feature = "sql")]
615 #[cfg_attr(not(test), allow(dead_code))]
616 #[must_use]
617 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
618 where
619 I: IntoIterator<Item = S>,
620 S: Into<String>,
621 {
622 self.inner = self.inner.select_fields(fields);
623 self
624 }
625
626 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
628 let Self { inner, .. } = self;
629 let inner = inner.group_by(field)?;
630
631 Ok(Self::from_inner(inner))
632 }
633
634 #[must_use]
636 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
637 self.inner = self.inner.aggregate(aggregate);
638 self
639 }
640
641 #[must_use]
643 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
644 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
645 self
646 }
647
648 pub fn having_group(
650 self,
651 field: impl AsRef<str>,
652 op: CompareOp,
653 value: Value,
654 ) -> Result<Self, QueryError> {
655 let Self { inner, .. } = self;
656 let inner = inner.having_group(field, op, value)?;
657
658 Ok(Self::from_inner(inner))
659 }
660
661 pub fn having_aggregate(
663 self,
664 aggregate_index: usize,
665 op: CompareOp,
666 value: Value,
667 ) -> Result<Self, QueryError> {
668 let Self { inner, .. } = self;
669 let inner = inner.having_aggregate(aggregate_index, op, value)?;
670
671 Ok(Self::from_inner(inner))
672 }
673
674 pub(crate) fn by_id(self, id: E::Key) -> Self {
676 let Self { inner, .. } = self;
677
678 Self::from_inner(inner.by_id(id.to_value()))
679 }
680
681 pub(crate) fn by_ids<I>(self, ids: I) -> Self
683 where
684 I: IntoIterator<Item = E::Key>,
685 {
686 let Self { inner, .. } = self;
687
688 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
689 }
690
691 #[must_use]
693 pub fn delete(mut self) -> Self {
694 self.inner = self.inner.delete();
695 self
696 }
697
698 #[must_use]
705 pub fn limit(mut self, limit: u32) -> Self {
706 self.inner = self.inner.limit(limit);
707 self
708 }
709
710 #[must_use]
716 pub fn offset(mut self, offset: u32) -> Self {
717 self.inner = self.inner.offset(offset);
718 self
719 }
720
721 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
723 let plan = self.planned()?;
724
725 Ok(plan.explain())
726 }
727
728 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
733 let plan = self.inner.build_plan()?;
734
735 Ok(plan.fingerprint().to_string())
736 }
737
738 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
740 where
741 E: EntityValue,
742 {
743 self.inner.explain_execution()
744 }
745
746 pub fn explain_execution_text(&self) -> Result<String, QueryError>
748 where
749 E: EntityValue,
750 {
751 self.inner.explain_execution_text()
752 }
753
754 pub fn explain_execution_json(&self) -> Result<String, QueryError>
756 where
757 E: EntityValue,
758 {
759 self.inner.explain_execution_json()
760 }
761
762 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
764 where
765 E: EntityValue,
766 {
767 self.inner.explain_execution_verbose()
768 }
769
770 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
772 let plan = self.inner.build_plan()?;
773 let _projection = plan.projection_spec(E::MODEL);
774
775 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
776 E::MODEL,
777 plan,
778 )))
779 }
780
781 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
785 let plan = self.inner.build_plan()?;
786 let _projection = plan.projection_spec(E::MODEL);
787
788 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
789 E::MODEL,
790 E::PATH,
791 plan,
792 )))
793 }
794}
795
796fn contains_execution_node_type(
797 descriptor: &ExplainExecutionNodeDescriptor,
798 target: ExplainExecutionNodeType,
799) -> bool {
800 descriptor.node_type() == target
801 || descriptor
802 .children()
803 .iter()
804 .any(|child| contains_execution_node_type(child, target))
805}
806
807fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
808 match order_pushdown {
809 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
810 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
811 format!("eligible(index={index},prefix_len={prefix_len})",)
812 }
813 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
814 }
815}
816
817fn plan_predicate_pushdown_label(
818 predicate: &ExplainPredicate,
819 access: &ExplainAccessPath,
820) -> String {
821 let access_label = match access {
822 ExplainAccessPath::ByKey { .. } => "by_key",
823 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
824 ExplainAccessPath::ByKeys { .. } => "by_keys",
825 ExplainAccessPath::KeyRange { .. } => "key_range",
826 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
827 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
828 ExplainAccessPath::IndexRange { .. } => "index_range",
829 ExplainAccessPath::FullScan => "full_scan",
830 ExplainAccessPath::Union(_) => "union",
831 ExplainAccessPath::Intersection(_) => "intersection",
832 };
833 if matches!(predicate, ExplainPredicate::None) {
834 return "none".to_string();
835 }
836 if matches!(access, ExplainAccessPath::FullScan) {
837 if explain_predicate_contains_non_strict_compare(predicate) {
838 return "fallback(non_strict_compare_coercion)".to_string();
839 }
840 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
841 return "fallback(starts_with_empty_prefix)".to_string();
842 }
843 if explain_predicate_contains_is_null(predicate) {
844 return "fallback(is_null_full_scan)".to_string();
845 }
846 if explain_predicate_contains_text_scan_operator(predicate) {
847 return "fallback(text_operator_full_scan)".to_string();
848 }
849
850 return format!("fallback({access_label})");
851 }
852
853 format!("applied({access_label})")
854}
855
856fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
857 match predicate {
858 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
859 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
860 .iter()
861 .any(explain_predicate_contains_non_strict_compare),
862 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
863 ExplainPredicate::None
864 | ExplainPredicate::True
865 | ExplainPredicate::False
866 | ExplainPredicate::IsNull { .. }
867 | ExplainPredicate::IsNotNull { .. }
868 | ExplainPredicate::IsMissing { .. }
869 | ExplainPredicate::IsEmpty { .. }
870 | ExplainPredicate::IsNotEmpty { .. }
871 | ExplainPredicate::TextContains { .. }
872 | ExplainPredicate::TextContainsCi { .. } => false,
873 }
874}
875
876fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
877 match predicate {
878 ExplainPredicate::IsNull { .. } => true,
879 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
880 children.iter().any(explain_predicate_contains_is_null)
881 }
882 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
883 ExplainPredicate::None
884 | ExplainPredicate::True
885 | ExplainPredicate::False
886 | ExplainPredicate::Compare { .. }
887 | ExplainPredicate::IsNotNull { .. }
888 | ExplainPredicate::IsMissing { .. }
889 | ExplainPredicate::IsEmpty { .. }
890 | ExplainPredicate::IsNotEmpty { .. }
891 | ExplainPredicate::TextContains { .. }
892 | ExplainPredicate::TextContainsCi { .. } => false,
893 }
894}
895
896fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
897 match predicate {
898 ExplainPredicate::Compare {
899 op: CompareOp::StartsWith,
900 value: Value::Text(prefix),
901 ..
902 } => prefix.is_empty(),
903 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
904 .iter()
905 .any(explain_predicate_contains_empty_prefix_starts_with),
906 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
907 ExplainPredicate::None
908 | ExplainPredicate::True
909 | ExplainPredicate::False
910 | ExplainPredicate::Compare { .. }
911 | ExplainPredicate::IsNull { .. }
912 | ExplainPredicate::IsNotNull { .. }
913 | ExplainPredicate::IsMissing { .. }
914 | ExplainPredicate::IsEmpty { .. }
915 | ExplainPredicate::IsNotEmpty { .. }
916 | ExplainPredicate::TextContains { .. }
917 | ExplainPredicate::TextContainsCi { .. } => false,
918 }
919}
920
921fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
922 match predicate {
923 ExplainPredicate::Compare {
924 op: CompareOp::EndsWith,
925 ..
926 }
927 | ExplainPredicate::TextContains { .. }
928 | ExplainPredicate::TextContainsCi { .. } => true,
929 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
930 .iter()
931 .any(explain_predicate_contains_text_scan_operator),
932 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
933 ExplainPredicate::Compare { .. }
934 | ExplainPredicate::None
935 | ExplainPredicate::True
936 | ExplainPredicate::False
937 | ExplainPredicate::IsNull { .. }
938 | ExplainPredicate::IsNotNull { .. }
939 | ExplainPredicate::IsMissing { .. }
940 | ExplainPredicate::IsEmpty { .. }
941 | ExplainPredicate::IsNotEmpty { .. } => false,
942 }
943}
944
945impl<E> Query<E>
946where
947 E: EntityKind + SingletonEntity,
948 E::Key: Default,
949{
950 pub(crate) fn only(self) -> Self {
952 let Self { inner, .. } = self;
953
954 Self::from_inner(inner.only(E::Key::default().to_value()))
955 }
956}