DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

Rails 8.1.4 把 decimal 大整数截短,显式选择 Decimal 属性类型

Rails 8.1.4 的无小数位 decimal 转换可截掉大整数字符串后半段。显式 decimal 精度是已确认的局部方案,但属性类型和序列化边界须重新检查。

Rails 从 numeric(20, 0) 字段读写大整数时,字符串明明有二十位,转成属性值后却只剩前十六位,可以检查 Rails 8.1.4 的无小数位 decimal 类型路径。数据库字段容量足够,仍可能在 Ruby 属性转换时已经丢失数字。

这类案例使用 PostgreSQL、Rails 8.1.4 和 Ruby 4.0.7,字段用于保存无符号 64 位整数。一个适合验收的值是:

value = '18446744073709551615'

8.1.4 的 Integer 字符串转换增加了按 _limit * 4 截取输入前缀的保护。BigInteger 虽然取消了普通整数的取值范围限制,却仍继承这段转换;无小数位的 DecimalWithoutScale 又继承 BigInteger。默认 limit 为四时,输入只保留十六个字节,然后才调用 to_i。这与数据库拒绝超范围值不同:SQL 发出前或读出后的类型转换已经改变了内容。

目前这里不宣称已有正式版本修复。针对这一具体字段,已确认的局部方案是显式选用 decimal 转换器并指定精度:

class RecordWithLargeNumber < ApplicationRecord
  attribute :my_field, :decimal, precision: 20
end

把 my_field 换成实际属性名,数据库仍保持原来的 numeric(20, 0) 定义。这个声明让属性使用独立的 Decimal 转换路径,避免经过无小数位 decimal 的整数转换分支。它不是去掉所有 Integer 输入保护,也不需要把整个应用的整数类型替换掉。

验收不能只看数据库迁移。先检查赋值,再检查保存和重新加载:

record = RecordWithLargeNumber.new(my_field: value)
raise 'cast lost digits' unless record.my_field.to_i.to_s == value
record.save!
raise 'reload lost digits' unless record.reload.my_field.to_i.to_s == value

这段代码是建议执行的验证,不代表所有数据库或模型组合都已经运行通过。还应检查零、边界值及业务接受的负值,并确认序列化到 API 时保持要求的表示。

显式 decimal 属性通常得到 BigDecimal,而非原来期望的 Integer。依赖对象类型、JSON 表示或整数专有方法的代码需要一并调整。把数字作为浮点数传入还可能先损失精度,应优先从精确字符串或整数输入开始。此局部方案也不自动解决其他无符号整数列或所有字符串转换限制。