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 { column_name: String },
191 ColumnarLengthMismatch {
193 context: String,
194 expected: usize,
195 actual: usize,
196 },
197 UnsupportedArrayType {
199 operation: String,
200 array_type: String,
201 },
202 SpatialGeometryError {
204 function_name: String,
205 message: String,
206 },
207 SpatialOperationFailed {
209 function_name: String,
210 message: String,
211 },
212 SpatialArgumentError {
214 function_name: String,
215 expected: String,
216 actual: String,
217 },
218 CursorAlreadyExists(String),
220 CursorNotFound(String),
222 CursorAlreadyOpen(String),
224 CursorNotOpen(String),
226 CursorNotScrollable(String),
228 Other(String),
229}
230
231fn find_closest_match<'a>(target: &str, candidates: &'a [String]) -> Option<&'a String> {
233 if candidates.is_empty() {
234 return None;
235 }
236
237 let target_lower = target.to_lowercase();
238
239 if let Some(exact) = candidates.iter().find(|c| c.to_lowercase() == target_lower) {
241 return Some(exact);
242 }
243
244 let mut best_match: Option<(&String, usize)> = None;
246
247 for candidate in candidates {
248 let distance = levenshtein_distance(&target_lower, &candidate.to_lowercase());
249
250 let max_distance = (target.len() / 3).max(2);
253
254 if distance <= max_distance {
255 if let Some((_, best_distance)) = best_match {
256 if distance < best_distance {
257 best_match = Some((candidate, distance));
258 }
259 } else {
260 best_match = Some((candidate, distance));
261 }
262 }
263 }
264
265 best_match.map(|(s, _)| s)
266}
267
268fn levenshtein_distance(s1: &str, s2: &str) -> usize {
270 let len1 = s1.len();
271 let len2 = s2.len();
272
273 if len1 == 0 {
274 return len2;
275 }
276 if len2 == 0 {
277 return len1;
278 }
279
280 let mut prev_row: Vec<usize> = (0..=len2).collect();
281 let mut curr_row = vec![0; len2 + 1];
282
283 for (i, c1) in s1.chars().enumerate() {
284 curr_row[0] = i + 1;
285
286 for (j, c2) in s2.chars().enumerate() {
287 let cost = if c1 == c2 { 0 } else { 1 };
288 curr_row[j + 1] = (curr_row[j] + 1)
289 .min(prev_row[j + 1] + 1)
290 .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) => write!(f, "Invalid index definition: {}", msg),
344 ExecutorError::TriggerNotFound(name) => write!(f, "Trigger '{}' not found", name),
345 ExecutorError::TriggerAlreadyExists(name) => write!(f, "Trigger '{}' already exists", name),
346 ExecutorError::SchemaNotFound(name) => write!(f, "Schema '{}' not found", name),
347 ExecutorError::SchemaAlreadyExists(name) => {
348 write!(f, "Schema '{}' already exists", name)
349 }
350 ExecutorError::SchemaNotEmpty(name) => {
351 write!(f, "Cannot drop schema '{}': schema is not empty", name)
352 }
353 ExecutorError::RoleNotFound(name) => write!(f, "Role '{}' not found", name),
354 ExecutorError::TypeNotFound(name) => write!(f, "Type '{}' not found", name),
355 ExecutorError::TypeAlreadyExists(name) => write!(f, "Type '{}' already exists", name),
356 ExecutorError::TypeInUse(name) => {
357 write!(f, "Cannot drop type '{}': type is still in use", name)
358 }
359 ExecutorError::DependentPrivilegesExist(msg) => {
360 write!(f, "Dependent privileges exist: {}", msg)
361 }
362 ExecutorError::PermissionDenied { role, privilege, object } => {
363 write!(
364 f,
365 "Permission denied: role '{}' lacks {} privilege on {}",
366 role, privilege, object
367 )
368 }
369 ExecutorError::ColumnIndexOutOfBounds { index } => {
370 write!(f, "Column index {} out of bounds", index)
371 }
372 ExecutorError::TypeMismatch { left, op, right } => {
373 write!(f, "Type mismatch: {:?} {} {:?}", left, op, right)
374 }
375 ExecutorError::DivisionByZero => write!(f, "Division by zero"),
376 ExecutorError::InvalidWhereClause(msg) => write!(f, "Invalid WHERE clause: {}", msg),
377 ExecutorError::UnsupportedExpression(msg) => {
378 write!(f, "Unsupported expression: {}", msg)
379 }
380 ExecutorError::UnsupportedFeature(msg) => write!(f, "Unsupported feature: {}", msg),
381 ExecutorError::StorageError(msg) => write!(f, "Storage error: {}", msg),
382 ExecutorError::SubqueryReturnedMultipleRows { expected, actual } => {
383 write!(f, "Scalar subquery returned {} rows, expected {}", actual, expected)
384 }
385 ExecutorError::SubqueryColumnCountMismatch { expected, actual } => {
386 write!(f, "Subquery returned {} columns, expected {}", actual, expected)
387 }
388 ExecutorError::ColumnCountMismatch { expected, provided } => {
389 write!(
390 f,
391 "Derived column list has {} columns but query produces {} columns",
392 provided, expected
393 )
394 }
395 ExecutorError::CastError { from_type, to_type } => {
396 write!(f, "Cannot cast {} to {}", from_type, to_type)
397 }
398 ExecutorError::TypeConversionError { from, to } => {
399 write!(f, "Cannot convert {} to {}", from, to)
400 }
401 ExecutorError::ConstraintViolation(msg) => {
402 write!(f, "Constraint violation: {}", msg)
403 }
404 ExecutorError::MultiplePrimaryKeys => {
405 write!(f, "Multiple PRIMARY KEY constraints are not allowed")
406 }
407 ExecutorError::CannotDropColumn(msg) => {
408 write!(f, "Cannot drop column: {}", msg)
409 }
410 ExecutorError::ConstraintNotFound { constraint_name, table_name } => {
411 write!(f, "Constraint '{}' not found in table '{}'", constraint_name, table_name)
412 }
413 ExecutorError::ExpressionDepthExceeded { depth, max_depth } => {
414 write!(
415 f,
416 "Expression depth limit exceeded: {} > {} (prevents stack overflow)",
417 depth, max_depth
418 )
419 }
420 ExecutorError::QueryTimeoutExceeded { elapsed_seconds, max_seconds } => {
421 write!(f, "Query timeout exceeded: {}s > {}s", elapsed_seconds, max_seconds)
422 }
423 ExecutorError::RowLimitExceeded { rows_processed, max_rows } => {
424 write!(f, "Row processing limit exceeded: {} > {}", rows_processed, max_rows)
425 }
426 ExecutorError::MemoryLimitExceeded { used_bytes, max_bytes } => {
427 write!(
428 f,
429 "Memory limit exceeded: {:.2} GB > {:.2} GB",
430 *used_bytes as f64 / 1024.0 / 1024.0 / 1024.0,
431 *max_bytes as f64 / 1024.0 / 1024.0 / 1024.0
432 )
433 }
434 ExecutorError::VariableNotFound { variable_name, available_variables } => {
435 if available_variables.is_empty() {
436 write!(f, "Variable '{}' not found", variable_name)
437 } else {
438 write!(
439 f,
440 "Variable '{}' not found. Available variables: {}",
441 variable_name,
442 available_variables.join(", ")
443 )
444 }
445 }
446 ExecutorError::LabelNotFound(name) => {
447 write!(f, "Label '{}' not found", name)
448 }
449 ExecutorError::SelectIntoRowCount { expected, actual } => {
450 write!(
451 f,
452 "Procedural SELECT INTO must return exactly {} row, got {} row{}",
453 expected,
454 actual,
455 if *actual == 1 { "" } else { "s" }
456 )
457 }
458 ExecutorError::SelectIntoColumnCount { expected, actual } => {
459 write!(
460 f,
461 "Procedural SELECT INTO column count mismatch: {} variable{} but query returned {} column{}",
462 expected,
463 if *expected == 1 { "" } else { "s" },
464 actual,
465 if *actual == 1 { "" } else { "s" }
466 )
467 }
468 ExecutorError::ProcedureNotFound { procedure_name, schema_name, available_procedures } => {
469 if available_procedures.is_empty() {
470 write!(f, "Procedure '{}' not found in schema '{}'", procedure_name, schema_name)
471 } else {
472 let suggestion = find_closest_match(procedure_name, available_procedures);
474 if let Some(similar) = suggestion {
475 write!(
476 f,
477 "Procedure '{}' not found in schema '{}'\nAvailable procedures: {}\nDid you mean '{}'?",
478 procedure_name,
479 schema_name,
480 available_procedures.join(", "),
481 similar
482 )
483 } else {
484 write!(
485 f,
486 "Procedure '{}' not found in schema '{}'\nAvailable procedures: {}",
487 procedure_name,
488 schema_name,
489 available_procedures.join(", ")
490 )
491 }
492 }
493 }
494 ExecutorError::FunctionNotFound { function_name, schema_name, available_functions } => {
495 if available_functions.is_empty() {
496 write!(f, "Function '{}' not found in schema '{}'", function_name, schema_name)
497 } else {
498 let suggestion = find_closest_match(function_name, available_functions);
499 if let Some(similar) = suggestion {
500 write!(
501 f,
502 "Function '{}' not found in schema '{}'\nAvailable functions: {}\nDid you mean '{}'?",
503 function_name,
504 schema_name,
505 available_functions.join(", "),
506 similar
507 )
508 } else {
509 write!(
510 f,
511 "Function '{}' not found in schema '{}'\nAvailable functions: {}",
512 function_name,
513 schema_name,
514 available_functions.join(", ")
515 )
516 }
517 }
518 }
519 ExecutorError::ParameterCountMismatch { routine_name, routine_type, expected, actual, parameter_signature } => {
520 write!(
521 f,
522 "{} '{}' expects {} parameter{} ({}), got {} argument{}",
523 routine_type,
524 routine_name,
525 expected,
526 if *expected == 1 { "" } else { "s" },
527 parameter_signature,
528 actual,
529 if *actual == 1 { "" } else { "s" }
530 )
531 }
532 ExecutorError::ParameterTypeMismatch { parameter_name, expected_type, actual_type, actual_value } => {
533 write!(
534 f,
535 "Parameter '{}' expects {}, got {} '{}'",
536 parameter_name,
537 expected_type,
538 actual_type,
539 actual_value
540 )
541 }
542 ExecutorError::TypeError(msg) => {
543 write!(f, "Type error: {}", msg)
544 }
545 ExecutorError::ArgumentCountMismatch { expected, actual } => {
546 write!(f, "Argument count mismatch: expected {}, got {}", expected, actual)
547 }
548 ExecutorError::RecursionLimitExceeded { message, call_stack, max_depth } => {
549 write!(f, "Maximum recursion depth ({}) exceeded: {}", max_depth, message)?;
550 if !call_stack.is_empty() {
551 write!(f, "\nCall stack:")?;
552 for call in call_stack {
553 write!(f, "\n {}", call)?;
554 }
555 }
556 Ok(())
557 }
558 ExecutorError::FunctionMustReturn => {
559 write!(f, "Function must return a value")
560 }
561 ExecutorError::InvalidControlFlow(msg) => {
562 write!(f, "Invalid control flow: {}", msg)
563 }
564 ExecutorError::InvalidFunctionBody(msg) => {
565 write!(f, "Invalid function body: {}", msg)
566 }
567 ExecutorError::FunctionReadOnlyViolation(msg) => {
568 write!(f, "Function read-only violation: {}", msg)
569 }
570 ExecutorError::ParseError(msg) => {
571 write!(f, "Parse error: {}", msg)
572 }
573 ExecutorError::InvalidExtractField { field, value_type } => {
574 write!(
575 f,
576 "Cannot extract {} from {} value",
577 field, value_type
578 )
579 }
580 ExecutorError::ArrowDowncastError { expected_type, context } => {
581 write!(
582 f,
583 "Failed to downcast Arrow array to {} ({})",
584 expected_type, context
585 )
586 }
587 ExecutorError::ColumnarTypeMismatch { operation, left_type, right_type } => {
588 if let Some(right) = right_type {
589 write!(
590 f,
591 "Incompatible types for {}: {} vs {}",
592 operation, left_type, right
593 )
594 } else {
595 write!(
596 f,
597 "Incompatible type for {}: {}",
598 operation, left_type
599 )
600 }
601 }
602 ExecutorError::SimdOperationFailed { operation, reason } => {
603 write!(f, "SIMD {} failed: {}", operation, reason)
604 }
605 ExecutorError::ColumnarColumnNotFound { column_index, batch_columns } => {
606 write!(
607 f,
608 "Column index {} out of bounds (batch has {} columns)",
609 column_index, batch_columns
610 )
611 }
612 ExecutorError::ColumnarColumnNotFoundByName { column_name } => {
613 write!(f, "Column not found: {}", column_name)
614 }
615 ExecutorError::ColumnarLengthMismatch { context, expected, actual } => {
616 write!(
617 f,
618 "Column length mismatch in {}: expected {}, got {}",
619 context, expected, actual
620 )
621 }
622 ExecutorError::UnsupportedArrayType { operation, array_type } => {
623 write!(
624 f,
625 "Unsupported array type for {}: {}",
626 operation, array_type
627 )
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) => ExecutorError::TableNotFound(name),
664 vibesql_storage::StorageError::IndexAlreadyExists(name) => {
665 ExecutorError::IndexAlreadyExists(name)
666 }
667 vibesql_storage::StorageError::IndexNotFound(name) => ExecutorError::IndexNotFound(name),
668 vibesql_storage::StorageError::ColumnCountMismatch { expected, actual } => {
669 ExecutorError::ColumnCountMismatch { expected, provided: actual }
670 }
671 vibesql_storage::StorageError::ColumnIndexOutOfBounds { index } => {
672 ExecutorError::ColumnIndexOutOfBounds { index }
673 }
674 vibesql_storage::StorageError::CatalogError(msg) => ExecutorError::StorageError(msg),
675 vibesql_storage::StorageError::TransactionError(msg) => ExecutorError::StorageError(msg),
676 vibesql_storage::StorageError::RowNotFound => {
677 ExecutorError::StorageError("Row not found".to_string())
678 }
679 vibesql_storage::StorageError::NullConstraintViolation { column } => {
680 ExecutorError::ConstraintViolation(format!(
681 "NOT NULL constraint violation: column '{}' cannot be NULL",
682 column
683 ))
684 }
685 vibesql_storage::StorageError::TypeMismatch { column, expected, actual } => {
686 ExecutorError::StorageError(format!(
687 "Type mismatch in column '{}': expected {}, got {}",
688 column, expected, actual
689 ))
690 }
691 vibesql_storage::StorageError::ColumnNotFound { column_name, table_name } => {
692 ExecutorError::StorageError(format!(
693 "Column '{}' not found in table '{}'",
694 column_name, table_name
695 ))
696 }
697 vibesql_storage::StorageError::UniqueConstraintViolation(msg) => {
698 ExecutorError::ConstraintViolation(msg)
699 }
700 vibesql_storage::StorageError::InvalidIndexColumn(msg) => {
701 ExecutorError::StorageError(msg)
702 }
703 vibesql_storage::StorageError::NotImplemented(msg) => {
704 ExecutorError::StorageError(format!("Not implemented: {}", msg))
705 }
706 vibesql_storage::StorageError::IoError(msg) => {
707 ExecutorError::StorageError(format!("I/O error: {}", msg))
708 }
709 vibesql_storage::StorageError::InvalidPageSize { expected, actual } => {
710 ExecutorError::StorageError(format!(
711 "Invalid page size: expected {}, got {}",
712 expected, actual
713 ))
714 }
715 vibesql_storage::StorageError::InvalidPageId(page_id) => {
716 ExecutorError::StorageError(format!("Invalid page ID: {}", page_id))
717 }
718 vibesql_storage::StorageError::LockError(msg) => {
719 ExecutorError::StorageError(format!("Lock error: {}", msg))
720 }
721 vibesql_storage::StorageError::MemoryBudgetExceeded { used, budget } => {
722 ExecutorError::StorageError(format!(
723 "Memory budget exceeded: using {} bytes, budget is {} bytes",
724 used, budget
725 ))
726 }
727 vibesql_storage::StorageError::NoIndexToEvict => {
728 ExecutorError::StorageError("No index available to evict (all indexes are already disk-backed)".to_string())
729 }
730 vibesql_storage::StorageError::Other(msg) => {
731 ExecutorError::StorageError(msg)
732 }
733 }
734 }
735}
736
737impl From<vibesql_catalog::CatalogError> for ExecutorError {
738 fn from(err: vibesql_catalog::CatalogError) -> Self {
739 match err {
740 vibesql_catalog::CatalogError::TableAlreadyExists(name) => {
741 ExecutorError::TableAlreadyExists(name)
742 }
743 vibesql_catalog::CatalogError::TableNotFound { table_name } => {
744 ExecutorError::TableNotFound(table_name)
745 }
746 vibesql_catalog::CatalogError::ColumnAlreadyExists(name) => {
747 ExecutorError::ColumnAlreadyExists(name)
748 }
749 vibesql_catalog::CatalogError::ColumnNotFound {
750 column_name,
751 table_name,
752 } => ExecutorError::ColumnNotFound {
753 column_name,
754 table_name,
755 searched_tables: vec![],
756 available_columns: vec![],
757 },
758 vibesql_catalog::CatalogError::SchemaNotFound(name) => ExecutorError::SchemaNotFound(name),
759 vibesql_catalog::CatalogError::SchemaAlreadyExists(name) => {
760 ExecutorError::SchemaAlreadyExists(name)
761 }
762 vibesql_catalog::CatalogError::SchemaNotEmpty(name) => ExecutorError::SchemaNotEmpty(name),
763 vibesql_catalog::CatalogError::RoleAlreadyExists(name) => {
764 ExecutorError::StorageError(format!("Role '{}' already exists", name))
765 }
766 vibesql_catalog::CatalogError::RoleNotFound(name) => ExecutorError::RoleNotFound(name),
767 vibesql_catalog::CatalogError::DomainAlreadyExists(name) => {
769 ExecutorError::Other(format!("Domain '{}' already exists", name))
770 }
771 vibesql_catalog::CatalogError::DomainNotFound(name) => {
772 ExecutorError::Other(format!("Domain '{}' not found", name))
773 }
774 vibesql_catalog::CatalogError::DomainInUse { domain_name, dependent_columns } => {
775 ExecutorError::Other(format!(
776 "Domain '{}' is still in use by {} column(s): {}",
777 domain_name,
778 dependent_columns.len(),
779 dependent_columns
780 .iter()
781 .map(|(t, c)| format!("{}.{}", t, c))
782 .collect::<Vec<_>>()
783 .join(", ")
784 ))
785 }
786 vibesql_catalog::CatalogError::SequenceAlreadyExists(name) => {
787 ExecutorError::Other(format!("Sequence '{}' already exists", name))
788 }
789 vibesql_catalog::CatalogError::SequenceNotFound(name) => {
790 ExecutorError::Other(format!("Sequence '{}' not found", name))
791 }
792 vibesql_catalog::CatalogError::SequenceInUse { sequence_name, dependent_columns } => {
793 ExecutorError::Other(format!(
794 "Sequence '{}' is still in use by {} column(s): {}",
795 sequence_name,
796 dependent_columns.len(),
797 dependent_columns
798 .iter()
799 .map(|(t, c)| format!("{}.{}", t, c))
800 .collect::<Vec<_>>()
801 .join(", ")
802 ))
803 }
804 vibesql_catalog::CatalogError::TypeAlreadyExists(name) => {
805 ExecutorError::TypeAlreadyExists(name)
806 }
807 vibesql_catalog::CatalogError::TypeNotFound(name) => ExecutorError::TypeNotFound(name),
808 vibesql_catalog::CatalogError::TypeInUse(name) => ExecutorError::TypeInUse(name),
809 vibesql_catalog::CatalogError::CollationAlreadyExists(name) => {
810 ExecutorError::Other(format!("Collation '{}' already exists", name))
811 }
812 vibesql_catalog::CatalogError::CollationNotFound(name) => {
813 ExecutorError::Other(format!("Collation '{}' not found", name))
814 }
815 vibesql_catalog::CatalogError::CharacterSetAlreadyExists(name) => {
816 ExecutorError::Other(format!("Character set '{}' already exists", name))
817 }
818 vibesql_catalog::CatalogError::CharacterSetNotFound(name) => {
819 ExecutorError::Other(format!("Character set '{}' not found", name))
820 }
821 vibesql_catalog::CatalogError::TranslationAlreadyExists(name) => {
822 ExecutorError::Other(format!("Translation '{}' already exists", name))
823 }
824 vibesql_catalog::CatalogError::TranslationNotFound(name) => {
825 ExecutorError::Other(format!("Translation '{}' not found", name))
826 }
827 vibesql_catalog::CatalogError::ViewAlreadyExists(name) => {
828 ExecutorError::Other(format!("View '{}' already exists", name))
829 }
830 vibesql_catalog::CatalogError::ViewNotFound(name) => {
831 ExecutorError::Other(format!("View '{}' not found", name))
832 }
833 vibesql_catalog::CatalogError::ViewInUse { view_name, dependent_views } => {
834 ExecutorError::Other(format!(
835 "View or table '{}' is still in use by {} view(s): {}",
836 view_name,
837 dependent_views.len(),
838 dependent_views.join(", ")
839 ))
840 }
841 vibesql_catalog::CatalogError::TriggerAlreadyExists(name) => {
842 ExecutorError::TriggerAlreadyExists(name)
843 }
844 vibesql_catalog::CatalogError::TriggerNotFound(name) => {
845 ExecutorError::TriggerNotFound(name)
846 }
847 vibesql_catalog::CatalogError::AssertionAlreadyExists(name) => {
848 ExecutorError::Other(format!("Assertion '{}' already exists", name))
849 }
850 vibesql_catalog::CatalogError::AssertionNotFound(name) => {
851 ExecutorError::Other(format!("Assertion '{}' not found", name))
852 }
853 vibesql_catalog::CatalogError::FunctionAlreadyExists(name) => {
854 ExecutorError::Other(format!("Function '{}' already exists", name))
855 }
856 vibesql_catalog::CatalogError::FunctionNotFound(name) => {
857 ExecutorError::Other(format!("Function '{}' not found", name))
858 }
859 vibesql_catalog::CatalogError::ProcedureAlreadyExists(name) => {
860 ExecutorError::Other(format!("Procedure '{}' already exists", name))
861 }
862 vibesql_catalog::CatalogError::ProcedureNotFound(name) => {
863 ExecutorError::Other(format!("Procedure '{}' not found", name))
864 }
865 vibesql_catalog::CatalogError::ConstraintAlreadyExists(name) => {
866 ExecutorError::ConstraintViolation(format!("Constraint '{}' already exists", name))
867 }
868 vibesql_catalog::CatalogError::ConstraintNotFound(name) => ExecutorError::ConstraintNotFound {
869 constraint_name: name,
870 table_name: "unknown".to_string(),
871 },
872 vibesql_catalog::CatalogError::IndexAlreadyExists {
873 index_name,
874 table_name,
875 } => ExecutorError::IndexAlreadyExists(format!("{} on table {}", index_name, table_name)),
876 vibesql_catalog::CatalogError::IndexNotFound {
877 index_name,
878 table_name,
879 } => ExecutorError::IndexNotFound(format!("{} on table {}", index_name, table_name)),
880 vibesql_catalog::CatalogError::CircularForeignKey { table_name, message } => {
881 ExecutorError::ConstraintViolation(format!(
882 "Circular foreign key dependency on table '{}': {}",
883 table_name, message
884 ))
885 }
886 }
887 }
888}