1use crate::{
7 db::{
8 access::{AccessPlan, ExecutableAccessPlan},
9 predicate::IndexCompileTarget,
10 predicate::{PredicateExecutionModel, PredicateProgram},
11 query::plan::{
12 AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
13 ExecutionShapeSignature, ExpressionOrderTerm, GroupPlan, GroupedAggregateExecutionSpec,
14 GroupedDistinctExecutionStrategy, LogicalPlan, PlannerRouteProfile, QueryMode,
15 ResolvedOrder, ResolvedOrderField, ResolvedOrderValueSource, ScalarPlan,
16 StaticPlanningShape, derive_logical_pushdown_eligibility,
17 expr::{
18 Expr, ProjectionField, ProjectionSpec, ScalarProjectionExpr,
19 compile_scalar_projection_plan,
20 },
21 grouped_aggregate_execution_specs_with_model,
22 grouped_aggregate_projection_specs_from_projection_spec,
23 grouped_cursor_policy_violation, lower_direct_projection_slots,
24 lower_projection_identity, lower_projection_intent,
25 residual_query_predicate_after_access_path_bounds,
26 residual_query_predicate_after_filtered_access,
27 resolved_grouped_distinct_execution_strategy_for_model,
28 },
29 },
30 error::InternalError,
31 model::{
32 entity::{EntityModel, resolve_field_slot},
33 index::IndexKeyItemsRef,
34 },
35};
36
37impl QueryMode {
38 #[must_use]
40 pub const fn is_load(&self) -> bool {
41 match self {
42 Self::Load(_) => true,
43 Self::Delete(_) => false,
44 }
45 }
46
47 #[must_use]
49 pub const fn is_delete(&self) -> bool {
50 match self {
51 Self::Delete(_) => true,
52 Self::Load(_) => false,
53 }
54 }
55}
56
57impl LogicalPlan {
58 #[must_use]
60 pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
61 match self {
62 Self::Scalar(plan) => plan,
63 Self::Grouped(plan) => &plan.scalar,
64 }
65 }
66
67 #[must_use]
69 #[cfg(test)]
70 pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
71 match self {
72 Self::Scalar(plan) => plan,
73 Self::Grouped(plan) => &mut plan.scalar,
74 }
75 }
76
77 #[must_use]
79 #[cfg(test)]
80 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
81 self.scalar_semantics()
82 }
83
84 #[must_use]
86 #[cfg(test)]
87 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
88 self.scalar_semantics_mut()
89 }
90}
91
92impl AccessPlannedQuery {
93 #[must_use]
95 pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
96 self.logical.scalar_semantics()
97 }
98
99 #[must_use]
101 #[cfg(test)]
102 pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
103 self.logical.scalar_semantics_mut()
104 }
105
106 #[must_use]
108 #[cfg(test)]
109 pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
110 self.scalar_plan()
111 }
112
113 #[must_use]
115 #[cfg(test)]
116 pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
117 self.scalar_plan_mut()
118 }
119
120 #[must_use]
122 pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
123 match &self.logical {
124 LogicalPlan::Scalar(_) => None,
125 LogicalPlan::Grouped(plan) => Some(plan),
126 }
127 }
128
129 #[must_use]
131 pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
132 if let Some(static_shape) = &self.static_planning_shape {
133 return static_shape.projection_spec.clone();
134 }
135
136 lower_projection_intent(model, &self.logical, &self.projection_selection)
137 }
138
139 #[must_use]
141 pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
142 lower_projection_identity(&self.logical)
143 }
144
145 #[must_use]
151 pub(in crate::db) fn execution_preparation_predicate(&self) -> Option<PredicateExecutionModel> {
152 let query_predicate = self.scalar_plan().predicate.as_ref()?;
153
154 match self.access.selected_index_model() {
155 Some(index) => residual_query_predicate_after_filtered_access(index, query_predicate),
156 None => Some(query_predicate.clone()),
157 }
158 }
159
160 #[must_use]
164 pub(in crate::db) fn effective_execution_predicate(&self) -> Option<PredicateExecutionModel> {
165 let filtered_residual = self.execution_preparation_predicate();
168 let filtered_residual = filtered_residual.as_ref()?;
169
170 residual_query_predicate_after_access_path_bounds(self.access.as_path(), filtered_residual)
174 }
175
176 #[must_use]
178 pub(in crate::db) const fn execution_preparation_compiled_predicate(
179 &self,
180 ) -> Option<&PredicateProgram> {
181 self.static_planning_shape()
182 .execution_preparation_compiled_predicate
183 .as_ref()
184 }
185
186 #[must_use]
188 pub(in crate::db) const fn effective_runtime_compiled_predicate(
189 &self,
190 ) -> Option<&PredicateProgram> {
191 self.static_planning_shape()
192 .effective_runtime_compiled_predicate
193 .as_ref()
194 }
195
196 #[must_use]
198 pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
199 if !self.scalar_plan().distinct {
200 return DistinctExecutionStrategy::None;
201 }
202
203 match distinct_runtime_dedup_strategy(&self.access) {
207 Some(strategy) => strategy,
208 None => DistinctExecutionStrategy::None,
209 }
210 }
211
212 pub(in crate::db) fn finalize_planner_route_profile_for_model(&mut self, model: &EntityModel) {
214 self.set_planner_route_profile(project_planner_route_profile_for_model(model, self));
215 }
216
217 pub(in crate::db) fn finalize_static_planning_shape_for_model(
219 &mut self,
220 model: &EntityModel,
221 ) -> Result<(), InternalError> {
222 self.static_planning_shape = Some(project_static_planning_shape_for_model(model, self)?);
223
224 Ok(())
225 }
226
227 #[must_use]
229 pub(in crate::db) fn execution_shape_signature(
230 &self,
231 entity_path: &'static str,
232 ) -> ExecutionShapeSignature {
233 ExecutionShapeSignature::new(self.continuation_signature(entity_path))
234 }
235
236 #[must_use]
239 pub(in crate::db) fn predicate_fully_satisfied_by_access_contract(&self) -> bool {
240 self.scalar_plan().predicate.is_some() && self.effective_execution_predicate().is_none()
241 }
242
243 #[must_use]
247 pub(in crate::db) fn has_residual_predicate(&self) -> bool {
248 self.scalar_plan().predicate.is_some()
249 && !self.predicate_fully_satisfied_by_access_contract()
250 }
251
252 #[must_use]
254 pub(in crate::db) fn scalar_projection_plan(&self) -> Option<&[ScalarProjectionExpr]> {
255 self.static_planning_shape()
256 .scalar_projection_plan
257 .as_deref()
258 }
259
260 #[must_use]
262 pub(in crate::db) const fn primary_key_name(&self) -> &'static str {
263 self.static_planning_shape().primary_key_name
264 }
265
266 #[must_use]
268 pub(in crate::db) const fn projection_referenced_slots(&self) -> &[usize] {
269 self.static_planning_shape()
270 .projection_referenced_slots
271 .as_slice()
272 }
273
274 #[must_use]
276 pub(in crate::db) const fn projected_slot_mask(&self) -> &[bool] {
277 self.static_planning_shape().projected_slot_mask.as_slice()
278 }
279
280 #[must_use]
282 pub(in crate::db) const fn projection_is_model_identity(&self) -> bool {
283 self.static_planning_shape().projection_is_model_identity
284 }
285
286 #[must_use]
288 pub(in crate::db) fn order_referenced_slots(&self) -> Option<&[usize]> {
289 self.static_planning_shape()
290 .order_referenced_slots
291 .as_deref()
292 }
293
294 #[must_use]
296 pub(in crate::db) const fn resolved_order(&self) -> Option<&ResolvedOrder> {
297 self.static_planning_shape().resolved_order.as_ref()
298 }
299
300 #[must_use]
302 pub(in crate::db) fn slot_map(&self) -> Option<&[usize]> {
303 self.static_planning_shape().slot_map.as_deref()
304 }
305
306 #[must_use]
308 pub(in crate::db) fn grouped_aggregate_execution_specs(
309 &self,
310 ) -> Option<&[GroupedAggregateExecutionSpec]> {
311 self.static_planning_shape()
312 .grouped_aggregate_execution_specs
313 .as_deref()
314 }
315
316 #[must_use]
318 pub(in crate::db) const fn grouped_distinct_execution_strategy(
319 &self,
320 ) -> Option<&GroupedDistinctExecutionStrategy> {
321 self.static_planning_shape()
322 .grouped_distinct_execution_strategy
323 .as_ref()
324 }
325
326 #[must_use]
328 pub(in crate::db) const fn frozen_projection_spec(&self) -> &ProjectionSpec {
329 &self.static_planning_shape().projection_spec
330 }
331
332 #[must_use]
334 pub(in crate::db) fn frozen_direct_projection_slots(&self) -> Option<&[usize]> {
335 self.static_planning_shape()
336 .projection_direct_slots
337 .as_deref()
338 }
339
340 #[must_use]
342 pub(in crate::db) fn index_compile_targets(&self) -> Option<&[IndexCompileTarget]> {
343 self.static_planning_shape()
344 .index_compile_targets
345 .as_deref()
346 }
347
348 const fn static_planning_shape(&self) -> &StaticPlanningShape {
349 self.static_planning_shape
350 .as_ref()
351 .expect("access-planned queries must freeze static planning shape before execution")
352 }
353}
354
355fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
356 match access {
357 AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
358 Some(DistinctExecutionStrategy::PreOrdered)
359 }
360 AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
361 Some(DistinctExecutionStrategy::HashMaterialize)
362 }
363 AccessPlan::Path(_) => None,
364 }
365}
366
367fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
368 let is_grouped_safe = plan
369 .grouped_plan()
370 .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
371
372 ContinuationPolicy::new(
373 true, true, is_grouped_safe,
376 )
377}
378
379#[must_use]
381pub(in crate::db) fn project_planner_route_profile_for_model(
382 model: &EntityModel,
383 plan: &AccessPlannedQuery,
384) -> PlannerRouteProfile {
385 let secondary_order_contract = plan
386 .scalar_plan()
387 .order
388 .as_ref()
389 .and_then(|order| order.deterministic_secondary_order_contract(model.primary_key.name));
390
391 PlannerRouteProfile::new(
392 derive_continuation_policy_validated(plan),
393 derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
394 secondary_order_contract,
395 )
396}
397
398fn project_static_planning_shape_for_model(
399 model: &EntityModel,
400 plan: &AccessPlannedQuery,
401) -> Result<StaticPlanningShape, InternalError> {
402 let projection_spec = lower_projection_intent(model, &plan.logical, &plan.projection_selection);
403 let execution_preparation_compiled_predicate = plan
404 .execution_preparation_predicate()
405 .as_ref()
406 .map(|predicate| PredicateProgram::compile_with_model(model, predicate));
407 let effective_runtime_compiled_predicate = plan
408 .effective_execution_predicate()
409 .as_ref()
410 .map(|predicate| PredicateProgram::compile_with_model(model, predicate));
411 let scalar_projection_plan =
412 if plan.grouped_plan().is_none() {
413 Some(compile_scalar_projection_plan(model, &projection_spec).ok_or_else(|| {
414 InternalError::query_executor_invariant(
415 "scalar projection program must compile during static planning finalization",
416 )
417 })?)
418 } else {
419 None
420 };
421 let grouped_aggregate_execution_specs = if let Some(grouped) = plan.grouped_plan() {
422 #[cfg(not(test))]
423 let aggregate_projection_specs = grouped_aggregate_projection_specs_from_projection_spec(
424 &projection_spec,
425 grouped.group.group_fields.as_slice(),
426 grouped.group.aggregates.as_slice(),
427 );
428 #[cfg(test)]
429 let aggregate_projection_specs = grouped_aggregate_projection_specs_from_projection_spec(
430 &projection_spec,
431 grouped.group.group_fields.as_slice(),
432 grouped.group.aggregates.as_slice(),
433 )?;
434
435 Some(grouped_aggregate_execution_specs_with_model(
436 model,
437 aggregate_projection_specs.as_slice(),
438 )?)
439 } else {
440 None
441 };
442 let grouped_distinct_execution_strategy = if let Some(grouped) = plan.grouped_plan() {
443 Some(resolved_grouped_distinct_execution_strategy_for_model(
444 model,
445 grouped.group.group_fields.as_slice(),
446 grouped.group.aggregates.as_slice(),
447 grouped.having.as_ref(),
448 )?)
449 } else {
450 None
451 };
452 let projection_direct_slots =
453 lower_direct_projection_slots(model, &plan.logical, &plan.projection_selection);
454 let projection_referenced_slots =
455 projection_referenced_slots_for_spec(model, &projection_spec)?;
456 let projected_slot_mask =
457 projected_slot_mask_for_spec(model, &projection_spec, projection_direct_slots.as_deref());
458 let projection_is_model_identity =
459 projection_is_model_identity_for_spec(model, &projection_spec);
460 let resolved_order = resolved_order_for_plan(model, plan)?;
461 let order_referenced_slots = order_referenced_slots_for_resolved_order(resolved_order.as_ref());
462 let slot_map = slot_map_for_model_plan(model, plan);
463 let index_compile_targets = index_compile_targets_for_model_plan(model, plan);
464
465 Ok(StaticPlanningShape {
466 primary_key_name: model.primary_key.name,
467 projection_spec,
468 execution_preparation_compiled_predicate,
469 effective_runtime_compiled_predicate,
470 scalar_projection_plan,
471 grouped_aggregate_execution_specs,
472 grouped_distinct_execution_strategy,
473 projection_direct_slots,
474 projection_referenced_slots,
475 projected_slot_mask,
476 projection_is_model_identity,
477 resolved_order,
478 order_referenced_slots,
479 slot_map,
480 index_compile_targets,
481 })
482}
483
484fn projection_referenced_slots_for_spec(
485 model: &EntityModel,
486 projection: &ProjectionSpec,
487) -> Result<Vec<usize>, InternalError> {
488 let mut referenced = vec![false; model.fields().len()];
489
490 for field in projection.fields() {
491 match field {
492 ProjectionField::Scalar { expr, .. } => {
493 mark_projection_expr_slots(model, expr, referenced.as_mut_slice())?;
494 }
495 }
496 }
497
498 Ok(referenced
499 .into_iter()
500 .enumerate()
501 .filter_map(|(slot, required)| required.then_some(slot))
502 .collect())
503}
504
505fn mark_projection_expr_slots(
506 model: &EntityModel,
507 expr: &Expr,
508 referenced: &mut [bool],
509) -> Result<(), InternalError> {
510 match expr {
511 Expr::Field(field_id) => {
512 let field_name = field_id.as_str();
513 let slot = resolve_field_slot(model, field_name).ok_or_else(|| {
514 InternalError::query_invalid_logical_plan(format!(
515 "projection expression references unknown field '{field_name}'",
516 ))
517 })?;
518 referenced[slot] = true;
519 }
520 Expr::Aggregate(_) => {}
521 #[cfg(test)]
522 Expr::Literal(_) => {}
523 #[cfg(test)]
524 Expr::Alias { expr, .. } => {
525 mark_projection_expr_slots(model, expr.as_ref(), referenced)?;
526 }
527 #[cfg(test)]
528 Expr::Unary { expr, .. } => {
529 mark_projection_expr_slots(model, expr.as_ref(), referenced)?;
530 }
531 #[cfg(test)]
532 Expr::Binary { left, right, .. } => {
533 mark_projection_expr_slots(model, left.as_ref(), referenced)?;
534 mark_projection_expr_slots(model, right.as_ref(), referenced)?;
535 }
536 }
537
538 Ok(())
539}
540
541fn projected_slot_mask_for_spec(
542 model: &EntityModel,
543 projection: &ProjectionSpec,
544 direct_projection_slots: Option<&[usize]>,
545) -> Vec<bool> {
546 let mut projected_slots = vec![false; model.fields().len()];
547
548 let Some(direct_projection_slots) = direct_projection_slots else {
549 return projected_slots;
550 };
551
552 for (field, slot) in projection
553 .fields()
554 .zip(direct_projection_slots.iter().copied())
555 {
556 if matches!(field, ProjectionField::Scalar { .. })
557 && let Some(projected) = projected_slots.get_mut(slot)
558 {
559 *projected = true;
560 }
561 }
562
563 projected_slots
564}
565
566fn projection_is_model_identity_for_spec(model: &EntityModel, projection: &ProjectionSpec) -> bool {
567 if projection.len() != model.fields().len() {
568 return false;
569 }
570
571 for (field_model, projected_field) in model.fields().iter().zip(projection.fields()) {
572 match projected_field {
573 ProjectionField::Scalar {
574 expr: Expr::Field(field_id),
575 alias: None,
576 } if field_id.as_str() == field_model.name() => {}
577 ProjectionField::Scalar { .. } => return false,
578 }
579 }
580
581 true
582}
583
584fn resolved_order_for_plan(
585 model: &EntityModel,
586 plan: &AccessPlannedQuery,
587) -> Result<Option<ResolvedOrder>, InternalError> {
588 let Some(order) = plan.scalar_plan().order.as_ref() else {
589 return Ok(None);
590 };
591
592 let mut fields = Vec::with_capacity(order.fields.len());
593 for (field, direction) in &order.fields {
594 fields.push(ResolvedOrderField::new(
595 resolved_order_value_source_for_field(model, field)?,
596 *direction,
597 ));
598 }
599
600 Ok(Some(ResolvedOrder::new(fields)))
601}
602
603fn resolved_order_value_source_for_field(
604 model: &EntityModel,
605 field: &str,
606) -> Result<ResolvedOrderValueSource, InternalError> {
607 if let Some(expression) = ExpressionOrderTerm::parse(field) {
608 let slot = resolve_field_slot(model, expression.field()).ok_or_else(|| {
609 InternalError::query_invalid_logical_plan(format!(
610 "order expression references unknown field '{field}'",
611 ))
612 })?;
613
614 return Ok(match expression {
615 ExpressionOrderTerm::Lower(_) => ResolvedOrderValueSource::expression_lower(slot),
616 ExpressionOrderTerm::Upper(_) => ResolvedOrderValueSource::expression_upper(slot),
617 });
618 }
619
620 let slot = resolve_field_slot(model, field).ok_or_else(|| {
621 InternalError::query_invalid_logical_plan(format!(
622 "order expression references unknown field '{field}'",
623 ))
624 })?;
625
626 Ok(ResolvedOrderValueSource::direct_field(slot))
627}
628
629fn order_referenced_slots_for_resolved_order(
630 resolved_order: Option<&ResolvedOrder>,
631) -> Option<Vec<usize>> {
632 let resolved_order = resolved_order?;
633 let mut referenced = Vec::new();
634
635 for field in resolved_order.fields() {
638 let slot = field.source().slot();
639 if !referenced.contains(&slot) {
640 referenced.push(slot);
641 }
642 }
643
644 Some(referenced)
645}
646
647fn slot_map_for_model_plan(model: &EntityModel, plan: &AccessPlannedQuery) -> Option<Vec<usize>> {
648 let access_strategy = plan.access.resolve_strategy();
649 let executable = access_strategy.executable();
650
651 resolved_index_slots_for_access_path(model, executable)
652}
653
654fn resolved_index_slots_for_access_path(
655 model: &EntityModel,
656 access: &ExecutableAccessPlan<'_, crate::value::Value>,
657) -> Option<Vec<usize>> {
658 let path = access.as_path()?;
659 let path_capabilities = path.capabilities();
660 let index_fields = path_capabilities.index_fields_for_slot_map()?;
661 let mut slots = Vec::with_capacity(index_fields.len());
662
663 for field_name in index_fields {
664 let slot = resolve_field_slot(model, field_name)?;
665 slots.push(slot);
666 }
667
668 Some(slots)
669}
670
671fn index_compile_targets_for_model_plan(
672 model: &EntityModel,
673 plan: &AccessPlannedQuery,
674) -> Option<Vec<IndexCompileTarget>> {
675 let index = plan.access.as_path()?.selected_index_model()?;
676 let mut targets = Vec::new();
677
678 match index.key_items() {
679 IndexKeyItemsRef::Fields(fields) => {
680 for (component_index, &field_name) in fields.iter().enumerate() {
681 let field_slot = resolve_field_slot(model, field_name)?;
682 targets.push(IndexCompileTarget {
683 component_index,
684 field_slot,
685 key_item: crate::model::index::IndexKeyItem::Field(field_name),
686 });
687 }
688 }
689 IndexKeyItemsRef::Items(items) => {
690 for (component_index, &key_item) in items.iter().enumerate() {
691 let field_slot = resolve_field_slot(model, key_item.field())?;
692 targets.push(IndexCompileTarget {
693 component_index,
694 field_slot,
695 key_item,
696 });
697 }
698 }
699 }
700
701 Some(targets)
702}