fix(db): expand migration error display to walk source chain
tokio_postgres::Error's Display prints only the placeholder "db error" for DB-side failures; the actual message (e.g. column already exists, SQLSTATE, constraint name) lives in source() as DbError. Without traversing the chain, startup logs showed the useless "migration 001 failed: db error", making failures impossible to diagnose. MigrateError::Display now appends each source layer with ": " and exposes source() so error-reporting layers that walk the chain themselves also benefit.
This commit is contained in:
parent
f3eb93f320
commit
e9f0b032b7
@ -92,16 +92,51 @@ impl From<tokio_postgres::Error> for MigrateError {
|
|||||||
impl std::fmt::Display for MigrateError {
|
impl std::fmt::Display for MigrateError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
MigrateError::Pool(e) => write!(f, "database pool error: {}", e),
|
MigrateError::Pool(e) => {
|
||||||
MigrateError::Query(e) => write!(f, "database query error: {}", e),
|
write!(f, "database pool error: {}", e)?;
|
||||||
|
fmt_source_chain(f, e)
|
||||||
|
}
|
||||||
|
MigrateError::Query(e) => {
|
||||||
|
write!(f, "database query error: {}", e)?;
|
||||||
|
fmt_source_chain(f, e)
|
||||||
|
}
|
||||||
MigrateError::Apply { version, source } => {
|
MigrateError::Apply { version, source } => {
|
||||||
write!(f, "migration {} failed: {}", version, source)
|
write!(f, "migration {} failed: {}", version, source)?;
|
||||||
|
fmt_source_chain(f, source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for MigrateError {}
|
impl std::error::Error for MigrateError {
|
||||||
|
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||||
|
match self {
|
||||||
|
MigrateError::Pool(e) => Some(e),
|
||||||
|
MigrateError::Query(e) => Some(e),
|
||||||
|
MigrateError::Apply { source, .. } => Some(source),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 把错误的 `source()` 链逐层拼到 Display 输出里,用 `: ` 分隔。
|
||||||
|
///
|
||||||
|
/// 存在的原因:`tokio_postgres::Error` 的 `Display` 对 DB 侧错误只会打印
|
||||||
|
/// 无信息量的 `db error`,真正的消息文本(如
|
||||||
|
/// `column "x" of relation "y" already exists`、SQLSTATE、约束名)藏在
|
||||||
|
/// `source()` 链里的 `postgres::error::DbError`。不主动遍历链就会丢失全部上下文,
|
||||||
|
/// 日志只显示 `migration 001 failed: db error`,难以定位。
|
||||||
|
fn fmt_source_chain(
|
||||||
|
f: &mut std::fmt::Formatter<'_>,
|
||||||
|
mut source: &(dyn std::error::Error + 'static),
|
||||||
|
) -> std::fmt::Result {
|
||||||
|
while let Some(next) = source.source() {
|
||||||
|
// `DbError` 的 Display 已经包含 message(必要时还有 detail/hint),
|
||||||
|
// 直接拼上即可;避免逐层重复打印 `db error` 这类占位串。
|
||||||
|
write!(f, ": {next}")?;
|
||||||
|
source = next;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// 在**已获取**的连接上执行迁移主体逻辑(咨询锁 + 建表 + 应用迁移 + 解锁)。
|
/// 在**已获取**的连接上执行迁移主体逻辑(咨询锁 + 建表 + 应用迁移 + 解锁)。
|
||||||
///
|
///
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user