Skip to main content

dbx_core/
error.rs

1//! Error types for the DBX database engine.
2//!
3//! All public APIs return `DbxResult<T>` — no panics in library code.
4
5use thiserror::Error;
6
7/// Unified error type for all DBX operations.
8#[derive(Debug, Error)]
9pub enum DbxError {
10    /// Storage layer error (I/O, corruption, etc.)
11    #[error("storage error: {0}")]
12    Storage(String),
13
14    /// Schema definition or validation error
15    #[error("schema error: {0}")]
16    Schema(String),
17
18    /// Apache Arrow error (RecordBatch operations)
19    #[error("arrow error: {source}")]
20    Arrow {
21        #[from]
22        source: arrow::error::ArrowError,
23    },
24
25    /// Apache Parquet error (file I/O)
26    #[error("parquet error: {source}")]
27    Parquet {
28        #[from]
29        source: parquet::errors::ParquetError,
30    },
31
32    /// sled embedded database error
33    #[error("sled error: {source}")]
34    Sled {
35        #[from]
36        source: sled::Error,
37    },
38
39    /// Standard I/O error
40    #[error("io error: {source}")]
41    Io {
42        #[from]
43        source: std::io::Error,
44    },
45
46    /// Requested table does not exist
47    #[error("table '{0}' not found")]
48    TableNotFound(String),
49
50    /// Requested key does not exist
51    #[error("key not found")]
52    KeyNotFound,
53
54    /// Type mismatch between expected and actual values
55    #[error("type mismatch: expected {expected}, got {actual}")]
56    TypeMismatch { expected: String, actual: String },
57
58    /// Constraint violation (PK, FK, CHECK, etc.)
59    #[error("constraint violation: {0}")]
60    ConstraintViolation(String),
61
62    /// Serialization/deserialization error
63    #[error("serialization error: {0}")]
64    Serialization(String),
65
66    /// Feature not yet implemented
67    #[error("not implemented: {0}")]
68    NotImplemented(String),
69
70    /// SQL parsing error
71    #[error("SQL parse error: {message}\nSQL: {sql}")]
72    SqlParse { message: String, sql: String },
73
74    /// SQL execution error
75    #[error("SQL execution error: {message}\nContext: {context}")]
76    SqlExecution { message: String, context: String },
77
78    /// Unsupported SQL feature
79    #[error("SQL feature not supported: {feature}\nHint: {hint}")]
80    SqlNotSupported { feature: String, hint: String },
81
82    /// Transaction conflict
83    #[error("transaction conflict: {message}")]
84    TransactionConflict { message: String },
85
86    /// Transaction aborted
87    #[error("transaction aborted: {reason}")]
88    TransactionAborted { reason: String },
89
90    /// Invalid operation
91    #[error("invalid operation: {message}\nContext: {context}")]
92    InvalidOperation { message: String, context: String },
93
94    /// Index already exists
95    #[error("index already exists on table '{table}', column '{column}'")]
96    IndexAlreadyExists { table: String, column: String },
97
98    /// Index not found
99    #[error("index not found on table '{table}', column '{column}'")]
100    IndexNotFound { table: String, column: String },
101
102    /// WAL error
103    #[error("WAL error: {0}")]
104    Wal(String),
105
106    /// Checkpoint failed
107    #[error("checkpoint failed: {0}")]
108    CheckpointFailed(String),
109
110    /// Recovery failed
111    #[error("recovery failed: {0}")]
112    RecoveryFailed(String),
113
114    /// Encryption/decryption error
115    #[error("encryption error: {0}")]
116    Encryption(String),
117
118    /// GPU operation error
119    #[error("GPU error: {0}")]
120    Gpu(String),
121
122    /// Callable not found
123    #[error("callable '{0}' not found")]
124    CallableNotFound(String),
125
126    /// Duplicate callable registration
127    #[error("callable '{0}' already registered")]
128    DuplicateCallable(String),
129
130    /// Invalid arguments
131    #[error("invalid arguments: {0}")]
132    InvalidArguments(String),
133
134    /// Lock poisoned
135    #[error("lock poisoned")]
136    LockPoisoned,
137
138    /// Performance regression detected
139    #[error(
140        "performance regression detected for '{name}': baseline={baseline:.2}ms, current={current:.2}ms, ratio={ratio:.2}x"
141    )]
142    PerformanceRegression {
143        name: String,
144        baseline: f64,
145        current: f64,
146        ratio: f64,
147    },
148}
149
150/// Result type alias for all DBX operations.
151pub type DbxResult<T> = Result<T, DbxError>;
152
153// From 구현들
154impl From<serde_json::Error> for DbxError {
155    fn from(err: serde_json::Error) -> Self {
156        DbxError::Serialization(err.to_string())
157    }
158}
159
160#[cfg(test)]
161mod tests {
162    use super::*;
163
164    #[test]
165    fn error_display_storage() {
166        let err = DbxError::Storage("disk full".to_string());
167        assert_eq!(err.to_string(), "storage error: disk full");
168    }
169
170    #[test]
171    fn error_display_table_not_found() {
172        let err = DbxError::TableNotFound("users".to_string());
173        assert_eq!(err.to_string(), "table 'users' not found");
174    }
175
176    #[test]
177    fn error_display_type_mismatch() {
178        let err = DbxError::TypeMismatch {
179            expected: "Int32".to_string(),
180            actual: "Utf8".to_string(),
181        };
182        assert_eq!(err.to_string(), "type mismatch: expected Int32, got Utf8");
183    }
184
185    #[test]
186    #[allow(clippy::unnecessary_literal_unwrap)]
187    fn dbx_result_ok() {
188        let result: DbxResult<i32> = Ok(42);
189        assert_eq!(result.unwrap(), 42);
190    }
191
192    #[test]
193    fn dbx_result_err() {
194        let result: DbxResult<i32> = Err(DbxError::KeyNotFound);
195        assert!(result.is_err());
196    }
197
198    #[test]
199    fn error_display_sql_parse() {
200        let err = DbxError::SqlParse {
201            message: "unexpected token".to_string(),
202            sql: "SELECT * FORM users".to_string(),
203        };
204        assert!(err.to_string().contains("SQL parse error"));
205        assert!(err.to_string().contains("FORM users"));
206    }
207
208    #[test]
209    fn error_display_sql_not_supported() {
210        let err = DbxError::SqlNotSupported {
211            feature: "WINDOW functions".to_string(),
212            hint: "Use subqueries instead".to_string(),
213        };
214        assert!(err.to_string().contains("not supported"));
215        assert!(err.to_string().contains("WINDOW functions"));
216        assert!(err.to_string().contains("subqueries"));
217    }
218
219    #[test]
220    fn error_display_transaction_conflict() {
221        let err = DbxError::TransactionConflict {
222            message: "write-write conflict on key 'user:123'".to_string(),
223        };
224        assert!(err.to_string().contains("transaction conflict"));
225        assert!(err.to_string().contains("user:123"));
226    }
227
228    #[test]
229    fn error_display_invalid_operation() {
230        let err = DbxError::InvalidOperation {
231            message: "cannot query after commit".to_string(),
232            context: "Transaction is in Committed state".to_string(),
233        };
234        assert!(err.to_string().contains("invalid operation"));
235        assert!(err.to_string().contains("after commit"));
236    }
237}