views:

24

answers:

1

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

+1  A: 

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

Gareth
Correct. I believe you have a proper understanding of the problem, thank you for the restatement.As an aside, when you use "DDL" in this way - are you speaking of CREATE TABLE and the like, or is this a railsism for some of the activerecord migration methods?
Tim Snowhite
Actually, I think I'm wrong about that anyway. My source which I was reading earlier today was http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html (see `Caveats` at the bottom) - which only says that DDL statements can't be handled the way that ActiveRecord emulates **nested** transactions. So that invalidates most of my answer really :-/
Gareth
No, the answer is correct. MySQL cannot handle transactional DDL changes. PostgreSQL on the other hand does, thus if a migration fails partway, the DB is left unchanged. That being said, try to have smaller migrations: if you do a single change per migration, then you can't leave your DB in an inconsistent state: only one change occured, and since it failed, it can be retried.
François Beausoleil
@francois-beausoleil The whole idea is to have my AR::Migrations correctly migrate a feature in its entirety, OR revert themselves. So having a bunch of small ones that fail in the middle would be effectively the same as having one big one that failed in the middle, save that the manual revert would be slightly easier, as I could use db:migrate:down the regular way. Nonetheless it would still require manual intervention.
Tim Snowhite
@francois-beausoleil That said; are you saying that AR:Migration is aware that Postgres has correctly rewound the transaction and will rewind the prior schema changes within the current migration?
Tim Snowhite