mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Support removing custom-names indexes when only specifying column names
This commit is contained in:
parent
9771f5e178
commit
8ceb883b06
6 changed files with 108 additions and 19 deletions
|
@ -699,15 +699,15 @@ module ActiveRecord
|
|||
|
||||
# Removes the given index from the table.
|
||||
#
|
||||
# Removes the +index_accounts_on_column+ in the +accounts+ table.
|
||||
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
||||
#
|
||||
# remove_index :accounts, :branch_id
|
||||
#
|
||||
# Removes the index named +index_accounts_on_branch_id+ in the +accounts+ table.
|
||||
# Removes the index on +branch_id+ in the +accounts+ table if exactly one such index exists.
|
||||
#
|
||||
# remove_index :accounts, column: :branch_id
|
||||
#
|
||||
# Removes the index named +index_accounts_on_branch_id_and_party_id+ in the +accounts+ table.
|
||||
# Removes the index on +branch_id+ and +party_id+ in the +accounts+ table if exactly one such index exists.
|
||||
#
|
||||
# remove_index :accounts, column: [:branch_id, :party_id]
|
||||
#
|
||||
|
@ -1126,21 +1126,35 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def index_name_for_remove(table_name, options = {})
|
||||
index_name = index_name(table_name, options)
|
||||
# if the adapter doesn't support the indexes call the best we can do
|
||||
# is return the default index name for the options provided
|
||||
return index_name(table_name, options) unless respond_to?(:indexes)
|
||||
|
||||
unless index_name_exists?(table_name, index_name, true)
|
||||
if options.is_a?(Hash) && options.has_key?(:name)
|
||||
options_without_column = options.dup
|
||||
options_without_column.delete :column
|
||||
index_name_without_column = index_name(table_name, options_without_column)
|
||||
checks = []
|
||||
|
||||
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
||||
end
|
||||
|
||||
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
||||
if options.is_a?(Hash)
|
||||
checks << lambda { |i| i.name == options[:name].to_s } if options.has_key?(:name)
|
||||
column_names = Array(options[:column]).map(&:to_s)
|
||||
else
|
||||
column_names = Array(options).map(&:to_s)
|
||||
end
|
||||
|
||||
index_name
|
||||
if column_names.any?
|
||||
checks << lambda { |i| i.columns.join('_and_') == column_names.join('_and_') }
|
||||
end
|
||||
|
||||
raise ArgumentError "No name or columns specified" if checks.none?
|
||||
|
||||
matching_indexes = indexes(table_name).select { |i| checks.all? { |check| check[i] } }
|
||||
|
||||
if matching_indexes.count > 1
|
||||
raise ArgumentError, "Multiple indexes found on #{table_name} columns #{column_names}. " \
|
||||
"Specify an index name from #{matching_indexes.map(&:name).join(', ')}"
|
||||
elsif matching_indexes.none?
|
||||
raise ArgumentError, "No indexes found on #{table_name} with the options provided."
|
||||
else
|
||||
matching_indexes.first.name
|
||||
end
|
||||
end
|
||||
|
||||
def rename_table_indexes(table_name, new_name)
|
||||
|
|
|
@ -39,6 +39,31 @@ module ActiveRecord
|
|||
end
|
||||
super
|
||||
end
|
||||
|
||||
def remove_index(table_name, options = {})
|
||||
index_name = index_name_for_remove(table_name, options)
|
||||
execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def index_name_for_remove(table_name, options = {})
|
||||
index_name = index_name(table_name, options)
|
||||
|
||||
unless index_name_exists?(table_name, index_name, true)
|
||||
if options.is_a?(Hash) && options.has_key?(:name)
|
||||
options_without_column = options.dup
|
||||
options_without_column.delete :column
|
||||
index_name_without_column = index_name(table_name, options_without_column)
|
||||
|
||||
return index_name_without_column if index_name_exists?(table_name, index_name_without_column, false)
|
||||
end
|
||||
|
||||
raise ArgumentError, "Index name '#{index_name}' on table '#{table_name}' does not exist"
|
||||
end
|
||||
|
||||
index_name
|
||||
end
|
||||
end
|
||||
|
||||
class V4_2 < V5_0
|
||||
|
|
|
@ -54,8 +54,10 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
|
|||
end
|
||||
|
||||
def test_remove_index
|
||||
# remove_index calls index_name_exists? which can't work since execute is stubbed
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) { |*| true }
|
||||
# remove_index calls index_name_for_remove which can't work since execute is stubbed
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_for_remove) do |*|
|
||||
'index_people_on_last_name'
|
||||
end
|
||||
|
||||
expected = %(DROP INDEX CONCURRENTLY "index_people_on_last_name")
|
||||
assert_equal expected, remove_index(:people, name: "index_people_on_last_name", algorithm: :concurrently)
|
||||
|
@ -64,7 +66,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
|
|||
add_index(:people, :last_name, algorithm: :copy)
|
||||
end
|
||||
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_exists?
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_for_remove
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -323,8 +323,6 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
|
|||
|
||||
def test_with_uppercase_index_name
|
||||
@connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
assert_nothing_raised { @connection.remove_index "things", name: "#{SCHEMA_NAME}.things_Index"}
|
||||
@connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
|
||||
with_schema_search_path SCHEMA_NAME do
|
||||
assert_nothing_raised { @connection.remove_index "things", name: "things_Index"}
|
||||
|
|
42
activerecord/test/cases/migration/compatibility_test.rb
Normal file
42
activerecord/test/cases/migration/compatibility_test.rb
Normal file
|
@ -0,0 +1,42 @@
|
|||
require 'cases/helper'
|
||||
|
||||
module ActiveRecord
|
||||
class Migration
|
||||
class CompatibilityTest < ActiveRecord::TestCase
|
||||
attr_reader :connection
|
||||
self.use_transactional_tests = false
|
||||
|
||||
def setup
|
||||
super
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@verbose_was = ActiveRecord::Migration.verbose
|
||||
ActiveRecord::Migration.verbose = false
|
||||
|
||||
connection.create_table :testings do |t|
|
||||
t.column :foo, :string, :limit => 100
|
||||
t.column :bar, :string, :limit => 100
|
||||
end
|
||||
end
|
||||
|
||||
teardown do
|
||||
connection.drop_table :testings rescue nil
|
||||
ActiveRecord::Migration.verbose = @verbose_was
|
||||
end
|
||||
|
||||
def test_migration_doesnt_remove_named_index
|
||||
connection.add_index :testings, :foo, :name => "custom_index_name"
|
||||
|
||||
migration = Class.new(ActiveRecord::Migration[4.2]) {
|
||||
def version; 101 end
|
||||
def migrate(x)
|
||||
remove_index :testings, :foo
|
||||
end
|
||||
}.new
|
||||
|
||||
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
|
||||
assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration]).migrate }
|
||||
assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -135,6 +135,14 @@ module ActiveRecord
|
|||
assert !connection.index_exists?(:testings, :foo, :name => "other_index_name")
|
||||
end
|
||||
|
||||
def test_remove_named_index
|
||||
connection.add_index :testings, :foo, :name => "custom_index_name"
|
||||
|
||||
assert connection.index_exists?(:testings, :foo)
|
||||
connection.remove_index :testings, :foo
|
||||
assert !connection.index_exists?(:testings, :foo)
|
||||
end
|
||||
|
||||
def test_add_index_attribute_length_limit
|
||||
connection.add_index :testings, [:foo, :bar], :length => {:foo => 10, :bar => nil}
|
||||
|
||||
|
|
Loading…
Reference in a new issue