summaryrefslogtreecommitdiff
path: root/lib/gitlab/background_migration/populate_user_highest_roles_table.rb
blob: 16386ebf9c3496ba33525153dc6f1b1f2ee08a39 (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
50
51
52
53
54
55
56
57
58
# frozen_string_literal: true

module Gitlab
  module BackgroundMigration
    # This background migration creates records on user_highest_roles according to
    # the given user IDs range. IDs will load users with a left outer joins to
    # have a record for users without a Group or Project. One INSERT per ID is
    # issued.
    class PopulateUserHighestRolesTable
      BATCH_SIZE = 100

      # rubocop:disable Style/Documentation
      class User < ActiveRecord::Base
        self.table_name = 'users'

        scope :active, -> {
          where(state: 'active', user_type: nil, bot_type: nil)
            .where('ghost IS NOT TRUE')
        }
      end

      def perform(from_id, to_id)
        return unless User.column_names.include?('bot_type')

        (from_id..to_id).each_slice(BATCH_SIZE) do |ids|
          execute(
            <<-EOF
              INSERT INTO user_highest_roles (updated_at, user_id, highest_access_level)
              #{select_sql(from_id, to_id)}
              ON CONFLICT (user_id) DO
              UPDATE SET highest_access_level = EXCLUDED.highest_access_level
            EOF
          )
        end
      end

      private

      def select_sql(from_id, to_id)
        User
          .select('NOW() as updated_at, users.id, MAX(access_level) AS highest_access_level')
          .joins('LEFT OUTER JOIN members ON members.user_id = users.id AND members.requested_at IS NULL')
          .where(users: { id: active_user_ids(from_id, to_id) })
          .group('users.id')
          .to_sql
      end

      def active_user_ids(from_id, to_id)
        User.active.where(users: { id: from_id..to_id }).pluck(:id)
      end

      def execute(sql)
        @connection ||= ActiveRecord::Base.connection
        @connection.execute(sql)
      end
    end
  end
end