# P0001 — raise_exception（引发异常）

> PostgreSQL 使用 SQLSTATE P0001 表示没有显式条件名或 SQLSTATE 的 PL/pgSQL RAISE EXCEPTION。应按应用控制流诊断，并在正确的事务边界恢复。
---

## 速览 {#at-a-glance}

`P0001` 是 PostgreSQL 专用类别 P0（`PL/pgSQL Error`）中的 `raise_exception` 条件。不写条件名、也不写显式 SQLSTATE 的 `RAISE EXCEPTION` 会默认使用这个代码。报文由函数作者提供，因此 `P0001` 通常是应用或过程的控制信号，而不是某个服务器子系统的诊断。

严重级别和代码是两个选择。`EXCEPTION` 是 `RAISE` 的默认级别，通常会中止当前事务。`NOTICE`、`WARNING`、`INFO`、`LOG` 和 `DEBUG` 只按对应优先级生成消息；显式的 `ERRCODE` 也可以选择其他 SQLSTATE。读取时应始终同时记录严重级别和 SQLSTATE。

代表性案例 `plpgsql_raise_exception` 创建包含 `RAISE EXCEPTION 'calibration exception'` 的函数，调用它，再在同一自动提交连接上执行 `SELECT 1`。PostgreSQL 18.6 和 10.21 都返回 `P0001`、保留该报文，连接状态为 `IDLE`，并接受后续查询。下面的 SQL 摘录是共享注册表中的完整有序函数、调用和后续语句；实际运行中的模式限定函数名和清理由测试执行器统一管理。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `P0001` |
| 条件名 | `raise_exception` |
| 状态 | `有效` |
| 已知存在于 | `8.0.0` |
| 锁定快照 | `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_RAISE_EXCEPTION` |
| 别名 | `—` |

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

## 含义与触发路径 {#meaning}

18.6 目录把 `P0001` 放在 PostgreSQL 专用的 P0 类 `PL/pgSQL Error` 中，并命名为 `raise_exception`。在 PL/pgSQL 执行器中，只有在未提供代码且级别不低于 `ERROR` 时才设置默认代码；之后执行器会报告调用方的报文，并附带可选的详细信息、提示和对象字段。

语法支持几条不同路径：

- `RAISE EXCEPTION 'message'` 默认使用 `P0001`。
- `RAISE EXCEPTION condition_name` 使用该条件自己的 SQLSTATE，因此不一定是 `P0001`。
- `RAISE EXCEPTION SQLSTATE '5-character-code'` 或 `RAISE ... USING ERRCODE = ...` 可以让过程暴露特定领域契约。除 `00000` 外，PostgreSQL 允许使用自选的五字符代码。
- 较低级别的 `RAISE WARNING 'message'` 只按该优先级送出消息。即使调用方显式选择了错误码，它也不是 `RAISE EXCEPTION` 的同一种事务事件。

无参数的 `RAISE;` 是活动异常处理器中的重新抛出控制。它的行为和 SQLSTATE 由正在重新抛出的异常决定，不是一次新的默认 `P0001` 事件。

## 报文与诊断 {#messages}

代表性注册表操作如下：

<!-- BEGIN SQLSTATE SNIPPET: plpgsql_raise_exception -->
```sql
CREATE FUNCTION raise_exception_case() RETURNS void
LANGUAGE plpgsql AS $$
BEGIN
    RAISE EXCEPTION 'calibration exception';
END
$$;
SELECT raise_exception_case();
SELECT 1;
```
<!-- END SQLSTATE SNIPPET -->

实际运行的函数名由测试执行器生成并带模式限定。18.6 观察到的诊断是：

```text
SQLSTATE: P0001
severity: ERROR
message_primary: calibration exception
context: PL/pgSQL function ...raise_exception_case() line 3 at RAISE
source: pl_exec.c / exec_stmt_raise / line 3923
```

`P0001` 没有统一的英文主报文，具体内容由函数提供。`USING MESSAGE`、`DETAIL`、`HINT`、`COLUMN`、`CONSTRAINT`、`DATATYPE`、`TABLE` 和 `SCHEMA` 可以添加结构化诊断。消息文本可以本地化而 SQLSTATE 保持不变，因此应按 `P0001` 分支，再读取结构化字段，不要解析主报文字符串。

## 诊断 {#diagnosis}

先判断该代码是否来自有意的 `RAISE EXCEPTION`，还是函数显式选择了某个领域条件。记录 SQLSTATE、本地化和未本地化严重级别、主报文、详细信息、提示、上下文、源码位置以及调用函数的语句。在 PL/pgSQL 处理器中，`SQLSTATE` 和 `SQLERRM` 表示当前异常，`GET STACKED DIAGNOSTICS` 可以取出其字段。

