Welcome to mirror list, hosted at ThFree Co, Russian Federation.

duplicate_indexes.rb « database « helpers « support « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0ad8ee1e05548f440ee5be10c9b071c243bf9411 (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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# frozen_string_literal: true

module Database
  class DuplicateIndexes
    attr_accessor :table_name, :indexes

    BTREE_INDEX_STRUCT = Struct.new(:name, :columns, :unique)

    def initialize(table_name, indexes)
      @table_name = table_name
      @indexes = indexes
    end

    def duplicate_indexes
      ret = {}

      btree_indexes.each do |btree_index|
        matching_indexes = matching_indexes_for(btree_index)
        next unless matching_indexes.any?

        ret[btree_index] = matching_indexes
      end

      ret
    end

    def self.btree_index_struct(index)
      BTREE_INDEX_STRUCT.new(
        index.name,
        Array.wrap(index.columns).map do |column|
          # https://apidock.com/rails/ActiveRecord/ConnectionAdapters/PostgreSQL/SchemaStatements/indexes
          # asc is the default order
          column_order = index.orders.is_a?(Symbol) ? index.orders : (index.orders[column] || :asc)
          { name: column, order: column_order }
        end,
        index.unique
      )
    end

    private

    def btree_indexes
      return @btree_indexes if @btree_indexes

      # We only scan non-conditional btree indexes
      @btree_indexes = indexes.select do |index|
        index.using == :btree && index.where.nil? && index.opclasses.blank?
      end

      @btree_indexes = @btree_indexes.map { |index| self.class.btree_index_struct(index) }
    end

    def matching_indexes_for(btree_index)
      all_matching_indexes = []

      # When comparing btree_index with other_index. btree_index is the index that can have more columns
      # than the other_index.
      (1..btree_index.columns.length).each do |subset_length|
        columns = btree_index.columns.first(subset_length)
        matching_indexes = btree_indexes.reject { |other_index| other_index == btree_index }.select do |other_index|
          other_index.columns == columns
        end

        # For now we ignore other indexes that are UNIQUE and have a matching columns subset of
        # the btree_index columns, as UNIQUE indexes are still needed to enforce uniqueness
        # constraints on subset of the columns.
        matching_indexes = matching_indexes.reject do |other_index|
          (other_index.unique && (other_index.columns.length < btree_index.columns.length))
        end

        all_matching_indexes += matching_indexes
      end

      all_matching_indexes
    end
  end
end