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 ExpressionDepthExceeded {
78 depth: usize,
79 max_depth: usize,
80 },
81 QueryTimeoutExceeded {
83 elapsed_seconds: u64,
84 max_seconds: u64,
85 },
86 RowLimitExceeded {
88 rows_processed: usize,
89 max_rows: usize,
90 },
91 MemoryLimitExceeded {
93 used_bytes: usize,
94 max_bytes: usize,
95 },
96 VariableNotFound {
98 variable_name: String,
99 available_variables: Vec<String>,
100 },
101 LabelNotFound(String),
103 SelectIntoRowCount {
105 expected: usize,
106 actual: usize,
107 },
108 SelectIntoColumnCount {
110 expected: usize,
111 actual: usize,
112 },
113 ProcedureNotFound {
115 procedure_name: String,
116 schema_name: String,
117 available_procedures: Vec<String>,
118 },
119 FunctionNotFound {
121 function_name: String,
122 schema_name: String,
123 available_functions: Vec<String>,
124 },
125 ParameterCountMismatch {
127 routine_name: String,
128 routine_type: String, expected: usize,
130 actual: usize,
131 parameter_signature: String,
132 },
133 ParameterTypeMismatch {
135 parameter_name: String,
136 expected_type: String,
137 actual_type: String,
138 actual_value: String,
139 },
140 TypeError(String),
142 ArgumentCountMismatch {
144 expected: usize,
145 actual: usize,
146 },
147 RecursionLimitExceeded {
149 message: String,
150 call_stack: Vec<String>,
151 max_depth: usize,
152 },
153 FunctionMustReturn,
155 InvalidControlFlow(String),
157 InvalidFunctionBody(String),
159 FunctionReadOnlyViolation(String),
161 ParseError(String),
163 InvalidExtractField {
165 field: String,
166 value_type: String,
167 },
168 ArrowDowncastError {
170 expected_type: String,
171 context: String,
172 },
173 ColumnarTypeMismatch {
175 operation: String,
176 left_type: String,
177 right_type: Option<String>,
178 },
179 SimdOperationFailed {
181 operation: String,
182 reason: String,
183 },
184 ColumnarColumnNotFound {
186 column_index: usize,
187 batch_columns: usize,
188 },
189 ColumnarColumnNotFoundByName {
191 column_name: String,
192 },
193 ColumnarLengthMismatch {
195 context: String,
196 expected: usize,
197 actual: usize,
198 },
199 UnsupportedArrayType {
201 operation: String,
202 array_type: String,
203 },
204 SpatialGeometryError {
206 function_name: String,
207 message: String,
208 },
209 SpatialOperationFailed {
211 function_name: String,
212 message: String,
213 },
214 SpatialArgumentError {
216 function_name: String,
217 expected: String,
218 actual: String,
219 },
220 CursorAlreadyExists(String),
222 CursorNotFound(String),
224 CursorAlreadyOpen(String),
226 CursorNotOpen(String),
228 CursorNotScrollable(String),
230 Other(String),
231}
232
233fn 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 if let Some(exact) = candidates.iter().find(|c| c.to_lowercase() == target_lower) {
243 return Some(exact);
244 }
245
246 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 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
270fn 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 match self {
302 ExecutorError::TableNotFound(name) => write!(f, "Table '{}' not found", name),
303 ExecutorError::TableAlreadyExists(name) => write!(f, "Table '{}' already exists", name),
304 ExecutorError::ColumnNotFound {
305 column_name,
306 table_name,
307 searched_tables,
308 available_columns,
309 } => {
310 if searched_tables.is_empty() {
311 write!(f, "Column '{}' not found in table '{}'", column_name, table_name)
312 } else if available_columns.is_empty() {
313 write!(
314 f,
315 "Column '{}' not found (searched tables: {})",
316 column_name,
317 searched_tables.join(", ")
318 )
319 } else {
320 write!(
321 f,
322 "Column '{}' not found (searched tables: {}). Available columns: {}",
323 column_name,
324 searched_tables.join(", "),
325 available_columns.join(", ")
326 )
327 }
328 }
329 ExecutorError::InvalidTableQualifier { qualifier, column, available_tables } => {
330 write!(
331 f,
332 "Invalid table qualifier '{}' for column '{}'. Available tables: {}",
333 qualifier,
334 column,
335 available_tables.join(", ")
336 )
337 }
338 ExecutorError::ColumnAlreadyExists(name) => {
339 write!(f, "Column '{}' already exists", name)
340 }
341 ExecutorError::IndexNotFound(name) => write!(f, "Index '{}' not found", name),
342 ExecutorError::IndexAlreadyExists(name) => write!(f, "Index '{}' already exists", name),
343 ExecutorError::InvalidIndexDefinition(msg) => {
344 write!(f, "Invalid index definition: {}", msg)
345 }
346 ExecutorError::TriggerNotFound(name) => write!(f, "Trigger '{}' not found", name),
347 ExecutorError::TriggerAlreadyExists(name) => {
348 write!(f, "Trigger '{}' already exists", name)
349 }
350 ExecutorError::SchemaNotFound(name) => write!(f, "Schema '{}' not found", name),
351 ExecutorError::SchemaAlreadyExists(name) => {
352 write!(f, "Schema '{}' already exists", name)
353 }
354 ExecutorError::SchemaNotEmpty(name) => {
355 write!(f, "Cannot drop schema '{}': schema is not empty", name)
356 }
357 ExecutorError::RoleNotFound(name) => write!(f, "Role '{}' not found", name),
358 ExecutorError::TypeNotFound(name) => write!(f, "Type '{}' not found", name),
359 ExecutorError::TypeAlreadyExists(name) => write!(f, "Type '{}' already exists", name),
360 ExecutorError::TypeInUse(name) => {
361 write!(f, "Cannot drop type '{}': type is still in use", name)
362 }
363 ExecutorError::DependentPrivilegesExist(msg) => {
364 write!(f, "Dependent privileges exist: {}", msg)
365 }
366 ExecutorError::PermissionDenied { role, privilege, object } => {
367 write!(
368 f,
369 "Permission denied: role '{}' lacks {} privilege on {}",
370 role, privilege, object
371 )
372 }
373 ExecutorError::ColumnIndexOutOfBounds { index } => {
374 write!(f, "Column index {} out of bounds", index)
375 }
376 ExecutorError::TypeMismatch { left, op, right } => {
377 write!(f, "Type mismatch: {:?} {} {:?}", left, op, right)
378 }
379 ExecutorError::DivisionByZero => write!(f, "Division by zero"),
380 ExecutorError::InvalidWhereClause(msg) => write!(f, "Invalid WHERE clause: {}", msg),
381 ExecutorError::UnsupportedExpression(msg) => {
382 write!(f, "Unsupported expression: {}", msg)
383 }
384 ExecutorError::UnsupportedFeature(msg) => write!(f, "Unsupported feature: {}", msg),
385 ExecutorError::StorageError(msg) => write!(f, "Storage error: {}", msg),
386 ExecutorError::SubqueryReturnedMultipleRows { expected, actual } => {
387 write!(f, "Scalar subquery returned {} rows, expected {}", actual, expected)
388 }
389 ExecutorError::SubqueryColumnCountMismatch { expected, actual } => {
390 write!(f, "Subquery returned {} columns, expected {}", actual, expected)
391 }
392 ExecutorError::ColumnCountMismatch { expected, provided } => {
393 write!(
394 f,
395 "Derived column list has {} columns but query produces {} columns",
396 provided, expected
397 )
398 }
399 ExecutorError::CastError { from_type, to_type } => {
400 write!(f, "Cannot cast {} to {}", from_type, to_type)
401 }
402 ExecutorError::TypeConversionError { from, to } => {
403 write!(f, "Cannot convert {} to {}", from, to)
404 }
405 ExecutorError::ConstraintViolation(msg) => {
406 write!(f, "Constraint violation: {}", msg)
407 }
408 ExecutorError::MultiplePrimaryKeys => {
409 write!(f, "Multiple PRIMARY KEY constraints are not allowed")
410 }
411 ExecutorError::CannotDropColumn(msg) => {
412 write!(f, "Cannot drop column: {}", msg)
413 }
414 ExecutorError::ConstraintNotFound { constraint_name, table_name } => {
415 write!(f, "Constraint '{}' not found in table '{}'", constraint_name, table_name)
416 }
417 ExecutorError::ExpressionDepthExceeded { depth, max_depth } => {
418 write!(
419 f,
420 "Expression depth limit exceeded: {} > {} (prevents stack overflow)",
421 depth, max_depth
422 )
423 }
424 ExecutorError::QueryTimeoutExceeded { elapsed_seconds, max_seconds } => {
425 write!(f, "Query timeout exceeded: {}s > {}s", elapsed_seconds, max_seconds)
426 }
427 ExecutorError::RowLimitExceeded { rows_processed, max_rows } => {
428 write!(f, "Row processing limit exceeded: {} > {}", rows_processed, max_rows)
429 }
430 ExecutorError::MemoryLimitExceeded { used_bytes, max_bytes } => {
431 write!(
432 f,
433 "Memory limit exceeded: {:.2} GB > {:.2} GB",
434 *used_bytes as f64 / 1024.0 / 1024.0 / 1024.0,
435 *max_bytes as f64 / 1024.0 / 1024.0 / 1024.0
436 )
437 }
438 ExecutorError::VariableNotFound { variable_name, available_variables } => {
439 if available_variables.is_empty() {
440 write!(f, "Variable '{}' not found", variable_name)
441 } else {
442 write!(
443 f,
444 "Variable '{}' not found. Available variables: {}",
445 variable_name,
446 available_variables.join(", ")
447 )
448 }
449 }
450 ExecutorError::LabelNotFound(name) => {
451 write!(f, "Label '{}' not found", name)
452 }
453 ExecutorError::SelectIntoRowCount { expected, actual } => {
454 write!(
455 f,
456 "Procedural SELECT INTO must return exactly {} row, got {} row{}",
457 expected,
458 actual,
459 if *actual == 1 { "" } else { "s" }
460 )
461 }
462 ExecutorError::SelectIntoColumnCount { expected, actual } => {
463 write!(
464 f,
465 "Procedural SELECT INTO column count mismatch: {} variable{} but query returned {} column{}",
466 expected,
467 if *expected == 1 { "" } else { "s" },
468 actual,
469 if *actual == 1 { "" } else { "s" }
470 )
471 }
472 ExecutorError::ProcedureNotFound {
473 procedure_name,
474 schema_name,
475 available_procedures,
476 } => {
477 if available_procedures.is_empty() {
478 write!(
479 f,
480 "Procedure '{}' not found in schema '{}'",
481 procedure_name, schema_name
482 )
483 } else {
484 let suggestion = find_closest_match(procedure_name, available_procedures);
486 if let Some(similar) = suggestion {
487 write!(
488 f,
489 "Procedure '{}' not found in schema '{}'\nAvailable procedures: {}\nDid you mean '{}'?",
490 procedure_name,
491 schema_name,
492 available_procedures.join(", "),
493 similar
494 )
495 } else {
496 write!(
497 f,
498 "Procedure '{}' not found in schema '{}'\nAvailable procedures: {}",
499 procedure_name,
500 schema_name,
501 available_procedures.join(", ")
502 )
503 }
504 }
505 }
506 ExecutorError::FunctionNotFound { function_name, schema_name, available_functions } => {
507 if available_functions.is_empty() {
508 write!(f, "Function '{}' not found in schema '{}'", function_name, schema_name)
509 } else {
510 let suggestion = find_closest_match(function_name, available_functions);
511 if let Some(similar) = suggestion {
512 write!(
513 f,
514 "Function '{}' not found in schema '{}'\nAvailable functions: {}\nDid you mean '{}'?",
515 function_name,
516 schema_name,
517 available_functions.join(", "),
518 similar
519 )
520 } else {
521 write!(
522 f,
523 "Function '{}' not found in schema '{}'\nAvailable functions: {}",
524 function_name,
525 schema_name,
526 available_functions.join(", ")
527 )
528 }
529 }
530 }
531 ExecutorError::ParameterCountMismatch {
532 routine_name,
533 routine_type,
534 expected,
535 actual,
536 parameter_signature,
537 } => {
538 write!(
539 f,
540 "{} '{}' expects {} parameter{} ({}), got {} argument{}",
541 routine_type,
542 routine_name,
543 expected,
544 if *expected == 1 { "" } else { "s" },
545 parameter_signature,
546 actual,
547 if *actual == 1 { "" } else { "s" }
548 )
549 }
550 ExecutorError::ParameterTypeMismatch {
551 parameter_name,
552 expected_type,
553 actual_type,
554 actual_value,
555 } => {
556 write!(
557 f,
558 "Parameter '{}' expects {}, got {} '{}'",
559 parameter_name, expected_type, actual_type, actual_value
560 )
561 }
562 ExecutorError::TypeError(msg) => {
563 write!(f, "Type error: {}", msg)
564 }
565 ExecutorError::ArgumentCountMismatch { expected, actual } => {
566 write!(f, "Argument count mismatch: expected {}, got {}", expected, actual)
567 }
568 ExecutorError::RecursionLimitExceeded { message, call_stack, max_depth } => {
569 write!(f, "Maximum recursion depth ({}) exceeded: {}", max_depth, message)?;
570 if !call_stack.is_empty() {
571 write!(f, "\nCall stack:")?;
572 for call in call_stack {
573 write!(f, "\n {}", call)?;
574 }
575 }
576 Ok(())
577 }
578 ExecutorError::FunctionMustReturn => {
579 write!(f, "Function must return a value")
580 }
581 ExecutorError::InvalidControlFlow(msg) => {
582 write!(f, "Invalid control flow: {}", msg)
583 }
584 ExecutorError::InvalidFunctionBody(msg) => {
585 write!(f, "Invalid function body: {}", msg)
586 }
587 ExecutorError::FunctionReadOnlyViolation(msg) => {
588 write!(f, "Function read-only violation: {}", msg)
589 }
590 ExecutorError::ParseError(msg) => {
591 write!(f, "Parse error: {}", msg)
592 }
593 ExecutorError::InvalidExtractField { field, value_type } => {
594 write!(f, "Cannot extract {} from {} value", field, value_type)
595 }
596 ExecutorError::ArrowDowncastError { expected_type, context } => {
597 write!(f, "Failed to downcast Arrow array to {} ({})", expected_type, context)
598 }
599 ExecutorError::ColumnarTypeMismatch { operation, left_type, right_type } => {
600 if let Some(right) = right_type {
601 write!(f, "Incompatible types for {}: {} vs {}", operation, left_type, right)
602 } else {
603 write!(f, "Incompatible type for {}: {}", operation, left_type)
604 }
605 }
606 ExecutorError::SimdOperationFailed { operation, reason } => {
607 write!(f, "SIMD {} failed: {}", operation, reason)
608 }
609 ExecutorError::ColumnarColumnNotFound { column_index, batch_columns } => {
610 write!(
611 f,
612 "Column index {} out of bounds (batch has {} columns)",
613 column_index, batch_columns
614 )
615 }
616 ExecutorError::ColumnarColumnNotFoundByName { column_name } => {
617 write!(f, "Column not found: {}", column_name)
618 }
619 ExecutorError::ColumnarLengthMismatch { context, expected, actual } => {
620 write!(
621 f,
622 "Column length mismatch in {}: expected {}, got {}",
623 context, expected, actual
624 )
625 }
626 ExecutorError::UnsupportedArrayType { operation, array_type } => {
627 write!(f, "Unsupported array type for {}: {}", operation, array_type)
628 }
629 ExecutorError::SpatialGeometryError { function_name, message } => {
630 write!(f, "{}: {}", function_name, message)
631 }
632 ExecutorError::SpatialOperationFailed { function_name, message } => {
633 write!(f, "{}: {}", function_name, message)
634 }
635 ExecutorError::SpatialArgumentError { function_name, expected, actual } => {
636 write!(f, "{} expects {}, got {}", function_name, expected, actual)
637 }
638 ExecutorError::CursorAlreadyExists(name) => {
639 write!(f, "Cursor '{}' already exists", name)
640 }
641 ExecutorError::CursorNotFound(name) => {
642 write!(f, "Cursor '{}' not found", name)
643 }
644 ExecutorError::CursorAlreadyOpen(name) => {
645 write!(f, "Cursor '{}' is already open", name)
646 }
647 ExecutorError::CursorNotOpen(name) => {
648 write!(f, "Cursor '{}' is not open", name)
649 }
650 ExecutorError::CursorNotScrollable(name) => {
651 write!(f, "Cursor '{}' is not scrollable (SCROLL not specified)", name)
652 }
653 ExecutorError::Other(msg) => write!(f, "{}", msg),
654 }
655 }
656}
657
658impl std::error::Error for ExecutorError {}
659
660impl From<vibesql_storage::StorageError> for ExecutorError {
661 fn from(err: vibesql_storage::StorageError) -> Self {
662 match err {
663 vibesql_storage::StorageError::TableNotFound(name) => {
664 ExecutorError::TableNotFound(name)
665 }
666 vibesql_storage::StorageError::IndexAlreadyExists(name) => {
667 ExecutorError::IndexAlreadyExists(name)
668 }
669 vibesql_storage::StorageError::IndexNotFound(name) => {
670 ExecutorError::IndexNotFound(name)
671 }
672 vibesql_storage::StorageError::ColumnCountMismatch { expected, actual } => {
673 ExecutorError::ColumnCountMismatch { expected, provided: actual }
674 }
675 vibesql_storage::StorageError::ColumnIndexOutOfBounds { index } => {
676 ExecutorError::ColumnIndexOutOfBounds { index }
677 }
678 vibesql_storage::StorageError::CatalogError(msg) => ExecutorError::StorageError(msg),
679 vibesql_storage::StorageError::TransactionError(msg) => {
680 ExecutorError::StorageError(msg)
681 }
682 vibesql_storage::StorageError::RowNotFound => {
683 ExecutorError::StorageError("Row not found".to_string())
684 }
685 vibesql_storage::StorageError::NullConstraintViolation { column } => {
686 ExecutorError::ConstraintViolation(format!(
687 "NOT NULL constraint violation: column '{}' cannot be NULL",
688 column
689 ))
690 }
691 vibesql_storage::StorageError::TypeMismatch { column, expected, actual } => {
692 ExecutorError::StorageError(format!(
693 "Type mismatch in column '{}': expected {}, got {}",
694 column, expected, actual
695 ))
696 }
697 vibesql_storage::StorageError::ColumnNotFound { column_name, table_name } => {
698 ExecutorError::StorageError(format!(
699 "Column '{}' not found in table '{}'",
700 column_name, table_name
701 ))
702 }
703 vibesql_storage::StorageError::UniqueConstraintViolation(msg) => {
704 ExecutorError::ConstraintViolation(msg)
705 }
706 vibesql_storage::StorageError::InvalidIndexColumn(msg) => {
707 ExecutorError::StorageError(msg)
708 }
709 vibesql_storage::StorageError::NotImplemented(msg) => {
710 ExecutorError::StorageError(format!("Not implemented: {}", msg))
711 }
712 vibesql_storage::StorageError::IoError(msg) => {
713 ExecutorError::StorageError(format!("I/O error: {}", msg))
714 }
715 vibesql_storage::StorageError::InvalidPageSize { expected, actual } => {
716 ExecutorError::StorageError(format!(
717 "Invalid page size: expected {}, got {}",
718 expected, actual
719 ))
720 }
721 vibesql_storage::StorageError::InvalidPageId(page_id) => {
722 ExecutorError::StorageError(format!("Invalid page ID: {}", page_id))
723 }
724 vibesql_storage::StorageError::LockError(msg) => {
725 ExecutorError::StorageError(format!("Lock error: {}", msg))
726 }
727 vibesql_storage::StorageError::MemoryBudgetExceeded { used, budget } => {
728 ExecutorError::StorageError(format!(
729 "Memory budget exceeded: using {} bytes, budget is {} bytes",
730 used, budget
731 ))
732 }
733 vibesql_storage::StorageError::NoIndexToEvict => ExecutorError::StorageError(
734 "No index available to evict (all indexes are already disk-backed)".to_string(),
735 ),
736 vibesql_storage::StorageError::Other(msg) => ExecutorError::StorageError(msg),
737 }
738 }
739}
740
741impl From<vibesql_catalog::CatalogError> for ExecutorError {
742 fn from(err: vibesql_catalog::CatalogError) -> Self {
743 match err {
744 vibesql_catalog::CatalogError::TableAlreadyExists(name) => {
745 ExecutorError::TableAlreadyExists(name)
746 }
747 vibesql_catalog::CatalogError::TableNotFound { table_name } => {
748 ExecutorError::TableNotFound(table_name)
749 }
750 vibesql_catalog::CatalogError::ColumnAlreadyExists(name) => {
751 ExecutorError::ColumnAlreadyExists(name)
752 }
753 vibesql_catalog::CatalogError::ColumnNotFound { column_name, table_name } => {
754 ExecutorError::ColumnNotFound {
755 column_name,
756 table_name,
757 searched_tables: vec![],
758 available_columns: vec![],
759 }
760 }
761 vibesql_catalog::CatalogError::SchemaNotFound(name) => {
762 ExecutorError::SchemaNotFound(name)
763 }
764 vibesql_catalog::CatalogError::SchemaAlreadyExists(name) => {
765 ExecutorError::SchemaAlreadyExists(name)
766 }
767 vibesql_catalog::CatalogError::SchemaNotEmpty(name) => {
768 ExecutorError::SchemaNotEmpty(name)
769 }
770 vibesql_catalog::CatalogError::RoleAlreadyExists(name) => {
771 ExecutorError::StorageError(format!("Role '{}' already exists", name))
772 }
773 vibesql_catalog::CatalogError::RoleNotFound(name) => ExecutorError::RoleNotFound(name),
774 vibesql_catalog::CatalogError::DomainAlreadyExists(name) => {
776 ExecutorError::Other(format!("Domain '{}' already exists", name))
777 }
778 vibesql_catalog::CatalogError::DomainNotFound(name) => {
779 ExecutorError::Other(format!("Domain '{}' not found", name))
780 }
781 vibesql_catalog::CatalogError::DomainInUse { domain_name, dependent_columns } => {
782 ExecutorError::Other(format!(
783 "Domain '{}' is still in use by {} column(s): {}",
784 domain_name,
785 dependent_columns.len(),
786 dependent_columns
787 .iter()
788 .map(|(t, c)| format!("{}.{}", t, c))
789 .collect::<Vec<_>>()
790 .join(", ")
791 ))
792 }
793 vibesql_catalog::CatalogError::SequenceAlreadyExists(name) => {
794 ExecutorError::Other(format!("Sequence '{}' already exists", name))
795 }
796 vibesql_catalog::CatalogError::SequenceNotFound(name) => {
797 ExecutorError::Other(format!("Sequence '{}' not found", name))
798 }
799 vibesql_catalog::CatalogError::SequenceInUse { sequence_name, dependent_columns } => {
800 ExecutorError::Other(format!(
801 "Sequence '{}' is still in use by {} column(s): {}",
802 sequence_name,
803 dependent_columns.len(),
804 dependent_columns
805 .iter()
806 .map(|(t, c)| format!("{}.{}", t, c))
807 .collect::<Vec<_>>()
808 .join(", ")
809 ))
810 }
811 vibesql_catalog::CatalogError::TypeAlreadyExists(name) => {
812 ExecutorError::TypeAlreadyExists(name)
813 }
814 vibesql_catalog::CatalogError::TypeNotFound(name) => ExecutorError::TypeNotFound(name),
815 vibesql_catalog::CatalogError::TypeInUse(name) => ExecutorError::TypeInUse(name),
816 vibesql_catalog::CatalogError::CollationAlreadyExists(name) => {
817 ExecutorError::Other(format!("Collation '{}' already exists", name))
818 }
819 vibesql_catalog::CatalogError::CollationNotFound(name) => {
820 ExecutorError::Other(format!("Collation '{}' not found", name))
821 }
822 vibesql_catalog::CatalogError::CharacterSetAlreadyExists(name) => {
823 ExecutorError::Other(format!("Character set '{}' already exists", name))
824 }
825 vibesql_catalog::CatalogError::CharacterSetNotFound(name) => {
826 ExecutorError::Other(format!("Character set '{}' not found", name))
827 }
828 vibesql_catalog::CatalogError::TranslationAlreadyExists(name) => {
829 ExecutorError::Other(format!("Translation '{}' already exists", name))
830 }
831 vibesql_catalog::CatalogError::TranslationNotFound(name) => {
832 ExecutorError::Other(format!("Translation '{}' not found", name))
833 }
834 vibesql_catalog::CatalogError::ViewAlreadyExists(name) => {
835 ExecutorError::Other(format!("View '{}' already exists", name))
836 }
837 vibesql_catalog::CatalogError::ViewNotFound(name) => {
838 ExecutorError::Other(format!("View '{}' not found", name))
839 }
840 vibesql_catalog::CatalogError::ViewInUse { view_name, dependent_views } => {
841 ExecutorError::Other(format!(
842 "View or table '{}' is still in use by {} view(s): {}",
843 view_name,
844 dependent_views.len(),
845 dependent_views.join(", ")
846 ))
847 }
848 vibesql_catalog::CatalogError::TriggerAlreadyExists(name) => {
849 ExecutorError::TriggerAlreadyExists(name)
850 }
851 vibesql_catalog::CatalogError::TriggerNotFound(name) => {
852 ExecutorError::TriggerNotFound(name)
853 }
854 vibesql_catalog::CatalogError::AssertionAlreadyExists(name) => {
855 ExecutorError::Other(format!("Assertion '{}' already exists", name))
856 }
857 vibesql_catalog::CatalogError::AssertionNotFound(name) => {
858 ExecutorError::Other(format!("Assertion '{}' not found", name))
859 }
860 vibesql_catalog::CatalogError::FunctionAlreadyExists(name) => {
861 ExecutorError::Other(format!("Function '{}' already exists", name))
862 }
863 vibesql_catalog::CatalogError::FunctionNotFound(name) => {
864 ExecutorError::Other(format!("Function '{}' not found", name))
865 }
866 vibesql_catalog::CatalogError::ProcedureAlreadyExists(name) => {
867 ExecutorError::Other(format!("Procedure '{}' already exists", name))
868 }
869 vibesql_catalog::CatalogError::ProcedureNotFound(name) => {
870 ExecutorError::Other(format!("Procedure '{}' not found", name))
871 }
872 vibesql_catalog::CatalogError::ConstraintAlreadyExists(name) => {
873 ExecutorError::ConstraintViolation(format!("Constraint '{}' already exists", name))
874 }
875 vibesql_catalog::CatalogError::ConstraintNotFound(name) => {
876 ExecutorError::ConstraintNotFound {
877 constraint_name: name,
878 table_name: "unknown".to_string(),
879 }
880 }
881 vibesql_catalog::CatalogError::IndexAlreadyExists { index_name, table_name } => {
882 ExecutorError::IndexAlreadyExists(format!("{} on table {}", index_name, table_name))
883 }
884 vibesql_catalog::CatalogError::IndexNotFound { index_name, table_name } => {
885 ExecutorError::IndexNotFound(format!("{} on table {}", index_name, table_name))
886 }
887 vibesql_catalog::CatalogError::CircularForeignKey { table_name, message } => {
888 ExecutorError::ConstraintViolation(format!(
889 "Circular foreign key dependency on table '{}': {}",
890 table_name, message
891 ))
892 }
893 }
894 }
895}