# 42P03 — 重复游标

> PostgreSQL SQLSTATE 42P03（duplicate_cursor）的来源与诊断参考。
---

# 42P03 — 重复游标

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

`42P03`（**duplicate_cursor**）报告游标或 portal 名称冲突，但同一 SQLSTATE 覆盖 ERROR、替换时的 WARNING，以及 PL/pgSQL 活动游标的 ERROR。调用方和 `CreatePortal` 选项决定具体行为。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `42P03` |
| 条件名 | `duplicate_cursor` |
| 状态 | `有效` |
| 已知存在于 | `7.4` |
| 锁定快照 | `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_DUPLICATE_CURSOR` |
| 别名 | `—` |

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

## 含义 {#meaning}

`CreatePortal(name, allowDup, dupSilent)` 在 `allowDup` 为 false 时报告 ERROR `cursor "%s" already exists`。`allowDup` 为 true 时，若 `dupSilent` 未设置，会先发 WARNING `closing existing cursor "%s"`，随后删除旧 portal 并创建新 portal。PL/pgSQL 是另一条路径：`SPI_cursor_find` 发现同名活动游标时报告 ERROR `cursor "%s" already in use`。

## 诊断 {#diagnosis}

记录准确名称、来源（SQL portal 创建还是 PL/pgSQL `OPEN`）、严重性和事务状态。SQL 路径检查 portal 创建/复用及适用时的 `pg_cursors`；PL/pgSQL 路径检查游标变量和 `OPEN`/`CLOSE` 流程。`allowDup` 的替换分支不能推出所有重复声明都会 WARNING，PL/pgSQL 分支也不是 portal 管理器的替换路径。

## 处理 {#response}

非替换 portal 路径报 ERROR 时，关闭旧 portal 或使用符合事务生命周期的新名称。WARNING 替换路径已经关闭旧 portal，应用仍应确认替换是预期行为。PL/pgSQL 应在重新打开前关闭命名游标，或使用不同变量/名称。只有 ERROR 路径需要通过回滚保存点或回滚事务恢复显式事务；重连不能替代查清连接池所有权。

## 消息变体 {#messages}

18.6 代表报文如下：

| 路径 | 严重性 | 主消息 |
| --- | --- | --- |
| `CreatePortal(..., allowDup=false, ...)` | `ERROR` | `cursor "%s" already exists` |
| `CreatePortal(..., allowDup=true, dupSilent=false)` | `WARNING` | `closing existing cursor "%s"` |
| 已找到 PL/pgSQL 命名游标 | `ERROR` | `cursor "%s" already in use` |

`%s` 为动态游标名称，本页不声称具体名称。

## 版本 {#versions}

目录范围从 7.4 到 18.6、19beta3，不是精确的功能引入判断。选定的是 18.6 固定 portal 管理器和 PL/pgSQL 源码路径，未运行数据库。

## 相关 {#related}

- [`34000`](../34000/)
- [`42P02`](../42p02/)
- [`25P02`](../25p02/)

## 来源 {#sources}
- `src.errcodes.42P03.18.6` — `src/backend/utils/errcodes.txt` line 378，固定于 `724edf9bde9d356724ad384a2e196edc3c9f80f7`；SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`（[源码](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L378)）。

- `src.call.42P03.800a8278cf453417f5af6d6e` — `src/backend/utils/mmgr/portalmem.c` lines 185-187，固定于 `724edf9bde9d356724ad384a2e196edc3c9f80f7`；SHA-256 `8e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04`（[源码](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/mmgr/portalmem.c#L185-L187)）。
- `src.call.42P03.208bdd3b576b5021fb35916d` — `src/backend/utils/mmgr/portalmem.c` lines 189-192，固定于 `724edf9bde9d356724ad384a2e196edc3c9f80f7`；SHA-256 `8e29a302a7837316765acc798f9ff6eb22a330e8098a02794cd4d683601d3b04`（[源码](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/mmgr/portalmem.c#L189-L192)）。
- `src.call.42P03.06c0580b43488eece2de1973` — `src/pl/plpgsql/src/pl_exec.c` lines 2895-2897，固定于 `724edf9bde9d356724ad384a2e196edc3c9f80f7`；SHA-256 `0df3c70a6bccf6dddf443fb99e152a16a67009827c44a820597e98e585cb8b20`（[源码](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/pl/plpgsql/src/pl_exec.c#L2895-L2897)）。
- `src.calls.REL_18_6.42P03` — 固定本地核心调用扫描；SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`。
- [`作者证据`](../../data/evidence/42p03.json) — 记录源码断言、消息角色和运行边界。
