22012 — division_by_zero
速览
除法或取模操作遇到零除数。PostgreSQL 18.6 在 numeric、整数、浮点、money 和 interval 除法路径中使用 SQLSTATE 22012,但每条路径仍有自己的操作数和特殊值规则。
| 字段 | 值 |
|---|---|
| SQLSTATE | 22012 |
| 条件名 | division_by_zero |
| 状态 | 有效 |
| 已知存在于 | 7.4 |
| 锁定快照 | 9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3 |
| 宏 | ERRCODE_DIVISION_BY_ZERO |
| 别名 | — |
保留的 PostgreSQL 18.6 和 10.21 实测显示,第一条语句返回 primary division by zero 与 SQLSTATE 22012,随后修正语句返回 5.0000000000000000;修复动作后两个后端均为 IDLE。公开案例 JSON 说明了这个 numeric 除法案例;它不是对下面所有生产路径的运行时比较。
含义
直接的 numeric_div 和 numeric_mod 会检查 numeric 零除数;内部 *_opt_error 形式也可以设置 have_error 标志并返回 NULL,由调用者自行处理。整数除法和取模函数在使用 / 或 % 前检查第二个操作数是否为零。float4/float8 除法经过 float*_div,当分子不是 NaN 且除数为零时抛错。money 除法检查整数或 money 除数,interval 除法检查浮点因子。这些路径共享 SQLSTATE 和 primary 文本,但实际解析出的运算符及特殊值仍决定结果。
诊断
在运算符和类型解析完成后定位除数或取模除数;不要把 int4 规则推广到全部数值类型。检查零是否来自输入、连接、聚合或业务规则,并阅读完整 primary。NULL 操作数通常在严格算术函数调用前得到 NULL;NULLIF(denominator, 0) 则是有意把零情况变为 NULL。CASE 可以选择 NULL、替代值或跳过分支,因此应选择符合业务结果的分支,不要静默地把错误改成另一个值。
还要区分规划期和执行期:PostgreSQL 18.6 的条件表达式文档警告,常量 1/0 子表达式即使位于运行时不会进入的 CASE 分支,也可能在规划期失败。规划器源码会递归简化常量参数并计算不可变的常量表达式。若必须延迟到执行期,应使用非恒定值,或在实际除数处使用 NULLIF/合适的 CASE;这不能让无效常量表达式变安全,也不能替应用决定 NULL 或替代值哪一种语义正确。
处理
修正产生零除数的来源,或明确选择零值策略。只有 NULL 结果符合业务含义时才使用 NULLIF;应用确有替代值或跳过规则时使用 CASE,然后检查下游聚合和过滤将看到的新结果。不要重放未改变的操作。普通固定路径抛出 ERROR;显式事务需先 ROLLBACK 或 ROLLBACK TO SAVEPOINT 再重试,自动提交只重试修正后的动作。保留的 18.6/10.21 numeric 实测仅覆盖这个案例。
报文
- Primary,
ERROR:division by zero。 - 引用的 numeric、整数、浮点、money 和 interval 除法 guard 没有固定 DETAIL 或 HINT。内部 numeric
have_error调用是软处理路径,不能据此声称直接 SQL 运算成功返回。
版本
锁定目录从 7.4 起记录该条件;固定源码覆盖 PostgreSQL 18.6。保留的 numeric 运行记录是 PostgreSQL 18.6 和 10.21,仅覆盖 10::numeric / 0::numeric 后接 10::numeric / 2::numeric。
相关
来源
src/backend/utils/adt/numeric.c#L3243-L3375展示 numeric 除法、特殊值、直接ERROR和内部have_error分支;#L3467-L3553展示 numeric 取模。src/backend/utils/adt/int.c#L861-L875和#L1158-L1184展示整数除法和取模;int8.c有对应的 bigint guard。src/include/utils/float.h#L222-L250与src/backend/utils/adt/float.c#L101-L106展示 float4/float8 零除数分派及 primary。src/backend/utils/adt/cash.c#L155-L161和#L728-L744展示 money 除法 guard;src/backend/utils/adt/timestamp.c#L3759-L3776展示 interval 除以零因子。src/backend/optimizer/util/clauses.c#L2437-L2462展示常量简化/计算;同版条件表达式文档说明不可达CASE中1/0的规划期边界。
结构化证据记录保留两份运行摘要/原始摘要哈希和固定源码声明。