百科 / 错误代码 / Class 22 数据异常
22012 division_by_zero
ERROR 已实测 详解 实测通过
- 条件名
division_by_zero- 宏名称
ERRCODE_DIVISION_BY_ZERO- 启用版本
- 7.4
- 状态
- 活跃
版本覆盖
速览
除法或取模操作遇到零除数。PostgreSQL 18.6 在 numeric、整数、浮点、money 和 interval 除法路径中使用 SQLSTATE 22012,但每条路径仍有自己的操作数和特殊值规则。
SELECT 10::numeric / 0::numeric;
SELECT 10::numeric / 2::numeric;
保留的 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 实测仅覆盖这个案例。
可复现案例
在一次性实例上执行过的场景。其中 1 个附有可执行 SQL,正文相应小节里给出。
division_by_zero_numeric PG 10 / 18 有 SQL
前置条件
- A runner-owned disposable target is provisioned.
触发
Evaluate a numeric expression with a zero denominator.
断言
- SQLSTATE is 22012 with primary division by zero
- The failed autocommit session remains IDLE
- A nonzero denominator returns the defined numeric result
处置
Validate the denominator or apply an explicitly chosen domain policy; do not silently turn a calculation into NULL with NULLIF unless that is the intended business result.
清理
Close the runner connection and drop the case schema with an owner connection.
报文
- Primary,
ERROR:division by zero。 - 引用的 numeric、整数、浮点、money 和 interval 除法 guard 没有固定 DETAIL 或 HINT。内部 numeric
have_error调用是软处理路径,不能据此声称直接 SQL 运算成功返回。
报文模板
源码里的格式串,不是某一次运行的输出。%s 之类是占位符,实际报文会填入对象名与取值。适用范围一栏是核验时留下的原始英文记录,未经翻译。
division by zero
来源:src/backend/utils/adt/numeric.c @ REL_18_6 · src/backend/utils/adt/numeric.c @ REL_18_6 · src/backend/utils/adt/int.c @ REL_18_6 · src/backend/utils/adt/int.c @ REL_18_6 · src/backend/utils/adt/int8.c @ REL_18_6 · src/backend/utils/adt/int8.c @ REL_18_6 · src/include/utils/float.h @ REL_18_6 · src/backend/utils/adt/float.c @ REL_18_6 · src/backend/utils/adt/cash.c @ REL_18_6 · src/backend/utils/adt/cash.c @ REL_18_6 · src/backend/utils/adt/timestamp.c @ REL_18_6
适用范围:Direct cited guards; special values and internal numeric soft callers are type/context dependent.
版本
锁定目录从 7.4 起记录该条件;固定源码覆盖 PostgreSQL 18.6。保留的 numeric 运行记录是 PostgreSQL 18.6 和 10.21,仅覆盖 10::numeric / 0::numeric 后接 10::numeric / 2::numeric。
来源
- 上游源码 src/backend/utils/adt/cash.c
- 上游源码 src/backend/utils/adt/cash.c
- 上游源码 doc/src/sgml/func.sgml
- 上游源码 src/backend/utils/errcodes.txt 第 182 行
- 上游源码 src/backend/utils/adt/float.c
- 上游源码 src/include/utils/float.h
- 上游源码 src/backend/utils/adt/int.c
- 上游源码 src/backend/utils/adt/int.c
- 上游源码 src/backend/utils/adt/int8.c
- 上游源码 src/backend/utils/adt/int8.c
- 上游源码 src/backend/utils/adt/timestamp.c
- 上游源码 src/backend/utils/adt/numeric.c
- 上游源码 src/backend/utils/adt/numeric.c
- 上游源码 src/backend/optimizer/util/clauses.c
- 核验材料 verify/cases/22012/snippets.json
证据
断言
每条断言都写明了是怎么核实的,以及它不覆盖什么。这一层是核验时留下的原始英文记录,照原样呈现,未经翻译。
-
22012 is division_by_zero in SQLSTATE Class 22.
-
Numeric division and modulo guard zero divisors; direct SQL calls raise, while internal opt-error forms can report through a have_error soft path.
-
The int4 and int8 division and modulo functions check their second operand before using the arithmetic operator.
-
float4/float8 division raises for a zero divisor only when the numerator is not NaN, through the inline zero-divide helper.
-
Money division checks its divisor and interval division rejects a zero factor with the same condition.
-
The planner can evaluate an immutable constant division while simplifying expressions; same-tag conditional-expression documentation calls out unreachable CASE 1/0 as a planning-time failure boundary.
-
The retained numeric division case observed 22012 for 10::numeric / 0::numeric and 5.0000000000000000 after 10::numeric / 2::numeric on PostgreSQL 18.6 and 10.21; both repaired sessions were IDLE.
-
The locked catalogue records 22012 from 7.4 without proving an exact implementation introduction date.
运行记录
| 目标 | 服务器版本 | 结果 | 覆盖案例 |
|---|---|---|---|
| latest | 18.6 (Homebrew) | passed | division_by_zero_numeric |
| pg10 | 10.21 (Debian 10.21-1.pgdg90+1) | passed | division_by_zero_numeric |
同类错误代码
Class 22 数据异常 下的其他成员。
22000data_exception22001string_data_right_truncation22002null_value_no_indicator_parameter22003numeric_value_out_of_range22004null_value_not_allowed22005error_in_assignment22007invalid_datetime_format22008datetime_field_overflow22009invalid_time_zone_displacement_value2200Bescape_character_conflict2200Cinvalid_use_of_escape_character2200Dinvalid_escape_octet2200Fzero_length_character_string2200Gmost_specific_type_mismatch2200Hsequence_generator_limit_exceeded2200Lnot_an_xml_document2200Minvalid_xml_document2200Ninvalid_xml_content2200Sinvalid_xml_comment2200Tinvalid_xml_processing_instruction22010invalid_indicator_parameter_value22011substring_error22013invalid_preceding_or_following_size22014invalid_argument_for_ntile_function22015interval_field_overflow22016invalid_argument_for_nth_value_function22018invalid_character_value_for_cast22019invalid_escape_character2201Binvalid_regular_expression2201Einvalid_argument_for_logarithm2201Finvalid_argument_for_power_function2201Ginvalid_argument_for_width_bucket_function2201Winvalid_row_count_in_limit_clause2201Xinvalid_row_count_in_result_offset_clause22021character_not_in_repertoire22022indicator_overflow22023invalid_parameter_value22024unterminated_c_string22025invalid_escape_sequence22026string_data_length_mismatch22027trim_error2202Earray_subscript_error2202Ginvalid_tablesample_repeat2202Hinvalid_tablesample_argument22030duplicate_json_object_key_value22031invalid_argument_for_sql_json_datetime_function22032invalid_json_text22033invalid_sql_json_subscript22034more_than_one_sql_json_item22035no_sql_json_item22036non_numeric_sql_json_item22037non_unique_keys_in_a_json_object22038singleton_sql_json_item_required22039sql_json_array_not_found2203Asql_json_member_not_found2203Bsql_json_number_not_found2203Csql_json_object_not_found2203Dtoo_many_json_array_elements2203Etoo_many_json_object_members2203Fsql_json_scalar_required2203Gsql_json_item_cannot_be_cast_to_target_type22P01floating_point_exception22P02invalid_text_representation22P03invalid_binary_representation22P04bad_copy_file_format22P05untranslatable_character22P06nonstandard_use_of_escape_character