# 22012 — division_by_zero

> PostgreSQL SQLSTATE 22012 的来源与诊断参考。
---

## 速览 {#at-a-glance}
除法或取模操作遇到零除数。PostgreSQL 18.6 在 numeric、整数、浮点、money 和 interval 除法路径中使用 SQLSTATE `22012`，但每条路径仍有自己的操作数和特殊值规则。

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| 字段 | 值 |
| --- | --- |
| 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` |
| 别名 | `—` |

<!-- source facts: data/errcodes/22012.json -->
<!-- END SQLSTATE FACTS -->

<!-- BEGIN SQLSTATE SNIPPET: division_by_zero_numeric -->
```sql
SELECT 10::numeric / 0::numeric;
SELECT 10::numeric / 2::numeric;
```
<!-- END SQLSTATE SNIPPET -->

保留的 PostgreSQL 18.6 和 10.21 实测显示，第一条语句返回 primary `division by zero` 与 SQLSTATE `22012`，随后修正语句返回 `5.0000000000000000`；修复动作后两个后端均为 `IDLE`。[公开案例 JSON](../../data/cases/22012.json) 说明了这个 numeric 除法案例；它不是对下面所有生产路径的运行时比较。

## 含义 {#meaning}
直接的 `numeric_div` 和 `numeric_mod` 会检查 numeric 零除数；内部 `*_opt_error` 形式也可以设置 `have_error` 标志并返回 NULL，由调用者自行处理。整数除法和取模函数在使用 `/` 或 `%` 前检查第二个操作数是否为零。float4/float8 除法经过 `float*_div`，当分子不是 NaN 且除数为零时抛错。money 除法检查整数或 money 除数，interval 除法检查浮点因子。这些路径共享 SQLSTATE 和 primary 文本，但实际解析出的运算符及特殊值仍决定结果。

## 诊断 {#diagnosis}
在运算符和类型解析完成后定位除数或取模除数；不要把 `int4` 规则推广到全部数值类型。检查零是否来自输入、连接、聚合或业务规则，并阅读完整 primary。NULL 操作数通常在严格算术函数调用前得到 NULL；`NULLIF(denominator, 0)` 则是有意把零情况变为 NULL。`CASE` 可以选择 NULL、替代值或跳过分支，因此应选择符合业务结果的分支，不要静默地把错误改成另一个值。

还要区分规划期和执行期：PostgreSQL 18.6 的条件表达式文档警告，常量 `1/0` 子表达式即使位于运行时不会进入的 `CASE` 分支，也可能在规划期失败。规划器源码会递归简化常量参数并计算不可变的常量表达式。若必须延迟到执行期，应使用非恒定值，或在实际除数处使用 `NULLIF`/合适的 `CASE`；这不能让无效常量表达式变安全，也不能替应用决定 NULL 或替代值哪一种语义正确。

## 处理 {#response}
修正产生零除数的来源，或明确选择零值策略。只有 NULL 结果符合业务含义时才使用 `NULLIF`；应用确有替代值或跳过规则时使用 `CASE`，然后检查下游聚合和过滤将看到的新结果。不要重放未改变的操作。普通固定路径抛出 `ERROR`；显式事务需先 `ROLLBACK` 或 `ROLLBACK TO SAVEPOINT` 再重试，自动提交只重试修正后的动作。保留的 18.6/10.21 numeric 实测仅覆盖这个案例。

## 报文 {#messages}
- Primary，`ERROR`：`division by zero`。
- 引用的 numeric、整数、浮点、money 和 interval 除法 guard 没有固定 DETAIL 或 HINT。内部 numeric `have_error` 调用是软处理路径，不能据此声称直接 SQL 运算成功返回。

## 版本 {#versions}
锁定目录从 7.4 起记录该条件；固定源码覆盖 PostgreSQL 18.6。保留的 numeric 运行记录是 PostgreSQL 18.6 和 10.21，仅覆盖 `10::numeric / 0::numeric` 后接 `10::numeric / 2::numeric`。

## 相关 {#related}
[`22003`](../22003/)、[`22008`](../22008/)、[`22015`](../22015/)

## 来源 {#sources}
- [`src/backend/utils/adt/numeric.c#L3243-L3375`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/numeric.c#L3243-L3375) 展示 numeric 除法、特殊值、直接 `ERROR` 和内部 `have_error` 分支；[`#L3467-L3553`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/numeric.c#L3467-L3553) 展示 numeric 取模。
- [`src/backend/utils/adt/int.c#L861-L875`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/int.c#L861-L875) 和 [`#L1158-L1184`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/int.c#L1158-L1184) 展示整数除法和取模；`int8.c` 有对应的 bigint guard。
- [`src/include/utils/float.h#L222-L250`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/include/utils/float.h#L222-L250) 与 [`src/backend/utils/adt/float.c#L101-L106`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/float.c#L101-L106) 展示 float4/float8 零除数分派及 primary。
- [`src/backend/utils/adt/cash.c#L155-L161`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/cash.c#L155-L161) 和 [`#L728-L744`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/cash.c#L728-L744) 展示 money 除法 guard；[`src/backend/utils/adt/timestamp.c#L3759-L3776`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/adt/timestamp.c#L3759-L3776) 展示 interval 除以零因子。
- [`src/backend/optimizer/util/clauses.c#L2437-L2462`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/optimizer/util/clauses.c#L2437-L2462) 展示常量简化/计算；同版[条件表达式文档](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/doc/src/sgml/func.sgml#L20143-L20150)说明不可达 `CASE` 中 `1/0` 的规划期边界。

结构化[证据记录](../../data/evidence/22012.json)保留两份运行摘要/原始摘要哈希和固定源码声明。
