Skip to main content

icydb_core/db/executor/
executable_plan.rs

1//! Module: db::executor::executable_plan
2//! Responsibility: bind validated access-planned queries to executor-ready contracts.
3//! Does not own: logical plan semantics or route policy decisions.
4//! Boundary: shared plan container for load/delete/aggregate runtime entrypoints.
5
6#[cfg(test)]
7use crate::db::executor::route::{
8    LoadTerminalFastPathContract, derive_load_terminal_fast_path_contract_for_model_plan,
9};
10use crate::{
11    db::{
12        access::AccessPlan,
13        cursor::{ContinuationSignature, CursorPlanError, GroupedPlannedCursor, PlannedCursor},
14        executor::{
15            EntityAuthority, ExecutionPreparation, ExecutorPlanError, GroupedPaginationWindow,
16            LoweredIndexPrefixSpec, LoweredIndexRangeSpec,
17            explain::assemble_load_execution_node_descriptor_with_model, lower_index_prefix_specs,
18            lower_index_range_specs, preparation::slot_map_for_model_plan,
19            traversal::row_read_consistency_for_plan,
20        },
21        predicate::MissingRowPolicy,
22        query::explain::ExplainExecutionNodeDescriptor,
23        query::plan::{
24            AccessPlannedQuery, ContinuationContract, ExecutionOrdering, GroupSpec, OrderSpec,
25            QueryMode, constant_covering_projection_value_from_access,
26            covering_index_projection_context,
27        },
28    },
29    error::InternalError,
30    traits::{EntityKind, EntityValue},
31};
32use std::marker::PhantomData;
33#[cfg(test)]
34use std::ops::Bound;
35///
36/// ExecutionStrategy
37///
38/// Executor-facing execution shape contract derived from planner ordering.
39/// Session and runtime entrypoints consume this strategy and must not
40/// re-derive grouped/scalar routing shape from boolean flags.
41///
42#[derive(Clone, Copy, Debug, Eq, PartialEq)]
43pub enum ExecutionStrategy {
44    PrimaryKey,
45    Ordered,
46    Grouped,
47}
48
49///
50/// BytesByProjectionMode
51///
52/// Canonical `bytes_by(field)` projection mode classification used by execution
53/// and explain surfaces.
54///
55
56#[derive(Clone, Copy, Debug, Eq, PartialEq)]
57pub(in crate::db) enum BytesByProjectionMode {
58    Materialized,
59    CoveringIndex,
60    CoveringConstant,
61}
62
63/// Classify canonical `bytes_by(field)` execution mode from one neutral access context.
64#[must_use]
65pub(in crate::db::executor) fn classify_bytes_by_projection_mode(
66    access: &AccessPlan<crate::value::Value>,
67    order_spec: Option<&OrderSpec>,
68    consistency: MissingRowPolicy,
69    has_predicate: bool,
70    target_field: &str,
71    primary_key_name: &'static str,
72) -> BytesByProjectionMode {
73    if !matches!(consistency, MissingRowPolicy::Ignore) {
74        return BytesByProjectionMode::Materialized;
75    }
76
77    if constant_covering_projection_value_from_access(access, target_field).is_some() {
78        return BytesByProjectionMode::CoveringConstant;
79    }
80
81    if has_predicate {
82        return BytesByProjectionMode::Materialized;
83    }
84
85    if covering_index_projection_context(access, order_spec, target_field, primary_key_name)
86        .is_some()
87    {
88        return BytesByProjectionMode::CoveringIndex;
89    }
90
91    BytesByProjectionMode::Materialized
92}
93
94/// ExecutablePlanCore
95///
96/// Generic-free executable-plan payload shared by typed `ExecutablePlan<E>`
97/// wrappers. This keeps cursor, ordering, and lowered structural plan state
98/// monomorphic while typed access and model-driven behavior remain at the
99/// outer executor boundary.
100///
101
102#[derive(Debug)]
103struct ExecutablePlanCore {
104    plan: AccessPlannedQuery,
105    continuation: Option<ContinuationContract>,
106    index_prefix_specs: Vec<LoweredIndexPrefixSpec>,
107    index_prefix_spec_invalid: bool,
108    index_range_specs: Vec<LoweredIndexRangeSpec>,
109    index_range_spec_invalid: bool,
110}
111
112impl ExecutablePlanCore {
113    #[must_use]
114    const fn new(
115        plan: AccessPlannedQuery,
116        continuation: Option<ContinuationContract>,
117        index_prefix_specs: Vec<LoweredIndexPrefixSpec>,
118        index_prefix_spec_invalid: bool,
119        index_range_specs: Vec<LoweredIndexRangeSpec>,
120        index_range_spec_invalid: bool,
121    ) -> Self {
122        Self {
123            plan,
124            continuation,
125            index_prefix_specs,
126            index_prefix_spec_invalid,
127            index_range_specs,
128            index_range_spec_invalid,
129        }
130    }
131
132    #[must_use]
133    const fn plan(&self) -> &AccessPlannedQuery {
134        &self.plan
135    }
136
137    #[must_use]
138    const fn mode(&self) -> QueryMode {
139        self.plan.scalar_plan().mode
140    }
141
142    #[must_use]
143    const fn is_grouped(&self) -> bool {
144        match self.continuation {
145            Some(ref contract) => contract.is_grouped(),
146            None => false,
147        }
148    }
149
150    fn execution_ordering(&self) -> Result<ExecutionOrdering, InternalError> {
151        let contract = self.continuation_contract()?;
152        Ok(contract.order_contract().ordering().clone())
153    }
154
155    fn execution_strategy(&self) -> Result<ExecutionStrategy, InternalError> {
156        let ordering = self.execution_ordering()?;
157
158        Ok(match ordering {
159            ExecutionOrdering::PrimaryKey => ExecutionStrategy::PrimaryKey,
160            ExecutionOrdering::Explicit(_) => ExecutionStrategy::Ordered,
161            ExecutionOrdering::Grouped(_) => ExecutionStrategy::Grouped,
162        })
163    }
164
165    #[must_use]
166    const fn consistency(&self) -> MissingRowPolicy {
167        row_read_consistency_for_plan(&self.plan)
168    }
169
170    #[must_use]
171    const fn order_spec(&self) -> Option<&OrderSpec> {
172        self.plan.scalar_plan().order.as_ref()
173    }
174
175    #[must_use]
176    fn has_predicate(&self) -> bool {
177        self.plan.has_residual_predicate()
178    }
179
180    fn index_prefix_specs(&self) -> Result<&[LoweredIndexPrefixSpec], InternalError> {
181        if self.index_prefix_spec_invalid {
182            return Err(
183                ExecutorPlanError::lowered_index_prefix_spec_invalid().into_internal_error()
184            );
185        }
186
187        Ok(self.index_prefix_specs.as_slice())
188    }
189
190    fn index_range_specs(&self) -> Result<&[LoweredIndexRangeSpec], InternalError> {
191        if self.index_range_spec_invalid {
192            return Err(ExecutorPlanError::lowered_index_range_spec_invalid().into_internal_error());
193        }
194
195        Ok(self.index_range_specs.as_slice())
196    }
197
198    #[must_use]
199    fn into_inner(self) -> AccessPlannedQuery {
200        self.plan
201    }
202
203    fn prepare_cursor(
204        &self,
205        authority: EntityAuthority,
206        cursor: Option<&[u8]>,
207    ) -> Result<PlannedCursor, ExecutorPlanError> {
208        let Some(contract) = self.continuation.as_ref() else {
209            return Err(ExecutorPlanError::continuation_cursor_requires_load_plan());
210        };
211
212        contract
213            .prepare_scalar_cursor(
214                authority.entity_path(),
215                authority.entity_tag(),
216                authority.model(),
217                cursor,
218            )
219            .map_err(ExecutorPlanError::from)
220    }
221
222    fn revalidate_cursor(
223        &self,
224        authority: EntityAuthority,
225        cursor: PlannedCursor,
226    ) -> Result<PlannedCursor, InternalError> {
227        let Some(contract) = self.continuation.as_ref() else {
228            return Err(
229                ExecutorPlanError::continuation_cursor_requires_load_plan().into_internal_error()
230            );
231        };
232
233        contract
234            .revalidate_scalar_cursor(authority.entity_tag(), authority.model(), cursor)
235            .map_err(CursorPlanError::into_internal_error)
236    }
237
238    fn revalidate_grouped_cursor(
239        &self,
240        cursor: GroupedPlannedCursor,
241    ) -> Result<GroupedPlannedCursor, InternalError> {
242        let Some(contract) = self.continuation.as_ref() else {
243            return Err(
244                ExecutorPlanError::grouped_cursor_revalidation_requires_grouped_plan()
245                    .into_internal_error(),
246            );
247        };
248
249        contract
250            .revalidate_grouped_cursor(cursor)
251            .map_err(CursorPlanError::into_internal_error)
252    }
253
254    fn continuation_signature_for_runtime(&self) -> Result<ContinuationSignature, InternalError> {
255        let contract = self.continuation_contract()?;
256        Ok(contract.continuation_signature())
257    }
258
259    fn grouped_cursor_boundary_arity(&self) -> Result<usize, InternalError> {
260        let contract = self.continuation_contract()?;
261        if !contract.is_grouped() {
262            return Err(
263                ExecutorPlanError::grouped_cursor_boundary_arity_requires_grouped_plan()
264                    .into_internal_error(),
265            );
266        }
267
268        Ok(contract.boundary_arity())
269    }
270
271    fn grouped_pagination_window(
272        &self,
273        cursor: &GroupedPlannedCursor,
274    ) -> Result<GroupedPaginationWindow, InternalError> {
275        let contract = self.continuation_contract()?;
276        let window = contract
277            .grouped_paging_window(cursor)
278            .map_err(CursorPlanError::into_internal_error)?;
279        let (
280            limit,
281            initial_offset_for_page,
282            selection_bound,
283            resume_initial_offset,
284            resume_boundary,
285        ) = window.into_parts();
286
287        Ok(GroupedPaginationWindow::new(
288            limit,
289            initial_offset_for_page,
290            selection_bound,
291            resume_initial_offset,
292            resume_boundary,
293        ))
294    }
295
296    // Borrow immutable continuation contract for load-mode plans.
297    fn continuation_contract(&self) -> Result<&ContinuationContract, InternalError> {
298        self.continuation.as_ref().ok_or_else(|| {
299            ExecutorPlanError::continuation_contract_requires_load_plan().into_internal_error()
300        })
301    }
302}
303
304// Build one canonical lowered executable-plan core from resolved authority
305// plus one logical plan, regardless of whether the caller started from a typed
306// `ExecutablePlan<E>` shell or a structural follow-on rewrite.
307fn build_executable_plan_core(
308    authority: EntityAuthority,
309    plan: AccessPlannedQuery,
310) -> ExecutablePlanCore {
311    // Phase 0: derive immutable continuation contract once from planner semantics.
312    let continuation = plan.continuation_contract(authority.entity_path());
313
314    // Phase 1: lower index-prefix specs once and retain invariant state.
315    let (index_prefix_specs, index_prefix_spec_invalid) =
316        match lower_index_prefix_specs(authority.entity_tag(), &plan.access) {
317            Ok(specs) => (specs, false),
318            Err(_) => (Vec::new(), true),
319        };
320
321    // Phase 2: lower index-range specs once and retain invariant state.
322    let (index_range_specs, index_range_spec_invalid) =
323        match lower_index_range_specs(authority.entity_tag(), &plan.access) {
324            Ok(specs) => (specs, false),
325            Err(_) => (Vec::new(), true),
326        };
327
328    ExecutablePlanCore::new(
329        plan,
330        continuation,
331        index_prefix_specs,
332        index_prefix_spec_invalid,
333        index_range_specs,
334        index_range_spec_invalid,
335    )
336}
337
338///
339/// ExecutablePlan
340///
341/// Executor-ready plan bound to a specific entity type.
342///
343
344#[derive(Debug)]
345pub(in crate::db) struct ExecutablePlan<E: EntityKind> {
346    core: ExecutablePlanCore,
347    marker: PhantomData<fn() -> E>,
348}
349
350///
351/// PreparedLoadPlan
352///
353/// Generic-free load-plan boundary consumed by continuation resolution and
354/// load pipeline preparation after the typed `ExecutablePlan<E>` shell is no
355/// longer needed.
356///
357
358#[derive(Debug)]
359pub(in crate::db::executor) struct PreparedLoadPlan {
360    authority: EntityAuthority,
361    core: ExecutablePlanCore,
362}
363
364impl PreparedLoadPlan {
365    #[must_use]
366    pub(in crate::db::executor) fn from_plan(
367        authority: EntityAuthority,
368        plan: AccessPlannedQuery,
369    ) -> Self {
370        Self {
371            authority,
372            core: build_executable_plan_core(authority, plan),
373        }
374    }
375
376    #[must_use]
377    pub(in crate::db::executor) const fn authority(&self) -> EntityAuthority {
378        self.authority
379    }
380
381    #[must_use]
382    pub(in crate::db::executor) const fn mode(&self) -> QueryMode {
383        self.core.mode()
384    }
385
386    #[must_use]
387    pub(in crate::db::executor) const fn logical_plan(&self) -> &AccessPlannedQuery {
388        self.core.plan()
389    }
390
391    pub(in crate::db::executor) fn execution_ordering(
392        &self,
393    ) -> Result<ExecutionOrdering, InternalError> {
394        self.core.execution_ordering()
395    }
396
397    pub(in crate::db::executor) fn revalidate_cursor(
398        &self,
399        cursor: PlannedCursor,
400    ) -> Result<PlannedCursor, InternalError> {
401        self.core.revalidate_cursor(self.authority, cursor)
402    }
403
404    pub(in crate::db::executor) fn revalidate_grouped_cursor(
405        &self,
406        cursor: GroupedPlannedCursor,
407    ) -> Result<GroupedPlannedCursor, InternalError> {
408        self.core.revalidate_grouped_cursor(cursor)
409    }
410
411    pub(in crate::db::executor) fn continuation_signature_for_runtime(
412        &self,
413    ) -> Result<ContinuationSignature, InternalError> {
414        self.core.continuation_signature_for_runtime()
415    }
416
417    pub(in crate::db::executor) fn grouped_cursor_boundary_arity(
418        &self,
419    ) -> Result<usize, InternalError> {
420        self.core.grouped_cursor_boundary_arity()
421    }
422
423    pub(in crate::db::executor) fn grouped_pagination_window(
424        &self,
425        cursor: &GroupedPlannedCursor,
426    ) -> Result<GroupedPaginationWindow, InternalError> {
427        self.core.grouped_pagination_window(cursor)
428    }
429
430    pub(in crate::db::executor) fn index_prefix_specs(
431        &self,
432    ) -> Result<&[LoweredIndexPrefixSpec], InternalError> {
433        self.core.index_prefix_specs()
434    }
435
436    pub(in crate::db::executor) fn index_range_specs(
437        &self,
438    ) -> Result<&[LoweredIndexRangeSpec], InternalError> {
439        self.core.index_range_specs()
440    }
441
442    #[must_use]
443    pub(in crate::db::executor) fn into_plan(self) -> AccessPlannedQuery {
444        self.core.into_inner()
445    }
446}
447
448///
449/// PreparedAggregatePlan
450///
451/// Generic-free aggregate-plan boundary consumed by aggregate terminal and
452/// runtime preparation after the typed `ExecutablePlan<E>` shell is no longer
453/// needed.
454///
455
456#[derive(Debug)]
457pub(in crate::db::executor) struct PreparedAggregatePlan {
458    authority: EntityAuthority,
459    core: ExecutablePlanCore,
460}
461
462impl PreparedAggregatePlan {
463    #[must_use]
464    pub(in crate::db::executor) const fn authority(&self) -> EntityAuthority {
465        self.authority
466    }
467
468    #[must_use]
469    pub(in crate::db::executor) fn execution_preparation(&self) -> ExecutionPreparation {
470        ExecutionPreparation::from_plan(
471            self.authority.model(),
472            self.core.plan(),
473            slot_map_for_model_plan(self.authority.model(), self.core.plan()),
474        )
475    }
476
477    pub(in crate::db::executor) fn into_streaming_parts(
478        self,
479    ) -> Result<
480        (
481            EntityAuthority,
482            AccessPlannedQuery,
483            Vec<LoweredIndexPrefixSpec>,
484            Vec<LoweredIndexRangeSpec>,
485        ),
486        InternalError,
487    > {
488        let Self { authority, core } = self;
489        if core.index_prefix_spec_invalid {
490            return Err(
491                ExecutorPlanError::lowered_index_prefix_spec_invalid().into_internal_error()
492            );
493        }
494        if core.index_range_spec_invalid {
495            return Err(ExecutorPlanError::lowered_index_range_spec_invalid().into_internal_error());
496        }
497
498        Ok((
499            authority,
500            core.plan,
501            core.index_prefix_specs,
502            core.index_range_specs,
503        ))
504    }
505
506    /// Re-shape one prepared aggregate plan into one grouped prepared load plan
507    /// without reconstructing a typed `ExecutablePlan<E>` shell.
508    #[must_use]
509    pub(in crate::db::executor) fn into_grouped_load_plan(
510        self,
511        group: GroupSpec,
512    ) -> PreparedLoadPlan {
513        PreparedLoadPlan::from_plan(self.authority, self.core.into_inner().into_grouped(group))
514    }
515}
516
517impl<E: EntityKind> ExecutablePlan<E> {
518    pub(in crate::db) fn new(plan: AccessPlannedQuery) -> Self {
519        Self::build(plan)
520    }
521
522    fn build(mut plan: AccessPlannedQuery) -> Self {
523        let authority = EntityAuthority::for_type::<E>();
524        plan.finalize_planner_route_profile_for_model(E::MODEL);
525
526        Self {
527            core: build_executable_plan_core(authority, plan),
528            marker: PhantomData,
529        }
530    }
531
532    /// Explain scalar load execution shape as one canonical execution-node descriptor tree.
533    pub(in crate::db) fn explain_load_execution_node_descriptor(
534        &self,
535    ) -> Result<ExplainExecutionNodeDescriptor, InternalError>
536    where
537        E: EntityValue,
538    {
539        if !self.mode().is_load() {
540            return Err(
541                ExecutorPlanError::load_execution_descriptor_requires_load_plan()
542                    .into_internal_error(),
543            );
544        }
545
546        assemble_load_execution_node_descriptor_with_model(E::MODEL, self.core.plan())
547    }
548
549    /// Validate and decode a continuation cursor into executor-ready cursor state.
550    pub(in crate::db) fn prepare_cursor(
551        &self,
552        cursor: Option<&[u8]>,
553    ) -> Result<PlannedCursor, ExecutorPlanError> {
554        self.core
555            .prepare_cursor(EntityAuthority::for_type::<E>(), cursor)
556    }
557
558    /// Return the plan mode (load vs delete).
559    #[must_use]
560    pub(in crate::db) const fn mode(&self) -> QueryMode {
561        self.core.mode()
562    }
563
564    /// Return whether this executable plan carries grouped logical shape.
565    #[must_use]
566    pub(in crate::db) const fn is_grouped(&self) -> bool {
567        self.core.is_grouped()
568    }
569
570    /// Return planner-projected execution strategy for entrypoint dispatch.
571    pub(in crate::db) fn execution_strategy(&self) -> Result<ExecutionStrategy, InternalError> {
572        self.core.execution_strategy()
573    }
574
575    /// Borrow the structural logical plan for executor-owned tests.
576    #[must_use]
577    #[cfg(test)]
578    pub(in crate::db) const fn logical_plan(&self) -> &AccessPlannedQuery {
579        self.core.plan()
580    }
581
582    /// Expose planner-projected execution ordering for executor/lowering tests.
583    #[cfg(test)]
584    pub(in crate::db) fn execution_ordering(&self) -> Result<ExecutionOrdering, InternalError> {
585        self.core.execution_ordering()
586    }
587
588    pub(in crate::db) const fn access(
589        &self,
590    ) -> &crate::db::access::AccessPlan<crate::value::Value> {
591        &self.core.plan().access
592    }
593
594    /// Borrow scalar row-consistency policy for runtime row reads.
595    #[must_use]
596    pub(in crate::db) const fn consistency(&self) -> MissingRowPolicy {
597        self.core.consistency()
598    }
599
600    /// Classify canonical `bytes_by(field)` execution mode for this plan/field.
601    #[must_use]
602    pub(in crate::db) fn bytes_by_projection_mode(
603        &self,
604        target_field: &str,
605    ) -> BytesByProjectionMode {
606        let authority = EntityAuthority::for_type::<E>();
607
608        classify_bytes_by_projection_mode(
609            self.access(),
610            self.order_spec(),
611            self.consistency(),
612            self.has_predicate(),
613            target_field,
614            authority.model().primary_key.name,
615        )
616    }
617
618    /// Return a stable explain/diagnostic label for one bytes-by mode.
619    #[must_use]
620    pub(in crate::db) const fn bytes_by_projection_mode_label(
621        mode: BytesByProjectionMode,
622    ) -> &'static str {
623        match mode {
624            BytesByProjectionMode::Materialized => "field_materialized",
625            BytesByProjectionMode::CoveringIndex => "field_covering_index",
626            BytesByProjectionMode::CoveringConstant => "field_covering_constant",
627        }
628    }
629
630    /// Borrow scalar ORDER BY contract for this executable plan, if any.
631    #[must_use]
632    pub(in crate::db::executor) const fn order_spec(&self) -> Option<&OrderSpec> {
633        self.core.order_spec()
634    }
635
636    /// Return whether this executable plan has a residual predicate.
637    #[must_use]
638    pub(in crate::db::executor) fn has_predicate(&self) -> bool {
639        self.core.has_predicate()
640    }
641
642    pub(in crate::db) fn index_prefix_specs(
643        &self,
644    ) -> Result<&[LoweredIndexPrefixSpec], InternalError> {
645        self.core.index_prefix_specs()
646    }
647
648    pub(in crate::db) fn index_range_specs(
649        &self,
650    ) -> Result<&[LoweredIndexRangeSpec], InternalError> {
651        self.core.index_range_specs()
652    }
653
654    /// Render one canonical executor snapshot for test-only planner/executor
655    /// contract checks.
656    #[cfg(test)]
657    pub(in crate::db) fn render_snapshot_canonical(&self) -> Result<String, InternalError>
658    where
659        E: EntityValue,
660    {
661        // Phase 1: project all executor-owned summary fields from the logical plan.
662        let plan = self.core.plan();
663        let projection_spec = plan.projection_spec(E::MODEL);
664        let projection_selection =
665            if plan.grouped_plan().is_some() || projection_spec.len() != E::MODEL.fields.len() {
666                "Declared"
667            } else {
668                "All"
669            };
670        let projection_coverage_flag = plan.grouped_plan().is_some();
671        let continuation_signature = self.core.continuation_signature_for_runtime()?;
672        let ordering_direction = self
673            .core
674            .continuation_contract()?
675            .order_contract()
676            .direction();
677        let load_terminal_fast_path =
678            derive_load_terminal_fast_path_contract_for_model_plan(E::MODEL, plan);
679
680        // Phase 2: lower index-bound summaries into stable compact text.
681        let index_prefix_specs = render_index_prefix_specs(self.core.index_prefix_specs()?);
682        let index_range_specs = render_index_range_specs(self.core.index_range_specs()?);
683        let explain_plan = plan.explain_with_model(E::MODEL);
684
685        // Phase 3: join the canonical snapshot payload in one stable line order.
686        Ok([
687            "snapshot_version=1".to_string(),
688            format!("plan_hash={}", plan.fingerprint()),
689            format!("mode={:?}", self.core.mode()),
690            format!("is_grouped={}", self.core.is_grouped()),
691            format!("execution_strategy={:?}", self.core.execution_strategy()?),
692            format!(
693                "load_terminal_fast_path={}",
694                render_load_terminal_fast_path_label(load_terminal_fast_path.as_ref())
695            ),
696            format!("ordering_direction={ordering_direction:?}"),
697            format!(
698                "distinct_execution_strategy={:?}",
699                plan.distinct_execution_strategy()
700            ),
701            format!("projection_selection={projection_selection}"),
702            format!("projection_spec={projection_spec:?}"),
703            format!("order_spec={:?}", plan.scalar_plan().order),
704            format!("page_spec={:?}", plan.scalar_plan().page),
705            format!("projection_coverage_flag={projection_coverage_flag}"),
706            format!("continuation_signature={continuation_signature}"),
707            format!("index_prefix_specs={index_prefix_specs}"),
708            format!("index_range_specs={index_range_specs}"),
709            format!("explain_plan={explain_plan:?}"),
710        ]
711        .join("\n"))
712    }
713
714    /// Split the executable plan into its canonical structural logical plan.
715    ///
716    /// Aggregate/scalar prepared boundaries should prefer this helper when they
717    /// no longer need the typed `ExecutablePlan<E>` shell after entering
718    /// structural execution preparation.
719    pub(in crate::db) fn into_plan(self) -> AccessPlannedQuery {
720        self.core.into_inner()
721    }
722
723    /// Validate and decode grouped continuation cursor state for grouped plans.
724    #[cfg(test)]
725    pub(in crate::db) fn prepare_grouped_cursor(
726        &self,
727        cursor: Option<&[u8]>,
728    ) -> Result<GroupedPlannedCursor, ExecutorPlanError> {
729        let Some(contract) = self.core.continuation.as_ref() else {
730            return Err(ExecutorPlanError::grouped_cursor_preparation_requires_grouped_plan());
731        };
732
733        contract
734            .prepare_grouped_cursor(EntityAuthority::for_type::<E>().entity_path(), cursor)
735            .map_err(ExecutorPlanError::from)
736    }
737
738    /// Validate one already-decoded grouped continuation token for grouped plans.
739    pub(in crate::db) fn prepare_grouped_cursor_token(
740        &self,
741        cursor: Option<crate::db::cursor::GroupedContinuationToken>,
742    ) -> Result<GroupedPlannedCursor, ExecutorPlanError> {
743        let Some(contract) = self.core.continuation.as_ref() else {
744            return Err(ExecutorPlanError::grouped_cursor_preparation_requires_grouped_plan());
745        };
746
747        contract
748            .prepare_grouped_cursor_token(EntityAuthority::for_type::<E>().entity_path(), cursor)
749            .map_err(ExecutorPlanError::from)
750    }
751
752    /// Consume one typed load executable plan into one generic-free boundary
753    /// payload for continuation and load-pipeline preparation.
754    #[must_use]
755    pub(in crate::db::executor) fn into_prepared_load_plan(self) -> PreparedLoadPlan {
756        PreparedLoadPlan {
757            authority: EntityAuthority::for_type::<E>(),
758            core: self.core,
759        }
760    }
761
762    /// Consume one typed aggregate executable plan into one generic-free
763    /// boundary payload for aggregate terminal and runtime preparation.
764    #[must_use]
765    pub(in crate::db::executor) fn into_prepared_aggregate_plan(self) -> PreparedAggregatePlan {
766        PreparedAggregatePlan {
767            authority: EntityAuthority::for_type::<E>(),
768            core: self.core,
769        }
770    }
771}
772
773#[cfg(test)]
774const fn render_load_terminal_fast_path_label(
775    contract: Option<&LoadTerminalFastPathContract>,
776) -> &'static str {
777    match contract {
778        Some(LoadTerminalFastPathContract::CoveringRead(_)) => "CoveringRead",
779        None => "Materialized",
780    }
781}
782
783#[cfg(test)]
784fn render_index_prefix_specs(specs: &[LoweredIndexPrefixSpec]) -> String {
785    let rendered = specs
786        .iter()
787        .map(|spec| {
788            format!(
789                "{{index:{},bound_type:equality,lower:{},upper:{}}}",
790                spec.index().name(),
791                render_lowered_bound(spec.lower()),
792                render_lowered_bound(spec.upper()),
793            )
794        })
795        .collect::<Vec<_>>();
796
797    format!("[{}]", rendered.join(","))
798}
799
800#[cfg(test)]
801fn render_index_range_specs(specs: &[LoweredIndexRangeSpec]) -> String {
802    let rendered = specs
803        .iter()
804        .map(|spec| {
805            format!(
806                "{{index:{},lower:{},upper:{}}}",
807                spec.index().name(),
808                render_lowered_bound(spec.lower()),
809                render_lowered_bound(spec.upper()),
810            )
811        })
812        .collect::<Vec<_>>();
813
814    format!("[{}]", rendered.join(","))
815}
816
817#[cfg(test)]
818fn render_lowered_bound(bound: &Bound<crate::db::access::LoweredKey>) -> String {
819    match bound {
820        Bound::Included(key) => format!("included({})", render_lowered_key_summary(key)),
821        Bound::Excluded(key) => format!("excluded({})", render_lowered_key_summary(key)),
822        Bound::Unbounded => "unbounded".to_string(),
823    }
824}
825
826#[cfg(test)]
827fn render_lowered_key_summary(key: &crate::db::access::LoweredKey) -> String {
828    let bytes = key.as_bytes();
829    let head_len = bytes.len().min(8);
830    let tail_len = bytes.len().min(8);
831    let head = crate::db::codec::cursor::encode_cursor(&bytes[..head_len]);
832    let tail = crate::db::codec::cursor::encode_cursor(&bytes[bytes.len() - tail_len..]);
833
834    format!("len:{}:head:{head}:tail:{tail}", bytes.len())
835}