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)

好的,让我们回滚:

$ 耙分贝:回滚
== AddLevelsToRoles:还原 ============================================= ==
-- remove_column(:roles, :level)
   -> 0.0778 秒
== AddLevelsToRoles:恢复(0.0779s)=======================================

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

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

$ rake db:migrate:down VERSION=20090326173033
$

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

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

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

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

4

9 回答 9

83

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

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

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

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

于 2009-03-26T18:26:32.687 回答
20

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

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

但是,如果迁移中途失败,您必须先清理它。一种方法是:

  • 编辑down迁移方法以撤消up有效的部分
  • 迁移回之前的状态(你开始的地方)
  • 修复迁移(包括撤消对 的更改down
  • 再试一次
于 2009-03-26T18:00:38.957 回答
20

好的,伙计们,这就是你实际的做法。我不知道上面的答案在说什么。

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

如果您想验证您现在是否拥有它,您可以向下迁移并再次备份。

于 2011-03-29T01:01:15.227 回答
13

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

rake db:migrate RAILS_ENV=test

您可以恢复到以前的状态,然后再试一次

rake db:schema:load RAILS_ENV=test
于 2010-12-10T10:09:53.173 回答
10

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

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

此外,我们可以检查 Rails 是如何完成这项工作的:迁移被包装在事务中,具体取决于连接适配器如何响应:supports_ddl_transactions?. 在 rails 源码(v 4.2.1)搜索了这个动作后,发现只有Sqlite3PostgreSql支持事务,默认不支持。

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

于 2015-04-29T11:58:20.713 回答
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['s MyISAM 表不] 支持事务”——这就是为什么您可以考虑总体上避免使用 MySQL 或至少特别避免使用 MyISAM。

如果您使用的是 MySQL 的 InnoDB,那么上面的方法就可以了。up 或 down 中的任何错误都将退出。

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

于 2009-04-01T17:41:30.343 回答
1

从控制台运行向下迁移:

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html(点击进入他的馅饼)

于 2010-01-24T22:37:05.153 回答
1

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

def self.up

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

结尾

def self.down

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

结尾

然后是您的问题(无法撤消部分失败的迁移)。在谷歌搜索失败后,我运行了这个:

def self.up

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

结尾

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

于 2010-03-02T10:04:42.760 回答
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;查询来逆转。

于 2015-05-01T18:07:02.147 回答