use std::fmt::Debug;
use mz_persist_client::error::UpperMismatch;
use mz_proto::TryFromProtoError;
use mz_repr::Timestamp;
use mz_sql::catalog::CatalogError as SqlCatalogError;
use mz_storage_types::controller::StorageError;
use crate::durable::persist::antichain_to_timestamp;
use crate::durable::Epoch;
#[derive(Debug, thiserror::Error)]
pub enum CatalogError {
#[error(transparent)]
Catalog(#[from] SqlCatalogError),
#[error(transparent)]
Durable(#[from] DurableCatalogError),
}
impl From<TryFromProtoError> for CatalogError {
fn from(e: TryFromProtoError) -> Self {
Self::Durable(e.into())
}
}
impl From<FenceError> for CatalogError {
fn from(err: FenceError) -> Self {
let err: DurableCatalogError = err.into();
err.into()
}
}
#[derive(Debug, thiserror::Error)]
pub enum DurableCatalogError {
#[error(transparent)]
Fence(#[from] FenceError),
#[error(
"incompatible Catalog version {found_version}, minimum: {min_catalog_version}, current: {catalog_version}"
)]
IncompatibleDataVersion {
found_version: u64,
min_catalog_version: u64,
catalog_version: u64,
},
#[error("incompatible persist version {found_version}, current: {catalog_version}, make sure to upgrade the catalog one version forward at a time")]
IncompatiblePersistVersion {
found_version: semver::Version,
catalog_version: semver::Version,
},
#[error("uninitialized")]
Uninitialized,
#[error("{0}")]
NotWritable(String),
#[error("proto: {0}")]
Proto(TryFromProtoError),
#[error("duplicate key")]
DuplicateKey,
#[error("uniqueness violation")]
UniquenessViolation,
#[error(transparent)]
Storage(StorageError<Timestamp>),
#[error("Internal catalog error: {0}")]
Internal(String),
}
impl DurableCatalogError {
pub fn can_recover_with_write_mode(&self) -> bool {
match self {
DurableCatalogError::NotWritable(_) => true,
_ => false,
}
}
}
impl From<StorageError<Timestamp>> for DurableCatalogError {
fn from(e: StorageError<Timestamp>) -> Self {
DurableCatalogError::Storage(e)
}
}
impl From<TryFromProtoError> for DurableCatalogError {
fn from(e: TryFromProtoError) -> Self {
DurableCatalogError::Proto(e)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, thiserror::Error)]
pub enum FenceError {
#[error("current catalog deployment generation {current_generation} fenced by new catalog epoch {fence_generation}")]
DeployGeneration {
current_generation: u64,
fence_generation: u64,
},
#[error("current catalog epoch {current_epoch} fenced by new catalog epoch {fence_epoch}")]
Epoch {
current_epoch: Epoch,
fence_epoch: Epoch,
},
#[error(
"builtin table migration shard upper {expected_upper:?} fenced by new builtin table migration shard upper {actual_upper:?}"
)]
MigrationUpper {
expected_upper: Timestamp,
actual_upper: Timestamp,
},
}
impl FenceError {
pub fn migration(err: UpperMismatch<Timestamp>) -> Self {
Self::MigrationUpper {
expected_upper: antichain_to_timestamp(err.expected),
actual_upper: antichain_to_timestamp(err.current),
}
}
}
#[cfg(test)]
mod tests {
use crate::durable::{Epoch, FenceError};
#[mz_ore::test]
fn test_fence_err_ord() {
let deploy_generation = FenceError::DeployGeneration {
current_generation: 90,
fence_generation: 91,
};
let epoch = FenceError::Epoch {
current_epoch: Epoch::new(80).expect("non zero"),
fence_epoch: Epoch::new(81).expect("non zero"),
};
assert!(deploy_generation < epoch);
let migration = FenceError::MigrationUpper {
expected_upper: 60.into(),
actual_upper: 61.into(),
};
assert!(epoch < migration);
}
}