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.

  1. Set up the basics
    
      sudo gem install magic_model_generator
      rails db_migration_app
      cd db_migration_app
      
  2. Download the backup.rake task and copy it to lib/tasks/
  3. 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.
  4. With the db set up properly, get the schema and magic models ready
    
      RAILS_ENV=production rake db:schema:dump
      RAILS_ENV=production ./script/generate magic_model
      
    (Just for kicks, you can inspect your entire db through the rails console via RAILS_ENV=production ./script/console)
  5. [OPTIONAL] Add any tables that should not be dumped/restored to the array in the rake task’s interesting_tables method. schema_info, sessions, and logged_exceptions are excluded by default.
  6. Let’s do some dumping. This may take some time, depending on your db size.
    
      RAILS_ENV=production rake db:backup:write
      
    At this point you have all your tables’ data in yaml format in db/backup/*.yaml.
  7. 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.
  8. Import the yaml data into the new production db.
    
      RAILS_ENV=production rake db:backup:read
      
    This also runs rake db:schema:load, and restores the correct schema_info.
  9. [EXTRA CREDIT] If you migrate to postgresql, fix your sequences in one easy step with the following rake task I created:
    
      RAILS_ENV=production rake db:backup:set_sequences
      
    If you want to check the sequence number in psql, run select last_value from table_name_id_seq; (where table_name is your table’s name)
  10. 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. :)

6 Responses to “How to change databases using Ruby on Rails”

  1. Matt Parrish Says:
    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
  2. Ben Says:
    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:
    
    desc "Set postgresql sequence currval to highest id for each table"
    task :set_sequences => :environment do
      if ActiveRecord::Base.connection.adapter_name.downcase == "postgresql"
        interesting_tables.each do |tbl|
          puts "Setting sequence's currval to highest id for #{tbl}"
          ActiveRecord::Base.connection.execute "select setval('#{tbl}_id_seq', (select max(id) from #{tbl}));"
        end
      else
        puts "This operation only works for postgresql databases."
      end
    end
    
  3. Matt Parrish Says:

    Thanks Ben. Somehow I missed it. I thought I downloaded the file from the link on this post. Anyway, thanks!

  4. hk Says:

    How can you complain that MySQL 4.1 screwed up your unicode, an then post this non-unicode-safe “solution”?

  5. Ben Says:

    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.

  6. Jon Moses Says:

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

Leave a Reply