summaryrefslogtreecommitdiff
path: root/db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb
diff options
context:
space:
mode:
Diffstat (limited to 'db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb')
-rw-r--r--db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb104
1 files changed, 104 insertions, 0 deletions
diff --git a/db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb b/db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb
new file mode 100644
index 00000000000..c78beda9d21
--- /dev/null
+++ b/db/post_migrate/20170518231126_fix_wrongly_renamed_routes.rb
@@ -0,0 +1,104 @@
+# 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