Skip to main content

alopex_sql/planner/
error.rs

1//! Planner error types for the Alopex SQL dialect.
2//!
3//! This module defines error types for the planning phase, including:
4//! - Catalog errors (ALOPEX-C*): Table/column/index lookup failures
5//! - Type errors (ALOPEX-T*): Type mismatches, constraint violations
6//! - Feature errors (ALOPEX-F*): Unsupported features
7
8use crate::ast::Span;
9use thiserror::Error;
10
11/// Planner errors for the Alopex SQL dialect.
12#[derive(Debug, Clone, PartialEq, Eq, Error)]
13pub enum PlannerError {
14    // === Catalog Errors (ALOPEX-C*) ===
15    /// ALOPEX-C001: Table not found.
16    #[error("error[ALOPEX-C001]: table '{name}' not found at line {line}, column {column}")]
17    TableNotFound {
18        name: String,
19        line: u64,
20        column: u64,
21    },
22
23    /// ALOPEX-C002: Table already exists.
24    #[error("error[ALOPEX-C002]: table '{name}' already exists")]
25    TableAlreadyExists { name: String },
26
27    /// ALOPEX-C003: Column not found.
28    #[error(
29        "error[ALOPEX-C003]: column '{column}' not found in table '{table}' at line {line}, column {col}"
30    )]
31    ColumnNotFound {
32        column: String,
33        table: String,
34        line: u64,
35        col: u64,
36    },
37
38    /// ALOPEX-C004: Ambiguous column reference.
39    #[error(
40        "error[ALOPEX-C004]: ambiguous column '{column}' found in tables: {tables:?} at line {line}, column {col}"
41    )]
42    AmbiguousColumn {
43        column: String,
44        tables: Vec<String>,
45        line: u64,
46        col: u64,
47    },
48
49    /// ALOPEX-C005: Index already exists.
50    #[error("error[ALOPEX-C005]: index '{name}' already exists")]
51    IndexAlreadyExists { name: String },
52
53    /// ALOPEX-C006: Index not found.
54    #[error("error[ALOPEX-C006]: index '{name}' not found")]
55    IndexNotFound { name: String },
56
57    // === Type Errors (ALOPEX-T*) ===
58    /// ALOPEX-T001: Type mismatch.
59    #[error(
60        "error[ALOPEX-T001]: type mismatch at line {line}, column {column}: expected {expected}, found {found}"
61    )]
62    TypeMismatch {
63        expected: String,
64        found: String,
65        line: u64,
66        column: u64,
67    },
68
69    /// ALOPEX-T002: Invalid operator for type.
70    #[error(
71        "error[ALOPEX-T002]: invalid operator '{op}' for type '{type_name}' at line {line}, column {column}"
72    )]
73    InvalidOperator {
74        op: String,
75        type_name: String,
76        line: u64,
77        column: u64,
78    },
79
80    /// ALOPEX-T003: NULL constraint violation.
81    #[error(
82        "error[ALOPEX-T003]: null constraint violation for column '{column}' at line {line}, column {col}"
83    )]
84    NullConstraintViolation { column: String, line: u64, col: u64 },
85
86    /// ALOPEX-T004: Vector dimension mismatch.
87    #[error(
88        "error[ALOPEX-T004]: vector dimension mismatch at line {line}, column {column}: expected {expected}, found {found}"
89    )]
90    VectorDimensionMismatch {
91        expected: u32,
92        found: u32,
93        line: u64,
94        column: u64,
95    },
96
97    /// ALOPEX-T005: Invalid metric.
98    #[error(
99        "error[ALOPEX-T005]: invalid metric '{value}' at line {line}, column {column}. Valid options: cosine, l2, inner"
100    )]
101    InvalidMetric {
102        value: String,
103        line: u64,
104        column: u64,
105    },
106
107    /// ALOPEX-T006: Column count does not match value count.
108    #[error(
109        "error[ALOPEX-T006]: column count ({columns}) does not match value count ({values}) at line {line}, column {column}"
110    )]
111    ColumnValueCountMismatch {
112        columns: usize,
113        values: usize,
114        line: u64,
115        column: u64,
116    },
117
118    /// ALOPEX-T007: Invalid expression for the current context.
119    #[error("error[ALOPEX-T007]: invalid expression: {message}")]
120    InvalidExpression { message: String },
121
122    // === Feature Errors (ALOPEX-F*) ===
123    /// ALOPEX-F001: Unsupported feature.
124    #[error(
125        "error[ALOPEX-F001]: feature '{feature}' is not supported in this version. Expected in {version}"
126    )]
127    UnsupportedFeature {
128        feature: String,
129        version: String,
130        line: u64,
131        column: u64,
132    },
133}
134
135impl PlannerError {
136    /// Create a TableNotFound error from a span.
137    pub fn table_not_found(name: impl Into<String>, span: Span) -> Self {
138        Self::TableNotFound {
139            name: name.into(),
140            line: span.start.line,
141            column: span.start.column,
142        }
143    }
144
145    /// Create a TableAlreadyExists error.
146    pub fn table_already_exists(name: impl Into<String>) -> Self {
147        Self::TableAlreadyExists { name: name.into() }
148    }
149
150    /// Create a ColumnNotFound error from a span.
151    pub fn column_not_found(
152        column: impl Into<String>,
153        table: impl Into<String>,
154        span: Span,
155    ) -> Self {
156        Self::ColumnNotFound {
157            column: column.into(),
158            table: table.into(),
159            line: span.start.line,
160            col: span.start.column,
161        }
162    }
163
164    /// Create an AmbiguousColumn error from a span.
165    pub fn ambiguous_column(column: impl Into<String>, tables: Vec<String>, span: Span) -> Self {
166        Self::AmbiguousColumn {
167            column: column.into(),
168            tables,
169            line: span.start.line,
170            col: span.start.column,
171        }
172    }
173
174    /// Create an IndexAlreadyExists error.
175    pub fn index_already_exists(name: impl Into<String>) -> Self {
176        Self::IndexAlreadyExists { name: name.into() }
177    }
178
179    /// Create an IndexNotFound error.
180    pub fn index_not_found(name: impl Into<String>) -> Self {
181        Self::IndexNotFound { name: name.into() }
182    }
183
184    /// Create a TypeMismatch error from a span.
185    pub fn type_mismatch(
186        expected: impl Into<String>,
187        found: impl Into<String>,
188        span: Span,
189    ) -> Self {
190        Self::TypeMismatch {
191            expected: expected.into(),
192            found: found.into(),
193            line: span.start.line,
194            column: span.start.column,
195        }
196    }
197
198    /// Create an InvalidExpression error.
199    pub fn invalid_expression(message: impl Into<String>) -> Self {
200        Self::InvalidExpression {
201            message: message.into(),
202        }
203    }
204
205    /// Create an InvalidOperator error from a span.
206    pub fn invalid_operator(
207        op: impl Into<String>,
208        type_name: impl Into<String>,
209        span: Span,
210    ) -> Self {
211        Self::InvalidOperator {
212            op: op.into(),
213            type_name: type_name.into(),
214            line: span.start.line,
215            column: span.start.column,
216        }
217    }
218
219    /// Create a NullConstraintViolation error from a span.
220    pub fn null_constraint_violation(column: impl Into<String>, span: Span) -> Self {
221        Self::NullConstraintViolation {
222            column: column.into(),
223            line: span.start.line,
224            col: span.start.column,
225        }
226    }
227
228    /// Create a VectorDimensionMismatch error from a span.
229    pub fn vector_dimension_mismatch(expected: u32, found: u32, span: Span) -> Self {
230        Self::VectorDimensionMismatch {
231            expected,
232            found,
233            line: span.start.line,
234            column: span.start.column,
235        }
236    }
237
238    /// Create an InvalidMetric error from a span.
239    pub fn invalid_metric(value: impl Into<String>, span: Span) -> Self {
240        Self::InvalidMetric {
241            value: value.into(),
242            line: span.start.line,
243            column: span.start.column,
244        }
245    }
246
247    /// Create a ColumnValueCountMismatch error from a span.
248    pub fn column_value_count_mismatch(columns: usize, values: usize, span: Span) -> Self {
249        Self::ColumnValueCountMismatch {
250            columns,
251            values,
252            line: span.start.line,
253            column: span.start.column,
254        }
255    }
256
257    /// Create an UnsupportedFeature error from a span.
258    pub fn unsupported_feature(
259        feature: impl Into<String>,
260        version: impl Into<String>,
261        span: Span,
262    ) -> Self {
263        Self::UnsupportedFeature {
264            feature: feature.into(),
265            version: version.into(),
266            line: span.start.line,
267            column: span.start.column,
268        }
269    }
270}