use crate::db::query::plan::OrderDirection;
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExecutionOptimization {
PrimaryKey,
PrimaryKeyTopNSeek,
SecondaryOrderPushdown,
SecondaryOrderTopNSeek,
IndexRangeLimitPushdown,
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct ExecutionStats {
rows_scanned_pre_filter: u64,
rows_after_predicate: u64,
rows_after_projection: u64,
rows_after_distinct: u64,
rows_sorted: u64,
keys_streamed: u64,
key_stream_micros: u64,
ordering_micros: u64,
projection_micros: u64,
aggregation_micros: u64,
}
impl ExecutionStats {
#[must_use]
#[expect(
clippy::too_many_arguments,
reason = "diagnostics stats DTO exposes the exact flat trace counter surface"
)]
pub(in crate::db) const fn new(
rows_scanned_pre_filter: u64,
rows_after_predicate: u64,
rows_after_projection: u64,
rows_after_distinct: u64,
rows_sorted: u64,
keys_streamed: u64,
key_stream_micros: u64,
ordering_micros: u64,
projection_micros: u64,
aggregation_micros: u64,
) -> Self {
Self {
rows_scanned_pre_filter,
rows_after_predicate,
rows_after_projection,
rows_after_distinct,
rows_sorted,
keys_streamed,
key_stream_micros,
ordering_micros,
projection_micros,
aggregation_micros,
}
}
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "execution profiling accessors are consumed by crate tests and diagnostics tooling"
)
)]
pub(in crate::db) const fn rows_scanned_pre_filter(&self) -> u64 {
self.rows_scanned_pre_filter
}
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "execution profiling accessors are consumed by crate tests and diagnostics tooling"
)
)]
pub(in crate::db) const fn rows_after_predicate(&self) -> u64 {
self.rows_after_predicate
}
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "execution profiling accessors are consumed by crate tests and diagnostics tooling"
)
)]
pub(in crate::db) const fn rows_after_projection(&self) -> u64 {
self.rows_after_projection
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn rows_after_distinct(&self) -> u64 {
self.rows_after_distinct
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn rows_sorted(&self) -> u64 {
self.rows_sorted
}
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "execution profiling accessors are consumed by crate tests and diagnostics tooling"
)
)]
pub(in crate::db) const fn keys_streamed(&self) -> u64 {
self.keys_streamed
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn key_stream_micros(&self) -> u64 {
self.key_stream_micros
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn ordering_micros(&self) -> u64 {
self.ordering_micros
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn projection_micros(&self) -> u64 {
self.projection_micros
}
#[must_use]
#[expect(
dead_code,
reason = "execution profiling records this for diagnostics consumers before response exposure"
)]
pub(in crate::db) const fn aggregation_micros(&self) -> u64 {
self.aggregation_micros
}
}
#[cfg_attr(
doc,
doc = "ExecutionAccessPathVariant\n\nCoarse access path shape recorded in execution traces."
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum ExecutionAccessPathVariant {
ByKey,
ByKeys,
KeyRange,
IndexPrefix,
IndexMultiLookup,
IndexRange,
FullScan,
Union,
Intersection,
}
#[cfg_attr(
doc,
doc = "ExecutionTrace\n\nStructured execution trace snapshot for one load path.\nCaptures plan shape and counters without affecting behavior."
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExecutionTrace {
pub(crate) access_path_variant: ExecutionAccessPathVariant,
pub(crate) direction: OrderDirection,
pub(crate) optimization: Option<ExecutionOptimization>,
pub(crate) keys_scanned: u64,
pub(crate) rows_materialized: u64,
pub(crate) rows_returned: u64,
pub(crate) execution_time_micros: u64,
pub(crate) index_only: bool,
pub(crate) continuation_applied: bool,
pub(crate) index_predicate_applied: bool,
pub(crate) index_predicate_keys_rejected: u64,
pub(crate) distinct_keys_deduped: u64,
pub(crate) execution_stats: Option<ExecutionStats>,
}
#[cfg_attr(
doc,
doc = "ExecutionMetrics\n\nCompact metrics view derived from one `ExecutionTrace`.\nKept small for lightweight observability surfaces."
)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ExecutionMetrics {
pub(crate) rows_scanned: u64,
pub(crate) rows_materialized: u64,
pub(crate) execution_time_micros: u64,
pub(crate) index_only: bool,
}
impl ExecutionTrace {
#[must_use]
pub(crate) const fn new_from_variant(
access_path_variant: ExecutionAccessPathVariant,
direction: OrderDirection,
continuation_applied: bool,
) -> Self {
Self {
access_path_variant,
direction,
optimization: None,
keys_scanned: 0,
rows_materialized: 0,
rows_returned: 0,
execution_time_micros: 0,
index_only: false,
continuation_applied,
index_predicate_applied: false,
index_predicate_keys_rejected: 0,
distinct_keys_deduped: 0,
execution_stats: None,
}
}
#[expect(clippy::too_many_arguments)]
pub(in crate::db) fn set_path_outcome(
&mut self,
optimization: Option<ExecutionOptimization>,
keys_scanned: usize,
rows_materialized: usize,
rows_returned: usize,
execution_time_micros: u64,
index_only: bool,
index_predicate_applied: bool,
index_predicate_keys_rejected: u64,
distinct_keys_deduped: u64,
) {
self.optimization = optimization;
self.keys_scanned = u64::try_from(keys_scanned).unwrap_or(u64::MAX);
self.rows_materialized = u64::try_from(rows_materialized).unwrap_or(u64::MAX);
self.rows_returned = u64::try_from(rows_returned).unwrap_or(u64::MAX);
self.execution_time_micros = execution_time_micros;
self.index_only = index_only;
self.index_predicate_applied = index_predicate_applied;
self.index_predicate_keys_rejected = index_predicate_keys_rejected;
self.distinct_keys_deduped = distinct_keys_deduped;
debug_assert_eq!(
self.keys_scanned,
u64::try_from(keys_scanned).unwrap_or(u64::MAX),
"execution trace keys_scanned must match rows_scanned metrics input",
);
}
pub(in crate::db) const fn set_execution_stats(&mut self, stats: Option<ExecutionStats>) {
self.execution_stats = stats;
}
#[must_use]
pub const fn metrics(&self) -> ExecutionMetrics {
ExecutionMetrics {
rows_scanned: self.keys_scanned,
rows_materialized: self.rows_materialized,
execution_time_micros: self.execution_time_micros,
index_only: self.index_only,
}
}
#[must_use]
pub const fn access_path_variant(&self) -> ExecutionAccessPathVariant {
self.access_path_variant
}
#[must_use]
pub const fn direction(&self) -> OrderDirection {
self.direction
}
#[must_use]
pub const fn optimization(&self) -> Option<ExecutionOptimization> {
self.optimization
}
#[must_use]
pub const fn keys_scanned(&self) -> u64 {
self.keys_scanned
}
#[must_use]
pub const fn rows_materialized(&self) -> u64 {
self.rows_materialized
}
#[must_use]
pub const fn rows_returned(&self) -> u64 {
self.rows_returned
}
#[must_use]
pub const fn execution_time_micros(&self) -> u64 {
self.execution_time_micros
}
#[must_use]
pub const fn index_only(&self) -> bool {
self.index_only
}
#[must_use]
pub const fn continuation_applied(&self) -> bool {
self.continuation_applied
}
#[must_use]
pub const fn index_predicate_applied(&self) -> bool {
self.index_predicate_applied
}
#[must_use]
pub const fn index_predicate_keys_rejected(&self) -> u64 {
self.index_predicate_keys_rejected
}
#[must_use]
pub const fn distinct_keys_deduped(&self) -> u64 {
self.distinct_keys_deduped
}
#[must_use]
#[cfg_attr(
not(test),
expect(
dead_code,
reason = "execution stats are an internal diagnostics/testing surface"
)
)]
pub(in crate::db) const fn execution_stats(&self) -> Option<ExecutionStats> {
self.execution_stats
}
}
impl ExecutionMetrics {
#[must_use]
pub const fn rows_scanned(&self) -> u64 {
self.rows_scanned
}
#[must_use]
pub const fn rows_materialized(&self) -> u64 {
self.rows_materialized
}
#[must_use]
pub const fn execution_time_micros(&self) -> u64 {
self.execution_time_micros
}
#[must_use]
pub const fn index_only(&self) -> bool {
self.index_only
}
}