为提高效率,提问时请提供以下信息,问题描述清晰可优先响应。
【DM版本】:DM8(问题位于 Node.js 驱动 dmdb 端,与 DM 服务器版本无关,任意版本均可复现)
【操作系统】:Windows / Linux 均可复现(Node.js ≥ 18
【CPU】:【CPU】:无关联(x64 / ARM 均可复现)
【问题描述】:
一、现象
BIGINT 列的值超出 JS 安全整数范围(Number.MAX_SAFE_INTEGER = 2^53-1 ≈ 9.007e15)时,驱动不抛错、静默返回丢精度后的 number。业务系统普遍使用雪花 ID(19 位,约 1e18 量级)作主键,会导致查出来的 ID 本身就是错的,且极难察觉——拿错误 ID 回查会得出"记录不存在"的错误结论。
二、复现步骤
SQL:
CREATE TABLE T(ID BIGINT);
INSERT INTO T VALUES(2071964095068016641);
COMMIT;
Node.js(dmdb@1.0.52452,npm 最新版):
const dmdb = require('dmdb');
const conn = await dmdb.getConnection({ connectString: 'dm://localhost:5236', user: 'SYSDBA', password: '**' });
const r = await conn.execute('SELECT ID FROM T');
console.log(r.rows[0][0]); // 输出 2071964095068016600(实际值 ...641,差 41)
console.log(typeof r.rows[0][0]); // number,无任何警告
三、根因定位(驱动源码)

您的定位是对的,根因确实是
DB2J.toNumber里的Number(BigInt)。我按您给的步骤实测复现了:边界也确认了,从 2^53+1 开始出错:
补充一个背景
这个行为不是缺陷,是 2023 年 v1.0.15238 的主动变更,驱动 README 的变更记录里写明:
NUMBER/NUMERIC 列在 v1.0.10728(2022-04-18)也是同样的改动和同样的说明。
所以官方推荐的方案就是
fetchAsString/fetchInfo,和您自己试出来的一致。三种规避方式(都已实测)
方式一:
fetchInfo按列(推荐,影响面最小)const r = await conn.execute('SELECT ID, N FROM T', [], { fetchInfo: { ID: { type: dmdb.STRING } } }); // ID -> '2071964095068016641' (string) 精确 // N -> 42 (number) 其他列不受影响方式二:
fetchAsString全局dmdb.fetchAsString = [dmdb.NUMBER]; // 所有 NUMBER 列返回字符串:'2071964095068016641'方式三:SQL 侧 CAST
SELECT CAST(ID AS VARCHAR(30)) AS ID FROM T关于建议
您提的 node-postgres(int8 默认字符串)和 mysql2(supportBigNumbers)确实是业界更保守的做法。不过达梦这里从 bigint 改成 number 是在 2023 年做的,现在再改回默认字符串属于兼容性变更,影响存量用户,短期内不太会动。
就当前版本,建议雪花 ID 这类列统一用
fetchInfo指定为字符串——只影响该列,其他数值列的类型和运算都不受影响。另外一个小建议:
fetchInfo的推荐目前主要写在 README 的变更记录里,如果能在 API 文档的fetchAsString/fetchInfo说明处也标注一下这个 2^53 边界,会更早被注意到(我这次查文档时也是先看到 API 说明、后来才翻到变更记录)。