Add primary key to preview_cards_statuses join table (includes deduplication migration) (#25243)

This commit is contained in:
Claire 2023-08-03 11:12:52 +02:00 committed by GitHub
parent a0fad5c8bb
commit 6b896b20cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 93 additions and 5 deletions

View file

@ -0,0 +1,39 @@
# frozen_string_literal: true
class AddUniqueIndexOnPreviewCardsStatuses < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def up
add_index :preview_cards_statuses, [:status_id, :preview_card_id], name: :preview_cards_statuses_pkey, algorithm: :concurrently, unique: true
rescue ActiveRecord::RecordNotUnique
deduplicate_and_reindex!
end
def down
remove_index :preview_cards_statuses, name: :preview_cards_statuses_pkey
end
private
def deduplicate_and_reindex!
deduplicate_preview_cards!
safety_assured { execute 'REINDEX INDEX preview_cards_statuses_pkey' }
rescue ActiveRecord::RecordNotUnique
retry
end
def deduplicate_preview_cards!
# Statuses should have only one preview card at most, even if that's not the database
# constraint we will end up with
duplicate_ids = select_all('SELECT status_id FROM preview_cards_statuses GROUP BY status_id HAVING count(*) > 1;').rows
duplicate_ids.each_slice(1000) do |ids|
# This one is tricky: since we don't have primary keys to keep only one record,
# use the physical `ctid`
safety_assured do
execute "DELETE FROM preview_cards_statuses p WHERE p.status_id IN (#{ids.join(', ')}) AND p.ctid NOT IN (SELECT q.ctid FROM preview_cards_statuses q WHERE q.status_id = p.status_id LIMIT 1)"
end
end
end
end

View file

@ -0,0 +1,20 @@
# frozen_string_literal: true
class AddPrimaryKeyToPreviewCardsStatusesJoinTable < ActiveRecord::Migration[6.1]
disable_ddl_transaction!
def up
safety_assured do
execute 'ALTER TABLE preview_cards_statuses ADD PRIMARY KEY USING INDEX preview_cards_statuses_pkey'
end
end
def down
safety_assured do
# I have found no way to demote the primary key to an index, instead, re-create the index
execute 'CREATE UNIQUE INDEX CONCURRENTLY preview_cards_statuses_pkey_tmp ON preview_cards_statuses (status_id, preview_card_id)'
execute 'ALTER TABLE preview_cards_statuses DROP CONSTRAINT preview_cards_statuses_pkey'
execute 'ALTER INDEX preview_cards_statuses_pkey_tmp RENAME TO preview_cards_statuses_pkey'
end
end
end