开发者

Use db:migrate:redo while properly handling exceptions from schema changes

I have a migration that's breaking in the middle of a couple of schema changes. When it breaks an exception is thrown and rake db:migrate exits, leaving my database in a half-migrated state.

How can I set it up so that the migration automatically reverts just the changes that have run so far? I'd like to do so globally when in development mode. Surely someone out there has a better way than embedding each successive AR::Migration::ClassMethod in a begin; rescue =>e opposite_action; end block.

Perhaps a common example is in order:

#2010010100000000_made_a_typo.rb
class MadeATypo < ActiveRecord::Migration

   def self.up
      rename_column :birds, :url, :photo_file_name
      rename_column :birds, :genius, :species #typo on :genius => :genus
   end
   def self.down
 开发者_StackOverflow     rename_column :birds, :photo_file_name, :url
      rename_column :birds, :species, :genius
   end
end

This migration will fail on the second line with "column genius not found", but not record the migration number in the schema_migrations table. I'd like it if it called

rename_column :birds, :photo_file_name, :url #this is a revert of the first line

before the exception was passed out of MadeATypo.up.

Responses to comments:

I understand that mysql might not have support for DDL transactions, I'm looking for a more application-level solution which (probably) uses AR::Migration itself. Surely someone has created a plugin which captures method calls to the main AR:M:ClassMethods and can rewind them in most cases if an exception occurs during a migration.


I don't have a solution, but the main problem is that DDL statements can't be transactioned (at least in MySQL, I don't know if that's a general thing).

So, because migrations are treated as atomic up/down actions, there's no easy way to undo "half" a migration - it's not easy to work out which parts of the down correspond to which parts of the up


I don't know of any way to do this currently, but the code for reversible migrations coming to Rails 3.1 looks like a good base to build this feature on. See these links:

  • http://edgerails.info/articles/what-s-new-in-edge-rails/2011/05/06/reversible-migrations/index.html
  • https://github.com/rails/rails/commit/47017bd1697d6b4d6780356a403f91536eacd689
  • https://github.com/rails/rails/blob/master/activerecord/lib/active_record/migration/command_recorder.rb

The way I'm thinking you could implement this is to run the migration against the recorder (as in https://github.com/rails/rails/commit/47017bd1697d6b4d6780356a403f91536eacd689#L0R337), then switch back to the live connection and run the recorder forward one command at a time, tracking how many have completed. All you need, then, is to wrap that forward running loop in a begin-rescue-end that executes as many inverse commands as you successfully executed forward commands, if there's an exception.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