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