回滚失败的 Rails 迁移
如何回滚失败的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
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)搜索此操作后,我发现只有 Sqlite3 和 PostgreSql 支持事务,并通过 默认不支持。编辑
因此,原始问题的当前答案是:必须手动修复失败的 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.
做到这一点的简单方法是将所有操作包装在事务中:
正如 Luke Francl 指出的那样,“MySql[的 MyISAM 表不]支持事务”——这就是为什么您可能会考虑在一般情况下或在某些情况下避免使用 MySQL尤其是MyISAM。
如果您使用 MySQL 的 InnoDB,那么上面的代码就可以正常工作。 向上或向下的任何错误都将退出。
请注意某些类型的操作无法通过交易恢复。 通常,表更改(删除表、删除或添加列等)无法回滚。
The easy way to do this is to wrap all of your actions in a transaction:
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.
仅从控制台运行向下迁移:
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)
我有一个错字(在“add_column”中):
然后是你的问题(无法撤消部分失败的迁移)。 在谷歌搜索失败后我运行了这个:
正如你所看到的,我只是手动添加了修正线,然后在我签入之前再次将其删除。
I had a typo (in "add_column"):
and then your problem (cannot undo partly failed migration). after some failed googling i ran this:
as you can see i just added the correction line by hand, and then removed it again, before i checked it in.
亚历杭德罗·巴比奥(Alejandro Babio)的上述答案提供了当前最佳答案。
我想补充的一个额外细节是:
当
myfailedmigration
迁移失败时,它不会被视为已应用,这可以通过运行rake db:migrate:status
来验证,其中将显示类似于以下内容的输出:在失败的迁移上执行的
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 runningrake db:migrate:status
, which would show output similar to the following: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 aalter table assets drop column test;
query.不幸的是,您必须手动清理 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.
好吧,伙计们,这就是你们实际做的方法。 我不知道上面的答案在说什么。
如果您想验证现在是否已完成迁移,则可以向下迁移并再次备份。
OK, folks, here's how you actually do it. I don't know what the above answers are talking about.
You can migrate down and back up again if you want to verify that you've got it right now.
要转到指定版本,只需使用:
但如果迁移中途失败,您必须先清理它。 一种方法是:
down
方法,仅撤消有效的up
部分down
所做的更改)To go to a specified version just use:
But if a migration fails part way, you'll have to clean it up first. One way would be:
down
method of the migration to just undo the part of theup
that workeddown
)我同意您应该尽可能使用 PostgreSQL。 但是,当您陷入 MySQL 困境时,您可以通过首先在测试数据库上尝试迁移来避免大多数问题:
您可以恢复到之前的状态,然后重试
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:
You can revert to the previous state and try again with