随后定位函数分支及其事务上下文。自动提交下的失败调用只结束该语句，连接仍可执行下一条命令，代表性案例正是如此。显式事务中的调用通常会让事务进入中止状态，直到客户端执行 `ROLLBACK` 或回滚到保存点；服务器连接本身不一定终止。

`EXCEPTION` 子句会改变边界。受保护主体在子事务中运行；主体出错后，主体内对持久数据库状态的修改会先回滚，再执行第一个匹配的条件处理器，块外的修改仍保留。`WHEN OTHERS` 匹配除 `QUERY_CANCELED` 和 `ASSERT_FAILURE` 以外的所有错误，范围很宽，不能用它代替对目标 `P0001` 路径的识别。

## 处理 {#response}

当过程确实要暴露通用的 PL/pgSQL `RAISE` 契约时使用 `P0001`。如果调用方需要区分校验、冲突、配额或其他业务结果，应选择并记录合适的 SQLSTATE，并补充便于处理的 `DETAIL` 或 `HINT`。不要为了简化应用代码就把无关的服务器错误都改成 `P0001`。

在客户端边界，显式事务需要先回滚后再执行无关命令；如果可以隔离函数调用，则使用保存点。在 PL/pgSQL 中，恢复逻辑明确时优先使用 `WHEN raise_exception` 或 `WHEN SQLSTATE 'P0001'` 这样的窄处理器。如果确实需要宽处理器，请先保存 `RETURNED_SQLSTATE`、`MESSAGE_TEXT`、`PG_EXCEPTION_DETAIL`、`PG_EXCEPTION_HINT` 和上下文，再决定继续还是重新抛出。

自然的 `RAISE EXCEPTION` 案例可以安全演示，因为它测试的正是预期的 PL/pgSQL 机制。这并不使 `P0001` 成为服务器故障证据；它是函数明确引发的异常。

## 版本 {#versions}

目录记录 `P0001` 存在于锁定的 8.0.0–8.4.22 pre-9.0 正式源码、9.0.23 至 18.6 的全部正式快照及 19 Beta 3 预览快照。同 tag 的 `REL8_1_4` `errcodes.sgml` 表已经列出 `P0001` 和条件名 `raise_exception`，因此至少可以确认 8.1.4 已有该条件名。9.0 头文件视图中的类标题为 `PL/pgSQL Error (PostgreSQL-specific error class)`，到 9.1 的文本定义变为 `PL/pgSQL Error`。7.0–7.3 仍有候选源码缺口；这些是目录观察边界，不是实现引入日期的断言。

18.6 固定源码 commit 为 `724edf9bde9d356724ad384a2e196edc3c9f80f7`。默认 `RAISE EXCEPTION` 规则有 PostgreSQL 18 文档和 18.6 执行器源码两方面依据。代表性案例在 PostgreSQL 18.6 和 10.21 上通过；它没有覆盖每一种自定义代码、处理器或事务模式。

## 相关 {#related}

[`P0000` — `plpgsql_error`](../p0000/) 是更宽的 PL/pgSQL 专用类条件。[`P0002` — `no_data_found`](../p0002/)、[`P0003` — `too_many_rows`](../p0003/) 和 [`P0004` — `assert_failure`](../p0004/) 是独立的 P0 条件。[`25P02` — `in_failed_sql_transaction`](../25p02/) 描述未处理异常中止显式事务后的后续状态。[`XX000` — `internal_error`](../xx000/) 是服务器内部错误代码，不应作为有意 `RAISE` 的同义词。

## 来源 {#sources}

结构化证据记录在[公开证据 JSON](../../data/evidence/p0001.json)中。源码记录固定到 PostgreSQL commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`；运行记录保留共享注册表、两个目标摘要和 `P0001-snippet-registry-final-20260909` 运行 ID。

- `src.errcodes.18.6` — [`errcodes.txt`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L487-L494)
- `src.pl-exec.18.6` — [`pl_exec.c`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/pl/plpgsql/src/pl_exec.c#L3889-L3923)
- `doc.plpgsql.18` — [Errors and Messages](https://www.postgresql.org/docs/18/plpgsql-errors-and-messages.html) 与 [Error Trapping](https://www.postgresql.org/docs/18/plpgsql-control-structures.html)
- `doc.protocol.18` — [Error and Notice Message Fields](https://www.postgresql.org/docs/18/protocol-error-fields.html)
- `doc.transactions.18` — [Transactions](https://www.postgresql.org/docs/18/tutorial-transactions.html)
