导读:本期聚焦于下班再修创作的《PostgreSQL常见错误码ERRCODE有哪些?如何快速对照排查问题?》,敬请观看详情。排查PostgreSQL报错时,如果只看信息文本而忽略SQLSTATE,往往会绕远路。错误信息可能随本地化设置变化,但SQLSTATE五位代码是稳定不变的,ERRCODE对应的条件名更是异常捕获的关键。本文整理常见错误码分类、ERRCODE与SQLSTATE对照关系,以及如何在psql、日志和应用代码中快速定位。内容包括类23完整性约束违反,如23505唯一约束冲突;类40事务回滚,如40001串行化失败;类42语法或访问规则,如42703未定义列;类08连接异常和类53资源不足等。同时给出PL/pgSQL捕获指定错误码的示例,以及驱动层获取sqlstate字段的方法。掌握这些对照关系,可以在不依赖本地化消息的前提下,构建稳定的异常处理逻辑。

PostgreSQL 在抛出错误时,除了常见的 ERROR: 前缀和消息文本,还会返回一个五位字符组成的 SQLSTATE。这个代码与官方文档里的 ERRCODE 条件名一一对应,例如 23505 对应 unique_violation,表示唯一约束冲突。实际定位问题时,如果只依赖本地化消息文本,一旦数据库语言环境改变,程序判断就会失效;而 SQLSTATE 是稳定、可预期的,适合写入异常处理逻辑。

PostgreSQL常见错误码ERRCODE有哪些?如何快速对照排查问题?

本文先梳理错误码的编码结构,再列出高频错误码对照表,最后说明如何在命令行、日志和应用代码中快速获取这些代码,并用它们驱动更可靠的异常处理。

SQLSTATE 与 ERRCODE 的编码结构

SQLSTATE 是 SQL 标准规定的五位状态码,前两个字符表示错误类别,后三个字符表示该类别下的具体条件。PostgreSQL 在实现上遵循了这套规则,并为每个 SQLSTATE 分配了一个可读性更好的 ERRCODE 条件名。比如 23505 属于类别 23(完整性约束违反),具体条件是 505,对应的条件名是 unique_violation。

从使用角度看,ERRCODE 与 SQLSTATE 是一组等价信息:ERRCODE 方便人阅读和书写,SQLSTATE 更适合程序判断。PostgreSQL 在错误日志与应用驱动中通常会同时暴露这两者。需要强调的是,类别号具有快速定位的价值,例如看到 42 开头的错误,基本可以确定是语法或访问规则问题;看到 23 开头,则是约束或数据完整性相关;看到 40 开头,往往与事务回滚或并发冲突有关。熟悉这些类别能减少排查时的搜索范围。

PostgreSQL 源码中的 errcodes.txt 维护完整的映射关系,官方文档也提供按类别排列的错误码列表。并不是所有五位代码都来自 SQL 标准,一些 P 开头的类别是 PostgreSQL 自身扩展,例如 P0001 是 PL/pgSQL 中的 raise_exception。在日常开发中更常接触的还是标准类别的错误码。

高频错误码对照与触发场景

下面是一张高频错误码对照表,覆盖约束违反、事务回滚、语法访问、连接和资源等常见类别。抓取错误码时,可以先从这张表快速判断问题方向。

SQLSTATEERRCODE含义典型触发场景
23505unique_violation唯一约束冲突INSERT 或 UPDATE 遇到重复键值
23503foreign_key_violation外键约束违反插入子表时父表无对应记录,或删除仍有引用的父记录
23502not_null_violation非空约束冲突向 NOT NULL 列写入 NULL
23514check_violation检查约束冲突数据不满足 CHECK 条件
40001serialization_failure串行化失败可重复读或可串行化隔离级别下并发写冲突
40P01deadlock_detected检测到死锁两个事务互相等待对方持有的锁
42601syntax_error语法错误SQL 语句书写不符合语法规则
42703undefined_column未定义列查询引用了不存在的列名
42P01undefined_table未定义表FROM 子句引用了不存在的表
22012division_by_zero除零错误整数除法或取模分母为 0
22P02invalid_text_representation文本表示无效类型转换失败,如将 abc 转为 integer
22001string_data_right_truncation字符串右截断写入内容超过字符列长度
53300too_many_connections连接数过多连接数超过 max_connections 限制
57014query_canceled查询被取消用户主动取消或达到 statement_timeout
53100disk_full磁盘满写数据文件、临时文件或 WAL 时空间不足

在这张表中,23505 和 23503 是写入链路最常见的两类错误。比如用户导入数据时,重复记录会触发 unique_violation;删除主表记录时如果还有子表引用,就会触发 foreign_key_violation。对于前者,可以在应用层做预检查,也可以直接利用 INSERT ... ON CONFLICT DO NOTHING 或 DO UPDATE 把冲突转成幂等操作。对于后者,通常需要先处理子表数据,再删除主表记录。

