1#[cfg(test)]
9mod tests;
10
11use icydb_diagnostic_code as diagnostic_code;
12use std::fmt;
13use thiserror::Error as ThisError;
14
15const COMPACT_QUERY_DIAGNOSTIC_MESSAGE: &str = "query diagnostic";
16const COMPACT_SCHEMA_DDL_STORE_MESSAGE: &str = "schema DDL diagnostic";
17
18#[derive(Debug, ThisError)]
119#[error("{message}")]
120pub struct InternalError {
121 pub(crate) class: ErrorClass,
122 pub(crate) origin: ErrorOrigin,
123 pub(crate) message: String,
124
125 pub(crate) detail: Option<ErrorDetail>,
128}
129
130impl InternalError {
131 #[cold]
135 #[inline(never)]
136 pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
137 let message = message.into();
138
139 let detail = match (class, origin) {
140 (ErrorClass::Corruption, ErrorOrigin::Store) => {
141 Some(ErrorDetail::Store(StoreError::Corrupt {
142 message: message.clone(),
143 }))
144 }
145 (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
146 Some(ErrorDetail::Store(StoreError::InvariantViolation {
147 message: message.clone(),
148 }))
149 }
150 _ => None,
151 };
152
153 Self {
154 class,
155 origin,
156 message,
157 detail,
158 }
159 }
160
161 #[must_use]
163 pub const fn class(&self) -> ErrorClass {
164 self.class
165 }
166
167 #[must_use]
169 pub const fn origin(&self) -> ErrorOrigin {
170 self.origin
171 }
172
173 #[must_use]
175 pub fn message(&self) -> &str {
176 &self.message
177 }
178
179 #[must_use]
181 pub const fn detail(&self) -> Option<&ErrorDetail> {
182 self.detail.as_ref()
183 }
184
185 #[must_use]
187 pub fn diagnostic(&self) -> diagnostic_code::Diagnostic {
188 diagnostic_code::Diagnostic::new(
189 self.diagnostic_code(),
190 self.origin.diagnostic_origin(),
191 self.detail
192 .as_ref()
193 .and_then(ErrorDetail::diagnostic_detail),
194 )
195 }
196
197 #[must_use]
199 pub fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
200 self.detail.as_ref().map_or_else(
201 || self.class.diagnostic_code(self.origin),
202 ErrorDetail::diagnostic_code,
203 )
204 }
205
206 #[must_use]
208 pub fn into_message(self) -> String {
209 self.message
210 }
211
212 #[cold]
214 #[inline(never)]
215 pub(crate) fn classified(
216 class: ErrorClass,
217 origin: ErrorOrigin,
218 message: impl Into<String>,
219 ) -> Self {
220 Self::new(class, origin, message)
221 }
222
223 #[cold]
225 #[inline(never)]
226 pub(crate) fn with_message(self, message: impl Into<String>) -> Self {
227 Self::classified(self.class, self.origin, message)
228 }
229
230 #[cold]
234 #[inline(never)]
235 pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
236 Self::classified(self.class, origin, self.message)
237 }
238
239 #[cold]
241 #[inline(never)]
242 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
243 Self::new(
244 ErrorClass::InvariantViolation,
245 ErrorOrigin::Index,
246 message.into(),
247 )
248 }
249
250 pub(crate) fn index_key_field_count_exceeds_max(
252 index_name: &str,
253 field_count: usize,
254 max_fields: usize,
255 ) -> Self {
256 Self::index_invariant(format!(
257 "index '{index_name}' has {field_count} fields (max {max_fields})",
258 ))
259 }
260
261 pub(crate) fn index_key_item_field_missing_on_entity_model(field: &str) -> Self {
263 Self::index_invariant(format!(
264 "index key item field missing on entity model: {field}",
265 ))
266 }
267
268 pub(crate) fn index_key_item_field_missing_on_lookup_row(field: &str) -> Self {
270 Self::index_invariant(format!(
271 "index key item field missing on lookup row: {field}",
272 ))
273 }
274
275 pub(crate) fn index_expression_source_type_mismatch(
277 index_name: &str,
278 expression: impl fmt::Display,
279 expected: &str,
280 source_label: &str,
281 ) -> Self {
282 Self::index_invariant(format!(
283 "index '{index_name}' expression '{expression}' expected {expected} source value, got {source_label}",
284 ))
285 }
286
287 #[cold]
290 #[inline(never)]
291 pub(crate) fn planner_executor_invariant(reason: impl Into<String>) -> Self {
292 Self::new(
293 ErrorClass::InvariantViolation,
294 ErrorOrigin::Planner,
295 Self::executor_invariant_message(reason),
296 )
297 }
298
299 #[cold]
302 #[inline(never)]
303 pub(crate) fn query_executor_invariant(reason: impl Into<String>) -> Self {
304 Self::new(
305 ErrorClass::InvariantViolation,
306 ErrorOrigin::Query,
307 Self::executor_invariant_message(reason),
308 )
309 }
310
311 #[cold]
314 #[inline(never)]
315 pub(crate) fn cursor_executor_invariant(reason: impl Into<String>) -> Self {
316 Self::new(
317 ErrorClass::InvariantViolation,
318 ErrorOrigin::Cursor,
319 Self::executor_invariant_message(reason),
320 )
321 }
322
323 #[cold]
325 #[inline(never)]
326 pub(crate) fn executor_invariant(message: impl Into<String>) -> Self {
327 Self::new(
328 ErrorClass::InvariantViolation,
329 ErrorOrigin::Executor,
330 message.into(),
331 )
332 }
333
334 #[cold]
336 #[inline(never)]
337 pub(crate) fn executor_internal(message: impl Into<String>) -> Self {
338 Self::new(ErrorClass::Internal, ErrorOrigin::Executor, message.into())
339 }
340
341 #[cold]
343 #[inline(never)]
344 pub(crate) fn executor_unsupported(message: impl Into<String>) -> Self {
345 Self::new(
346 ErrorClass::Unsupported,
347 ErrorOrigin::Executor,
348 message.into(),
349 )
350 }
351
352 pub(crate) fn mutation_entity_primary_key_missing(entity_path: &str, field_name: &str) -> Self {
354 Self::executor_invariant(format!(
355 "entity primary key field missing: {entity_path} field={field_name}",
356 ))
357 }
358
359 pub(crate) fn mutation_entity_primary_key_invalid_value(
361 entity_path: &str,
362 field_name: &str,
363 value: &crate::value::Value,
364 ) -> Self {
365 Self::executor_invariant(format!(
366 "entity primary key field has invalid value: {entity_path} field={field_name} value={value:?}",
367 ))
368 }
369
370 pub(crate) fn mutation_entity_primary_key_type_mismatch(
372 entity_path: &str,
373 field_name: &str,
374 value: &crate::value::Value,
375 ) -> Self {
376 Self::executor_invariant(format!(
377 "entity primary key field type mismatch: {entity_path} field={field_name} value={value:?}",
378 ))
379 }
380
381 pub(crate) fn mutation_entity_primary_key_mismatch(
383 entity_path: &str,
384 field_name: &str,
385 field_value: &crate::value::Value,
386 identity_key: &crate::value::Value,
387 ) -> Self {
388 Self::executor_invariant(format!(
389 "entity primary key mismatch: {entity_path} field={field_name} field_value={field_value:?} id_key={identity_key:?}",
390 ))
391 }
392
393 pub(crate) fn mutation_entity_field_missing(
395 entity_path: &str,
396 field_name: &str,
397 indexed: bool,
398 ) -> Self {
399 let indexed_note = if indexed { " (indexed)" } else { "" };
400
401 Self::executor_invariant(format!(
402 "entity field missing: {entity_path} field={field_name}{indexed_note}",
403 ))
404 }
405
406 pub(crate) fn mutation_structural_patch_required_field_missing(
408 entity_path: &str,
409 field_name: &str,
410 ) -> Self {
411 Self::executor_invariant(format!(
412 "structural patch missing required field: {entity_path} field={field_name}",
413 ))
414 }
415
416 pub(crate) fn mutation_entity_field_type_mismatch(
418 entity_path: &str,
419 field_name: &str,
420 value: &crate::value::Value,
421 ) -> Self {
422 Self::executor_invariant(format!(
423 "entity field type mismatch: {entity_path} field={field_name} value={value:?}",
424 ))
425 }
426
427 pub(crate) fn mutation_generated_field_explicit(entity_path: &str, field_name: &str) -> Self {
429 Self::executor_unsupported(format!(
430 "generated field may not be explicitly written: {entity_path} field={field_name}",
431 ))
432 }
433
434 #[must_use]
436 pub fn mutation_create_missing_authored_fields(entity_path: &str, field_names: &str) -> Self {
437 Self::executor_unsupported(format!(
438 "create requires explicit values for authorable fields {field_names}: {entity_path}",
439 ))
440 }
441
442 pub(crate) fn mutation_structural_after_image_invalid(
447 entity_path: &str,
448 data_key: impl fmt::Display,
449 detail: impl AsRef<str>,
450 ) -> Self {
451 Self::executor_invariant(format!(
452 "mutation result is invalid: {entity_path} key={data_key} ({})",
453 detail.as_ref(),
454 ))
455 }
456
457 pub(crate) fn mutation_structural_field_unknown(entity_path: &str, field_name: &str) -> Self {
459 Self::executor_invariant(format!(
460 "mutation field not found: {entity_path} field={field_name}",
461 ))
462 }
463
464 pub(crate) fn mutation_decimal_scale_mismatch(
466 entity_path: &str,
467 field_name: &str,
468 expected_scale: impl fmt::Display,
469 actual_scale: impl fmt::Display,
470 ) -> Self {
471 Self::executor_unsupported(format!(
472 "decimal field scale mismatch: {entity_path} field={field_name} expected_scale={expected_scale} actual_scale={actual_scale}",
473 ))
474 }
475
476 pub(crate) fn mutation_text_max_len_exceeded(
478 entity_path: &str,
479 field_name: &str,
480 max_len: impl fmt::Display,
481 actual_len: impl fmt::Display,
482 ) -> Self {
483 Self::executor_unsupported(format!(
484 "text length exceeds max_len: {entity_path} field={field_name} max_len={max_len} actual_len={actual_len}",
485 ))
486 }
487
488 pub(crate) fn mutation_set_field_list_required(entity_path: &str, field_name: &str) -> Self {
490 Self::executor_invariant(format!(
491 "set field must encode as Value::List: {entity_path} field={field_name}",
492 ))
493 }
494
495 pub(crate) fn mutation_set_field_not_canonical(entity_path: &str, field_name: &str) -> Self {
497 Self::executor_invariant(format!(
498 "set field must be strictly ordered and deduplicated: {entity_path} field={field_name}",
499 ))
500 }
501
502 pub(crate) fn mutation_map_field_map_required(entity_path: &str, field_name: &str) -> Self {
504 Self::executor_invariant(format!(
505 "map field must encode as Value::Map: {entity_path} field={field_name}",
506 ))
507 }
508
509 pub(crate) fn mutation_map_field_entries_invalid(
511 entity_path: &str,
512 field_name: &str,
513 detail: impl fmt::Display,
514 ) -> Self {
515 Self::executor_invariant(format!(
516 "map field entries violate map invariants: {entity_path} field={field_name} ({detail})",
517 ))
518 }
519
520 pub(crate) fn mutation_map_field_entries_not_canonical(
522 entity_path: &str,
523 field_name: &str,
524 ) -> Self {
525 Self::executor_invariant(format!(
526 "map field entries are not in canonical deterministic order: {entity_path} field={field_name}",
527 ))
528 }
529
530 pub(crate) fn scalar_page_ordering_after_filtering_required() -> Self {
532 Self::query_executor_invariant("ordering must run after filtering")
533 }
534
535 pub(crate) fn scalar_page_cursor_boundary_order_required() -> Self {
537 Self::query_executor_invariant("cursor boundary requires ordering")
538 }
539
540 pub(crate) fn scalar_page_cursor_boundary_after_ordering_required() -> Self {
542 Self::query_executor_invariant("cursor boundary must run after ordering")
543 }
544
545 pub(crate) fn scalar_page_pagination_after_ordering_required() -> Self {
547 Self::query_executor_invariant("pagination must run after ordering")
548 }
549
550 pub(crate) fn scalar_page_delete_limit_after_ordering_required() -> Self {
552 Self::query_executor_invariant("delete limit must run after ordering")
553 }
554
555 pub(crate) fn load_runtime_scalar_payload_required() -> Self {
557 Self::query_executor_invariant("scalar load mode must carry scalar runtime payload")
558 }
559
560 pub(crate) fn load_runtime_grouped_payload_required() -> Self {
562 Self::query_executor_invariant("grouped load mode must carry grouped runtime payload")
563 }
564
565 pub(crate) fn load_runtime_scalar_surface_payload_required() -> Self {
567 Self::query_executor_invariant("scalar page load mode must carry scalar runtime payload")
568 }
569
570 pub(crate) fn load_runtime_grouped_surface_payload_required() -> Self {
572 Self::query_executor_invariant("grouped page load mode must carry grouped runtime payload")
573 }
574
575 pub(crate) fn load_executor_load_plan_required() -> Self {
577 Self::query_executor_invariant("load executor requires load plans")
578 }
579
580 pub(crate) fn delete_executor_grouped_unsupported() -> Self {
582 Self::executor_unsupported("grouped query execution is not yet enabled in this release")
583 }
584
585 pub(crate) fn delete_executor_delete_plan_required() -> Self {
587 Self::query_executor_invariant("delete executor requires delete plans")
588 }
589
590 pub(crate) fn aggregate_fold_mode_terminal_contract_required() -> Self {
592 Self::query_executor_invariant(
593 "aggregate fold mode must match route fold-mode contract for aggregate terminal",
594 )
595 }
596
597 pub(crate) fn fast_stream_route_kind_request_match_required() -> Self {
599 Self::query_executor_invariant("fast-stream route kind/request mismatch")
600 }
601
602 pub(crate) fn secondary_index_prefix_spec_required() -> Self {
604 Self::query_executor_invariant(
605 "index-prefix executable spec must be materialized for index-prefix plans",
606 )
607 }
608
609 pub(crate) fn index_range_limit_spec_required() -> Self {
611 Self::query_executor_invariant(
612 "index-range executable spec must be materialized for index-range plans",
613 )
614 }
615
616 pub(crate) fn mutation_atomic_save_duplicate_key(
618 entity_path: &str,
619 key: impl fmt::Display,
620 ) -> Self {
621 Self::executor_unsupported(format!(
622 "atomic save batch rejected duplicate key: entity={entity_path} key={key}",
623 ))
624 }
625
626 pub(crate) fn mutation_index_store_generation_changed(
628 expected_generation: u64,
629 observed_generation: u64,
630 ) -> Self {
631 Self::executor_invariant(format!(
632 "index store generation changed between preflight and apply: expected {expected_generation}, found {observed_generation}",
633 ))
634 }
635
636 #[must_use]
638 #[cold]
639 #[inline(never)]
640 pub(crate) fn executor_invariant_message(reason: impl Into<String>) -> String {
641 format!("executor invariant violated: {}", reason.into())
642 }
643
644 #[cold]
646 #[inline(never)]
647 pub(crate) fn planner_invariant(message: impl Into<String>) -> Self {
648 Self::new(
649 ErrorClass::InvariantViolation,
650 ErrorOrigin::Planner,
651 message.into(),
652 )
653 }
654
655 #[must_use]
657 pub(crate) fn invalid_logical_plan_message(reason: impl Into<String>) -> String {
658 format!("invalid logical plan: {}", reason.into())
659 }
660
661 pub(crate) fn query_invalid_logical_plan(reason: impl Into<String>) -> Self {
663 Self::planner_invariant(Self::invalid_logical_plan_message(reason))
664 }
665
666 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
668 Self::new(
669 ErrorClass::InvariantViolation,
670 ErrorOrigin::Store,
671 message.into(),
672 )
673 }
674
675 pub(crate) fn duplicate_runtime_hooks_for_entity_tag(
677 entity_tag: crate::types::EntityTag,
678 ) -> Self {
679 Self::store_invariant(format!(
680 "duplicate runtime hooks for entity tag '{}'",
681 entity_tag.value()
682 ))
683 }
684
685 pub(crate) fn duplicate_runtime_hooks_for_entity_path(entity_path: &str) -> Self {
687 Self::store_invariant(format!(
688 "duplicate runtime hooks for entity path '{entity_path}'"
689 ))
690 }
691
692 #[cold]
694 #[inline(never)]
695 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
696 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
697 }
698
699 pub(crate) fn commit_memory_id_unconfigured() -> Self {
701 Self::store_internal(
702 "commit memory id is not configured; initialize recovery before commit store access",
703 )
704 }
705
706 pub(crate) fn commit_memory_id_mismatch(cached_id: u8, configured_id: u8) -> Self {
708 Self::store_internal(format!(
709 "commit memory id mismatch: cached={cached_id}, configured={configured_id}",
710 ))
711 }
712
713 pub(crate) fn commit_memory_stable_key_mismatch(
715 cached_key: &str,
716 configured_key: &str,
717 ) -> Self {
718 Self::store_internal(format!(
719 "commit memory stable key mismatch: cached={cached_key}, configured={configured_key}",
720 ))
721 }
722
723 pub(crate) fn delete_rollback_row_required() -> Self {
725 Self::store_internal("missing raw row for delete rollback")
726 }
727
728 pub(crate) fn recovery_integrity_validation_failed(
730 missing_index_entries: u64,
731 divergent_index_entries: u64,
732 orphan_index_references: u64,
733 ) -> Self {
734 Self::store_corruption(format!(
735 "recovery integrity validation failed: missing_index_entries={missing_index_entries} divergent_index_entries={divergent_index_entries} orphan_index_references={orphan_index_references}",
736 ))
737 }
738
739 #[cold]
741 #[inline(never)]
742 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
743 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
744 }
745
746 pub(crate) fn structural_index_removal_entity_key_required() -> Self {
748 Self::index_internal("missing old entity key for structural index removal")
749 }
750
751 pub(crate) fn structural_index_insertion_entity_key_required() -> Self {
753 Self::index_internal("missing new entity key for structural index insertion")
754 }
755
756 pub(crate) fn index_commit_op_old_entity_key_required() -> Self {
758 Self::index_internal("missing old entity key for index removal")
759 }
760
761 pub(crate) fn index_commit_op_new_entity_key_required() -> Self {
763 Self::index_internal("missing new entity key for index insertion")
764 }
765
766 #[cfg(test)]
768 pub(crate) fn query_internal(message: impl Into<String>) -> Self {
769 Self::new(ErrorClass::Internal, ErrorOrigin::Query, message.into())
770 }
771
772 #[cold]
774 #[inline(never)]
775 pub(crate) fn query_unsupported(message: impl Into<String>) -> Self {
776 Self::new(ErrorClass::Unsupported, ErrorOrigin::Query, message.into())
777 }
778
779 #[cold]
781 #[inline(never)]
782 #[cfg(feature = "sql")]
783 pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
784 Self {
785 class: ErrorClass::Unsupported,
786 origin: ErrorOrigin::Query,
787 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
788 detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
789 error,
790 })),
791 }
792 }
793
794 #[cold]
796 #[inline(never)]
797 pub(crate) fn query_numeric_overflow() -> Self {
798 Self {
799 class: ErrorClass::Unsupported,
800 origin: ErrorOrigin::Query,
801 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
802 detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
803 }
804 }
805
806 #[cold]
809 #[inline(never)]
810 pub(crate) fn query_numeric_not_representable() -> Self {
811 Self {
812 class: ErrorClass::Unsupported,
813 origin: ErrorOrigin::Query,
814 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
815 detail: Some(ErrorDetail::Query(
816 QueryErrorDetail::NumericNotRepresentable,
817 )),
818 }
819 }
820
821 #[cold]
823 #[inline(never)]
824 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
825 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
826 }
827
828 pub(crate) fn persisted_row_encode_failed(detail: impl fmt::Display) -> Self {
830 Self::serialize_internal(format!("row encode failed: {detail}"))
831 }
832
833 pub(crate) fn persisted_row_field_encode_failed(
835 field_name: &str,
836 detail: impl fmt::Display,
837 ) -> Self {
838 Self::serialize_internal(format!(
839 "row encode failed for field '{field_name}': {detail}",
840 ))
841 }
842
843 pub(crate) fn bytes_field_value_encode_failed(detail: impl fmt::Display) -> Self {
845 Self::serialize_internal(format!("bytes(field) value encode failed: {detail}"))
846 }
847
848 #[cold]
850 #[inline(never)]
851 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
852 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
853 }
854
855 pub(crate) fn commit_corruption(detail: impl fmt::Display) -> Self {
857 Self::store_corruption(format!("commit marker corrupted: {detail}"))
858 }
859
860 pub(crate) fn commit_component_corruption(component: &str, detail: impl fmt::Display) -> Self {
862 Self::store_corruption(format!("commit marker {component} corrupted: {detail}"))
863 }
864
865 pub(crate) fn commit_id_generation_failed(detail: impl fmt::Display) -> Self {
867 Self::store_internal(format!("commit id generation failed: {detail}"))
868 }
869
870 pub(crate) fn commit_marker_payload_exceeds_u32_length_limit(label: &str, len: usize) -> Self {
872 Self::store_unsupported(format!("{label} exceeds u32 length limit: {len} bytes"))
873 }
874
875 pub(crate) fn commit_component_length_invalid(
877 component: &str,
878 len: usize,
879 expected: impl fmt::Display,
880 ) -> Self {
881 Self::commit_component_corruption(
882 component,
883 format!("invalid length {len}, expected {expected}"),
884 )
885 }
886
887 pub(crate) fn commit_marker_exceeds_max_size(size: usize, max_size: u32) -> Self {
889 Self::commit_corruption(format!(
890 "commit marker exceeds max size: {size} bytes (limit {max_size})",
891 ))
892 }
893
894 pub(crate) fn commit_control_slot_exceeds_max_size(size: usize, max_size: u32) -> Self {
896 Self::store_unsupported(format!(
897 "commit control slot exceeds max size: {size} bytes (limit {max_size})",
898 ))
899 }
900
901 pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit(size: usize) -> Self {
903 Self::store_unsupported(format!(
904 "commit marker bytes exceed u32 length limit: {size} bytes",
905 ))
906 }
907
908 pub(crate) fn startup_index_rebuild_invalid_data_key(
910 store_path: &str,
911 detail: impl fmt::Display,
912 ) -> Self {
913 Self::store_corruption(format!(
914 "startup index rebuild failed: invalid data key in store '{store_path}' ({detail})",
915 ))
916 }
917
918 #[cold]
920 #[inline(never)]
921 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
922 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
923 }
924
925 pub(crate) fn index_unique_validation_corruption(
927 entity_path: &str,
928 fields: &str,
929 detail: impl fmt::Display,
930 ) -> Self {
931 Self::index_plan_index_corruption(format!(
932 "index corrupted: {entity_path} ({fields}) -> {detail}",
933 ))
934 }
935
936 pub(crate) fn structural_index_entry_corruption(
938 entity_path: &str,
939 fields: &str,
940 detail: impl fmt::Display,
941 ) -> Self {
942 Self::index_plan_index_corruption(format!(
943 "index corrupted: {entity_path} ({fields}) -> {detail}",
944 ))
945 }
946
947 pub(crate) fn index_unique_validation_entity_key_required() -> Self {
949 Self::index_invariant("missing entity key during unique validation")
950 }
951
952 pub(crate) fn index_unique_validation_row_deserialize_failed(
954 data_key: impl fmt::Display,
955 source: impl fmt::Display,
956 ) -> Self {
957 Self::index_plan_serialize_corruption(format!(
958 "failed to structurally deserialize row: {data_key} ({source})"
959 ))
960 }
961
962 pub(crate) fn index_unique_validation_primary_key_decode_failed(
964 data_key: impl fmt::Display,
965 source: impl fmt::Display,
966 ) -> Self {
967 Self::index_plan_serialize_corruption(format!(
968 "failed to decode structural primary-key slot: {data_key} ({source})"
969 ))
970 }
971
972 pub(crate) fn index_unique_validation_key_rebuild_failed(
974 data_key: impl fmt::Display,
975 entity_path: &str,
976 source: impl fmt::Display,
977 ) -> Self {
978 Self::index_plan_serialize_corruption(format!(
979 "failed to structurally decode unique key row {data_key} for {entity_path}: {source}",
980 ))
981 }
982
983 pub(crate) fn index_unique_validation_row_required(data_key: impl fmt::Display) -> Self {
985 Self::index_plan_store_corruption(format!("missing row: {data_key}"))
986 }
987
988 pub(crate) fn index_only_predicate_component_required() -> Self {
990 Self::index_invariant("index-only predicate program referenced missing index component")
991 }
992
993 pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
995 Self::index_invariant(
996 "index-range continuation anchor is outside the requested range envelope",
997 )
998 }
999
1000 pub(crate) fn index_scan_continuation_advancement_required() -> Self {
1002 Self::index_invariant("index-range continuation scan did not advance beyond the anchor")
1003 }
1004
1005 pub(crate) fn index_scan_key_corrupted_during(
1007 context: &'static str,
1008 err: impl fmt::Display,
1009 ) -> Self {
1010 Self::index_corruption(format!("index key corrupted during {context}: {err}"))
1011 }
1012
1013 pub(crate) fn index_projection_component_required(
1015 index_name: &str,
1016 component_index: usize,
1017 ) -> Self {
1018 Self::index_invariant(format!(
1019 "index projection referenced missing component: index='{index_name}' component_index={component_index}",
1020 ))
1021 }
1022
1023 pub(crate) fn index_entry_decode_failed(err: impl fmt::Display) -> Self {
1025 Self::index_corruption(err.to_string())
1026 }
1027
1028 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
1030 Self::new(
1031 ErrorClass::Corruption,
1032 ErrorOrigin::Serialize,
1033 message.into(),
1034 )
1035 }
1036
1037 pub(crate) fn persisted_row_decode_failed(detail: impl fmt::Display) -> Self {
1039 Self::serialize_corruption(format!("row decode: {detail}"))
1040 }
1041
1042 pub(crate) fn persisted_row_field_decode_failed(
1044 field_name: &str,
1045 detail: impl fmt::Display,
1046 ) -> Self {
1047 Self::serialize_corruption(format!(
1048 "row decode failed for field '{field_name}': {detail}",
1049 ))
1050 }
1051
1052 pub(crate) fn persisted_row_field_kind_decode_failed(
1054 field_name: &str,
1055 field_kind: impl fmt::Debug,
1056 detail: impl fmt::Display,
1057 ) -> Self {
1058 Self::persisted_row_field_decode_failed(
1059 field_name,
1060 format!("kind={field_kind:?}: {detail}"),
1061 )
1062 }
1063
1064 pub(crate) fn persisted_row_field_payload_exact_len_required(
1066 field_name: &str,
1067 payload_kind: &str,
1068 expected_len: usize,
1069 ) -> Self {
1070 let unit = if expected_len == 1 { "byte" } else { "bytes" };
1071
1072 Self::persisted_row_field_decode_failed(
1073 field_name,
1074 format!("{payload_kind} payload must be exactly {expected_len} {unit}"),
1075 )
1076 }
1077
1078 pub(crate) fn persisted_row_field_payload_must_be_empty(
1080 field_name: &str,
1081 payload_kind: &str,
1082 ) -> Self {
1083 Self::persisted_row_field_decode_failed(
1084 field_name,
1085 format!("{payload_kind} payload must be empty"),
1086 )
1087 }
1088
1089 pub(crate) fn persisted_row_field_payload_invalid_byte(
1091 field_name: &str,
1092 payload_kind: &str,
1093 value: u8,
1094 ) -> Self {
1095 Self::persisted_row_field_decode_failed(
1096 field_name,
1097 format!("invalid {payload_kind} payload byte {value}"),
1098 )
1099 }
1100
1101 pub(crate) fn persisted_row_field_payload_non_finite(
1103 field_name: &str,
1104 payload_kind: &str,
1105 ) -> Self {
1106 Self::persisted_row_field_decode_failed(
1107 field_name,
1108 format!("{payload_kind} payload is non-finite"),
1109 )
1110 }
1111
1112 pub(crate) fn persisted_row_field_payload_out_of_range(
1114 field_name: &str,
1115 payload_kind: &str,
1116 ) -> Self {
1117 Self::persisted_row_field_decode_failed(
1118 field_name,
1119 format!("{payload_kind} payload out of range for target type"),
1120 )
1121 }
1122
1123 pub(crate) fn persisted_row_field_text_payload_invalid_utf8(
1125 field_name: &str,
1126 detail: impl fmt::Display,
1127 ) -> Self {
1128 Self::persisted_row_field_decode_failed(
1129 field_name,
1130 format!("invalid UTF-8 text payload ({detail})"),
1131 )
1132 }
1133
1134 pub(crate) fn persisted_row_slot_lookup_out_of_bounds(model_path: &str, slot: usize) -> Self {
1136 Self::index_invariant(format!(
1137 "slot lookup outside model bounds during structural row access: model='{model_path}' slot={slot}",
1138 ))
1139 }
1140
1141 pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
1143 model_path: &str,
1144 slot: usize,
1145 ) -> Self {
1146 Self::index_invariant(format!(
1147 "slot cache lookup outside model bounds during structural row access: model='{model_path}' slot={slot}",
1148 ))
1149 }
1150
1151 pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
1153 data_key: impl fmt::Debug,
1154 detail: impl fmt::Display,
1155 ) -> Self {
1156 Self::persisted_row_decode_failed(format!(
1157 "primary-key value is not primary-key encodable: {data_key:?} ({detail})",
1158 ))
1159 }
1160
1161 pub(crate) fn persisted_row_primary_key_slot_missing(data_key: impl fmt::Debug) -> Self {
1163 Self::persisted_row_decode_failed(format!(
1164 "missing primary-key slot while validating {data_key:?}",
1165 ))
1166 }
1167
1168 pub(crate) fn persisted_row_key_mismatch(
1170 expected_key: impl fmt::Debug,
1171 found_key: impl fmt::Debug,
1172 ) -> Self {
1173 Self::store_corruption(format!(
1174 "row key mismatch: expected {expected_key:?}, found {found_key:?}",
1175 ))
1176 }
1177
1178 pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
1180 Self::persisted_row_decode_failed(format!("missing declared field `{field_name}`"))
1181 }
1182
1183 pub(crate) fn data_key_entity_mismatch(
1185 expected: impl fmt::Display,
1186 found: impl fmt::Display,
1187 ) -> Self {
1188 Self::store_corruption(format!(
1189 "data key entity mismatch: expected {expected}, found {found}",
1190 ))
1191 }
1192
1193 pub(crate) fn reverse_index_ordinal_overflow(
1195 source_path: &str,
1196 field_name: &str,
1197 target_path: &str,
1198 detail: impl fmt::Display,
1199 ) -> Self {
1200 Self::index_internal(format!(
1201 "reverse index ordinal overflow: source={source_path} field={field_name} target={target_path} ({detail})",
1202 ))
1203 }
1204
1205 pub(crate) fn reverse_index_entry_corrupted(
1207 source_path: &str,
1208 field_name: &str,
1209 target_path: &str,
1210 index_key: impl fmt::Debug,
1211 detail: impl fmt::Display,
1212 ) -> Self {
1213 Self::index_corruption(format!(
1214 "reverse index entry corrupted: source={source_path} field={field_name} target={target_path} key={index_key:?} ({detail})",
1215 ))
1216 }
1217
1218 pub(crate) fn relation_target_store_missing(
1220 source_path: &str,
1221 field_name: &str,
1222 target_path: &str,
1223 store_path: &str,
1224 detail: impl fmt::Display,
1225 ) -> Self {
1226 Self::executor_internal(format!(
1227 "relation target store missing: source={source_path} field={field_name} target={target_path} store={store_path} ({detail})",
1228 ))
1229 }
1230
1231 pub(crate) fn relation_target_key_decode_failed(
1233 context_label: &str,
1234 source_path: &str,
1235 field_name: &str,
1236 target_path: &str,
1237 detail: impl fmt::Display,
1238 ) -> Self {
1239 Self::identity_corruption(format!(
1240 "{context_label}: source={source_path} field={field_name} target={target_path} ({detail})",
1241 ))
1242 }
1243
1244 pub(crate) fn relation_target_entity_mismatch(
1246 context_label: &str,
1247 source_path: &str,
1248 field_name: &str,
1249 target_path: &str,
1250 target_entity_name: &str,
1251 expected_tag: impl fmt::Display,
1252 actual_tag: impl fmt::Display,
1253 ) -> Self {
1254 Self::store_corruption(format!(
1255 "{context_label}: source={source_path} field={field_name} target={target_path} expected={target_entity_name} (tag={expected_tag}) actual_tag={actual_tag}",
1256 ))
1257 }
1258
1259 pub(crate) fn relation_source_row_decode_failed(
1261 source_path: &str,
1262 field_name: &str,
1263 target_path: &str,
1264 detail: impl fmt::Display,
1265 ) -> Self {
1266 Self::serialize_corruption(format!(
1267 "relation source row decode: source={source_path} field={field_name} target={target_path} ({detail})",
1268 ))
1269 }
1270
1271 pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
1273 source_path: &str,
1274 field_name: &str,
1275 target_path: &str,
1276 ) -> Self {
1277 Self::serialize_corruption(format!(
1278 "relation source row decode: unsupported scalar relation key: source={source_path} field={field_name} target={target_path}",
1279 ))
1280 }
1281
1282 pub(crate) fn relation_source_row_unsupported_key_kind(field_kind: impl fmt::Debug) -> Self {
1284 Self::serialize_corruption(format!(
1285 "unsupported strong relation key kind during structural decode: {field_kind:?}"
1286 ))
1287 }
1288
1289 pub(crate) fn reverse_index_relation_target_decode_invariant_violated(
1291 source_path: &str,
1292 field_name: &str,
1293 target_path: &str,
1294 ) -> Self {
1295 Self::executor_internal(format!(
1296 "relation target decode invariant violated while preparing reverse index: source={source_path} field={field_name} target={target_path}",
1297 ))
1298 }
1299
1300 pub(crate) fn bytes_covering_component_payload_empty() -> Self {
1302 Self::index_corruption("index component payload is empty during covering projection decode")
1303 }
1304
1305 pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
1307 Self::index_corruption("bool covering component payload is truncated")
1308 }
1309
1310 pub(crate) fn bytes_covering_component_payload_invalid_length(payload_kind: &str) -> Self {
1312 Self::index_corruption(format!(
1313 "{payload_kind} covering component payload has invalid length"
1314 ))
1315 }
1316
1317 pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
1319 Self::index_corruption("bool covering component payload has invalid value")
1320 }
1321
1322 pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
1324 Self::index_corruption("text covering component payload has invalid terminator")
1325 }
1326
1327 pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
1329 Self::index_corruption("text covering component payload contains trailing bytes")
1330 }
1331
1332 pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
1334 Self::index_corruption("text covering component payload is not valid UTF-8")
1335 }
1336
1337 pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
1339 Self::index_corruption("text covering component payload has invalid escape byte")
1340 }
1341
1342 pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
1344 Self::index_corruption("text covering component payload is missing terminator")
1345 }
1346
1347 #[must_use]
1349 pub fn missing_persisted_slot(field_name: &'static str) -> Self {
1350 Self::serialize_corruption(format!("row decode: missing required field '{field_name}'"))
1351 }
1352
1353 pub(crate) fn identity_corruption(message: impl Into<String>) -> Self {
1355 Self::new(
1356 ErrorClass::Corruption,
1357 ErrorOrigin::Identity,
1358 message.into(),
1359 )
1360 }
1361
1362 #[cold]
1364 #[inline(never)]
1365 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
1366 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
1367 }
1368
1369 pub(crate) fn schema_ddl_publication_race_lost(entity_path: &'static str) -> Self {
1371 Self {
1372 class: ErrorClass::Unsupported,
1373 origin: ErrorOrigin::Store,
1374 message: COMPACT_SCHEMA_DDL_STORE_MESSAGE.to_string(),
1375 detail: Some(ErrorDetail::Store(
1376 StoreError::SchemaDdlPublicationRaceLost {
1377 entity_path: entity_path.to_string(),
1378 },
1379 )),
1380 }
1381 }
1382
1383 #[cfg(feature = "sql")]
1385 pub(crate) fn schema_ddl_set_not_null_validation_failed(
1386 entity_path: &'static str,
1387 column_name: &str,
1388 ) -> Self {
1389 Self {
1390 class: ErrorClass::Unsupported,
1391 origin: ErrorOrigin::Store,
1392 message: COMPACT_SCHEMA_DDL_STORE_MESSAGE.to_string(),
1393 detail: Some(ErrorDetail::Store(
1394 StoreError::SchemaDdlSetNotNullValidationFailed {
1395 entity_path: entity_path.to_string(),
1396 column_name: column_name.to_string(),
1397 },
1398 )),
1399 }
1400 }
1401
1402 pub(crate) fn unsupported_entity_tag_in_data_store(
1404 entity_tag: crate::types::EntityTag,
1405 ) -> Self {
1406 Self::store_unsupported(format!(
1407 "unsupported entity tag in data store: '{}'",
1408 entity_tag.value()
1409 ))
1410 }
1411
1412 #[cfg_attr(test, expect(dead_code))]
1414 pub(crate) fn commit_memory_id_registration_failed(err: impl fmt::Display) -> Self {
1415 Self::store_internal(format!("commit memory id registration failed: {err}"))
1416 }
1417
1418 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
1420 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
1421 }
1422
1423 pub(crate) fn index_component_exceeds_max_size(
1425 key_item: impl fmt::Display,
1426 len: usize,
1427 max_component_size: usize,
1428 ) -> Self {
1429 Self::index_unsupported(format!(
1430 "index component exceeds max size: key item '{key_item}' -> {len} bytes (limit {max_component_size})",
1431 ))
1432 }
1433
1434 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
1436 Self::new(
1437 ErrorClass::Unsupported,
1438 ErrorOrigin::Serialize,
1439 message.into(),
1440 )
1441 }
1442
1443 pub(crate) fn cursor_unsupported(message: impl Into<String>) -> Self {
1445 Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor, message.into())
1446 }
1447
1448 pub(crate) fn serialize_incompatible_persisted_format(message: impl Into<String>) -> Self {
1450 Self::new(
1451 ErrorClass::IncompatiblePersistedFormat,
1452 ErrorOrigin::Serialize,
1453 message.into(),
1454 )
1455 }
1456
1457 #[cfg(feature = "sql")]
1460 pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
1461 Self {
1462 class: ErrorClass::Unsupported,
1463 origin: ErrorOrigin::Query,
1464 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1465 detail: Some(ErrorDetail::Query(
1466 QueryErrorDetail::UnsupportedSqlFeature { feature },
1467 )),
1468 }
1469 }
1470
1471 #[cfg(feature = "sql")]
1474 pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
1475 Self {
1476 class: ErrorClass::Unsupported,
1477 origin: ErrorOrigin::Query,
1478 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1479 detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
1480 }
1481 }
1482
1483 pub(crate) fn query_unsupported_projection(
1486 reason: diagnostic_code::QueryProjectionCode,
1487 ) -> Self {
1488 Self {
1489 class: ErrorClass::Unsupported,
1490 origin: ErrorOrigin::Query,
1491 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1492 detail: Some(ErrorDetail::Query(
1493 QueryErrorDetail::UnsupportedProjection { reason },
1494 )),
1495 }
1496 }
1497
1498 pub(crate) fn query_unknown_aggregate_target_field() -> Self {
1500 Self {
1501 class: ErrorClass::Unsupported,
1502 origin: ErrorOrigin::Query,
1503 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1504 detail: Some(ErrorDetail::Query(
1505 QueryErrorDetail::UnknownAggregateTargetField,
1506 )),
1507 }
1508 }
1509
1510 pub(crate) fn query_result_shape_mismatch(
1513 reason: diagnostic_code::QueryResultShapeCode,
1514 ) -> Self {
1515 Self {
1516 class: ErrorClass::Unsupported,
1517 origin: ErrorOrigin::Query,
1518 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1519 detail: Some(ErrorDetail::Query(QueryErrorDetail::ResultShapeMismatch {
1520 reason,
1521 })),
1522 }
1523 }
1524
1525 #[cfg(feature = "sql")]
1528 pub(crate) fn query_sql_surface_mismatch(
1529 mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1530 ) -> Self {
1531 Self {
1532 class: ErrorClass::Unsupported,
1533 origin: ErrorOrigin::Query,
1534 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1535 detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
1536 mismatch,
1537 })),
1538 }
1539 }
1540
1541 #[cfg(feature = "sql")]
1543 pub(crate) fn query_sql_write_boundary(
1544 boundary: diagnostic_code::SqlWriteBoundaryCode,
1545 ) -> Self {
1546 Self {
1547 class: ErrorClass::Unsupported,
1548 origin: ErrorOrigin::Query,
1549 message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
1550 detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
1551 boundary,
1552 })),
1553 }
1554 }
1555
1556 pub fn store_not_found(key: impl Into<String>) -> Self {
1557 let key = key.into();
1558
1559 Self {
1560 class: ErrorClass::NotFound,
1561 origin: ErrorOrigin::Store,
1562 message: format!("data key not found: {key}"),
1563 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
1564 }
1565 }
1566
1567 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
1569 let path = path.into();
1570
1571 Self::new(
1572 ErrorClass::Unsupported,
1573 ErrorOrigin::Store,
1574 format!("unsupported entity path: '{path}'"),
1575 )
1576 }
1577
1578 #[must_use]
1579 pub const fn is_not_found(&self) -> bool {
1580 matches!(
1581 self.detail,
1582 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
1583 )
1584 }
1585
1586 #[must_use]
1587 pub fn display_with_class(&self) -> String {
1588 format!("{}:{}: {}", self.origin, self.class, self.message)
1589 }
1590
1591 #[cold]
1593 #[inline(never)]
1594 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
1595 let message = message.into();
1596 Self::new(
1597 ErrorClass::Corruption,
1598 origin,
1599 format!("corruption detected ({origin}): {message}"),
1600 )
1601 }
1602
1603 #[cold]
1605 #[inline(never)]
1606 pub(crate) fn index_plan_index_corruption(message: impl Into<String>) -> Self {
1607 Self::index_plan_corruption(ErrorOrigin::Index, message)
1608 }
1609
1610 #[cold]
1612 #[inline(never)]
1613 pub(crate) fn index_plan_store_corruption(message: impl Into<String>) -> Self {
1614 Self::index_plan_corruption(ErrorOrigin::Store, message)
1615 }
1616
1617 #[cold]
1619 #[inline(never)]
1620 pub(crate) fn index_plan_serialize_corruption(message: impl Into<String>) -> Self {
1621 Self::index_plan_corruption(ErrorOrigin::Serialize, message)
1622 }
1623
1624 #[cfg(test)]
1626 pub(crate) fn index_plan_invariant(origin: ErrorOrigin, message: impl Into<String>) -> Self {
1627 let message = message.into();
1628 Self::new(
1629 ErrorClass::InvariantViolation,
1630 origin,
1631 format!("invariant violation detected ({origin}): {message}"),
1632 )
1633 }
1634
1635 #[cfg(test)]
1637 pub(crate) fn index_plan_store_invariant(message: impl Into<String>) -> Self {
1638 Self::index_plan_invariant(ErrorOrigin::Store, message)
1639 }
1640
1641 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
1643 Self::new(
1644 ErrorClass::Conflict,
1645 ErrorOrigin::Index,
1646 format!(
1647 "index constraint violation: {path} ({})",
1648 index_fields.join(", ")
1649 ),
1650 )
1651 }
1652}
1653
1654#[derive(Debug, ThisError)]
1662pub enum ErrorDetail {
1663 #[error("{0}")]
1664 Store(StoreError),
1665 #[error("{0}")]
1666 Query(QueryErrorDetail),
1667 }
1674
1675#[derive(Debug, ThisError)]
1683pub enum StoreError {
1684 #[error("key not found: {key}")]
1685 NotFound { key: String },
1686
1687 #[error("store corruption: {message}")]
1688 Corrupt { message: String },
1689
1690 #[error("store invariant violation: {message}")]
1691 InvariantViolation { message: String },
1692
1693 #[error("schema DDL diagnostic")]
1694 SchemaDdlPublicationRaceLost { entity_path: String },
1695
1696 #[error("schema DDL diagnostic")]
1697 SchemaDdlSetNotNullValidationFailed {
1698 entity_path: String,
1699 column_name: String,
1700 },
1701}
1702
1703#[derive(Debug)]
1710pub enum QueryErrorDetail {
1711 NumericOverflow,
1712
1713 NumericNotRepresentable,
1714
1715 UnsupportedSqlFeature {
1716 feature: diagnostic_code::SqlFeatureCode,
1717 },
1718
1719 SqlLowering {
1720 reason: diagnostic_code::SqlLoweringCode,
1721 },
1722
1723 UnsupportedProjection {
1724 reason: diagnostic_code::QueryProjectionCode,
1725 },
1726
1727 UnknownAggregateTargetField,
1728
1729 ResultShapeMismatch {
1730 reason: diagnostic_code::QueryResultShapeCode,
1731 },
1732
1733 SqlSurfaceMismatch {
1734 mismatch: diagnostic_code::SqlSurfaceMismatchCode,
1735 },
1736
1737 SqlWriteBoundary {
1738 boundary: diagnostic_code::SqlWriteBoundaryCode,
1739 },
1740
1741 SchemaDdlAdmission {
1742 error: SchemaDdlAdmissionError,
1743 },
1744}
1745
1746impl fmt::Display for QueryErrorDetail {
1747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1748 f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
1749 }
1750}
1751
1752impl std::error::Error for QueryErrorDetail {}
1753
1754#[derive(Clone, Copy, Debug, Eq, PartialEq)]
1763pub enum SchemaDdlAdmissionError {
1764 MissingExpectedSchemaVersion,
1765
1766 MissingNextSchemaVersion,
1767
1768 StaleExpectedSchemaVersion,
1769
1770 InvalidExpectedSchemaVersion,
1771
1772 InvalidNextSchemaVersion,
1773
1774 AcceptedSchemaChangeWithoutVersionBump,
1775
1776 EmptyVersionBump,
1777
1778 VersionGap,
1779
1780 VersionRollback,
1781
1782 FingerprintMethodMismatch,
1783
1784 UnsupportedTransitionClass,
1785
1786 PhysicalRunnerMissing,
1787
1788 ValidationFailed,
1789
1790 PublicationRaceLost,
1791
1792 InvalidAddColumnDefault,
1793
1794 InvalidAlterColumnDefault,
1795
1796 GeneratedIndexDropRejected,
1797
1798 RequiredDropDefaultUnsupported,
1799
1800 GeneratedFieldDefaultChangeRejected,
1801
1802 GeneratedFieldNullabilityChangeRejected,
1803
1804 SetNotNullValidationFailed,
1805}
1806
1807impl fmt::Display for SchemaDdlAdmissionError {
1808 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1809 f.write_str("schema DDL admission")
1810 }
1811}
1812
1813impl std::error::Error for SchemaDdlAdmissionError {}
1814
1815impl ErrorDetail {
1816 #[must_use]
1818 pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1819 match self {
1820 Self::Store(error) => error.diagnostic_code(),
1821 Self::Query(error) => error.diagnostic_code(),
1822 }
1823 }
1824
1825 #[must_use]
1827 pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1828 match self {
1829 Self::Store(error) => error.diagnostic_detail(),
1830 Self::Query(error) => error.diagnostic_detail(),
1831 }
1832 }
1833}
1834
1835impl StoreError {
1836 #[must_use]
1838 pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1839 match self {
1840 Self::NotFound { .. } => diagnostic_code::DiagnosticCode::StoreNotFound,
1841 Self::Corrupt { .. } => diagnostic_code::DiagnosticCode::StoreCorruption,
1842 Self::InvariantViolation { .. } => {
1843 diagnostic_code::DiagnosticCode::StoreInvariantViolation
1844 }
1845 Self::SchemaDdlPublicationRaceLost { .. }
1846 | Self::SchemaDdlSetNotNullValidationFailed { .. } => {
1847 diagnostic_code::DiagnosticCode::SchemaDdlAdmission
1848 }
1849 }
1850 }
1851
1852 #[must_use]
1854 pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1855 match self {
1856 Self::SchemaDdlPublicationRaceLost { .. } => {
1857 Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1858 reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
1859 })
1860 }
1861 Self::SchemaDdlSetNotNullValidationFailed { .. } => {
1862 Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1863 reason: diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed,
1864 })
1865 }
1866 Self::NotFound { .. } | Self::Corrupt { .. } | Self::InvariantViolation { .. } => None,
1867 }
1868 }
1869}
1870
1871impl QueryErrorDetail {
1872 #[must_use]
1874 pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
1875 match self {
1876 Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
1877 Self::NumericNotRepresentable => {
1878 diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
1879 }
1880 Self::UnsupportedSqlFeature { .. } => {
1881 diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
1882 }
1883 Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
1884 Self::UnsupportedProjection { .. } => {
1885 diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
1886 }
1887 Self::UnknownAggregateTargetField => {
1888 diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
1889 }
1890 Self::ResultShapeMismatch { .. } => {
1891 diagnostic_code::DiagnosticCode::QueryResultShapeMismatch
1892 }
1893 Self::SqlSurfaceMismatch { .. } => {
1894 diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
1895 }
1896 Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
1897 Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
1898 }
1899 }
1900
1901 #[must_use]
1903 pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
1904 match self {
1905 Self::UnsupportedSqlFeature { feature } => {
1906 Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
1907 }
1908 Self::SqlLowering { reason } => {
1909 Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
1910 }
1911 Self::UnsupportedProjection { reason } => {
1912 Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
1913 }
1914 Self::ResultShapeMismatch { reason } => {
1915 Some(diagnostic_code::DiagnosticDetail::QueryResultShape { reason: *reason })
1916 }
1917 Self::SqlSurfaceMismatch { mismatch } => {
1918 Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
1919 mismatch: *mismatch,
1920 })
1921 }
1922 Self::SqlWriteBoundary { boundary } => {
1923 Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
1924 boundary: *boundary,
1925 })
1926 }
1927 Self::SchemaDdlAdmission { error } => {
1928 Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
1929 reason: error.diagnostic_code(),
1930 })
1931 }
1932 Self::NumericOverflow
1933 | Self::NumericNotRepresentable
1934 | Self::UnknownAggregateTargetField => None,
1935 }
1936 }
1937}
1938
1939impl SchemaDdlAdmissionError {
1940 #[must_use]
1942 pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
1943 match self {
1944 Self::MissingExpectedSchemaVersion => {
1945 diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
1946 }
1947 Self::MissingNextSchemaVersion => {
1948 diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
1949 }
1950 Self::StaleExpectedSchemaVersion => {
1951 diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
1952 }
1953 Self::InvalidExpectedSchemaVersion => {
1954 diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
1955 }
1956 Self::InvalidNextSchemaVersion => {
1957 diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
1958 }
1959 Self::AcceptedSchemaChangeWithoutVersionBump => {
1960 diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
1961 }
1962 Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
1963 Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
1964 Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
1965 Self::FingerprintMethodMismatch => {
1966 diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
1967 }
1968 Self::UnsupportedTransitionClass => {
1969 diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
1970 }
1971 Self::PhysicalRunnerMissing => {
1972 diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
1973 }
1974 Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
1975 Self::PublicationRaceLost => {
1976 diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
1977 }
1978 Self::InvalidAddColumnDefault => {
1979 diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
1980 }
1981 Self::InvalidAlterColumnDefault => {
1982 diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
1983 }
1984 Self::GeneratedIndexDropRejected => {
1985 diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
1986 }
1987 Self::RequiredDropDefaultUnsupported => {
1988 diagnostic_code::SchemaDdlAdmissionCode::RequiredDropDefaultUnsupported
1989 }
1990 Self::GeneratedFieldDefaultChangeRejected => {
1991 diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
1992 }
1993 Self::GeneratedFieldNullabilityChangeRejected => {
1994 diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
1995 }
1996 Self::SetNotNullValidationFailed => {
1997 diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed
1998 }
1999 }
2000 }
2001}
2002
2003#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2010pub enum ErrorClass {
2011 Corruption,
2012 IncompatiblePersistedFormat,
2013 NotFound,
2014 Internal,
2015 Conflict,
2016 Unsupported,
2017 InvariantViolation,
2018}
2019
2020impl ErrorClass {
2021 #[must_use]
2023 pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
2024 match self {
2025 Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
2026 diagnostic_code::DiagnosticCode::StoreCorruption
2027 }
2028 Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
2029 Self::IncompatiblePersistedFormat => {
2030 diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
2031 }
2032 Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
2033 diagnostic_code::DiagnosticCode::StoreNotFound
2034 }
2035 Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
2036 Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
2037 Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
2038 Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
2039 Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
2040 diagnostic_code::DiagnosticCode::StoreInvariantViolation
2041 }
2042 Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
2043 }
2044 }
2045}
2046
2047impl fmt::Display for ErrorClass {
2048 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2049 let label = match self {
2050 Self::Corruption => "corruption",
2051 Self::IncompatiblePersistedFormat => "incompatible_persisted_format",
2052 Self::NotFound => "not_found",
2053 Self::Internal => "internal",
2054 Self::Conflict => "conflict",
2055 Self::Unsupported => "unsupported",
2056 Self::InvariantViolation => "invariant_violation",
2057 };
2058 write!(f, "{label}")
2059 }
2060}
2061
2062#[derive(Clone, Copy, Debug, Eq, PartialEq)]
2069pub enum ErrorOrigin {
2070 Serialize,
2071 Store,
2072 Index,
2073 Identity,
2074 Query,
2075 Planner,
2076 Cursor,
2077 Recovery,
2078 Response,
2079 Executor,
2080 Interface,
2081}
2082
2083impl ErrorOrigin {
2084 #[must_use]
2086 pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
2087 match self {
2088 Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
2089 Self::Store => diagnostic_code::ErrorOrigin::Store,
2090 Self::Index => diagnostic_code::ErrorOrigin::Index,
2091 Self::Identity => diagnostic_code::ErrorOrigin::Identity,
2092 Self::Query => diagnostic_code::ErrorOrigin::Query,
2093 Self::Planner => diagnostic_code::ErrorOrigin::Planner,
2094 Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
2095 Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
2096 Self::Response => diagnostic_code::ErrorOrigin::Response,
2097 Self::Executor => diagnostic_code::ErrorOrigin::Executor,
2098 Self::Interface => diagnostic_code::ErrorOrigin::Interface,
2099 }
2100 }
2101}
2102
2103impl fmt::Display for ErrorOrigin {
2104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2105 let label = match self {
2106 Self::Serialize => "serialize",
2107 Self::Store => "store",
2108 Self::Index => "index",
2109 Self::Identity => "identity",
2110 Self::Query => "query",
2111 Self::Planner => "planner",
2112 Self::Cursor => "cursor",
2113 Self::Recovery => "recovery",
2114 Self::Response => "response",
2115 Self::Executor => "executor",
2116 Self::Interface => "interface",
2117 };
2118 write!(f, "{label}")
2119 }
2120}