vibesql_executor/
errors.rs

1#[derive(Debug, Clone, PartialEq)]
2pub enum ExecutorError {
3    TableNotFound(String),
4    TableAlreadyExists(String),
5    ColumnNotFound {
6        column_name: String,
7        table_name: String,
8        searched_tables: Vec<String>,
9        available_columns: Vec<String>,
10    },
11    InvalidTableQualifier {
12        qualifier: String,
13        column: String,
14        available_tables: Vec<String>,
15    },
16    ColumnAlreadyExists(String),
17    IndexNotFound(String),
18    IndexAlreadyExists(String),
19    InvalidIndexDefinition(String),
20    TriggerNotFound(String),
21    TriggerAlreadyExists(String),
22    SchemaNotFound(String),
23    SchemaAlreadyExists(String),
24    SchemaNotEmpty(String),
25    RoleNotFound(String),
26    TypeNotFound(String),
27    TypeAlreadyExists(String),
28    TypeInUse(String),
29    DependentPrivilegesExist(String),
30    PermissionDenied {
31        role: String,
32        privilege: String,
33        object: String,
34    },
35    ColumnIndexOutOfBounds {
36        index: usize,
37    },
38    TypeMismatch {
39        left: vibesql_types::SqlValue,
40        op: String,
41        right: vibesql_types::SqlValue,
42    },
43    DivisionByZero,
44    InvalidWhereClause(String),
45    UnsupportedExpression(String),
46    UnsupportedFeature(String),
47    StorageError(String),
48    SubqueryReturnedMultipleRows {
49        expected: usize,
50        actual: usize,
51    },
52    SubqueryColumnCountMismatch {
53        expected: usize,
54        actual: usize,
55    },
56    ColumnCountMismatch {
57        expected: usize,
58        provided: usize,
59    },
60    CastError {
61        from_type: String,
62        to_type: String,
63    },
64    TypeConversionError {
65        from: String,
66        to: String,
67    },
68    ConstraintViolation(String),
69    MultiplePrimaryKeys,
70    CannotDropColumn(String),
71    ConstraintNotFound {
72        constraint_name: String,
73        table_name: String,
74    },
75    /// Expression evaluation exceeded maximum recursion depth
76    /// This prevents stack overflow from deeply nested expressions or subqueries
77    ExpressionDepthExceeded {
78        depth: usize,
79        max_depth: usize,
80    },
81    /// Query exceeded maximum execution time
82    QueryTimeoutExceeded {
83        elapsed_seconds: u64,
84        max_seconds: u64,
85    },
86    /// Query exceeded maximum row processing limit
87    RowLimitExceeded {
88        rows_processed: usize,
89        max_rows: usize,
90    },
91    /// Query exceeded maximum memory limit
92    MemoryLimitExceeded {
93        used_bytes: usize,
94        max_bytes: usize,
95    },
96    /// Variable not found in procedural context (with available variables)
97    VariableNotFound {
98        variable_name: String,
99        available_variables: Vec<String>,
100    },
101    /// Label not found in procedural context
102    LabelNotFound(String),
103    /// Procedural SELECT INTO returned wrong number of rows (must be exactly 1)
104    SelectIntoRowCount {
105        expected: usize,
106        actual: usize,
107    },
108    /// Procedural SELECT INTO column count doesn't match variable count
109    SelectIntoColumnCount {
110        expected: usize,
111        actual: usize,
112    },
113    /// Procedure not found (with suggestions)
114    ProcedureNotFound {
115        procedure_name: String,
116        schema_name: String,
117        available_procedures: Vec<String>,
118    },
119    /// Function not found (with suggestions)
120    FunctionNotFound {
121        function_name: String,
122        schema_name: String,
123        available_functions: Vec<String>,
124    },
125    /// Parameter count mismatch with details
126    ParameterCountMismatch {
127        routine_name: String,
128        routine_type: String, // "Procedure" or "Function"
129        expected: usize,
130        actual: usize,
131        parameter_signature: String,
132    },
133    /// Parameter type mismatch with details
134    ParameterTypeMismatch {
135        parameter_name: String,
136        expected_type: String,
137        actual_type: String,
138        actual_value: String,
139    },
140    /// Type error in expression evaluation
141    TypeError(String),
142    /// Function argument count mismatch
143    ArgumentCountMismatch {
144        expected: usize,
145        actual: usize,
146    },
147    /// Recursion limit exceeded in function/procedure calls (with call stack)
148    RecursionLimitExceeded {
149        message: String,
150        call_stack: Vec<String>,
151        max_depth: usize,
152    },
153    /// Function must return a value but did not
154    FunctionMustReturn,
155    /// Invalid control flow (e.g., LEAVE/ITERATE outside of loop)
156    InvalidControlFlow(String),
157    /// Invalid function body syntax
158    InvalidFunctionBody(String),
159    /// Function attempted to modify data (read-only violation)
160    FunctionReadOnlyViolation(String),
161    /// Parse error
162    ParseError(String),
163    /// Invalid EXTRACT field for the given value type
164    InvalidExtractField {
165        field: String,
166        value_type: String,
167    },
168    /// Arrow array downcast failed (columnar execution)
169    ArrowDowncastError {
170        expected_type: String,
171        context: String,
172    },
173    /// Type mismatch in columnar operations
174    ColumnarTypeMismatch {
175        operation: String,
176        left_type: String,
177        right_type: Option<String>,
178    },
179    /// SIMD operation failed (returned None or error)
180    SimdOperationFailed {
181        operation: String,
182        reason: String,
183    },
184    /// Column not found in batch by index
185    ColumnarColumnNotFound {
186        column_index: usize,
187        batch_columns: usize,
188    },
189    /// Column not found in batch by name
190    ColumnarColumnNotFoundByName {
191        column_name: String,
192    },
193    /// Column length mismatch in batch operations
194    ColumnarLengthMismatch {
195        context: String,
196        expected: usize,
197        actual: usize,
198    },
199    /// Unsupported array type for columnar operation
200    UnsupportedArrayType {
201        operation: String,
202        array_type: String,
203    },
204    /// Invalid or incompatible geometry type in spatial function
205    SpatialGeometryError {
206        function_name: String,
207        message: String,
208    },
209    /// Spatial operation failed (distance, intersection, etc.)
210    SpatialOperationFailed {
211        function_name: String,
212        message: String,
213    },
214    /// Spatial function argument count or type mismatch
215    SpatialArgumentError {
216        function_name: String,
217        expected: String,
218        actual: String,
219    },
220    /// Cursor already exists with this name
221    CursorAlreadyExists(String),
222    /// Cursor not found
223    CursorNotFound(String),
224    /// Cursor is already open
225    CursorAlreadyOpen(String),
226    /// Cursor is not open (must OPEN before FETCH)
227    CursorNotOpen(String),
228    /// Cursor does not support backward movement (not declared with SCROLL)
229    CursorNotScrollable(String),
230    Other(String),
231}
232
233/// Find the closest matching string using simple Levenshtein distance
234fn find_closest_match<'a>(target: &str, candidates: &'a [String]) -> Option<&'a String> {
235    if candidates.is_empty() {
236        return None;
237    }
238
239    let target_lower = target.to_lowercase();
240
241    // First check for exact case-insensitive match
242    if let Some(exact) = candidates.iter().find(|c| c.to_lowercase() == target_lower) {
243        return Some(exact);
244    }
245
246    // Calculate Levenshtein distance for each candidate
247    let mut best_match: Option<(&String, usize)> = None;
248
249    for candidate in candidates {
250        let distance = levenshtein_distance(&target_lower, &candidate.to_lowercase());
251
252        // Only suggest if distance is small relative to the target length
253        // (e.g., within 2 edits or 30% of the length)
254        let max_distance = (target.len() / 3).max(2);
255
256        if distance <= max_distance {
257            if let Some((_, best_distance)) = best_match {
258                if distance < best_distance {
259                    best_match = Some((candidate, distance));
260                }
261            } else {
262                best_match = Some((candidate, distance));
263            }
264        }
265    }
266
267    best_match.map(|(s, _)| s)
268}
269
270/// Calculate Levenshtein distance between two strings
271fn levenshtein_distance(s1: &str, s2: &str) -> usize {
272    let len1 = s1.len();
273    let len2 = s2.len();
274
275    if len1 == 0 {
276        return len2;
277    }
278    if len2 == 0 {
279        return len1;
280    }
281
282    let mut prev_row: Vec<usize> = (0..=len2).collect();
283    let mut curr_row = vec![0; len2 + 1];
284
285    for (i, c1) in s1.chars().enumerate() {
286        curr_row[0] = i + 1;
287
288        for (j, c2) in s2.chars().enumerate() {
289            let cost = if c1 == c2 { 0 } else { 1 };
290            curr_row[j + 1] = (curr_row[j] + 1).min(prev_row[j + 1] + 1).min(prev_row[j] + cost);
291        }
292
293        std::mem::swap(&mut prev_row, &mut curr_row);
294    }
295
296    prev_row[len2]
297}
298
299impl std::fmt::Display for ExecutorError {
300    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
301        use vibesql_l10n::vibe_msg;
302        match self {
303            ExecutorError::TableNotFound(name) => {
304                write!(f, "{}", vibe_msg!("executor-table-not-found", name = name.as_str()))
305            }
306            ExecutorError::TableAlreadyExists(name) => {
307                write!(f, "{}", vibe_msg!("executor-table-already-exists", name = name.as_str()))
308            }
309            ExecutorError::ColumnNotFound {
310                column_name,
311                table_name,
312                searched_tables,
313                available_columns,
314            } => {
315                if searched_tables.is_empty() {
316                    write!(f, "{}", vibe_msg!("executor-column-not-found-simple", column_name = column_name.as_str(), table_name = table_name.as_str()))
317                } else if available_columns.is_empty() {
318                    let searched = searched_tables.join(", ");
319                    write!(f, "{}", vibe_msg!("executor-column-not-found-searched", column_name = column_name.as_str(), searched_tables = searched.as_str()))
320                } else {
321                    let searched = searched_tables.join(", ");
322                    let available = available_columns.join(", ");
323                    write!(f, "{}", vibe_msg!("executor-column-not-found-with-available", column_name = column_name.as_str(), searched_tables = searched.as_str(), available_columns = available.as_str()))
324                }
325            }
326            ExecutorError::InvalidTableQualifier { qualifier, column, available_tables } => {
327                let available = available_tables.join(", ");
328                write!(f, "{}", vibe_msg!("executor-invalid-table-qualifier", qualifier = qualifier.as_str(), column = column.as_str(), available_tables = available.as_str()))
329            }
330            ExecutorError::ColumnAlreadyExists(name) => {
331                write!(f, "{}", vibe_msg!("executor-column-already-exists", name = name.as_str()))
332            }
333            ExecutorError::IndexNotFound(name) => {
334                write!(f, "{}", vibe_msg!("executor-index-not-found", name = name.as_str()))
335            }
336            ExecutorError::IndexAlreadyExists(name) => {
337                write!(f, "{}", vibe_msg!("executor-index-already-exists", name = name.as_str()))
338            }
339            ExecutorError::InvalidIndexDefinition(msg) => {
340                write!(f, "{}", vibe_msg!("executor-invalid-index-definition", message = msg.as_str()))
341            }
342            ExecutorError::TriggerNotFound(name) => {
343                write!(f, "{}", vibe_msg!("executor-trigger-not-found", name = name.as_str()))
344            }
345            ExecutorError::TriggerAlreadyExists(name) => {
346                write!(f, "{}", vibe_msg!("executor-trigger-already-exists", name = name.as_str()))
347            }
348            ExecutorError::SchemaNotFound(name) => {
349                write!(f, "{}", vibe_msg!("executor-schema-not-found", name = name.as_str()))
350            }
351            ExecutorError::SchemaAlreadyExists(name) => {
352                write!(f, "{}", vibe_msg!("executor-schema-already-exists", name = name.as_str()))
353            }
354            ExecutorError::SchemaNotEmpty(name) => {
355                write!(f, "{}", vibe_msg!("executor-schema-not-empty", name = name.as_str()))
356            }
357            ExecutorError::RoleNotFound(name) => {
358                write!(f, "{}", vibe_msg!("executor-role-not-found", name = name.as_str()))
359            }
360            ExecutorError::TypeNotFound(name) => {
361                write!(f, "{}", vibe_msg!("executor-type-not-found", name = name.as_str()))
362            }
363            ExecutorError::TypeAlreadyExists(name) => {
364                write!(f, "{}", vibe_msg!("executor-type-already-exists", name = name.as_str()))
365            }
366            ExecutorError::TypeInUse(name) => {
367                write!(f, "{}", vibe_msg!("executor-type-in-use", name = name.as_str()))
368            }
369            ExecutorError::DependentPrivilegesExist(msg) => {
370                write!(f, "{}", vibe_msg!("executor-dependent-privileges-exist", message = msg.as_str()))
371            }
372            ExecutorError::PermissionDenied { role, privilege, object } => {
373                write!(f, "{}", vibe_msg!("executor-permission-denied", role = role.as_str(), privilege = privilege.as_str(), object = object.as_str()))
374            }
375            ExecutorError::ColumnIndexOutOfBounds { index } => {
376                write!(f, "{}", vibe_msg!("executor-column-index-out-of-bounds", index = *index as i64))
377            }
378            ExecutorError::TypeMismatch { left, op, right } => {
379                let left_str = format!("{:?}", left);
380                let right_str = format!("{:?}", right);
381                write!(f, "{}", vibe_msg!("executor-type-mismatch", left = left_str.as_str(), op = op.as_str(), right = right_str.as_str()))
382            }
383            ExecutorError::DivisionByZero => {
384                write!(f, "{}", vibe_msg!("executor-division-by-zero"))
385            }
386            ExecutorError::InvalidWhereClause(msg) => {
387                write!(f, "{}", vibe_msg!("executor-invalid-where-clause", message = msg.as_str()))
388            }
389            ExecutorError::UnsupportedExpression(msg) => {
390                write!(f, "{}", vibe_msg!("executor-unsupported-expression", message = msg.as_str()))
391            }
392            ExecutorError::UnsupportedFeature(msg) => {
393                write!(f, "{}", vibe_msg!("executor-unsupported-feature", message = msg.as_str()))
394            }
395            ExecutorError::StorageError(msg) => {
396                write!(f, "{}", vibe_msg!("executor-storage-error", message = msg.as_str()))
397            }
398            ExecutorError::SubqueryReturnedMultipleRows { expected, actual } => {
399                write!(f, "{}", vibe_msg!("executor-subquery-returned-multiple-rows", expected = *expected as i64, actual = *actual as i64))
400            }
401            ExecutorError::SubqueryColumnCountMismatch { expected, actual } => {
402                write!(f, "{}", vibe_msg!("executor-subquery-column-count-mismatch", expected = *expected as i64, actual = *actual as i64))
403            }
404            ExecutorError::ColumnCountMismatch { expected, provided } => {
405                write!(f, "{}", vibe_msg!("executor-column-count-mismatch", expected = *expected as i64, provided = *provided as i64))
406            }
407            ExecutorError::CastError { from_type, to_type } => {
408                write!(f, "{}", vibe_msg!("executor-cast-error", from_type = from_type.as_str(), to_type = to_type.as_str()))
409            }
410            ExecutorError::TypeConversionError { from, to } => {
411                write!(f, "{}", vibe_msg!("executor-type-conversion-error", from = from.as_str(), to = to.as_str()))
412            }
413            ExecutorError::ConstraintViolation(msg) => {
414                write!(f, "{}", vibe_msg!("executor-constraint-violation", message = msg.as_str()))
415            }
416            ExecutorError::MultiplePrimaryKeys => {
417                write!(f, "{}", vibe_msg!("executor-multiple-primary-keys"))
418            }
419            ExecutorError::CannotDropColumn(msg) => {
420                write!(f, "{}", vibe_msg!("executor-cannot-drop-column", message = msg.as_str()))
421            }
422            ExecutorError::ConstraintNotFound { constraint_name, table_name } => {
423                write!(f, "{}", vibe_msg!("executor-constraint-not-found", constraint_name = constraint_name.as_str(), table_name = table_name.as_str()))
424            }
425            ExecutorError::ExpressionDepthExceeded { depth, max_depth } => {
426                write!(f, "{}", vibe_msg!("executor-expression-depth-exceeded", depth = *depth as i64, max_depth = *max_depth as i64))
427            }
428            ExecutorError::QueryTimeoutExceeded { elapsed_seconds, max_seconds } => {
429                write!(f, "{}", vibe_msg!("executor-query-timeout-exceeded", elapsed_seconds = *elapsed_seconds as i64, max_seconds = *max_seconds as i64))
430            }
431            ExecutorError::RowLimitExceeded { rows_processed, max_rows } => {
432                write!(f, "{}", vibe_msg!("executor-row-limit-exceeded", rows_processed = *rows_processed as i64, max_rows = *max_rows as i64))
433            }
434            ExecutorError::MemoryLimitExceeded { used_bytes, max_bytes } => {
435                let used_gb = format!("{:.2}", *used_bytes as f64 / 1024.0 / 1024.0 / 1024.0);
436                let max_gb = format!("{:.2}", *max_bytes as f64 / 1024.0 / 1024.0 / 1024.0);
437                write!(f, "{}", vibe_msg!("executor-memory-limit-exceeded", used_gb = used_gb.as_str(), max_gb = max_gb.as_str()))
438            }
439            ExecutorError::VariableNotFound { variable_name, available_variables } => {
440                if available_variables.is_empty() {
441                    write!(f, "{}", vibe_msg!("executor-variable-not-found-simple", variable_name = variable_name.as_str()))
442                } else {
443                    let available = available_variables.join(", ");
444                    write!(f, "{}", vibe_msg!("executor-variable-not-found-with-available", variable_name = variable_name.as_str(), available_variables = available.as_str()))
445                }
446            }
447            ExecutorError::LabelNotFound(name) => {
448                write!(f, "{}", vibe_msg!("executor-label-not-found", name = name.as_str()))
449            }
450            ExecutorError::SelectIntoRowCount { expected, actual } => {
451                let plural = if *actual == 1 { "" } else { "s" };
452                write!(f, "{}", vibe_msg!("executor-select-into-row-count", expected = *expected as i64, actual = *actual as i64, plural = plural))
453            }
454            ExecutorError::SelectIntoColumnCount { expected, actual } => {
455                let expected_plural = if *expected == 1 { "" } else { "s" };
456                let actual_plural = if *actual == 1 { "" } else { "s" };
457                write!(f, "{}", vibe_msg!("executor-select-into-column-count", expected = *expected as i64, expected_plural = expected_plural, actual = *actual as i64, actual_plural = actual_plural))
458            }
459            ExecutorError::ProcedureNotFound {
460                procedure_name,
461                schema_name,
462                available_procedures,
463            } => {
464                if available_procedures.is_empty() {
465                    write!(f, "{}", vibe_msg!("executor-procedure-not-found-simple", procedure_name = procedure_name.as_str(), schema_name = schema_name.as_str()))
466                } else {
467                    let suggestion = find_closest_match(procedure_name, available_procedures);
468                    if let Some(similar) = suggestion {
469                        // Use multi-line format with suggestion
470                        write!(f, "{}\nAvailable procedures: {}\nDid you mean '{}'?",
471                            vibe_msg!("executor-procedure-not-found-simple", procedure_name = procedure_name.as_str(), schema_name = schema_name.as_str()),
472                            available_procedures.join(", "),
473                            similar)
474                    } else {
475                        write!(f, "{}\nAvailable procedures: {}",
476                            vibe_msg!("executor-procedure-not-found-simple", procedure_name = procedure_name.as_str(), schema_name = schema_name.as_str()),
477                            available_procedures.join(", "))
478                    }
479                }
480            }
481            ExecutorError::FunctionNotFound { function_name, schema_name, available_functions } => {
482                if available_functions.is_empty() {
483                    write!(f, "{}", vibe_msg!("executor-function-not-found-simple", function_name = function_name.as_str(), schema_name = schema_name.as_str()))
484                } else {
485                    let suggestion = find_closest_match(function_name, available_functions);
486                    if let Some(similar) = suggestion {
487                        write!(f, "{}\nAvailable functions: {}\nDid you mean '{}'?",
488                            vibe_msg!("executor-function-not-found-simple", function_name = function_name.as_str(), schema_name = schema_name.as_str()),
489                            available_functions.join(", "),
490                            similar)
491                    } else {
492                        write!(f, "{}\nAvailable functions: {}",
493                            vibe_msg!("executor-function-not-found-simple", function_name = function_name.as_str(), schema_name = schema_name.as_str()),
494                            available_functions.join(", "))
495                    }
496                }
497            }
498            ExecutorError::ParameterCountMismatch {
499                routine_name,
500                routine_type,
501                expected,
502                actual,
503                parameter_signature,
504            } => {
505                let expected_plural = if *expected == 1 { "" } else { "s" };
506                let actual_plural = if *actual == 1 { "" } else { "s" };
507                write!(f, "{}", vibe_msg!("executor-parameter-count-mismatch",
508                    routine_type = routine_type.as_str(),
509                    routine_name = routine_name.as_str(),
510                    expected = *expected as i64,
511                    expected_plural = expected_plural,
512                    parameter_signature = parameter_signature.as_str(),
513                    actual = *actual as i64,
514                    actual_plural = actual_plural))
515            }
516            ExecutorError::ParameterTypeMismatch {
517                parameter_name,
518                expected_type,
519                actual_type,
520                actual_value,
521            } => {
522                write!(f, "{}", vibe_msg!("executor-parameter-type-mismatch",
523                    parameter_name = parameter_name.as_str(),
524                    expected_type = expected_type.as_str(),
525                    actual_type = actual_type.as_str(),
526                    actual_value = actual_value.as_str()))
527            }
528            ExecutorError::TypeError(msg) => {
529                write!(f, "{}", vibe_msg!("executor-type-error", message = msg.as_str()))
530            }
531            ExecutorError::ArgumentCountMismatch { expected, actual } => {
532                write!(f, "{}", vibe_msg!("executor-argument-count-mismatch", expected = *expected as i64, actual = *actual as i64))
533            }
534            ExecutorError::RecursionLimitExceeded { message, call_stack, max_depth } => {
535                write!(f, "{}", vibe_msg!("executor-recursion-limit-exceeded", max_depth = *max_depth as i64, message = message.as_str()))?;
536                if !call_stack.is_empty() {
537                    write!(f, "\n{}", vibe_msg!("executor-recursion-call-stack"))?;
538                    for call in call_stack {
539                        write!(f, "\n  {}", call)?;
540                    }
541                }
542                Ok(())
543            }
544            ExecutorError::FunctionMustReturn => {
545                write!(f, "{}", vibe_msg!("executor-function-must-return"))
546            }
547            ExecutorError::InvalidControlFlow(msg) => {
548                write!(f, "{}", vibe_msg!("executor-invalid-control-flow", message = msg.as_str()))
549            }
550            ExecutorError::InvalidFunctionBody(msg) => {
551                write!(f, "{}", vibe_msg!("executor-invalid-function-body", message = msg.as_str()))
552            }
553            ExecutorError::FunctionReadOnlyViolation(msg) => {
554                write!(f, "{}", vibe_msg!("executor-function-read-only-violation", message = msg.as_str()))
555            }
556            ExecutorError::ParseError(msg) => {
557                write!(f, "{}", vibe_msg!("executor-parse-error", message = msg.as_str()))
558            }
559            ExecutorError::InvalidExtractField { field, value_type } => {
560                write!(f, "{}", vibe_msg!("executor-invalid-extract-field", field = field.as_str(), value_type = value_type.as_str()))
561            }
562            ExecutorError::ArrowDowncastError { expected_type, context } => {
563                write!(f, "{}", vibe_msg!("executor-arrow-downcast-error", expected_type = expected_type.as_str(), context = context.as_str()))
564            }
565            ExecutorError::ColumnarTypeMismatch { operation, left_type, right_type } => {
566                if let Some(right) = right_type {
567                    write!(f, "{}", vibe_msg!("executor-columnar-type-mismatch-binary", operation = operation.as_str(), left_type = left_type.as_str(), right_type = right.as_str()))
568                } else {
569                    write!(f, "{}", vibe_msg!("executor-columnar-type-mismatch-unary", operation = operation.as_str(), left_type = left_type.as_str()))
570                }
571            }
572            ExecutorError::SimdOperationFailed { operation, reason } => {
573                write!(f, "{}", vibe_msg!("executor-simd-operation-failed", operation = operation.as_str(), reason = reason.as_str()))
574            }
575            ExecutorError::ColumnarColumnNotFound { column_index, batch_columns } => {
576                write!(f, "{}", vibe_msg!("executor-columnar-column-not-found", column_index = *column_index as i64, batch_columns = *batch_columns as i64))
577            }
578            ExecutorError::ColumnarColumnNotFoundByName { column_name } => {
579                write!(f, "{}", vibe_msg!("executor-columnar-column-not-found-by-name", column_name = column_name.as_str()))
580            }
581            ExecutorError::ColumnarLengthMismatch { context, expected, actual } => {
582                write!(f, "{}", vibe_msg!("executor-columnar-length-mismatch", context = context.as_str(), expected = *expected as i64, actual = *actual as i64))
583            }
584            ExecutorError::UnsupportedArrayType { operation, array_type } => {
585                write!(f, "{}", vibe_msg!("executor-unsupported-array-type", operation = operation.as_str(), array_type = array_type.as_str()))
586            }
587            ExecutorError::SpatialGeometryError { function_name, message } => {
588                write!(f, "{}", vibe_msg!("executor-spatial-geometry-error", function_name = function_name.as_str(), message = message.as_str()))
589            }
590            ExecutorError::SpatialOperationFailed { function_name, message } => {
591                write!(f, "{}", vibe_msg!("executor-spatial-operation-failed", function_name = function_name.as_str(), message = message.as_str()))
592            }
593            ExecutorError::SpatialArgumentError { function_name, expected, actual } => {
594                write!(f, "{}", vibe_msg!("executor-spatial-argument-error", function_name = function_name.as_str(), expected = expected.as_str(), actual = actual.as_str()))
595            }
596            ExecutorError::CursorAlreadyExists(name) => {
597                write!(f, "{}", vibe_msg!("executor-cursor-already-exists", name = name.as_str()))
598            }
599            ExecutorError::CursorNotFound(name) => {
600                write!(f, "{}", vibe_msg!("executor-cursor-not-found", name = name.as_str()))
601            }
602            ExecutorError::CursorAlreadyOpen(name) => {
603                write!(f, "{}", vibe_msg!("executor-cursor-already-open", name = name.as_str()))
604            }
605            ExecutorError::CursorNotOpen(name) => {
606                write!(f, "{}", vibe_msg!("executor-cursor-not-open", name = name.as_str()))
607            }
608            ExecutorError::CursorNotScrollable(name) => {
609                write!(f, "{}", vibe_msg!("executor-cursor-not-scrollable", name = name.as_str()))
610            }
611            ExecutorError::Other(msg) => {
612                write!(f, "{}", vibe_msg!("executor-other", message = msg.as_str()))
613            }
614        }
615    }
616}
617
618impl std::error::Error for ExecutorError {}
619
620impl From<vibesql_storage::StorageError> for ExecutorError {
621    fn from(err: vibesql_storage::StorageError) -> Self {
622        match err {
623            vibesql_storage::StorageError::TableNotFound(name) => {
624                ExecutorError::TableNotFound(name)
625            }
626            vibesql_storage::StorageError::IndexAlreadyExists(name) => {
627                ExecutorError::IndexAlreadyExists(name)
628            }
629            vibesql_storage::StorageError::IndexNotFound(name) => {
630                ExecutorError::IndexNotFound(name)
631            }
632            vibesql_storage::StorageError::ColumnCountMismatch { expected, actual } => {
633                ExecutorError::ColumnCountMismatch { expected, provided: actual }
634            }
635            vibesql_storage::StorageError::ColumnIndexOutOfBounds { index } => {
636                ExecutorError::ColumnIndexOutOfBounds { index }
637            }
638            vibesql_storage::StorageError::CatalogError(msg) => ExecutorError::StorageError(msg),
639            vibesql_storage::StorageError::TransactionError(msg) => {
640                ExecutorError::StorageError(msg)
641            }
642            vibesql_storage::StorageError::RowNotFound => {
643                ExecutorError::StorageError("Row not found".to_string())
644            }
645            vibesql_storage::StorageError::NullConstraintViolation { column } => {
646                ExecutorError::ConstraintViolation(format!(
647                    "NOT NULL constraint violation: column '{}' cannot be NULL",
648                    column
649                ))
650            }
651            vibesql_storage::StorageError::TypeMismatch { column, expected, actual } => {
652                ExecutorError::StorageError(format!(
653                    "Type mismatch in column '{}': expected {}, got {}",
654                    column, expected, actual
655                ))
656            }
657            vibesql_storage::StorageError::ColumnNotFound { column_name, table_name } => {
658                ExecutorError::StorageError(format!(
659                    "Column '{}' not found in table '{}'",
660                    column_name, table_name
661                ))
662            }
663            vibesql_storage::StorageError::UniqueConstraintViolation(msg) => {
664                ExecutorError::ConstraintViolation(msg)
665            }
666            vibesql_storage::StorageError::InvalidIndexColumn(msg) => {
667                ExecutorError::StorageError(msg)
668            }
669            vibesql_storage::StorageError::NotImplemented(msg) => {
670                ExecutorError::StorageError(format!("Not implemented: {}", msg))
671            }
672            vibesql_storage::StorageError::IoError(msg) => {
673                ExecutorError::StorageError(format!("I/O error: {}", msg))
674            }
675            vibesql_storage::StorageError::InvalidPageSize { expected, actual } => {
676                ExecutorError::StorageError(format!(
677                    "Invalid page size: expected {}, got {}",
678                    expected, actual
679                ))
680            }
681            vibesql_storage::StorageError::InvalidPageId(page_id) => {
682                ExecutorError::StorageError(format!("Invalid page ID: {}", page_id))
683            }
684            vibesql_storage::StorageError::LockError(msg) => {
685                ExecutorError::StorageError(format!("Lock error: {}", msg))
686            }
687            vibesql_storage::StorageError::MemoryBudgetExceeded { used, budget } => {
688                ExecutorError::StorageError(format!(
689                    "Memory budget exceeded: using {} bytes, budget is {} bytes",
690                    used, budget
691                ))
692            }
693            vibesql_storage::StorageError::NoIndexToEvict => ExecutorError::StorageError(
694                "No index available to evict (all indexes are already disk-backed)".to_string(),
695            ),
696            vibesql_storage::StorageError::Other(msg) => ExecutorError::StorageError(msg),
697        }
698    }
699}
700
701impl From<vibesql_catalog::CatalogError> for ExecutorError {
702    fn from(err: vibesql_catalog::CatalogError) -> Self {
703        match err {
704            vibesql_catalog::CatalogError::TableAlreadyExists(name) => {
705                ExecutorError::TableAlreadyExists(name)
706            }
707            vibesql_catalog::CatalogError::TableNotFound { table_name } => {
708                ExecutorError::TableNotFound(table_name)
709            }
710            vibesql_catalog::CatalogError::ColumnAlreadyExists(name) => {
711                ExecutorError::ColumnAlreadyExists(name)
712            }
713            vibesql_catalog::CatalogError::ColumnNotFound { column_name, table_name } => {
714                ExecutorError::ColumnNotFound {
715                    column_name,
716                    table_name,
717                    searched_tables: vec![],
718                    available_columns: vec![],
719                }
720            }
721            vibesql_catalog::CatalogError::SchemaNotFound(name) => {
722                ExecutorError::SchemaNotFound(name)
723            }
724            vibesql_catalog::CatalogError::SchemaAlreadyExists(name) => {
725                ExecutorError::SchemaAlreadyExists(name)
726            }
727            vibesql_catalog::CatalogError::SchemaNotEmpty(name) => {
728                ExecutorError::SchemaNotEmpty(name)
729            }
730            vibesql_catalog::CatalogError::RoleAlreadyExists(name) => {
731                ExecutorError::StorageError(format!("Role '{}' already exists", name))
732            }
733            vibesql_catalog::CatalogError::RoleNotFound(name) => ExecutorError::RoleNotFound(name),
734            // Advanced SQL:1999 objects
735            vibesql_catalog::CatalogError::DomainAlreadyExists(name) => {
736                ExecutorError::Other(format!("Domain '{}' already exists", name))
737            }
738            vibesql_catalog::CatalogError::DomainNotFound(name) => {
739                ExecutorError::Other(format!("Domain '{}' not found", name))
740            }
741            vibesql_catalog::CatalogError::DomainInUse { domain_name, dependent_columns } => {
742                ExecutorError::Other(format!(
743                    "Domain '{}' is still in use by {} column(s): {}",
744                    domain_name,
745                    dependent_columns.len(),
746                    dependent_columns
747                        .iter()
748                        .map(|(t, c)| format!("{}.{}", t, c))
749                        .collect::<Vec<_>>()
750                        .join(", ")
751                ))
752            }
753            vibesql_catalog::CatalogError::SequenceAlreadyExists(name) => {
754                ExecutorError::Other(format!("Sequence '{}' already exists", name))
755            }
756            vibesql_catalog::CatalogError::SequenceNotFound(name) => {
757                ExecutorError::Other(format!("Sequence '{}' not found", name))
758            }
759            vibesql_catalog::CatalogError::SequenceInUse { sequence_name, dependent_columns } => {
760                ExecutorError::Other(format!(
761                    "Sequence '{}' is still in use by {} column(s): {}",
762                    sequence_name,
763                    dependent_columns.len(),
764                    dependent_columns
765                        .iter()
766                        .map(|(t, c)| format!("{}.{}", t, c))
767                        .collect::<Vec<_>>()
768                        .join(", ")
769                ))
770            }
771            vibesql_catalog::CatalogError::TypeAlreadyExists(name) => {
772                ExecutorError::TypeAlreadyExists(name)
773            }
774            vibesql_catalog::CatalogError::TypeNotFound(name) => ExecutorError::TypeNotFound(name),
775            vibesql_catalog::CatalogError::TypeInUse(name) => ExecutorError::TypeInUse(name),
776            vibesql_catalog::CatalogError::CollationAlreadyExists(name) => {
777                ExecutorError::Other(format!("Collation '{}' already exists", name))
778            }
779            vibesql_catalog::CatalogError::CollationNotFound(name) => {
780                ExecutorError::Other(format!("Collation '{}' not found", name))
781            }
782            vibesql_catalog::CatalogError::CharacterSetAlreadyExists(name) => {
783                ExecutorError::Other(format!("Character set '{}' already exists", name))
784            }
785            vibesql_catalog::CatalogError::CharacterSetNotFound(name) => {
786                ExecutorError::Other(format!("Character set '{}' not found", name))
787            }
788            vibesql_catalog::CatalogError::TranslationAlreadyExists(name) => {
789                ExecutorError::Other(format!("Translation '{}' already exists", name))
790            }
791            vibesql_catalog::CatalogError::TranslationNotFound(name) => {
792                ExecutorError::Other(format!("Translation '{}' not found", name))
793            }
794            vibesql_catalog::CatalogError::ViewAlreadyExists(name) => {
795                ExecutorError::Other(format!("View '{}' already exists", name))
796            }
797            vibesql_catalog::CatalogError::ViewNotFound(name) => {
798                ExecutorError::Other(format!("View '{}' not found", name))
799            }
800            vibesql_catalog::CatalogError::ViewInUse { view_name, dependent_views } => {
801                ExecutorError::Other(format!(
802                    "View or table '{}' is still in use by {} view(s): {}",
803                    view_name,
804                    dependent_views.len(),
805                    dependent_views.join(", ")
806                ))
807            }
808            vibesql_catalog::CatalogError::TriggerAlreadyExists(name) => {
809                ExecutorError::TriggerAlreadyExists(name)
810            }
811            vibesql_catalog::CatalogError::TriggerNotFound(name) => {
812                ExecutorError::TriggerNotFound(name)
813            }
814            vibesql_catalog::CatalogError::AssertionAlreadyExists(name) => {
815                ExecutorError::Other(format!("Assertion '{}' already exists", name))
816            }
817            vibesql_catalog::CatalogError::AssertionNotFound(name) => {
818                ExecutorError::Other(format!("Assertion '{}' not found", name))
819            }
820            vibesql_catalog::CatalogError::FunctionAlreadyExists(name) => {
821                ExecutorError::Other(format!("Function '{}' already exists", name))
822            }
823            vibesql_catalog::CatalogError::FunctionNotFound(name) => {
824                ExecutorError::Other(format!("Function '{}' not found", name))
825            }
826            vibesql_catalog::CatalogError::ProcedureAlreadyExists(name) => {
827                ExecutorError::Other(format!("Procedure '{}' already exists", name))
828            }
829            vibesql_catalog::CatalogError::ProcedureNotFound(name) => {
830                ExecutorError::Other(format!("Procedure '{}' not found", name))
831            }
832            vibesql_catalog::CatalogError::ConstraintAlreadyExists(name) => {
833                ExecutorError::ConstraintViolation(format!("Constraint '{}' already exists", name))
834            }
835            vibesql_catalog::CatalogError::ConstraintNotFound(name) => {
836                ExecutorError::ConstraintNotFound {
837                    constraint_name: name,
838                    table_name: "unknown".to_string(),
839                }
840            }
841            vibesql_catalog::CatalogError::IndexAlreadyExists { index_name, table_name } => {
842                ExecutorError::IndexAlreadyExists(format!("{} on table {}", index_name, table_name))
843            }
844            vibesql_catalog::CatalogError::IndexNotFound { index_name, table_name } => {
845                ExecutorError::IndexNotFound(format!("{} on table {}", index_name, table_name))
846            }
847            vibesql_catalog::CatalogError::CircularForeignKey { table_name, message } => {
848                ExecutorError::ConstraintViolation(format!(
849                    "Circular foreign key dependency on table '{}': {}",
850                    table_name, message
851                ))
852            }
853        }
854    }
855}