summaryrefslogtreecommitdiff
path: root/db
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-07-09 03:09:01 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-07-09 03:09:01 +0000
commit0a9efe02885d9ad4dbdaf61746ae81daea0f575b (patch)
treeccd02a93eaaadb1c547c5f6e1d239d9276908bc6 /db
parentf44bf01f69a491d4dfca8d631b390371bd0eec7a (diff)
downloadgitlab-ce-0a9efe02885d9ad4dbdaf61746ae81daea0f575b.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'db')
-rw-r--r--db/migrate/20200305020458_add_label_restore_table.rb31
-rw-r--r--db/migrate/20200305020459_add_label_restore_foreign_keys.rb35
-rw-r--r--db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb130
-rw-r--r--db/post_migrate/20200305082858_add_uniqueness_index_to_label_title_and_project.rb21
-rw-r--r--db/structure.sql46
5 files changed, 262 insertions, 1 deletions
diff --git a/db/migrate/20200305020458_add_label_restore_table.rb b/db/migrate/20200305020458_add_label_restore_table.rb
new file mode 100644
index 00000000000..a5809cfe14b
--- /dev/null
+++ b/db/migrate/20200305020458_add_label_restore_table.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class AddLabelRestoreTable < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ def up
+ # copy table
+ execute "CREATE TABLE #{backup_labels_table_name} (LIKE #{labels_table_name} INCLUDING ALL);"
+
+ # make the primary key a real functioning one rather than incremental
+ execute "ALTER TABLE #{backup_labels_table_name} ALTER COLUMN ID DROP DEFAULT;"
+
+ # add some fields that make changes trackable
+ execute "ALTER TABLE #{backup_labels_table_name} ADD COLUMN restore_action INTEGER;"
+ execute "ALTER TABLE #{backup_labels_table_name} ADD COLUMN new_title VARCHAR;"
+ end
+
+ def down
+ drop_table backup_labels_table_name
+ end
+
+ private
+
+ def labels_table_name
+ :labels
+ end
+
+ def backup_labels_table_name
+ :backup_labels
+ end
+end
diff --git a/db/migrate/20200305020459_add_label_restore_foreign_keys.rb b/db/migrate/20200305020459_add_label_restore_foreign_keys.rb
new file mode 100644
index 00000000000..4b7c68cb20b
--- /dev/null
+++ b/db/migrate/20200305020459_add_label_restore_foreign_keys.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+class AddLabelRestoreForeignKeys < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ # create foreign keys
+ connection.foreign_keys(labels_table_name).each do |fk|
+ fk_options = fk.options
+ add_concurrent_foreign_key(backup_labels_table_name, fk.to_table, name: fk.name, column: fk_options[:column])
+ end
+ end
+
+ def down
+ connection.foreign_keys(backup_labels_table_name).each do |fk|
+ with_lock_retries do
+ remove_foreign_key backup_labels_table_name, name: fk.name
+ end
+ end
+ end
+
+ private
+
+ def labels_table_name
+ :labels
+ end
+
+ def backup_labels_table_name
+ :backup_labels
+ end
+end
diff --git a/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb b/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb
new file mode 100644
index 00000000000..33f8118534d
--- /dev/null
+++ b/db/post_migrate/20200305082754_remove_duplicate_labels_from_project.rb
@@ -0,0 +1,130 @@
+# frozen_string_literal: true
+
+class RemoveDuplicateLabelsFromProject < ActiveRecord::Migration[6.0]
+ DOWNTIME = false
+
+ CREATE = 1
+ RENAME = 2
+
+ disable_ddl_transaction!
+
+ class BackupLabel < Label
+ self.table_name = 'backup_labels'
+ end
+
+ class Label < ApplicationRecord
+ self.table_name = 'labels'
+ end
+
+ class Project < ApplicationRecord
+ include EachBatch
+
+ self.table_name = 'projects'
+ end
+
+ BATCH_SIZE = 100_000
+
+ def up
+ # Split to smaller chunks
+ # Loop rather than background job, every 100,000
+ # there are 45,000,000 projects in total
+ Project.each_batch(of: BATCH_SIZE) do |batch|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ transaction do
+ remove_full_duplicates(*range)
+ end
+
+ transaction do
+ rename_partial_duplicates(*range)
+ end
+ end
+ end
+
+ def down
+ Project.each_batch(of: BATCH_SIZE) do |batch|
+ range = batch.pluck('MIN(id)', 'MAX(id)').first
+
+ restore_renamed_labels(*range)
+ restore_deleted_labels(*range)
+ end
+ end
+
+ def remove_full_duplicates(start_id, stop_id)
+ # Fields that are considered duplicate:
+ # project_id title template description type color
+
+ duplicate_labels = ApplicationRecord.connection.execute(<<-SQL.squish)
+WITH data AS (
+ SELECT labels.*,
+ row_number() OVER (PARTITION BY labels.project_id, labels.title, labels.template, labels.description, labels.type, labels.color ORDER BY labels.id) AS row_number,
+ #{CREATE} AS restore_action
+ FROM labels
+ WHERE labels.project_id BETWEEN #{start_id} AND #{stop_id}
+ AND NOT EXISTS (SELECT * FROM board_labels WHERE board_labels.label_id = labels.id)
+ AND NOT EXISTS (SELECT * FROM label_links WHERE label_links.label_id = labels.id)
+ AND NOT EXISTS (SELECT * FROM label_priorities WHERE label_priorities.label_id = labels.id)
+ AND NOT EXISTS (SELECT * FROM lists WHERE lists.label_id = labels.id)
+ AND NOT EXISTS (SELECT * FROM resource_label_events WHERE resource_label_events.label_id = labels.id)
+) SELECT * FROM data WHERE row_number > 1;
+ SQL
+
+ if duplicate_labels.any?
+ # create backup records
+ BackupLabel.insert_all!(duplicate_labels.map { |label| label.except("row_number") })
+
+ Label.where(id: duplicate_labels.pluck("id")).delete_all
+ end
+ end
+
+ def rename_partial_duplicates(start_id, stop_id)
+ # We need to ensure that the new title (with `_duplicate#{ID}`) doesn't exceed the limit.
+ # Truncate the original title (if needed) to 245 characters minus the length of the ID
+ # then add `_duplicate#{ID}`
+
+ soft_duplicates = ApplicationRecord.connection.execute(<<-SQL.squish)
+WITH data AS (
+ SELECT
+ *,
+ substring(title from 1 for 245 - length(id::text)) || '_duplicate' || id::text as new_title,
+ #{RENAME} AS restore_action,
+ row_number() OVER (PARTITION BY project_id, title ORDER BY id) AS row_number
+ FROM labels
+ WHERE project_id BETWEEN #{start_id} AND #{stop_id}
+) SELECT * FROM data WHERE row_number > 1;
+ SQL
+
+ if soft_duplicates.any?
+ # create backup records
+ BackupLabel.insert_all!(soft_duplicates.map { |label| label.except("row_number") })
+
+ ApplicationRecord.connection.execute(<<-SQL.squish)
+UPDATE labels SET title = substring(title from 1 for 245 - length(id::text)) || '_duplicate' || id::text
+WHERE labels.id IN (#{soft_duplicates.map { |dup| dup["id"] }.join(", ")});
+ SQL
+ end
+ end
+
+ def restore_renamed_labels(start_id, stop_id)
+ # the backup label IDs are not incremental, they are copied directly from the Labels table
+ ApplicationRecord.connection.execute(<<-SQL.squish)
+WITH backups AS (
+ SELECT id, title
+ FROM backup_labels
+ WHERE project_id BETWEEN #{start_id} AND #{stop_id} AND
+ restore_action = #{RENAME}
+) UPDATE labels SET title = backups.title
+FROM backups
+WHERE labels.id = backups.id;
+ SQL
+ end
+
+ def restore_deleted_labels(start_id, stop_id)
+ ActiveRecord::Base.connection.execute(<<-SQL.squish)
+INSERT INTO labels
+SELECT id, title, color, project_id, created_at, updated_at, template, description, description_html, type, group_id, cached_markdown_version FROM backup_labels
+ WHERE backup_labels.project_id BETWEEN #{start_id} AND #{stop_id}
+ AND backup_labels.restore_action = #{CREATE}
+ SQL
+ end
+end
diff --git a/db/post_migrate/20200305082858_add_uniqueness_index_to_label_title_and_project.rb b/db/post_migrate/20200305082858_add_uniqueness_index_to_label_title_and_project.rb
new file mode 100644
index 00000000000..ce235ba4aea
--- /dev/null
+++ b/db/post_migrate/20200305082858_add_uniqueness_index_to_label_title_and_project.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class AddUniquenessIndexToLabelTitleAndProject < ActiveRecord::Migration[6.0]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ PROJECT_AND_TITLE = [:project_id, :title]
+
+ def up
+ add_concurrent_index :labels, PROJECT_AND_TITLE, where: "labels.group_id IS NULL", unique: true, name: "index_labels_on_project_id_and_title_unique"
+ remove_concurrent_index :labels, PROJECT_AND_TITLE, name: "index_labels_on_project_id_and_title"
+ end
+
+ def down
+ add_concurrent_index :labels, PROJECT_AND_TITLE, where: "labels.group_id IS NULL", unique: false, name: "index_labels_on_project_id_and_title"
+ remove_concurrent_index :labels, PROJECT_AND_TITLE, name: "index_labels_on_project_id_and_title_unique"
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index f13d51f10b0..2b2188d15ee 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -9413,6 +9413,23 @@ CREATE TABLE public.aws_roles (
role_external_id character varying(64) NOT NULL
);
+CREATE TABLE public.backup_labels (
+ id integer NOT NULL,
+ title character varying,
+ color character varying,
+ project_id integer,
+ created_at timestamp without time zone,
+ updated_at timestamp without time zone,
+ template boolean DEFAULT false,
+ description character varying,
+ description_html text,
+ type character varying,
+ group_id integer,
+ cached_markdown_version integer,
+ restore_action integer,
+ new_title character varying
+);
+
CREATE TABLE public.badges (
id integer NOT NULL,
link_url character varying NOT NULL,
@@ -17234,6 +17251,9 @@ ALTER TABLE ONLY public.award_emoji
ALTER TABLE ONLY public.aws_roles
ADD CONSTRAINT aws_roles_pkey PRIMARY KEY (user_id);
+ALTER TABLE ONLY public.backup_labels
+ ADD CONSTRAINT backup_labels_pkey PRIMARY KEY (id);
+
ALTER TABLE ONLY public.badges
ADD CONSTRAINT badges_pkey PRIMARY KEY (id);
@@ -18368,6 +18388,20 @@ CREATE UNIQUE INDEX any_approver_project_rule_type_unique_index ON public.approv
CREATE UNIQUE INDEX approval_rule_name_index_for_code_owners ON public.approval_merge_request_rules USING btree (merge_request_id, code_owner, name) WHERE ((code_owner = true) AND (section IS NULL));
+CREATE UNIQUE INDEX backup_labels_group_id_project_id_title_idx ON public.backup_labels USING btree (group_id, project_id, title);
+
+CREATE INDEX backup_labels_group_id_title_idx ON public.backup_labels USING btree (group_id, title) WHERE (project_id = NULL::integer);
+
+CREATE INDEX backup_labels_project_id_idx ON public.backup_labels USING btree (project_id);
+
+CREATE UNIQUE INDEX backup_labels_project_id_title_idx ON public.backup_labels USING btree (project_id, title) WHERE (group_id = NULL::integer);
+
+CREATE INDEX backup_labels_template_idx ON public.backup_labels USING btree (template) WHERE template;
+
+CREATE INDEX backup_labels_title_idx ON public.backup_labels USING btree (title);
+
+CREATE INDEX backup_labels_type_project_id_idx ON public.backup_labels USING btree (type, project_id);
+
CREATE INDEX ci_builds_gitlab_monitor_metrics ON public.ci_builds USING btree (status, created_at, project_id) WHERE ((type)::text = 'Ci::Build'::text);
CREATE INDEX code_owner_approval_required ON public.protected_branches USING btree (project_id, code_owner_approval_required) WHERE (code_owner_approval_required = true);
@@ -19374,7 +19408,7 @@ CREATE INDEX index_labels_on_group_id_and_title ON public.labels USING btree (gr
CREATE INDEX index_labels_on_project_id ON public.labels USING btree (project_id);
-CREATE INDEX index_labels_on_project_id_and_title ON public.labels USING btree (project_id, title) WHERE (group_id = NULL::integer);
+CREATE UNIQUE INDEX index_labels_on_project_id_and_title_unique ON public.labels USING btree (project_id, title) WHERE (group_id IS NULL);
CREATE INDEX index_labels_on_template ON public.labels USING btree (template) WHERE template;
@@ -21016,6 +21050,9 @@ ALTER TABLE ONLY public.vulnerabilities
ALTER TABLE ONLY public.labels
ADD CONSTRAINT fk_7de4989a69 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
+ALTER TABLE ONLY public.backup_labels
+ ADD CONSTRAINT fk_7de4989a69 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY public.merge_requests
ADD CONSTRAINT fk_7e85395a64 FOREIGN KEY (sprint_id) REFERENCES public.sprints(id) ON DELETE CASCADE;
@@ -22222,6 +22259,9 @@ ALTER TABLE ONLY public.serverless_domain_cluster
ALTER TABLE ONLY public.labels
ADD CONSTRAINT fk_rails_c1ac5161d8 FOREIGN KEY (group_id) REFERENCES public.namespaces(id) ON DELETE CASCADE;
+ALTER TABLE ONLY public.backup_labels
+ ADD CONSTRAINT fk_rails_c1ac5161d8 FOREIGN KEY (group_id) REFERENCES public.namespaces(id) ON DELETE CASCADE;
+
ALTER TABLE ONLY public.project_feature_usages
ADD CONSTRAINT fk_rails_c22a50024b FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE;
@@ -23208,6 +23248,10 @@ COPY "schema_migrations" (version) FROM STDIN;
20200304160801
20200304160823
20200304211738
+20200305020458
+20200305020459
+20200305082754
+20200305082858
20200305121159
20200305151736
20200305200641