50

Right now, the current migration might fail, if the books table doesn't have created_at or updated_at fields:

class AddTimestampIndexes < ActiveRecord::Migration
  def up
    remove_index :books, :created_at
    remove_index :books, :updated_at

    add_index  :books, :created_at
    add_index  :books, :updated_at
  end

  def down
    remove_index :books, :created_at
    remove_index :books, :updated_at
  end
end

Does remove_index take any options to silently proceed if it fails to remove the index rather than raising an error?

TheFooProgrammer
  • 2,439
  • 5
  • 28
  • 43

3 Answers3

87

You can use the index_exists? method within your migration to test whether the index you need to remove is actually there.

Take a look at the documentation here: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/index_exists%3F

I've not tested it, but you should be able to use something like this:

class AddTimestampIndexes < ActiveRecord::Migration
  def up
    remove_index :books, :created_at if index_exists?(:books, :created_at)
    remove_index :books, :updated_at if index_exists?(:books, :updated_at)

    add_index  :books, :created_at
    add_index  :books, :updated_at
  end

  def down
    remove_index :books, :created_at
    remove_index :books, :updated_at
  end
end

Although, by the looks of things, you really only want to create them if they don't exist? This might be more appropriate for your migration:

class AddTimestampIndexes < ActiveRecord::Migration
  def up
    add_index  :books, :created_at unless index_exists?(:books, :created_at)
    add_index  :books, :updated_at unless index_exists?(:books, :updated_at)
  end

  def down
    remove_index :books, :created_at
    remove_index :books, :updated_at
  end
end
Jon
  • 10,678
  • 2
  • 36
  • 48
11

Rails 6.1+ if_exists / if_not_exists options

Rails 6.1 added if_exists option to remove_index in order to not raise an error when the index is already removed.

Rails 6.1 added if_not_exists option to add_index in order to not raise an error when the index is already added.

As a result, your migration can be rewritten in the following way:

class AddTimestampIndexes < ActiveRecord::Migration
  def up
    remove_index :books, :created_at, if_exists: true
    remove_index :books, :updated_at, if_exists: true

    add_index :books, :created_at
    add_index :books, :updated_at
  end

  def down
    remove_index :books, :created_at, if_exists: true
    remove_index :books, :updated_at, if_exists: true
  end
end

Here is a list of the links to the corresponding PRs:

Marian13
  • 7,740
  • 2
  • 47
  • 51
9

There is also index_name_exists?(table_name, index_name) method which let's you check for an index by it's name. It's helpful for checking for existence of multi-column indexes.

Documentation - index_name_exists

dvvrt
  • 599
  • 7
  • 7
  • 2
    Unfortunately that will only check if an index with the given name exists, whereas `index_exists?` will check for an index matching the given definition (correct columns etc) – Jon Jun 16 '20 at 22:02