From 3822bd68f2e504aec5b4e0e0d21a884311badb36 Mon Sep 17 00:00:00 2001
From: Tianwen Chen <tchen@gitlab.com>
Date: Tue, 28 Nov 2023 17:40:27 +1100
Subject: [PATCH 1/2] Update the migration to work for any foreign key name

---
 ...elines_pipeline_id_bigint_for_self_host.rb | 19 ++++-
 ...s_pipeline_id_bigint_for_self_host_spec.rb | 74 +++++++++++++++++++
 2 files changed, 90 insertions(+), 3 deletions(-)
 create mode 100644 spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb

diff --git a/db/post_migrate/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host.rb b/db/post_migrate/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host.rb
index a960258ff3d54c..a422811415b120 100644
--- a/db/post_migrate/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host.rb
+++ b/db/post_migrate/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host.rb
@@ -10,11 +10,13 @@ class SwapColumnsForCiPipelinesPipelineIdBigintForSelfHost < Gitlab::Database::M
   TRIGGER_FUNCTION_NAME = :trigger_1bd97da9c1a4
   COLUMN_NAME = :auto_canceled_by_id
   BIGINT_COLUMN_NAME = :auto_canceled_by_id_convert_to_bigint
-  FK_NAME = :fk_262d4c2d19
-  BIGINT_FK_NAME = :fk_67e4288f3a
   INDEX_NAME = :index_ci_pipelines_on_auto_canceled_by_id
   BIGINT_INDEX_NAME = :index_ci_pipelines_on_auto_canceled_by_id_bigint
 
+  class PgForeignKeys < MigrationRecord
+    self.table_name = :postgres_foreign_keys
+  end
+
   def up
     return if column_type_of?(:bigint)
 
@@ -45,10 +47,21 @@ def swap
       reset_trigger_function(TRIGGER_FUNCTION_NAME)
 
       # Swap fkey constraint
-      swap_foreign_keys(TABLE_NAME, FK_NAME, BIGINT_FK_NAME)
+      swap_foreign_keys(
+        TABLE_NAME,
+        foreign_key_name_for(TABLE_NAME, COLUMN_NAME),
+        foreign_key_name_for(TABLE_NAME, BIGINT_COLUMN_NAME)
+      )
 
       # Swap index
       swap_indexes(TABLE_NAME, INDEX_NAME, BIGINT_INDEX_NAME)
     end
   end
+
+  def foreign_key_name_for(source, column)
+    PgForeignKeys
+      .where(constrained_table_name: source)
+      .where(constrained_columns: [column])
+      .first&.name || raise("Required foreign key for #{source} #{column} is missing.")
+  end
 end
diff --git a/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb b/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb
new file mode 100644
index 00000000000000..858d1258016c3b
--- /dev/null
+++ b/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe SwapColumnsForCiPipelinesPipelineIdBigintForSelfHost, feature_category: :continuous_integration do
+  let(:connection) { active_record_base.connection }
+
+  before do
+    connection.execute('ALTER TABLE ci_pipelines ALTER COLUMN auto_canceled_by_id TYPE integer')
+    connection.execute('ALTER TABLE ci_pipelines ALTER COLUMN auto_canceled_by_id_convert_to_bigint TYPE bigint')
+  end
+
+  it_behaves_like(
+    'swap conversion columns',
+    table_name: :ci_pipelines,
+    from: :auto_canceled_by_id,
+    to: :auto_canceled_by_id_convert_to_bigint
+  )
+
+  context 'when foreign key names are different' do
+    before do
+      if connection.foreign_key_exists?(:ci_pipelines, name: :fk_262d4c2d19)
+        connection.execute(
+          'ALTER TABLE "ci_pipelines" RENAME CONSTRAINT "fk_262d4c2d19" TO "fk_4_auto_canceled_by_id"'
+        )
+      end
+
+      if connection.foreign_key_exists?(:ci_pipelines, name: :fk_67e4288f3a)
+        connection.execute(
+          'ALTER TABLE "ci_pipelines" RENAME CONSTRAINT "fk_67e4288f3a" TO "fk_4_auto_canceled_by_id_convert_to_bigint"'
+        )
+      end
+    end
+
+    it 'swaps the foreign key properly' do
+      disable_migrations_output do
+        recorder = ActiveRecord::QueryRecorder.new { migrate! }
+        expect(recorder.log).to include(
+          /RENAME CONSTRAINT "fk_4_auto_canceled_by_id_convert_to_bigint" TO "temp_name_for_renaming"/
+        )
+        expect(recorder.log).to include(
+          /RENAME CONSTRAINT "fk_4_auto_canceled_by_id" TO "fk_4_auto_canceled_by_id_convert_to_bigint"/
+        )
+        expect(recorder.log).to include(/RENAME CONSTRAINT "temp_name_for_renaming" TO "fk_4_auto_canceled_by_id"/)
+      end
+    end
+  end
+
+  context 'when foreign key is missing' do
+    before do
+      if connection.foreign_key_exists?(:ci_pipelines, name: :fk_262d4c2d19)
+        connection.remove_foreign_key(:ci_pipelines, name: :fk_262d4c2d19)
+      end
+
+      if connection.foreign_key_exists?(:ci_pipelines, name: :fk_4_auto_canceled_by_id)
+        connection.remove_foreign_key(:ci_pipelines, name: :fk_4_auto_canceled_by_id)
+      end
+    end
+
+    after do
+      connection.add_foreign_key(
+        :ci_pipelines, :ci_pipelines,
+        name: :fk_262d4c2d19, column: :auto_canceled_by_id, on_delete: :nullify
+      )
+    end
+
+    it 'raises error' do
+      disable_migrations_output do
+        expect { migrate! }.to raise_error(/Required foreign key for .* is missing./)
+      end
+    end
+  end
+end
-- 
GitLab


From 7185af9725d951bd44fb6e7a8cc5cdb31ec989a4 Mon Sep 17 00:00:00 2001
From: Tianwen Chen <tchen@gitlab.com>
Date: Fri, 1 Dec 2023 11:50:06 +1100
Subject: [PATCH 2/2] Add a comment

---
 ...mns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb | 1 +
 1 file changed, 1 insertion(+)

diff --git a/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb b/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb
index 858d1258016c3b..ab0a8fe8c45d16 100644
--- a/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb
+++ b/spec/migrations/20231025025733_swap_columns_for_ci_pipelines_pipeline_id_bigint_for_self_host_spec.rb
@@ -59,6 +59,7 @@
     end
 
     after do
+      # Need to add the foreign key back or it will fail the other tests
       connection.add_foreign_key(
         :ci_pipelines, :ci_pipelines,
         name: :fk_262d4c2d19, column: :auto_canceled_by_id, on_delete: :nullify
-- 
GitLab

