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