Skip to main content

icydb_core/db/query/plan/semantics/
logical.rs

1//! Module: query::plan::semantics::logical
2//! Responsibility: logical-plan semantic lowering from planner contracts to access-planned queries.
3//! Does not own: access-path index selection internals or runtime execution behavior.
4//! Boundary: derives planner-owned execution semantics, shape signatures, and continuation policy.
5
6#[cfg(any(test, feature = "sql"))]
7use crate::db::predicate::MissingRowPolicy;
8use crate::{
9    db::{
10        access::{AccessPlan, ExecutableAccessPlan, SemanticIndexKeyItemsRef},
11        predicate::{IndexCompileTarget, Predicate, PredicateProgram},
12        query::plan::{
13            AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
14            EffectiveRuntimeFilterProgram, ExecutionShapeSignature, FieldSlot, GroupPlan,
15            GroupedAggregateExecutionSpec, GroupedDistinctExecutionStrategy, GroupedPlanStrategy,
16            LogicalPlan, PlannerRouteProfile, PredicatePushdownDiagnostics, QueryMode,
17            ResidualFilterContract, ResidualFilterShape, ResolvedOrder, ResolvedOrderField,
18            ResolvedOrderValueSource, ScalarPlan, StaticExecutionPlanningContract,
19            derive_logical_pushdown_eligibility,
20            expr::{
21                CompiledExpr, Expr, ProjectionSpec, compile_scalar_projection_expr_with_schema,
22                compile_scalar_projection_plan_with_schema,
23            },
24            extend_unique_grouped_aggregate_specs_from_expr, grouped_aggregate_execution_specs,
25            grouped_aggregate_specs_from_projection_spec, grouped_cursor_policy_violation,
26            grouped_plan_strategy, lower_data_row_direct_projection_slots_with_schema,
27            lower_direct_projection_slots_with_schema, lower_projection_identity,
28            lower_projection_intent, residual_query_predicate_after_access_path_bounds,
29            residual_query_predicate_after_filtered_access_contract,
30            resolved_grouped_distinct_execution_strategy_with_schema_info,
31        },
32        schema::SchemaInfo,
33    },
34    error::InternalError,
35    model::{
36        entity::EntityModel,
37        index::{IndexKeyItem, IndexKeyItemsRef},
38    },
39};
40
41impl QueryMode {
42    /// True if this mode represents a load intent.
43    #[must_use]
44    pub const fn is_load(&self) -> bool {
45        match self {
46            Self::Load(_) => true,
47            Self::Delete(_) => false,
48        }
49    }
50
51    /// True if this mode represents a delete intent.
52    #[must_use]
53    pub const fn is_delete(&self) -> bool {
54        match self {
55            Self::Delete(_) => true,
56            Self::Load(_) => false,
57        }
58    }
59}
60
61impl LogicalPlan {
62    /// Borrow scalar semantic fields shared by scalar/grouped logical variants.
63    #[must_use]
64    pub(in crate::db) const fn scalar_semantics(&self) -> &ScalarPlan {
65        match self {
66            Self::Scalar(plan) => plan,
67            Self::Grouped(plan) => &plan.scalar,
68        }
69    }
70
71    /// Borrow scalar semantic fields mutably across logical variants for tests.
72    #[must_use]
73    #[cfg(test)]
74    pub(in crate::db) const fn scalar_semantics_mut(&mut self) -> &mut ScalarPlan {
75        match self {
76            Self::Scalar(plan) => plan,
77            Self::Grouped(plan) => &mut plan.scalar,
78        }
79    }
80
81    /// Test-only shorthand for explicit scalar semantic borrowing.
82    #[must_use]
83    #[cfg(test)]
84    pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
85        self.scalar_semantics()
86    }
87
88    /// Test-only shorthand for explicit mutable scalar semantic borrowing.
89    #[must_use]
90    #[cfg(test)]
91    pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
92        self.scalar_semantics_mut()
93    }
94}
95
96impl AccessPlannedQuery {
97    /// Borrow scalar semantic fields shared by scalar/grouped logical variants.
98    #[must_use]
99    pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
100        self.logical.scalar_semantics()
101    }
102
103    /// Borrow scalar missing-row consistency without exposing the full scalar
104    /// plan to executor owners that only need row-presence policy.
105    #[must_use]
106    #[cfg(any(test, feature = "sql"))]
107    pub(in crate::db) const fn scalar_consistency(&self) -> MissingRowPolicy {
108        self.scalar_plan().consistency
109    }
110
111    /// Borrow scalar semantic fields mutably across logical variants for tests.
112    #[must_use]
113    #[cfg(test)]
114    pub(in crate::db) const fn scalar_plan_mut(&mut self) -> &mut ScalarPlan {
115        self.logical.scalar_semantics_mut()
116    }
117
118    /// Test-only shorthand for explicit scalar plan borrowing.
119    #[must_use]
120    #[cfg(test)]
121    pub(in crate::db) const fn scalar(&self) -> &ScalarPlan {
122        self.scalar_plan()
123    }
124
125    /// Test-only shorthand for explicit mutable scalar plan borrowing.
126    #[must_use]
127    #[cfg(test)]
128    pub(in crate::db) const fn scalar_mut(&mut self) -> &mut ScalarPlan {
129        self.scalar_plan_mut()
130    }
131
132    /// Borrow grouped semantic fields when this plan is grouped.
133    #[must_use]
134    pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
135        match &self.logical {
136            LogicalPlan::Scalar(_) => None,
137            LogicalPlan::Grouped(plan) => Some(plan),
138        }
139    }
140
141    /// Lower this plan into one canonical planner-owned projection semantic spec.
142    #[must_use]
143    pub(in crate::db) fn projection_spec(&self, model: &EntityModel) -> ProjectionSpec {
144        if let Some(static_contract) = &self.static_execution_planning_contract {
145            return static_contract.projection_spec.clone();
146        }
147
148        lower_projection_intent(model, &self.logical, &self.projection_selection)
149    }
150
151    /// Lower this plan into one projection semantic shape for identity hashing.
152    #[must_use]
153    pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
154        lower_projection_identity(&self.logical, &self.projection_selection)
155    }
156
157    /// Return the executor-facing predicate after removing only filtered-index
158    /// guard clauses the chosen access path already proves.
159    ///
160    /// This conservative form is used by preparation/explain surfaces that
161    /// still need to see access-bound equalities as index-predicate input.
162    #[must_use]
163    pub(in crate::db) fn execution_preparation_predicate(&self) -> Option<Predicate> {
164        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
165            return static_contract.execution_preparation_predicate.clone();
166        }
167
168        derive_execution_preparation_predicate(self)
169    }
170
171    /// Return the executor-facing residual predicate after removing any
172    /// filtered-index guard clauses and fixed access-bound equalities already
173    /// guaranteed by the chosen path.
174    #[must_use]
175    pub(in crate::db) fn effective_execution_predicate(&self) -> Option<Predicate> {
176        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
177            return static_contract
178                .residual_filter_contract
179                .residual_filter_predicate()
180                .cloned();
181        }
182
183        derive_residual_filter_predicate(self)
184    }
185
186    /// Return whether one explicit residual predicate survives access
187    /// planning and still participates in residual execution.
188    #[must_use]
189    pub(in crate::db) fn has_residual_filter_predicate(&self) -> bool {
190        self.effective_execution_predicate().is_some()
191    }
192
193    /// Borrow the planner-owned residual scalar filter expression when one
194    /// surviving semantic remainder still requires runtime evaluation.
195    #[must_use]
196    pub(in crate::db) fn residual_filter_expr(&self) -> Option<&Expr> {
197        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
198            return static_contract
199                .residual_filter_contract
200                .residual_filter_expr();
201        }
202
203        if !derive_has_residual_filter(self) {
204            return None;
205        }
206
207        self.scalar_plan().filter_expr.as_ref()
208    }
209
210    /// Return whether one explicit residual scalar filter expression survives
211    /// access planning and still requires runtime evaluation.
212    #[must_use]
213    pub(in crate::db) fn has_residual_filter_expr(&self) -> bool {
214        self.residual_filter_expr().is_some()
215    }
216
217    /// Return the planner-owned residual-filter shape used by diagnostics.
218    #[must_use]
219    pub(in crate::db) fn residual_filter_shape(&self) -> ResidualFilterShape {
220        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
221            return static_contract.residual_filter_contract.shape();
222        }
223
224        ResidualFilterShape::from_presence(
225            self.residual_filter_expr().is_some(),
226            self.effective_execution_predicate().is_some(),
227        )
228    }
229
230    /// Return the planner-owned predicate pushdown label consumed by verbose
231    /// execution diagnostics.
232    #[must_use]
233    pub(in crate::db) fn predicate_pushdown_label(&self) -> String {
234        self.predicate_pushdown_diagnostics().label()
235    }
236
237    /// Return planner-owned predicate-pushdown diagnostics.
238    #[must_use]
239    pub(in crate::db) fn predicate_pushdown_diagnostics(&self) -> PredicatePushdownDiagnostics {
240        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
241            return static_contract.predicate_pushdown_diagnostics;
242        }
243
244        derive_predicate_pushdown_diagnostics(self, self.residual_filter_shape())
245    }
246
247    /// Return the planner-owned predicate-pushdown outcome label.
248    #[must_use]
249    pub(in crate::db) fn predicate_pushdown_outcome_label(&self) -> &'static str {
250        self.predicate_pushdown_diagnostics().outcome_label()
251    }
252
253    /// Return the planner-owned predicate-pushdown reason label.
254    #[must_use]
255    pub(in crate::db) fn predicate_pushdown_reason_label(&self) -> &'static str {
256        self.predicate_pushdown_diagnostics().reason_label()
257    }
258
259    /// Borrow the planner-compiled execution-preparation predicate program.
260    #[must_use]
261    pub(in crate::db) fn execution_preparation_compiled_predicate(
262        &self,
263    ) -> Option<&PredicateProgram> {
264        self.static_execution_planning_contract()?
265            .execution_preparation_compiled_predicate
266            .as_ref()
267    }
268
269    /// Borrow the planner-compiled effective runtime predicate program.
270    #[must_use]
271    pub(in crate::db) fn effective_runtime_compiled_predicate(&self) -> Option<&PredicateProgram> {
272        match self
273            .static_execution_planning_contract()?
274            .residual_filter_contract
275            .effective_runtime_filter_program()
276        {
277            Some(program) => program.predicate_program(),
278            None => None,
279        }
280    }
281
282    /// Borrow the planner-compiled effective runtime scalar filter expression.
283    #[cfg(test)]
284    #[must_use]
285    pub(in crate::db) fn effective_runtime_compiled_filter_expr(&self) -> Option<&CompiledExpr> {
286        match self
287            .static_execution_planning_contract()?
288            .residual_filter_contract
289            .effective_runtime_filter_program()
290        {
291            Some(program) => program.expression_filter(),
292            None => None,
293        }
294    }
295
296    /// Borrow the planner-frozen effective runtime scalar filter program.
297    #[must_use]
298    pub(in crate::db) fn effective_runtime_filter_program(
299        &self,
300    ) -> Option<&EffectiveRuntimeFilterProgram> {
301        self.static_execution_planning_contract()?
302            .residual_filter_contract
303            .effective_runtime_filter_program()
304    }
305
306    /// Lower scalar DISTINCT semantics into one executor-facing execution strategy.
307    #[must_use]
308    pub(in crate::db) fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
309        if !self.scalar_plan().distinct {
310            return DistinctExecutionStrategy::None;
311        }
312
313        // DISTINCT on duplicate-safe single-path access shapes is a planner
314        // no-op for runtime dedup mechanics. Composite shapes can surface
315        // duplicate keys and therefore retain explicit dedup execution.
316        match distinct_runtime_dedup_strategy(&self.access) {
317            Some(strategy) => strategy,
318            None => DistinctExecutionStrategy::None,
319        }
320    }
321
322    /// Freeze one planner-owned route profile after model validation completes.
323    #[cfg(test)]
324    pub(in crate::db) fn finalize_planner_route_profile_for_model(&mut self, model: &EntityModel) {
325        self.set_planner_route_profile(project_planner_route_profile_for_model(model, self));
326    }
327
328    /// Freeze one planner-owned route profile from accepted schema authority.
329    pub(in crate::db) fn finalize_planner_route_profile_for_model_with_schema(
330        &mut self,
331        schema_info: &SchemaInfo,
332    ) {
333        self.set_planner_route_profile(project_planner_route_profile_for_schema(schema_info, self));
334    }
335
336    /// Freeze model-only executor metadata after logical/access planning completes.
337    #[cfg(test)]
338    pub(in crate::db) fn finalize_static_execution_planning_contract_for_model_only(
339        &mut self,
340        model: &EntityModel,
341    ) -> Result<(), InternalError> {
342        self.finalize_static_execution_planning_contract_for_model_with_schema(
343            model,
344            SchemaInfo::cached_for_generated_entity_model(model),
345        )
346    }
347
348    /// Freeze planner-owned executor metadata with explicit schema authority.
349    pub(in crate::db) fn finalize_static_execution_planning_contract_for_model_with_schema(
350        &mut self,
351        model: &EntityModel,
352        schema_info: &SchemaInfo,
353    ) -> Result<(), InternalError> {
354        self.bind_group_field_slots_to_schema(schema_info)?;
355        self.static_execution_planning_contract = Some(
356            project_static_execution_planning_contract_for_model(model, schema_info, self)?,
357        );
358
359        Ok(())
360    }
361
362    // Replace authoring-time model-only group slots with the accepted slots
363    // used by the executable plan. Scalar plans and explicit model-only schema
364    // views require no authority transition.
365    fn bind_group_field_slots_to_schema(
366        &mut self,
367        schema_info: &SchemaInfo,
368    ) -> Result<(), InternalError> {
369        if !schema_info.has_accepted_authority() {
370            return Ok(());
371        }
372        let LogicalPlan::Grouped(grouped) = &mut self.logical else {
373            return Ok(());
374        };
375
376        let accepted_slots = grouped
377            .group
378            .group_fields
379            .iter()
380            .map(|field_slot| FieldSlot::resolve_with_schema(schema_info, field_slot.field()))
381            .collect::<Option<Vec<_>>>()
382            .ok_or_else(InternalError::planner_executor_invariant)?;
383        grouped.group.group_fields = accepted_slots;
384
385        Ok(())
386    }
387
388    /// Build one immutable execution-shape signature contract for runtime layers.
389    #[must_use]
390    pub(in crate::db) fn execution_shape_signature(
391        &self,
392        entity_path: &'static str,
393    ) -> ExecutionShapeSignature {
394        ExecutionShapeSignature::new(self.continuation_signature(entity_path))
395    }
396
397    /// Return whether the chosen access contract fully satisfies the current
398    /// scalar query predicate without any additional post-access filtering.
399    #[must_use]
400    pub(in crate::db) fn predicate_fully_satisfied_by_access_contract(&self) -> bool {
401        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
402            return self.scalar_plan().predicate.is_some()
403                && !static_contract
404                    .residual_filter_contract
405                    .has_residual_filter();
406        }
407
408        derive_predicate_fully_satisfied_by_access_contract(self)
409    }
410
411    /// Borrow the planner-frozen compiled scalar projection program.
412    #[must_use]
413    pub(in crate::db) fn scalar_projection_plan(&self) -> Option<&[CompiledExpr]> {
414        self.static_execution_planning_contract()?
415            .scalar_projection_plan
416            .as_deref()
417    }
418
419    /// Return whether planner-owned static execution metadata has already been frozen.
420    #[must_use]
421    pub(in crate::db) const fn has_static_execution_planning_contract(&self) -> bool {
422        self.static_execution_planning_contract.is_some()
423    }
424
425    /// Borrow the planner-frozen ordered primary-key field names.
426    pub(in crate::db) fn primary_key_names(&self) -> Result<Vec<&str>, InternalError> {
427        Ok(self
428            .require_static_execution_planning_contract()?
429            .primary_key_names
430            .iter()
431            .map(String::as_str)
432            .collect())
433    }
434
435    /// Borrow the planner-frozen projection slot reachability set.
436    pub(in crate::db) fn projection_referenced_slots(&self) -> Result<&[usize], InternalError> {
437        Ok(self
438            .require_static_execution_planning_contract()?
439            .projection_referenced_slots
440            .as_slice())
441    }
442
443    /// Borrow the planner-frozen mask for direct projected output slots.
444    #[cfg(any(test, all(feature = "sql", feature = "diagnostics")))]
445    pub(in crate::db) fn projected_slot_mask(&self) -> Result<&[bool], InternalError> {
446        Ok(self
447            .require_static_execution_planning_contract()?
448            .projected_slot_mask
449            .as_slice())
450    }
451
452    /// Return whether projection remains the full model-identity field list.
453    pub(in crate::db) fn projection_is_model_identity(&self) -> Result<bool, InternalError> {
454        Ok(self
455            .require_static_execution_planning_contract()?
456            .projection_is_model_identity)
457    }
458
459    /// Borrow the planner-frozen ORDER BY slot reachability set, if any.
460    #[must_use]
461    pub(in crate::db) fn order_referenced_slots(&self) -> Option<&[usize]> {
462        self.static_execution_planning_contract()?
463            .order_referenced_slots
464            .as_deref()
465    }
466
467    /// Borrow the planner-frozen resolved ORDER BY program, if one exists.
468    #[must_use]
469    pub(in crate::db) fn resolved_order(&self) -> Option<&ResolvedOrder> {
470        self.static_execution_planning_contract()?
471            .resolved_order
472            .as_ref()
473    }
474
475    /// Borrow the planner-frozen access slot map used by index predicate compilation.
476    #[must_use]
477    pub(in crate::db) fn slot_map(&self) -> Option<&[usize]> {
478        self.static_execution_planning_contract()?
479            .slot_map
480            .as_deref()
481    }
482
483    /// Borrow grouped aggregate execution specs already resolved during static planning.
484    #[must_use]
485    pub(in crate::db) fn grouped_aggregate_execution_specs(
486        &self,
487    ) -> Option<&[GroupedAggregateExecutionSpec]> {
488        self.static_execution_planning_contract()?
489            .grouped_aggregate_execution_specs
490            .as_deref()
491    }
492
493    /// Borrow the planner-resolved grouped DISTINCT execution strategy when present.
494    #[must_use]
495    pub(in crate::db) fn grouped_distinct_execution_strategy(
496        &self,
497    ) -> Option<&GroupedDistinctExecutionStrategy> {
498        self.static_execution_planning_contract()?
499            .grouped_distinct_execution_strategy
500            .as_ref()
501    }
502
503    /// Borrow the frozen projection semantic shape without reopening model ownership.
504    pub(in crate::db) fn frozen_projection_spec(&self) -> Result<&ProjectionSpec, InternalError> {
505        Ok(&self
506            .require_static_execution_planning_contract()?
507            .projection_spec)
508    }
509
510    /// Borrow the frozen direct projection slots without reopening model ownership.
511    #[must_use]
512    #[cfg(any(test, feature = "sql"))]
513    pub(in crate::db) fn frozen_direct_projection_slots(&self) -> Option<&[usize]> {
514        self.static_execution_planning_contract()?
515            .projection_direct_slots
516            .as_deref()
517    }
518
519    /// Borrow duplicate-preserving direct projection slots for raw data-row readers.
520    #[must_use]
521    #[cfg(any(test, feature = "sql"))]
522    pub(in crate::db) fn frozen_data_row_direct_projection_slots(&self) -> Option<&[usize]> {
523        self.static_execution_planning_contract()?
524            .projection_data_row_direct_slots
525            .as_deref()
526    }
527
528    /// Borrow the planner-frozen key-item-aware compile targets for the chosen access path.
529    #[must_use]
530    pub(in crate::db) fn index_compile_targets(&self) -> Option<&[IndexCompileTarget]> {
531        self.static_execution_planning_contract()?
532            .index_compile_targets
533            .as_deref()
534    }
535
536    const fn static_execution_planning_contract(&self) -> Option<&StaticExecutionPlanningContract> {
537        self.static_execution_planning_contract.as_ref()
538    }
539
540    fn require_static_execution_planning_contract(
541        &self,
542    ) -> Result<&StaticExecutionPlanningContract, InternalError> {
543        self.static_execution_planning_contract
544            .as_ref()
545            .ok_or_else(InternalError::query_executor_invariant)
546    }
547}
548
549fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> Option<DistinctExecutionStrategy> {
550    match access {
551        AccessPlan::Union(_) | AccessPlan::Intersection(_) => {
552            Some(DistinctExecutionStrategy::PreOrdered)
553        }
554        AccessPlan::Path(path) if path.as_ref().is_index_multi_lookup() => {
555            Some(DistinctExecutionStrategy::HashMaterialize)
556        }
557        AccessPlan::Path(_) => None,
558    }
559}
560
561fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
562    let is_grouped_safe = plan
563        .grouped_plan()
564        .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
565
566    ContinuationPolicy::new(
567        true, // Continuation resume windows require anchor semantics for pushdown-safe replay.
568        true, // Continuation resumes must advance strictly to prevent replay/regression loops.
569        is_grouped_safe,
570    )
571}
572
573/// Project one planner-owned route profile from the finalized logical+access plan.
574#[must_use]
575#[cfg(test)]
576pub(in crate::db) fn project_planner_route_profile_for_model(
577    model: &EntityModel,
578    plan: &AccessPlannedQuery,
579) -> PlannerRouteProfile {
580    let primary_key_names = ordered_primary_key_names(model);
581    let secondary_order_contract = plan.scalar_plan().order.as_ref().and_then(|order| {
582        order.deterministic_secondary_order_contract_fields(primary_key_names.as_slice())
583    });
584
585    PlannerRouteProfile::new(
586        derive_continuation_policy_validated(plan),
587        derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
588        secondary_order_contract,
589    )
590}
591
592/// Project one planner-owned route profile from accepted schema authority.
593#[must_use]
594pub(in crate::db) fn project_planner_route_profile_for_schema(
595    schema_info: &SchemaInfo,
596    plan: &AccessPlannedQuery,
597) -> PlannerRouteProfile {
598    let primary_key_names = primary_key_names_from_schema(schema_info);
599    let secondary_order_contract = plan.scalar_plan().order.as_ref().and_then(|order| {
600        order.deterministic_secondary_order_contract_fields(primary_key_names.as_slice())
601    });
602
603    PlannerRouteProfile::new(
604        derive_continuation_policy_validated(plan),
605        derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
606        secondary_order_contract,
607    )
608}
609
610fn project_static_execution_planning_contract_for_model(
611    model: &EntityModel,
612    schema_info: &SchemaInfo,
613    plan: &AccessPlannedQuery,
614) -> Result<StaticExecutionPlanningContract, InternalError> {
615    let projection_spec = lower_projection_intent(model, &plan.logical, &plan.projection_selection);
616    let execution_preparation_predicate = plan.execution_preparation_predicate();
617    let residual_filter_predicate = derive_residual_filter_predicate_from_preparation(
618        plan,
619        execution_preparation_predicate.as_ref(),
620    );
621    let residual_filter_expr = derive_residual_filter_expr_for_model(model, plan);
622    let effective_runtime_filter_program = compile_effective_runtime_filter_program(
623        schema_info,
624        residual_filter_expr.as_ref(),
625        residual_filter_predicate.as_ref(),
626    )?;
627    let residual_filter_contract = ResidualFilterContract::new(
628        residual_filter_expr,
629        residual_filter_predicate,
630        effective_runtime_filter_program,
631    );
632    let residual_filter_shape = residual_filter_contract.shape();
633    let execution_preparation_compiled_predicate =
634        should_compile_execution_preparation_predicate(residual_filter_shape)
635            .then(|| {
636                compile_optional_predicate(schema_info, execution_preparation_predicate.as_ref())
637            })
638            .flatten();
639    let predicate_pushdown_diagnostics =
640        derive_predicate_pushdown_diagnostics(plan, residual_filter_shape);
641    let scalar_projection_plan = if plan.grouped_plan().is_none() {
642        Some(
643            compile_scalar_projection_plan_with_schema(schema_info, &projection_spec)
644                .ok_or_else(InternalError::query_executor_invariant)?
645                .iter()
646                .map(CompiledExpr::compile)
647                .collect(),
648        )
649    } else {
650        None
651    };
652    let (grouped_aggregate_execution_specs, grouped_distinct_execution_strategy) =
653        resolve_grouped_static_planning_semantics(schema_info, plan, &projection_spec)?;
654    let projection_direct_slots = lower_direct_projection_slots_with_schema(
655        model,
656        schema_info,
657        &plan.logical,
658        &plan.projection_selection,
659    );
660    let projection_data_row_direct_slots = lower_data_row_direct_projection_slots_with_schema(
661        model,
662        schema_info,
663        &plan.logical,
664        &plan.projection_selection,
665    );
666    let projection_referenced_slots =
667        projection_spec.referenced_slots_for_schema(model, schema_info)?;
668    let projected_slot_mask =
669        projected_slot_mask_for_spec(model, projection_direct_slots.as_deref());
670    let projection_is_model_identity = projection_spec.is_model_identity_for(model);
671    let resolved_order = resolved_order_for_plan(schema_info, plan)?;
672    let order_referenced_slots = order_referenced_slots_for_resolved_order(resolved_order.as_ref());
673    let slot_map = slot_map_for_schema_plan(schema_info, plan);
674    let index_compile_targets = index_compile_targets_for_schema_plan(schema_info, plan);
675
676    Ok(StaticExecutionPlanningContract {
677        primary_key_names: schema_info.primary_key_names().to_vec(),
678        projection_spec,
679        execution_preparation_predicate,
680        execution_preparation_compiled_predicate,
681        residual_filter_contract,
682        predicate_pushdown_diagnostics,
683        scalar_projection_plan,
684        grouped_aggregate_execution_specs,
685        grouped_distinct_execution_strategy,
686        projection_direct_slots,
687        projection_data_row_direct_slots,
688        projection_referenced_slots,
689        projected_slot_mask,
690        projection_is_model_identity,
691        resolved_order,
692        order_referenced_slots,
693        slot_map,
694        index_compile_targets,
695    })
696}
697
698#[cfg(test)]
699fn ordered_primary_key_names(model: &EntityModel) -> Vec<&'static str> {
700    model.primary_key_names()
701}
702
703fn primary_key_names_from_schema(schema_info: &SchemaInfo) -> Vec<&str> {
704    schema_info
705        .primary_key_names()
706        .iter()
707        .map(String::as_str)
708        .collect()
709}
710
711// Compile the executor-owned residual scalar filter contract once from the
712// planner-derived residual artifacts so runtime never has to rediscover
713// residual presence or shape from semantic/filter/pushdown state.
714fn compile_effective_runtime_filter_program(
715    schema_info: &SchemaInfo,
716    residual_filter_expr: Option<&Expr>,
717    residual_filter_predicate: Option<&Predicate>,
718) -> Result<Option<EffectiveRuntimeFilterProgram>, InternalError> {
719    // Keep the existing predicate fast path when the residual semantics still
720    // fit the derived predicate contract. The expression-owned lane is only
721    // needed once pushdown loses semantic coverage and a residual predicate no
722    // longer exists.
723    if let Some(predicate) = residual_filter_predicate {
724        return Ok(Some(EffectiveRuntimeFilterProgram::predicate(
725            PredicateProgram::compile_with_schema_info(schema_info, predicate),
726        )));
727    }
728
729    if let Some(filter_expr) = residual_filter_expr {
730        let compiled = compile_scalar_projection_expr_with_schema(schema_info, filter_expr)
731            .ok_or_else(InternalError::query_invalid_logical_plan)?;
732
733        return Ok(Some(EffectiveRuntimeFilterProgram::expression(
734            CompiledExpr::compile(&compiled),
735        )));
736    }
737
738    Ok(None)
739}
740
741// Derive the executor-preparation predicate once from the selected access path.
742// This strips only filtered-index guard clauses while preserving access-bound
743// equalities that still matter to preparation/explain consumers.
744fn derive_execution_preparation_predicate(plan: &AccessPlannedQuery) -> Option<Predicate> {
745    let query_predicate = plan.scalar_plan().predicate.as_ref()?;
746
747    match plan.access.selected_index_contract() {
748        Some(index) => {
749            residual_query_predicate_after_filtered_access_contract(index, query_predicate)
750        }
751        None => Some(query_predicate.clone()),
752    }
753}
754
755// Derive the final residual predicate once from the already-filtered
756// preparation predicate plus any equality bounds guaranteed by the concrete
757// access path.
758fn derive_residual_filter_predicate(plan: &AccessPlannedQuery) -> Option<Predicate> {
759    let filtered_residual = derive_execution_preparation_predicate(plan);
760
761    derive_residual_filter_predicate_from_preparation(plan, filtered_residual.as_ref())
762}
763
764fn derive_residual_filter_predicate_from_preparation(
765    plan: &AccessPlannedQuery,
766    execution_preparation_predicate: Option<&Predicate>,
767) -> Option<Predicate> {
768    let execution_preparation_predicate = execution_preparation_predicate?;
769
770    residual_query_predicate_after_access_path_bounds(
771        plan.access.as_path(),
772        execution_preparation_predicate,
773    )
774}
775
776// Derive the explicit residual semantic expression once for finalized plans.
777// The residual expression remains the planner-owned semantic filter when any
778// runtime filtering still survives access satisfaction.
779fn derive_residual_filter_expr(plan: &AccessPlannedQuery) -> Option<Expr> {
780    let filter_expr = plan.scalar_plan().filter_expr.as_ref()?;
781    if derive_semantic_filter_fully_satisfied_by_access_contract(plan) {
782        return None;
783    }
784
785    Some(filter_expr.clone())
786}
787
788// Derive the explicit residual semantic expression during finalization using
789// the trusted entity schema so compare-family literal normalization matches the
790// planner-owned predicate contract before residual ownership is decided.
791fn derive_residual_filter_expr_for_model(
792    model: &EntityModel,
793    plan: &AccessPlannedQuery,
794) -> Option<Expr> {
795    let filter_expr = plan.scalar_plan().filter_expr.as_ref()?;
796    if derive_semantic_filter_fully_satisfied_by_access_contract_for_model(model, plan) {
797        return None;
798    }
799
800    Some(filter_expr.clone())
801}
802
803// Return whether any residual filtering survives after access planning. This
804// helper exists only for pre-finalization assembly; finalized plans must read
805// the explicit residual artifacts frozen in `StaticExecutionPlanningContract`.
806fn derive_has_residual_filter(plan: &AccessPlannedQuery) -> bool {
807    match (
808        plan.scalar_plan().filter_expr.as_ref(),
809        plan.scalar_plan().predicate.as_ref(),
810    ) {
811        (None, None) => false,
812        (Some(_), None) => true,
813        (Some(_) | None, Some(_)) => !plan.predicate_fully_satisfied_by_access_contract(),
814    }
815}
816
817// Freeze predicate-pushdown diagnostics from one logical plan shape. This keeps
818// lazy plan accessors and finalized static planning on the same argument
819// contract while leaving route selection and residual filtering unchanged.
820fn derive_predicate_pushdown_diagnostics(
821    plan: &AccessPlannedQuery,
822    residual_filter_shape: ResidualFilterShape,
823) -> PredicatePushdownDiagnostics {
824    PredicatePushdownDiagnostics::from_plan(
825        plan.scalar_plan().filter_expr.is_some(),
826        plan.scalar_plan().predicate_covers_filter_expr,
827        plan.scalar_plan().predicate.as_ref(),
828        &plan.access,
829        residual_filter_shape,
830    )
831}
832
833// Return true when the planner-owned predicate contract is fully satisfied by
834// access planning and no semantic residual filter expression survives.
835fn derive_predicate_fully_satisfied_by_access_contract(plan: &AccessPlannedQuery) -> bool {
836    plan.scalar_plan().predicate.is_some()
837        && derive_residual_filter_predicate(plan).is_none()
838        && derive_residual_filter_expr(plan).is_none()
839}
840
841// Return true when the semantic filter expression is entirely represented by
842// the planner-owned predicate contract and the chosen access path satisfies
843// that predicate without any runtime remainder.
844const fn derive_semantic_filter_fully_satisfied_by_access_contract(
845    plan: &AccessPlannedQuery,
846) -> bool {
847    plan.scalar_plan().filter_expr.is_some()
848        && plan.scalar_plan().predicate.is_some()
849        && plan.scalar_plan().predicate_covers_filter_expr
850}
851
852// Return true when finalized planning can prove that the semantic filter
853// expression is completely represented by the planner-owned predicate contract
854// after aligning compare literals through the trusted entity schema.
855const fn derive_semantic_filter_fully_satisfied_by_access_contract_for_model(
856    _model: &EntityModel,
857    plan: &AccessPlannedQuery,
858) -> bool {
859    derive_semantic_filter_fully_satisfied_by_access_contract(plan)
860}
861
862// Compile one optional planner-frozen predicate program while keeping the
863// static planning assembly path free of repeated `Option` mapping boilerplate.
864fn compile_optional_predicate(
865    schema_info: &SchemaInfo,
866    predicate: Option<&Predicate>,
867) -> Option<PredicateProgram> {
868    predicate.map(|predicate| PredicateProgram::compile_with_schema_info(schema_info, predicate))
869}
870
871// Avoid compiling large access-proven predicates into executor preparation.
872// When no residual filter survives, the chosen access route already enforces
873// the predicate and route/explain consumers can use the explicit residual
874// contract instead of recompiling access-bound literals.
875const fn should_compile_execution_preparation_predicate(
876    residual_filter_shape: ResidualFilterShape,
877) -> bool {
878    !residual_filter_shape.is_absent()
879}
880
881// Resolve the grouped-only static planning semantics bundle once so grouped
882// aggregate execution specs and grouped DISTINCT strategy stay derived under
883// one shared grouped-plan branch.
884fn resolve_grouped_static_planning_semantics(
885    schema_info: &SchemaInfo,
886    plan: &AccessPlannedQuery,
887    projection_spec: &ProjectionSpec,
888) -> Result<
889    (
890        Option<Vec<GroupedAggregateExecutionSpec>>,
891        Option<GroupedDistinctExecutionStrategy>,
892    ),
893    InternalError,
894> {
895    let Some(grouped) = plan.grouped_plan() else {
896        return Ok((None, None));
897    };
898
899    let mut aggregate_specs = grouped_aggregate_specs_from_projection_spec(
900        projection_spec,
901        grouped.group.group_fields.as_slice(),
902        grouped.group.aggregates.as_slice(),
903    )?;
904    extend_grouped_having_aggregate_specs(&mut aggregate_specs, grouped)?;
905
906    let grouped_aggregate_execution_specs = Some(grouped_aggregate_execution_specs(
907        schema_info,
908        aggregate_specs.as_slice(),
909    )?);
910    let grouped_distinct_execution_strategy = Some(
911        resolved_grouped_distinct_execution_strategy_with_schema_info(
912            schema_info,
913            grouped.group.group_fields.as_slice(),
914            grouped.group.aggregates.as_slice(),
915            grouped.having_expr.as_ref(),
916        )?,
917    );
918
919    Ok((
920        grouped_aggregate_execution_specs,
921        grouped_distinct_execution_strategy,
922    ))
923}
924
925fn extend_grouped_having_aggregate_specs(
926    aggregate_specs: &mut Vec<GroupedAggregateExecutionSpec>,
927    grouped: &GroupPlan,
928) -> Result<(), InternalError> {
929    if let Some(having_expr) = grouped.having_expr.as_ref() {
930        extend_unique_grouped_aggregate_specs_from_expr(aggregate_specs, having_expr)?;
931    }
932
933    Ok(())
934}
935
936fn projected_slot_mask_for_spec(
937    model: &EntityModel,
938    direct_projection_slots: Option<&[usize]>,
939) -> Vec<bool> {
940    let schema_slot_len = direct_projection_slots
941        .and_then(|slots| slots.iter().copied().max())
942        .map_or(0, |slot| slot.saturating_add(1));
943    let mut projected_slots = vec![false; model.fields().len().max(schema_slot_len)];
944
945    let Some(direct_projection_slots) = direct_projection_slots else {
946        return projected_slots;
947    };
948
949    for slot in direct_projection_slots.iter().copied() {
950        if let Some(projected) = projected_slots.get_mut(slot) {
951            *projected = true;
952        }
953    }
954
955    projected_slots
956}
957
958fn resolved_order_for_plan(
959    schema_info: &SchemaInfo,
960    plan: &AccessPlannedQuery,
961) -> Result<Option<ResolvedOrder>, InternalError> {
962    if grouped_plan_strategy(plan).is_some_and(GroupedPlanStrategy::is_top_k_group) {
963        return Ok(None);
964    }
965
966    let Some(order) = plan.scalar_plan().order.as_ref() else {
967        return Ok(None);
968    };
969
970    let mut fields = Vec::with_capacity(order.fields.len());
971    for term in &order.fields {
972        fields.push(ResolvedOrderField::new(
973            resolved_order_value_source_for_term(schema_info, term)?,
974            term.direction(),
975        ));
976    }
977
978    Ok(Some(ResolvedOrder::new(fields)))
979}
980
981fn resolved_order_value_source_for_term(
982    schema_info: &SchemaInfo,
983    term: &crate::db::query::plan::OrderTerm,
984) -> Result<ResolvedOrderValueSource, InternalError> {
985    if term.direct_field().is_none() {
986        let rendered = term.rendered_label();
987        validate_resolved_order_expr_fields(schema_info, term.expr(), rendered.as_str())?;
988        let compiled = compile_scalar_projection_expr_with_schema(schema_info, term.expr())
989            .ok_or_else(|| order_expression_scalar_seam_error(rendered.as_str()))?;
990
991        return Ok(ResolvedOrderValueSource::expression(CompiledExpr::compile(
992            &compiled,
993        )));
994    }
995
996    let Some(field) = term.direct_field() else {
997        return Err(InternalError::query_invalid_logical_plan());
998    };
999    let slot = resolve_required_schema_slot(
1000        schema_info,
1001        field,
1002        InternalError::query_invalid_logical_plan,
1003    )?;
1004
1005    Ok(ResolvedOrderValueSource::direct_field(slot))
1006}
1007
1008fn validate_resolved_order_expr_fields(
1009    schema_info: &SchemaInfo,
1010    expr: &Expr,
1011    rendered: &str,
1012) -> Result<(), InternalError> {
1013    expr.try_for_each_tree_expr(&mut |node| match node {
1014        Expr::Field(field_id) => resolve_required_schema_slot(
1015            schema_info,
1016            field_id.as_str(),
1017            InternalError::query_invalid_logical_plan,
1018        )
1019        .map(|_| ()),
1020        Expr::Aggregate(_) => Err(order_expression_scalar_seam_error(rendered)),
1021        #[cfg(test)]
1022        Expr::Alias { .. } => Err(order_expression_scalar_seam_error(rendered)),
1023        Expr::Unary { .. } => Err(order_expression_scalar_seam_error(rendered)),
1024        _ => Ok(()),
1025    })
1026}
1027
1028// Resolve one schema-authoritative field slot while keeping planner
1029// invalid-logical-plan error construction at the callsite that owns the
1030// diagnostic wording.
1031fn resolve_required_schema_slot<F>(
1032    schema_info: &SchemaInfo,
1033    field: &str,
1034    invalid_plan_error: F,
1035) -> Result<usize, InternalError>
1036where
1037    F: FnOnce() -> InternalError,
1038{
1039    schema_info
1040        .field_slot_index(field)
1041        .ok_or_else(invalid_plan_error)
1042}
1043
1044// Keep the scalar-order expression seam violation text under one helper so the
1045// parse validation and compile validation paths do not drift.
1046fn order_expression_scalar_seam_error(_rendered: &str) -> InternalError {
1047    InternalError::query_invalid_logical_plan()
1048}
1049
1050// Keep one stable executor-facing slot list for grouped order terms after the
1051// planner has frozen the structural `ResolvedOrder`. The grouped Top-K route
1052// now consumes this same referenced-slot contract instead of re-deriving order
1053// sources from planner strategy at runtime.
1054fn order_referenced_slots_for_resolved_order(
1055    resolved_order: Option<&ResolvedOrder>,
1056) -> Option<Vec<usize>> {
1057    Some(resolved_order?.referenced_slots())
1058}
1059
1060fn slot_map_for_schema_plan(
1061    schema_info: &SchemaInfo,
1062    plan: &AccessPlannedQuery,
1063) -> Option<Vec<usize>> {
1064    let executable = plan.access.executable_contract();
1065
1066    resolved_index_slots_for_access_path(schema_info, &executable)
1067}
1068
1069fn resolved_index_slots_for_access_path(
1070    schema_info: &SchemaInfo,
1071    access: &ExecutableAccessPlan<'_, crate::value::Value>,
1072) -> Option<Vec<usize>> {
1073    let path = access.as_path()?;
1074    let path_facts = path.shape_facts();
1075    let key_items = path_facts.index_key_items_for_slot_map()?;
1076    let mut slots = Vec::new();
1077
1078    match key_items.key_items() {
1079        SemanticIndexKeyItemsRef::Fields(fields) => {
1080            slots.reserve(fields.len());
1081            for field_name in fields {
1082                let slot = schema_info.field_slot_index(field_name)?;
1083                slots.push(slot);
1084            }
1085        }
1086        SemanticIndexKeyItemsRef::Accepted(items) => {
1087            slots.reserve(items.len());
1088            for key_item in items {
1089                let slot = schema_info.field_slot_index(key_item.as_ref().field())?;
1090                slots.push(slot);
1091            }
1092        }
1093        SemanticIndexKeyItemsRef::Static(IndexKeyItemsRef::Fields(fields)) => {
1094            slots.reserve(fields.len());
1095            for &field_name in fields {
1096                let slot = schema_info.field_slot_index(field_name)?;
1097                slots.push(slot);
1098            }
1099        }
1100        SemanticIndexKeyItemsRef::Static(IndexKeyItemsRef::Items(items)) => {
1101            slots.reserve(items.len());
1102            for key_item in items {
1103                let slot = schema_info.field_slot_index(key_item.field())?;
1104                slots.push(slot);
1105            }
1106        }
1107    }
1108
1109    Some(slots)
1110}
1111
1112fn index_compile_targets_for_schema_plan(
1113    schema_info: &SchemaInfo,
1114    plan: &AccessPlannedQuery,
1115) -> Option<Vec<IndexCompileTarget>> {
1116    let executable = plan.access.executable_contract();
1117    let path = executable.as_path()?;
1118    let key_items = path.shape_facts().index_key_items_for_slot_map()?;
1119    let mut targets = Vec::new();
1120
1121    match key_items.key_items() {
1122        SemanticIndexKeyItemsRef::Fields(_) | SemanticIndexKeyItemsRef::Accepted(_) => {
1123            return None;
1124        }
1125        SemanticIndexKeyItemsRef::Static(IndexKeyItemsRef::Fields(fields)) => {
1126            for (component_index, &field_name) in fields.iter().enumerate() {
1127                let field_slot = schema_info.field_slot_index(field_name)?;
1128                targets.push(IndexCompileTarget {
1129                    component_index,
1130                    field_slot,
1131                    key_item: IndexKeyItem::Field(field_name),
1132                });
1133            }
1134        }
1135        SemanticIndexKeyItemsRef::Static(IndexKeyItemsRef::Items(items)) => {
1136            for (component_index, &key_item) in items.iter().enumerate() {
1137                let field_slot = schema_info.field_slot_index(key_item.field())?;
1138                targets.push(IndexCompileTarget {
1139                    component_index,
1140                    field_slot,
1141                    key_item,
1142                });
1143            }
1144        }
1145    }
1146
1147    Some(targets)
1148}