summaryrefslogtreecommitdiff
path: root/db/post_migrate/20161109150329_fix_project_records_with_invalid_visibility.rb
blob: bea1cfa4c5d4f57d29214f7427fa8b016f533f7c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
class FixProjectRecordsWithInvalidVisibility < ActiveRecord::Migration
  include Gitlab::Database::MigrationHelpers

  BATCH_SIZE = 1000
  DOWNTIME = false

  # This migration is idempotent and there's no sense in throwing away the
  # partial result if it's interrupted
  disable_ddl_transaction!

  def up
    projects = Arel::Table.new(:projects)
    namespaces = Arel::Table.new(:namespaces)

    finder =
      projects.
      join(namespaces, Arel::Nodes::InnerJoin).
      on(projects[:namespace_id].eq(namespaces[:id])).
      where(projects[:visibility_level].gt(namespaces[:visibility_level])).
      project(projects[:id]).
      take(BATCH_SIZE)

    # MySQL requires a derived table to perform this query
    nested_finder =
      projects.
      from(finder.as("AS projects_inner")).
      project(projects[:id])

    valuer =
      namespaces.
      where(namespaces[:id].eq(projects[:namespace_id])).
      project(namespaces[:visibility_level])

    # Update matching rows until none remain. The finder contains a limit.
    loop do
      updater = Arel::UpdateManager.new(ActiveRecord::Base).
        table(projects).
        set(projects[:visibility_level] => Arel::Nodes::SqlLiteral.new("(#{valuer.to_sql})")).
        where(projects[:id].in(nested_finder))

      num_updated = connection.exec_update(updater.to_sql, self.class.name, [])
      break if num_updated == 0
    end
  end

  def down
    # no-op
  end
end