#[cfg(test)]
mod tests;
use icydb_diagnostic_code as diagnostic_code;
use std::fmt;
use thiserror::Error as ThisError;
const COMPACT_QUERY_DIAGNOSTIC_MESSAGE: &str = "query diagnostic";
const COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE: &str = "runtime diagnostic";
const COMPACT_SCHEMA_DDL_STORE_MESSAGE: &str = "schema DDL diagnostic";
const COMPACT_STORE_DIAGNOSTIC_MESSAGE: &str = "store diagnostic";
const COMPACT_INDEX_DIAGNOSTIC_MESSAGE: &str = "index diagnostic";
const COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE: &str = "serialize diagnostic";
const COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE: &str = "identity diagnostic";
const fn compact_message_for(_class: ErrorClass, origin: ErrorOrigin) -> &'static str {
match origin {
ErrorOrigin::Serialize => COMPACT_SERIALIZE_DIAGNOSTIC_MESSAGE,
ErrorOrigin::Store => COMPACT_STORE_DIAGNOSTIC_MESSAGE,
ErrorOrigin::Index => COMPACT_INDEX_DIAGNOSTIC_MESSAGE,
ErrorOrigin::Identity => COMPACT_IDENTITY_DIAGNOSTIC_MESSAGE,
ErrorOrigin::Query | ErrorOrigin::Planner | ErrorOrigin::Response => {
COMPACT_QUERY_DIAGNOSTIC_MESSAGE
}
ErrorOrigin::Cursor
| ErrorOrigin::Recovery
| ErrorOrigin::Executor
| ErrorOrigin::Interface => COMPACT_RUNTIME_DIAGNOSTIC_MESSAGE,
}
}
#[derive(Debug, ThisError)]
#[error("{message}")]
pub struct InternalError {
pub(crate) class: ErrorClass,
pub(crate) origin: ErrorOrigin,
pub(crate) message: String,
pub(crate) detail: Option<ErrorDetail>,
}
impl InternalError {
#[must_use]
#[cold]
#[inline(never)]
pub fn new(class: ErrorClass, origin: ErrorOrigin) -> Self {
let message = compact_message_for(class, origin);
let detail = match (class, origin) {
(ErrorClass::Corruption, ErrorOrigin::Store) => {
Some(ErrorDetail::Store(StoreError::Corrupt {
message: message.to_string(),
}))
}
(ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
Some(ErrorDetail::Store(StoreError::InvariantViolation {
message: message.to_string(),
}))
}
_ => None,
};
Self {
class,
origin,
message: message.to_string(),
detail,
}
}
#[must_use]
pub const fn class(&self) -> ErrorClass {
self.class
}
#[must_use]
pub const fn origin(&self) -> ErrorOrigin {
self.origin
}
#[must_use]
pub fn message(&self) -> &str {
&self.message
}
#[must_use]
pub const fn detail(&self) -> Option<&ErrorDetail> {
self.detail.as_ref()
}
#[must_use]
pub fn diagnostic(&self) -> diagnostic_code::Diagnostic {
diagnostic_code::Diagnostic::new(
self.diagnostic_code(),
self.origin.diagnostic_origin(),
self.detail
.as_ref()
.and_then(ErrorDetail::diagnostic_detail),
)
}
#[must_use]
pub fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
self.detail.as_ref().map_or_else(
|| self.class.diagnostic_code(self.origin),
ErrorDetail::diagnostic_code,
)
}
#[must_use]
pub fn into_message(self) -> String {
self.message
}
#[cold]
#[inline(never)]
pub(crate) fn classified(class: ErrorClass, origin: ErrorOrigin) -> Self {
Self::new(class, origin)
}
#[cold]
#[inline(never)]
pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
Self::classified(self.class, origin)
}
#[cold]
#[inline(never)]
pub(crate) fn index_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Index)
}
pub(crate) fn index_key_field_count_exceeds_max(
_index_name: &str,
_field_count: usize,
_max_fields: usize,
) -> Self {
Self::index_invariant()
}
pub(crate) fn index_key_item_field_missing_on_entity_model(_field: &str) -> Self {
Self::index_invariant()
}
pub(crate) fn index_key_item_field_missing_on_lookup_row(_field: &str) -> Self {
Self::index_invariant()
}
pub(crate) fn index_expression_source_type_mismatch(
_index_name: &str,
_expression: impl fmt::Display,
_expected: &str,
_source_label: &str,
) -> Self {
Self::index_invariant()
}
#[cold]
#[inline(never)]
pub(crate) fn planner_executor_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
}
#[cold]
#[inline(never)]
pub(crate) fn query_executor_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Query)
}
#[cold]
#[inline(never)]
pub(crate) fn cursor_executor_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Cursor)
}
#[cold]
#[inline(never)]
pub(crate) fn executor_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Executor)
}
#[cold]
#[inline(never)]
pub(crate) fn executor_internal() -> Self {
Self::new(ErrorClass::Internal, ErrorOrigin::Executor)
}
#[cold]
#[inline(never)]
pub(crate) fn executor_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Executor)
}
pub(crate) fn mutation_entity_primary_key_missing(
_entity_path: &str,
_field_name: &str,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_entity_primary_key_invalid_value(
_entity_path: &str,
_field_name: &str,
_value: &crate::value::Value,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_entity_primary_key_type_mismatch(
_entity_path: &str,
_field_name: &str,
_value: &crate::value::Value,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_entity_primary_key_mismatch(
_entity_path: &str,
_field_name: &str,
_field_value: &crate::value::Value,
_identity_key: &crate::value::Value,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_entity_field_missing(
_entity_path: &str,
_field_name: &str,
_indexed: bool,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_structural_patch_required_field_missing(
_entity_path: &str,
_field_name: &str,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_entity_field_type_mismatch(
_entity_path: &str,
_field_name: &str,
_value: &crate::value::Value,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_generated_field_explicit(_entity_path: &str, _field_name: &str) -> Self {
Self::executor_unsupported()
}
#[must_use]
pub fn mutation_create_missing_authored_fields(_entity_path: &str, _field_names: &str) -> Self {
Self::executor_unsupported()
}
pub(crate) fn mutation_structural_after_image_invalid(
_entity_path: &str,
_data_key: impl fmt::Display,
_detail: impl AsRef<str>,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_structural_field_unknown(_entity_path: &str, _field_name: &str) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_decimal_scale_mismatch(
_entity_path: &str,
_field_name: &str,
_expected_scale: impl fmt::Display,
_actual_scale: impl fmt::Display,
) -> Self {
Self::executor_unsupported()
}
pub(crate) fn mutation_text_max_len_exceeded(
_entity_path: &str,
_field_name: &str,
_max_len: impl fmt::Display,
_actual_len: impl fmt::Display,
) -> Self {
Self::executor_unsupported()
}
pub(crate) fn mutation_set_field_list_required(_entity_path: &str, _field_name: &str) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_set_field_not_canonical(_entity_path: &str, _field_name: &str) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_map_field_map_required(_entity_path: &str, _field_name: &str) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_map_field_entries_invalid(
_entity_path: &str,
_field_name: &str,
_detail: impl fmt::Display,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn mutation_map_field_entries_not_canonical(
_entity_path: &str,
_field_name: &str,
) -> Self {
Self::executor_invariant()
}
pub(crate) fn scalar_page_ordering_after_filtering_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn scalar_page_cursor_boundary_order_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn scalar_page_cursor_boundary_after_ordering_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn scalar_page_pagination_after_ordering_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn scalar_page_delete_limit_after_ordering_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn load_runtime_scalar_payload_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn load_runtime_grouped_payload_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn load_runtime_scalar_surface_payload_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn load_runtime_grouped_surface_payload_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn load_executor_load_plan_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn delete_executor_grouped_unsupported() -> Self {
Self::executor_unsupported()
}
pub(crate) fn delete_executor_delete_plan_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn aggregate_fold_mode_terminal_contract_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn fast_stream_route_kind_request_match_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn secondary_index_prefix_spec_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn index_range_limit_spec_required() -> Self {
Self::query_executor_invariant()
}
pub(crate) fn mutation_atomic_save_duplicate_key(
_entity_path: &str,
_key: impl fmt::Display,
) -> Self {
Self::executor_unsupported()
}
pub(crate) fn mutation_index_store_generation_changed(
_expected_generation: u64,
_observed_generation: u64,
) -> Self {
Self::executor_invariant()
}
#[cold]
#[inline(never)]
pub(crate) fn planner_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Planner)
}
pub(crate) fn query_invalid_logical_plan() -> Self {
Self::planner_invariant()
}
pub(crate) fn store_invariant() -> Self {
Self::new(ErrorClass::InvariantViolation, ErrorOrigin::Store)
}
pub(crate) fn duplicate_runtime_hooks_for_entity_tag(
_entity_tag: crate::types::EntityTag,
) -> Self {
Self::store_invariant()
}
pub(crate) fn duplicate_runtime_hooks_for_entity_path(_entity_path: &str) -> Self {
Self::store_invariant()
}
#[cold]
#[inline(never)]
pub(crate) fn store_internal() -> Self {
Self::new(ErrorClass::Internal, ErrorOrigin::Store)
}
pub(crate) fn commit_memory_id_unconfigured() -> Self {
Self::store_internal()
}
pub(crate) fn commit_memory_id_mismatch(_cached_id: u8, _configured_id: u8) -> Self {
Self::store_internal()
}
pub(crate) fn commit_memory_stable_key_mismatch(
_cached_key: &str,
_configured_key: &str,
) -> Self {
Self::store_internal()
}
pub(crate) fn delete_rollback_row_required() -> Self {
Self::store_internal()
}
pub(crate) fn recovery_integrity_validation_failed(
_missing_index_entries: u64,
_divergent_index_entries: u64,
_orphan_index_references: u64,
) -> Self {
Self::store_corruption()
}
#[cold]
#[inline(never)]
pub(crate) fn index_internal() -> Self {
Self::new(ErrorClass::Internal, ErrorOrigin::Index)
}
pub(crate) fn structural_index_removal_entity_key_required() -> Self {
Self::index_internal()
}
pub(crate) fn structural_index_insertion_entity_key_required() -> Self {
Self::index_internal()
}
pub(crate) fn index_commit_op_old_entity_key_required() -> Self {
Self::index_internal()
}
pub(crate) fn index_commit_op_new_entity_key_required() -> Self {
Self::index_internal()
}
#[cfg(test)]
pub(crate) fn query_internal() -> Self {
Self::new(ErrorClass::Internal, ErrorOrigin::Query)
}
#[cold]
#[inline(never)]
pub(crate) fn query_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Query)
}
#[cold]
#[inline(never)]
#[cfg(feature = "sql")]
pub(crate) fn query_schema_ddl_admission(error: SchemaDdlAdmissionError) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::SchemaDdlAdmission {
error,
})),
}
}
#[cold]
#[inline(never)]
pub(crate) fn query_numeric_overflow() -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::NumericOverflow)),
}
}
#[cold]
#[inline(never)]
pub(crate) fn query_numeric_not_representable() -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(
QueryErrorDetail::NumericNotRepresentable,
)),
}
}
#[cold]
#[inline(never)]
pub(crate) fn serialize_internal() -> Self {
Self::new(ErrorClass::Internal, ErrorOrigin::Serialize)
}
pub(crate) fn persisted_row_encode_failed(_detail: impl fmt::Display) -> Self {
Self::persisted_row_encode_internal()
}
pub(crate) fn persisted_row_encode_internal() -> Self {
Self::serialize_internal()
}
pub(crate) fn persisted_row_field_encode_failed(
field_name: &str,
_detail: impl fmt::Display,
) -> Self {
Self::persisted_row_field_encode_internal(field_name)
}
pub(crate) fn persisted_row_field_encode_internal(_field_name: &str) -> Self {
Self::persisted_row_encode_internal()
}
pub(crate) fn bytes_field_value_encode_failed(_detail: impl fmt::Display) -> Self {
Self::serialize_internal()
}
#[cold]
#[inline(never)]
pub(crate) fn store_corruption() -> Self {
Self::new(ErrorClass::Corruption, ErrorOrigin::Store)
}
pub(crate) fn commit_corruption() -> Self {
Self::store_corruption()
}
pub(crate) fn commit_component_corruption() -> Self {
Self::commit_corruption()
}
pub(crate) fn commit_id_generation_failed() -> Self {
Self::store_internal()
}
pub(crate) fn commit_marker_payload_exceeds_u32_length_limit() -> Self {
Self::store_unsupported()
}
pub(crate) fn commit_component_length_invalid() -> Self {
Self::commit_corruption()
}
pub(crate) fn commit_marker_exceeds_max_size() -> Self {
Self::commit_corruption()
}
pub(crate) fn commit_control_slot_exceeds_max_size() -> Self {
Self::store_unsupported()
}
pub(crate) fn commit_control_slot_marker_bytes_exceed_u32_length_limit() -> Self {
Self::store_unsupported()
}
pub(crate) fn startup_index_rebuild_invalid_data_key() -> Self {
Self::store_corruption()
}
#[cold]
#[inline(never)]
pub(crate) fn index_corruption() -> Self {
Self::new(ErrorClass::Corruption, ErrorOrigin::Index)
}
pub(crate) fn index_unique_validation_corruption() -> Self {
Self::index_plan_index_corruption()
}
pub(crate) fn structural_index_entry_corruption() -> Self {
Self::index_plan_index_corruption()
}
pub(crate) fn index_unique_validation_entity_key_required() -> Self {
Self::index_invariant()
}
pub(crate) fn index_unique_validation_row_deserialize_failed() -> Self {
Self::index_plan_serialize_corruption()
}
pub(crate) fn index_unique_validation_primary_key_decode_failed() -> Self {
Self::index_plan_serialize_corruption()
}
pub(crate) fn index_unique_validation_key_rebuild_failed() -> Self {
Self::index_plan_serialize_corruption()
}
pub(crate) fn index_unique_validation_row_required() -> Self {
Self::index_plan_store_corruption()
}
pub(crate) fn index_only_predicate_component_required() -> Self {
Self::index_invariant()
}
pub(crate) fn index_scan_continuation_anchor_within_envelope_required() -> Self {
Self::index_invariant()
}
pub(crate) fn index_scan_continuation_advancement_required() -> Self {
Self::index_invariant()
}
pub(crate) fn index_scan_key_corrupted_during(
_context: &'static str,
_err: impl fmt::Display,
) -> Self {
Self::index_corruption()
}
pub(crate) fn index_projection_component_required(
_index_name: &str,
_component_index: usize,
) -> Self {
Self::index_invariant()
}
pub(crate) fn index_entry_decode_failed() -> Self {
Self::index_corruption()
}
pub(crate) fn serialize_corruption() -> Self {
Self::new(ErrorClass::Corruption, ErrorOrigin::Serialize)
}
pub(crate) fn persisted_row_decode_failed(_detail: impl fmt::Display) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn persisted_row_decode_corruption() -> Self {
Self::serialize_corruption()
}
pub(crate) fn persisted_row_field_decode_failed(
field_name: &str,
_detail: impl fmt::Display,
) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_decode_corruption(_field_name: &str) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn persisted_row_field_kind_decode_failed(
field_name: &str,
_field_kind: impl fmt::Debug,
_detail: impl fmt::Display,
) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_payload_exact_len_required(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_payload_must_be_empty(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_payload_invalid_byte(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_payload_non_finite(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_payload_out_of_range(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_field_text_payload_invalid_utf8(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn persisted_row_slot_lookup_out_of_bounds(_model_path: &str, _slot: usize) -> Self {
Self::index_invariant()
}
pub(crate) fn persisted_row_slot_cache_lookup_out_of_bounds(
_model_path: &str,
_slot: usize,
) -> Self {
Self::index_invariant()
}
pub(crate) fn persisted_row_primary_key_not_primary_key_encodable(
_data_key: impl fmt::Debug,
_detail: impl fmt::Display,
) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn persisted_row_primary_key_slot_missing(_data_key: impl fmt::Debug) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn persisted_row_key_mismatch() -> Self {
Self::store_corruption()
}
pub(crate) fn persisted_row_declared_field_missing(field_name: &str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn data_key_entity_mismatch() -> Self {
Self::store_corruption()
}
pub(crate) fn reverse_index_ordinal_overflow(
_source_path: &str,
_field_name: &str,
_target_path: &str,
_detail: impl fmt::Display,
) -> Self {
Self::index_internal()
}
pub(crate) fn reverse_index_entry_corrupted(
_source_path: &str,
_field_name: &str,
_target_path: &str,
_index_key: impl fmt::Debug,
_detail: impl fmt::Display,
) -> Self {
Self::index_corruption()
}
pub(crate) fn relation_target_store_missing(
_source_path: &str,
_field_name: &str,
_target_path: &str,
_store_path: &str,
_detail: impl fmt::Display,
) -> Self {
Self::executor_internal()
}
pub(crate) fn relation_target_key_decode_failed(
_context_label: &str,
_source_path: &str,
_field_name: &str,
_target_path: &str,
_detail: impl fmt::Display,
) -> Self {
Self::identity_corruption()
}
pub(crate) fn relation_target_entity_mismatch(
_context_label: &str,
_source_path: &str,
_field_name: &str,
_target_path: &str,
_target_entity_name: &str,
_expected_tag: impl fmt::Display,
_actual_tag: impl fmt::Display,
) -> Self {
Self::store_corruption()
}
pub(crate) fn relation_source_row_decode_failed(
_source_path: &str,
_field_name: &str,
_target_path: &str,
_detail: impl fmt::Display,
) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn relation_source_row_unsupported_scalar_relation_key(
_source_path: &str,
_field_name: &str,
_target_path: &str,
) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn relation_source_row_unsupported_key_kind(_field_kind: impl fmt::Debug) -> Self {
Self::persisted_row_decode_corruption()
}
pub(crate) fn reverse_index_relation_target_decode_invariant_violated(
_source_path: &str,
_field_name: &str,
_target_path: &str,
) -> Self {
Self::executor_internal()
}
pub(crate) fn bytes_covering_component_payload_empty() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_bool_payload_truncated() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_component_payload_invalid_length() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_bool_payload_invalid_value() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_text_payload_invalid_terminator() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_text_payload_trailing_bytes() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_text_payload_invalid_utf8() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_text_payload_invalid_escape_byte() -> Self {
Self::index_corruption()
}
pub(crate) fn bytes_covering_text_payload_missing_terminator() -> Self {
Self::index_corruption()
}
#[must_use]
pub fn missing_persisted_slot(field_name: &'static str) -> Self {
Self::persisted_row_field_decode_corruption(field_name)
}
pub(crate) fn identity_corruption() -> Self {
Self::new(ErrorClass::Corruption, ErrorOrigin::Identity)
}
#[cold]
#[inline(never)]
pub(crate) fn store_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Store)
}
pub(crate) fn schema_ddl_publication_race_lost(entity_path: &'static str) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Store,
message: COMPACT_SCHEMA_DDL_STORE_MESSAGE.to_string(),
detail: Some(ErrorDetail::Store(
StoreError::SchemaDdlPublicationRaceLost {
entity_path: entity_path.to_string(),
},
)),
}
}
#[cfg(feature = "sql")]
pub(crate) fn schema_ddl_set_not_null_validation_failed(
entity_path: &'static str,
column_name: &str,
) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Store,
message: COMPACT_SCHEMA_DDL_STORE_MESSAGE.to_string(),
detail: Some(ErrorDetail::Store(
StoreError::SchemaDdlSetNotNullValidationFailed {
entity_path: entity_path.to_string(),
column_name: column_name.to_string(),
},
)),
}
}
pub(crate) fn unsupported_entity_tag_in_data_store(
_entity_tag: crate::types::EntityTag,
) -> Self {
Self::store_unsupported()
}
#[cfg_attr(test, expect(dead_code))]
pub(crate) fn commit_memory_id_registration_failed(_err: impl fmt::Display) -> Self {
Self::store_internal()
}
pub(crate) fn index_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Index)
}
pub(crate) fn index_component_exceeds_max_size() -> Self {
Self::index_unsupported()
}
pub(crate) fn serialize_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Serialize)
}
pub(crate) fn cursor_unsupported() -> Self {
Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor)
}
pub(crate) fn serialize_incompatible_persisted_format() -> Self {
Self::new(
ErrorClass::IncompatiblePersistedFormat,
ErrorOrigin::Serialize,
)
}
#[cfg(feature = "sql")]
pub(crate) fn query_unsupported_sql_feature(feature: diagnostic_code::SqlFeatureCode) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(
QueryErrorDetail::UnsupportedSqlFeature { feature },
)),
}
}
#[cfg(feature = "sql")]
pub(crate) fn query_sql_lowering(reason: diagnostic_code::SqlLoweringCode) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlLowering { reason })),
}
}
pub(crate) fn query_unsupported_projection(
reason: diagnostic_code::QueryProjectionCode,
) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(
QueryErrorDetail::UnsupportedProjection { reason },
)),
}
}
pub(crate) fn query_unknown_aggregate_target_field() -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(
QueryErrorDetail::UnknownAggregateTargetField,
)),
}
}
pub(crate) fn query_result_shape_mismatch(
reason: diagnostic_code::QueryResultShapeCode,
) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::ResultShapeMismatch {
reason,
})),
}
}
#[cfg(feature = "sql")]
pub(crate) fn query_sql_surface_mismatch(
mismatch: diagnostic_code::SqlSurfaceMismatchCode,
) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlSurfaceMismatch {
mismatch,
})),
}
}
#[cfg(feature = "sql")]
pub(crate) fn query_sql_write_boundary(
boundary: diagnostic_code::SqlWriteBoundaryCode,
) -> Self {
Self {
class: ErrorClass::Unsupported,
origin: ErrorOrigin::Query,
message: COMPACT_QUERY_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Query(QueryErrorDetail::SqlWriteBoundary {
boundary,
})),
}
}
pub fn store_not_found(key: impl Into<String>) -> Self {
let key = key.into();
Self {
class: ErrorClass::NotFound,
origin: ErrorOrigin::Store,
message: COMPACT_STORE_DIAGNOSTIC_MESSAGE.to_string(),
detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
}
}
pub fn unsupported_entity_path(_path: impl Into<String>) -> Self {
Self::store_unsupported()
}
#[must_use]
pub const fn is_not_found(&self) -> bool {
matches!(
self.detail,
Some(ErrorDetail::Store(StoreError::NotFound { .. }))
)
}
#[must_use]
pub fn display_with_class(&self) -> String {
format!("{}:{}: {}", self.origin, self.class, self.message)
}
#[cold]
#[inline(never)]
pub(crate) fn index_plan_corruption(origin: ErrorOrigin) -> Self {
Self::new(ErrorClass::Corruption, origin)
}
#[cold]
#[inline(never)]
pub(crate) fn index_plan_index_corruption() -> Self {
Self::index_plan_corruption(ErrorOrigin::Index)
}
#[cold]
#[inline(never)]
pub(crate) fn index_plan_store_corruption() -> Self {
Self::index_plan_corruption(ErrorOrigin::Store)
}
#[cold]
#[inline(never)]
pub(crate) fn index_plan_serialize_corruption() -> Self {
Self::index_plan_corruption(ErrorOrigin::Serialize)
}
#[cfg(test)]
pub(crate) fn index_plan_invariant(origin: ErrorOrigin) -> Self {
Self::new(ErrorClass::InvariantViolation, origin)
}
#[cfg(test)]
pub(crate) fn index_plan_store_invariant() -> Self {
Self::index_plan_invariant(ErrorOrigin::Store)
}
pub(crate) fn index_violation(_path: &str, _index_fields: &[&str]) -> Self {
Self::new(ErrorClass::Conflict, ErrorOrigin::Index)
}
}
#[derive(ThisError)]
pub enum ErrorDetail {
#[error("{0}")]
Store(StoreError),
#[error("{0}")]
Query(QueryErrorDetail),
}
#[derive(ThisError)]
pub enum StoreError {
#[error("key not found: {key}")]
NotFound { key: String },
#[error("store corruption: {message}")]
Corrupt { message: String },
#[error("store invariant violation: {message}")]
InvariantViolation { message: String },
#[error("schema DDL diagnostic")]
SchemaDdlPublicationRaceLost { entity_path: String },
#[error("schema DDL diagnostic")]
SchemaDdlSetNotNullValidationFailed {
entity_path: String,
column_name: String,
},
}
pub enum QueryErrorDetail {
NumericOverflow,
NumericNotRepresentable,
UnsupportedSqlFeature {
feature: diagnostic_code::SqlFeatureCode,
},
SqlLowering {
reason: diagnostic_code::SqlLoweringCode,
},
UnsupportedProjection {
reason: diagnostic_code::QueryProjectionCode,
},
UnknownAggregateTargetField,
ResultShapeMismatch {
reason: diagnostic_code::QueryResultShapeCode,
},
SqlSurfaceMismatch {
mismatch: diagnostic_code::SqlSurfaceMismatchCode,
},
SqlWriteBoundary {
boundary: diagnostic_code::SqlWriteBoundaryCode,
},
SchemaDdlAdmission {
error: SchemaDdlAdmissionError,
},
}
impl fmt::Display for QueryErrorDetail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(COMPACT_QUERY_DIAGNOSTIC_MESSAGE)
}
}
impl std::error::Error for QueryErrorDetail {}
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum SchemaDdlAdmissionError {
MissingExpectedSchemaVersion,
MissingNextSchemaVersion,
StaleExpectedSchemaVersion,
InvalidExpectedSchemaVersion,
InvalidNextSchemaVersion,
AcceptedSchemaChangeWithoutVersionBump,
EmptyVersionBump,
VersionGap,
VersionRollback,
FingerprintMethodMismatch,
UnsupportedTransitionClass,
PhysicalRunnerMissing,
ValidationFailed,
PublicationRaceLost,
InvalidAddColumnDefault,
InvalidAlterColumnDefault,
GeneratedIndexDropRejected,
RequiredDropDefaultUnsupported,
GeneratedFieldDefaultChangeRejected,
GeneratedFieldNullabilityChangeRejected,
SetNotNullValidationFailed,
}
impl fmt::Display for SchemaDdlAdmissionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("schema DDL admission")
}
}
impl std::error::Error for SchemaDdlAdmissionError {}
impl fmt::Debug for ErrorDetail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
}
}
impl fmt::Debug for StoreError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
}
}
impl fmt::Debug for QueryErrorDetail {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_compact_diagnostic(f, self.diagnostic_code(), self.diagnostic_detail())
}
}
impl fmt::Debug for SchemaDdlAdmissionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt_compact_diagnostic(
f,
diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
reason: self.diagnostic_code(),
}),
)
}
}
fn fmt_compact_diagnostic(
f: &mut fmt::Formatter<'_>,
code: diagnostic_code::DiagnosticCode,
detail: Option<diagnostic_code::DiagnosticDetail>,
) -> fmt::Result {
write!(
f,
"{}",
diagnostic_code::ErrorCode::from_parts(code, detail).raw()
)
}
impl ErrorDetail {
#[must_use]
pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
match self {
Self::Store(error) => error.diagnostic_code(),
Self::Query(error) => error.diagnostic_code(),
}
}
#[must_use]
pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
match self {
Self::Store(error) => error.diagnostic_detail(),
Self::Query(error) => error.diagnostic_detail(),
}
}
}
impl StoreError {
#[must_use]
pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
match self {
Self::NotFound { .. } => diagnostic_code::DiagnosticCode::StoreNotFound,
Self::Corrupt { .. } => diagnostic_code::DiagnosticCode::StoreCorruption,
Self::InvariantViolation { .. } => {
diagnostic_code::DiagnosticCode::StoreInvariantViolation
}
Self::SchemaDdlPublicationRaceLost { .. }
| Self::SchemaDdlSetNotNullValidationFailed { .. } => {
diagnostic_code::DiagnosticCode::SchemaDdlAdmission
}
}
}
#[must_use]
pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
match self {
Self::SchemaDdlPublicationRaceLost { .. } => {
Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
reason: diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost,
})
}
Self::SchemaDdlSetNotNullValidationFailed { .. } => {
Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
reason: diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed,
})
}
Self::NotFound { .. } | Self::Corrupt { .. } | Self::InvariantViolation { .. } => None,
}
}
}
impl QueryErrorDetail {
#[must_use]
pub const fn diagnostic_code(&self) -> diagnostic_code::DiagnosticCode {
match self {
Self::NumericOverflow => diagnostic_code::DiagnosticCode::QueryNumericOverflow,
Self::NumericNotRepresentable => {
diagnostic_code::DiagnosticCode::QueryNumericNotRepresentable
}
Self::UnsupportedSqlFeature { .. } => {
diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature
}
Self::SqlLowering { .. } => diagnostic_code::DiagnosticCode::QueryUnsupportedSqlFeature,
Self::UnsupportedProjection { .. } => {
diagnostic_code::DiagnosticCode::QueryUnsupportedProjection
}
Self::UnknownAggregateTargetField => {
diagnostic_code::DiagnosticCode::QueryUnknownAggregateTargetField
}
Self::ResultShapeMismatch { .. } => {
diagnostic_code::DiagnosticCode::QueryResultShapeMismatch
}
Self::SqlSurfaceMismatch { .. } => {
diagnostic_code::DiagnosticCode::QuerySqlSurfaceMismatch
}
Self::SqlWriteBoundary { .. } => diagnostic_code::DiagnosticCode::QuerySqlWriteBoundary,
Self::SchemaDdlAdmission { .. } => diagnostic_code::DiagnosticCode::SchemaDdlAdmission,
}
}
#[must_use]
pub const fn diagnostic_detail(&self) -> Option<diagnostic_code::DiagnosticDetail> {
match self {
Self::UnsupportedSqlFeature { feature } => {
Some(diagnostic_code::DiagnosticDetail::UnsupportedSqlFeature { feature: *feature })
}
Self::SqlLowering { reason } => {
Some(diagnostic_code::DiagnosticDetail::SqlLowering { reason: *reason })
}
Self::UnsupportedProjection { reason } => {
Some(diagnostic_code::DiagnosticDetail::QueryProjection { reason: *reason })
}
Self::ResultShapeMismatch { reason } => {
Some(diagnostic_code::DiagnosticDetail::QueryResultShape { reason: *reason })
}
Self::SqlSurfaceMismatch { mismatch } => {
Some(diagnostic_code::DiagnosticDetail::SqlSurfaceMismatch {
mismatch: *mismatch,
})
}
Self::SqlWriteBoundary { boundary } => {
Some(diagnostic_code::DiagnosticDetail::SqlWriteBoundary {
boundary: *boundary,
})
}
Self::SchemaDdlAdmission { error } => {
Some(diagnostic_code::DiagnosticDetail::SchemaDdlAdmission {
reason: error.diagnostic_code(),
})
}
Self::NumericOverflow
| Self::NumericNotRepresentable
| Self::UnknownAggregateTargetField => None,
}
}
}
impl SchemaDdlAdmissionError {
#[must_use]
pub const fn diagnostic_code(&self) -> diagnostic_code::SchemaDdlAdmissionCode {
match self {
Self::MissingExpectedSchemaVersion => {
diagnostic_code::SchemaDdlAdmissionCode::MissingExpectedSchemaVersion
}
Self::MissingNextSchemaVersion => {
diagnostic_code::SchemaDdlAdmissionCode::MissingNextSchemaVersion
}
Self::StaleExpectedSchemaVersion => {
diagnostic_code::SchemaDdlAdmissionCode::StaleExpectedSchemaVersion
}
Self::InvalidExpectedSchemaVersion => {
diagnostic_code::SchemaDdlAdmissionCode::InvalidExpectedSchemaVersion
}
Self::InvalidNextSchemaVersion => {
diagnostic_code::SchemaDdlAdmissionCode::InvalidNextSchemaVersion
}
Self::AcceptedSchemaChangeWithoutVersionBump => {
diagnostic_code::SchemaDdlAdmissionCode::AcceptedSchemaChangeWithoutVersionBump
}
Self::EmptyVersionBump => diagnostic_code::SchemaDdlAdmissionCode::EmptyVersionBump,
Self::VersionGap => diagnostic_code::SchemaDdlAdmissionCode::VersionGap,
Self::VersionRollback => diagnostic_code::SchemaDdlAdmissionCode::VersionRollback,
Self::FingerprintMethodMismatch => {
diagnostic_code::SchemaDdlAdmissionCode::FingerprintMethodMismatch
}
Self::UnsupportedTransitionClass => {
diagnostic_code::SchemaDdlAdmissionCode::UnsupportedTransitionClass
}
Self::PhysicalRunnerMissing => {
diagnostic_code::SchemaDdlAdmissionCode::PhysicalRunnerMissing
}
Self::ValidationFailed => diagnostic_code::SchemaDdlAdmissionCode::ValidationFailed,
Self::PublicationRaceLost => {
diagnostic_code::SchemaDdlAdmissionCode::PublicationRaceLost
}
Self::InvalidAddColumnDefault => {
diagnostic_code::SchemaDdlAdmissionCode::InvalidAddColumnDefault
}
Self::InvalidAlterColumnDefault => {
diagnostic_code::SchemaDdlAdmissionCode::InvalidAlterColumnDefault
}
Self::GeneratedIndexDropRejected => {
diagnostic_code::SchemaDdlAdmissionCode::GeneratedIndexDropRejected
}
Self::RequiredDropDefaultUnsupported => {
diagnostic_code::SchemaDdlAdmissionCode::RequiredDropDefaultUnsupported
}
Self::GeneratedFieldDefaultChangeRejected => {
diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldDefaultChangeRejected
}
Self::GeneratedFieldNullabilityChangeRejected => {
diagnostic_code::SchemaDdlAdmissionCode::GeneratedFieldNullabilityChangeRejected
}
Self::SetNotNullValidationFailed => {
diagnostic_code::SchemaDdlAdmissionCode::SetNotNullValidationFailed
}
}
}
}
#[repr(u16)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum ErrorClass {
Corruption,
IncompatiblePersistedFormat,
NotFound,
Internal,
Conflict,
Unsupported,
InvariantViolation,
}
impl ErrorClass {
#[must_use]
pub const fn diagnostic_code(self, origin: ErrorOrigin) -> diagnostic_code::DiagnosticCode {
match self {
Self::Corruption if matches!(origin, ErrorOrigin::Store) => {
diagnostic_code::DiagnosticCode::StoreCorruption
}
Self::Corruption => diagnostic_code::DiagnosticCode::RuntimeCorruption,
Self::IncompatiblePersistedFormat => {
diagnostic_code::DiagnosticCode::RuntimeIncompatiblePersistedFormat
}
Self::NotFound if matches!(origin, ErrorOrigin::Store) => {
diagnostic_code::DiagnosticCode::StoreNotFound
}
Self::NotFound => diagnostic_code::DiagnosticCode::RuntimeNotFound,
Self::Internal => diagnostic_code::DiagnosticCode::RuntimeInternal,
Self::Conflict => diagnostic_code::DiagnosticCode::RuntimeConflict,
Self::Unsupported => diagnostic_code::DiagnosticCode::RuntimeUnsupported,
Self::InvariantViolation if matches!(origin, ErrorOrigin::Store) => {
diagnostic_code::DiagnosticCode::StoreInvariantViolation
}
Self::InvariantViolation => diagnostic_code::DiagnosticCode::RuntimeInvariantViolation,
}
}
}
impl fmt::Display for ErrorClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
Self::Corruption => "corruption",
Self::IncompatiblePersistedFormat => "incompatible_persisted_format",
Self::NotFound => "not_found",
Self::Internal => "internal",
Self::Conflict => "conflict",
Self::Unsupported => "unsupported",
Self::InvariantViolation => "invariant_violation",
};
write!(f, "{label}")
}
}
impl fmt::Debug for ErrorClass {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", *self as u16)
}
}
#[repr(u16)]
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum ErrorOrigin {
Serialize,
Store,
Index,
Identity,
Query,
Planner,
Cursor,
Recovery,
Response,
Executor,
Interface,
}
impl ErrorOrigin {
#[must_use]
pub const fn diagnostic_origin(self) -> diagnostic_code::ErrorOrigin {
match self {
Self::Serialize => diagnostic_code::ErrorOrigin::Serialize,
Self::Store => diagnostic_code::ErrorOrigin::Store,
Self::Index => diagnostic_code::ErrorOrigin::Index,
Self::Identity => diagnostic_code::ErrorOrigin::Identity,
Self::Query => diagnostic_code::ErrorOrigin::Query,
Self::Planner => diagnostic_code::ErrorOrigin::Planner,
Self::Cursor => diagnostic_code::ErrorOrigin::Cursor,
Self::Recovery => diagnostic_code::ErrorOrigin::Recovery,
Self::Response => diagnostic_code::ErrorOrigin::Response,
Self::Executor => diagnostic_code::ErrorOrigin::Executor,
Self::Interface => diagnostic_code::ErrorOrigin::Interface,
}
}
}
impl fmt::Display for ErrorOrigin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let label = match self {
Self::Serialize => "serialize",
Self::Store => "store",
Self::Index => "index",
Self::Identity => "identity",
Self::Query => "query",
Self::Planner => "planner",
Self::Cursor => "cursor",
Self::Recovery => "recovery",
Self::Response => "response",
Self::Executor => "executor",
Self::Interface => "interface",
};
write!(f, "{label}")
}
}
impl fmt::Debug for ErrorOrigin {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", *self as u16)
}
}