22012
Read PG 18 manual ↗division_by_zero
- SQLSTATE
- 22012
- Condition name
- division_by_zero
- Class
- Data Exception
- Source macro
- ERRCODE_DIVISION_BY_ZERO
- Evidence
- Observed at runtime in the source evidence
English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗
At a glance
A division or modulo operation reached a zero divisor. PostgreSQL 18.6 uses SQLSTATE 22012 in numeric, integer, floating-point, money, and interval-division paths, while each path keeps its own operand and special-value rules.
SELECT 10::numeric / 0::numeric;
SELECT 10::numeric / 2::numeric;
The retained PostgreSQL 18.6 and 10.21 observations show the first statement returning primary division by zero with SQLSTATE 22012, then the corrected statement returning 5.0000000000000000; both backends were IDLE after the repaired action. The public case JSON identifies this numeric division case; it is not a runtime comparison of every producer listed below.
Meaning
The direct numeric functions numeric_div and numeric_mod guard a zero numeric divisor; their internal *_opt_error forms can instead set a have_error flag and return NULL to a caller that handles the failure. Integer division and modulo functions check their zero second operand before using / or %. Float4/float8 division routes through float*_div, which raises for a zero divisor when the numerator is not NaN. Money division checks its integer or money divisor, and interval division checks its float factor. These paths share the SQLSTATE and primary text, but the resolved operator and special values still determine the result.
Diagnosis
Locate the denominator or modulo divisor after operator/type resolution; do not infer an int4 rule for all numeric types. Check whether zero came from input, a join, an aggregate, or a business rule, and inspect the complete primary message. A NULL operand normally produces a NULL result before a strict arithmetic function is called, while NULLIF(denominator, 0) deliberately turns the zero case into NULL. A CASE expression can choose a NULL, substitute, or skip policy, so choose the branch that matches the business result rather than silently changing an error into a value.
The planning boundary matters: the PostgreSQL 18.6 conditional-expression documentation warns that a constant 1/0 subexpression can fail during planning even in an unreachable CASE arm. The planner source recursively simplifies constant arguments and evaluates immutable constant expressions. Use a non-constant value or guard the actual denominator with NULLIF/a suitable CASE when the division must be deferred to execution; this does not make an invalid constant expression safe, and it does not decide whether NULL or a substitute is correct for the application.
Response
Repair the producer of the zero divisor or select an explicit zero policy. Use NULLIF when a NULL result is meaningful, or a CASE branch when the application has a documented substitute or skip rule; then validate the downstream aggregates and filters that will see that result. Do not replay the unchanged operation. The ordinary fixed paths raise ERROR; in an explicit transaction use ROLLBACK or ROLLBACK TO SAVEPOINT before retrying, while autocommit can retry only the corrected action. The retained 18.6/10.21 numeric observation is limited to this case.
Messages
- Primary,
ERROR:division by zero. - No fixed DETAIL or HINT is attached to the cited numeric, integer, float, money, or interval-division guards. Internal numeric
have_errorcallers are a soft handling path, not a claim that a direct SQL operator returns successfully.
Versions
The locked catalogue records this condition from 7.4; fixed source coverage is PostgreSQL 18.6. The retained numeric runtime records are PostgreSQL 18.6 and 10.21 and cover only 10::numeric / 0::numeric followed by 10::numeric / 2::numeric.
Sources
src/backend/utils/adt/numeric.c#L3243-L3375covers numeric division, special values, the directERROR, and the internalhave_errorbranch;#L3467-L3553covers numeric modulo.src/backend/utils/adt/int.c#L861-L875and#L1158-L1184show int division and modulo;int8.chas the corresponding bigint guards.src/include/utils/float.h#L222-L250andsrc/backend/utils/adt/float.c#L101-L106show float4/float8 zero-divisor dispatch and the primary.src/backend/utils/adt/cash.c#L155-L161and#L728-L744show money division guards;src/backend/utils/adt/timestamp.c#L3759-L3776shows interval division by a zero factor.src/backend/optimizer/util/clauses.c#L2437-L2462shows constant simplification/evaluation; the same-tag conditional-expression documentation states the unreachable-CASE1/0planning boundary.
The structured evidence record retains both runtime summaries/raw digests and the fixed source claims.
Source evidence
Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.
22012 is division_by_zero in SQLSTATE Class 22.
Method: Read the fixed errcodes row and macro.
Numeric division and modulo guard zero divisors; direct SQL calls raise, while internal opt-error forms can report through a have_error soft path.
Method: Read the complete numeric division and modulo functions.
The int4 and int8 division and modulo functions check their second operand before using the arithmetic operator.
Method: Read the representative integer and bigint guards.
src.int-div.18.6 src.int-mod.18.6 src.int8-div.18.6 src.int8-mod.18.6
float4/float8 division raises for a zero divisor only when the numerator is not NaN, through the inline zero-divide helper.
Method: Read the exact val2==0 and !isnan(val1) guard.
Money division checks its divisor and interval division rejects a zero factor with the same condition.
Method: Read the type-specific division guards.
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.
Method: Read the planner evaluation path and same-tag documentation.
Limits: This is a planning/execution boundary, not an assertion that every CASE expression is evaluated early.
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.
Method: Read both retained summaries, raw digests, and the fixed snippet registry.
Limits: The observation covers this numeric case and does not generalize to every producer.
runtime.22012-data-runtime-latest.latest runtime.22012-data-runtime-pg10.pg10 snippet-registry.22012
The locked catalogue records 22012 from 7.4 without proving an exact implementation introduction date.
Method: Use catalogue presence and history boundaries.
- src/backend/utils/errcodes.txt · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/numeric.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/numeric.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/int.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/int.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/int8.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/int8.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/include/utils/float.h · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/float.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/cash.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/cash.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/utils/adt/timestamp.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - src/backend/optimizer/util/clauses.c · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 - doc/src/sgml/func.sgml · REL_18_6 ·
724edf9bde9d356724ad384a2e196edc3c9f80f7 verify/cases/22012/snippets.json· ·
Message templates
ERROR · message.division-by-zero
Primary
division by zero
Direct cited guards; special values and internal numeric soft callers are type/context dependent.
Reproduction & repair cases
division_by_zero_numeric · PG 10, 18
Preconditions
- A runner-owned disposable target is provisioned.
Trigger: Evaluate a numeric expression with a zero denominator.
Expected assertions
- SQLSTATE is 22012 with primary division by zero
- The failed autocommit session remains IDLE
- A nonzero denominator returns the defined numeric result
Repair: 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.
Cleanup: Close the runner connection and drop the case schema with an owner connection.
Recorded runtime evidence
18.6 (Homebrew) · passed
Run: 22012-data-runtime-latest
{
"sqlstate": "22012",
"status_after_repair": "IDLE"
}10.21 (Debian 10.21-1.pgdg90+1) · passed
Run: 22012-data-runtime-pg10
{
"sqlstate": "22012",
"status_after_repair": "IDLE"
}Definition snapshot: english-manuals:91bbab6760ed934e46e985b4e0a… · English manual source