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
6use crate::db::{QueryError, query::preparation::PreparationWork};
7use icydb_diagnostic_code::DiagnosticExecutionBudgetResource as Resource;
8
9use crate::db::predicate::MissingRowPolicy;
10use crate::{
11    db::{
12        access::{AccessPlan, SemanticIndexKeyItemRef},
13        predicate::{IndexCompileTarget, IndexCompileTargetKind, Predicate, PredicateProgram},
14        query::plan::{
15            AccessPlannedQuery, ContinuationPolicy, DistinctExecutionStrategy,
16            EffectiveRuntimeFilterProgram, ExecutionShapeSignature, GroupPlan,
17            GroupedAggregateExecutionSpec, GroupedDistinctExecutionStrategy, GroupedPlanStrategy,
18            LogicalPlan, PlannerRouteProfile, PredicatePushdownDiagnostics, QueryMode,
19            ResidualFilterContract, ResidualFilterShape, ResolvedOrder, ResolvedOrderField,
20            ResolvedOrderValueSource, ScalarPlan, StaticExecutionPlanningContract,
21            derive_logical_pushdown_eligibility,
22            expr::{
23                CompiledExpr, Expr, ProjectionSpec, compile_scalar_projection_expr_with_schema,
24                compile_scalar_projection_plan_with_schema,
25            },
26            extend_unique_grouped_aggregate_specs_from_expr, grouped_aggregate_execution_specs,
27            grouped_aggregate_specs_from_projection_spec, grouped_cursor_policy_violation,
28            grouped_plan_strategy, lower_direct_projection_layouts_with_schema,
29            lower_projection_identity, lower_projection_intent_with_schema,
30            residual_query_predicate_after_access_path_bounds,
31            residual_query_predicate_after_filtered_access_contract,
32            resolved_grouped_distinct_execution_strategy_with_schema_info,
33        },
34        schema::SchemaInfo,
35    },
36    error::InternalError,
37    value::Value,
38};
39
40impl QueryMode {
41    /// True if this mode represents a load intent.
42    #[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    /// True if this mode represents a delete intent.
51    #[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    /// Borrow scalar semantic fields shared by scalar/grouped logical variants.
62    #[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
71impl AccessPlannedQuery {
72    /// Borrow scalar semantic fields shared by scalar/grouped logical variants.
73    #[must_use]
74    pub(in crate::db) const fn scalar_plan(&self) -> &ScalarPlan {
75        self.logical.scalar_semantics()
76    }
77
78    /// Borrow scalar missing-row consistency without exposing the full scalar
79    /// plan to executor owners that only need row-presence policy.
80    #[must_use]
81    pub(in crate::db) fn scalar_consistency(&self) -> MissingRowPolicy {
82        if self.access.has_selected_index_access_path() {
83            // An accepted secondary index is a persisted claim that every
84            // emitted key identifies an authoritative row. Ignoring a missing
85            // row would turn accepted-index corruption into an incomplete
86            // successful result.
87            MissingRowPolicy::Error
88        } else {
89            self.scalar_plan().consistency
90        }
91    }
92
93    /// Borrow grouped semantic fields when this plan is grouped.
94    #[must_use]
95    pub(in crate::db) const fn grouped_plan(&self) -> Option<&GroupPlan> {
96        match &self.logical {
97            LogicalPlan::Scalar(_) => None,
98            LogicalPlan::Grouped(plan) => Some(plan),
99        }
100    }
101
102    /// Borrow the projection retained by successful accepted-schema finalization.
103    /// Unprepared plans cannot reconstruct execution metadata on demand.
104    pub(in crate::db) fn projection_spec(&self) -> Result<&ProjectionSpec, QueryError> {
105        self.static_execution_planning_contract
106            .as_ref()
107            .map(|contract| &contract.projection_spec)
108            .ok_or_else(QueryError::invariant)
109    }
110
111    /// Construct the projection once for semantic validation and finalization.
112    pub(in crate::db) fn prepare_projection(
113        &self,
114        schema: &SchemaInfo,
115        work: &PreparationWork<'_>,
116    ) -> Result<ProjectionSpec, QueryError> {
117        lower_projection_intent_with_schema(schema, &self.logical, &self.projection_selection, work)
118    }
119
120    /// Lower this plan into one projection semantic shape for identity hashing.
121    #[must_use]
122    pub(in crate::db::query) fn projection_spec_for_identity(&self) -> ProjectionSpec {
123        lower_projection_identity(&self.logical, &self.projection_selection)
124    }
125
126    /// Return the executor-facing predicate after removing only filtered-index
127    /// guard clauses the chosen access path already proves.
128    ///
129    /// This conservative form is used by preparation/explain surfaces that
130    /// still need to see access-bound equalities as index-predicate input.
131    /// Copies are admitted before construction; guard pruning consumes them.
132    pub(in crate::db) fn execution_preparation_predicate(
133        &self,
134        work: &PreparationWork<'_>,
135    ) -> Result<Option<Predicate>, QueryError> {
136        if let Some(static_contract) = self.static_execution_planning_contract.as_ref() {
137            return static_contract
138                .execution_preparation_predicate
139                .as_ref()
140                .map(|predicate| work.copy_predicate(predicate))
141                .transpose();
142        }
143
144        let predicate = self
145            .scalar_plan()
146            .predicate
147            .as_ref()
148            .map(|predicate| work.copy_predicate(predicate))
149            .transpose()?;
150        derive_execution_preparation_predicate(&self.access, predicate, work)
151            .map_err(QueryError::execute)
152    }
153
154    /// Require frozen residual facts. Inspection never performs planning work.
155    pub(in crate::db) fn residual_filter_contract(
156        &self,
157    ) -> Result<&ResidualFilterContract, InternalError> {
158        Ok(&self
159            .require_static_execution_planning_contract()?
160            .residual_filter_contract)
161    }
162
163    /// Borrow the frozen executor-facing residual predicate.
164    pub(in crate::db) fn effective_execution_predicate(
165        &self,
166    ) -> Result<Option<&Predicate>, InternalError> {
167        Ok(self.residual_filter_contract()?.residual_filter_predicate())
168    }
169
170    /// Return whether the frozen contract retains predicate filtering.
171    pub(in crate::db) fn has_residual_filter_predicate(&self) -> Result<bool, InternalError> {
172        Ok(self.effective_execution_predicate()?.is_some())
173    }
174
175    /// Borrow the frozen semantic expression used for residual filtering.
176    pub(in crate::db) fn residual_filter_expr(&self) -> Result<Option<&Expr>, InternalError> {
177        Ok(self.residual_filter_contract()?.residual_filter_expr())
178    }
179
180    /// Return the frozen diagnostics-facing residual shape.
181    pub(in crate::db) fn residual_filter_shape(
182        &self,
183    ) -> Result<ResidualFilterShape, InternalError> {
184        Ok(self.residual_filter_contract()?.shape())
185    }
186
187    /// Prepare pre-finalization shape explicitly; inspection uses frozen facts.
188    pub(in crate::db) fn prepare_residual_filter_shape(
189        &self,
190        budget: &dyn crate::db::resource::construction::ConstructionBudget,
191    ) -> Result<ResidualFilterShape, InternalError> {
192        if self.has_static_execution_planning_contract() {
193            return self.residual_filter_shape();
194        }
195        let predicate = self
196            .scalar_plan()
197            .predicate
198            .as_ref()
199            .map(|predicate| budget.copy_predicate(predicate))
200            .transpose()?;
201        Ok(
202            residual_filter_facts_for_access(self.scalar_plan(), &self.access, predicate, budget)?
203                .0,
204        )
205    }
206
207    /// Borrow planner-frozen predicate-pushdown diagnostics.
208    #[cfg(feature = "sql")]
209    pub(in crate::db) fn predicate_pushdown_diagnostics(
210        &self,
211    ) -> Result<PredicatePushdownDiagnostics, InternalError> {
212        Ok(self
213            .require_static_execution_planning_contract()?
214            .predicate_pushdown_diagnostics)
215    }
216
217    /// Borrow the planner-compiled execution-preparation predicate program.
218    #[must_use]
219    pub(in crate::db) fn execution_preparation_compiled_predicate(
220        &self,
221    ) -> Option<&PredicateProgram> {
222        self.static_execution_planning_contract()?
223            .execution_preparation_compiled_predicate
224            .as_ref()
225    }
226
227    /// Borrow a native predicate only when it is the complete runtime filter.
228    #[must_use]
229    pub(in crate::db) fn effective_runtime_compiled_predicate(&self) -> Option<&PredicateProgram> {
230        let program = self.effective_runtime_filter_program()?;
231        if program.expression_filter().is_some() {
232            return None;
233        }
234
235        program.predicate_program()
236    }
237
238    /// Borrow the planner-frozen effective runtime scalar filter program.
239    #[must_use]
240    pub(in crate::db) fn effective_runtime_filter_program(
241        &self,
242    ) -> Option<&EffectiveRuntimeFilterProgram> {
243        self.static_execution_planning_contract()?
244            .residual_filter_contract
245            .effective_runtime_filter_program()
246    }
247
248    /// Lower scalar DISTINCT semantics into one executor-facing execution strategy.
249    #[must_use]
250    pub(in crate::db) const fn distinct_execution_strategy(&self) -> DistinctExecutionStrategy {
251        if !self.scalar_plan().distinct {
252            return DistinctExecutionStrategy::None;
253        }
254
255        // Key identity follows the access contract, independently of projected
256        // value DISTINCT. Canonical multi-lookup prefixes are disjoint: a row's
257        // single leading index component belongs to exactly one branch. Their
258        // secondary order must not acquire a primary-key monotonicity check.
259        distinct_runtime_dedup_strategy(&self.access)
260    }
261
262    /// Freeze one planner-owned route profile from accepted schema authority.
263    pub(in crate::db) fn finalize_planner_route_profile_for_model_with_schema(
264        &mut self,
265        schema_info: &SchemaInfo,
266        work: &PreparationWork<'_>,
267    ) -> Result<(), InternalError> {
268        self.set_planner_route_profile(project_planner_route_profile_for_schema(
269            schema_info,
270            self,
271            work,
272        )?);
273        Ok(())
274    }
275
276    /// Freeze planner-owned executor metadata, consuming the validated projection.
277    /// Accepted group keys are already resolved during intent conversion;
278    /// rebinding refreshes slot authority without changing projection expressions.
279    pub(in crate::db) fn finalize_static_execution_planning_contract_with_schema(
280        &mut self,
281        schema_info: &SchemaInfo,
282        projection: ProjectionSpec,
283        work: &PreparationWork<'_>,
284    ) -> Result<(), QueryError> {
285        self.bind_group_field_slots_to_schema(schema_info, work)?;
286        self.static_execution_planning_contract =
287            Some(project_static_execution_planning_contract_with_schema(
288                schema_info,
289                self,
290                projection,
291                work,
292            )?);
293
294        Ok(())
295    }
296
297    // Resolve authoring-time group field names onto accepted slots before the
298    // plan becomes executable.
299    fn bind_group_field_slots_to_schema(
300        &mut self,
301        schema_info: &SchemaInfo,
302        work: &PreparationWork<'_>,
303    ) -> Result<(), QueryError> {
304        let LogicalPlan::Grouped(grouped) = &mut self.logical else {
305            return Ok(());
306        };
307
308        let accepted_fields = grouped
309            .group
310            .group_fields
311            .resolve_with_schema(schema_info, work)?
312            .ok_or_else(|| QueryError::execute(InternalError::planner_executor_invariant()))?;
313        grouped.group.group_fields = accepted_fields;
314
315        Ok(())
316    }
317
318    /// Build one immutable execution-shape signature contract for runtime layers.
319    pub(in crate::db) fn execution_shape_signature(
320        &self,
321        entity_path: &str,
322        budget: &dyn crate::db::resource::construction::ConstructionBudget,
323    ) -> Result<ExecutionShapeSignature, InternalError> {
324        Ok(ExecutionShapeSignature::new(
325            self.continuation_signature(entity_path, budget)?,
326        ))
327    }
328
329    /// Borrow the planner-frozen compiled scalar projection program.
330    #[must_use]
331    pub(in crate::db) fn scalar_projection_plan(&self) -> Option<&[CompiledExpr]> {
332        self.static_execution_planning_contract()?
333            .scalar_projection_plan
334            .as_deref()
335    }
336
337    /// Return whether planner-owned static execution metadata has already been frozen.
338    #[must_use]
339    pub(in crate::db) const fn has_static_execution_planning_contract(&self) -> bool {
340        self.static_execution_planning_contract.is_some()
341    }
342
343    /// Borrow the planner-frozen ordered primary-key field names.
344    pub(in crate::db) fn primary_key_names(&self) -> Result<&[String], InternalError> {
345        Ok(&self
346            .require_static_execution_planning_contract()?
347            .primary_key_names)
348    }
349
350    /// Borrow the planner-frozen projection slot reachability set.
351    pub(in crate::db) fn projection_referenced_slots(&self) -> Result<&[usize], InternalError> {
352        Ok(self
353            .require_static_execution_planning_contract()?
354            .projection_referenced_slots
355            .as_slice())
356    }
357
358    /// Return whether projection remains the full model-identity field list.
359    pub(in crate::db) fn projection_is_model_identity(&self) -> Result<bool, InternalError> {
360        Ok(self
361            .require_static_execution_planning_contract()?
362            .projection_is_model_identity)
363    }
364
365    /// Borrow the planner-frozen ORDER BY slot reachability set, if any.
366    #[must_use]
367    pub(in crate::db) fn order_referenced_slots(&self) -> Option<&[usize]> {
368        self.static_execution_planning_contract()?
369            .order_referenced_slots
370            .as_deref()
371    }
372
373    /// Borrow the planner-frozen resolved ORDER BY program, if one exists.
374    #[must_use]
375    pub(in crate::db) fn resolved_order(&self) -> Option<&ResolvedOrder> {
376        self.static_execution_planning_contract()?
377            .resolved_order
378            .as_ref()
379    }
380
381    /// Borrow the planner-frozen access slot map used by index predicate compilation.
382    #[must_use]
383    pub(in crate::db) fn slot_map(&self) -> Option<&[usize]> {
384        self.static_execution_planning_contract()?
385            .slot_map
386            .as_deref()
387    }
388
389    /// Borrow grouped aggregate execution specs already resolved during static planning.
390    #[must_use]
391    pub(in crate::db) fn grouped_aggregate_execution_specs(
392        &self,
393    ) -> Option<&[GroupedAggregateExecutionSpec]> {
394        self.static_execution_planning_contract()?
395            .grouped_aggregate_execution_specs
396            .as_deref()
397    }
398
399    /// Borrow the planner-resolved grouped DISTINCT execution strategy when present.
400    #[must_use]
401    pub(in crate::db) fn grouped_distinct_execution_strategy(
402        &self,
403    ) -> Option<&GroupedDistinctExecutionStrategy> {
404        self.static_execution_planning_contract()?
405            .grouped_distinct_execution_strategy
406            .as_ref()
407    }
408
409    /// Borrow the frozen projection semantic shape without reopening model ownership.
410    pub(in crate::db) fn frozen_projection_spec(&self) -> Result<&ProjectionSpec, InternalError> {
411        Ok(&self
412            .require_static_execution_planning_contract()?
413            .projection_spec)
414    }
415
416    /// Borrow the frozen direct projection slots without reopening model ownership.
417    #[must_use]
418    pub(in crate::db) fn frozen_direct_projection_slots(&self) -> Option<&[usize]> {
419        self.static_execution_planning_contract()?
420            .projection_direct_slots
421            .as_deref()
422    }
423
424    /// Borrow duplicate-preserving direct projection slots for raw data-row readers.
425    #[must_use]
426    pub(in crate::db) fn frozen_data_row_direct_projection_slots(&self) -> Option<&[usize]> {
427        self.static_execution_planning_contract()?
428            .projection_data_row_direct_slots
429            .as_deref()
430    }
431
432    /// Borrow the planner-frozen key-item-aware compile targets for the chosen access path.
433    #[must_use]
434    pub(in crate::db) fn index_compile_targets(&self) -> Option<&[IndexCompileTarget]> {
435        self.static_execution_planning_contract()?
436            .index_compile_targets
437            .as_deref()
438    }
439
440    const fn static_execution_planning_contract(&self) -> Option<&StaticExecutionPlanningContract> {
441        self.static_execution_planning_contract.as_ref()
442    }
443
444    fn require_static_execution_planning_contract(
445        &self,
446    ) -> Result<&StaticExecutionPlanningContract, InternalError> {
447        self.static_execution_planning_contract
448            .as_ref()
449            .ok_or_else(InternalError::query_executor_invariant)
450    }
451}
452
453const fn distinct_runtime_dedup_strategy<K>(access: &AccessPlan<K>) -> DistinctExecutionStrategy {
454    match access {
455        AccessPlan::Union(_) | AccessPlan::Intersection(_) => DistinctExecutionStrategy::PreOrdered,
456        AccessPlan::Path(_) => DistinctExecutionStrategy::None,
457    }
458}
459
460fn derive_continuation_policy_validated(plan: &AccessPlannedQuery) -> ContinuationPolicy {
461    let is_grouped_safe = plan
462        .grouped_plan()
463        .is_none_or(|grouped| grouped_cursor_policy_violation(grouped, true).is_none());
464
465    ContinuationPolicy::new(
466        true, // Continuation resume windows require anchor semantics for pushdown-safe replay.
467        true, // Continuation resumes must advance strictly to prevent replay/regression loops.
468        is_grouped_safe,
469    )
470}
471
472/// Project one planner-owned route profile from accepted schema authority.
473pub(in crate::db) fn project_planner_route_profile_for_schema(
474    schema_info: &SchemaInfo,
475    plan: &AccessPlannedQuery,
476    work: &PreparationWork<'_>,
477) -> Result<PlannerRouteProfile, InternalError> {
478    let secondary_order_contract = plan
479        .scalar_plan()
480        .order
481        .as_ref()
482        .map(|order| {
483            order.deterministic_secondary_order_contract_fields(
484                schema_info.shared_primary_key_names(),
485                work,
486            )
487        })
488        .transpose()?
489        .flatten();
490
491    Ok(PlannerRouteProfile::new(
492        derive_continuation_policy_validated(plan),
493        derive_logical_pushdown_eligibility(plan, secondary_order_contract.as_ref()),
494        secondary_order_contract,
495    ))
496}
497
498fn project_static_execution_planning_contract_with_schema(
499    schema_info: &SchemaInfo,
500    plan: &AccessPlannedQuery,
501    projection_spec: ProjectionSpec,
502    work: &PreparationWork<'_>,
503) -> Result<StaticExecutionPlanningContract, QueryError> {
504    let execution_preparation_predicate = plan.execution_preparation_predicate(work)?;
505    // Preparation retains its predicate independently. Admit the residual's
506    // owned copy before pruning it in place; pruning creates no new predicate backing.
507    let residual_input = execution_preparation_predicate
508        .as_ref()
509        .map(|predicate| work.copy_predicate(predicate))
510        .transpose()?;
511    let (residual_filter_predicate, access_satisfied) =
512        derive_residual_filter_predicate_from_preparation(
513            plan.scalar_plan(),
514            &plan.access,
515            residual_input,
516            work,
517        )
518        .map_err(QueryError::execute)?;
519    let residual_filter_expr = derive_residual_filter_expr(plan, access_satisfied);
520    let effective_runtime_filter_program = compile_effective_runtime_filter_program(
521        schema_info,
522        residual_filter_expr.as_ref(),
523        residual_filter_predicate.as_ref(),
524        plan.scalar_plan().predicate_covers_filter_expr,
525        work,
526    )
527    .map_err(QueryError::execute)?;
528    let residual_filter_contract = ResidualFilterContract::new(
529        residual_filter_expr,
530        residual_filter_predicate,
531        effective_runtime_filter_program,
532    );
533    let residual_filter_shape = residual_filter_contract.shape();
534    let execution_preparation_compiled_predicate =
535        (should_compile_execution_preparation_predicate(residual_filter_shape)
536            && !planner_predicate_requires_expression_runtime(plan.scalar_plan()))
537        .then(|| compile_optional_predicate(schema_info, execution_preparation_predicate.as_ref()))
538        .flatten();
539    let predicate_pushdown_diagnostics =
540        derive_predicate_pushdown_diagnostics(plan, residual_filter_shape);
541    let scalar_projection_plan = if plan.grouped_plan().is_none() {
542        Some(
543            compile_scalar_projection_plan_with_schema(schema_info, &projection_spec, work)
544                .map_err(QueryError::execute)?
545                .ok_or_else(|| QueryError::execute(InternalError::query_executor_invariant()))?,
546        )
547    } else {
548        None
549    };
550    let (grouped_aggregate_execution_specs, grouped_distinct_execution_strategy) =
551        resolve_grouped_static_planning_semantics(schema_info, plan, &projection_spec, work)
552            .map_err(QueryError::execute)?;
553    let (projection_direct_slots, projection_data_row_direct_slots) =
554        lower_direct_projection_layouts_with_schema(
555            schema_info,
556            &plan.logical,
557            &projection_spec,
558            work,
559        )?;
560    let projection_referenced_slots =
561        projection_spec.referenced_slots_for_schema(schema_info, work)?;
562    let projection_is_model_identity = projection_spec.is_schema_identity_for(schema_info, work)?;
563    let resolved_order = resolved_order_for_plan(schema_info, plan, work)?;
564    let order_referenced_slots = resolved_order
565        .as_ref()
566        .map(|order| order.referenced_slots(work))
567        .transpose()?;
568    let (slot_map, index_compile_targets) =
569        index_execution_metadata_for_schema_plan(schema_info, plan, work)?
570            .map_or((None, None), |(slots, targets)| {
571                (Some(slots), Some(targets))
572            });
573
574    Ok(StaticExecutionPlanningContract {
575        primary_key_names: schema_info.shared_primary_key_names(),
576        projection_spec,
577        execution_preparation_predicate,
578        execution_preparation_compiled_predicate,
579        residual_filter_contract,
580        predicate_pushdown_diagnostics,
581        scalar_projection_plan,
582        grouped_aggregate_execution_specs,
583        grouped_distinct_execution_strategy,
584        projection_direct_slots,
585        projection_data_row_direct_slots,
586        projection_referenced_slots,
587        projection_is_model_identity,
588        resolved_order,
589        order_referenced_slots,
590        slot_map,
591        index_compile_targets,
592    })
593}
594
595// Compile the executor-owned residual scalar filter contract once from the
596// planner-derived residual artifacts so runtime never has to rediscover
597// residual presence or shape from semantic/filter/pushdown state.
598fn compile_effective_runtime_filter_program(
599    schema_info: &SchemaInfo,
600    residual_filter_expr: Option<&Expr>,
601    residual_filter_predicate: Option<&Predicate>,
602    predicate_covers_filter_expr: bool,
603    work: &PreparationWork<'_>,
604) -> Result<Option<EffectiveRuntimeFilterProgram>, InternalError> {
605    // A predicate may replace an expression only with the intent coverage proof.
606    // Otherwise both residual authorities survive in the single runtime program.
607    if let Some(predicate) = residual_filter_predicate
608        && (residual_filter_expr.is_none() || predicate_covers_filter_expr)
609    {
610        return Ok(Some(EffectiveRuntimeFilterProgram::predicate(
611            PredicateProgram::compile_with_schema_info(schema_info, predicate),
612        )));
613    }
614
615    if let Some(filter_expr) = residual_filter_expr {
616        let compiled = compile_scalar_projection_expr_with_schema(schema_info, filter_expr, work)?
617            .ok_or_else(InternalError::query_invalid_logical_plan)?;
618
619        return Ok(Some(EffectiveRuntimeFilterProgram::expression(
620            compiled,
621            compile_optional_predicate(schema_info, residual_filter_predicate),
622        )));
623    }
624
625    Ok(None)
626}
627
628// Derive the executor-preparation predicate once from the selected access path.
629// This strips only filtered-index guard clauses while preserving access-bound
630// equalities that still matter to preparation/explain consumers.
631fn derive_execution_preparation_predicate(
632    access: &AccessPlan<Value>,
633    query_predicate: Option<Predicate>,
634    budget: &dyn crate::db::resource::construction::ConstructionBudget,
635) -> Result<Option<Predicate>, InternalError> {
636    let Some(query_predicate) = query_predicate else {
637        return Ok(None);
638    };
639    match access.selected_index_contract() {
640        Some(index) => {
641            residual_query_predicate_after_filtered_access_contract(index, query_predicate, budget)
642        }
643        None => Ok(Some(query_predicate)),
644    }
645}
646
647// Derive the final residual predicate once from the already-filtered
648// preparation predicate plus any equality bounds guaranteed by the concrete
649// access path.
650fn derive_residual_filter_predicate(
651    scalar: &ScalarPlan,
652    access: &AccessPlan<Value>,
653    query_predicate: Option<Predicate>,
654    budget: &dyn crate::db::resource::construction::ConstructionBudget,
655) -> Result<Option<Predicate>, InternalError> {
656    let filtered = derive_execution_preparation_predicate(access, query_predicate, budget)?;
657    Ok(derive_residual_filter_predicate_from_preparation(scalar, access, filtered, budget)?.0)
658}
659
660fn derive_residual_filter_predicate_from_preparation(
661    scalar: &ScalarPlan,
662    access: &AccessPlan<Value>,
663    execution_preparation_predicate: Option<Predicate>,
664    budget: &dyn crate::db::resource::construction::ConstructionBudget,
665) -> Result<(Option<Predicate>, bool), InternalError> {
666    let Some(execution_preparation_predicate) = execution_preparation_predicate else {
667        return Ok((None, false));
668    };
669
670    let residual = residual_query_predicate_after_access_path_bounds(
671        access.as_path(),
672        execution_preparation_predicate,
673        budget,
674    )?;
675    if residual.is_some() && planner_predicate_requires_expression_runtime(scalar) {
676        return Ok((None, false));
677    }
678
679    let access_satisfied = residual.is_none();
680    Ok((residual, access_satisfied))
681}
682
683// Derive the explicit residual semantic expression once for finalized plans.
684// The residual expression remains the planner-owned semantic filter when any
685// runtime filtering still survives access satisfaction.
686fn derive_residual_filter_expr(plan: &AccessPlannedQuery, access_satisfied: bool) -> Option<Expr> {
687    let filter_expr = plan.scalar_plan().filter_expr.as_ref()?;
688    if derive_semantic_filter_fully_satisfied_by_access_contract(plan.scalar_plan())
689        && (!planner_predicate_requires_expression_runtime(plan.scalar_plan()) || access_satisfied)
690    {
691        return None;
692    }
693
694    Some(filter_expr.clone())
695}
696
697// Nested-path predicate shells are planner facts only: the predicate runtime
698// addresses top-level row slots. Keep the already-compiled expression as the
699// execution authority unless the selected access path proves the predicate in
700// full and no runtime filter remains.
701fn planner_predicate_requires_expression_runtime(scalar: &ScalarPlan) -> bool {
702    scalar.predicate_covers_filter_expr
703        && scalar
704            .filter_expr
705            .as_ref()
706            .is_some_and(Expr::contains_field_path)
707}
708
709// Freeze predicate-pushdown diagnostics with the static execution contract.
710// Inspection borrows this result instead of repeating preparation.
711fn derive_predicate_pushdown_diagnostics(
712    plan: &AccessPlannedQuery,
713    residual_filter_shape: ResidualFilterShape,
714) -> PredicatePushdownDiagnostics {
715    PredicatePushdownDiagnostics::from_plan(
716        plan.scalar_plan().filter_expr.is_some(),
717        plan.scalar_plan().predicate_covers_filter_expr,
718        plan.scalar_plan().predicate.as_ref(),
719        &plan.access,
720        residual_filter_shape,
721    )
722}
723
724// Return true when the semantic filter expression is entirely represented by
725// the planner-owned predicate contract and the chosen access path satisfies
726// that predicate without any runtime remainder.
727const fn derive_semantic_filter_fully_satisfied_by_access_contract(scalar: &ScalarPlan) -> bool {
728    scalar.filter_expr.is_some()
729        && scalar.predicate.is_some()
730        && scalar.predicate_covers_filter_expr
731}
732
733/// Derive pre-finalization residual facts, consuming the caller's predicate copy.
734/// Budget-bearing callers admit ownership before entering this shared derivation.
735/// Finalized plans must continue reading their frozen residual contract instead.
736pub(in crate::db::query) fn residual_filter_facts_for_access(
737    scalar: &ScalarPlan,
738    access: &AccessPlan<Value>,
739    query_predicate: Option<Predicate>,
740    budget: &dyn crate::db::resource::construction::ConstructionBudget,
741) -> Result<(ResidualFilterShape, Option<Predicate>), InternalError> {
742    let predicate = derive_residual_filter_predicate(scalar, access, query_predicate, budget)?;
743    // Preserve the pre-finalization shape policy: a fully predicate-represented
744    // filter does not add a second expression category to candidate ranking.
745    let expression_required = scalar.filter_expr.is_some()
746        && !derive_semantic_filter_fully_satisfied_by_access_contract(scalar);
747
748    Ok((
749        ResidualFilterShape::from_presence(expression_required, predicate.is_some()),
750        predicate,
751    ))
752}
753
754// Compile one optional planner-frozen predicate program while keeping the
755// static planning assembly path free of repeated `Option` mapping boilerplate.
756fn compile_optional_predicate(
757    schema_info: &SchemaInfo,
758    predicate: Option<&Predicate>,
759) -> Option<PredicateProgram> {
760    predicate.map(|predicate| PredicateProgram::compile_with_schema_info(schema_info, predicate))
761}
762
763// Avoid compiling large access-proven predicates into executor preparation.
764// When no residual filter survives, the chosen access route already enforces
765// the predicate and route/explain consumers can use the explicit residual
766// contract instead of recompiling access-bound literals.
767const fn should_compile_execution_preparation_predicate(
768    residual_filter_shape: ResidualFilterShape,
769) -> bool {
770    !residual_filter_shape.is_absent()
771}
772
773// Resolve the grouped-only static planning semantics bundle once so grouped
774// aggregate execution specs and grouped DISTINCT strategy stay derived under
775// one shared grouped-plan branch.
776fn resolve_grouped_static_planning_semantics(
777    schema_info: &SchemaInfo,
778    plan: &AccessPlannedQuery,
779    projection_spec: &ProjectionSpec,
780    work: &PreparationWork<'_>,
781) -> Result<
782    (
783        Option<Vec<GroupedAggregateExecutionSpec>>,
784        Option<GroupedDistinctExecutionStrategy>,
785    ),
786    InternalError,
787> {
788    let Some(grouped) = plan.grouped_plan() else {
789        return Ok((None, None));
790    };
791
792    let mut aggregate_specs = grouped_aggregate_specs_from_projection_spec(projection_spec)?;
793    extend_grouped_having_aggregate_specs(&mut aggregate_specs, grouped)?;
794
795    let grouped_aggregate_execution_specs = Some(grouped_aggregate_execution_specs(
796        schema_info,
797        aggregate_specs,
798        work,
799    )?);
800    let grouped_distinct_execution_strategy = Some(
801        resolved_grouped_distinct_execution_strategy_with_schema_info(
802            schema_info,
803            &grouped.group.group_fields,
804            grouped.group.aggregates.as_slice(),
805            grouped.having_expr.as_ref(),
806        )?,
807    );
808
809    Ok((
810        grouped_aggregate_execution_specs,
811        grouped_distinct_execution_strategy,
812    ))
813}
814
815fn extend_grouped_having_aggregate_specs(
816    aggregate_specs: &mut Vec<GroupedAggregateExecutionSpec>,
817    grouped: &GroupPlan,
818) -> Result<(), InternalError> {
819    if let Some(having_expr) = grouped.having_expr.as_ref() {
820        extend_unique_grouped_aggregate_specs_from_expr(aggregate_specs, having_expr)?;
821    }
822
823    Ok(())
824}
825
826fn resolved_order_for_plan(
827    schema_info: &SchemaInfo,
828    plan: &AccessPlannedQuery,
829    work: &PreparationWork<'_>,
830) -> Result<Option<ResolvedOrder>, QueryError> {
831    if grouped_plan_strategy(plan, || plan.prepare_residual_filter_shape(work))
832        .map_err(QueryError::execute)?
833        .is_some_and(GroupedPlanStrategy::is_top_k_group)
834    {
835        return Ok(None);
836    }
837
838    let Some(order) = plan.scalar_plan().order.as_ref() else {
839        return Ok(None);
840    };
841
842    let mut fields = work.vec_with_capacity(order.fields.len())?;
843    for term in &order.fields {
844        fields.push(ResolvedOrderField::new(
845            resolved_order_value_source_for_term(schema_info, term, work)?,
846            term.direction(),
847        ));
848    }
849
850    Ok(Some(ResolvedOrder::new(fields)))
851}
852
853fn resolved_order_value_source_for_term(
854    schema_info: &SchemaInfo,
855    term: &crate::db::query::plan::OrderTerm,
856    work: &PreparationWork<'_>,
857) -> Result<ResolvedOrderValueSource, QueryError> {
858    if let Some(field) = term.direct_field() {
859        work.charge(Resource::PredicateExpressionSteps, 1 + field.len() as u64)?;
860        let slot = schema_info
861            .field_slot_index(field)
862            .ok_or_else(|| QueryError::execute(InternalError::query_invalid_logical_plan()))?;
863
864        return Ok(ResolvedOrderValueSource::direct_field(slot));
865    }
866
867    validate_resolved_order_scalar_seam(term.expr(), work)?;
868    // Scalar compilation owns accepted field resolution. Do not resolve every
869    // field again in the seam check or render a label for payload-free errors.
870    let compiled = compile_scalar_projection_expr_with_schema(schema_info, term.expr(), work)
871        .map_err(QueryError::execute)?
872        .ok_or_else(|| QueryError::execute(InternalError::query_invalid_logical_plan()))?;
873
874    Ok(ResolvedOrderValueSource::expression(compiled))
875}
876
877// The input-admitted tree has bounded depth. Charge each visited node before
878// descending; compilation and referenced-slot construction remain separate owners.
879fn validate_resolved_order_scalar_seam(
880    expr: &Expr,
881    work: &PreparationWork<'_>,
882) -> Result<(), QueryError> {
883    expr.try_for_each_tree_expr(&mut |node| {
884        work.charge(Resource::PredicateExpressionSteps, 1)?;
885        match node {
886            Expr::Aggregate(_) | Expr::Unary { .. } => Err(QueryError::execute(
887                InternalError::query_invalid_logical_plan(),
888            )),
889            _ => Ok(()),
890        }
891    })
892}
893
894type IndexExecutionMetadata = (Vec<usize>, Vec<IndexCompileTarget>);
895
896// Both executor slot maps and predicate compile targets describe the same
897// ordered key items. Resolve each root once and admit both destination arrays
898// before filling them. Missing schema resolution stays distinct from exhaustion.
899fn index_execution_metadata_for_schema_plan(
900    schema_info: &SchemaInfo,
901    plan: &AccessPlannedQuery,
902    work: &PreparationWork<'_>,
903) -> Result<Option<IndexExecutionMetadata>, QueryError> {
904    let executable = plan.access.executable_contract();
905    let Some(path) = executable.as_path() else {
906        return Ok(None);
907    };
908    let Some(key_items) = path.shape_facts().index_key_items_for_slot_map() else {
909        return Ok(None);
910    };
911    let mut slots = work.vec_with_capacity(key_items.key_arity())?;
912    let mut targets = work.vec_with_capacity(key_items.key_arity())?;
913
914    for (component_index, key_item) in key_items.key_items().iter().enumerate() {
915        let key_item = key_item.as_ref();
916        let field = key_item.field();
917        work.charge(Resource::PredicateExpressionSteps, 1 + field.len() as u64)?;
918        let root = field.split_once('.').map_or(field, |(root, _)| root);
919        let Some(field_slot) = schema_info.field_slot_index(root) else {
920            return Ok(None);
921        };
922        slots.push(field_slot);
923        targets.push(IndexCompileTarget {
924            component_index,
925            field_slot,
926            kind: match key_item {
927                SemanticIndexKeyItemRef::Field(_) => IndexCompileTargetKind::Field,
928                SemanticIndexKeyItemRef::AcceptedExpression(expression) => {
929                    IndexCompileTargetKind::Expression(expression.op())
930                }
931            },
932        });
933    }
934
935    Ok(Some((slots, targets)))
936}