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    ///
185    /// # Examples
186    ///
187    /// ```
188    /// use llkv_result::Error;
189    ///
190    /// fn parse_number(input: &str) -> Result<u32, Error> {
191    ///     input.parse::<u32>().map_err(Error::expr_cast)
192    /// }
193    ///
194    /// assert_eq!(parse_number("42").unwrap(), 42);
195    /// assert!(matches!(parse_number("abc"), Err(Error::ExprCast(_))));
196    /// ```
197    #[inline]
198    pub fn expr_cast<E: fmt::Display>(err: E) -> Self {
199        Error::ExprCast(err.to_string())
200    }
201
202    /// Create a predicate build error from any displayable error.
203    ///
204    /// This is a convenience method for converting other error types into
205    /// [`Error::PredicateBuild`] while preserving the original error message.
206    ///
207    /// # Examples
208    ///
209    /// ```
210    /// use llkv_result::Error;
211    ///
212    /// fn unsupported_predicate() -> Result<(), Error> {
213    ///     let io_err = std::io::Error::new(std::io::ErrorKind::Other, "not supported");
214    ///     Err(Error::predicate_build(io_err))
215    /// }
216    ///
217    /// let err = unsupported_predicate().unwrap_err();
218    /// assert!(matches!(err, Error::PredicateBuild(msg) if msg.contains("not supported")));
219    /// ```
220    #[inline]
221    pub fn predicate_build<E: fmt::Display>(err: E) -> Self {
222        Error::PredicateBuild(err.to_string())
223    }
224
225    /// Create a reserved table ID error.
226    ///
227    /// Currently only table ID 0 is reserved. This method creates an error
228    /// when user code attempts to use a reserved ID.
229    #[inline]
230    pub fn reserved_table_id(table_id: impl Into<u16>) -> Self {
231        Error::ReservedTableId(table_id.into())
232    }
233}