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    /// Invalid column reference (index out of bounds).
181    #[error("invalid column reference: index {index}")]
182    InvalidColumnRef { index: usize },
183
184    /// Unsupported expression type.
185    #[error("unsupported expression: {0}")]
186    UnsupportedExpression(String),
187
188    /// Unsupported function call.
189    #[error("unsupported function: {0}")]
190    UnsupportedFunction(String),
191
192    /// Vector evaluation error.
193    #[error("vector error: {0}")]
194    Vector(#[from] VectorError),
195}
196
197/// Type alias for executor results.
198pub type Result<T> = std::result::Result<T, ExecutorError>;
199
200#[cfg(test)]
201mod tests {
202    use super::*;
203
204    #[test]
205    fn test_executor_error_display() {
206        let err = ExecutorError::TableNotFound("users".into());
207        assert_eq!(err.to_string(), "table not found: users");
208    }
209
210    #[test]
211    fn test_constraint_violation_display() {
212        let err = ConstraintViolation::NotNull {
213            column: "name".into(),
214        };
215        assert_eq!(
216            err.to_string(),
217            "NOT NULL constraint violated on column: name"
218        );
219    }
220
221    #[test]
222    fn test_evaluation_error_display() {
223        let err = EvaluationError::DivisionByZero;
224        assert_eq!(err.to_string(), "division by zero");
225    }
226
227    #[test]
228    fn test_constraint_violation_from() {
229        let violation = ConstraintViolation::NotNull {
230            column: "age".into(),
231        };
232        let err: ExecutorError = violation.into();
233        assert!(matches!(err, ExecutorError::ConstraintViolation(_)));
234    }
235
236    #[test]
237    fn test_evaluation_error_from() {
238        let eval_err = EvaluationError::Overflow;
239        let err: ExecutorError = eval_err.into();
240        assert!(matches!(err, ExecutorError::Evaluation(_)));
241    }
242
243    #[test]
244    fn test_vector_error_conversion() {
245        let vector_err = VectorError::ZeroNormVector;
246        let eval_err: EvaluationError = vector_err.clone().into();
247        assert!(matches!(eval_err, EvaluationError::Vector(_)));
248
249        let exec_err: ExecutorError = vector_err.into();
250        assert!(matches!(exec_err, ExecutorError::Vector(_)));
251    }
252
253    #[test]
254    fn test_path_validation_failed_display() {
255        let err = ExecutorError::PathValidationFailed {
256            path: "/tmp/data.parquet".into(),
257            reason: "symbolic links not allowed".into(),
258        };
259        assert_eq!(
260            err.to_string(),
261            "path validation failed for '/tmp/data.parquet': symbolic links not allowed"
262        );
263    }
264}