并发方面的 40001 与 40P01 需要特别区分。死锁检测会回滚其中一个事务,数据库会自动处理,但应用必须实现重试。串行化失败则主要出现在高隔离级别下,重试幅度可能要覆盖整个事务逻辑,而不仅是单条 SQL。资源类错误如 53300 常被误认为是应用连接池配置问题,实际上首先应查看数据库当前连接数,并确认 max_connections 是否留有足够余量。

如何从不同来源获取错误码

在 psql 交互终端中,默认的错误输出只显示 ERROR: 和消息文本,看不到 SQLSTATE。可以通过 \set VERBOSITY verbose 让错误信息包含错误码、位置等更多细节。这个设置对当前会话有效,适合临时排查。

\set VERBOSITY verbose
SELECT * FROM no_such_table;

设置后,psql 会返回类似下面的信息:错误码 42P01 出现在消息前端,紧接着是错误文本,然后是行号和 LOCATION 等内部定位信息。虽然 LOCATION 一般只在源码排查时有用,但它能帮助确认问题是不是由 PostgreSQL 内部的某个解析或执行阶段抛出。

对于服务器日志,同样可以通过参数 log_error_verbosity 控制详细程度。将其设置为 verbose 后,PostgreSQL 会在日志文件中记录 SQLSTATE、源文件和行号。生产环境通常建议保持 default 的日志量,但在做复杂故障复盘时可以临时开启,收集足够上下文后再调整回来。

应用层获取错误码则更直接。以 Python 的 psycopg2 驱动为例,捕获异常后可以通过 pgcode 属性拿到 SQLSTATE,通过 pgerror 拿到人类可读消息。Java 的 JDBC 驱动则通过 SQLException.getSQLState() 提供同样能力。下面分别给出最小示例。

import psycopg2

conn = psycopg2.connect(dbname="appdb", user="appuser")
cur = conn.cursor()
try:
    cur.execute("INSERT INTO users(email) VALUES ('a@ipipp.com')")
    conn.commit()
except psycopg2.Error as e:
    print(e.pgcode)   # 23505
    print(e.pgerror)  # 完整错误消息
    print(e.diag.message_primary)
    conn.rollback()
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class PgErrorDemo {
    public static void main(String[] args) {
        try (Connection conn = DriverManager.getConnection("jdbc:postgresql://localhost/appdb", "appuser", "secret");
             Statement stmt = conn.createStatement()) {
            stmt.executeUpdate("INSERT INTO users(email) VALUES ('a@ipipp.com')");
        } catch (SQLException e) {
            System.out.println(e.getSQLState()); // 23505
            System.out.println(e.getMessage());
        }
    }
}

多数 PostgreSQL 驱动在异常对象内部已经解析好 SQLSTATE,不需要再手工从消息文本中截取。这样就能把错误码作为一个稳定的机器可读字段保存到日志系统或告警规则中。

错误码驱动的异常处理实践

在数据库内部编写 PL/pgSQL 函数或过程时,可以用 EXCEPTION WHEN 直接捕获 ERRCODE 条件名,也可以使用 SQLSTATE '23505' 这种写法。下面示例在插入用户时捕获唯一约束冲突,并输出一个更友好的提示,而不是把原始错误直接抛出。

DO $$
DECLARE
  v_err_code text;
BEGIN
  INSERT INTO users(email) VALUES ('a@ipipp.com');
EXCEPTION
  WHEN unique_violation THEN
    v_err_code := SQLSTATE;
    RAISE NOTICE 'Duplicate entry, code: %', v_err_code;
    -- 这里可以执行记录日志或返回自定义响应
END
$$;

需要注意的是,PL/pgSQL 的异常块是通过子事务实现的,每进入一个带 EXCEPTION 的块,PostgreSQL 都需要额外的保存点开销。如果在一个高频写入路径上对每条 INSERT 都包一层异常捕获,性能会有明显下降。更好的做法是先用约束检查或 ON CONFLICT 处理可预见的冲突,把异常捕获留给真正的意外场景。

在应用层,错误码的更大价值是建立统一的映射关系。比如把 23505 映射为“邮箱已被注册”,把 23503 映射为“关联数据不存在”,把 40001 和 40P01 映射为“系统繁忙,请稍后重试”。这样做的好处是,无论数据库返回的本地化消息如何变化,用户看到的提示语都能保持一致,同时便于监控系统按照错误码聚合统计。不要把数据库原始消息直接展示给最终用户,这既可能泄露表结构信息,也会让错误提示风格难以统一。

最后再强调一点:错误码虽然稳定,但并不是所有异常都能被预设处理。对于 XX000 这类内部错误,或者无法识别的 SQLSTATE,应保留完整错误上下文并记录日志,而不是简单吞掉。合理的策略是:能识别的高频错误码走用户友好的分支处理,未知错误码统一走兜底处理并触发告警。

PostgreSQL错误码ERRCODESQLSTATE修改时间:2026-09-28 15:58:04

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0928/63040.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。