Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gitlab/database/migration_helpers.rb')
-rw-r--r--lib/gitlab/database/migration_helpers.rb43
1 files changed, 38 insertions, 5 deletions
diff --git a/lib/gitlab/database/migration_helpers.rb b/lib/gitlab/database/migration_helpers.rb
index 4858a96c173..e41107370ec 100644
--- a/lib/gitlab/database/migration_helpers.rb
+++ b/lib/gitlab/database/migration_helpers.rb
@@ -281,6 +281,9 @@ module Gitlab
# target_column - The name of the referenced column, defaults to "id".
# on_delete - The action to perform when associated data is removed,
# defaults to "CASCADE".
+ # on_update - The action to perform when associated data is updated,
+ # defaults to nil. This is useful for multi column FKs if
+ # it's desirable to update one of the columns.
# name - The name of the foreign key.
# validate - Flag that controls whether the new foreign key will be validated after creation.
# If the flag is not set, the constraint will only be enforced for new data.
@@ -288,7 +291,8 @@ module Gitlab
# order of the ALTER TABLE. This can be useful in situations where the foreign
# key creation could deadlock with another process.
#
- def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, target_column: :id, name: nil, validate: true, reverse_lock_order: false)
+ # rubocop: disable Metrics/ParameterLists
+ def add_concurrent_foreign_key(source, target, column:, on_delete: :cascade, on_update: nil, target_column: :id, name: nil, validate: true, reverse_lock_order: false)
# Transactions would result in ALTER TABLE locks being held for the
# duration of the transaction, defeating the purpose of this method.
if transaction_open?
@@ -298,6 +302,7 @@ module Gitlab
options = {
column: column,
on_delete: on_delete,
+ on_update: on_update,
name: name.presence || concurrent_foreign_key_name(source, column),
primary_key: target_column
}
@@ -306,7 +311,8 @@ module Gitlab
warning_message = "Foreign key not created because it exists already " \
"(this may be due to an aborted migration or similar): " \
"source: #{source}, target: #{target}, column: #{options[:column]}, "\
- "name: #{options[:name]}, on_delete: #{options[:on_delete]}"
+ "name: #{options[:name]}, on_update: #{options[:on_update]}, "\
+ "on_delete: #{options[:on_delete]}"
Gitlab::AppLogger.warn warning_message
else
@@ -322,6 +328,7 @@ module Gitlab
ADD CONSTRAINT #{options[:name]}
FOREIGN KEY (#{multiple_columns(options[:column])})
REFERENCES #{target} (#{multiple_columns(target_column)})
+ #{on_update_statement(options[:on_update])}
#{on_delete_statement(options[:on_delete])}
NOT VALID;
EOF
@@ -343,6 +350,7 @@ module Gitlab
end
end
end
+ # rubocop: enable Metrics/ParameterLists
def validate_foreign_key(source, column, name: nil)
fk_name = name || concurrent_foreign_key_name(source, column)
@@ -357,10 +365,28 @@ module Gitlab
end
def foreign_key_exists?(source, target = nil, **options)
- foreign_keys(source).any? do |foreign_key|
- tables_match?(target.to_s, foreign_key.to_table.to_s) &&
- options_match?(foreign_key.options, options)
+ # This if block is necessary because foreign_key_exists? is called in down migrations that may execute before
+ # the postgres_foreign_keys view had necessary columns added, or even before the view existed.
+ # In that case, we revert to the previous behavior of this method.
+ # The behavior in the if block has a bug: it always returns false if the fk being checked has multiple columns.
+ # This can be removed after init_schema.rb passes 20221122210711_add_columns_to_postgres_foreign_keys.rb
+ # Tracking issue: https://gitlab.com/gitlab-org/gitlab/-/issues/386796
+ if ActiveRecord::Migrator.current_version < 20221122210711
+ return foreign_keys(source).any? do |foreign_key|
+ tables_match?(target.to_s, foreign_key.to_table.to_s) &&
+ options_match?(foreign_key.options, options)
+ end
end
+
+ fks = Gitlab::Database::PostgresForeignKey.by_constrained_table_name(source)
+
+ fks = fks.by_referenced_table_name(target) if target
+ fks = fks.by_name(options[:name]) if options[:name]
+ fks = fks.by_constrained_columns(options[:column]) if options[:column]
+ fks = fks.by_referenced_columns(options[:primary_key]) if options[:primary_key]
+ fks = fks.by_on_delete_action(options[:on_delete]) if options[:on_delete]
+
+ fks.exists?
end
# Returns the name for a concurrent foreign key.
@@ -1278,6 +1304,13 @@ into similar problems in the future (e.g. when new tables are created).
"ON DELETE #{on_delete.upcase}"
end
+ def on_update_statement(on_update)
+ return '' if on_update.blank?
+ return 'ON UPDATE SET NULL' if on_update == :nullify
+
+ "ON UPDATE #{on_update.upcase}"
+ end
+
def create_column_from(table, old, new, type: nil, batch_column_name: :id, type_cast_function: nil, limit: nil)
old_col = column_for(table, old)
new_type = type || old_col.type