Skip to main content

icydb_core/db/query/
diagnostics.rs

1//! Public, read-only diagnostics for query planning and execution.
2//!
3//! Diagnostics contract:
4//! - `ExplainPlan` is deterministic for equivalent queries and plans.
5//! - `PlanFingerprint` is stable within a major version (inputs are normalized).
6//! - Execution trace events are best-effort diagnostics and may evolve.
7//! - Diagnostics never execute queries unless explicitly requested.
8//! - Diagnostics are observational only; they are not correctness proofs.
9
10pub use crate::db::executor::trace::{QueryTraceEvent, TraceAccess, TraceExecutorKind, TracePhase};
11use crate::db::query::plan::{AccessPath, AccessPlan, ExplainPlan, PlanFingerprint};
12
13///
14/// QueryDiagnostics
15///
16/// Read-only planning diagnostics derived from a `Query`.
17///
18
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct QueryDiagnostics {
21    pub explain: ExplainPlan,
22    pub fingerprint: PlanFingerprint,
23}
24
25impl From<ExplainPlan> for QueryDiagnostics {
26    fn from(explain: ExplainPlan) -> Self {
27        let fingerprint = explain.fingerprint();
28        Self {
29            explain,
30            fingerprint,
31        }
32    }
33}
34
35///
36/// QueryExecutionDiagnostics
37///
38/// Read-only execution diagnostics emitted for a single query execution.
39///
40
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct QueryExecutionDiagnostics {
43    pub fingerprint: PlanFingerprint,
44    pub events: Vec<QueryTraceEvent>,
45}
46
47/// Public alias for trace access kinds in query diagnostics.
48pub type QueryTraceAccess = TraceAccess;
49
50/// Public alias for trace executor kinds in query diagnostics.
51pub type QueryTraceExecutorKind = TraceExecutorKind;
52
53/// Public alias for trace phase kinds in query diagnostics.
54pub type QueryTracePhase = TracePhase;
55
56pub(crate) fn trace_access_from_plan<K>(plan: &AccessPlan<K>) -> TraceAccess {
57    match plan {
58        AccessPlan::Path(path) => trace_access_from_path(path),
59        AccessPlan::Union(children) => {
60            // NOTE: Diagnostics are best-effort; overflow saturates to preserve determinism.
61            TraceAccess::Union {
62                branches: u32::try_from(children.len()).unwrap_or(u32::MAX),
63            }
64        }
65        AccessPlan::Intersection(children) => {
66            // NOTE: Diagnostics are best-effort; overflow saturates to preserve determinism.
67            TraceAccess::Intersection {
68                branches: u32::try_from(children.len()).unwrap_or(u32::MAX),
69            }
70        }
71    }
72}
73
74fn trace_access_from_path<K>(path: &AccessPath<K>) -> TraceAccess {
75    match path {
76        AccessPath::ByKey(_) => TraceAccess::ByKey,
77        AccessPath::ByKeys(keys) => {
78            // NOTE: Diagnostics are best-effort; overflow saturates to preserve determinism.
79            TraceAccess::ByKeys {
80                count: u32::try_from(keys.len()).unwrap_or(u32::MAX),
81            }
82        }
83        AccessPath::KeyRange { .. } => TraceAccess::KeyRange,
84        AccessPath::IndexPrefix { index, values } => {
85            // NOTE: Diagnostics are best-effort; overflow saturates to preserve determinism.
86            TraceAccess::IndexPrefix {
87                name: index.name,
88                prefix_len: u32::try_from(values.len()).unwrap_or(u32::MAX),
89            }
90        }
91        AccessPath::FullScan => TraceAccess::FullScan,
92    }
93}
94
95#[must_use]
96pub const fn start_event(
97    fingerprint: PlanFingerprint,
98    access: Option<TraceAccess>,
99    executor: TraceExecutorKind,
100) -> QueryTraceEvent {
101    QueryTraceEvent::Start {
102        fingerprint,
103        executor,
104        access,
105    }
106}
107
108#[must_use]
109pub const fn finish_event(
110    fingerprint: PlanFingerprint,
111    access: Option<TraceAccess>,
112    executor: TraceExecutorKind,
113    rows: u64,
114) -> QueryTraceEvent {
115    QueryTraceEvent::Finish {
116        fingerprint,
117        executor,
118        access,
119        rows,
120    }
121}