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