回滚失败的 Rails 迁移

发布于 2024-07-16 07:40:23 字数 1717 浏览 5 评论 0原文

如何回滚失败的 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: 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)

没有。 显然 migrate:down 不起作用。 这不是失败,只是没有执行。

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

How do you roll back a failed rails migration? I would expect that rake db:rollback would undo the failed migration, but no, it rolls back the previous migration (the failed migration minus one). And rake db:migrate:down VERSION=myfailedmigration doesn't work either. I've ran into this a few times and it's very frustrating. Here's a simple test I made to duplicate the problem:

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

result:

==  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)

ok, lets roll it back:

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

huh? that was my last migration before SimpleTest, not the failed migration. (And oh, it would be nice if the migration output included the version number.)

So lets try running the down for the failed migration SimpleTest:

$ rake db:migrate:down VERSION=20090326173033
$

Nothing happens, and no output either. But maybe it ran the migration anyway? So lets fix the syntax error in the SimpleTest migration, and try to run it again.

$ 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)

Nope. Obviously the migrate:down didn't work. It's not failing, it's just not executing.

No way to get rid of that duplicate table other than manually going into the database and removing it, and then running the test. There's got to be a better way than that.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

ι不睡觉的鱼゛ 2024-07-23 07:40:24

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

不支持 DDL 语句的回滚(参见 MySQL 5.7 手册)。 Rails 对此无能为力。

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

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

At 2015 with Rails 4.2.1 and MySQL 5.7, a failed migration can't be fixed with standard rake actions that Rails provide, as it was at 2009.

MySql does not support rollback of DDL statments (at MySQL 5.7 Manual). And Rails can not do anything with that.

Also, we can check how Rails is doing the job: A migration is wrapped in a transaction depending on how connection adapter respond to :supports_ddl_transactions?. After a search of this action at rails source (v 4.2.1), I found that only Sqlite3 and PostgreSql supports transactions, and by default it is not supported.

Edit
Thus the current answer to the original question: A failed MySQL migration must be manually fixed.

终止放荡 2024-07-23 07:40:24

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

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

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

The easy way to do this is to wrap all of your actions in a transaction:

class WhateverMigration < ActiveRecord::Migration

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

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

end

As Luke Francl noted, "MySql['s MyISAM tables don't] support transactions" -- which is why you might consider avoiding MySQL in general or at least MyISAM in particular.

If you're using MySQL's InnoDB, then the above will work just fine. Any errors in either up or down will back out.

BE AWARE some types of actions cannot be reverted via transactions. Generally, table changes (dropping a table, removing or adding columns, etc.) cannot be rolled back.

泪是无色的血 2024-07-23 07:40:24

仅从控制台运行向下迁移:

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

Run just the down migration from the console:

http://gilesbowkett.blogspot.com/2007/07/how-to-use-migrations-from-console.html (click through to his pastie)

坠似风落 2024-07-23 07:40:24

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

def self.up

add_column :媒体、:标题、:文本 
  添加列:媒体、:enctype、:文本 
  

结束

def self.down

remove_column :medias, :title 
  删除列:媒体,:enctype    
  

结束

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

def self.up

remove_column :medias, :title 
  添加列:媒体、:标题、:文本 
  添加列:媒体、:enctype、:文本 
  

结束

def self.down

remove_column :medias, :title 
  删除列:媒体,:enctype 
  

结束

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

I had a typo (in "add_column"):

def self.up

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

end

def self.down

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

end

and then your problem (cannot undo partly failed migration). after some failed googling i ran this:

def self.up

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

end

def self.down

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

end

as you can see i just added the correction line by hand, and then removed it again, before i checked it in.

南七夏 2024-07-23 07:40:24

亚历杭德罗·巴比奥(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 逆转资产删除列测试; 查询。

Alejandro Babio's answer above provides the best current answer.

One additional detail I want to add:

When the myfailedmigration migration fails, it is not considered as applied, and this can be verified by running rake db:migrate:status, which would show output similar to the following:

$  rake db:migrate:status
database: sample_app_dev

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

The residual effect of add_column :assets, :test, :integer being executed on the failed migration will have to be reversed at the database level with a alter table assets drop column test; query.

鱼窥荷 2024-07-23 07:40:23

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

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

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

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

Unfortunately, you must manually clean up failed migrations for MySQL. MySQL does not support transactional database definition changes.

Rails 2.2 includes transactional migrations for PostgreSQL. Rails 2.3 includes transactional migrations for SQLite.

This doesn't really help you for your problem right now, but if you have a choice of database on future projects, I recommend using one with support for transactional DDL because it makes migrations much more pleasant.

Update - this is still true in 2017, on Rails 4.2.7 and MySQL 5.7, reported by Alejandro Babio in another answer here.

一抹苦笑 2024-07-23 07:40:23

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

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

如果您想验证现在是否已完成迁移,则可以向下迁移并再次备份。

OK, folks, here's how you actually do it. I don't know what the above answers are talking about.

  1. Figure out which part of the up migration worked. Comment those out.
  2. Also comment out/remove the part of the migration that broke.
  3. Run the migration again. Now it will complete the non-broken parts of the migration, skipping the parts that have already been done.
  4. Uncomment the bits of the migration you commented out in step 1.

You can migrate down and back up again if you want to verify that you've got it right now.

残花月 2024-07-23 07:40:23

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

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

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

  • 编辑迁移的 down 方法,仅撤消有效的 up 部分
  • 迁移回之前的状态(您开始的位置)
  • 修复迁移(包括撤消对 down 所做的更改)
  • 重试

To go to a specified version just use:

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

But if a migration fails part way, you'll have to clean it up first. One way would be:

  • edit the down method of the migration to just undo the part of the up that worked
  • migrate back to the prior state (where you started)
  • fix the migration (including undoing your changes to the down)
  • try again
渔村楼浪 2024-07-23 07:40:23

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

rake db:migrate RAILS_ENV=test

您可以恢复到之前的状态,然后重试

rake db:schema:load RAILS_ENV=test

I agree that you should use PostgreSQL when possible. However, when you are stuck with MySQL, you can avoid most of these problems by trying your migration on your test database first:

rake db:migrate RAILS_ENV=test

You can revert to the previous state and try again with

rake db:schema:load RAILS_ENV=test
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文