postgresql用when condition捕获特定sqlstate错误需通过预定义条件(如unique_violation→'23505')或直接匹配sqlstate五位码,后者支持自定义码如'45000';捕获后事务已标记aborted,须配合savepoint才能继续执行;行号等位置信息需在exception块中用get stacked diagnostics提取v_context等变量获取。

怎么用 WHEN condition 捕获特定 SQLSTATE 错误
PostgreSQL 不支持像 SQL Server 那样用错误号(如 2627)直接匹配,它靠 SQLSTATE 五位码识别错误类型。预定义条件(如 unique_violation、foreign_key_violation)本质是 SQLSTATE 的别名,写起来更直观,但底层仍是匹配字符串。
常见预定义条件对应关系:
-
unique_violation→23505(唯一约束冲突) -
foreign_key_violation→23503(外键约束违例) -
not_null_violation→23502(非空约束失败) -
check_violation→23514(检查约束不满足) -
no_data_found→02000(SELECT INTO 未返回行)
用法示例:
EXCEPTION
WHEN unique_violation THEN
RAISE NOTICE '用户名已存在,跳过插入';
RETURN FALSE;
WHEN foreign_key_violation THEN
RAISE EXCEPTION '关联用户不存在,操作非法' USING ERRCODE = '45001';
为什么直接写 SQLSTATE 字符串比用预定义名更灵活
预定义名覆盖不了所有场景,比如自定义错误码 '45000' 或 PostgreSQL 扩展抛出的非标码(如 'XX000'),这时必须用 SQLSTATE 字面量匹配。
语法必须严格:WHEN 后跟 SQLSTATE 'XXXXX',单引号不能省,长度必须为 5。
示例:
EXCEPTION
WHEN SQLSTATE '45000' THEN
-- 处理业务校验失败
INSERT INTO audit_log (msg) VALUES ('业务规则触发:' || username_var);
RETURN NULL;
WHEN SQLSTATE '23505' THEN
-- 重复插入,但想走不同逻辑(比如更新 instead)
PERFORM update_on_conflict(username_var, email_var);
注意:SQLSTATE '23505' 和 unique_violation 效果等价,但前者可读性差、易打错;后者推荐用于标准错误,前者留作兜底或扩展场景。
捕获后继续执行?小心事务状态被污染
PostgreSQL 的 EXCEPTION 块只“捕获”,不“恢复”事务状态。一旦错误发生,当前事务已标记为 aborted,后续任何 DML 都会报 current transaction is aborted。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
想在捕获后继续干活,必须提前设 SAVEPOINT:
- 在 BEGIN 块开头写
SAVEPOINT sp1; - 在 EXCEPTION 块里写
ROLLBACK TO sp1; - 再执行你想干的语句(如日志写入、替代逻辑)
否则,哪怕你写了 INSERT INTO log_table,也会因事务已 abort 而失败。
典型结构:
BEGIN
SAVEPOINT sp_main;
INSERT INTO users (name) VALUES (username_var);
EXCEPTION
WHEN unique_violation THEN
ROLLBACK TO sp_main;
INSERT INTO conflict_log (username, ts) VALUES (username_var, NOW());
RETURN 'conflicted';
END;
怎么拿到错误发生的精确位置(行号、函数名)
PostgreSQL 没有 ERROR_LINE() 这种函数,但可用 GET STACKED DIAGNOSTICS 提取堆栈信息,包括出错行号、列号、函数名。
必须声明变量接收,且只能在 WHEN 分支内赋值:
DECLARE
v_state TEXT;
v_msg TEXT;
v_detail TEXT;
v_hint TEXT;
v_context TEXT;
BEGIN
...
EXCEPTION
WHEN OTHERS THEN
GET STACKED DIAGNOSTICS
v_state = RETURNED_SQLSTATE,
v_msg = MESSAGE_TEXT,
v_detail = PG_EXCEPTION_DETAIL,
v_hint = PG_EXCEPTION_HINT,
v_context = PG_EXCEPTION_CONTEXT;
RAISE LOG 'Error at %: % (detail: %)', v_context, v_msg, v_detail;
END;
v_context 通常包含类似 PL/pgSQL function my_func() line 42 at SQL statement 的内容——这才是真实出错位置,不是函数入口行。
容易忽略的是:PG_EXCEPTION_CONTEXT 默认不启用,需确保 log_error_verbosity = verbose 或在会话级设置 client_min_messages = debug,否则为空。










