资讯动态

better-sqlite3 BigInt 安全整数完全指南:绑定、读取与自定义函数中的 64 位整数处理

发布时间:2026/9/28 11:08:30 来源:尧图企业网站定制
数据库嵌入式数据库【免费下载链接】better-sqlite3The fastest and simplest library for SQLite3 in Node.js.项目地址https://gitcode.com/gh_mirrors/be/better-sqlite3点击查看免费下载better-sqlite3在 Node.js 中对 SQLite 的 64 位有符号整数提供完整的 BigInt 为核心结合仓库源码与测试用例系统讲解 BigInt 的绑定、读取、defaultSafeIntegers/safeIntegers开关的优先级规则以及在用户自定义函数、聚合与虚拟表中接收 BigInt 的完整方案。读完你将能够彻底规避大整数精度丢失问题安全地在表主键、自增 ID 与用户自定义逻辑之间传递 64 位整数。为什么 SQLite 需要 BigIntSQLite 的存储引擎支持 64 位有符号整数INTEGER存储类范围约为-9223372036854775808到9223372036854775807。而 JavaScript 的 number 格式IEEE 754 双精度浮点数只能精确表示绝对值小于2^53约9007199254740992的整数。一旦数据库中的整数超出该范围用普通 number 读取时就会发生精度丢失。这种丢失并非理论风险仓库的测试用例 test/40.bigints.js 直接展示了这一现象const int BigInt(1006028374637854687); // 使用普通 number 读取时会变成 // 1006028374637854700末尾 13 位被舍入为了完整支持 SQLite 的这一数据类型better-sqlite3全面兼容 JavaScript 的BigIntconst big BigInt(1152735103331642317); big 1152735103331642317n; // returns true big.toString(); // returns 1152735103331642317 typeof big; // returns bigint绑定 BigInt与普通数字完全等价BigInt可以像普通数字一样绑定到 Statement也可以从用户自定义函数中返回。例如db.prepare(SELECT * FROM users WHERE id?).get(BigInt(1152735103331642317)); db.prepare(INSERT INTO users (id) VALUES (?)).run(BigInt(1152735103331642317));这得益于底层绑定的原生实现。在 src/util/data.cpp 的JS_VALUE_TO_SQLITE宏中当检测到传入值是BigInt时会调用value.AsNapi::BigInt().Int64Value(lossless)尝试无损转换为int64_t只有在转换过程无精度损失lossless true时才会调用sqlite3_bind_int64绑定为整数。64 位边界保护better-sqlite3对超出 64 位有符号整数范围的值会直接抛出RangeError以保护数据完整性db.prepare(SELECT ?).get(2n ** 63n - 1n); // returns successfully最大合法值 db.prepare(SELECT ?).get(2n ** 63n); // throws a RangeError超出范围从源码看这一保护有两道防线绑定参数时src/util/binder.cpp 在转换失败时会抛出RangeError错误信息为 The bound string, buffer, or bigint is too big从用户自定义函数返回值时src/util/data-converter.cpp 会针对BigInt抛出 a bigint that was too big 的RangeError并将 JS 异常传播回 SQLite 调用上下文。因此超出2^63 - 1的数据永远不会被静默截断写入数据库。从数据库读取 BigInt两级开关默认情况下从数据库返回的整数包括info.lastInsertRowid属性都是普通 JavaScript number。better-sqlite3提供了数据库级与语句级两级开关来改变这一默认行为。数据库级defaultSafeIntegersdb.defaultSafeIntegers(); // BigInts by default db.defaultSafeIntegers(true); // BigInts by default db.defaultSafeIntegers(false); // Numbers by default不传参数等价于传true之后该数据库实例上所有新准备的语句都会默认返回BigInt传入false则恢复为普通 number 默认该方法返回数据库实例本身可链式调用。JS 层的实现在 lib/database.js 与 lib/methods/wrappers.jsDatabase.prototype.defaultSafeIntegers调用原生cppdb.defaultSafeIntegersC 层在 src/objects/database.cpp 的Database::JS_defaultSafeIntegers中维护数据库状态db-safe_ints。语句准备时会把数据库默认值快照到语句自身在 src/objects/statement.cpp 中this-safe_ints db-GetState()-safe_ints。这意味着开关只影响创建时点之后准备的语句测试 test/40.bigints.js 对此有充分验证先db.defaultSafeIntegers(true)再 prepare 的语句默认返回BigInt而之前已 prepare 的语句行为不变。语句级safeIntegers对于单个语句可以覆盖数据库默认设置const stmt db.prepare(SQL); stmt.safeIntegers(); // Safe integers ON stmt.safeIntegers(true); // Safe integers ON stmt.safeIntegers(false); // Safe integers OFF同样地无参数等价于true返回语句本身以便链式调用例如stmt.safeIntegers().get()。原生实现位于 src/objects/statement.cpp 的Statement::JS_safeIntegers直接改写语句的safe_ints字段。读取转换的底层原理读取整数时src/util/data.cpp 的SQLITE_VALUE_TO_JS宏根据safe_ints标志决定类型当safe_ints为真且列类型是SQLITE_INTEGER时用Napi::BigInt::New(env, (int64_t)sqlite3_column_int64(...))构造BigInt否则走SQLITE_FLOAT/SQLITE_TEXT/SQLITE_BLOB等常规分支。值得注意的是宏中的case SQLITE_INTEGER分支没有显式break会自然落入SQLITE_FLOAT分支——这意味着当safe_ints为假时整数会被当作 double 的 number 返回这正是精度丢失的根源。lastInsertRowid 同样受开关影响run()返回的info.lastInsertRowid同样受safeIntegers控制。在 src/objects/statement.cpp 中lastInsertRowid属性的构造会检查stmt-safe_ints为真时返回Napi::BigInt::New(env, (int64_t)id)否则返回普通 number。测试用例验证了两种模式下的行为expect(stmt.run(int, int, int).lastInsertRowid).to.equal(lastRowid); // number expect(stmt.safeIntegers().run(int, int, int).lastInsertRowid).to.deep.equal(BigInt(lastRowid)); // bigint对于INTEGER PRIMARY KEY自增表当 ID 增长到超出2^53时务必开启safeIntegers才能拿到精确的自增 ID。用户自定义函数与 BigInt 参数用户自定义函数可以接收BigInt作为参数但需要通过safeIntegers: true选项显式开启db.function(isInt, { safeIntegers: true }, (value) { return String(typeof value bigint); }); db.prepare(SELECT isInt(?)).pluck().get(10); // false db.prepare(SELECT isInt(?)).pluck().get(10n); // true同一个函数在开启safeIntegers后会根据传入值类型收到number或bigint——这正是测试 test/40.bigints.js 中断言的行为number2与bigint2。选项的三态语义跟随数据库默认safeIntegers选项在 JS 层存在三种状态处理逻辑可见 lib/methods/function.jsconst safeIntegers safeIntegers in options ? getBooleanOption(options, safeIntegers) : 2;不传该选项时取值为2表示跟随数据库当前默认值显式传true/false时取值为1/0表示强制开启 / 关闭。这个三态值传入原生层后在 src/objects/database.cpp 的Database::JS_function与Database::JS_aggregate中执行传播逻辑safe_ints safe_ints 2 ? safe_ints : static_castint(db-safe_ints);即只有显式指定时才使用指定值否则采用数据库当前默认。这也解释了测试 test/40.bigints.js 中的完整矩阵验证数据库默认开启 BigInt 时不传选项的函数收到BigInt显式传{ safeIntegers: false }时收到 number数据库默认关闭时行为相反。聚合与虚拟表同样支持用户自定义聚合和虚拟表也可以接收BigInt参数db.aggregate(addInts, { safeIntegers: true, start: 0n, step: (total, nextValue) total nextValue, });db.table(sequence, { safeIntegers: true, columns: [value], parameters: [length, start], rows: function* (length, start 0n) { const end start length; for (let n start; n end; n) { yield { value: n }; } }, });聚合的safeIntegers解析见 lib/methods/aggregate.js同样的2表示跟随数据库默认虚拟表的safeIntegers校验见 lib/methods/table.js要求必须是 boolean传入非布尔值会抛出TypeError并随定义传递给原生层。测试 test/40.bigints.js 验证了聚合与虚拟表在safeIntegers: true下收到BigInt、默认模式下收到 number 的行为且同样支持数据库级默认值的继承与覆盖。REALFLOAT值始终是普通数字需要特别注意的是数据库返回的 REALFLOAT值永远以普通 number 表示不会因safeIntegers开关变成BigInt。这一点在 src/util/data.cpp 的SQLITE_VALUE_TO_JS宏中明确体现——只有SQLITE_INTEGER分支受safe_ints影响SQLITE_FLOAT无条件走Napi::Number::New。测试 test/40.bigints.js 也验证了 REAL 列在开关开启前后返回值完全一致都是 number。因此将REAL列的取整结果作为 ID 使用仍然是不可靠的只有整数存储类INTEGER的数据才能通过safeIntegers获得精确的BigInt表示。优先级总结与实战建议综合以上开关better-sqlite3的 BigInt 行为遵循以下优先级语句级stmt.safeIntegers(true/false)显式覆盖优先级最高函数/聚合/虚拟表选项级{ safeIntegers: true/false }显式覆盖数据库默认不传选项则跟随数据库默认数据库级db.defaultSafeIntegers(true/false)设定默认值影响之后 prepare 的语句与未显式指定选项的注册函数。实战建议凡是可能存储超出2^53的整数如雪花 ID、INTEGER PRIMARY KEY自增列、大整数计数务必在读取侧开启safeIntegers否则数值会被静默舍入在INSERT/SELECT时优先使用BigInt(...)或123n字面量显式传递避免字符串隐式转换需要精确的lastInsertRowid时对run()语句开启safeIntegers()超出2^63 - 1的BigInt会触发RangeError这是数据完整性保护机制应在上层业务中先行校验而非依赖静默截断。更多 API 细节可参考 docs/api.md 中的 Statement、defaultSafeIntegers 与 user-defined functions 等章节。赞分享数据库嵌入式数据库【免费下载链接】better-sqlite3The fastest and simplest library for SQLite3 in Node.js.项目地址https://gitcode.com/gh_mirrors/be/better-sqlite3点击查看免费下载相关推荐better-sqlite3 完整 API 指南Database、Statement、SqliteError 与参数绑定全解better sqlite3 完整 API 指南Database、Statement、SqliteError 与参数绑定全解 导读 本文以 better s数据库嵌入式数据库better-sqlite3绑定参数完全手册匿名参数与命名参数终极指南better sqlite3绑定参数完全手册匿名参数与命名参数终极指南 better sqlite3是Node.js中最快最简单的SQLite3库其强大的绑数据库嵌入式数据库Godot UI动画快速上手5分钟搞定淡入淡出、滑动入场与页面过渡Godot UI动画快速上手5分钟搞定淡入淡出、滑动入场与页面过渡 页面切换像硬切镜头、菜单弹出来没有任何铺垫、按钮点了毫无手感——这是接口体验最被吐槽的三件文档教程游戏开发上一篇BetterNCM安装器终极指南5分钟解锁网易云音乐无限可能下一篇BetterNCM安装器完整指南5分钟解锁网易云音乐插件生态创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