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    /// Input file not found.
76    #[error("file not found: {0}")]
77    FileNotFound(String),
78
79    /// File path failed validation.
80    #[error("path validation failed for '{path}': {reason}")]
81    PathValidationFailed { path: String, reason: String },
82
83    /// Unsupported file format.
84    #[error("unsupported format: {0}")]
85    UnsupportedFormat(String),
86
87    /// Schema mismatch between input and table.
88    #[error("schema mismatch: expected {expected} columns, got {actual} - {reason}")]
89    SchemaMismatch {
90        expected: usize,
91        actual: usize,
92        reason: String,
93    },
94
95    /// Invalid storage type option.
96    #[error("invalid storage type: {0}")]
97    InvalidStorageType(String),
98
99    /// Invalid compression option.
100    #[error("invalid compression: {0}")]
101    InvalidCompression(String),
102
103    /// Invalid row group size option.
104    #[error("invalid row_group_size: {0}")]
105    InvalidRowGroupSize(String),
106
107    /// Invalid rowid_mode option.
108    #[error("invalid rowid_mode: {0}")]
109    InvalidRowIdMode(String),
110
111    /// Unknown table option key.
112    #[error("unknown table option: {0}")]
113    UnknownTableOption(String),
114
115    /// Duplicate option key.
116    #[error("duplicate option: {0}")]
117    DuplicateOption(String),
118
119    /// Vector processing error.
120    #[error("vector error: {0}")]
121    Vector(#[from] VectorError),
122
123    /// Bulk load generic error.
124    #[error("bulk load error: {0}")]
125    BulkLoad(String),
126
127    /// Columnar engine error.
128    #[error("columnar engine error: {0}")]
129    Columnar(String),
130
131    /// Planner error (wrapped for convenience).
132    #[error("planner error: {0}")]
133    Planner(#[from] crate::planner::PlannerError),
134}
135
136/// Constraint violation details.
137#[derive(Debug, Error, Clone, PartialEq, Eq)]
138pub enum ConstraintViolation {
139    /// NOT NULL constraint violated.
140    #[error("NOT NULL constraint violated on column: {column}")]
141    NotNull { column: String },
142
143    /// PRIMARY KEY constraint violated.
144    #[error("PRIMARY KEY constraint violated on columns: {columns:?}, value: {value:?}")]
145    PrimaryKey {
146        columns: Vec<String>,
147        value: Option<String>,
148    },
149
150    /// UNIQUE constraint violated.
151    #[error(
152        "UNIQUE constraint violated on index: {index_name}, columns: {columns:?}, value: {value:?}"
153    )]
154    Unique {
155        index_name: String,
156        columns: Vec<String>,
157        value: Option<String>,
158    },
159}
160
161/// Expression evaluation errors.
162#[derive(Debug, Error, Clone, PartialEq, Eq)]
163pub enum EvaluationError {
164    /// Division by zero.
165    #[error("division by zero")]
166    DivisionByZero,
167
168    /// Integer overflow.
169    #[error("integer overflow")]
170    Overflow,
171
172    /// Type mismatch during evaluation.
173    #[error("type mismatch: expected {expected}, got {actual}")]
174    TypeMismatch { expected: String, actual: String },
175
176    /// Invalid column reference (index out of bounds).
177    #[error("invalid column reference: index {index}")]
178    InvalidColumnRef { index: usize },
179
180    /// Unsupported expression type.
181    #[error("unsupported expression: {0}")]
182    UnsupportedExpression(String),
183
184    /// Unsupported function call.
185    #[error("unsupported function: {0}")]
186    UnsupportedFunction(String),
187
188    /// Vector evaluation error.
189    #[error("vector error: {0}")]
190    Vector(#[from] VectorError),
191}
192
193/// Type alias for executor results.
194pub type Result<T> = std::result::Result<T, ExecutorError>;
195
196#[cfg(test)]
197mod tests {
198    use super::*;
199
200    #[test]
201    fn test_executor_error_display() {
202        let err = ExecutorError::TableNotFound("users".into());
203        assert_eq!(err.to_string(), "table not found: users");
204    }
205
206    #[test]
207    fn test_constraint_violation_display() {
208        let err = ConstraintViolation::NotNull {
209            column: "name".into(),
210        };
211        assert_eq!(
212            err.to_string(),
213            "NOT NULL constraint violated on column: name"
214        );
215    }
216
217    #[test]
218    fn test_evaluation_error_display() {
219        let err = EvaluationError::DivisionByZero;
220        assert_eq!(err.to_string(), "division by zero");
221    }
222
223    #[test]
224    fn test_constraint_violation_from() {
225        let violation = ConstraintViolation::NotNull {
226            column: "age".into(),
227        };
228        let err: ExecutorError = violation.into();
229        assert!(matches!(err, ExecutorError::ConstraintViolation(_)));
230    }
231
232    #[test]
233    fn test_evaluation_error_from() {
234        let eval_err = EvaluationError::Overflow;
235        let err: ExecutorError = eval_err.into();
236        assert!(matches!(err, ExecutorError::Evaluation(_)));
237    }
238
239    #[test]
240    fn test_vector_error_conversion() {
241        let vector_err = VectorError::ZeroNormVector;
242        let eval_err: EvaluationError = vector_err.clone().into();
243        assert!(matches!(eval_err, EvaluationError::Vector(_)));
244
245        let exec_err: ExecutorError = vector_err.into();
246        assert!(matches!(exec_err, ExecutorError::Vector(_)));
247    }
248
249    #[test]
250    fn test_path_validation_failed_display() {
251        let err = ExecutorError::PathValidationFailed {
252            path: "/tmp/data.parquet".into(),
253            reason: "symbolic links not allowed".into(),
254        };
255        assert_eq!(
256            err.to_string(),
257            "path validation failed for '/tmp/data.parquet': symbolic links not allowed"
258        );
259    }
260}