psql 导入 SQL 时连续报 current transaction is aborted
psql 导入 SQL 时,一条语句失败可能让后续命令连续报 current transaction is aborted。定位首条错误,用 ON_ERROR_STOP 停止脚本,并按事务边界决定完整回滚或保存点恢复。
通过 psql 导入一个带 BEGIN、COMMIT 的 SQL 文件,中间某条语句失败后,后面几千条语句都可能报同一个错误:
ERROR: current transaction is aborted, commands ignored until end of transaction block
对应的 SQLSTATE 是 25P02,表示当前事务已经处于失败状态。后面那些 SQL 即使单独执行没有问题,放在这个尚未结束的失败事务里,也会被拒绝。
应从同一事务中更早出现的第一条错误查起,例如唯一约束冲突、对象不存在或权限不足。只看日志末尾的 25P02,无法知道最初是什么导致了失败。
以下处理针对 PostgreSQL 的事务块和 psql 导入纯文本 SQL 的场景。GUI、驱动和 ORM 也可能遇到同样的事务状态,但 psql 的命令行参数不会替它们管理事务;二进制或自定义格式备份也不能直接照搬 psql 的导入命令。
让 psql 在第一条错误处停止
psql 默认在遇到错误后继续处理后续命令。事务已经失败时,后续命令就会产生大量重复报错。对要求遇错停止的导入任务,显式设置 ON_ERROR_STOP:
psql -X --set=ON_ERROR_STOP=on \
--dbname=example_db \
--file=import.sql
example_db 和 import.sql 是示例数据库名与文件名,应替换为实际测试目标,并沿用已确认的主机、端口和账号设置。不要为查找首条错误就直接重放生产导入;先在隔离数据库检查 SQL 和依赖,正式重试前备份目标数据。
-X 禁止加载系统和用户的 psql 启动文件,减少本地配置对导入行为的影响。--file 读取脚本,报错时也便于定位文件行号。
启用 ON_ERROR_STOP 后,非交互脚本执行遇错会停止,psql 以状态码 3 退出。自动化任务必须接住非零退出状态,不能在 psql 失败后继续打印“导入完成”。成功结束通常返回 0;文件打不开等客户端错误和连接中断还有其他非零状态,因此不要只把 3 当成失败。
如果需要把设置放进专供 psql 使用的 SQL 文件,可以在导入语句前加入:
\set ON_ERROR_STOP on
这是 psql 元命令,不是 PostgreSQL SQL。文件还会交给其他工具执行时,不要未经检查就加入这行。
停止执行不等于所有内容都已撤销
ON_ERROR_STOP 控制客户端何时停止发送命令,不会自动把本来分开的事务合成一笔事务。
如果文件已经用 BEGIN 和 COMMIT 包住一批普通表数据修改,中途出错会使这笔事务无法正常继续。若文件中有更早的成功提交,或者此前语句在自动提交模式下各自完成,那些已提交内容不会因为后面出错而撤销。
重跑之前应检查文件里的事务边界和目标库状态,而不是看到退出码非零就假设“什么都没写进去”。重复执行已经成功的部分,可能引出新的唯一约束冲突,也可能重复业务操作。
对于没有自带事务控制、且所有命令都允许在事务块内执行的脚本,可以在经过隔离验证后使用:
psql -X --set=ON_ERROR_STOP=on --single-transaction \
--dbname=example_db \
--file=import.sql
--single-transaction 会在脚本前后包上事务控制;配合 ON_ERROR_STOP,命令失败时会回滚。它不能无条件叠加到任意导入文件上:文件自带 BEGIN、COMMIT 或 ROLLBACK 时,事务边界可能不再符合预期;含有不能在事务块内执行的命令时,也会失败。
对本来已包着一个事务的文件,先保留原事务设计,用遇错停止帮助定位问题。不要同时改事务边界和数据内容,否则更难判断哪项变化影响了结果。
交互窗口仍留在失败事务中
在交互式 psql 中,开启 ON_ERROR_STOP 后发生错误,只会回到命令提示符。它不会替你决定是否丢弃整个事务。此时继续执行查询,仍可能得到 25P02。
确认当前事务内尚未提交的修改都可以放弃后,在发生错误的同一个连接中执行:
ROLLBACK;
SELECT 1;
ROLLBACK 结束当前事务并丢弃其中的修改;随后简单查询能够正常返回,才说明该连接不再卡在失败事务里。另开一个连接执行回滚,不能恢复原连接的事务。
不要把回滚当作无影响的“清除提示”。即使前面某条 INSERT 已经显示执行成功,只要还没有提交,完整回滚仍会丢弃这笔事务中的普通表数据修改。需要保留或重做哪些业务操作,必须在重试前确定。
需要保留部分操作时,保存点必须提前建立
如果业务明确允许只撤回一部分操作,可以在容易失败的步骤前建立保存点,出错后回到该位置。下面用只读表达式说明顺序,故意触发一次除零错误:
BEGIN;
SELECT 10;
SAVEPOINT before_step;
SELECT 1 / 0;
ROLLBACK TO SAVEPOINT before_step;
SELECT 20;
COMMIT;
这是供理解流程的示例,不是让生产导入忽略所有错误。应在独立测试会话中逐条验证;若把它当作启用了 ON_ERROR_STOP 的非交互脚本一次执行,psql 会在除零处退出,不会继续发送后面的 ROLLBACK TO。
回滚到保存点会丢弃保存点之后的相关修改,保留之前的事务内容,并让事务有机会继续。保存点必须在失败之前存在;已经进入失败状态后,不能靠临时补一个保存点挽救前面的工作。
psql 另有 ON_ERROR_ROLLBACK,它通过隐式保存点让某条命令失败后事务继续。这和 ON_ERROR_STOP 的目的不同。对于必须完整导入的数据,不应为了减少错误提示就启用它,否则可能让不完整的数据集走到提交阶段。
修复后验证什么
保留第一条错误、对应 SQL 行号和相关约束信息,修正实际原因,再在隔离库重新导入。遇错停止后,仍须修正首条错误对应的 SQL 或数据。
正式重试前确认上一轮哪些事务已经提交、哪些已撤销,选择从可靠备份恢复、清理经过确认的测试结果,或按既定幂等方案重跑。不要只截掉报错前的 SQL,便假定剩余文件仍满足依赖顺序和事务要求。
验收应包括 psql 的退出状态、目标记录数量及关键业务约束。交互场景还应确认原连接能正常执行下一条命令。此次调整无需重启 PostgreSQL;若已发生部分提交,撤回 psql 参数也不能撤销这些提交,数据恢复要单独处理。


