Skip to main content

icydb_core/db/executor/trace/
mod.rs

1//! Module: executor::trace
2//! Responsibility: execution trace contracts and mutation boundaries.
3//! Does not own: execution routing policy or stream/materialization behavior.
4//! Boundary: shared trace surface used by load/response APIs.
5
6mod projection;
7
8use crate::db::{access::AccessPlan, direction::Direction, query::plan::OrderDirection};
9use projection::{access_path_variant, execution_order_direction};
10
11///
12/// ExecutionAccessPathVariant
13///
14/// Coarse access path shape used by the load execution trace surface.
15///
16
17#[derive(Clone, Copy, Debug, Eq, PartialEq)]
18pub enum ExecutionAccessPathVariant {
19    ByKey,
20    ByKeys,
21    KeyRange,
22    IndexPrefix,
23    IndexRange,
24    FullScan,
25    Union,
26    Intersection,
27}
28
29///
30/// ExecutionOptimization
31///
32/// Canonical load optimization selected by execution, if any.
33///
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
36pub enum ExecutionOptimization {
37    PrimaryKey,
38    SecondaryOrderPushdown,
39    IndexRangeLimitPushdown,
40}
41
42///
43/// ExecutionTrace
44///
45/// Structured, opt-in load execution introspection snapshot.
46/// Captures plan-shape and execution decisions without changing semantics.
47///
48
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
50pub struct ExecutionTrace {
51    pub access_path_variant: ExecutionAccessPathVariant,
52    pub direction: OrderDirection,
53    pub optimization: Option<ExecutionOptimization>,
54    pub keys_scanned: u64,
55    pub rows_returned: u64,
56    pub continuation_applied: bool,
57    pub index_predicate_applied: bool,
58    pub index_predicate_keys_rejected: u64,
59    pub distinct_keys_deduped: u64,
60}
61
62impl ExecutionTrace {
63    /// Build one trace payload from canonical access shape and runtime direction.
64    #[must_use]
65    pub(in crate::db::executor) fn new<K>(
66        access: &AccessPlan<K>,
67        direction: Direction,
68        continuation_applied: bool,
69    ) -> Self {
70        Self {
71            access_path_variant: access_path_variant(access),
72            direction: execution_order_direction(direction),
73            optimization: None,
74            keys_scanned: 0,
75            rows_returned: 0,
76            continuation_applied,
77            index_predicate_applied: false,
78            index_predicate_keys_rejected: 0,
79            distinct_keys_deduped: 0,
80        }
81    }
82
83    /// Apply one finalized path outcome to this trace snapshot.
84    pub(in crate::db::executor) fn set_path_outcome(
85        &mut self,
86        optimization: Option<ExecutionOptimization>,
87        keys_scanned: usize,
88        rows_returned: usize,
89        index_predicate_applied: bool,
90        index_predicate_keys_rejected: u64,
91        distinct_keys_deduped: u64,
92    ) {
93        self.optimization = optimization;
94        self.keys_scanned = u64::try_from(keys_scanned).unwrap_or(u64::MAX);
95        self.rows_returned = u64::try_from(rows_returned).unwrap_or(u64::MAX);
96        self.index_predicate_applied = index_predicate_applied;
97        self.index_predicate_keys_rejected = index_predicate_keys_rejected;
98        self.distinct_keys_deduped = distinct_keys_deduped;
99        debug_assert_eq!(
100            self.keys_scanned,
101            u64::try_from(keys_scanned).unwrap_or(u64::MAX),
102            "execution trace keys_scanned must match rows_scanned metrics input",
103        );
104    }
105}