1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
|
# frozen_string_literal: true
class SwapCiPipelineVariablesPkWithBigint < Gitlab::Database::Migration[2.1]
include Gitlab::Database::MigrationHelpers::ConvertToBigint
disable_ddl_transaction!
TABLE_NAME = 'ci_pipeline_variables'
def up
swap
end
def down
swap(stepping_down: true)
end
private
def swap(stepping_down: false)
# Prepare the names we need below
primary_key_constraint_name = "#{TABLE_NAME}_pkey"
sequence_name = "#{TABLE_NAME}_id_seq"
bigint_primary_key_index_name = "index_#{TABLE_NAME}_on_id_convert_to_bigint"
temp_name = quote_column_name(:id_tmp)
id_name = quote_column_name(:id)
id_convert_to_bigint_name = quote_column_name(:id_convert_to_bigint)
function_name = quote_table_name(
Gitlab::Database::UnidirectionalCopyTrigger.on_table(
TABLE_NAME, connection: Ci::ApplicationRecord.connection
).name(:id, :id_convert_to_bigint)
)
# 2. Create indexes using the bigint columns that match the existing indexes using the integer column
# NOTE: this index is already created in:
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120946
# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120950
# Therefore, this won't have any effect for `up` step, but will be used for `down` step.
add_concurrent_index TABLE_NAME, :id_convert_to_bigint, unique: true, name: bigint_primary_key_index_name
# 4. Inside a transaction, swap the columns
# When stepping up, it will swap the bigint column as the primary key and the int column as `bigint`
# When stepping down, it will swap the int column as the primary key and the bigint column as `bigint`
with_lock_retries(raise_on_exhaustion: true) do
# a. Lock the tables involved.
execute "LOCK TABLE #{TABLE_NAME} IN ACCESS EXCLUSIVE MODE"
# b. Rename the columns to swap names
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{id_name} TO #{temp_name}"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{id_convert_to_bigint_name} TO #{id_name}"
execute "ALTER TABLE #{TABLE_NAME} RENAME COLUMN #{temp_name} TO #{id_convert_to_bigint_name}"
# c. Reset the trigger function
execute "ALTER FUNCTION #{function_name} RESET ALL"
# d. Swap the defaults
execute "ALTER SEQUENCE #{sequence_name} OWNED BY #{TABLE_NAME}.id"
change_column_default TABLE_NAME, :id, -> { "nextval('#{sequence_name}'::regclass)" }
change_column_default TABLE_NAME, :id_convert_to_bigint, 0
# e. Swap the PK constraint
execute "ALTER TABLE #{TABLE_NAME} DROP CONSTRAINT #{primary_key_constraint_name} CASCADE"
rename_index TABLE_NAME, bigint_primary_key_index_name, primary_key_constraint_name
execute <<~SQL
ALTER TABLE #{TABLE_NAME}
ADD CONSTRAINT #{primary_key_constraint_name} PRIMARY KEY
USING INDEX #{primary_key_constraint_name}
SQL
end
return unless stepping_down
# For stepping down, we will need to recreate the index after the swap
add_concurrent_index TABLE_NAME, :id_convert_to_bigint, unique: true, name: bigint_primary_key_index_name
end
end
|