Commit 09e993ec authored by blackst0ne's avatar blackst0ne

Add Migration/DropTable rubocop cop

Calling `drop_table` in deployment migrations can cause downtime as
there might still be code that uses tables.

This MR adds a cop that checks that tables get droppped only in
post-deployment migrations.
parent df71d756
...@@ -17,6 +17,8 @@ class CreateMergeRequestAssigneesTable < ActiveRecord::Migration[5.0] ...@@ -17,6 +17,8 @@ class CreateMergeRequestAssigneesTable < ActiveRecord::Migration[5.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :merge_request_assignees drop_table :merge_request_assignees
# rubocop:enable Migration/DropTable
end end
end end
...@@ -193,7 +193,9 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] ...@@ -193,7 +193,9 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0]
end end
def drop_table_if_exists(table) def drop_table_if_exists(table)
# rubocop:disable Migration/DropTable
drop_table(table) if table_exists?(table) drop_table(table) if table_exists?(table)
# rubocop:enable Migration/DropTable
end end
def add_column_with_default_if_not_exists(table, name, *args) def add_column_with_default_if_not_exists(table, name, *args)
......
...@@ -15,6 +15,8 @@ class CreateMilestoneReleasesTable < ActiveRecord::Migration[5.2] ...@@ -15,6 +15,8 @@ class CreateMilestoneReleasesTable < ActiveRecord::Migration[5.2]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :milestone_releases drop_table :milestone_releases
# rubocop:enable Migration/DropTable
end end
end end
...@@ -24,6 +24,8 @@ class CreateDescriptionVersions < ActiveRecord::Migration[5.2] ...@@ -24,6 +24,8 @@ class CreateDescriptionVersions < ActiveRecord::Migration[5.2]
def down def down
remove_column :system_note_metadata, :description_version_id remove_column :system_note_metadata, :description_version_id
# rubocop:disable Migration/DropTable
drop_table :description_versions drop_table :description_versions
# rubocop:enable Migration/DropTable
end end
end end
...@@ -23,6 +23,8 @@ class AddGroupDeletionSchedules < ActiveRecord::Migration[5.2] ...@@ -23,6 +23,8 @@ class AddGroupDeletionSchedules < ActiveRecord::Migration[5.2]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :group_deletion_schedules drop_table :group_deletion_schedules
# rubocop:enable Migration/DropTable
end end
end end
...@@ -23,6 +23,8 @@ class CreateGitlabSubscriptionHistories < ActiveRecord::Migration[5.2] ...@@ -23,6 +23,8 @@ class CreateGitlabSubscriptionHistories < ActiveRecord::Migration[5.2]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :gitlab_subscription_histories drop_table :gitlab_subscription_histories
# rubocop:enable Migration/DropTable
end end
end end
...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFilesTable < ActiveRecord::Migration[5.2] ...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFilesTable < ActiveRecord::Migration[5.2]
def up def up
# Requires ExclusiveLock on the table. Not in use, no records, no FKs. # Requires ExclusiveLock on the table. Not in use, no records, no FKs.
# rubocop:disable Migration/DropTable
drop_table :analytics_repository_files drop_table :analytics_repository_files
# rubocop:enable Migration/DropTable
end end
def down def down
......
...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileCommitsTable < ActiveRecord::Migration[5.2] ...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileCommitsTable < ActiveRecord::Migration[5.2]
def up def up
# Requires ExclusiveLock on the table. Not in use, no records, no FKs. # Requires ExclusiveLock on the table. Not in use, no records, no FKs.
# rubocop:disable Migration/DropTable
drop_table :analytics_repository_file_commits drop_table :analytics_repository_file_commits
# rubocop:enable Migration/DropTable
end end
def down def down
......
...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileEditsTable < ActiveRecord::Migration[5.2] ...@@ -7,7 +7,9 @@ class DropAnalyticsRepositoryFileEditsTable < ActiveRecord::Migration[5.2]
def up def up
# Requires ExclusiveLock on the table. Not in use, no records, no FKs. # Requires ExclusiveLock on the table. Not in use, no records, no FKs.
# rubocop:disable Migration/DropTable
drop_table :analytics_repository_file_edits if table_exists?(:analytics_repository_file_edits) # this table might be already dropped on development environment drop_table :analytics_repository_file_edits if table_exists?(:analytics_repository_file_edits) # this table might be already dropped on development environment
# rubocop:enable Migration/DropTable
end end
def down def down
......
...@@ -20,7 +20,9 @@ class AddCanonicalEmails < ActiveRecord::Migration[6.0] ...@@ -20,7 +20,9 @@ class AddCanonicalEmails < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table(:user_canonical_emails) drop_table(:user_canonical_emails)
# rubocop:enable Migration/DropTable
end end
end end
end end
...@@ -8,6 +8,7 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0] ...@@ -8,6 +8,7 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0]
DOWNTIME = false DOWNTIME = false
def change def change
# rubocop:disable Migration/DropTable
drop_table "forked_project_links", id: :serial do |t| drop_table "forked_project_links", id: :serial do |t|
t.integer "forked_to_project_id", null: false t.integer "forked_to_project_id", null: false
t.integer "forked_from_project_id", null: false t.integer "forked_from_project_id", null: false
...@@ -15,5 +16,6 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0] ...@@ -15,5 +16,6 @@ class DropForkedProjectLinksTable < ActiveRecord::Migration[6.0]
t.datetime "updated_at" t.datetime "updated_at"
t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true t.index ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true
end end
# rubocop:enable Migration/DropTable
end end
end end
...@@ -20,7 +20,9 @@ class CreateUserDetails < ActiveRecord::Migration[6.0] ...@@ -20,7 +20,9 @@ class CreateUserDetails < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :user_details drop_table :user_details
# rubocop:enable Migration/DropTable
end end
end end
end end
...@@ -19,7 +19,9 @@ class CreateUserHighestRoles < ActiveRecord::Migration[6.0] ...@@ -19,7 +19,9 @@ class CreateUserHighestRoles < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :user_highest_roles drop_table :user_highest_roles
# rubocop:enable Migration/DropTable
end end
end end
end end
...@@ -30,6 +30,8 @@ class CreateDiffNotePositions < ActiveRecord::Migration[6.0] ...@@ -30,6 +30,8 @@ class CreateDiffNotePositions < ActiveRecord::Migration[6.0]
# rubocop:enable Migration/AddLimitToTextColumns # rubocop:enable Migration/AddLimitToTextColumns
def down def down
# rubocop:disable Migration/DropTable
drop_table :diff_note_positions drop_table :diff_note_positions
# rubocop:enable Migration/DropTable
end end
end end
...@@ -9,7 +9,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0] ...@@ -9,7 +9,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0]
def up def up
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :ci_refs drop_table :ci_refs
# rubocop:enable Migration/DropTable
create_table :ci_refs do |t| create_table :ci_refs do |t|
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :bigint t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :bigint
...@@ -23,7 +25,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0] ...@@ -23,7 +25,9 @@ class RecreateCiRef < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :ci_refs drop_table :ci_refs
# rubocop:enable Migration/DropTable
create_table :ci_refs do |t| create_table :ci_refs do |t|
t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :integer t.references :project, null: false, index: false, foreign_key: { on_delete: :cascade }, type: :integer
......
...@@ -16,7 +16,9 @@ class AddProjectComplianceFrameworkSettingsTable < ActiveRecord::Migration[6.0] ...@@ -16,7 +16,9 @@ class AddProjectComplianceFrameworkSettingsTable < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :project_compliance_framework_settings drop_table :project_compliance_framework_settings
# rubocop:enable Migration/DropTable
end end
end end
end end
...@@ -26,6 +26,8 @@ class CreatePartitionedForeignKeys < ActiveRecord::Migration[6.0] ...@@ -26,6 +26,8 @@ class CreatePartitionedForeignKeys < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :partitioned_foreign_keys drop_table :partitioned_foreign_keys
# rubocop:enable Migration/DropTable
end end
end end
...@@ -26,6 +26,8 @@ class CreateProjectRepositoryStorageMoves < ActiveRecord::Migration[6.0] ...@@ -26,6 +26,8 @@ class CreateProjectRepositoryStorageMoves < ActiveRecord::Migration[6.0]
remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_source_storage_name') remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_source_storage_name')
remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_destination_storage_name') remove_check_constraint(:project_repository_storage_moves, 'project_repository_storage_moves_destination_storage_name')
# rubocop:disable Migration/DropTable
drop_table :project_repository_storage_moves drop_table :project_repository_storage_moves
# rubocop:enable Migration/DropTable
end end
end end
...@@ -25,6 +25,8 @@ class CreateCiFreezePeriods < ActiveRecord::Migration[6.0] ...@@ -25,6 +25,8 @@ class CreateCiFreezePeriods < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :ci_freeze_periods drop_table :ci_freeze_periods
# rubocop:enable Migration/DropTable
end end
end end
...@@ -15,6 +15,8 @@ class CreateStatusPagePublishedIncidents < ActiveRecord::Migration[6.0] ...@@ -15,6 +15,8 @@ class CreateStatusPagePublishedIncidents < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :status_page_published_incidents drop_table :status_page_published_incidents
# rubocop:enable Migration/DropTable
end end
end end
...@@ -39,6 +39,8 @@ class CreateAlertManagementAlerts < ActiveRecord::Migration[6.0] ...@@ -39,6 +39,8 @@ class CreateAlertManagementAlerts < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :alert_management_alerts drop_table :alert_management_alerts
# rubocop:enable Migration/DropTable
end end
end end
...@@ -20,6 +20,8 @@ class AddGroupImportStatesTable < ActiveRecord::Migration[6.0] ...@@ -20,6 +20,8 @@ class AddGroupImportStatesTable < ActiveRecord::Migration[6.0]
# rubocop:enable Migration/AddLimitToTextColumns # rubocop:enable Migration/AddLimitToTextColumns
def down def down
# rubocop:disable Migration/DropTable
drop_table :group_import_states drop_table :group_import_states
# rubocop:enable Migration/DropTable
end end
end end
...@@ -20,6 +20,8 @@ class CreateMetricsUsersStarredDashboard < ActiveRecord::Migration[6.0] ...@@ -20,6 +20,8 @@ class CreateMetricsUsersStarredDashboard < ActiveRecord::Migration[6.0]
# rubocop: enable Migration/AddLimitToTextColumns # rubocop: enable Migration/AddLimitToTextColumns
def down def down
# rubocop:disable Migration/DropTable
drop_table :metrics_users_starred_dashboards drop_table :metrics_users_starred_dashboards
# rubocop:enable Migration/DropTable
end end
end end
...@@ -26,6 +26,8 @@ class CreateCiInstanceVariables < ActiveRecord::Migration[6.0] ...@@ -26,6 +26,8 @@ class CreateCiInstanceVariables < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :ci_instance_variables drop_table :ci_instance_variables
# rubocop:enable Migration/DropTable
end end
end end
...@@ -21,6 +21,8 @@ class CreateNugetDependencyLinkMetadata < ActiveRecord::Migration[6.0] ...@@ -21,6 +21,8 @@ class CreateNugetDependencyLinkMetadata < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :packages_nuget_dependency_link_metadata drop_table :packages_nuget_dependency_link_metadata
# rubocop:enable Migration/DropTable
end end
end end
...@@ -29,6 +29,8 @@ class CreatePackagesNugetMetadata < ActiveRecord::Migration[6.0] ...@@ -29,6 +29,8 @@ class CreatePackagesNugetMetadata < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :packages_nuget_metadata drop_table :packages_nuget_metadata
# rubocop:enable Migration/DropTable
end end
end end
...@@ -31,6 +31,8 @@ class CreateGroupDeployKeys < ActiveRecord::Migration[6.0] ...@@ -31,6 +31,8 @@ class CreateGroupDeployKeys < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :group_deploy_keys drop_table :group_deploy_keys
# rubocop:enable Migration/DropTable
end end
end end
...@@ -17,6 +17,8 @@ class CreateAlertManagementAlertAssignees < ActiveRecord::Migration[6.0] ...@@ -17,6 +17,8 @@ class CreateAlertManagementAlertAssignees < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :alert_management_alert_assignees drop_table :alert_management_alert_assignees
# rubocop:enable Migration/DropTable
end end
end end
...@@ -21,7 +21,9 @@ class CreateProjectSecuritySettings < ActiveRecord::Migration[6.0] ...@@ -21,7 +21,9 @@ class CreateProjectSecuritySettings < ActiveRecord::Migration[6.0]
def down def down
with_lock_retries do with_lock_retries do
# rubocop:disable Migration/DropTable
drop_table :project_security_settings drop_table :project_security_settings
# rubocop:enable Migration/DropTable
end end
end end
end end
...@@ -15,6 +15,8 @@ class CreateBoardUserPreferences < ActiveRecord::Migration[6.0] ...@@ -15,6 +15,8 @@ class CreateBoardUserPreferences < ActiveRecord::Migration[6.0]
end end
def down def down
# rubocop:disable Migration/DropTable
drop_table :board_user_preferences drop_table :board_user_preferences
# rubocop:enable Migration/DropTable
end end
end end
...@@ -52,7 +52,10 @@ class MigrateCiJobArtifactsToSeparateRegistry < ActiveRecord::Migration[4.2] ...@@ -52,7 +52,10 @@ class MigrateCiJobArtifactsToSeparateRegistry < ActiveRecord::Migration[4.2]
end end
def down def down
# rubocop:disable Migration/DropTable
tracking_db.drop_table :job_artifact_registry tracking_db.drop_table :job_artifact_registry
# rubocop:enable Migration/DropTable
execute('DROP TRIGGER IF EXISTS replicate_job_artifact_registry ON file_registry') execute('DROP TRIGGER IF EXISTS replicate_job_artifact_registry ON file_registry')
execute('DROP FUNCTION IF EXISTS replicate_job_artifact_registry()') execute('DROP FUNCTION IF EXISTS replicate_job_artifact_registry()')
end end
......
# frozen_string_literal: true
require_relative '../../migration_helpers'
module RuboCop
module Cop
module Migration
# Cop that checks if `drop_table` is called in deployment migrations.
# Calling it in deployment migrations can cause downtimes as there still may be code using the target tables.
class DropTable < RuboCop::Cop::Cop
include MigrationHelpers
MSG = <<-MESSAGE.delete("\n").squeeze
`drop_table` in deployment migrations requires downtime.
Drop tables in post-deployment migrations instead.
MESSAGE
def on_def(node)
return unless in_deployment_migration?(node)
node.each_descendant(:send) do |send_node|
next unless offensible?(send_node)
add_offense(send_node, location: :selector)
end
end
private
def offensible?(node)
drop_table?(node) || drop_table_in_execute?(node)
end
def drop_table?(node)
node.children[1] == :drop_table
end
def drop_table_in_execute?(node)
execute?(node) && drop_table_in_execute_sql?(node)
end
def execute?(node)
node.children[1] == :execute
end
def drop_table_in_execute_sql?(node)
node.children[2].to_s.match?(/drop\s+table/i)
end
end
end
end
end
...@@ -23,7 +23,11 @@ module RuboCop ...@@ -23,7 +23,11 @@ module RuboCop
# Returns true if the given node originated from the db/migrate directory. # Returns true if the given node originated from the db/migrate directory.
def in_migration?(node) def in_migration?(node)
dirname(node).end_with?('db/migrate', 'db/geo/migrate') || in_post_deployment_migration?(node) in_deployment_migration?(node) || in_post_deployment_migration?(node)
end
def in_deployment_migration?(node)
dirname(node).end_with?('db/migrate', 'db/geo/migrate')
end end
def in_post_deployment_migration?(node) def in_post_deployment_migration?(node)
......
# frozen_string_literal: true
require 'spec_helper'
require 'rubocop'
require 'rubocop/rspec/support'
require_relative '../../../../rubocop/cop/migration/drop_table'
describe RuboCop::Cop::Migration::DropTable do
include CopHelper
subject(:cop) { described_class.new }
context 'when in deployment migration' do
before do
allow(cop).to receive(:in_deployment_migration?).and_return(true)
end
it 'registers an offense' do
expect_offense(<<~PATTERN)
def change
drop_table :table
^^^^^^^^^^ #{described_class::MSG}
add_column(:users, :username, :text)
execute "DROP TABLE table"
^^^^^^^ #{described_class::MSG}
execute "CREATE UNIQUE INDEX email_index ON users (email);"
end
PATTERN
end
end
context 'when in post-deployment migration' do
before do
allow(cop).to receive(:in_post_deployment_migration?).and_return(true)
end
it 'registers no offense' do
expect_no_offenses(<<~PATTERN)
def change
drop_table :table
execute "DROP TABLE table"
end
PATTERN
end
end
context 'when outside of migration' do
it 'registers no offense' do
expect_no_offenses(<<~PATTERN)
def change
drop_table :table
execute "DROP TABLE table"
end
PATTERN
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment