1#[derive(Debug, Clone, PartialEq)]
2pub enum ExecutorError {
3 TableNotFound(String),
4 TableAlreadyExists(String),
5 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 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 IntegerOverflow,
57 InvalidWhereClause(String),
58 WrongNumberOfArguments {
60 function_name: String,
61 },
62 MisuseOfAggregate {
66 function_name: String,
67 },
68 MisuseOfAggregateContext {
71 function_name: String,
72 },
73 MisuseOfAliasedAggregate {
76 alias_name: String,
77 },
78 MisuseOfWindowFunction {
82 function_name: String,
83 },
84 UnsupportedExpression(String),
85 UnsupportedFeature(String),
86 SqliteCompatError(String),
89 StorageError(String),
90 SubqueryReturnedMultipleRows {
91 expected: usize,
92 actual: usize,
93 },
94 SubqueryColumnCountMismatch {
95 expected: usize,
96 actual: usize,
97 },
98 SetOperationColumnMismatch {
101 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 has_explicit_columns: bool,
114 },
115 ValuesRowCountMismatch,
118 InsertNoSuchColumn {
121 table_name: String,
122 column_name: String,
123 },
124 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 ExpressionDepthExceeded {
147 depth: usize,
148 max_depth: usize,
149 },
150 QueryTimeoutExceeded {
152 elapsed_seconds: u64,
153 max_seconds: u64,
154 },
155 RowLimitExceeded {
157 rows_processed: usize,
158 max_rows: usize,
159 },
160 MemoryLimitExceeded {
162 used_bytes: usize,
163 max_bytes: usize,
164 },
165 JoinTableLimitExceeded {
168 table_count: usize,
169 max_tables: usize,
170 },
171 VariableNotFound {
173 variable_name: String,
174 available_variables: Vec<String>,
175 },
176 LabelNotFound(String),
178 SelectIntoRowCount {
180 expected: usize,
181 actual: usize,
182 },
183 SelectIntoColumnCount {
185 expected: usize,
186 actual: usize,
187 },
188 ProcedureNotFound {
190 procedure_name: String,
191 schema_name: String,
192 available_procedures: Vec<String>,
193 },
194 FunctionNotFound {
196 function_name: String,
197 schema_name: String,
198 available_functions: Vec<String>,
199 },
200 ParameterCountMismatch {
202 routine_name: String,
203 routine_type: String, expected: usize,
205 actual: usize,
206 parameter_signature: String,
207 },
208 ParameterTypeMismatch {
210 parameter_name: String,
211 expected_type: String,
212 actual_type: String,
213 actual_value: String,
214 },
215 TypeError(String),
217 ArgumentCountMismatch {
219 expected: usize,
220 actual: usize,
221 },
222 RecursionLimitExceeded {
224 message: String,
225 call_stack: Vec<String>,
226 max_depth: usize,
227 },
228 FunctionMustReturn,
230 InvalidControlFlow(String),
232 InvalidFunctionBody(String),
234 FunctionReadOnlyViolation(String),
236 ParseError(String),
238 InvalidExtractField {
240 field: String,
241 value_type: String,
242 },
243 ArrowDowncastError {
245 expected_type: String,
246 context: String,
247 },
248 ColumnarTypeMismatch {
250 operation: String,
251 left_type: String,
252 right_type: Option<String>,
253 },
254 SimdOperationFailed {
256 operation: String,
257 reason: String,
258 },
259 ColumnarColumnNotFound {
261 column_index: usize,
262 batch_columns: usize,
263 },
264 ColumnarColumnNotFoundByName {
266 column_name: String,
267 },
268 ColumnarLengthMismatch {
270 context: String,
271 expected: usize,
272 actual: usize,
273 },
274 UnsupportedArrayType {
276 operation: String,
277 array_type: String,
278 },
279 SpatialGeometryError {
281 function_name: String,
282 message: String,
283 },
284 SpatialOperationFailed {
286 function_name: String,
287 message: String,
288 },
289 SpatialArgumentError {
291 function_name: String,
292 expected: String,
293 actual: String,
294 },
295 CursorAlreadyExists(String),
297 CursorNotFound(String),
299 CursorAlreadyOpen(String),
301 CursorNotOpen(String),
303 CursorNotScrollable(String),
305 AssertionViolation {
307 assertion_name: String,
308 },
309 OrderByOutOfRange {
311 term_position: usize, column_number: i64, select_list_len: usize,
314 },
315 GroupByOutOfRange {
317 term_position: usize, column_number: i64, select_list_len: usize,
320 },
321 OrderByTermNotInResultSet {
325 term_position: usize, },
327 InvalidLimitOffset {
329 clause: String, value: String, reason: String, },
333 JoinUsingColumnNotPresent {
336 column_name: String,
337 },
338 OnClauseReferencesRightTable,
343 NoSuchColumn {
346 column_ref: String,
347 },
348 NoSuchFunction {
351 function_name: String,
352 },
353 Other(String),
354}
355
356fn 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 if let Some(exact) = candidates.iter().find(|c| c.to_lowercase() == target_lower) {
366 return Some(exact);
367 }
368
369 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 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
393fn 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 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 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 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 write!(f, "wrong number of arguments to function {}()", function_name)
591 }
592 ExecutorError::MisuseOfAggregate { function_name } => {
593 write!(f, "misuse of aggregate function {}()", function_name)
595 }
596 ExecutorError::MisuseOfAggregateContext { function_name } => {
597 write!(f, "misuse of aggregate: {}()", function_name)
600 }
601 ExecutorError::MisuseOfAliasedAggregate { alias_name } => {
602 write!(f, "misuse of aliased aggregate {}", alias_name)
604 }
605 ExecutorError::MisuseOfWindowFunction { function_name } => {
606 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 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 write!(f, "{} values for {} columns", provided, expected)
670 } else {
671 write!(f, "table {} has {} columns but {} values were supplied", table_name, expected, provided)
673 }
674 }
675 ExecutorError::ValuesRowCountMismatch => {
676 write!(f, "all VALUES must have the same number of terms")
678 }
679 ExecutorError::InsertNoSuchColumn { table_name, column_name } => {
680 write!(f, "table {} has no column named {}", table_name, column_name)
682 }
683 ExecutorError::CannotInsertIntoGeneratedColumn { column_name } => {
684 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 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 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 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 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 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 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 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 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}