1use crate::{
7 db::{
8 access::{AccessPlan, ExecutableAccessPlan},
9 predicate::IndexCompileTarget,
10 predicate::{PredicateExecutionModel, PredicateProgram},
11 query::plan::{
12 AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
13 ExecutionShapeSignature, GroupHavingExpr, GroupHavingValueExpr, GroupPlan,
14 GroupedAggregateExecutionSpec, GroupedDistinctExecutionStrategy, GroupedPlanStrategy,
15 LogicalPlan, PlannerRouteProfile, QueryMode, ResolvedOrder, ResolvedOrderField,
16 ResolvedOrderValueSource, ScalarPlan, StaticPlanningShape,
17 derive_logical_pushdown_eligibility,
18 expr::{
19 Expr, ProjectionField, ProjectionSpec, ScalarProjectionExpr,
20 compile_scalar_projection_expr, compile_scalar_projection_plan,
21 parse_supported_computed_order_expr, projection_field_expr,
22 },
23 group::GroupedAggregateProjectionSpec,
24 grouped_aggregate_execution_specs_with_model,
25 grouped_aggregate_projection_specs_from_projection_spec,
26 grouped_cursor_policy_violation, grouped_plan_strategy, lower_direct_projection_slots,
27 lower_projection_identity, lower_projection_intent,
28 residual_query_predicate_after_access_path_bounds,
29 residual_query_predicate_after_filtered_access,
30 resolved_grouped_distinct_execution_strategy_for_model,
31 },
32 },
33 error::InternalError,
34 model::{
35 entity::{EntityModel, resolve_field_slot},
36 index::IndexKeyItemsRef,
37 },
38};
39
40impl QueryMode {
41 #[must_use]
43 pub const fn is_load(&self) -> bool {
44 match self {
45 Self::Load(_) => true,
46 Self::Delete(_) => false,
47 }
48 }
49
50 #[must_use]
52 pub const fn is_delete(&self) -> bool {
53 match self {
54 Self::Delete(_) => true,
55 Self::Load(_) => false,
56 }
57 }
58}
59
60impl LogicalPlan {
61 #[must_use]
63 pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
64 match self {
65 Self::Scalar(plan) => plan,
66 Self::Grouped(plan) => &plan.scalar,
67 }
68 }
69
70 #[must_use]
72 #[cfg(test)]
73 pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
74 match self {
75 Self::Scalar(plan) => plan,
76 Self::Grouped(plan) => &mut plan.scalar,
77 }
78 }
79
80 #[must_use]
82 #[cfg(test)]
83 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
84 self.scalar_semantics()
85 }
86
87 #[must_use]
89 #[cfg(test)]
90 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
91 self.scalar_semantics_mut()
92 }
93}
94
95impl AccessPlannedQuery {
96 #[must_use]
98 pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
99 self.logical.scalar_semantics()
100 }
101
102 #[must_use]
104 #[cfg(test)]
105 pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
106 self.logical.scalar_semantics_mut()
107 }
108
109 #[must_use]
111 #[cfg(test)]
112 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
113 self.scalar_plan()
114 }
115
116 #[must_use]
118 #[cfg(test)]
119 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
120 self.scalar_plan_mut()
121 }
122
123 #[must_use]
125 pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
126 match &self.logical {
127 LogicalPlan::Scalar(_) => None,
128 LogicalPlan::Grouped(plan) => Some(plan),
129 }
130 }
131
132 #[must_use]
134 pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
135 if let Some(static_shape) = &self.static_planning_shape {
136 return static_shape.projection_spec.clone();
137 }
138
139 lower_projection_intent(model, &self.logical, &self.projection_selection)
140 }
141
142 #[must_use]
144 pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
145 lower_projection_identity(&self.logical)
146 }
147
148 #[must_use]
154 pub(in crate::db) fn execution_preparation_predicate(&self) -> Option<PredicateExecutionModel> {
155 let query_predicate = self.scalar_plan().predicate.as_ref()?;
156
157 match self.access.selected_index_model() {
158 Some(index) => residual_query_predicate_after_filtered_access(index, query_predicate),
159 None => Some(query_predicate.clone()),
160 }
161 }
162
163 #[must_use]
167 pub(in crate::db) fn effective_execution_predicate(&self) -> Option<PredicateExecutionModel> {
168 let filtered_residual = self.execution_preparation_predicate();
171 let filtered_residual = filtered_residual.as_ref()?;
172
173 residual_query_predicate_after_access_path_bounds(self.access.as_path(), filtered_residual)
177 }
178
179 #[must_use]
181 pub(in crate::db) const fn execution_preparation_compiled_predicate(
182 &self,
183 ) -> Option<&PredicateProgram> {
184 self.static_planning_shape()
185 .execution_preparation_compiled_predicate
186 .as_ref()
187 }
188
189 #[must_use]
191 pub(in crate::db) const fn effective_runtime_compiled_predicate(
192 &self,
193 ) -> Option<&PredicateProgram> {
194 self.static_planning_shape()
195 .effective_runtime_compiled_predicate
196 .as_ref()
197 }
198
199 #[must_use]
201 pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
202 if !self.scalar_plan().distinct {
203 return DistinctExecutionStrategy::None;
204 }
205
206 match distinct_runtime_dedup_strategy(&self.access) {
210 Some(strategy) => strategy,
211 None => DistinctExecutionStrategy::None,
212 }
213 }
214
215 pub(in crate::db) fn finalize_planner_route_profile_for_model(&mut self, model: &EntityModel) {
217 self.set_planner_route_profile(project_planner_route_profile_for_model(model, self));
218 }
219
220 pub(in crate::db) fn finalize_static_planning_shape_for_model(
222 &mut self,
223 model: &EntityModel,
224 ) -> Result<(), InternalError> {
225 self.static_planning_shape = Some(project_static_planning_shape_for_model(model, self)?);
226
227 Ok(())
228 }
229
230 #[must_use]
232 pub(in crate::db) fn execution_shape_signature(
233 &self,
234 entity_path: &'static str,
235 ) -> ExecutionShapeSignature {
236 ExecutionShapeSignature::new(self.continuation_signature(entity_path))
237 }
238
239 #[must_use]
242 pub(in crate::db) fn predicate_fully_satisfied_by_access_contract(&self) -> bool {
243 self.scalar_plan().predicate.is_some() && self.effective_execution_predicate().is_none()
244 }
245
246 #[must_use]
250 pub(in crate::db) fn has_residual_predicate(&self) -> bool {
251 self.scalar_plan().predicate.is_some()
252 && !self.predicate_fully_satisfied_by_access_contract()
253 }
254
255 #[must_use]
257 pub(in crate::db) fn scalar_projection_plan(&self) -> Option<&[ScalarProjectionExpr]> {
258 self.static_planning_shape()
259 .scalar_projection_plan
260 .as_deref()
261 }
262
263 #[must_use]
265 pub(in crate::db) const fn primary_key_name(&self) -> &'static str {
266 self.static_planning_shape().primary_key_name
267 }
268
269 #[must_use]
271 pub(in crate::db) const fn projection_referenced_slots(&self) -> &[usize] {
272 self.static_planning_shape()
273 .projection_referenced_slots
274 .as_slice()
275 }
276
277 #[must_use]
279 #[cfg(any(test, feature = "diagnostics"))]
280 pub(in crate::db) const fn projected_slot_mask(&self) -> &[bool] {
281 self.static_planning_shape().projected_slot_mask.as_slice()
282 }
283
284 #[must_use]
286 pub(in crate::db) const fn projection_is_model_identity(&self) -> bool {
287 self.static_planning_shape().projection_is_model_identity
288 }
289
290 #[must_use]
292 pub(in crate::db) fn order_referenced_slots(&self) -> Option<&[usize]> {
293 self.static_planning_shape()
294 .order_referenced_slots
295 .as_deref()
296 }
297
298 #[must_use]
300 pub(in crate::db) const fn resolved_order(&self) -> Option<&ResolvedOrder> {
301 self.static_planning_shape().resolved_order.as_ref()
302 }
303
304 #[must_use]
306 pub(in crate::db) fn slot_map(&self) -> Option<&[usize]> {
307 self.static_planning_shape().slot_map.as_deref()
308 }
309
310 #[must_use]
312 pub(in crate::db) fn grouped_aggregate_execution_specs(
313 &self,
314 ) -> Option<&[GroupedAggregateExecutionSpec]> {
315 self.static_planning_shape()
316 .grouped_aggregate_execution_specs
317 .as_deref()
318 }
319
320 #[must_use]
322 pub(in crate::db) const fn grouped_distinct_execution_strategy(
323 &self,
324 ) -> Option<&GroupedDistinctExecutionStrategy> {
325 self.static_planning_shape()
326 .grouped_distinct_execution_strategy
327 .as_ref()
328 }
329
330 #[must_use]
332 pub(in crate::db) const fn frozen_projection_spec(&self) -> &ProjectionSpec {
333 &self.static_planning_shape().projection_spec
334 }
335
336 #[must_use]
338 pub(in crate::db) fn frozen_direct_projection_slots(&self) -> Option<&[usize]> {
339 self.static_planning_shape()
340 .projection_direct_slots
341 .as_deref()
342 }
343
344 #[must_use]
346 pub(in crate::db) fn index_compile_targets(&self) -> Option<&[IndexCompileTarget]> {
347 self.static_planning_shape()
348 .index_compile_targets
349 .as_deref()
350 }
351
352 const fn static_planning_shape(&self) -> &StaticPlanningShape {
353 self.static_planning_shape
354 .as_ref()
355 .expect("access-planned queries must freeze static planning shape before execution")
356 }
357}
358
359fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
360 match access {
361 AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
362 Some(DistinctExecutionStrategy::PreOrdered)
363 }
364 AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
365 Some(DistinctExecutionStrategy::HashMaterialize)
366 }
367 AccessPlan::Path(_) => None,
368 }
369}
370
371fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
372 let is_grouped_safe = plan
373 .grouped_plan()
374 .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
375
376 ContinuationPolicy::new(
377 true, true, is_grouped_safe,
380 )
381}
382
383#[must_use]
385pub(in crate::db) fn project_planner_route_profile_for_model(
386 model: &EntityModel,
387 plan: &AccessPlannedQuery,
388) -> PlannerRouteProfile {
389 let secondary_order_contract = plan
390 .scalar_plan()
391 .order
392 .as_ref()
393 .and_then(|order| order.deterministic_secondary_order_contract(model.primary_key.name));
394
395 PlannerRouteProfile::new(
396 derive_continuation_policy_validated(plan),
397 derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
398 secondary_order_contract,
399 )
400}
401
402fn project_static_planning_shape_for_model(
403 model: &EntityModel,
404 plan: &AccessPlannedQuery,
405) -> Result<StaticPlanningShape, InternalError> {
406 let projection_spec = lower_projection_intent(model, &plan.logical, &plan.projection_selection);
407 let execution_preparation_compiled_predicate = compile_optional_predicate_with_model(
408 model,
409 plan.execution_preparation_predicate().as_ref(),
410 );
411 let effective_runtime_compiled_predicate =
412 compile_optional_predicate_with_model(model, plan.effective_execution_predicate().as_ref());
413 let scalar_projection_plan =
414 if plan.grouped_plan().is_none() {
415 Some(compile_scalar_projection_plan(model, &projection_spec).ok_or_else(|| {
416 InternalError::query_executor_invariant(
417 "scalar projection program must compile during static planning finalization",
418 )
419 })?)
420 } else {
421 None
422 };
423 let (grouped_aggregate_execution_specs, grouped_distinct_execution_strategy) =
424 resolve_grouped_static_planning_semantics(model, plan, &projection_spec)?;
425 let projection_direct_slots =
426 lower_direct_projection_slots(model, &plan.logical, &plan.projection_selection);
427 let projection_referenced_slots =
428 projection_referenced_slots_for_spec(model, &projection_spec)?;
429 let projected_slot_mask =
430 projected_slot_mask_for_spec(model, &projection_spec, projection_direct_slots.as_deref());
431 let projection_is_model_identity =
432 projection_is_model_identity_for_spec(model, &projection_spec);
433 let resolved_order = resolved_order_for_plan(model, plan)?;
434 let order_referenced_slots = order_referenced_slots_for_resolved_order(resolved_order.as_ref());
435 let slot_map = slot_map_for_model_plan(model, plan);
436 let index_compile_targets = index_compile_targets_for_model_plan(model, plan);
437
438 Ok(StaticPlanningShape {
439 primary_key_name: model.primary_key.name,
440 projection_spec,
441 execution_preparation_compiled_predicate,
442 effective_runtime_compiled_predicate,
443 scalar_projection_plan,
444 grouped_aggregate_execution_specs,
445 grouped_distinct_execution_strategy,
446 projection_direct_slots,
447 projection_referenced_slots,
448 projected_slot_mask,
449 projection_is_model_identity,
450 resolved_order,
451 order_referenced_slots,
452 slot_map,
453 index_compile_targets,
454 })
455}
456
457fn compile_optional_predicate_with_model(
460 model: &EntityModel,
461 predicate: Option<&PredicateExecutionModel>,
462) -> Option<PredicateProgram> {
463 predicate.map(|predicate| PredicateProgram::compile_with_model(model, predicate))
464}
465
466fn resolve_grouped_static_planning_semantics(
470 model: &EntityModel,
471 plan: &AccessPlannedQuery,
472 projection_spec: &ProjectionSpec,
473) -> Result<
474 (
475 Option<Vec<GroupedAggregateExecutionSpec>>,
476 Option<GroupedDistinctExecutionStrategy>,
477 ),
478 InternalError,
479> {
480 let Some(grouped) = plan.grouped_plan() else {
481 return Ok((None, None));
482 };
483
484 #[cfg(not(test))]
485 let mut aggregate_projection_specs = grouped_aggregate_projection_specs_from_projection_spec(
486 projection_spec,
487 grouped.group.group_fields.as_slice(),
488 grouped.group.aggregates.as_slice(),
489 );
490 #[cfg(test)]
491 let mut aggregate_projection_specs = grouped_aggregate_projection_specs_from_projection_spec(
492 projection_spec,
493 grouped.group.group_fields.as_slice(),
494 grouped.group.aggregates.as_slice(),
495 )?;
496 extend_grouped_having_aggregate_projection_specs(&mut aggregate_projection_specs, grouped)?;
497
498 let grouped_aggregate_execution_specs = Some(grouped_aggregate_execution_specs_with_model(
499 model,
500 aggregate_projection_specs.as_slice(),
501 )?);
502 let grouped_distinct_execution_strategy =
503 Some(resolved_grouped_distinct_execution_strategy_for_model(
504 model,
505 grouped.group.group_fields.as_slice(),
506 grouped.group.aggregates.as_slice(),
507 grouped.having_expr.as_ref(),
508 )?);
509
510 Ok((
511 grouped_aggregate_execution_specs,
512 grouped_distinct_execution_strategy,
513 ))
514}
515
516fn extend_grouped_having_aggregate_projection_specs(
517 aggregate_projection_specs: &mut Vec<GroupedAggregateProjectionSpec>,
518 grouped: &GroupPlan,
519) -> Result<(), InternalError> {
520 if let Some(having_expr) = grouped.having_expr.as_ref() {
521 collect_grouped_having_expr_aggregate_projection_specs(
522 aggregate_projection_specs,
523 grouped,
524 having_expr,
525 )?;
526 }
527
528 Ok(())
529}
530
531fn collect_grouped_having_expr_aggregate_projection_specs(
532 aggregate_projection_specs: &mut Vec<GroupedAggregateProjectionSpec>,
533 grouped: &GroupPlan,
534 expr: &GroupHavingExpr,
535) -> Result<(), InternalError> {
536 match expr {
537 GroupHavingExpr::Compare { left, right, .. } => {
538 collect_grouped_having_value_expr_aggregate_projection_specs(
539 aggregate_projection_specs,
540 grouped,
541 left,
542 )?;
543 collect_grouped_having_value_expr_aggregate_projection_specs(
544 aggregate_projection_specs,
545 grouped,
546 right,
547 )?;
548 }
549 GroupHavingExpr::And(children) => {
550 for child in children {
551 collect_grouped_having_expr_aggregate_projection_specs(
552 aggregate_projection_specs,
553 grouped,
554 child,
555 )?;
556 }
557 }
558 }
559
560 Ok(())
561}
562
563fn collect_grouped_having_value_expr_aggregate_projection_specs(
564 aggregate_projection_specs: &mut Vec<GroupedAggregateProjectionSpec>,
565 grouped: &GroupPlan,
566 expr: &GroupHavingValueExpr,
567) -> Result<(), InternalError> {
568 match expr {
569 GroupHavingValueExpr::GroupField(_) | GroupHavingValueExpr::Literal(_) => {}
570 GroupHavingValueExpr::AggregateIndex(aggregate_index) => {
571 push_grouped_having_aggregate_projection_spec(
572 aggregate_projection_specs,
573 grouped,
574 *aggregate_index,
575 )?;
576 }
577 GroupHavingValueExpr::FunctionCall { args, .. } => {
578 for arg in args {
579 collect_grouped_having_value_expr_aggregate_projection_specs(
580 aggregate_projection_specs,
581 grouped,
582 arg,
583 )?;
584 }
585 }
586 GroupHavingValueExpr::Binary { left, right, .. } => {
587 collect_grouped_having_value_expr_aggregate_projection_specs(
588 aggregate_projection_specs,
589 grouped,
590 left,
591 )?;
592 collect_grouped_having_value_expr_aggregate_projection_specs(
593 aggregate_projection_specs,
594 grouped,
595 right,
596 )?;
597 }
598 }
599
600 Ok(())
601}
602
603fn push_grouped_having_aggregate_projection_spec(
604 aggregate_projection_specs: &mut Vec<GroupedAggregateProjectionSpec>,
605 grouped: &GroupPlan,
606 aggregate_index: usize,
607) -> Result<(), InternalError> {
608 let Some(aggregate) = grouped.group.aggregates.get(aggregate_index) else {
609 return Err(InternalError::planner_executor_invariant(format!(
610 "grouped static planning semantics referenced HAVING aggregate index {aggregate_index} but aggregate_count={}",
611 grouped.group.aggregates.len(),
612 )));
613 };
614 let aggregate_projection_spec =
615 GroupedAggregateProjectionSpec::from_group_aggregate_spec(aggregate);
616
617 if aggregate_projection_specs
618 .iter()
619 .all(|current| current != &aggregate_projection_spec)
620 {
621 aggregate_projection_specs.push(aggregate_projection_spec);
622 }
623
624 Ok(())
625}
626
627fn projection_referenced_slots_for_spec(
628 model: &EntityModel,
629 projection: &ProjectionSpec,
630) -> Result<Vec<usize>, InternalError> {
631 let mut referenced = vec![false; model.fields().len()];
632
633 for field in projection.fields() {
634 mark_projection_expr_slots(
635 model,
636 projection_field_expr(field),
637 referenced.as_mut_slice(),
638 )?;
639 }
640
641 Ok(referenced
642 .into_iter()
643 .enumerate()
644 .filter_map(|(slot, required)| required.then_some(slot))
645 .collect())
646}
647
648fn mark_projection_expr_slots(
649 model: &EntityModel,
650 expr: &Expr,
651 referenced: &mut [bool],
652) -> Result<(), InternalError> {
653 match expr {
654 Expr::Field(field_id) => {
655 let field_name = field_id.as_str();
656 let slot = resolve_required_field_slot(model, field_name, || {
657 InternalError::query_invalid_logical_plan(format!(
658 "projection expression references unknown field '{field_name}'",
659 ))
660 })?;
661 referenced[slot] = true;
662 }
663 Expr::Literal(_) => {}
664 Expr::FunctionCall { args, .. } => {
665 for arg in args {
666 mark_projection_expr_slots(model, arg, referenced)?;
667 }
668 }
669 Expr::Aggregate(_) => {}
670 #[cfg(test)]
671 Expr::Alias { expr, .. } => {
672 mark_projection_expr_slots(model, expr.as_ref(), referenced)?;
673 }
674 #[cfg(test)]
675 Expr::Unary { expr, .. } => {
676 mark_projection_expr_slots(model, expr.as_ref(), referenced)?;
677 }
678 Expr::Binary { left, right, .. } => {
679 mark_projection_expr_slots(model, left.as_ref(), referenced)?;
680 mark_projection_expr_slots(model, right.as_ref(), referenced)?;
681 }
682 }
683
684 Ok(())
685}
686
687fn projected_slot_mask_for_spec(
688 model: &EntityModel,
689 projection: &ProjectionSpec,
690 direct_projection_slots: Option<&[usize]>,
691) -> Vec<bool> {
692 let mut projected_slots = vec![false; model.fields().len()];
693
694 let Some(direct_projection_slots) = direct_projection_slots else {
695 return projected_slots;
696 };
697
698 for (field, slot) in projection
699 .fields()
700 .zip(direct_projection_slots.iter().copied())
701 {
702 if matches!(field, ProjectionField::Scalar { .. })
703 && let Some(projected) = projected_slots.get_mut(slot)
704 {
705 *projected = true;
706 }
707 }
708
709 projected_slots
710}
711
712fn projection_is_model_identity_for_spec(model: &EntityModel, projection: &ProjectionSpec) -> bool {
713 if projection.len() != model.fields().len() {
714 return false;
715 }
716
717 for (field_model, projected_field) in model.fields().iter().zip(projection.fields()) {
718 match projected_field {
719 ProjectionField::Scalar {
720 expr: Expr::Field(field_id),
721 alias: None,
722 } if field_id.as_str() == field_model.name() => {}
723 ProjectionField::Scalar { .. } => return false,
724 }
725 }
726
727 true
728}
729
730fn resolved_order_for_plan(
731 model: &EntityModel,
732 plan: &AccessPlannedQuery,
733) -> Result<Option<ResolvedOrder>, InternalError> {
734 if grouped_plan_strategy(plan).is_some_and(GroupedPlanStrategy::is_top_k_group) {
735 return Ok(None);
736 }
737
738 let Some(order) = plan.scalar_plan().order.as_ref() else {
739 return Ok(None);
740 };
741
742 let mut fields = Vec::with_capacity(order.fields.len());
743 for (field, direction) in &order.fields {
744 fields.push(ResolvedOrderField::new(
745 resolved_order_value_source_for_field(model, field)?,
746 *direction,
747 ));
748 }
749
750 Ok(Some(ResolvedOrder::new(fields)))
751}
752
753fn resolved_order_value_source_for_field(
754 model: &EntityModel,
755 field: &str,
756) -> Result<ResolvedOrderValueSource, InternalError> {
757 if let Some(expr) = parse_supported_computed_order_expr(field) {
758 validate_resolved_order_expr_fields(model, &expr, field)?;
759 let compiled = compile_scalar_projection_expr(model, &expr)
760 .ok_or_else(|| order_expression_scalar_seam_error(field))?;
761
762 return Ok(ResolvedOrderValueSource::expression(compiled));
763 }
764
765 let slot = resolve_required_field_slot(model, field, || {
766 InternalError::query_invalid_logical_plan(format!(
767 "order expression references unknown field '{field}'",
768 ))
769 })?;
770
771 Ok(ResolvedOrderValueSource::direct_field(slot))
772}
773
774fn validate_resolved_order_expr_fields(
775 model: &EntityModel,
776 expr: &Expr,
777 rendered: &str,
778) -> Result<(), InternalError> {
779 match expr {
780 Expr::Field(field_id) => {
781 resolve_required_field_slot(model, field_id.as_str(), || {
782 InternalError::query_invalid_logical_plan(format!(
783 "order expression references unknown field '{rendered}'",
784 ))
785 })?;
786 }
787 Expr::Literal(_) => {}
788 Expr::FunctionCall { args, .. } => {
789 for arg in args {
790 validate_resolved_order_expr_fields(model, arg, rendered)?;
791 }
792 }
793 Expr::Binary { left, right, .. } => {
794 validate_resolved_order_expr_fields(model, left.as_ref(), rendered)?;
795 validate_resolved_order_expr_fields(model, right.as_ref(), rendered)?;
796 }
797 Expr::Aggregate(_) => {
798 return Err(order_expression_scalar_seam_error(rendered));
799 }
800 #[cfg(test)]
801 Expr::Alias { .. } | Expr::Unary { .. } => {
802 return Err(order_expression_scalar_seam_error(rendered));
803 }
804 }
805
806 Ok(())
807}
808
809fn resolve_required_field_slot<F>(
812 model: &EntityModel,
813 field: &str,
814 invalid_plan_error: F,
815) -> Result<usize, InternalError>
816where
817 F: FnOnce() -> InternalError,
818{
819 resolve_field_slot(model, field).ok_or_else(invalid_plan_error)
820}
821
822fn order_expression_scalar_seam_error(rendered: &str) -> InternalError {
825 InternalError::query_invalid_logical_plan(format!(
826 "order expression '{rendered}' did not stay on the scalar expression seam",
827 ))
828}
829
830fn order_referenced_slots_for_resolved_order(
835 resolved_order: Option<&ResolvedOrder>,
836) -> Option<Vec<usize>> {
837 let resolved_order = resolved_order?;
838 let mut referenced = Vec::new();
839
840 for field in resolved_order.fields() {
843 field.source().extend_referenced_slots(&mut referenced);
844 }
845
846 Some(referenced)
847}
848
849fn slot_map_for_model_plan(model: &EntityModel, plan: &AccessPlannedQuery) -> Option<Vec<usize>> {
850 let access_strategy = plan.access.resolve_strategy();
851 let executable = access_strategy.executable();
852
853 resolved_index_slots_for_access_path(model, executable)
854}
855
856fn resolved_index_slots_for_access_path(
857 model: &EntityModel,
858 access: &ExecutableAccessPlan<'_, crate::value::Value>,
859) -> Option<Vec<usize>> {
860 let path = access.as_path()?;
861 let path_capabilities = path.capabilities();
862 let index_fields = path_capabilities.index_fields_for_slot_map()?;
863 let mut slots = Vec::with_capacity(index_fields.len());
864
865 for field_name in index_fields {
866 let slot = resolve_field_slot(model, field_name)?;
867 slots.push(slot);
868 }
869
870 Some(slots)
871}
872
873fn index_compile_targets_for_model_plan(
874 model: &EntityModel,
875 plan: &AccessPlannedQuery,
876) -> Option<Vec<IndexCompileTarget>> {
877 let index = plan.access.as_path()?.selected_index_model()?;
878 let mut targets = Vec::new();
879
880 match index.key_items() {
881 IndexKeyItemsRef::Fields(fields) => {
882 for (component_index, &field_name) in fields.iter().enumerate() {
883 let field_slot = resolve_field_slot(model, field_name)?;
884 targets.push(IndexCompileTarget {
885 component_index,
886 field_slot,
887 key_item: crate::model::index::IndexKeyItem::Field(field_name),
888 });
889 }
890 }
891 IndexKeyItemsRef::Items(items) => {
892 for (component_index, &key_item) in items.iter().enumerate() {
893 let field_slot = resolve_field_slot(model, key_item.field())?;
894 targets.push(IndexCompileTarget {
895 component_index,
896 field_slot,
897 key_item,
898 });
899 }
900 }
901 }
902
903 Some(targets)
904}