MySQL 回滚失败的 Rails 迁移

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/686852/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 13:00:11  来源:igfitidea点击:

Rolling back a failed Rails migration

mysqlruby-on-railsrails-activerecordrails-migrations

提问by insane.dreamer

How do you roll back a failed rails migration? I would expect that rake db:rollbackwould undo the failed migration, but no, it rolls back the previous migration (the failed migration minus one). And rake db:migrate:down VERSION=myfailedmigrationdoesn'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:

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

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

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

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

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

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

什么都没有发生,也没有输出。但也许它无论如何都运行了迁移?因此,让我们修复 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)

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

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

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.

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

采纳答案by Luke Francl

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

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

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

Rails 2.2 包括 PostgreSQL 的事务迁移。Rails 2.3 包括 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.

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

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

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

回答by MarkusQ

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 downmethod of the migration to just undo the part of the upthat worked
  • migrate back to the prior state (where you started)
  • fix the migration (including undoing your changes to the down)
  • try again
  • 编辑down迁移到的方法,只需拧开的部分up奏效
  • 迁移回之前的状态(您开始的地方)
  • 修复迁移(包括撤消对 的更改down
  • 再试一次

回答by Simon Woodside

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.
  1. 找出向上迁移的哪一部分起作用。把那些注释掉。
  2. 还要注释掉/删除损坏的迁移部分。
  3. 再次运行迁移。现在它将完成迁移的非中断部分,跳过已经完成的部分。
  4. 取消注释您在步骤 1 中注释掉的迁移部分。

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

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

回答by StefanH

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:

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

rake db:migrate RAILS_ENV=test

You can revert to the previous state and try again with

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

rake db:schema:load RAILS_ENV=test

回答by Alejandro Babio

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.

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

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

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

Also, we can check how Rails is doing the job: A migration is wrapped in a transactiondepending 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 Sqlite3and PostgreSqlsupports transactions, and by defaultit is not supported.

此外,我们可以检查如何Rails正在做的工作:迁移被包裹在一个交易取决于如何连接适配器来响应:supports_ddl_transactions?。在rails source(v 4.2.1)上搜索了这个action,发现只有Sqlite3PostgreSql支持事务,默认不支持。

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

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

回答by BryanH

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.

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

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

如果您使用的是 MySQL 的 InnoDB,那么上面的方法就可以正常工作。向上或向下的任何错误都将退出。

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

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

回答by Ivan

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)

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

回答by poster

I had a typo (in "add_column"):

我有一个错字(在“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

def self.up

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

结尾

def self.down

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

结尾

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

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

结尾

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

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

回答by Prakash Murthy

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

上面亚历杭德罗·巴比奥 (Alejandro Babio) 的回答提供了当前最好的答案。

One additional detail I want to add:

我想补充的另外一个细节:

When the myfailedmigrationmigration 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:

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

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

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