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