If you have any moderately old rails applications in production, and you find yourself screwing around with indexes in newer migrations, keep in mind that the index naming convention rails uses changed at some point (specifically, in changeset 4768).
So if you had this in an application from a while ago - specifically, if you have an application that was in production prior to the 1.2 release of rails - and you have a migration like this…
# db/migrate/097_add_title_url_to_articles
class AddTitleUrlToArticles < ActiveRecord::Migration
def self.up
add_column :articles, :title_url, :string
add_index :articles, :title_url
end
def self.down
remove_column :articles, :title_url
end
end
…and as part of regular maintenance you create a migration like this…
# db/migrate/297_add_indexes_to_articles
class AddIndexesToArticles < ActiveRecord::Migration
def self.up
remove_index :articles, :site_id
remove_index :articles, :title_url
add_index :articles, [:site_id, :title_url]
end
def self.down
remove_index :articles, [:site_id, :title_url]
add_index :articles, :title_url
add_index :articles, :site_id
end
end
…assuming that you have entirely dropped and rebuilt your development environment database at some point since you wrote the original migration (which may be quite some time ago, at this point) - the migrations will run fine, and your local development database will have the indexes you want it to.
However, when it’s time to go to production, and you’re dealing with a database on the live cluster which has not been reset/rebuilt/whatever since it’s initial deployment (because seriously…why would it have?), you will find that migration #297 does not run against the production data because the application will try to use a different name than what it originally used when it was running an older version of rails with the former naming style. Hmmm.
This is not a problem though, rails gives us a :name option on indexes in migrations. You can change your new migration to reference what you know the old indexes were called…
# db/migrate/297_add_indexes_to_articles
class AddIndexesToArticles < ActiveRecord::Migration
def self.up
# The indexes below were made with "old style" database index naming
remove_index :articles, :name => :articles_site_id_index
remove_index :articles, :name => :articles_title_url_index
add_index :articles, [:site_id, :title_url]
end
def self.down
remove_index :articles, [:site_id, :title_url]
# Restore indexes with "old style" naming
add_index :articles, :title_url, :name => :article_title_url_index
add_index :articles, :site_id, :name => :article_site_id_index
end
end
…but wait! You’re not done.
Even though you can deploy now and the production database will run the migrations successfully, what about the new developers coming onto the project who are going to be building a database from scratch? Now we need to go modify the old migration so that it produces an index named the way the indexes were originally named back in the time that they were actually run on the production server, using that earlier version of the application and of rails.
# db/migrate/097_add_title_url_to_articles
class AddTitleUrlToArticles < ActiveRecord::Migration
def self.up
add_column :articles, :title_url, :string
add_index :articles, :title_url, :name => :articles_title_url_index
end
def self.down
remove_column :articles, :title_url
end
end
This is all sort of annoying, and violates the never commit a change to an old migration rule. However, since the framework doesn’t handle it for you, you get to handle it for yourself.