From dafed47b074c3867db5445e5193a2b5cc8b93ea9 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 27 May 2026 15:40:28 +0800 Subject: [PATCH] fix: improve database error message extraction for queries - frontend: use e.message instead of String(e) in toErrorResult to avoid "Error: " prefix for HTTP-mode errors, matching connectionErrorMessage - frontend: apply same fix to MongoDocBrowser error display - backend(postgres): use pg_error_to_string across all query execution paths (execute, execute_batch, copy_out, copy_in) to extract DbError details instead of generic transport-layer errors Other databases (MySQL, SQL Server, SQLite, DuckDB) already include server-level error details in their native Error::to_string(). --- .../src/components/mongo/MongoDocBrowser.vue | 6 +++--- apps/desktop/src/stores/queryStore.ts | 3 ++- crates/dbx-core/src/db/postgres.rs | 18 +++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/apps/desktop/src/components/mongo/MongoDocBrowser.vue b/apps/desktop/src/components/mongo/MongoDocBrowser.vue index dd6a1ac6c6..2bbab63e85 100644 --- a/apps/desktop/src/components/mongo/MongoDocBrowser.vue +++ b/apps/desktop/src/components/mongo/MongoDocBrowser.vue @@ -167,7 +167,7 @@ async function load() { documents.value = result.documents.map(asRecord); total.value = result.total; } catch (e: unknown) { - error.value = String(e); + error.value = e instanceof Error ? e.message : String(e); } finally { loading.value = false; } @@ -354,7 +354,7 @@ async function saveDoc() { editJson.value = JSON.stringify(documents.value[selectedIdx.value], null, 2); } } catch (e: unknown) { - error.value = String(e); + error.value = e instanceof Error ? e.message : String(e); } } @@ -371,7 +371,7 @@ async function applyDeleteDoc(idx: number) { } await load(); } catch (e: unknown) { - error.value = String(e); + error.value = e instanceof Error ? e.message : String(e); } } diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index 60fae3e3f5..3d2950e05a 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -403,9 +403,10 @@ export const useQueryStore = defineStore("query", () => { } function toErrorResult(e: any): NonNullable { + const message = e instanceof Error ? e.message : String(e); return { columns: ["Error"], - rows: [[String(e)]], + rows: [[message]], affected_rows: 0, execution_time_ms: 0, }; diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index 9358214ad0..1919882faf 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -880,7 +880,7 @@ pub async fn execute_query_with_max_rows( execute_select_query(&client, sql, start, row_limit).await } else { let client = pool.get().await.map_err(|e| e.to_string())?; - let affected = client.execute(sql, &[]).await.map_err(|e| e.to_string())?; + let affected = client.execute(sql, &[]).await.map_err(pg_error_to_string)?; Ok(QueryResult { columns: vec![], @@ -908,7 +908,7 @@ pub async fn execute_query_with_schema_and_max_rows( client .execute(&format!("SET search_path TO {}, public", pg_quote_ident(schema)), &[]) .await - .map_err(|e| e.to_string())?; + .map_err(pg_error_to_string)?; let result = execute_query_with_max_rows_inner(&client, sql, max_rows).await; @@ -929,7 +929,7 @@ async fn execute_query_with_max_rows_inner( if starts_with_executable_sql_keyword(sql, &["SELECT", "SHOW", "EXPLAIN", "WITH", "TABLE"]) { execute_select_query(client, sql, start, row_limit).await } else { - let affected = client.execute(sql, &[]).await.map_err(|e| e.to_string())?; + let affected = client.execute(sql, &[]).await.map_err(pg_error_to_string)?; Ok(QueryResult { columns: vec![], @@ -1055,7 +1055,7 @@ pub async fn execute_batch(pool: &Pool, statements: &[String]) -> Result<(), Str return Ok(()); } let client = pool.get().await.map_err(|e| e.to_string())?; - client.batch_execute(&combined).await.map_err(|e| e.to_string()) + client.batch_execute(&combined).await.map_err(pg_error_to_string) } /// Export data via COPY TO STDOUT. `sql` must be a complete COPY statement, e.g. @@ -1063,11 +1063,11 @@ pub async fn execute_batch(pool: &Pool, statements: &[String]) -> Result<(), Str /// Returns the raw COPY output bytes. pub async fn copy_out(pool: &Pool, sql: &str) -> Result, String> { let client = pool.get().await.map_err(|e| e.to_string())?; - let stream = client.copy_out(sql).await.map_err(|e| e.to_string())?; + let stream = client.copy_out(sql).await.map_err(pg_error_to_string)?; tokio::pin!(stream); let mut result = Vec::new(); while let Some(chunk) = stream.next().await { - result.extend_from_slice(&chunk.map_err(|e| e.to_string())?); + result.extend_from_slice(&chunk.map_err(pg_error_to_string)?); } Ok(result) } @@ -1077,10 +1077,10 @@ pub async fn copy_out(pool: &Pool, sql: &str) -> Result, String> { /// `data` is the raw input in the format specified by the COPY command. pub async fn copy_in(pool: &Pool, sql: &str, data: &[u8]) -> Result<(), String> { let client = pool.get().await.map_err(|e| e.to_string())?; - let sink = client.copy_in::(sql).await.map_err(|e: tokio_postgres::Error| e.to_string())?; + let sink = client.copy_in::(sql).await.map_err(pg_error_to_string)?; let mut sink = Box::pin(sink); - sink.as_mut().send(bytes::Bytes::copy_from_slice(data)).await.map_err(|e| e.to_string())?; - sink.as_mut().close().await.map_err(|e| e.to_string()) + sink.as_mut().send(bytes::Bytes::copy_from_slice(data)).await.map_err(pg_error_to_string)?; + sink.as_mut().close().await.map_err(pg_error_to_string) } #[cfg(test)]