Ruby on Rails,数据库迁移任务出错后的解决办法

在《Ruby on Rails,数据库迁移命令和迁移任务编写》中创建的迁移任务中包含若干条命令。其中无论哪一条在书写或逻辑上存在错误,都会阻断迁移工作继续进行。

比如说,迁移任务第一步先将users表的名称改成了admin_users,然后添加了username,email列。如果修改email列属性的语句中出现了拼写错误(比如把email写成了emial)。

class AlterUsers < ActiveRecord::Migration
  def up
	rename_table("users","admin_users")
	add_column("admin_users","username",:string,:limit=>25)
	add_column("admin_users","email",:string,:limit=>50)	
	change_column("admin_users","emial",:string,:default=>"",:limit=>100)
	add_column("admin_users","password",:string,:limit=>25)
	rename_column("admin_users","password","hashed_password")
	add_column("admin_users","salt",:string,:limit=>40)
	puts "***about to add an index ***"
	add_index("admin_users","username")
  end

  def down
	...
  end
end

迁移任务会按照顺序执行每条命令,但是到了修改email列属性这条发生了错误。

 

E:\greensoft\RailsInstaller\Sites\simple_cms>rake db:migrate
==  AlterUsers: migrating =====================================================
-- rename_table("users", "admin_users")
   -> 0.0230s
-- add_column("admin_users", "username", :string, {:limit=>25})
   -> 0.1550s
-- add_column("admin_users", "email", :string, {:limit=>50})
   -> 0.1290s
-- change_column("admin_users", "emial", :string, {:default=>"", :limit=>100})
rake aborted!
An error has occurred, all later migrations canceled:

No such column: admin_users.emial

令人不爽的是,这种情况下,前面的几条命令已经生效不会被回滚。也就是说迁移工作部分成功部分失败了!如下所示,从修改email列属性后面的操作没有生效,但之前的诸如users表改名字、添加username、email列的操作生效。

 

mysql> show fields from admin_users;
+------------+-------------+------+-----+---------+----------------+
| Field      | Type        | Null | Key | Default | Extra          |
+------------+-------------+------+-----+---------+----------------+
| id         | int(11)     | NO   | PRI | NULL    | auto_increment |
| created_at | datetime    | NO   |     | NULL    |                |
| updated_at | datetime    | NO   |     | NULL    |                |
| username   | varchar(25) | YES  |     | NULL    |                |
| email      | varchar(50) | YES  |     | NULL    |                |
+------------+-------------+------+-----+---------+----------------+

由于迁移过程中出现了错误,所以在schema_migrations表中没有将这次的版本记录。

 

mysql> select * from schema_migrations;
+----------------+
| version        |
+----------------+
| 20120613163730 |
| 20120613163818 |
+----------------+

尝试将错误修改,重新执行迁移操作。但由于前面几条操作已经生效了,将迁移文件修改正确后重新执行的话会报错。

 

E:\greensoft\RailsInstaller\Sites\simple_cms>rake db:migrate
==  AlterUsers: migrating =====================================================
-- rename_table("users", "admin_users")
rake aborted!
An error has occurred, all later migrations canceled:

Mysql2::Error: Table 'admin_users' already exists: RENAME TABLE `users` TO `admin_users`

既然没有记录这次的版本,我也就无法使用rake db:migrate:down VERSION=20120706144844命令将本次的操作回滚;

那么通过rake db:migrate VERSION=0回到最初的版本可以么?答案是否定的,因为错误的这次迁移版本(20120706144844)没有执行回滚。所以状态仍然对不上号。

 

E:\greensoft\RailsInstaller\Sites\simple_cms>rake db:migrate VERSION=0
==  DoNothing: reverting ======================================================
==  DoNothing: reverted (0.0000s) =============================================

==  CreateUsers: reverting ====================================================
-- drop_table("users")
rake aborted!
An error has occurred, all later migrations canceled:

Mysql2::Error: Unknown table 'users': DROP TABLE `users`

如此一来就尴尬了,发生这种问题应该怎么办呢?正确的作法是注释掉已经成功执行的那些语句,然后执行修改正确的迁移操作。

 

class AlterUsers < ActiveRecord::Migration
  def up
	#rename_table("users","admin_users")
	#add_column("admin_users","username",:string,:limit=>25)
	#add_column("admin_users","email",:string,:limit=>50)	
	change_column("admin_users","email",:string,:default=>"",:limit=>100)
	add_column("admin_users","password",:string,:limit=>25)
	rename_column("admin_users","password","hashed_password")
	add_column("admin_users","salt",:string,:limit=>40)
	puts "***about to add an index ***"
	add_index("admin_users","username")
  end

  def down
       ...
  end
end

想想也对,前面几句执行成功了,重复执行的时候跳过它们就得了。

 

E:\greensoft\RailsInstaller\Sites\simple_cms>rake db:migrate
==  AlterUsers: migrating =====================================================
-- change_column("admin_users", "email", :string, {:default=>"", :limit=>100})
   -> 0.1880s
-- add_column("admin_users", "password", :string, {:limit=>25})
   -> 0.1530s
-- rename_column("admin_users", "password", "hashed_password")
   -> 0.2200s
-- add_column("admin_users", "salt", :string, {:limit=>40})
   -> 0.2130s
***about to add an index ***
-- add_index("admin_users", "username")
   -> 0.1600s
==  AlterUsers: migrated (0.9571s) ============================================

解决这个问题告诉我们一个经验,手工编写的迁移任务应该尽可能的短小一些,或者将包含好多条操作的迁移分解成数个简单的迁移来执行,更有利于调试。

posted on 2012-07-08 23:47  边晓宇  阅读(449)  评论(0编辑  收藏  举报