Skip to main content

laminar_core/
error_codes.rs

1//! LaminarDB structured error code registry.
2//!
3//! Every error in LaminarDB carries a stable `LDB-NNNN` code that is:
4//! - Present in the error message (grep-able in logs)
5//! - Present in the source code (grep-able in code)
6//! - Stable across versions (codes are never reused)
7//!
8//! # Code Ranges
9//!
10//! | Range | Category |
11//! |-------|----------|
12//! | `LDB-0xxx` | General / configuration |
13//! | `LDB-1xxx` | SQL parsing & validation |
14//! | `LDB-2xxx` | Window / watermark operations |
15//! | `LDB-3xxx` | Join operations |
16//! | `LDB-4xxx` | Serialization / state |
17//! | `LDB-5xxx` | Connector / I/O |
18//! | `LDB-6xxx` | Checkpoint / recovery |
19//! | `LDB-7xxx` | DataFusion / Arrow interop |
20//! | `LDB-8xxx` | Internal / should-not-happen |
21//!
22//! Codes within the `LDB-1xxx` through `LDB-3xxx` ranges are defined in
23//! `laminar-sql::error::codes` (the SQL layer) and are re-exported here
24//! for reference. This module is the canonical registry for all other ranges.
25
26// ── General / Configuration (LDB-0xxx) ──
27
28/// Invalid configuration value.
29pub const INVALID_CONFIG: &str = "LDB-0001";
30/// Missing required configuration key.
31pub const MISSING_CONFIG: &str = "LDB-0002";
32/// Unresolved config variable (e.g. `${VAR}` placeholder).
33pub const UNRESOLVED_CONFIG_VAR: &str = "LDB-0003";
34/// Database is shut down.
35pub const SHUTDOWN: &str = "LDB-0004";
36/// Invalid operation for the current state.
37pub const INVALID_OPERATION: &str = "LDB-0005";
38/// Schema mismatch between Rust type and SQL definition.
39pub const SCHEMA_MISMATCH: &str = "LDB-0006";
40
41// ── SQL Parsing & Validation (LDB-1xxx) ──
42// Canonical definitions are in `laminar_sql::error::codes`.
43// Repeated here for reference only.
44
45/// Unsupported SQL syntax (canonical: `laminar_sql::error::codes::UNSUPPORTED_SQL`).
46pub const SQL_UNSUPPORTED: &str = "LDB-1001";
47/// Query planning failed.
48pub const SQL_PLANNING_FAILED: &str = "LDB-1002";
49/// Column not found.
50pub const SQL_COLUMN_NOT_FOUND: &str = "LDB-1100";
51/// Table or source not found.
52pub const SQL_TABLE_NOT_FOUND: &str = "LDB-1101";
53/// Type mismatch.
54pub const SQL_TYPE_MISMATCH: &str = "LDB-1200";
55
56// ── Window / Watermark (LDB-2xxx) ──
57
58/// Watermark required for this operation.
59pub const WATERMARK_REQUIRED: &str = "LDB-2001";
60/// Invalid window specification.
61pub const WINDOW_INVALID: &str = "LDB-2002";
62/// Window size must be positive.
63pub const WINDOW_SIZE_INVALID: &str = "LDB-2003";
64/// Late data rejected by window policy.
65pub const LATE_DATA_REJECTED: &str = "LDB-2004";
66
67// ── Join (LDB-3xxx) ──
68
69/// Join key column not found or invalid.
70pub const JOIN_KEY_MISSING: &str = "LDB-3001";
71/// Time bound required for stream-stream join.
72pub const JOIN_TIME_BOUND_MISSING: &str = "LDB-3002";
73/// Temporal join requires a primary key on the right-side table.
74pub const TEMPORAL_JOIN_NO_PK: &str = "LDB-3003";
75/// Unsupported join type for streaming queries.
76pub const JOIN_TYPE_UNSUPPORTED: &str = "LDB-3004";
77
78// ── Serialization / State (LDB-4xxx) ──
79
80/// State serialization failed for an operator.
81pub const SERIALIZATION_FAILED: &str = "LDB-4001";
82/// State deserialization failed for an operator.
83pub const DESERIALIZATION_FAILED: &str = "LDB-4002";
84/// JSON parse error (connector config, CDC payload, etc.).
85pub const JSON_PARSE_ERROR: &str = "LDB-4003";
86/// Base64 decode error (inline checkpoint state).
87pub const BASE64_DECODE_ERROR: &str = "LDB-4004";
88/// State store key not found.
89pub const STATE_KEY_MISSING: &str = "LDB-4005";
90/// State corruption detected (checksum mismatch, invalid data).
91pub const STATE_CORRUPTION: &str = "LDB-4006";
92
93// ── Connector / I/O (LDB-5xxx) ──
94
95/// Connector failed to establish a connection.
96pub const CONNECTOR_CONNECTION_FAILED: &str = "LDB-5001";
97/// Connector authentication failed.
98pub const CONNECTOR_AUTH_FAILED: &str = "LDB-5002";
99/// Connector read error.
100pub const CONNECTOR_READ_ERROR: &str = "LDB-5003";
101/// Connector write error.
102pub const CONNECTOR_WRITE_ERROR: &str = "LDB-5004";
103/// Connector configuration error.
104pub const CONNECTOR_CONFIG_ERROR: &str = "LDB-5005";
105/// Source not found.
106pub const SOURCE_NOT_FOUND: &str = "LDB-5010";
107/// Sink not found.
108pub const SINK_NOT_FOUND: &str = "LDB-5011";
109/// Source already exists.
110pub const SOURCE_ALREADY_EXISTS: &str = "LDB-5012";
111/// Sink already exists.
112pub const SINK_ALREADY_EXISTS: &str = "LDB-5013";
113/// Connector serde (serialization/deserialization) error.
114pub const CONNECTOR_SERDE_ERROR: &str = "LDB-5020";
115/// Schema inference or compatibility error.
116pub const CONNECTOR_SCHEMA_ERROR: &str = "LDB-5021";
117/// Exactly-once requires all sources to support replay.
118pub const EXACTLY_ONCE_NON_REPLAYABLE: &str = "LDB-5030";
119/// Exactly-once requires all sinks to support exactly-once semantics.
120pub const EXACTLY_ONCE_SINK_UNSUPPORTED: &str = "LDB-5031";
121/// Exactly-once requires checkpointing to be enabled.
122pub const EXACTLY_ONCE_NO_CHECKPOINT: &str = "LDB-5032";
123/// Mixed delivery capabilities — some sources are non-replayable.
124pub const MIXED_DELIVERY_CAPABILITIES: &str = "LDB-5033";
125
126// ── Checkpoint / Recovery (LDB-6xxx) ──
127
128/// Checkpoint creation failed.
129pub const CHECKPOINT_FAILED: &str = "LDB-6001";
130/// Checkpoint not found.
131pub const CHECKPOINT_NOT_FOUND: &str = "LDB-6002";
132/// Checkpoint recovery failed.
133pub const RECOVERY_FAILED: &str = "LDB-6003";
134/// Sink rollback failed during checkpoint abort.
135pub const SINK_ROLLBACK_FAILED: &str = "LDB-6004";
136/// WAL (write-ahead log) error.
137pub const WAL_ERROR: &str = "LDB-6005";
138/// WAL entry has invalid length (possible corruption).
139pub const WAL_INVALID_LENGTH: &str = "LDB-6006";
140/// WAL checksum mismatch.
141pub const WAL_CHECKSUM_MISMATCH: &str = "LDB-6007";
142/// Checkpoint manifest persistence failed.
143pub const MANIFEST_PERSIST_FAILED: &str = "LDB-6008";
144/// Checkpoint prune (old checkpoint cleanup) failed.
145pub const CHECKPOINT_PRUNE_FAILED: &str = "LDB-6009";
146/// Sidecar state data missing or corrupted.
147pub const SIDECAR_CORRUPTION: &str = "LDB-6010";
148/// Source offset metadata missing during recovery.
149pub const OFFSET_METADATA_MISSING: &str = "LDB-6011";
150
151// ── DataFusion / Arrow Interop (LDB-7xxx) ──
152
153/// Query execution failed (`DataFusion` engine error).
154/// Note: the SQL layer uses `LDB-9001` for execution failures visible to users;
155/// `LDB-7001` is for internal `DataFusion` interop issues.
156pub const QUERY_EXECUTION_FAILED: &str = "LDB-7001";
157/// Arrow schema or record batch error.
158pub const ARROW_ERROR: &str = "LDB-7002";
159/// `DataFusion` plan optimization failed.
160pub const PLAN_OPTIMIZATION_FAILED: &str = "LDB-7003";
161
162// ── Internal / Should-Not-Happen (LDB-8xxx) ──
163
164/// Internal error — this is a bug.
165pub const INTERNAL: &str = "LDB-8001";
166/// Pipeline error (start/shutdown lifecycle).
167pub const PIPELINE_ERROR: &str = "LDB-8002";
168/// Materialized view error.
169pub const MATERIALIZED_VIEW_ERROR: &str = "LDB-8003";
170/// Query pipeline error (stream execution context).
171pub const QUERY_PIPELINE_ERROR: &str = "LDB-8004";
172
173// ── Ring 0 Hot Path Errors ──
174
175/// Ring 0 error — no heap allocation, no formatting on construction.
176///
177/// Only formatted when actually displayed (which happens outside Ring 0).
178/// Uses `Copy` and `repr(u16)` for zero-cost error reporting via counters.
179#[derive(Debug, Clone, Copy, PartialEq, Eq)]
180#[repr(u16)]
181pub enum HotPathError {
182    /// Event arrived after watermark — dropped.
183    LateEvent = 0x0001,
184    /// State store key not found.
185    StateKeyMissing = 0x0002,
186    /// Downstream backpressure — event buffered.
187    Backpressure = 0x0003,
188    /// Serialization buffer overflow.
189    SerializationOverflow = 0x0004,
190    /// Record batch schema does not match expected.
191    SchemaMismatch = 0x0005,
192    /// Aggregate state corruption detected.
193    AggregateStateCorruption = 0x0006,
194    /// Queue is full — cannot push event.
195    QueueFull = 0x0007,
196    /// Channel is closed/disconnected.
197    ChannelClosed = 0x0008,
198}
199
200impl HotPathError {
201    /// Returns a static error message. Cost: one match. No allocation.
202    #[must_use]
203    pub const fn message(self) -> &'static str {
204        match self {
205            Self::LateEvent => "Event arrived after watermark; dropped",
206            Self::StateKeyMissing => "State key not found in store",
207            Self::Backpressure => "Downstream backpressure; event buffered",
208            Self::SerializationOverflow => "Serialization buffer capacity exceeded",
209            Self::SchemaMismatch => "Record batch schema does not match expected",
210            Self::AggregateStateCorruption => "Aggregate state checksum mismatch detected",
211            Self::QueueFull => "Queue is full; cannot push event",
212            Self::ChannelClosed => "Channel is closed or disconnected",
213        }
214    }
215
216    /// Numeric code for metrics counters. Zero-cost.
217    #[must_use]
218    pub const fn code(self) -> u16 {
219        self as u16
220    }
221
222    /// Returns the `LDB-NNNN` error code string for this hot path error.
223    #[must_use]
224    pub const fn ldb_code(self) -> &'static str {
225        match self {
226            Self::LateEvent => LATE_DATA_REJECTED,
227            Self::StateKeyMissing => STATE_KEY_MISSING,
228            Self::Backpressure => "LDB-8010",
229            Self::SerializationOverflow => SERIALIZATION_FAILED,
230            Self::SchemaMismatch => SCHEMA_MISMATCH,
231            Self::AggregateStateCorruption => STATE_CORRUPTION,
232            Self::QueueFull => "LDB-8011",
233            Self::ChannelClosed => "LDB-8012",
234        }
235    }
236}
237
238impl std::fmt::Display for HotPathError {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        write!(f, "[{}] {}", self.ldb_code(), self.message())
241    }
242}
243
244impl std::error::Error for HotPathError {}
245
246#[cfg(test)]
247mod tests {
248    use super::*;
249
250    #[test]
251    fn hot_path_error_is_copy_and_small() {
252        let e = HotPathError::LateEvent;
253        let e2 = e; // Copy
254        assert_eq!(e, e2);
255        assert_eq!(std::mem::size_of::<HotPathError>(), 2);
256    }
257
258    #[test]
259    fn hot_path_error_codes_are_nonzero() {
260        let variants = [
261            HotPathError::LateEvent,
262            HotPathError::StateKeyMissing,
263            HotPathError::Backpressure,
264            HotPathError::SerializationOverflow,
265            HotPathError::SchemaMismatch,
266            HotPathError::AggregateStateCorruption,
267            HotPathError::QueueFull,
268            HotPathError::ChannelClosed,
269        ];
270        for v in &variants {
271            assert!(v.code() > 0, "{v:?} has zero code");
272            assert!(!v.message().is_empty(), "{v:?} has empty message");
273            assert!(
274                v.ldb_code().starts_with("LDB-"),
275                "{v:?} has bad ldb_code: {}",
276                v.ldb_code()
277            );
278        }
279    }
280
281    #[test]
282    fn hot_path_error_display() {
283        let e = HotPathError::LateEvent;
284        let s = e.to_string();
285        assert!(s.starts_with("[LDB-"));
286        assert!(s.contains("watermark"));
287    }
288
289    #[test]
290    fn error_codes_are_stable_strings() {
291        assert_eq!(INVALID_CONFIG, "LDB-0001");
292        assert_eq!(SERIALIZATION_FAILED, "LDB-4001");
293        assert_eq!(CHECKPOINT_FAILED, "LDB-6001");
294        assert_eq!(INTERNAL, "LDB-8001");
295    }
296}