This post originated from an RSS feed registered with Ruby Buzz
by Rick DeNatale.
Original Post: Rails 2.1RC1 and the Enhanced Migrations Plugin
Feed Title: Talk Like A Duck
Feed URL: http://talklikeaduck.denhaven2.com/articles.atom
Feed Description: Musings on Ruby, Rails, and other topics by an experienced object technologist.
Yesterday I started the work of moving the app to Rails 2.1RC1.
With version 2.1, Rails is moving to timestamp based migration names, instead of the traditional sequence numbering. This effectively does the same thing as the enhanced_migrations plugin, but the implementation is slightly different, more on this in a bit.
After installing or gitting Rails 2.1 into my vendor/rails directory, I ran the rake task to run all of our tests and specs.
Early on this task does a rake db:migrate, which attempted to run ALL of our migrations since Rails 2.1 looks in a different place to determine if a migration has been already run.
Implementation Differences
Until now Rails has tracked migrations using a single row, single column table in each database called schema_version. This table holds the number of the latest migration which has been run.
The enhanced_migrations plugin uses a w table, named migrations_info, with one row for each migration which has been applied to the database. The key used for this table is the migration number which the migration generator sets to Time.now.to_i. The only column besides the id is created_at. So the enhanced_migration plugin captures the time at which the migration was run against the database, which is different than the time it was generated.
Rails 2.1 uses a table called schema_migrations, which also has a row per applied migration, but this table has a string key whose value is the migration number, in Rails 2.1, the migration number is also a utc timestamp but is a string in the the form “yyyymmddhhmmss”
Blake Watters and I noticed that the timestamp forms can’t overlap since enhanced migrations uses a 10-digit timestamp and Rails 2.1 uses a 14-digit one. So we wrote a new migration 1_convert_migration_schema_to_rails21.rb
class ConvertMigrationSchemaToRails21<ActiveRecord::Migrationdef self.upvalues=select_values('select id from migrations_info')values.each{|value|execute("replace into schema_migrations VALUES('#{id}')")}enddef self.downend
We then ran rake db:migrate:up VERSION=1
This populated schema_migrations so that it now “knew” that all of our current migrations had already been run.
Not quite there
For many current users of enhanced_migrations, this might be enough. In our case it wasn’t. When I tried to run rake test again, it blew up in the prerequisite task db:test:clone_structure, complaining about bad sql syntax on an insert statement. The insert statement which was logged looked perfectly fine. It was the second of a series of statements inserting rows into our new friend, the schema_migrations table.
The SQL in question looked like this:
INSERT INTO schema_migrations (version) VALUES ('0');
INSERT INTO schema_migrations (version) VALUES ('1');
Followed by a similar line for each of our 500 or so migrations.
The rake task for db:test:clone structure, basically breaks the file db/development_structure.sql into chunks delimited by empty lines, and ‘plays’ them against the test database using ActiveRecord::Base.connection.execute. After some head scratching over why this seemingly legal SQL was being rejected, it occurred to us that it was trying to execute all of these lines as one sql statement.
So the next step was to figure out how to get db/development_structure.sql into a form which would cause the task to execute each sql statement individually. This was a simple patch to rails, which once made, allowed rake db:test:prepare to run successfully, which in turn allowed me to actually move on to running the test suite and working out the problems caused by the changes to rails.