llkv_result/error.rs
1use std::{fmt, io};
2use thiserror::Error;
3
4/// Unified error type for all LLKV operations.
5///
6/// This enum encompasses all failure modes across the LLKV stack, from low-level storage
7/// errors to high-level transaction conflicts. Each variant includes context-specific
8/// information to help diagnose and handle the error appropriately.
9///
10/// # Error Handling Strategy
11///
12/// Errors propagate upward through the call stack using Rust's `?` operator. At API boundaries
13/// (e.g., SQL interface), errors are typically converted to user-friendly messages. Internal
14/// code can match on specific variants for fine-grained error handling.
15///
16/// # Thread Safety
17///
18/// `Error` implements `Send` and `Sync`, allowing errors to be safely passed between threads.
19/// This is important for concurrent query execution and transaction processing.
20#[derive(Error, Debug)]
21pub enum Error {
22 /// I/O error during file or disk operations.
23 ///
24 /// This error wraps standard library I/O errors and typically occurs during:
25 /// - Opening or creating database files
26 /// - Reading or writing pages to disk
27 /// - Flushing data to persistent storage
28 ///
29 /// The underlying `io::Error` provides detailed information about the failure
30 /// (e.g., permission denied, disk full, file not found).
31 #[error("I/O error: {0}")]
32 Io(#[from] io::Error),
33
34 /// Arrow library error during columnar data operations.
35 ///
36 /// This error occurs when:
37 /// - Serializing or deserializing Arrow RecordBatches
38 /// - Converting between Arrow data types
39 /// - Building Arrow arrays with invalid data
40 /// - Schema mismatches during batch operations
41 ///
42 /// Arrow is the underlying columnar memory format used by LLKV, so these errors
43 /// typically indicate data format incompatibilities or memory allocation failures.
44 #[error("Arrow error: {0}")]
45 Arrow(#[from] arrow::error::ArrowError),
46
47 /// Invalid user input or API parameter.
48 ///
49 /// This error indicates a problem with arguments passed to LLKV APIs:
50 /// - Invalid SQL syntax (e.g., malformed queries)
51 /// - Type mismatches (e.g., comparing incompatible column types)
52 /// - Out-of-range values (e.g., negative row counts)
53 /// - Malformed identifiers (e.g., invalid table names)
54 /// - Schema violations (e.g., missing required columns)
55 ///
56 /// The message string provides specific details about what was invalid and why.
57 ///
58 /// # Recovery
59 ///
60 /// These errors are typically recoverable—fix the input and retry the operation.
61 #[error("Invalid argument: {0}")]
62 InvalidArgumentError(String),
63
64 /// Storage key or entity not found.
65 ///
66 /// This error occurs when attempting to access a non-existent:
67 /// - Table (by name or ID)
68 /// - Column (by name or field ID)
69 /// - Row (by row_id)
70 /// - Chunk or page (by physical key)
71 /// - Descriptor or metadata entry
72 ///
73 /// This is a common error when queries reference dropped tables or columns,
74 /// or when low-level storage operations fail to locate expected data.
75 ///
76 /// # Recovery
77 ///
78 /// For user-facing errors, present a "table/column not found" message.
79 /// For internal errors, this may indicate data corruption or a bug.
80 #[error("Storage key not found")]
81 NotFound,
82
83 /// Catalog metadata error.
84 ///
85 /// This error indicates a problem with system catalog operations:
86 /// - Corruption in catalog tables (`__llkv_catalog_*`)
87 /// - Inconsistency between catalog and actual stored data
88 /// - Failure to read or update table/column metadata
89 /// - Schema evolution issues
90 ///
91 /// Catalog errors are serious as they affect the database's understanding of
92 /// its own structure. They may require manual inspection or repair.
93 #[error("{0}")]
94 CatalogError(String),
95
96 /// Data constraint violation.
97 ///
98 /// This error occurs when an operation would violate data integrity rules:
99 /// - Primary key conflicts (duplicate key on insert)
100 /// - Type constraints (inserting wrong type into column)
101 /// - NOT NULL constraint violations
102 /// - Foreign key violations (if implemented)
103 /// - Check constraint failures (if implemented)
104 ///
105 /// The message describes which constraint was violated and what data caused the issue.
106 ///
107 /// # Recovery
108 ///
109 /// These errors are expected during normal operation (e.g., duplicate key inserts).
110 /// The application should handle them gracefully and inform the user.
111 #[error("Constraint Error: {0}")]
112 ConstraintError(String),
113
114 /// Transaction execution or isolation error.
115 ///
116 /// This error occurs during MVCC transaction processing:
117 /// - Transaction conflicts during commit (write-write conflicts)
118 /// - Isolation violations (attempting to see uncommitted data)
119 /// - Transaction abort requests
120 /// - Invalid transaction state transitions
121 /// - Exceeding transaction limits
122 ///
123 /// These errors are part of normal transaction processing and indicate that
124 /// a transaction must be retried or aborted.
125 #[error("{0}")]
126 TransactionContextError(String),
127
128 /// Internal error indicating a bug or unexpected state.
129 ///
130 /// This error should never occur during normal operation. It indicates:
131 /// - Violated internal invariants
132 /// - Unexpected state transitions
133 /// - Logic errors in LLKV code
134 /// - Data structure corruption
135 ///
136 /// If you encounter this error, it likely indicates a bug in LLKV that should
137 /// be reported with reproduction steps.
138 ///
139 /// # Debugging
140 ///
141 /// The message includes details about what assertion failed or what unexpected
142 /// state was encountered. Enable debug logging for more context.
143 #[error("An internal operation failed: {0}")]
144 Internal(String),
145
146 /// Expression type casting error.
147 ///
148 /// This error occurs when evaluating SQL expressions that require type conversions:
149 /// - Invalid casts (e.g., string to integer when string isn't numeric)
150 /// - Unsupported type conversions
151 /// - Overflow during numeric conversions
152 ///
153 /// This is typically a user error (invalid SQL expression) rather than a bug.
154 #[error("expression cast error: {0}")]
155 ExprCast(String),
156
157 /// Predicate or filter construction error.
158 ///
159 /// This error occurs when building scan predicates or filter expressions:
160 /// - Type mismatches in comparison operations
161 /// - Unsupported predicate types for columnar scans
162 /// - Invalid filter push-down operations
163 ///
164 /// These errors typically occur during query planning when attempting to
165 /// optimize filters into storage-layer scans.
166 #[error("predicate build error: {0}")]
167 PredicateBuild(String),
168
169 /// Attempted to use a reserved table ID for a user table.
170 ///
171 /// Currently, only table ID 0 is reserved for the system catalog.
172 /// This error prevents accidental corruption of system metadata.
173 ///
174 /// User tables receive IDs starting from 1 and incrementing.
175 #[error("table id {0} is reserved for system catalogs")]
176 ReservedTableId(u16),
177}
178
179impl Error {
180 /// Create an expression cast error from any displayable error.
181 ///
182 /// This is a convenience method for converting other error types into
183 /// [`Error::ExprCast`] while preserving the original error message.
184 #[inline]
185 pub fn expr_cast<E: fmt::Display>(err: E) -> Self {
186 Error::ExprCast(err.to_string())
187 }
188
189 /// Create a predicate build error from any displayable error.
190 ///
191 /// This is a convenience method for converting other error types into
192 /// [`Error::PredicateBuild`] while preserving the original error message.
193 #[inline]
194 pub fn predicate_build<E: fmt::Display>(err: E) -> Self {
195 Error::PredicateBuild(err.to_string())
196 }
197
198 /// Create a reserved table ID error.
199 ///
200 /// Currently only table ID 0 is reserved. This method creates an error
201 /// when user code attempts to use a reserved ID.
202 #[inline]
203 pub fn reserved_table_id(table_id: impl Into<u16>) -> Self {
204 Error::ReservedTableId(table_id.into())
205 }
206}