百科 / 错误代码 / Class P0 PL/pgSQL错误
P0004 assert_failure
ERROR 已实测 详解 实测通过
- 条件名
assert_failure- 宏名称
ERRCODE_ASSERT_FAILURE- 启用版本
- 9.5
- 状态
- 活跃
版本覆盖
速览
P0004 是 PL/pgSQL assert_failure。固定 ASSERT 执行路径确认它以 ERROR 报告,并按是否提供 message 选择错误文本;运行案例已在 PostgreSQL 18.6 和 10.21 上通过。
共享的 assertion_failure_recovery 案例已在 PostgreSQL 18.6 和 PostgreSQL 10.21 上通过。启用 plpgsql.check_asserts 后,失败的 ASSERT 返回 P0004 和 value must be positive,显式事务进入 INERROR;ROLLBACK 恢复为 IDLE,有效输入执行成功,关闭断言的对照调用返回零。
CREATE OR REPLACE FUNCTION p0004_assert(value integer) RETURNS integer LANGUAGE plpgsql AS $$ BEGIN ASSERT value > 0, 'value must be positive'; RETURN value; END $$;
SET plpgsql.check_asserts = on;
SHOW plpgsql.check_asserts;
BEGIN;
SELECT p0004_assert(0);
ROLLBACK;
SELECT p0004_assert(1);
SET plpgsql.check_asserts = off;
SHOW plpgsql.check_asserts;
SELECT p0004_assert(0);
SET plpgsql.check_asserts = on;
SHOW plpgsql.check_asserts;
SELECT p0004_assert(1);
含义
ASSERT 是 PL/pgSQL 中执行的不变量检查。PostgreSQL 计算其 Boolean 条件;结果为 false 或 NULL 时进入断言失败路径。固定执行器随后以 ERROR 和 SQLSTATE P0004 报告:只在进入该路径后计算 message 表达式,非 NULL 结果成为主报文,NULL 或省略 message 时使用 assertion failed。关闭检查时,条件和 message 表达式都会跳过。
plpgsql.check_asserts 是按会话生效的设置,用来控制是否检查 ASSERT。启用时,失败断言是真正的 ERROR,显式事务会进入 INERROR;关闭时 ASSERT 会被跳过,同一个 false 输入不能证明不变量成立,也不能替代生产环境的输入校验。
选定运行覆盖了带非 NULL message 的 false 条件,以及关闭检查时的 false 条件。NULL 条件和无 message 的回退文本是源码确认的语义,不是本批额外的自然运行观察。
报文模板
源码里的格式串,不是某一次运行的输出。%s 之类是占位符,实际报文会填入对象名与取值。适用范围一栏是核验时留下的原始英文记录,未经翻译。
诊断
先把 ErrorResponse 字段与服务器日志中的同一条记录对照。固定 18.6 路径的 message_primary 是计算后的 message 或 assertion failed;运行案例还记录了 ERROR、P0004、exec_stmt_assert,以及指向 PL/pgSQL 函数和 line 1 at ASSERT 的 context。要在出错的同一个会话执行 SHOW plpgsql.check_asserts,其他连接的设置不会影响它。
如果主报文是 assertion failed,检查 ASSERT 是否没有 message,或 message 表达式是否计算为 NULL。如果完全没有 P0004,先检查 SHOW plpgsql.check_asserts:关闭时会在计算条件前跳过检查。比较调用时要同时保留函数源码和会话设置;连接池中的另一个连接可能有不同设置,即使它们调用的是同一个函数。
区分程序不变量和预期业务输入。value > 0 这类 ASSERT 适合检查经过验证后本应永远成立的假设;预期的负数等业务输入应使用普通校验、约束或明确的应用错误。若错误发生在显式事务中,先检查事务状态再发送下一条命令:运行案例中事务在 ROLLBACK 前是 INERROR,并不是连接已经失效。
处置
显式事务中先执行 ROLLBACK,再以修正后的不变量或输入重现。共享案例验证了 ROLLBACK → IDLE、有效调用返回 1,以及把 plpgsql.check_asserts 设为 off 后同一个 false 输入不再报告断言。
如果 PL/pgSQL block 有意处理这个命名条件,可以使用 WHEN ASSERT_FAILURE。WHEN OTHERS 不会捕获 ASSERT_FAILURE,因此不能靠宽泛 handler 隐藏断言失败。异常块可以按文档规定的子事务边界恢复,但吞掉错误前必须检查不变量;关闭断言只是诊断对照,不是修复。
可复现案例
在一次性实例上执行过的场景。其中 1 个附有可执行 SQL,正文相应小节里给出。
assertion_failure_recovery PG 10 / 18 有 SQL
前置条件
- A disposable runner target has PL/pgSQL available.
- plpgsql.check_asserts is explicitly enabled before the trigger.
触发
Call a PL/pgSQL function whose ASSERT condition is false inside an explicit transaction.
断言
- The natural ASSERT failure has SQLSTATE P0004 and the expected diagnostic.
- The failed explicit transaction is INERROR and ROLLBACK returns it to IDLE.
- A valid call succeeds after rollback, and an explicit check_asserts=off control suppresses the assertion.
- Re-enabling check_asserts makes the valid call succeed and the runner cleans up.
处置
Rollback the failed transaction, use a valid input, and keep assertions enabled for production behavior.
清理
Drop the case schema with an owner connection.
版本
锁定目录从 9.5.0 记录 P0004,并在列出的正式快照及 19beta3 中出现。固定执行器源码是 PostgreSQL 18.6 的 pl_exec.c#L3965-L3968。官方 PL/pgSQL 错误和消息文档说明 ASSERT 和命名条件;控制结构中的错误捕获文档定义 EXCEPTION 子事务及 handler 匹配边界。最新版本与 PG10 的运行案例确认了共享函数中的 P0004 和事务行为。
来源
- 上游源码 doc/src/sgml/plpgsql.sgml
- 上游源码 doc/src/sgml/plpgsql.sgml
- 上游源码 src/backend/utils/errcodes.txt 第 494 行
- 上游源码 src/pl/plpgsql/src/pl_exec.c 第 3965–3968 行
- 核验材料 verify/results/P0004-same-session-latest-final/latest/summary.json
- 核验材料 verify/results/P0004-same-session-latest-final/latest/raw.jsonl
- 核验材料 verify/results/P0004-same-session-pg10-final/pg10/summary.json
- 核验材料 verify/results/P0004-same-session-pg10-final/pg10/raw.jsonl
- 核验材料 verify/cases/P0004/snippets.json
- 核验材料 raw/calls/REL_18_6.jsonl
证据
断言
每条断言都写明了是怎么核实的,以及它不覆盖什么。这一层是核验时留下的原始英文记录,照原样呈现,未经翻译。
-
P0004 is assert_failure in PostgreSQL-specific Class P0.
-
PL/pgSQL assertion execution reports P0004 at ERROR level, using the evaluated message when present and `assertion failed` when no message is supplied.
-
The shared assertion_failure_recovery case passed on PostgreSQL 18.6 and PostgreSQL 10.21: the false ASSERT returned P0004 with value must be positive, the explicit transaction was INERROR until ROLLBACK, a valid input succeeded, and the disabled-assert control returned zero.
-
The latest target recorded ERROR/P0004 from exec_stmt_assert at pl_exec.c:3968 with context at ASSERT; the PG10 target recorded the same SQLSTATE and diagnostic with its version-specific source line.
-
The fixed PL/pgSQL ASSERT path reports P0004 when its Boolean condition evaluates false or NULL; the supplied message expression is used as primary text, otherwise the executor uses assertion failed.
-
plpgsql.check_asserts controls assertion checking for the session, and the PL/pgSQL handler rules exclude ASSERT_FAILURE from WHEN OTHERS while allowing a named ASSERT_FAILURE condition.
-
The observed case entered INERROR after P0004, returned to IDLE after ROLLBACK, and then accepted a valid call; disabling check_asserts suppressed the false assertion in the control branch.
运行记录
| 目标 | 服务器版本 | 结果 | 覆盖案例 |
|---|---|---|---|
| latest | 18.6 (Homebrew) | passed | assertion_failure_recovery |
| pg10 | 10.21 (Debian 10.21-1.pgdg90+1) | passed | assertion_failure_recovery |
同类错误代码
Class P0 PL/pgSQL错误 下的其他成员。