You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails 6.1执行rake db:migrate报PG::InFailedSqlTransaction错误

问题根因

该报错是Rails 6.1升级后PostgreSQL迁移事务规则变化,结合错误的异常捕获写法共同导致的,具体逻辑如下:

  • PostgreSQL事务有严格的状态约束:同一个事务块内只要有一条SQL执行报错,整个事务会立即进入失败状态,后续所有SQL都会被直接拒绝,直到事务回滚,应用层无法通过捕获异常的方式绕开这个数据库层的状态限制。
  • Rails 6.1之前,PostgreSQL适配层不会把单个迁移的所有DDL语句包裹在同一个全局事务中,单条SQL报错后会隐式结束当前小事务,不会阻塞后续语句执行,因此add_index ... rescue nil这种吞异常的写法不会出问题。
  • 执行rename_column :company_inquiries, :inquiry_id, :mandate_id时,PostgreSQL会自动将原列上绑定的索引同步重命名为新列名对应的索引,此时后续的add_index :company_inquiries, :mandate_id语句会触发「索引已存在」的数据库错误。你在Ruby层用rescue nil吞掉了抛出的Ruby异常,但数据库端的迁移事务已经被标记为失败,后续执行create_join_table等所有SQL时,就会抛出PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block错误。
解决方案

根据实际场景选以下方案即可,优先选前两种无副作用的方案:

  • 直接删除冗余的add_index :company_inquiries, :mandate_id rescue nil语句。列重命名操作已经自动继承了原列上的索引,不需要重复创建,从根源避免触发SQL错误,是最简洁的修复方式。
  • 如果需要做索引存在性的兼容判断,不要用内联rescue nil吞异常,改用Rails原生的安全参数,把该行替换为add_index :company_inquiries, :mandate_id, if_not_exists: true。该参数会让Rails在生成SQL时提前判断索引是否存在,不会触发数据库层的报错,自然不会破坏事务状态。
  • 特殊场景下确实需要在迁移内捕获、处理数据库异常的,可以在迁移类的顶部添加disable_ddl_transaction!,关闭单迁移的DDL事务包裹。该方案会失去迁移失败自动回滚的能力,需要自行编写回滚逻辑,非必要不推荐使用。

内容的提问来源于stack exchange,提问作者alireza nobakht

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 14:48:23