1use crate::ast::Span;
9use thiserror::Error;
10
11#[derive(Debug, Clone, PartialEq, Eq, Error)]
13pub enum PlannerError {
14 #[error("invalid PRAGMA '{name}': {reason}")]
16 InvalidPragma { name: String, reason: String },
17 #[error("error[ALOPEX-C001]: table '{name}' not found at line {line}, column {column}")]
20 TableNotFound {
21 name: String,
22 line: u64,
23 column: u64,
24 },
25
26 #[error("error[ALOPEX-C002]: table '{name}' already exists")]
28 TableAlreadyExists { name: String },
29
30 #[error(
32 "error[ALOPEX-C003]: column '{column}' not found in table '{table}' at line {line}, column {col}"
33 )]
34 ColumnNotFound {
35 column: String,
36 table: String,
37 line: u64,
38 col: u64,
39 },
40
41 #[error(
43 "error[ALOPEX-C004]: ambiguous column '{column}' found in tables: {tables:?} at line {line}, column {col}"
44 )]
45 AmbiguousColumn {
46 column: String,
47 tables: Vec<String>,
48 line: u64,
49 col: u64,
50 },
51
52 #[error("error[ALOPEX-C005]: index '{name}' already exists")]
54 IndexAlreadyExists { name: String },
55
56 #[error("error[ALOPEX-C006]: index '{name}' not found")]
58 IndexNotFound { name: String },
59
60 #[error(
62 "error[ALOPEX-C007]: table function '{name}' does not exist at line {line}, column {column}"
63 )]
64 UnknownTableFunction {
65 name: String,
66 line: u64,
67 column: u64,
68 },
69
70 #[error(
73 "error[ALOPEX-T001]: type mismatch at line {line}, column {column}: expected {expected}, found {found}"
74 )]
75 TypeMismatch {
76 expected: String,
77 found: String,
78 line: u64,
79 column: u64,
80 },
81
82 #[error(
84 "error[ALOPEX-T002]: invalid operator '{op}' for type '{type_name}' at line {line}, column {column}"
85 )]
86 InvalidOperator {
87 op: String,
88 type_name: String,
89 line: u64,
90 column: u64,
91 },
92
93 #[error(
95 "error[ALOPEX-T003]: null constraint violation for column '{column}' at line {line}, column {col}"
96 )]
97 NullConstraintViolation { column: String, line: u64, col: u64 },
98
99 #[error(
101 "error[ALOPEX-T004]: vector dimension mismatch at line {line}, column {column}: expected {expected}, found {found}"
102 )]
103 VectorDimensionMismatch {
104 expected: u32,
105 found: u32,
106 line: u64,
107 column: u64,
108 },
109
110 #[error(
112 "error[ALOPEX-T005]: invalid metric '{value}' at line {line}, column {column}. Valid options: cosine, l2, inner"
113 )]
114 InvalidMetric {
115 value: String,
116 line: u64,
117 column: u64,
118 },
119
120 #[error(
122 "error[ALOPEX-T006]: column count ({columns}) does not match value count ({values}) at line {line}, column {column}"
123 )]
124 ColumnValueCountMismatch {
125 columns: usize,
126 values: usize,
127 line: u64,
128 column: u64,
129 },
130
131 #[error("error[ALOPEX-T007]: invalid expression: {message}")]
133 InvalidExpression { message: String },
134
135 #[error(
137 "error[ALOPEX-T008]: set operation column count mismatch: left {left}, right {right} at line {line}, column {column}"
138 )]
139 SetOperationColumnCountMismatch {
140 left: usize,
141 right: usize,
142 line: u64,
143 column: u64,
144 },
145
146 #[error(
148 "error[ALOPEX-T009]: common table expression '{cte}' declares {declared} column names but its query returns {actual} columns at line {line}, column {column}"
149 )]
150 CteColumnCountMismatch {
151 cte: String,
152 declared: usize,
153 actual: usize,
154 line: u64,
155 column: u64,
156 },
157
158 #[error(
160 "error[ALOPEX-T010]: common table expression '{cte}' declares column '{name}' more than once at line {line}, column {column}"
161 )]
162 DuplicateCteColumn {
163 cte: String,
164 name: String,
165 line: u64,
166 column: u64,
167 },
168
169 #[error(
171 "error[ALOPEX-T011]: VALUES row {row} has {actual} columns but row 1 has {expected} at line {line}, column {column}"
172 )]
173 ValuesColumnCountMismatch {
174 row: usize,
175 expected: usize,
176 actual: usize,
177 line: u64,
178 column: u64,
179 },
180
181 #[error(
185 "error[ALOPEX-T012]: relation alias '{alias}' declares {declared} column names but the relation has {actual} columns at line {line}, column {column}"
186 )]
187 TableAliasColumnCountMismatch {
188 alias: String,
189 declared: usize,
190 actual: usize,
191 line: u64,
192 column: u64,
193 },
194
195 #[error(
197 "error[ALOPEX-T013]: row value has {actual} fields but its comparison operand has {expected} at line {line}, column {column}"
198 )]
199 RowArityMismatch {
200 expected: usize,
201 actual: usize,
202 line: u64,
203 column: u64,
204 },
205
206 #[error(
210 "error[ALOPEX-T014]: SELECT DISTINCT ON expressions must match initial ORDER BY expressions at line {line}, column {column}"
211 )]
212 DistinctOnOrderByMismatch { line: u64, column: u64 },
213
214 #[error(
218 "error[ALOPEX-T015]: {join_type} JOIN cannot have a LATERAL right side at line {line}, column {column}"
219 )]
220 LateralJoinTypeUnsupported {
221 join_type: String,
222 line: u64,
223 column: u64,
224 },
225
226 #[error(
229 "error[ALOPEX-F001]: feature '{feature}' is not supported in this version. Expected in {version}"
230 )]
231 UnsupportedFeature {
232 feature: String,
233 version: String,
234 line: u64,
235 column: u64,
236 },
237}
238
239impl PlannerError {
240 pub fn table_not_found(name: impl Into<String>, span: Span) -> Self {
242 Self::TableNotFound {
243 name: name.into(),
244 line: span.start.line,
245 column: span.start.column,
246 }
247 }
248
249 pub fn table_already_exists(name: impl Into<String>) -> Self {
251 Self::TableAlreadyExists { name: name.into() }
252 }
253
254 pub fn unknown_table_function(name: impl Into<String>, span: Span) -> Self {
256 Self::UnknownTableFunction {
257 name: name.into(),
258 line: span.start.line,
259 column: span.start.column,
260 }
261 }
262
263 pub fn lateral_join_type_unsupported(join_type: impl Into<String>, span: Span) -> Self {
265 Self::LateralJoinTypeUnsupported {
266 join_type: join_type.into(),
267 line: span.start.line,
268 column: span.start.column,
269 }
270 }
271
272 pub fn column_not_found(
274 column: impl Into<String>,
275 table: impl Into<String>,
276 span: Span,
277 ) -> Self {
278 Self::ColumnNotFound {
279 column: column.into(),
280 table: table.into(),
281 line: span.start.line,
282 col: span.start.column,
283 }
284 }
285
286 pub fn ambiguous_column(column: impl Into<String>, tables: Vec<String>, span: Span) -> Self {
288 Self::AmbiguousColumn {
289 column: column.into(),
290 tables,
291 line: span.start.line,
292 col: span.start.column,
293 }
294 }
295
296 pub fn index_already_exists(name: impl Into<String>) -> Self {
298 Self::IndexAlreadyExists { name: name.into() }
299 }
300
301 pub fn index_not_found(name: impl Into<String>) -> Self {
303 Self::IndexNotFound { name: name.into() }
304 }
305
306 pub fn type_mismatch(
308 expected: impl Into<String>,
309 found: impl Into<String>,
310 span: Span,
311 ) -> Self {
312 Self::TypeMismatch {
313 expected: expected.into(),
314 found: found.into(),
315 line: span.start.line,
316 column: span.start.column,
317 }
318 }
319
320 pub fn invalid_expression(message: impl Into<String>) -> Self {
322 Self::InvalidExpression {
323 message: message.into(),
324 }
325 }
326
327 pub fn set_operation_column_count_mismatch(left: usize, right: usize, span: Span) -> Self {
329 Self::SetOperationColumnCountMismatch {
330 left,
331 right,
332 line: span.start.line,
333 column: span.start.column,
334 }
335 }
336
337 pub fn cte_column_count_mismatch(
339 cte: impl Into<String>,
340 declared: usize,
341 actual: usize,
342 span: Span,
343 ) -> Self {
344 Self::CteColumnCountMismatch {
345 cte: cte.into(),
346 declared,
347 actual,
348 line: span.start.line,
349 column: span.start.column,
350 }
351 }
352
353 pub fn duplicate_cte_column(
355 cte: impl Into<String>,
356 column_name: impl Into<String>,
357 span: Span,
358 ) -> Self {
359 Self::DuplicateCteColumn {
360 cte: cte.into(),
361 name: column_name.into(),
362 line: span.start.line,
363 column: span.start.column,
364 }
365 }
366
367 pub fn values_column_count_mismatch(
369 row: usize,
370 expected: usize,
371 actual: usize,
372 span: Span,
373 ) -> Self {
374 Self::ValuesColumnCountMismatch {
375 row,
376 expected,
377 actual,
378 line: span.start.line,
379 column: span.start.column,
380 }
381 }
382
383 pub fn table_alias_column_count_mismatch(
385 alias: impl Into<String>,
386 declared: usize,
387 actual: usize,
388 span: Span,
389 ) -> Self {
390 Self::TableAliasColumnCountMismatch {
391 alias: alias.into(),
392 declared,
393 actual,
394 line: span.start.line,
395 column: span.start.column,
396 }
397 }
398
399 pub fn invalid_operator(
401 op: impl Into<String>,
402 type_name: impl Into<String>,
403 span: Span,
404 ) -> Self {
405 Self::InvalidOperator {
406 op: op.into(),
407 type_name: type_name.into(),
408 line: span.start.line,
409 column: span.start.column,
410 }
411 }
412
413 pub fn null_constraint_violation(column: impl Into<String>, span: Span) -> Self {
415 Self::NullConstraintViolation {
416 column: column.into(),
417 line: span.start.line,
418 col: span.start.column,
419 }
420 }
421
422 pub fn vector_dimension_mismatch(expected: u32, found: u32, span: Span) -> Self {
424 Self::VectorDimensionMismatch {
425 expected,
426 found,
427 line: span.start.line,
428 column: span.start.column,
429 }
430 }
431
432 pub fn invalid_metric(value: impl Into<String>, span: Span) -> Self {
434 Self::InvalidMetric {
435 value: value.into(),
436 line: span.start.line,
437 column: span.start.column,
438 }
439 }
440
441 pub fn column_value_count_mismatch(columns: usize, values: usize, span: Span) -> Self {
443 Self::ColumnValueCountMismatch {
444 columns,
445 values,
446 line: span.start.line,
447 column: span.start.column,
448 }
449 }
450
451 pub fn distinct_on_order_by_mismatch(span: Span) -> Self {
453 Self::DistinctOnOrderByMismatch {
454 line: span.start.line,
455 column: span.start.column,
456 }
457 }
458
459 pub fn unsupported_feature(
461 feature: impl Into<String>,
462 version: impl Into<String>,
463 span: Span,
464 ) -> Self {
465 Self::UnsupportedFeature {
466 feature: feature.into(),
467 version: version.into(),
468 line: span.start.line,
469 column: span.start.column,
470 }
471 }
472}