gitlab-org--gitlab-foss/db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb
Sean McGivern d8be981466 Prevent update_column_in_batches on large tables
add_column_with_default is implemented in terms of update_column_in_batches, but
update_column_in_batches can be used independently. Neither of these should be
used on the specified large tables, because they will cause issues on large
instances like GitLab.com.

This also ignores the cop for all existing migrations, renaming
AddColumnWithDefaultToLargeTable where appropriate.
2017-11-17 16:56:50 +00:00

105 lines
3.1 KiB
Ruby

# rubocop:disable Migration/UpdateLargeTable
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class FixWronglyRenamedRoutes < ActiveRecord::Migration
include Gitlab::Database::RenameReservedPathsMigration::V1
DOWNTIME = false
disable_ddl_transaction!
DISALLOWED_ROOT_PATHS = %w[
-
abuse_reports
api
autocomplete
explore
health_check
import
invites
jwt
koding
member
notification_settings
oauth
sent_notifications
unicorn_test
uploads
users
]
FIXED_PATHS = DISALLOWED_ROOT_PATHS.map { |p| "#{p}0" }
class Route < Gitlab::Database::RenameReservedPathsMigration::V1::MigrationClasses::Route
self.table_name = 'routes'
end
def routes
@routes ||= Route.arel_table
end
def namespaces
@namespaces ||= Arel::Table.new(:namespaces)
end
def wildcard_collection(collection)
collection.map { |word| "#{word}%" }
end
# The routes that got incorrectly renamed before, still have a namespace that
# contains the correct path.
# This query fetches all rows from the `routes` table that meet the following
# conditions using `api` as an example:
# - route.path ILIKE `api0%`
# - route.source_type = `Namespace`
# - namespace.parent_id IS NULL
# - namespace.path ILIKE `api%`
# - NOT(namespace.path ILIKE `api0%`)
# This gives us all root-routes, that were renamed, but their namespace was not.
#
def wrongly_renamed
Route.joins("INNER JOIN namespaces ON routes.source_id = namespaces.id")
.where(
routes[:source_type].eq('Namespace')
.and(namespaces[:parent_id].eq(nil))
)
.where(namespaces[:path].matches_any(wildcard_collection(DISALLOWED_ROOT_PATHS)))
.where.not(namespaces[:path].matches_any(wildcard_collection(FIXED_PATHS)))
.where(routes[:path].matches_any(wildcard_collection(FIXED_PATHS)))
end
# Using the query above, we just fetch the `route.path` & the `namespace.path`
# `route.path` is the part of the route that is now incorrect
# `namespace.path` is what it should be
# We can use `route.path` to find all the namespaces that need to be fixed
# And we can use `namespace.path` to apply the correct name.
#
def paths_and_corrections
connection.select_all(
wrongly_renamed.select(routes[:path], namespaces[:path].as('namespace_path')).to_sql
)
end
# This can be used to limit the `update_in_batches` call to all routes for a
# single namespace, note the `/` that's what went wrong in the initial migration.
#
def routes_in_namespace_query(namespace)
routes[:path].matches_any([namespace, "#{namespace}/%"])
end
def up
paths_and_corrections.each do |root_namespace|
wrong_path = root_namespace['path']
correct_path = root_namespace['namespace_path']
replace_statement = replace_sql(Route.arel_table[:path], wrong_path, correct_path)
update_column_in_batches(:routes, :path, replace_statement) do |table, query|
query.where(routes_in_namespace_query(wrong_path))
end
end
end
def down
end
end