vibesql_executor/
errors.rs

1#[derive(Debug, Clone, PartialEq)]
2pub enum ExecutorError {
3    TableNotFound(String),
4    TableAlreadyExists(String),
5    /// SQLite system table may not be modified (sqlite_master, sqlite_schema)
6    SqliteSystemTableReadOnly {
7        table_name: String,
8        operation: String,
9    },
10    ColumnNotFound {
11        column_name: String,
12        table_name: String,
13        searched_tables: Vec<String>,
14        available_columns: Vec<String>,
15    },
16    InvalidTableQualifier {
17        qualifier: String,
18        column: String,
19        available_tables: Vec<String>,
20    },
21    /// Ambiguous column name in JOIN query (SQLite-compatible error)
22    /// Raised when an unqualified column reference matches columns in multiple tables
23    AmbiguousColumnName {
24        column_name: String,
25    },
26    ColumnAlreadyExists(String),
27    IndexNotFound(String),
28    IndexAlreadyExists(String),
29    InvalidIndexDefinition(String),
30    TriggerNotFound(String),
31    TriggerAlreadyExists(String),
32    SchemaNotFound(String),
33    SchemaAlreadyExists(String),
34    SchemaNotEmpty(String),
35    RoleNotFound(String),
36    TypeNotFound(String),
37    TypeAlreadyExists(String),
38    TypeInUse(String),
39    DependentPrivilegesExist(String),
40    PermissionDenied {
41        role: String,
42        privilege: String,
43        object: String,
44    },
45    ColumnIndexOutOfBounds {
46        index: usize,
47    },
48    TypeMismatch {
49        left: vibesql_types::SqlValue,
50        op: String,
51        right: vibesql_types::SqlValue,
52    },
53    DivisionByZero,
54    /// Integer overflow in arithmetic operation (SQLite-compatible error)
55    /// Format: "integer overflow"
56    IntegerOverflow,
57    InvalidWhereClause(String),
58    /// Wrong number of arguments to a function (SQLite-compatible error)
59    WrongNumberOfArguments {
60        function_name: String,
61    },
62    /// Misuse of aggregate function (SQLite-compatible error)
63    /// e.g., aggregate in WHERE clause or nested aggregates
64    /// Format: "misuse of aggregate function X()"
65    MisuseOfAggregate {
66        function_name: String,
67    },
68    /// Misuse of aggregate in ORDER BY or other execution contexts (SQLite-compatible)
69    /// Format: "misuse of aggregate: X()" (note the colon, not "function")
70    MisuseOfAggregateContext {
71        function_name: String,
72    },
73    /// Misuse of aliased aggregate (SQLite-compatible error)
74    /// e.g., HAVING max(alias) where alias refers to an aggregate like min(x)
75    MisuseOfAliasedAggregate {
76        alias_name: String,
77    },
78    /// Misuse of window function (SQLite-compatible error)
79    /// e.g., window function in WHERE, GROUP BY, or HAVING clause
80    /// Format: "misuse of window function X()"
81    MisuseOfWindowFunction {
82        function_name: String,
83    },
84    UnsupportedExpression(String),
85    UnsupportedFeature(String),
86    /// SQLite-compatible error message (output as-is without prefix)
87    /// Used when we need to match SQLite's exact error message format
88    SqliteCompatError(String),
89    StorageError(String),
90    SubqueryReturnedMultipleRows {
91        expected: usize,
92        actual: usize,
93    },
94    SubqueryColumnCountMismatch {
95        expected: usize,
96        actual: usize,
97    },
98    /// Set operation (UNION, INTERSECT, EXCEPT) column count mismatch (SQLite-compatible)
99    /// Format: "SELECTs to the left and right of {op} do not have the same number of result columns"
100    SetOperationColumnMismatch {
101        /// The set operator name: "UNION", "UNION ALL", "INTERSECT", "INTERSECT ALL", "EXCEPT", "EXCEPT ALL"
102        operator: String,
103    },
104    ColumnCountMismatch {
105        expected: usize,
106        provided: usize,
107    },
108    InsertColumnCountMismatch {
109        table_name: String,
110        expected: usize,
111        provided: usize,
112        /// Whether explicit column list was provided in INSERT statement
113        has_explicit_columns: bool,
114    },
115    /// VALUES rows have inconsistent number of terms (SQLite-compatible error)
116    /// Format: "all VALUES must have the same number of terms"
117    ValuesRowCountMismatch,
118    /// Column not found in INSERT statement (SQLite-compatible error)
119    /// Format: "table T has no column named C"
120    InsertNoSuchColumn {
121        table_name: String,
122        column_name: String,
123    },
124    /// Cannot INSERT into a generated column (SQLite-compatible error)
125    /// Format: "cannot INSERT into generated column \"C\""
126    CannotInsertIntoGeneratedColumn {
127        column_name: String,
128    },
129    CastError {
130        from_type: String,
131        to_type: String,
132    },
133    TypeConversionError {
134        from: String,
135        to: String,
136    },
137    ConstraintViolation(String),
138    MultiplePrimaryKeys,
139    CannotDropColumn(String),
140    ConstraintNotFound {
141        constraint_name: String,
142        table_name: String,
143    },
144    /// Expression evaluation exceeded maximum recursion depth
145    /// This prevents stack overflow from deeply nested expressions or subqueries
146    ExpressionDepthExceeded {
147        depth: usize,
148        max_depth: usize,
149    },
150    /// Query exceeded maximum execution time
151    QueryTimeoutExceeded {
152        elapsed_seconds: u64,
153        max_seconds: u64,
154    },
155    /// Query exceeded maximum row processing limit
156    RowLimitExceeded {
157        rows_processed: usize,
158        max_rows: usize,
159    },
160    /// Query exceeded maximum memory limit
161    MemoryLimitExceeded {
162        used_bytes: usize,
163        max_bytes: usize,
164    },
165    /// Join exceeded maximum number of tables (SQLite-compatible error)
166    /// Format: "at most 64 tables in a join"
167    JoinTableLimitExceeded {
168        table_count: usize,
169        max_tables: usize,
170    },
171    /// Variable not found in procedural context (with available variables)
172    VariableNotFound {
173        variable_name: String,
174        available_variables: Vec<String>,
175    },
176    /// Label not found in procedural context
177    LabelNotFound(String),
178    /// Procedural SELECT INTO returned wrong number of rows (must be exactly 1)
179    SelectIntoRowCount {
180        expected: usize,
181        actual: usize,
182    },
183    /// Procedural SELECT INTO column count doesn't match variable count
184    SelectIntoColumnCount {
185        expected: usize,
186        actual: usize,
187    },
188    /// Procedure not found (with suggestions)
189    ProcedureNotFound {
190        procedure_name: String,
191        schema_name: String,
192        available_procedures: Vec<String>,
193    },
194    /// Function not found (with suggestions)
195    FunctionNotFound {
196        function_name: String,
197        schema_name: String,
198        available_functions: Vec<String>,
199    },
200    /// Parameter count mismatch with details
201    ParameterCountMismatch {
202        routine_name: String,
203        routine_type: String, // "Procedure" or "Function"
204        expected: usize,
205        actual: usize,
206        parameter_signature: String,
207    },
208    /// Parameter type mismatch with details
209    ParameterTypeMismatch {
210        parameter_name: String,
211        expected_type: String,
212        actual_type: String,
213        actual_value: String,
214    },
215    /// Type error in expression evaluation
216    TypeError(String),
217    /// Function argument count mismatch
218    ArgumentCountMismatch {
219        expected: usize,
220        actual: usize,
221    },
222    /// Recursion limit exceeded in function/procedure calls (with call stack)
223    RecursionLimitExceeded {
224        message: String,
225        call_stack: Vec<String>,
226        max_depth: usize,
227    },
228    /// Function must return a value but did not
229    FunctionMustReturn,
230    /// Invalid control flow (e.g., LEAVE/ITERATE outside of loop)
231    InvalidControlFlow(String),
232    /// Invalid function body syntax
233    InvalidFunctionBody(String),
234    /// Function attempted to modify data (read-only violation)
235    FunctionReadOnlyViolation(String),
236    /// Parse error
237    ParseError(String),
238    /// Invalid EXTRACT field for the given value type
239    InvalidExtractField {
240        field: String,
241        value_type: String,
242    },
243    /// Arrow array downcast failed (columnar execution)
244    ArrowDowncastError {
245        expected_type: String,
246        context: String,
247    },
248    /// Type mismatch in columnar operations
249    ColumnarTypeMismatch {
250        operation: String,
251        left_type: String,
252        right_type: Option<String>,
253    },
254    /// SIMD operation failed (returned None or error)
255    SimdOperationFailed {
256        operation: String,
257        reason: String,
258    },
259    /// Column not found in batch by index
260    ColumnarColumnNotFound {
261        column_index: usize,
262        batch_columns: usize,
263    },
264    /// Column not found in batch by name
265    ColumnarColumnNotFoundByName {
266        column_name: String,
267    },
268    /// Column length mismatch in batch operations
269    ColumnarLengthMismatch {
270        context: String,
271        expected: usize,
272        actual: usize,
273    },
274    /// Unsupported array type for columnar operation
275    UnsupportedArrayType {
276        operation: String,
277        array_type: String,
278    },
279    /// Invalid or incompatible geometry type in spatial function
280    SpatialGeometryError {
281        function_name: String,
282        message: String,
283    },
284    /// Spatial operation failed (distance, intersection, etc.)
285    SpatialOperationFailed {
286        function_name: String,
287        message: String,
288    },
289    /// Spatial function argument count or type mismatch
290    SpatialArgumentError {
291        function_name: String,
292        expected: String,
293        actual: String,
294    },
295    /// Cursor already exists with this name
296    CursorAlreadyExists(String),
297    /// Cursor not found
298    CursorNotFound(String),
299    /// Cursor is already open
300    CursorAlreadyOpen(String),
301    /// Cursor is not open (must OPEN before FETCH)
302    CursorNotOpen(String),
303    /// Cursor does not support backward movement (not declared with SCROLL)
304    CursorNotScrollable(String),
305    /// Assertion constraint violation (SQL:1999 Feature F671/F672)
306    AssertionViolation {
307        assertion_name: String,
308    },
309    /// ORDER BY column number out of range (SQLite-compatible error)
310    OrderByOutOfRange {
311        term_position: usize, // 1-indexed position of the ORDER BY term
312        column_number: i64,   // The column number that was specified
313        select_list_len: usize,
314    },
315    /// GROUP BY column number out of range (SQLite-compatible error)
316    GroupByOutOfRange {
317        term_position: usize, // 1-indexed position of the GROUP BY term
318        column_number: i64,   // The column number that was specified
319        select_list_len: usize,
320    },
321    /// ORDER BY term doesn't match any column in the result set
322    /// Used for compound queries (UNION, INTERSECT, EXCEPT) where ORDER BY
323    /// must reference result columns by position or alias
324    OrderByTermNotInResultSet {
325        term_position: usize, // 1-indexed position of the ORDER BY term
326    },
327    /// Invalid LIMIT or OFFSET value (SQLite-compatible error)
328    InvalidLimitOffset {
329        clause: String, // "LIMIT" or "OFFSET"
330        value: String,  // The value that was provided
331        reason: String, // Why it's invalid
332    },
333    /// JOIN USING column not present in both tables (SQLite-compatible error)
334    /// Format: "cannot join using column X - column not present in both tables"
335    JoinUsingColumnNotPresent {
336        column_name: String,
337    },
338    /// ON clause references a table that appears to the right (SQLite-compatible error)
339    /// Format: "ON clause references tables to its right"
340    /// This error occurs when a JOIN ON clause references a table that hasn't been
341    /// introduced yet (appears later in the FROM clause).
342    OnClauseReferencesRightTable,
343    /// No such column (SQLite-compatible error)
344    /// Format: "no such column: X" or "no such column: table.column"
345    NoSuchColumn {
346        column_ref: String,
347    },
348    /// No such function (SQLite-compatible error)
349    /// Format: "no such function: X"
350    NoSuchFunction {
351        function_name: String,
352    },
353    Other(String),
354}
355
356/// Find the closest matching string using simple Levenshtein distance
357fn find_closest_match<'a>(target: &str, candidates: &'a [String]) -> Option<&'a String> {
358    if candidates.is_empty() {
359        return None;
360    }
361
362    let target_lower = target.to_lowercase();
363
364    // First check for exact case-insensitive match
365    if let Some(exact) = candidates.iter().find(|c| c.to_lowercase() == target_lower) {
366        return Some(exact);
367    }
368
369    // Calculate Levenshtein distance for each candidate
370    let mut best_match: Option<(&String, usize)> = None;
371
372    for candidate in candidates {
373        let distance = levenshtein_distance(&target_lower, &candidate.to_lowercase());
374
375        // Only suggest if distance is small relative to the target length
376        // (e.g., within 2 edits or 30% of the length)
377        let max_distance = (target.len() / 3).max(2);
378
379        if distance <= max_distance {
380            if let Some((_, best_distance)) = best_match {
381                if distance < best_distance {
382                    best_match = Some((candidate, distance));
383                }
384            } else {
385                best_match = Some((candidate, distance));
386            }
387        }
388    }
389
390    best_match.map(|(s, _)| s)
391}
392
393/// Calculate Levenshtein distance between two strings
394fn levenshtein_distance(s1: &str, s2: &str) -> usize {
395    let len1 = s1.len();
396    let len2 = s2.len();
397
398    if len1 == 0 {
399        return len2;
400    }
401    if len2 == 0 {
402        return len1;
403    }
404
405    let mut prev_row: Vec<usize> = (0..=len2).collect();
406    let mut curr_row = vec![0; len2 + 1];
407
408    for (i, c1) in s1.chars().enumerate() {
409        curr_row[0] = i + 1;
410
411        for (j, c2) in s2.chars().enumerate() {
412            let cost = if c1 == c2 { 0 } else { 1 };
413            curr_row[j + 1] = (curr_row[j] + 1).min(prev_row[j + 1] + 1).min(prev_row[j] + cost);
414        }
415
416        std::mem::swap(&mut prev_row, &mut curr_row);
417    }
418
419    prev_row[len2]
420}
421
422impl std::fmt::Display for ExecutorError {
423    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
424        use vibesql_l10n::vibe_msg;
425        match self {
426            ExecutorError::TableNotFound(name) => {
427                write!(f, "{}", vibe_msg!("executor-table-not-found", name = name.as_str()))
428            }
429            ExecutorError::TableAlreadyExists(name) => {
430                write!(f, "{}", vibe_msg!("executor-table-already-exists", name = name.as_str()))
431            }
432            ExecutorError::SqliteSystemTableReadOnly { table_name, operation } => {
433                // SQLite-compatible error message format
434                write!(f, "table {} may not be {}", table_name, operation)
435            }
436            ExecutorError::ColumnNotFound {
437                column_name,
438                table_name,
439                searched_tables,
440                available_columns,
441            } => {
442                if searched_tables.is_empty() {
443                    write!(
444                        f,
445                        "{}",
446                        vibe_msg!(
447                            "executor-column-not-found-simple",
448                            column_name = column_name.as_str(),
449                            table_name = table_name.as_str()
450                        )
451                    )
452                } else if available_columns.is_empty() {
453                    let searched = searched_tables.join(", ");
454                    write!(
455                        f,
456                        "{}",
457                        vibe_msg!(
458                            "executor-column-not-found-searched",
459                            column_name = column_name.as_str(),
460                            searched_tables = searched.as_str()
461                        )
462                    )
463                } else {
464                    let searched = searched_tables.join(", ");
465                    let available = available_columns.join(", ");
466                    write!(
467                        f,
468                        "{}",
469                        vibe_msg!(
470                            "executor-column-not-found-with-available",
471                            column_name = column_name.as_str(),
472                            searched_tables = searched.as_str(),
473                            available_columns = available.as_str()
474                        )
475                    )
476                }
477            }
478            ExecutorError::InvalidTableQualifier { qualifier, column, available_tables } => {
479                let available = available_tables.join(", ");
480                write!(
481                    f,
482                    "{}",
483                    vibe_msg!(
484                        "executor-invalid-table-qualifier",
485                        qualifier = qualifier.as_str(),
486                        column = column.as_str(),
487                        available_tables = available.as_str()
488                    )
489                )
490            }
491            ExecutorError::AmbiguousColumnName { column_name } => {
492                // SQLite-compatible error message format
493                write!(f, "ambiguous column name: {}", column_name)
494            }
495            ExecutorError::ColumnAlreadyExists(name) => {
496                write!(f, "{}", vibe_msg!("executor-column-already-exists", name = name.as_str()))
497            }
498            ExecutorError::IndexNotFound(name) => {
499                write!(f, "{}", vibe_msg!("executor-index-not-found", name = name.as_str()))
500            }
501            ExecutorError::IndexAlreadyExists(name) => {
502                write!(f, "{}", vibe_msg!("executor-index-already-exists", name = name.as_str()))
503            }
504            ExecutorError::InvalidIndexDefinition(msg) => {
505                write!(
506                    f,
507                    "{}",
508                    vibe_msg!("executor-invalid-index-definition", message = msg.as_str())
509                )
510            }
511            ExecutorError::TriggerNotFound(name) => {
512                write!(f, "{}", vibe_msg!("executor-trigger-not-found", name = name.as_str()))
513            }
514            ExecutorError::TriggerAlreadyExists(name) => {
515                write!(f, "{}", vibe_msg!("executor-trigger-already-exists", name = name.as_str()))
516            }
517            ExecutorError::SchemaNotFound(name) => {
518                write!(f, "{}", vibe_msg!("executor-schema-not-found", name = name.as_str()))
519            }
520            ExecutorError::SchemaAlreadyExists(name) => {
521                write!(f, "{}", vibe_msg!("executor-schema-already-exists", name = name.as_str()))
522            }
523            ExecutorError::SchemaNotEmpty(name) => {
524                write!(f, "{}", vibe_msg!("executor-schema-not-empty", name = name.as_str()))
525            }
526            ExecutorError::RoleNotFound(name) => {
527                write!(f, "{}", vibe_msg!("executor-role-not-found", name = name.as_str()))
528            }
529            ExecutorError::TypeNotFound(name) => {
530                write!(f, "{}", vibe_msg!("executor-type-not-found", name = name.as_str()))
531            }
532            ExecutorError::TypeAlreadyExists(name) => {
533                write!(f, "{}", vibe_msg!("executor-type-already-exists", name = name.as_str()))
534            }
535            ExecutorError::TypeInUse(name) => {
536                write!(f, "{}", vibe_msg!("executor-type-in-use", name = name.as_str()))
537            }
538            ExecutorError::DependentPrivilegesExist(msg) => {
539                write!(
540                    f,
541                    "{}",
542                    vibe_msg!("executor-dependent-privileges-exist", message = msg.as_str())
543                )
544            }
545            ExecutorError::PermissionDenied { role, privilege, object } => {
546                write!(
547                    f,
548                    "{}",
549                    vibe_msg!(
550                        "executor-permission-denied",
551                        role = role.as_str(),
552                        privilege = privilege.as_str(),
553                        object = object.as_str()
554                    )
555                )
556            }
557            ExecutorError::ColumnIndexOutOfBounds { index } => {
558                write!(
559                    f,
560                    "{}",
561                    vibe_msg!("executor-column-index-out-of-bounds", index = *index as i64)
562                )
563            }
564            ExecutorError::TypeMismatch { left, op, right } => {
565                let left_str = format!("{:?}", left);
566                let right_str = format!("{:?}", right);
567                write!(
568                    f,
569                    "{}",
570                    vibe_msg!(
571                        "executor-type-mismatch",
572                        left = left_str.as_str(),
573                        op = op.as_str(),
574                        right = right_str.as_str()
575                    )
576                )
577            }
578            ExecutorError::DivisionByZero => {
579                write!(f, "{}", vibe_msg!("executor-division-by-zero"))
580            }
581            ExecutorError::IntegerOverflow => {
582                // SQLite-compatible error message format
583                write!(f, "integer overflow")
584            }
585            ExecutorError::InvalidWhereClause(msg) => {
586                write!(f, "{}", vibe_msg!("executor-invalid-where-clause", message = msg.as_str()))
587            }
588            ExecutorError::WrongNumberOfArguments { function_name } => {
589                // SQLite-compatible error message format
590                write!(f, "wrong number of arguments to function {}()", function_name)
591            }
592            ExecutorError::MisuseOfAggregate { function_name } => {
593                // SQLite-compatible error message format
594                write!(f, "misuse of aggregate function {}()", function_name)
595            }
596            ExecutorError::MisuseOfAggregateContext { function_name } => {
597                // SQLite-compatible error message format for ORDER BY and execution contexts
598                // Note: uses colon instead of "function" to match SQLite's expr.c format
599                write!(f, "misuse of aggregate: {}()", function_name)
600            }
601            ExecutorError::MisuseOfAliasedAggregate { alias_name } => {
602                // SQLite-compatible error message format
603                write!(f, "misuse of aliased aggregate {}", alias_name)
604            }
605            ExecutorError::MisuseOfWindowFunction { function_name } => {
606                // SQLite-compatible error message format
607                write!(f, "misuse of window function {}()", function_name)
608            }
609            ExecutorError::UnsupportedExpression(msg) => {
610                write!(
611                    f,
612                    "{}",
613                    vibe_msg!("executor-unsupported-expression", message = msg.as_str())
614                )
615            }
616            ExecutorError::UnsupportedFeature(msg) => {
617                write!(f, "{}", vibe_msg!("executor-unsupported-feature", message = msg.as_str()))
618            }
619            ExecutorError::SqliteCompatError(msg) => {
620                // Output the message as-is without any prefix (for SQLite compatibility)
621                write!(f, "{}", msg)
622            }
623            ExecutorError::StorageError(msg) => {
624                write!(f, "{}", vibe_msg!("executor-storage-error", message = msg.as_str()))
625            }
626            ExecutorError::SubqueryReturnedMultipleRows { expected, actual } => {
627                write!(
628                    f,
629                    "{}",
630                    vibe_msg!(
631                        "executor-subquery-returned-multiple-rows",
632                        expected = *expected as i64,
633                        actual = *actual as i64
634                    )
635                )
636            }
637            ExecutorError::SubqueryColumnCountMismatch { expected, actual } => {
638                write!(
639                    f,
640                    "{}",
641                    vibe_msg!(
642                        "executor-subquery-column-count-mismatch",
643                        expected = *expected as i64,
644                        actual = *actual as i64
645                    )
646                )
647            }
648            ExecutorError::SetOperationColumnMismatch { operator } => {
649                write!(
650                    f,
651                    "SELECTs to the left and right of {} do not have the same number of result columns",
652                    operator
653                )
654            }
655            ExecutorError::ColumnCountMismatch { expected, provided } => {
656                write!(
657                    f,
658                    "{}",
659                    vibe_msg!(
660                        "executor-column-count-mismatch",
661                        expected = *expected as i64,
662                        provided = *provided as i64
663                    )
664                )
665            }
666            ExecutorError::InsertColumnCountMismatch { table_name, expected, provided, has_explicit_columns } => {
667                if *has_explicit_columns {
668                    // SQLite format when explicit column list: "M values for N columns"
669                    write!(f, "{} values for {} columns", provided, expected)
670                } else {
671                    // SQLite format when no column list: "table T has N columns but M values were supplied"
672                    write!(f, "table {} has {} columns but {} values were supplied", table_name, expected, provided)
673                }
674            }
675            ExecutorError::ValuesRowCountMismatch => {
676                // SQLite format for inconsistent VALUES row lengths
677                write!(f, "all VALUES must have the same number of terms")
678            }
679            ExecutorError::InsertNoSuchColumn { table_name, column_name } => {
680                // SQLite format: "table T has no column named C"
681                write!(f, "table {} has no column named {}", table_name, column_name)
682            }
683            ExecutorError::CannotInsertIntoGeneratedColumn { column_name } => {
684                // SQLite format: cannot INSERT into generated column "C"
685                write!(f, "cannot INSERT into generated column \"{}\"", column_name)
686            }
687            ExecutorError::CastError { from_type, to_type } => {
688                write!(
689                    f,
690                    "{}",
691                    vibe_msg!(
692                        "executor-cast-error",
693                        from_type = from_type.as_str(),
694                        to_type = to_type.as_str()
695                    )
696                )
697            }
698            ExecutorError::TypeConversionError { from, to } => {
699                write!(
700                    f,
701                    "{}",
702                    vibe_msg!(
703                        "executor-type-conversion-error",
704                        from = from.as_str(),
705                        to = to.as_str()
706                    )
707                )
708            }
709            ExecutorError::ConstraintViolation(msg) => {
710                write!(f, "{}", vibe_msg!("executor-constraint-violation", message = msg.as_str()))
711            }
712            ExecutorError::MultiplePrimaryKeys => {
713                write!(f, "{}", vibe_msg!("executor-multiple-primary-keys"))
714            }
715            ExecutorError::CannotDropColumn(msg) => {
716                write!(f, "{}", vibe_msg!("executor-cannot-drop-column", message = msg.as_str()))
717            }
718            ExecutorError::ConstraintNotFound { constraint_name, table_name } => {
719                write!(
720                    f,
721                    "{}",
722                    vibe_msg!(
723                        "executor-constraint-not-found",
724                        constraint_name = constraint_name.as_str(),
725                        table_name = table_name.as_str()
726                    )
727                )
728            }
729            ExecutorError::ExpressionDepthExceeded { depth, max_depth } => {
730                write!(
731                    f,
732                    "{}",
733                    vibe_msg!(
734                        "executor-expression-depth-exceeded",
735                        depth = *depth as i64,
736                        max_depth = *max_depth as i64
737                    )
738                )
739            }
740            ExecutorError::QueryTimeoutExceeded { elapsed_seconds, max_seconds } => {
741                write!(
742                    f,
743                    "{}",
744                    vibe_msg!(
745                        "executor-query-timeout-exceeded",
746                        elapsed_seconds = *elapsed_seconds as i64,
747                        max_seconds = *max_seconds as i64
748                    )
749                )
750            }
751            ExecutorError::RowLimitExceeded { rows_processed, max_rows } => {
752                write!(
753                    f,
754                    "{}",
755                    vibe_msg!(
756                        "executor-row-limit-exceeded",
757                        rows_processed = *rows_processed as i64,
758                        max_rows = *max_rows as i64
759                    )
760                )
761            }
762            ExecutorError::MemoryLimitExceeded { used_bytes, max_bytes } => {
763                let used_gb = format!("{:.2}", *used_bytes as f64 / 1024.0 / 1024.0 / 1024.0);
764                let max_gb = format!("{:.2}", *max_bytes as f64 / 1024.0 / 1024.0 / 1024.0);
765                write!(
766                    f,
767                    "{}",
768                    vibe_msg!(
769                        "executor-memory-limit-exceeded",
770                        used_gb = used_gb.as_str(),
771                        max_gb = max_gb.as_str()
772                    )
773                )
774            }
775            ExecutorError::JoinTableLimitExceeded { max_tables, .. } => {
776                // SQLite-compatible error message: "at most 64 tables in a join"
777                write!(f, "at most {} tables in a join", max_tables)
778            }
779            ExecutorError::VariableNotFound { variable_name, available_variables } => {
780                if available_variables.is_empty() {
781                    write!(
782                        f,
783                        "{}",
784                        vibe_msg!(
785                            "executor-variable-not-found-simple",
786                            variable_name = variable_name.as_str()
787                        )
788                    )
789                } else {
790                    let available = available_variables.join(", ");
791                    write!(
792                        f,
793                        "{}",
794                        vibe_msg!(
795                            "executor-variable-not-found-with-available",
796                            variable_name = variable_name.as_str(),
797                            available_variables = available.as_str()
798                        )
799                    )
800                }
801            }
802            ExecutorError::LabelNotFound(name) => {
803                write!(f, "{}", vibe_msg!("executor-label-not-found", name = name.as_str()))
804            }
805            ExecutorError::SelectIntoRowCount { expected, actual } => {
806                let plural = if *actual == 1 { "" } else { "s" };
807                write!(
808                    f,
809                    "{}",
810                    vibe_msg!(
811                        "executor-select-into-row-count",
812                        expected = *expected as i64,
813                        actual = *actual as i64,
814                        plural = plural
815                    )
816                )
817            }
818            ExecutorError::SelectIntoColumnCount { expected, actual } => {
819                let expected_plural = if *expected == 1 { "" } else { "s" };
820                let actual_plural = if *actual == 1 { "" } else { "s" };
821                write!(
822                    f,
823                    "{}",
824                    vibe_msg!(
825                        "executor-select-into-column-count",
826                        expected = *expected as i64,
827                        expected_plural = expected_plural,
828                        actual = *actual as i64,
829                        actual_plural = actual_plural
830                    )
831                )
832            }
833            ExecutorError::ProcedureNotFound {
834                procedure_name,
835                schema_name,
836                available_procedures,
837            } => {
838                if available_procedures.is_empty() {
839                    write!(
840                        f,
841                        "{}",
842                        vibe_msg!(
843                            "executor-procedure-not-found-simple",
844                            procedure_name = procedure_name.as_str(),
845                            schema_name = schema_name.as_str()
846                        )
847                    )
848                } else {
849                    let suggestion = find_closest_match(procedure_name, available_procedures);
850                    if let Some(similar) = suggestion {
851                        // Use multi-line format with suggestion
852                        write!(
853                            f,
854                            "{}\nAvailable procedures: {}\nDid you mean '{}'?",
855                            vibe_msg!(
856                                "executor-procedure-not-found-simple",
857                                procedure_name = procedure_name.as_str(),
858                                schema_name = schema_name.as_str()
859                            ),
860                            available_procedures.join(", "),
861                            similar
862                        )
863                    } else {
864                        write!(
865                            f,
866                            "{}\nAvailable procedures: {}",
867                            vibe_msg!(
868                                "executor-procedure-not-found-simple",
869                                procedure_name = procedure_name.as_str(),
870                                schema_name = schema_name.as_str()
871                            ),
872                            available_procedures.join(", ")
873                        )
874                    }
875                }
876            }
877            ExecutorError::FunctionNotFound { function_name, schema_name, available_functions } => {
878                if available_functions.is_empty() {
879                    write!(
880                        f,
881                        "{}",
882                        vibe_msg!(
883                            "executor-function-not-found-simple",
884                            function_name = function_name.as_str(),
885                            schema_name = schema_name.as_str()
886                        )
887                    )
888                } else {
889                    let suggestion = find_closest_match(function_name, available_functions);
890                    if let Some(similar) = suggestion {
891                        write!(
892                            f,
893                            "{}\nAvailable functions: {}\nDid you mean '{}'?",
894                            vibe_msg!(
895                                "executor-function-not-found-simple",
896                                function_name = function_name.as_str(),
897                                schema_name = schema_name.as_str()
898                            ),
899                            available_functions.join(", "),
900                            similar
901                        )
902                    } else {
903                        write!(
904                            f,
905                            "{}\nAvailable functions: {}",
906                            vibe_msg!(
907                                "executor-function-not-found-simple",
908                                function_name = function_name.as_str(),
909                                schema_name = schema_name.as_str()
910                            ),
911                            available_functions.join(", ")
912                        )
913                    }
914                }
915            }
916            ExecutorError::ParameterCountMismatch {
917                routine_name,
918                routine_type,
919                expected,
920                actual,
921                parameter_signature,
922            } => {
923                let expected_plural = if *expected == 1 { "" } else { "s" };
924                let actual_plural = if *actual == 1 { "" } else { "s" };
925                write!(
926                    f,
927                    "{}",
928                    vibe_msg!(
929                        "executor-parameter-count-mismatch",
930                        routine_type = routine_type.as_str(),
931                        routine_name = routine_name.as_str(),
932                        expected = *expected as i64,
933                        expected_plural = expected_plural,
934                        parameter_signature = parameter_signature.as_str(),
935                        actual = *actual as i64,
936                        actual_plural = actual_plural
937                    )
938                )
939            }
940            ExecutorError::ParameterTypeMismatch {
941                parameter_name,
942                expected_type,
943                actual_type,
944                actual_value,
945            } => {
946                write!(
947                    f,
948                    "{}",
949                    vibe_msg!(
950                        "executor-parameter-type-mismatch",
951                        parameter_name = parameter_name.as_str(),
952                        expected_type = expected_type.as_str(),
953                        actual_type = actual_type.as_str(),
954                        actual_value = actual_value.as_str()
955                    )
956                )
957            }
958            ExecutorError::TypeError(msg) => {
959                write!(f, "{}", vibe_msg!("executor-type-error", message = msg.as_str()))
960            }
961            ExecutorError::ArgumentCountMismatch { expected, actual } => {
962                write!(
963                    f,
964                    "{}",
965                    vibe_msg!(
966                        "executor-argument-count-mismatch",
967                        expected = *expected as i64,
968                        actual = *actual as i64
969                    )
970                )
971            }
972            ExecutorError::RecursionLimitExceeded { message, call_stack, max_depth } => {
973                write!(
974                    f,
975                    "{}",
976                    vibe_msg!(
977                        "executor-recursion-limit-exceeded",
978                        max_depth = *max_depth as i64,
979                        message = message.as_str()
980                    )
981                )?;
982                if !call_stack.is_empty() {
983                    write!(f, "\n{}", vibe_msg!("executor-recursion-call-stack"))?;
984                    for call in call_stack {
985                        write!(f, "\n  {}", call)?;
986                    }
987                }
988                Ok(())
989            }
990            ExecutorError::FunctionMustReturn => {
991                write!(f, "{}", vibe_msg!("executor-function-must-return"))
992            }
993            ExecutorError::InvalidControlFlow(msg) => {
994                write!(f, "{}", vibe_msg!("executor-invalid-control-flow", message = msg.as_str()))
995            }
996            ExecutorError::InvalidFunctionBody(msg) => {
997                write!(f, "{}", vibe_msg!("executor-invalid-function-body", message = msg.as_str()))
998            }
999            ExecutorError::FunctionReadOnlyViolation(msg) => {
1000                write!(
1001                    f,
1002                    "{}",
1003                    vibe_msg!("executor-function-read-only-violation", message = msg.as_str())
1004                )
1005            }
1006            ExecutorError::ParseError(msg) => {
1007                write!(f, "{}", vibe_msg!("executor-parse-error", message = msg.as_str()))
1008            }
1009            ExecutorError::InvalidExtractField { field, value_type } => {
1010                write!(
1011                    f,
1012                    "{}",
1013                    vibe_msg!(
1014                        "executor-invalid-extract-field",
1015                        field = field.as_str(),
1016                        value_type = value_type.as_str()
1017                    )
1018                )
1019            }
1020            ExecutorError::ArrowDowncastError { expected_type, context } => {
1021                write!(
1022                    f,
1023                    "{}",
1024                    vibe_msg!(
1025                        "executor-arrow-downcast-error",
1026                        expected_type = expected_type.as_str(),
1027                        context = context.as_str()
1028                    )
1029                )
1030            }
1031            ExecutorError::ColumnarTypeMismatch { operation, left_type, right_type } => {
1032                if let Some(right) = right_type {
1033                    write!(
1034                        f,
1035                        "{}",
1036                        vibe_msg!(
1037                            "executor-columnar-type-mismatch-binary",
1038                            operation = operation.as_str(),
1039                            left_type = left_type.as_str(),
1040                            right_type = right.as_str()
1041                        )
1042                    )
1043                } else {
1044                    write!(
1045                        f,
1046                        "{}",
1047                        vibe_msg!(
1048                            "executor-columnar-type-mismatch-unary",
1049                            operation = operation.as_str(),
1050                            left_type = left_type.as_str()
1051                        )
1052                    )
1053                }
1054            }
1055            ExecutorError::SimdOperationFailed { operation, reason } => {
1056                write!(
1057                    f,
1058                    "{}",
1059                    vibe_msg!(
1060                        "executor-simd-operation-failed",
1061                        operation = operation.as_str(),
1062                        reason = reason.as_str()
1063                    )
1064                )
1065            }
1066            ExecutorError::ColumnarColumnNotFound { column_index, batch_columns } => {
1067                write!(
1068                    f,
1069                    "{}",
1070                    vibe_msg!(
1071                        "executor-columnar-column-not-found",
1072                        column_index = *column_index as i64,
1073                        batch_columns = *batch_columns as i64
1074                    )
1075                )
1076            }
1077            ExecutorError::ColumnarColumnNotFoundByName { column_name } => {
1078                write!(
1079                    f,
1080                    "{}",
1081                    vibe_msg!(
1082                        "executor-columnar-column-not-found-by-name",
1083                        column_name = column_name.as_str()
1084                    )
1085                )
1086            }
1087            ExecutorError::ColumnarLengthMismatch { context, expected, actual } => {
1088                write!(
1089                    f,
1090                    "{}",
1091                    vibe_msg!(
1092                        "executor-columnar-length-mismatch",
1093                        context = context.as_str(),
1094                        expected = *expected as i64,
1095                        actual = *actual as i64
1096                    )
1097                )
1098            }
1099            ExecutorError::UnsupportedArrayType { operation, array_type } => {
1100                write!(
1101                    f,
1102                    "{}",
1103                    vibe_msg!(
1104                        "executor-unsupported-array-type",
1105                        operation = operation.as_str(),
1106                        array_type = array_type.as_str()
1107                    )
1108                )
1109            }
1110            ExecutorError::SpatialGeometryError { function_name, message } => {
1111                write!(
1112                    f,
1113                    "{}",
1114                    vibe_msg!(
1115                        "executor-spatial-geometry-error",
1116                        function_name = function_name.as_str(),
1117                        message = message.as_str()
1118                    )
1119                )
1120            }
1121            ExecutorError::SpatialOperationFailed { function_name, message } => {
1122                write!(
1123                    f,
1124                    "{}",
1125                    vibe_msg!(
1126                        "executor-spatial-operation-failed",
1127                        function_name = function_name.as_str(),
1128                        message = message.as_str()
1129                    )
1130                )
1131            }
1132            ExecutorError::SpatialArgumentError { function_name, expected, actual } => {
1133                write!(
1134                    f,
1135                    "{}",
1136                    vibe_msg!(
1137                        "executor-spatial-argument-error",
1138                        function_name = function_name.as_str(),
1139                        expected = expected.as_str(),
1140                        actual = actual.as_str()
1141                    )
1142                )
1143            }
1144            ExecutorError::CursorAlreadyExists(name) => {
1145                write!(f, "{}", vibe_msg!("executor-cursor-already-exists", name = name.as_str()))
1146            }
1147            ExecutorError::CursorNotFound(name) => {
1148                write!(f, "{}", vibe_msg!("executor-cursor-not-found", name = name.as_str()))
1149            }
1150            ExecutorError::CursorAlreadyOpen(name) => {
1151                write!(f, "{}", vibe_msg!("executor-cursor-already-open", name = name.as_str()))
1152            }
1153            ExecutorError::CursorNotOpen(name) => {
1154                write!(f, "{}", vibe_msg!("executor-cursor-not-open", name = name.as_str()))
1155            }
1156            ExecutorError::CursorNotScrollable(name) => {
1157                write!(f, "{}", vibe_msg!("executor-cursor-not-scrollable", name = name.as_str()))
1158            }
1159            ExecutorError::AssertionViolation { assertion_name } => {
1160                write!(f, "Assertion '{}' violated", assertion_name)
1161            }
1162            ExecutorError::OrderByOutOfRange {
1163                term_position,
1164                column_number: _,
1165                select_list_len,
1166            } => {
1167                // SQLite-compatible error format: "1st ORDER BY term out of range - should be between 1 and N"
1168                let ordinal = match term_position {
1169                    1 => "1st".to_string(),
1170                    2 => "2nd".to_string(),
1171                    3 => "3rd".to_string(),
1172                    n => format!("{}th", n),
1173                };
1174                if *select_list_len == 0 {
1175                    write!(f, "{} ORDER BY term out of range - should be between 1 and 1", ordinal)
1176                } else {
1177                    write!(
1178                        f,
1179                        "{} ORDER BY term out of range - should be between 1 and {}",
1180                        ordinal, select_list_len
1181                    )
1182                }
1183            }
1184            ExecutorError::GroupByOutOfRange {
1185                term_position,
1186                column_number: _,
1187                select_list_len,
1188            } => {
1189                // SQLite-compatible error format: "1st GROUP BY term out of range - should be between 1 and N"
1190                let ordinal = match term_position {
1191                    1 => "1st".to_string(),
1192                    2 => "2nd".to_string(),
1193                    3 => "3rd".to_string(),
1194                    n => format!("{}th", n),
1195                };
1196                if *select_list_len == 0 {
1197                    write!(f, "{} GROUP BY term out of range - should be between 1 and 1", ordinal)
1198                } else {
1199                    write!(
1200                        f,
1201                        "{} GROUP BY term out of range - should be between 1 and {}",
1202                        ordinal, select_list_len
1203                    )
1204                }
1205            }
1206            ExecutorError::OrderByTermNotInResultSet { term_position } => {
1207                // SQLite-compatible error format: "1st ORDER BY term does not match any column in the result set"
1208                let ordinal = match term_position {
1209                    1 => "1st".to_string(),
1210                    2 => "2nd".to_string(),
1211                    3 => "3rd".to_string(),
1212                    n => format!("{}th", n),
1213                };
1214                write!(f, "{} ORDER BY term does not match any column in the result set", ordinal)
1215            }
1216            ExecutorError::InvalidLimitOffset { clause, value, reason } => {
1217                write!(f, "{} value {} {}", clause, value, reason)
1218            }
1219            ExecutorError::JoinUsingColumnNotPresent { column_name } => {
1220                write!(
1221                    f,
1222                    "{}",
1223                    vibe_msg!(
1224                        "executor-join-using-column-not-present",
1225                        column_name = column_name.as_str()
1226                    )
1227                )
1228            }
1229            ExecutorError::OnClauseReferencesRightTable => {
1230                // SQLite-compatible error message format
1231                write!(f, "ON clause references tables to its right")
1232            }
1233            ExecutorError::NoSuchColumn { column_ref } => {
1234                write!(
1235                    f,
1236                    "{}",
1237                    vibe_msg!("executor-no-such-column", column_ref = column_ref.as_str())
1238                )
1239            }
1240            ExecutorError::NoSuchFunction { function_name } => {
1241                write!(
1242                    f,
1243                    "{}",
1244                    vibe_msg!("executor-no-such-function", function_name = function_name.as_str())
1245                )
1246            }
1247            ExecutorError::Other(msg) => {
1248                write!(f, "{}", vibe_msg!("executor-other", message = msg.as_str()))
1249            }
1250        }
1251    }
1252}
1253
1254impl std::error::Error for ExecutorError {}
1255
1256impl From<vibesql_storage::StorageError> for ExecutorError {
1257    fn from(err: vibesql_storage::StorageError) -> Self {
1258        match err {
1259            vibesql_storage::StorageError::TableNotFound(name) => {
1260                ExecutorError::TableNotFound(name)
1261            }
1262            vibesql_storage::StorageError::IndexAlreadyExists(name) => {
1263                ExecutorError::IndexAlreadyExists(name)
1264            }
1265            vibesql_storage::StorageError::IndexNotFound(name) => {
1266                ExecutorError::IndexNotFound(name)
1267            }
1268            vibesql_storage::StorageError::ColumnCountMismatch { expected, actual } => {
1269                ExecutorError::ColumnCountMismatch { expected, provided: actual }
1270            }
1271            vibesql_storage::StorageError::ColumnIndexOutOfBounds { index } => {
1272                ExecutorError::ColumnIndexOutOfBounds { index }
1273            }
1274            vibesql_storage::StorageError::CatalogError(msg) => ExecutorError::StorageError(msg),
1275            vibesql_storage::StorageError::TransactionError(msg) => {
1276                ExecutorError::StorageError(msg)
1277            }
1278            vibesql_storage::StorageError::RowNotFound => {
1279                ExecutorError::StorageError("Row not found".to_string())
1280            }
1281            vibesql_storage::StorageError::NullConstraintViolation { column } => {
1282                ExecutorError::ConstraintViolation(format!(
1283                    "NOT NULL constraint violation: column '{}' cannot be NULL",
1284                    column
1285                ))
1286            }
1287            vibesql_storage::StorageError::TypeMismatch { column, expected, actual } => {
1288                ExecutorError::StorageError(format!(
1289                    "Type mismatch in column '{}': expected {}, got {}",
1290                    column, expected, actual
1291                ))
1292            }
1293            vibesql_storage::StorageError::ColumnNotFound { column_name, table_name } => {
1294                ExecutorError::StorageError(format!(
1295                    "Column '{}' not found in table '{}'",
1296                    column_name, table_name
1297                ))
1298            }
1299            vibesql_storage::StorageError::UniqueConstraintViolation(msg) => {
1300                // SQLite-compatible: output the message as-is without prefix
1301                // Format: "UNIQUE constraint failed: table.column"
1302                ExecutorError::SqliteCompatError(msg)
1303            }
1304            vibesql_storage::StorageError::InvalidIndexColumn(msg) => {
1305                ExecutorError::StorageError(msg)
1306            }
1307            vibesql_storage::StorageError::NotImplemented(msg) => {
1308                ExecutorError::StorageError(format!("Not implemented: {}", msg))
1309            }
1310            vibesql_storage::StorageError::IoError(msg) => {
1311                ExecutorError::StorageError(format!("I/O error: {}", msg))
1312            }
1313            vibesql_storage::StorageError::InvalidPageSize { expected, actual } => {
1314                ExecutorError::StorageError(format!(
1315                    "Invalid page size: expected {}, got {}",
1316                    expected, actual
1317                ))
1318            }
1319            vibesql_storage::StorageError::InvalidPageId(page_id) => {
1320                ExecutorError::StorageError(format!("Invalid page ID: {}", page_id))
1321            }
1322            vibesql_storage::StorageError::LockError(msg) => {
1323                ExecutorError::StorageError(format!("Lock error: {}", msg))
1324            }
1325            vibesql_storage::StorageError::MemoryBudgetExceeded { used, budget } => {
1326                ExecutorError::StorageError(format!(
1327                    "Memory budget exceeded: using {} bytes, budget is {} bytes",
1328                    used, budget
1329                ))
1330            }
1331            vibesql_storage::StorageError::NoIndexToEvict => ExecutorError::StorageError(
1332                "No index available to evict (all indexes are already disk-backed)".to_string(),
1333            ),
1334            vibesql_storage::StorageError::Other(msg) => ExecutorError::StorageError(msg),
1335        }
1336    }
1337}
1338
1339impl From<vibesql_catalog::CatalogError> for ExecutorError {
1340    fn from(err: vibesql_catalog::CatalogError) -> Self {
1341        match err {
1342            vibesql_catalog::CatalogError::TableAlreadyExists(name) => {
1343                ExecutorError::TableAlreadyExists(name)
1344            }
1345            vibesql_catalog::CatalogError::TableNotFound { table_name } => {
1346                ExecutorError::TableNotFound(table_name)
1347            }
1348            vibesql_catalog::CatalogError::ColumnAlreadyExists(name) => {
1349                ExecutorError::ColumnAlreadyExists(name)
1350            }
1351            vibesql_catalog::CatalogError::ColumnNotFound { column_name, table_name } => {
1352                ExecutorError::ColumnNotFound {
1353                    column_name,
1354                    table_name,
1355                    searched_tables: vec![],
1356                    available_columns: vec![],
1357                }
1358            }
1359            vibesql_catalog::CatalogError::SchemaNotFound(name) => {
1360                ExecutorError::SchemaNotFound(name)
1361            }
1362            vibesql_catalog::CatalogError::SchemaAlreadyExists(name) => {
1363                ExecutorError::SchemaAlreadyExists(name)
1364            }
1365            vibesql_catalog::CatalogError::SchemaNotEmpty(name) => {
1366                ExecutorError::SchemaNotEmpty(name)
1367            }
1368            vibesql_catalog::CatalogError::RoleAlreadyExists(name) => {
1369                ExecutorError::StorageError(format!("Role '{}' already exists", name))
1370            }
1371            vibesql_catalog::CatalogError::RoleNotFound(name) => ExecutorError::RoleNotFound(name),
1372            // Advanced SQL:1999 objects
1373            vibesql_catalog::CatalogError::DomainAlreadyExists(name) => {
1374                ExecutorError::Other(format!("Domain '{}' already exists", name))
1375            }
1376            vibesql_catalog::CatalogError::DomainNotFound(name) => {
1377                ExecutorError::Other(format!("Domain '{}' not found", name))
1378            }
1379            vibesql_catalog::CatalogError::DomainInUse { domain_name, dependent_columns } => {
1380                ExecutorError::Other(format!(
1381                    "Domain '{}' is still in use by {} column(s): {}",
1382                    domain_name,
1383                    dependent_columns.len(),
1384                    dependent_columns
1385                        .iter()
1386                        .map(|(t, c)| format!("{}.{}", t, c))
1387                        .collect::<Vec<_>>()
1388                        .join(", ")
1389                ))
1390            }
1391            vibesql_catalog::CatalogError::SequenceAlreadyExists(name) => {
1392                ExecutorError::Other(format!("Sequence '{}' already exists", name))
1393            }
1394            vibesql_catalog::CatalogError::SequenceNotFound(name) => {
1395                ExecutorError::Other(format!("Sequence '{}' not found", name))
1396            }
1397            vibesql_catalog::CatalogError::SequenceInUse { sequence_name, dependent_columns } => {
1398                ExecutorError::Other(format!(
1399                    "Sequence '{}' is still in use by {} column(s): {}",
1400                    sequence_name,
1401                    dependent_columns.len(),
1402                    dependent_columns
1403                        .iter()
1404                        .map(|(t, c)| format!("{}.{}", t, c))
1405                        .collect::<Vec<_>>()
1406                        .join(", ")
1407                ))
1408            }
1409            vibesql_catalog::CatalogError::TypeAlreadyExists(name) => {
1410                ExecutorError::TypeAlreadyExists(name)
1411            }
1412            vibesql_catalog::CatalogError::TypeNotFound(name) => ExecutorError::TypeNotFound(name),
1413            vibesql_catalog::CatalogError::TypeInUse(name) => ExecutorError::TypeInUse(name),
1414            vibesql_catalog::CatalogError::CollationAlreadyExists(name) => {
1415                ExecutorError::Other(format!("Collation '{}' already exists", name))
1416            }
1417            vibesql_catalog::CatalogError::CollationNotFound(name) => {
1418                ExecutorError::Other(format!("Collation '{}' not found", name))
1419            }
1420            vibesql_catalog::CatalogError::CharacterSetAlreadyExists(name) => {
1421                ExecutorError::Other(format!("Character set '{}' already exists", name))
1422            }
1423            vibesql_catalog::CatalogError::CharacterSetNotFound(name) => {
1424                ExecutorError::Other(format!("Character set '{}' not found", name))
1425            }
1426            vibesql_catalog::CatalogError::TranslationAlreadyExists(name) => {
1427                ExecutorError::Other(format!("Translation '{}' already exists", name))
1428            }
1429            vibesql_catalog::CatalogError::TranslationNotFound(name) => {
1430                ExecutorError::Other(format!("Translation '{}' not found", name))
1431            }
1432            vibesql_catalog::CatalogError::ViewAlreadyExists(name) => {
1433                ExecutorError::Other(format!("View '{}' already exists", name))
1434            }
1435            vibesql_catalog::CatalogError::ViewNotFound(name) => {
1436                ExecutorError::Other(format!("View '{}' not found", name))
1437            }
1438            vibesql_catalog::CatalogError::ViewInUse { view_name, dependent_views } => {
1439                ExecutorError::Other(format!(
1440                    "View or table '{}' is still in use by {} view(s): {}",
1441                    view_name,
1442                    dependent_views.len(),
1443                    dependent_views.join(", ")
1444                ))
1445            }
1446            vibesql_catalog::CatalogError::TriggerAlreadyExists(name) => {
1447                ExecutorError::TriggerAlreadyExists(name)
1448            }
1449            vibesql_catalog::CatalogError::TriggerNotFound(name) => {
1450                ExecutorError::TriggerNotFound(name)
1451            }
1452            vibesql_catalog::CatalogError::AssertionAlreadyExists(name) => {
1453                ExecutorError::Other(format!("Assertion '{}' already exists", name))
1454            }
1455            vibesql_catalog::CatalogError::AssertionNotFound(name) => {
1456                ExecutorError::Other(format!("Assertion '{}' not found", name))
1457            }
1458            vibesql_catalog::CatalogError::FunctionAlreadyExists(name) => {
1459                ExecutorError::Other(format!("Function '{}' already exists", name))
1460            }
1461            vibesql_catalog::CatalogError::FunctionNotFound(name) => {
1462                ExecutorError::Other(format!("Function '{}' not found", name))
1463            }
1464            vibesql_catalog::CatalogError::ProcedureAlreadyExists(name) => {
1465                ExecutorError::Other(format!("Procedure '{}' already exists", name))
1466            }
1467            vibesql_catalog::CatalogError::ProcedureNotFound(name) => {
1468                ExecutorError::Other(format!("Procedure '{}' not found", name))
1469            }
1470            vibesql_catalog::CatalogError::ConstraintAlreadyExists(name) => {
1471                ExecutorError::ConstraintViolation(format!("Constraint '{}' already exists", name))
1472            }
1473            vibesql_catalog::CatalogError::ConstraintNotFound(name) => {
1474                ExecutorError::ConstraintNotFound {
1475                    constraint_name: name,
1476                    table_name: "unknown".to_string(),
1477                }
1478            }
1479            vibesql_catalog::CatalogError::IndexAlreadyExists { index_name, table_name } => {
1480                ExecutorError::IndexAlreadyExists(format!("{} on table {}", index_name, table_name))
1481            }
1482            vibesql_catalog::CatalogError::IndexNotFound { index_name, table_name } => {
1483                ExecutorError::IndexNotFound(format!("{} on table {}", index_name, table_name))
1484            }
1485            vibesql_catalog::CatalogError::CircularForeignKey { table_name, message } => {
1486                ExecutorError::ConstraintViolation(format!(
1487                    "Circular foreign key dependency on table '{}': {}",
1488                    table_name, message
1489                ))
1490            }
1491        }
1492    }
1493}