P0004 — assert_failure
PL/pgSQL 的 ASSERT 断言条件不成立。
英文手册覆盖始于 PostgreSQL 8.1. 来源历史与运行验证各自保留独立版本边界。
当前阅读 PG 18·选择有来源记录的版本
- aliases
- 未知
- class name
- PL/pgSQL Error
- condition name
- assert_failure
- lang
- en
- sqlstate
- P0004
版本定义 PG 18
P0004
速览 {#at-a-glance}
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);
含义 {#meaning}
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 的回退文本是源码确认的语义,不是本批额外的自然运行观察。
诊断 {#diagnosis}
先把 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,并不是连接已经失效。
处置 {#response}
显式事务中先执行 ROLLBACK,再以修正后的不变量或输入重现。共享案例验证了 ROLLBACK → IDLE、有效调用返回 1,以及把 plpgsql.check_asserts 设为 off 后同一个 false 输入不再报告断言。
如果 PL/pgSQL block 有意处理这个命名条件,可以使用 WHEN ASSERT_FAILURE。WHEN OTHERS 不会捕获 ASSERT_FAILURE,因此不能靠宽泛 handler 隐藏断言失败。异常块可以按文档规定的子事务边界恢复,但吞掉错误前必须检查不变量;关闭断言只是诊断对照,不是修复。
版本 {#versions}
锁定目录从 9.5.0 记录 P0004,并在列出的正式快照及 19beta3 中出现。固定执行器源码是 PostgreSQL 18.6 的 pl_exec.c#L3965-L3968。官方 PL/pgSQL 错误和消息文档说明 ASSERT 和命名条件;控制结构中的错误捕获文档定义 EXCEPTION 子事务及 handler 匹配边界。最新版本与 PG10 的运行案例确认了共享函数中的 P0004 和事务行为。
相关条件 {#related}
P0002 是 PL/pgSQL 无数据条件, P0003 是严格多行条件, P0000 是 PL/pgSQL 错误类别。应以响应中的实际 SQLSTATE 为准,不要把所有 PL/pgSQL 失败都归为 P0004。
来源 {#sources}
固定实现见 pl_exec.c#L3965-L3968。官方 PL/pgSQL 错误和消息参考说明 ASSERT 语义;控制结构中的错误捕获参考说明 WHEN OTHERS 排除项和子事务边界。结构化的证据记录固定了源码 SHA、运行摘要、原始结果和共享片段注册表。
比较版本
SQLSTATE 证据与已记录的运行版本
cases · assertion_failure_recovery
{
"assertions": [
"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."
],
"case_id": "assertion_failure_recovery",
"cleanup": "Drop the case schema with an owner connection.",
"has_snippet": true,
"position": 0,
"preconditions": [
"A disposable runner target has PL/pgSQL available.",
"plpgsql.check_asserts is explicitly enabled before the trigger."
],
"repair": "Rollback the failed transaction, use a valid input, and keep assertions enabled for production behavior.",
"trigger": "Call a PL/pgSQL function whose ASSERT condition is false inside an explicit transaction.",
"versions": [
"10",
"18"
]
}claims · identity
{
"claim_id": "identity",
"limits": "",
"method": "Read the fixed definition row.",
"position": 0,
"runtime": [],
"sources": [
"src.errcodes.18.6"
],
"statement": "P0004 is assert_failure in PostgreSQL-specific Class P0."
}claims · assert-path
{
"claim_id": "assert-path",
"limits": "",
"method": "Read the fixed `ASSERT` execution path and both errmsg branches.",
"position": 1,
"runtime": [],
"sources": [
"src.path.plpgsql"
],
"statement": "PL/pgSQL assertion execution reports P0004 at ERROR level, using the evaluated message when present and `assertion failed` when no message is supplied."
}claims · runtime.assertion-case
{
"claim_id": "runtime.assertion-case",
"limits": "This is a bounded case for the shared function and ASSERT setting; it does not establish behavior for arbitrary assertion messages or non-PL/pgSQL code.",
"method": "Read both target summaries and the fixed snippet registry.",
"position": 2,
"runtime": [],
"sources": [
"runtime.P0004-same-session-latest-final.latest",
"runtime.P0004-same-session-pg10-final.pg10",
"snippet-registry.P0004"
],
"statement": "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."
}claims · runtime.diagnostics
{
"claim_id": "runtime.diagnostics",
"limits": "",
"method": "Compare structured diagnostics in both target summaries.",
"position": 3,
"runtime": [],
"sources": [
"runtime.P0004-same-session-latest-final.latest",
"runtime.P0004-same-session-pg10-final.pg10"
],
"statement": "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."
}claims · assert-semantics
{
"claim_id": "assert-semantics",
"limits": "",
"method": "Read the fixed executor branch and the official PL/pgSQL ASSERT documentation.",
"position": 4,
"runtime": [],
"sources": [
"src.path.plpgsql",
"doc.plpgsql.18"
],
"statement": "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."
}claims · settings-and-handler-boundary
{
"claim_id": "settings-and-handler-boundary",
"limits": "The setting is session-scoped; this claim does not make a connection-level setting change global.",
"method": "Read the PL/pgSQL control-structures error-trapping section for EXCEPTION subtransactions and condition matching; read the fixed executor for the session setting boundary.",
"position": 5,
"runtime": [],
"sources": [
"doc.plpgsql-control.18",
"src.path.plpgsql"
],
"statement": "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."
}claims · transaction-recovery
{
"claim_id": "transaction-recovery",
"limits": "",
"method": "Read both passed target summaries and the ordered shared registry.",
"position": 6,
"runtime": [],
"sources": [
"runtime.P0004-same-session-latest-final.latest",
"runtime.P0004-same-session-pg10-final.pg10",
"snippet-registry.P0004"
],
"statement": "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."
}messages · message.assert
{
"limits": "",
"message_id": "message.assert",
"path": "",
"position": 0,
"raw": {
"alternate_template": "%s",
"id": "message.assert",
"primary_template": "assertion failed",
"severity_source": "ERROR",
"sources": [
"src.path.plpgsql"
],
"sqlstate": "P0004"
},
"severity": "ERROR",
"sources": [
"src.path.plpgsql"
],
"templates": [
{
"kind": "primary",
"literal": "assertion failed",
"position": 0,
"role": "",
"template": "assertion failed"
},
{
"kind": "primary",
"literal": "",
"position": 1,
"role": "alternate",
"template": "%s"
}
]
}runtimes · runtime.P0004-same-session-latest-final.latest · 18.6 (Homebrew) · passed
{
"cases": [
"assertion_failure_recovery"
],
"limits": "",
"position": 0,
"raw": {
"cases": [
"assertion_failure_recovery"
],
"id": "runtime.P0004-same-session-latest-final.latest",
"observed": {
"after_error": "INERROR",
"after_rollback": "IDLE",
"disabled_control_result": 0,
"message_primary": "value must be positive",
"repair_result": 1,
"severity": "ERROR",
"sqlstate": "P0004"
},
"raw": "verify/results/P0004-same-session-latest-final/latest/raw.jsonl",
"raw_sha256": "2b03b5469ea34a0e49ad4a71e93cef4b19df0d01d86fc1156e478d88bc93168c",
"run_id": "P0004-same-session-latest-final",
"server_version": "18.6 (Homebrew)",
"server_version_num": 180006,
"status": "passed",
"summary": "verify/results/P0004-same-session-latest-final/latest/summary.json",
"summary_sha256": "5246ae67ee8dd29e769c3a03db7bfb6cde2f201c9f45993201fb269cc53988ad",
"target": "latest"
},
"run_id": "P0004-same-session-latest-final",
"runtime_id": "runtime.P0004-same-session-latest-final.latest",
"server_version": "18.6 (Homebrew)",
"status": "passed",
"target": "latest"
}runtimes · runtime.P0004-same-session-pg10-final.pg10 · 10.21 (Debian 10.21-1.pgdg90+1) · passed
{
"cases": [
"assertion_failure_recovery"
],
"limits": "",
"position": 1,
"raw": {
"cases": [
"assertion_failure_recovery"
],
"id": "runtime.P0004-same-session-pg10-final.pg10",
"observed": {
"after_error": "INERROR",
"after_rollback": "IDLE",
"disabled_control_result": 0,
"message_primary": "value must be positive",
"repair_result": 1,
"severity": "ERROR",
"sqlstate": "P0004"
},
"raw": "verify/results/P0004-same-session-pg10-final/pg10/raw.jsonl",
"raw_sha256": "2369b62eb7c22d99122c9929134a878f2f3c0fe828887ae657f87b7b95e07ac8",
"run_id": "P0004-same-session-pg10-final",
"server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
"server_version_num": 100021,
"status": "passed",
"summary": "verify/results/P0004-same-session-pg10-final/pg10/summary.json",
"summary_sha256": "50819f9620584f17766ffb62e2bb1c14e5aaa9162f91ec6cd26795cced3e6d30",
"target": "pg10"
},
"run_id": "P0004-same-session-pg10-final",
"runtime_id": "runtime.P0004-same-session-pg10-final.pg10",
"server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
"status": "passed",
"target": "pg10"
}sources · src.errcodes.18.6
{
"commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
"docs_url": "",
"kind": "upstream_source",
"location": "",
"path": "src/backend/utils/errcodes.txt",
"position": 0,
"sha256": "6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba",
"source_id": "src.errcodes.18.6",
"tag": "REL_18_6",
"url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L494"
}sources · src.calls.REL_18_6
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "raw/calls/REL_18_6.jsonl",
"position": 1,
"sha256": "9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf",
"source_id": "src.calls.REL_18_6",
"tag": "",
"url": ""
}sources · src.path.plpgsql
{
"commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
"docs_url": "",
"kind": "upstream_source",
"location": "",
"path": "src/pl/plpgsql/src/pl_exec.c",
"position": 2,
"sha256": "0df3c70a6bccf6dddf443fb99e152a16a67009827c44a820597e98e585cb8b20",
"source_id": "src.path.plpgsql",
"tag": "REL_18_6",
"url": "https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/pl/plpgsql/src/pl_exec.c#L3965-L3968"
}sources · runtime.P0004-same-session-latest-final.latest
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "verify/results/P0004-same-session-latest-final/latest/summary.json",
"position": 3,
"sha256": "5246ae67ee8dd29e769c3a03db7bfb6cde2f201c9f45993201fb269cc53988ad",
"source_id": "runtime.P0004-same-session-latest-final.latest",
"tag": "",
"url": ""
}sources · runtime.P0004-same-session-latest-final.latest.raw
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "verify/results/P0004-same-session-latest-final/latest/raw.jsonl",
"position": 4,
"sha256": "2b03b5469ea34a0e49ad4a71e93cef4b19df0d01d86fc1156e478d88bc93168c",
"source_id": "runtime.P0004-same-session-latest-final.latest.raw",
"tag": "",
"url": ""
}sources · runtime.P0004-same-session-pg10-final.pg10
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "verify/results/P0004-same-session-pg10-final/pg10/summary.json",
"position": 5,
"sha256": "50819f9620584f17766ffb62e2bb1c14e5aaa9162f91ec6cd26795cced3e6d30",
"source_id": "runtime.P0004-same-session-pg10-final.pg10",
"tag": "",
"url": ""
}sources · runtime.P0004-same-session-pg10-final.pg10.raw
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "verify/results/P0004-same-session-pg10-final/pg10/raw.jsonl",
"position": 6,
"sha256": "2369b62eb7c22d99122c9929134a878f2f3c0fe828887ae657f87b7b95e07ac8",
"source_id": "runtime.P0004-same-session-pg10-final.pg10.raw",
"tag": "",
"url": ""
}sources · snippet-registry.P0004
{
"commit": "",
"docs_url": "",
"kind": "local_artifact",
"location": "",
"path": "verify/cases/P0004/snippets.json",
"position": 7,
"sha256": "ef124f75ec61be666abc87ff431ebfb496dfa3352f78a3bee4432430dd68c229",
"source_id": "snippet-registry.P0004",
"tag": "",
"url": ""
}sources · doc.plpgsql.18
{
"commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
"docs_url": "",
"kind": "upstream_source",
"location": "",
"path": "doc/src/sgml/plpgsql.sgml",
"position": 8,
"sha256": "43d705adbded343336341fd3e34ea8940432e8a8211f737d36ba3041ad0a4a51",
"source_id": "doc.plpgsql.18",
"tag": "PG18-docs",
"url": "https://www.postgresql.org/docs/18/plpgsql-errors-and-messages.html"
}sources · doc.plpgsql-control.18
{
"commit": "724edf9bde9d356724ad384a2e196edc3c9f80f7",
"docs_url": "",
"kind": "upstream_source",
"location": "",
"path": "doc/src/sgml/plpgsql.sgml",
"position": 9,
"sha256": "43d705adbded343336341fd3e34ea8940432e8a8211f737d36ba3041ad0a4a51",
"source_id": "doc.plpgsql-control.18",
"tag": "PG18-docs",
"url": "https://www.postgresql.org/docs/18/plpgsql-control-structures.html#PLPGSQL-ERROR-TRAPPING"
}来源引用
定义来源
center · PostgreSQL 18 · english-manuals:7b52e2fa72c2635d8c6f026e04c4234c170292416ad23065c2234b43e0c4d270
正文语言: zh-Hans · english-manuals:7b52e2fa72c2635d8c6f026e04c4234c170292416ad23065c2234b43e0c4d270