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