diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb index cc61bb7fa028bae45fb119d902e3b951c593e67b..1b5cd0fbb0745d51e54f551090a6f041672f5165 100644 --- a/lib/gitlab/database/migration_helpers.rb +++ b/lib/gitlab/database/migration_helpers.rb @@ -149,7 +149,7 @@ module Gitlab # column - The name of the column to create the foreign key on. # on_delete - The action to perform when associated data is removed, # defaults to "CASCADE". - def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade) + def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, name: nil) # Transactions would result in ALTER TABLE locks being held for the # duration of the transaction, defeating the purpose of this method. if transaction_open? @@ -167,14 +167,18 @@ module Gitlab return end - return add_foreign_key(source, target, - column: column, - on_delete: on_delete) + key_options = { column: column, on_delete: on_delete } + + # The MySQL adapter tries to create a foreign key without a name when + # `:name` is nil, instead of generating a name for us. + key_options[:name] = name if name + + return add_foreign_key(source, target, key_options) else on_delete = 'SET NULL' if on_delete == :nullify end - key_name = concurrent_foreign_key_name(source, column) + key_name = name || concurrent_foreign_key_name(source, column) unless foreign_key_exists?(source, target, column: column) Rails.logger.warn "Foreign key not created because it exists already " \ diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb index 4e83b27e4a53281639814a2fe6f5566237a392db..1e4c4c38f7482c7683ffe681562e26ca1cff09ac 100644 --- a/spec/lib/gitlab/database/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/migration_helpers_spec.rb @@ -214,6 +214,23 @@ describe Gitlab::Database::MigrationHelpers do model.add_concurrent_foreign_key(:projects, :users, column: :user_id) end + it 'allows the use of a custom key name' do + expect(model).to receive(:add_foreign_key).with( + :projects, + :users, + column: :user_id, + on_delete: :cascade, + name: :foo + ) + + model.add_concurrent_foreign_key( + :projects, + :users, + column: :user_id, + name: :foo + ) + end + it 'does not create a foreign key if it exists already' do expect(model).to receive(:foreign_key_exists?).with(:projects, :users, column: :user_id).and_return(true) expect(model).not_to receive(:add_foreign_key) @@ -257,6 +274,16 @@ describe Gitlab::Database::MigrationHelpers do model.add_concurrent_foreign_key(:projects, :users, column: :user_id) end + + it 'allows the use of a custom key name' do + expect(model).to receive(:disable_statement_timeout).and_call_original + expect(model).to receive(:execute).with(/statement_timeout/) + expect(model).to receive(:execute).ordered.with(/NOT VALID/) + expect(model).to receive(:execute).ordered.with(/VALIDATE CONSTRAINT.+foo/) + expect(model).to receive(:execute).with(/RESET ALL/) + + model.add_concurrent_foreign_key(:projects, :users, column: :user_id, name: :foo) + end end end end