回滚失败的Rails迁移


82

如何回滚失败的Rails迁移?我希望这rake db:rollback会撤消失败的迁移,但是不,它会回滚以前的迁移(失败的迁移减去一个)。而且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:迁移============================================= ========
-add_column(:assets,:test,:integer)
   -> 0.0932秒
-add_column(:asset,:error)
耙子流产了!
发生错误,所有后来的迁移都被取消:

错误的参数数量(2对3)

好的,让我们回滚一下:

$ rake db:rollback
== AddLevelsToRoles:还原============================================ ==
-remove_column(:roles,:level)
   -> 0.0778秒
== AddLevelsToRoles:恢复(0.0779s)=====================================

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

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

$ rake db:migrate:down VERSION = 20090326173033
$

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

$ rake db:migrate:up VERSION = 20090326173033
== SimpleTest:迁移============================================= ========
-add_column(:assets,:test,:integer)
耙子流产了!
Mysql :: Error:重复的列名'test':ALTER TABLE`assets` ADD`test` int(11)

不。显然,migrate:down无效。它没有失败,只是没有执行。

除了手动进入数据库并删除它,然后运行测试之外,没有办法摆脱重复的表。有比这更好的方法。

Answers:


79

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

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

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

更新-在2017年,Rails 4.2.7和MySQL 5.7上仍然如此,这是Alejandro Babio在另一个答案中报告的。


1
太好了,谢谢。我将使用PGSQL进行新项目,所以很高兴知道这是一个选择。
insane.dreamer 2009年

这仍然是最好的答案,因此这值得悬赏恕我直言。
nathanvda

20

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

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

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

  • 编辑down迁移方法,以撤消已up起作用的部分
  • 迁移回先前的状态(开始的位置)
  • 解决迁移问题(包括撤消对的更改down
  • 再试一次

谢谢。是的,我知道我可以一直迁移到失败的迁移,但是如果我有很长的迁移历史,有时可能会遇到问题。理想情况下,它们应该很好地执行所有操作,但我经常
会让

20

好的,伙计们,这是您实际的操作方式。我不知道上面的答案在说什么。

  1. 找出向上迁移的哪一部分有效。注释掉那些。
  2. 还要注释掉/删除迁移失败的部分。
  3. 再次运行迁移。现在,它将完成迁移的不中断部分,而跳过已完成的部分。
  4. 取消注释您在步骤1中注释掉的迁移位。

如果您想验证自己是否已安装,可以向下迁移并再次备份。


2
我做了一些非常相似的事情,但是我将步骤2替换为“修复了迁移失败的部分”。
Don Kirkby 2015年

2
值得强调最后一点bundle exec rake db:migrate:redo。它将向前后退一步,因此您可以验证最新的迁移是否一直在进行。在您必须随同一些代码更新一起进行迁移时,这是一个好习惯。
mahemoff 2015年

12

我同意您应尽可能使用PostgreSQL。但是,当您使用MySQL时,可以通过首先在测试数据库上尝试迁移来避免大多数此类问题:

rake db:migrate RAILS_ENV=test

您可以恢复到之前的状态,然后通过

rake db:schema:load RAILS_ENV=test

解决方法多于答案,但这是我以前从未想到的一个好主意。
艾米丽

10

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

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

此外,我们还可以检查Rails的工作方式:迁移将事务包装在事务中,具体取决于连接适配器的响应方式:supports_ddl_transactions?。在rails source(v 4.2.1)上搜索此操作后,我发现仅Sqlite3PostgreSql支持事务,并且默认情况下不支持事务。

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


我对这个答案不太了解:除了更新版本号外,它不会为最初接受的答案添加任何内容。
nathanvda

1
非常正确,最初的问题。对于安德鲁·格里姆(Andrew Grimm)的赏金开始:“想知道自从2009年3月问这个问题以来情况是否有所改变。” 这是当前的答案,并提供了一种方法来检查将来的任何更改。
亚历杭德罗·巴比奥

8

最简单的方法是将所有操作包装在事务中:

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,那么上面的命令就可以正常工作。向上或向下的任何错误都将退出。

请注意,某些类型的操作无法通过事务恢复。通常,表更改(删除表,删除或添加列等)无法回滚。


5
这不是MyISAM或InnoDB的问题。InnoDB支持事务,但不支持事务数据库定义(DDL)更改。在PostgreSQL中,您可以删除表,然后回滚该更改!
路加·弗朗克

1
Luke是正确的,mysql不支持DDL更改的事务。我必须自己考虑进行清理,例如从表中添加和删除列。
关冠


1

我有一个错字(在“ add_column”中):

def自检

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

结束

def self.down

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

结束

然后是您的问题(无法撤消部分失败的迁移)。经过一些失败的谷歌搜索后,我跑了这个:

def自检

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

结束

def self.down

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

结束

如您所见,在我签入之前,我只是手动添加了校正线,然后再次将其删除。


1

上述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;查询在数据库级别撤消。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.