d8be981466
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.
105 lines
3.1 KiB
Ruby
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
|