From 6eba8d27e6feecca88303df6500ef6b376344e29 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Tue, 26 Nov 2013 09:58:24 +0100 Subject: [PATCH] `rename_index`: add the new index before removing the old one. This prevents the following error when a MySQL index on a foreign key column is renamed: ``` ActiveRecord::StatementInvalid: Mysql2::Error: Cannot drop index 'index_engines_on_car_id': needed in a foreign key constraint: DROP INDEX `index_engines_on_car_id` ON `engines` ``` refs: #13038. --- activerecord/CHANGELOG.md | 8 ++++++ .../abstract/schema_statements.rb | 4 +-- .../adapters/mysql2/schema_migrations_test.rb | 25 ++++++++++++++----- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index bce288aefb..fc40bd88be 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,9 +1,17 @@ +* `rename_index` adds the new index before removing the old one. This allows + to rename indexes on columns with a foreign key and prevents the following error: + + `Cannot drop index 'index_engines_on_car_id': needed in a foreign key constraint` + + *Cody Cutrer*, *Yves Senn* + * Raise `ActiveRecord::RecordNotDestroyed` when a replaced child marked with `dependent: destroy` fails to be destroyed. Fix #12812 *Brian Thomas Storti* + * Fix validation on uniqueness of empty association. *Evgeny Li* diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 4b425494d0..6268ae4875 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -558,8 +558,8 @@ def rename_index(table_name, old_name, new_name) # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance) old_index_def = indexes(table_name).detect { |i| i.name == old_name } return unless old_index_def - remove_index(table_name, :name => old_name) - add_index(table_name, old_index_def.columns, :name => new_name, :unique => old_index_def.unique) + add_index(table_name, old_index_def.columns, name: new_name, unique: old_index_def.unique) + remove_index(table_name, name: old_name) end def index_name(table_name, options) #:nodoc: diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb index 9ecd901eac..ec73ec35aa 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb @@ -4,22 +4,35 @@ module ActiveRecord module ConnectionAdapters class Mysql2Adapter class SchemaMigrationsTest < ActiveRecord::TestCase - def test_initializes_schema_migrations_for_encoding_utf8mb4 - conn = ActiveRecord::Base.connection + def test_renaming_index_on_foreign_key + connection.add_index "engines", "car_id" + connection.execute "ALTER TABLE engines ADD CONSTRAINT fk_engines_cars FOREIGN KEY (car_id) REFERENCES cars(id)" + + connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed") + assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name) + ensure + connection.execute "ALTER TABLE engines DROP FOREIGN KEY fk_engines_cars" + end + def test_initializes_schema_migrations_for_encoding_utf8mb4 smtn = ActiveRecord::Migrator.schema_migrations_table_name - conn.drop_table(smtn) if conn.table_exists?(smtn) + connection.drop_table(smtn) if connection.table_exists?(smtn) - config = conn.instance_variable_get(:@config) + config = connection.instance_variable_get(:@config) original_encoding = config[:encoding] config[:encoding] = 'utf8mb4' - conn.initialize_schema_migrations_table + connection.initialize_schema_migrations_table - assert conn.column_exists?(smtn, :version, :string, limit: Mysql2Adapter::MAX_INDEX_LENGTH_FOR_UTF8MB4) + assert connection.column_exists?(smtn, :version, :string, limit: Mysql2Adapter::MAX_INDEX_LENGTH_FOR_UTF8MB4) ensure config[:encoding] = original_encoding end + + private + def connection + @connection ||= ActiveRecord::Base.connection + end end end end -- GitLab