How to change databases using Ruby on Rails
June 12th, 2007
I was running a mysql 4.1 db that kept screwing up my unicode, so I decided why not double version numbers and move to postgresql 8.2. Great idea in theory, but a pain in the ass in practice. The sql dumps of the different DBMS are incompatible, especially booleans causing trouble. The scripts I found were pretty much useless, so I was stuck until I came across the idea of using rails to export the db to yaml and then re-import it into the new db.
This sounds very elegant in theory, but there are still a few pitfalls. Tobias Luetke posted how he did it and provided a very useful rake task that became the basis for my approach. For good measure I threw Dr. Nic’s magic model generator in the mix to allow for very powerful and flexible migrations, even if your application doesn’t define models for all tables, e.g. when you have several apps using the same db.
The following is my 10-step program for database migration bliss.
- Set up the basics
sudo gem install magic_model_generator rails db_migration_app cd db_migration_app - Download the backup.rake task and copy it to lib/tasks/
- Connect to your production db in config/database.yml. This is also a good time back up your db before you ruin things with jittery fingers and then blame me.
- With the db set up properly, get the schema and magic models ready
(Just for kicks, you can inspect your entire db through the rails console via RAILS_ENV=production ./script/console)RAILS_ENV=production rake db:schema:dump RAILS_ENV=production ./script/generate magic_model - [OPTIONAL] Add any tables that should not be dumped/restored to the array in the rake task’s
interesting_tablesmethod.schema_info,sessions, andlogged_exceptionsare excluded by default. - Let’s do some dumping. This may take some time, depending on your db size.
At this point you have all your tables’ data in yaml format in db/backup/*.yaml.RAILS_ENV=production rake db:backup:write - Now change your database.yml to the new, empty db (e.g. your brand new pg db). Don’t mess this up, because the next step will completely wipe whichever db is defined as the production db.
- Import the yaml data into the new production db.
This also runs rake db:schema:load, and restores the correct schema_info.RAILS_ENV=production rake db:backup:read - [EXTRA CREDIT] If you migrate to postgresql, fix your sequences in one easy step with the following rake task I created:
If you want to check the sequence number in psql, runRAILS_ENV=production rake db:backup:set_sequencesselect last_value from table_name_id_seq;(where table_name is your table’s name) - Lastly, make sure you don’t forget to adjust your actual application’s database.yml to point to the new db.
Enjoy your newfound freedom from the stranglehold of your old database. :)
June 14th, 2007 at 06:43 AM This is just what I needed! The only problem I'm having is that the db:backup:set_sequences task doesn't seem to be in the backup.rake file. Perhaps the file on this site isn't up to date? Thanks, Matt
June 15th, 2007 at 05:23 AM Hi Matt, I'm glad you found my post useful. Did you download the backup.rake task from this site or from Tobi's site? Only the version I link to from this site has the set_sequences task defined (Tobi's original doesn't). Just in case, here's the source:
June 15th, 2007 at 03:36 PM
Thanks Ben. Somehow I missed it. I thought I downloaded the file from the link on this post. Anyway, thanks!
February 5th, 2008 at 09:44 AM
How can you complain that MySQL 4.1 screwed up your unicode, an then post this non-unicode-safe “solution”?
February 6th, 2008 at 02:37 AM
hk – I think you’re right that ruby’s yaml export/import is not unicode safe, though I can’t look up the details right now. The remark about mysql’s unicode errors was just a side note.
A possible unicode-safe solution might be to wrap the serialization/de-serialization in Base64.
July 3rd, 2008 at 12:51 PM
Yanked and slapped on github (http://github.com/jmoses/rails-db-migrate/tree/master). Minor changes, but more coming. It makes my laptop run really hot. ;)