# 58P02 — duplicate_file

> PostgreSQL SQLSTATE 58P02 的源码核验与诊断参考。
---

# 58P02

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

`58P02` 是固定服务器端基础备份输出路径中的 duplicate-file 条件。缺失目录会尝试创建，已有空目录可以使用，已有非空目录则报告此码。

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

| 字段 | 值 |
| --- | --- |
| SQLSTATE | `58P02` |
| 条件名 | `duplicate_file` |
| 状态 | `有效` |
| 已知存在于 | `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_FILE` |
| 别名 | `—` |

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

## 含义 {#meaning}

`bbsink_server_new()` 先检查并提交权限校验事务，然后拒绝相对路径并调用 `pg_check_dir(pathname)`：结果 0 创建目录，结果 1 接受空目录，结果 2–4 进入固定的 `ERRCODE_DUPLICATE_FILE` 分支；访问错误走另一条 `errcode_for_file_access()` 分支。这是基础备份输出保护，不证明 PostgreSQL 中所有 `EEXIST` 或非空目录都使用 `58P02`。

## 消息 {#messages}

- **ERROR**，SQLSTATE `58P02`：`directory "%s" exists but is not empty`。
- **ERROR**，使用 `errcode_for_file_access()`：`could not access directory "%s": %m`。
- **ERROR**，目标不存在但创建失败时，使用 `errcode_for_file_access()`：`could not create directory "%s": %m`。

后两个模板的 SQLSTATE 由保存的 OS errno 决定，不能因为它们在同一函数中就都标成 `58P02`。

## 诊断 {#diagnosis}

确认路径确实是预期的基础备份目标，检查所有者、挂载和内容，并判断是完整旧备份还是中断产物；变更前保留目录清单。源码在内部权限校验事务提交后才准备输出目录，因此这里表示备份建立失败，不能假设可以靠继续执行 SQL 修复用户 DML 事务。

## 处理 {#response}

按备份策略使用已确认为空的目标，或先归档旧输出。创建后验证完整备份和 manifest；不要递归删除或强制覆盖未知目录。若外层客户端报告 `ERROR`，按实际会话状态回滚到已有保存点或事务；`ROLLBACK` 不能把非空备份目录变安全。只有所属服务器进程被终止时才需要新连接。

## 版本 {#versions}

锁定目录从 7.4 记录此条件；固定基础备份路径来自 PostgreSQL 18.6。本源码条目没有修改或测试备份目标。

## 相关 {#related}

[`58P01`](../58p01/)、[`58P03`](../58p03/)、[`53100`](../53100/)

## 来源 {#sources}

[`src/backend/backup/basebackup_server.c#L59-L125`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/backup/basebackup_server.c#L59)

结构化[证据记录](../../data/evidence/58p02.json)保存目录状态分支、动态 errno 路径和源码/运行边界。
