回滚失败的 Rails 迁移

如何回滚失败的轨道迁移?我原以为 rake db:rollback会撤消失败的迁移,但是没有,它回滚了以前的迁移(失败的迁移减去1)。rake db:migrate:down VERSION=myfailedmigration也不起作用。我碰到过几次这种情况,真的很令人沮丧。下面是我为了复制这个问题而做的一个简单测试:

class SimpleTest < ActiveRecord::Migration
def self.up
add_column :assets, :test, :integer
# the following syntax error will cause the migration to fail
add_column :asset, :test2, :integer
end


def self.down
remove_column :assets, :test
remove_column :assets, :test2
end
end

结果:

==  SimpleTest: migrating =====================================================
-- add_column(:assets, :test, :integer)
-> 0.0932s
-- add_column(:asset, :error)
rake aborted!
An error has occurred, all later migrations canceled:


wrong number of arguments (2 for 3)

好吧,我们倒回去:

$ rake db:rollback
==  AddLevelsToRoles: reverting ===============================================
-- remove_column(:roles, :level)
-> 0.0778s
==  AddLevelsToRoles: reverted (0.0779s) ======================================

嗯?这是我在 SimpleTest 之前的最后一次迁移,而不是失败的迁移。(哦,如果迁移输出包含版本号就好了。)

因此,让我们尝试运行失败的迁移 SimpleTest:

$ rake db:migrate:down VERSION=20090326173033
$

什么都没有发生,也没有输出。但也许它还是在迁徙?因此,让我们修复 SimpleTest 迁移中的语法错误,并尝试再次运行它。

$ rake db:migrate:up VERSION=20090326173033
==  SimpleTest: migrating =====================================================
-- add_column(:assets, :test, :integer)
rake aborted!
Mysql::Error: Duplicate column name 'test': ALTER TABLE `assets` ADD `test` int(11)

没有。很明显,向下迁移没有起作用。它没有失败,只是没有执行。

除了手动进入数据库并删除它,然后运行测试之外,没有其他方法可以删除这个重复的表。肯定有更好的办法。

39910 次浏览

要转到指定的版本,只需使用:

rake db:migrate VERSION=(the version you want to go to)

但是,如果迁移在一定程度上失败了,你必须首先清理它,一种方法是:

  • 编辑迁移的 down方法,只是撤消工作的 up部分
  • 迁移回以前的状态(开始时的状态)
  • 修复迁移(包括撤消对 down的更改)
  • 再试一次

不幸的是,您必须手动清除 MySQL 的迁移失败,因为 MySQL 不支持事务性数据库定义更改。

Rails 2.2包括 PostgreSQL 的事务迁移,Rails 2.3包括 SQLite 的事务迁移。

现在这并不能真正帮助您解决问题,但是如果您可以选择未来项目中的数据库,我建议使用一个支持事务 DDL 的数据库,因为它使迁移更加愉快。

更新——这在2017年的 Rails 4.2.7和 MySQL 5.7上仍然是正确的,Alejandro Babio 在这里的另一个答案中报道了这一点。

做到这一点的简单方法是将所有操作包装在一个事务中:

class WhateverMigration < ActiveRecord::Migration


def self.up
ActiveRecord::Base.transaction do
...
end
end


def self.down
ActiveRecord::Base.transaction do
...
end
end


end

正如 Luke Francl 指出的,“ MySql [的 MyISAM 表不支持事务”——这就是为什么您可以考虑一般地避免使用 MySQL,或者至少避免使用 MyISAM。

如果您正在使用 MySQL 的 InnoDB,那么上面的代码就可以很好地工作了。任何向上或向下的错误都将退出。

某些类型的操作不能通过事务恢复。通常,不能回滚表更改(删除表、删除或添加列等)。

我打错字了(在“ add _ column”中) :

自我反省

add_column :medias, :title, :text
add_colunm :medias, :enctype, :text

结束

自我保护,自我保护

remove_column :medias, :title
remove_column :medias, :enctype

结束

然后是你的问题(不能撤销部分失败的迁移):

自我反省

remove_column :medias, :title
add_column :medias, :title, :text
add_column :medias, :enctype, :text

结束

自我保护,自我保护

remove_column :medias, :title
remove_column :medias, :enctype

结束

正如你所看到的,我只是手工添加了修正线,然后删除它再次,在我签入之前。

我同意在可能的情况下应该使用 PostgreSQL。然而,当你使用 MySQL 的时候,你可以通过在你的测试数据库上进行迁移来避免这些问题:

rake db:migrate RAILS_ENV=test

您可以恢复到以前的状态,然后使用

rake db:schema:load RAILS_ENV=test

好了,伙计们,你们实际上是这样做的。我不知道上面的答案是什么意思。

  1. 弄清楚向上迁移的哪一部分起作用了,然后把它们注释出来。
  2. 还要注释掉/删除中断的迁移部分。
  3. 再次运行迁移。现在它将完成迁移的非中断部分,跳过已经完成的部分。
  4. 取消在步骤1中注释掉的迁移位的注释。

如果您想确认您现在已经得到了它,那么可以向下迁移并再次向上备份。

在2015年 Rails 4.2.1和 MySQL 5.7中,迁移失败无法像2009年那样通过 Rails 提供的标准 rake 操作来修复。

MySql 不支持 DDL 语句的回滚(在 MySQL 5.7手册) ,而 Rails 对此无能为力。

此外,我们还可以检查 Rails 是如何完成这项工作的: 根据连接适配器对 :supports_ddl_transactions?的响应,迁移是 包装在一个交易中。在 Railssource (v4.2.1)上搜索了这个操作之后,我发现只有 Sqlite3PostgreSql支持事务,而 违约不支持事务。

剪辑 因此,当前对原始问题的回答是: 失败的 MySQL 迁移必须手动修复。

Alejandro Babio 的上述答案提供了当前最佳答案。

我还想补充一个细节:

myfailedmigration迁移失败时,它不被认为是应用的,这可以通过运行 rake db:migrate:status来验证,它将显示类似于下面的输出:

$  rake db:migrate:status
database: sample_app_dev


Status   Migration ID    Migration Name
--------------------------------------------------
up      20130206203115  Create users
...
...
down    20150501173156  Test migration

在迁移失败时执行的 add_column :assets, :test, :integer的剩余影响必须通过 alter table assets drop column test;查询在数据库级进行逆转。