From 9b4e16500e97d0eadad983be2e5ac306ae283c13 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Tue, 16 Jun 2026 01:05:48 +0800 Subject: [PATCH] fix(postgres): show connection failure details --- crates/dbx-core/src/db/postgres.rs | 42 ++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index 204f3b251..e4e144c2d 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -1,5 +1,5 @@ use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; -use deadpool_postgres::{ManagerConfig, Pool, RecyclingMethod, Runtime}; +use deadpool_postgres::{ManagerConfig, Pool, PoolError, RecyclingMethod, Runtime}; use futures::{SinkExt, StreamExt}; use percent_encoding::percent_decode_str; use rust_decimal::Decimal; @@ -679,6 +679,43 @@ fn pg_error_to_string(err: tokio_postgres::Error) -> String { err.as_db_error().map(ToString::to_string).unwrap_or_else(|| err.to_string()) } +fn pg_db_error_to_string(err: &tokio_postgres::error::DbError) -> String { + format!("{err} (SQLSTATE {})", err.code().code()) +} + +fn pg_error_from_sources(err: &(dyn std::error::Error + 'static)) -> Option { + let mut current = Some(err); + while let Some(source) = current { + if let Some(pg_error) = source.downcast_ref::() { + if let Some(db_error) = pg_error.as_db_error() { + return Some(pg_db_error_to_string(db_error)); + } + } + if let Some(db_error) = source.downcast_ref::() { + return Some(pg_db_error_to_string(db_error)); + } + current = source.source(); + } + None +} + +fn error_with_sources_to_string(err: &(dyn std::error::Error + 'static)) -> String { + let mut messages = vec![err.to_string()]; + let mut current = err.source(); + while let Some(source) = current { + let message = source.to_string(); + if !messages.iter().any(|existing| existing == &message) { + messages.push(message); + } + current = source.source(); + } + messages.join(": ") +} + +fn pg_pool_error_to_string(err: PoolError) -> String { + pg_error_from_sources(&err).unwrap_or_else(|| error_with_sources_to_string(&err)) +} + fn should_retry_postgres_text_query(err: &tokio_postgres::Error) -> bool { let message = err.as_db_error().map(ToString::to_string).unwrap_or_else(|| err.to_string()).to_ascii_lowercase(); message.contains("no binary output function") @@ -845,7 +882,8 @@ pub async fn connect(url: &str, fallback_timeout: Duration) -> Result