# 3B001 — invalid_savepoint_specification（保存点说明无效）

> PostgreSQL SQLSTATE 3B001：缺失保存点、事务状态与恢复。
---

# 3B001 — invalid_savepoint_specification（保存点说明无效）

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

`3B001` 表示保存点操作引用了当前事务层级中不存在的保存点。它不同于 `25P01`：后者表示根本没有活动事务块。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `3B001` |
| 条件名 | `invalid_savepoint_specification` |
| 状态 | `有效` |
| 已知存在于 | `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_S_E_INVALID_SPECIFICATION` |
| 别名 | `—` |

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

## 含义 {#meaning}

当 `ROLLBACK TO SAVEPOINT` 或相关操作无法在当前保存点栈中找到名称时，事务管理器会发出 `3B001`。PG18 源码模板包含名称；PG10 源码仍使用较早的 `no such savepoint` 措辞。缺失名称是事务结构问题，不是表数据冲突。

## 诊断 {#diagnosis}

保存 SQLSTATE、主报文、事务状态和完整保存点序列。检查拼写、保存点是否已经 RELEASE，以及嵌套过程或子事务是否改变了当前层级。显式事务中的 ERROR 会令连接处于 `INERROR`；回滚之前不要在那里继续执行目录查询或修复 SQL。

## 处理 {#response}

回滚失败块，开启新的显式事务，并建立名称和嵌套层级都明确的保存点。如果希望保留部分工作，应在风险语句前建立正确名称的保存点，并只回滚到已存在的标记；错误发生后不能凭空恢复缺失标记。重试应视为新的事务计划，并保留业务幂等检查。

## 报文 {#messages}

18.6 固定源码包含 `savepoint "%s" does not exist` 及当前层级的相关变体；选定 PG10 输出为 `no such savepoint`。这些是有源码依据的版本/调用差异，应根据 `3B001` 和事务状态处理，不要依赖单一英文完整报文。

## 代表案例 {#case}

`verify/cases/3B001/snippets.json`（SHA-256 `595ce7692e34d36cc31df94c65c7040ee9bc4558fa887c23b56ba3187d235deb`）包含显式 `BEGIN`、已存在的 `present` 保存点、`ROLLBACK TO SAVEPOINT missing`，以及回滚后的修复序列。见[公开案例导出](../../data/cases/3b001.json)和[结构化证据](../../data/evidence/3b001.json)。

<!-- BEGIN SQLSTATE SNIPPET: invalid_savepoint_recovery -->

```sql
BEGIN;
SAVEPOINT present;
ROLLBACK TO SAVEPOINT missing;
ROLLBACK;
BEGIN;
SAVEPOINT present;
RELEASE SAVEPOINT present;
COMMIT;
```
<!-- END SQLSTATE SNIPPET -->

运行器使用 autocommit 只是为了让 registry 中的 `BEGIN`/`ROLLBACK`/`COMMIT` 边界完全显式；它断言缺失标记后是 `INERROR`，恢复提交后是 `IDLE`。

选定案例在 PostgreSQL 18.6 和 10.21 观察到 `3B001`。PG18 报 `savepoint "missing" does not exist`，PG10 报 `no such savepoint`；两者都让显式事务在 `ROLLBACK` 前保持 `INERROR`，之后合法保存点序列可以提交。

## 版本 {#versions}

目录从 8.0.0 或更早边界到正式快照都记录了 `3B001`。选定的 18.6 与 10.21 运行证明 SQLSTATE 和恢复边界；主要报文则从旧版泛化措辞变为包含保存点名称的模板。

## 相关 {#related}

可对照[3B000 保存点异常](../3b000/)以及没有事务块时的[25P01 无活动 SQL 事务](../25p01/)。

## 来源 {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt)（SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`）
- [`src.xact.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/access/transam/xact.c#L4482-L4484)（SHA-256 `75b012c0b047d1dc905a30975c244beec366e45eac0dbf109fd21bcd611a8e39`）
- [`src.xact.10.23`](https://github.com/postgres/postgres/blob/02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4/src/backend/access/transam/xact.c#L3914-L3916)（SHA-256 `9120bd418bd0f18e3f1065a3772c92ff68f3be54e1a8c6d415043197f6daa47c`）
- [`SAVEPOINT 官方文档`](https://www.postgresql.org/docs/18/sql-savepoint.html) · 本地调用扫描 `src.calls.REL_18_6`（SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`）
