From 2083f48add912a1224ff023fe459afe505d91e42 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Thu, 7 May 2026 18:12:23 +0800 Subject: [PATCH] fix: preserve bigint precision for values exceeding JS safe integer range Closes #137 --- crates/dbx-core/src/db/mod.rs | 18 ++++++++++++++++++ crates/dbx-core/src/db/mysql.rs | 4 ++-- crates/dbx-core/src/db/postgres.rs | 2 +- crates/dbx-core/src/db/sqlite.rs | 2 +- crates/dbx-core/src/db/sqlserver.rs | 2 +- 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/crates/dbx-core/src/db/mod.rs b/crates/dbx-core/src/db/mod.rs index 5b4092cc88..d54f5e8e1f 100644 --- a/crates/dbx-core/src/db/mod.rs +++ b/crates/dbx-core/src/db/mod.rs @@ -40,6 +40,24 @@ pub fn connection_timeout() -> Duration { Duration::from_secs(CONNECTION_TIMEOUT_SECS) } +const JS_MAX_SAFE_INTEGER: i64 = 9_007_199_254_740_991; + +pub fn safe_i64_to_json(v: i64) -> serde_json::Value { + if v > JS_MAX_SAFE_INTEGER || v < -JS_MAX_SAFE_INTEGER { + serde_json::Value::String(v.to_string()) + } else { + serde_json::Value::Number(v.into()) + } +} + +pub fn safe_u64_to_json(v: u64) -> serde_json::Value { + if v > JS_MAX_SAFE_INTEGER as u64 { + serde_json::Value::String(v.to_string()) + } else { + serde_json::Value::Number(v.into()) + } +} + pub fn tcp_probe_timeout() -> Duration { Duration::from_secs(TCP_PROBE_TIMEOUT_SECS) } diff --git a/crates/dbx-core/src/db/mysql.rs b/crates/dbx-core/src/db/mysql.rs index 9012a93d33..aaa78e25a7 100644 --- a/crates/dbx-core/src/db/mysql.rs +++ b/crates/dbx-core/src/db/mysql.rs @@ -130,8 +130,8 @@ fn mysql_value_to_json(row: &MySqlRow, idx: usize, type_name: &str) -> serde_jso row.try_get::(idx) .map(serde_json::Value::String) - .or_else(|_| row.try_get::(idx).map(|v| serde_json::Value::Number(v.into()))) - .or_else(|_| row.try_get::(idx).map(|v| serde_json::Value::Number(v.into()))) + .or_else(|_| row.try_get::(idx).map(super::safe_i64_to_json)) + .or_else(|_| row.try_get::(idx).map(super::safe_u64_to_json)) .or_else(|_| { row.try_get::(idx).map(|v| { serde_json::Number::from_f64(v).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null) diff --git a/crates/dbx-core/src/db/postgres.rs b/crates/dbx-core/src/db/postgres.rs index 9f1bc8801e..86dd55dbcf 100644 --- a/crates/dbx-core/src/db/postgres.rs +++ b/crates/dbx-core/src/db/postgres.rs @@ -72,7 +72,7 @@ fn pg_value_to_json(row: &PgRow, idx: usize, type_name: &str) -> serde_json::Val row.try_get::(idx) .map(serde_json::Value::String) - .or_else(|_| row.try_get::(idx).map(|v| serde_json::Value::Number(v.into()))) + .or_else(|_| row.try_get::(idx).map(super::safe_i64_to_json)) .or_else(|_| row.try_get::(idx).map(|v| serde_json::Value::Number(v.into()))) .or_else(|_| row.try_get::(idx).map(|v| serde_json::Value::Number(v.into()))) .or_else(|_| { diff --git a/crates/dbx-core/src/db/sqlite.rs b/crates/dbx-core/src/db/sqlite.rs index ca1a661126..99a4cdb382 100644 --- a/crates/dbx-core/src/db/sqlite.rs +++ b/crates/dbx-core/src/db/sqlite.rs @@ -180,7 +180,7 @@ pub async fn execute_query(pool: &SqlitePool, sql: &str) -> Result(i) .map(serde_json::Value::String) - .or_else(|_| row.try_get::(i).map(|v| serde_json::Value::Number(v.into()))) + .or_else(|_| row.try_get::(i).map(super::safe_i64_to_json)) .or_else(|_| { row.try_get::(i).map(|v| { serde_json::Number::from_f64(v) diff --git a/crates/dbx-core/src/db/sqlserver.rs b/crates/dbx-core/src/db/sqlserver.rs index db7443fe0a..9a7366d7ba 100644 --- a/crates/dbx-core/src/db/sqlserver.rs +++ b/crates/dbx-core/src/db/sqlserver.rs @@ -62,7 +62,7 @@ fn row_to_json(row: &tiberius::Row) -> Vec { } else if let Some(v) = row.try_get::(i).ok().flatten() { serde_json::Value::Number(v.into()) } else if let Some(v) = row.try_get::(i).ok().flatten() { - serde_json::Value::Number(v.into()) + super::safe_i64_to_json(v) } else if let Some(v) = row.try_get::(i).ok().flatten() { serde_json::Number::from_f64(v).map(serde_json::Value::Number).unwrap_or(serde_json::Value::Null) } else if let Some(v) = row.try_get::(i).ok().flatten() {