Skip to main content

alopex_sql/executor/
error.rs

1//! Error types for the Executor module.
2//!
3//! This module defines error types for SQL execution:
4//! - [`ExecutorError`]: Top-level executor errors
5//! - [`ConstraintViolation`]: Constraint violation details
6//! - [`EvaluationError`]: Expression evaluation errors
7
8use crate::executor::evaluator::VectorError;
9use crate::storage::StorageError;
10use thiserror::Error;
11
12/// Errors that can occur during SQL execution.
13#[derive(Debug, Error)]
14pub enum ExecutorError {
15    /// Underlying HNSW/kv error.
16    #[error("hnsw error: {0}")]
17    Core(#[from] alopex_core::Error),
18
19    /// Table not found in catalog.
20    #[error("table not found: {0}")]
21    TableNotFound(String),
22
23    /// Table already exists in catalog.
24    #[error("table already exists: {0}")]
25    TableAlreadyExists(String),
26
27    /// Index not found in catalog.
28    #[error("index not found: {0}")]
29    IndexNotFound(String),
30
31    /// Index already exists in catalog.
32    #[error("index already exists: {0}")]
33    IndexAlreadyExists(String),
34
35    /// Column not found in table.
36    #[error("column not found: {0}")]
37    ColumnNotFound(String),
38
39    /// Constraint violation during DML operation.
40    #[error("constraint violation: {0}")]
41    ConstraintViolation(#[from] ConstraintViolation),
42
43    /// Expression evaluation error.
44    #[error("evaluation error: {0}")]
45    Evaluation(#[from] EvaluationError),
46
47    /// Unsupported SQL operation.
48    #[error("unsupported operation: {0}")]
49    UnsupportedOperation(String),
50
51    /// Transaction conflict (concurrent modification).
52    #[error("transaction conflict")]
53    TransactionConflict,
54
55    /// Read-only トランザクションで書き込み操作を実行しようとした。
56    #[error("read-only transaction: cannot execute {operation}")]
57    ReadOnlyTransaction { operation: String },
58
59    /// Storage layer error.
60    #[error("storage error: {0}")]
61    Storage(#[from] StorageError),
62
63    /// Column value required (DEFAULT not supported in v0.1.2).
64    #[error("column required: {column} (DEFAULT not supported in v0.1.2)")]
65    ColumnRequired { column: String },
66
67    /// Invalid index name (reserved prefix).
68    #[error("invalid index name: {name} - {reason}")]
69    InvalidIndexName { name: String, reason: String },
70
71    /// Invalid operation.
72    #[error("invalid operation: {operation} - {reason}")]
73    InvalidOperation { operation: String, reason: String },
74
75    /// Resource exhausted during execution.
76    #[error("resource exhausted: {message}")]
77    ResourceExhausted { message: String },
78
79    /// Input file not found.
80    #[error("file not found: {0}")]
81    FileNotFound(String),
82
83    /// File path failed validation.
84    #[error("path validation failed for '{path}': {reason}")]
85    PathValidationFailed { path: String, reason: String },
86
87    /// Unsupported file format.
88    #[error("unsupported format: {0}")]
89    UnsupportedFormat(String),
90
91    /// Schema mismatch between input and table.
92    #[error("schema mismatch: expected {expected} columns, got {actual} - {reason}")]
93    SchemaMismatch {
94        expected: usize,
95        actual: usize,
96        reason: String,
97    },
98
99    /// Invalid storage type option.
100    #[error("invalid storage type: {0}")]
101    InvalidStorageType(String),
102
103    /// Invalid compression option.
104    #[error("invalid compression: {0}")]
105    InvalidCompression(String),
106
107    /// Invalid row group size option.
108    #[error("invalid row_group_size: {0}")]
109    InvalidRowGroupSize(String),
110
111    /// Invalid rowid_mode option.
112    #[error("invalid rowid_mode: {0}")]
113    InvalidRowIdMode(String),
114
115    /// Unknown table option key.
116    #[error("unknown table option: {0}")]
117    UnknownTableOption(String),
118
119    /// Duplicate option key.
120    #[error("duplicate option: {0}")]
121    DuplicateOption(String),
122
123    /// Vector processing error.
124    #[error("vector error: {0}")]
125    Vector(#[from] VectorError),
126
127    /// Bulk load generic error.
128    #[error("bulk load error: {0}")]
129    BulkLoad(String),
130
131    /// Columnar engine error.
132    #[error("columnar engine error: {0}")]
133    Columnar(String),
134
135    /// Planner error (wrapped for convenience).
136    #[error("planner error: {0}")]
137    Planner(#[from] crate::planner::PlannerError),
138}
139
140/// Constraint violation details.
141#[derive(Debug, Error, Clone, PartialEq, Eq)]
142pub enum ConstraintViolation {
143    /// NOT NULL constraint violated.
144    #[error("NOT NULL constraint violated on column: {column}")]
145    NotNull { column: String },
146
147    /// PRIMARY KEY constraint violated.
148    #[error("PRIMARY KEY constraint violated on columns: {columns:?}, value: {value:?}")]
149    PrimaryKey {
150        columns: Vec<String>,
151        value: Option<String>,
152    },
153
154    /// UNIQUE constraint violated.
155    #[error(
156        "UNIQUE constraint violated on index: {index_name}, columns: {columns:?}, value: {value:?}"
157    )]
158    Unique {
159        index_name: String,
160        columns: Vec<String>,
161        value: Option<String>,
162    },
163}
164
165/// Expression evaluation errors.
166#[derive(Debug, Error, Clone, PartialEq, Eq)]
167pub enum EvaluationError {
168    /// Division by zero.
169    #[error("division by zero")]
170    DivisionByZero,
171
172    /// Integer overflow.
173    #[error("integer overflow")]
174    Overflow,
175
176    /// Type mismatch during evaluation.
177    #[error("type mismatch: expected {expected}, got {actual}")]
178    TypeMismatch { expected: String, actual: String },
179
180    /// An explicit CAST conversion could not represent the source value.
181    #[error("cannot cast {source_type} to {target}: {reason}")]
182    CastFailed {
183        source_type: String,
184        target: String,
185        reason: String,
186    },
187
188    /// Invalid column reference (index out of bounds).
189    #[error("invalid column reference: index {index}")]
190    InvalidColumnRef { index: usize },
191
192    /// Unsupported expression type.
193    #[error("unsupported expression: {0}")]
194    UnsupportedExpression(String),
195
196    /// Unsupported function call.
197    #[error("unsupported function: {0}")]
198    UnsupportedFunction(String),
199
200    /// Invalid or resource-limited regular-expression input.
201    #[error("invalid regular expression '{pattern}': {reason}")]
202    InvalidRegex { pattern: String, reason: String },
203
204    /// Invalid function argument or encoding payload.
205    #[error("invalid argument for {function}: {reason}")]
206    InvalidArgument { function: String, reason: String },
207
208    /// Vector evaluation error.
209    #[error("vector error: {0}")]
210    Vector(#[from] VectorError),
211}
212
213/// Type alias for executor results.
214pub type Result<T> = std::result::Result<T, ExecutorError>;
215
216#[cfg(test)]
217mod tests {
218    use super::*;
219
220    #[test]
221    fn test_executor_error_display() {
222        let err = ExecutorError::TableNotFound("users".into());
223        assert_eq!(err.to_string(), "table not found: users");
224    }
225
226    #[test]
227    fn test_constraint_violation_display() {
228        let err = ConstraintViolation::NotNull {
229            column: "name".into(),
230        };
231        assert_eq!(
232            err.to_string(),
233            "NOT NULL constraint violated on column: name"
234        );
235    }
236
237    #[test]
238    fn test_evaluation_error_display() {
239        let err = EvaluationError::DivisionByZero;
240        assert_eq!(err.to_string(), "division by zero");
241    }
242
243    #[test]
244    fn test_constraint_violation_from() {
245        let violation = ConstraintViolation::NotNull {
246            column: "age".into(),
247        };
248        let err: ExecutorError = violation.into();
249        assert!(matches!(err, ExecutorError::ConstraintViolation(_)));
250    }
251
252    #[test]
253    fn test_evaluation_error_from() {
254        let eval_err = EvaluationError::Overflow;
255        let err: ExecutorError = eval_err.into();
256        assert!(matches!(err, ExecutorError::Evaluation(_)));
257    }
258
259    #[test]
260    fn test_vector_error_conversion() {
261        let vector_err = VectorError::ZeroNormVector;
262        let eval_err: EvaluationError = vector_err.clone().into();
263        assert!(matches!(eval_err, EvaluationError::Vector(_)));
264
265        let exec_err: ExecutorError = vector_err.into();
266        assert!(matches!(exec_err, ExecutorError::Vector(_)));
267    }
268
269    #[test]
270    fn test_path_validation_failed_display() {
271        let err = ExecutorError::PathValidationFailed {
272            path: "/tmp/data.parquet".into(),
273            reason: "symbolic links not allowed".into(),
274        };
275        assert_eq!(
276            err.to_string(),
277            "path validation failed for '/tmp/data.parquet': symbolic links not allowed"
278        );
279    }
280}