跳转到主要内容

指南

阅读 PostgreSQL 诊断、恢复事务状态以及选择客户端边界的通用方法。

先读协议诊断

先确认协议消息类型。PostgreSQL 用 ErrorResponse 表示错误,用 NoticeResponse 表示通知;两者都携带以零字节结束的带标识字段。C 是不可本地化的 SQLSTATE,M 是主消息,D/H 分别是可选 detail 与 hint。S 可能本地化,V 是服务器提供时的未本地化 severity。schema(s)、table(t)、column(c)、constraint(n)、position(P)、context(W)、源码文件(F)、行号(L)和 routine(R)等字段都是有条件的;有就保留,没有就不要推断。参见 PostgreSQL 18 错误与通知字段消息格式

目录类别只是分类。决定语句是否失败前,要先读取实际协议严重性和消息类型。WARNINGNOTICE 可以作为通知送达,同时命令仍然完成;ERROR 作为错误送达,通常会改变显式事务状态。成功的 CommandComplete 只有命令标签,不携带 ErrorResponse SQLSTATE。把服务器版本和操作一起保留,因为相同 SQLSTATE 可能对应不同源码路径。

恢复正确的事务边界

自动提交为每条语句建立独立事务边界。语句错误后,连接通常仍可执行下一条语句。显式事务中的未处理 ERROR 会使事务进入失败状态;之后普通命令会被 PostgreSQL 以 25P02 拒绝,直到回滚。25P02 是后续观察,不是第一条错误的替代品。实际边界应参照 ROLLBACKSAVEPOINT 文档。

如果一个已经存在的保存点包围了可选内部单元,则执行 ROLLBACK TO SAVEPOINT name,然后继续外层事务或释放保存点;保存点以前的工作会保留。没有这样的保存点时,ROLLBACK 会结束失败事务,重试必须从新事务开始。已接受的 25P02 案例 展示根错误 23505、后续 25P02 和恢复;23505 证据 记录更窄的保存点边界。

PL/pgSQL 的 EXCEPTION 块提供另一种窄边界。进入处理器前,块内变更会回滚;块以前的外层变更保留。处理器中,SQLSTATESQLERRM 表示当前异常;GET STACKED DIAGNOSTICS 可读取 RETURNED_SQLSTATE、主消息、detail、hint、context 和对象字段。参见错误捕获堆叠诊断RAISE。处理器应只包围能够安全分类的操作;WHEN OTHERS 不应抹掉原始字段。

只有在结果边界明确时才重试完整单元

重试是业务决策,不是 SQLSTATE 类别的属性。若序列化失败或死锁等原因具有瞬时性且操作可安全重复,应从新快照重试整个事务。不要只重放最后一条语句,因为前面的读取、写入、锁、通知或外部调用可能共同构成一个业务单元。客户端超时或在收到结果前失联时,服务器可能已经提交但客户端没有响应,完成状态不确定;对外部可见操作应使用幂等键、持久业务键或状态查询后再重复。

遇到权限错误、对象不存在或无效数据时,先检查并修复权限、对象或输入,再判断修正后的操作是否安全。已经完成命令的警告不应触发自动重放。保留第一条诊断和事务状态;随后出现的 25P02 应作为后果记录。退避和尝试次数上限可以控制负载,却不能使非幂等操作变得安全。服务器明确报告超时或取消表示操作失败;客户端在获知结果前失联则属于完成状态不确定的另一类情况。

关联应用与服务器日志

在格式化消息前,记录 SQLSTATE、未本地化 severity、主消息、detail、hint、对象名称、position/context、服务器版本、可用时的 backend PID,以及客户端事务状态。添加应用请求或幂等键和时间戳,便于在服务器日志中找到同一操作。PID、session 标识和 query ID 是关联线索,不能替代协议字段。

CSV 和 JSON 服务器日志是结构化输出,但形状不等同于 ErrorResponse。PostgreSQL 18 的 csvlog 列包含 severity、SQLSTATE、message、detail、hint、context、query、源码位置、application name、backend type 和 query ID。jsonlog 输出 JSON,并可能省略 null 字段;处理器应忽略未来新增字段。按日志文档配置 log_destinationlogging_collector,再按 PID/session/request 关联,不要假定 CSV 列名或 JSON 键名与协议字段相同。

PL/pgSQL 与自定义条件

RAISE 可以报告 DEBUGLOGINFONOTICEWARNINGEXCEPTIONEXCEPTION 通常中止当前事务,其他级别只发送消息。它可以指定条件名或五字符 SQLSTATE,并通过 USING 设置 MESSAGEDETAILHINTSCHEMATABLECOLUMNDATATYPECONSTRAINT。PostgreSQL 允许使用除 00000 外、由数字和大写 ASCII 字母组成的任意五字符代码,包括自定义代码。末三位全为零的代码是类别码,只能用整个类别捕获。参见 RAISE 语法

命名处理器匹配指定条件及其别名;类处理器范围更宽。WHEN OTHERS 捕获除 QUERY_CANCELEDASSERT_FAILURE 以外的所有错误条件;通知级消息不是异常,因此不会被它捕获。PL/pgSQL 主动抛出的自定义代码只能证明该函数有意报告,不能证明 core、contrib、FDW、ECPG 或 driver 自然产生同一代码。

客户端如何暴露诊断

下表固定到已检查的发行版或主文档;不声称本项目对六个客户端都做过运行测试。

客户端 检查版本/来源 暴露的诊断 应保留的边界
libpq PostgreSQL 18.6,commit 724edf9b PQresultErrorField 从错误或警告 PGresult 读取字段;PQstatus 描述连接状态。 启动失败可能没有 PGresult;结果字段 API 不是通用启动错误 API,应分开记录连接文本和服务器日志。
psycopg 3.3.5,tag 解析到 commit ea542c95 Error 暴露 sqlstatediagpgconnpgresult;同一源码构造具名 SQLSTATE 异常类。 连接错误可能是 sqlstate=None,失败查询则可能有结果诊断。
PostgreSQL JDBC pgjdbc REL42.7.8,commit 9a5492d9 PSQLException.javaServerErrorMessage.getSQLState() 映射到 JDBC getSQLState(),并保留服务器消息。 客户端和传输异常也会被包装;要同时看异常类型和 server-message 是否存在。
pgx/pgconn pgx v5.7.6,commit a2fca037 errors.go 为服务器错误定义 PgError.SQLState() 连接、context 和解析错误是其他 Go error 类型或包装;使用 errors.As
node-postgres / pg-protocol node-postgres pg@8.16.3,commit 8f8e7315 messages.tsparser.tsE/N 字段解析为数据库错误和通知消息。 code、severity、detail、hint、对象字段来自服务器诊断;socket 错误是独立 JavaScript error,可能没有 SQLSTATE。
Npgsql Npgsql v10.0.0,commit a1802184 PostgresException.cs 暴露 SqlState诊断指南 区分通知和异常类型。 NpgsqlException 可以包装网络/客户端失败;通知通过 Notice 事件到达,不是命令错误。

跨客户端看,具名异常类、常量和包装类型只是便利映射;跨客户端证据仍是协议 SQLSTATE 和字段。客户端错误消息或 null SQLSTATE 不会覆盖服务器日志中确认的代码。已有 psycopg 3.3.5/libpq 18.6 启动记录中 driver 为 null、服务器为 28P01/53300,只证明这一栈的边界,不能外推到其他五个客户端。