summaryrefslogtreecommitdiff
path: root/db/migrate/20180113220114_rework_redirect_routes_indexes.rb
blob: ca7ce6286dc2969f992f87573ef9518b837b1892 (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
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.

class ReworkRedirectRoutesIndexes < ActiveRecord::Migration[4.2]
  include Gitlab::Database::MigrationHelpers

  # Set this constant to true if this migration requires downtime.
  DOWNTIME = false

  disable_ddl_transaction!

  INDEX_NAME_UNIQUE = "index_redirect_routes_on_path_unique_text_pattern_ops"

  INDEX_NAME_PERM = "index_redirect_routes_on_path_text_pattern_ops_where_permanent"
  INDEX_NAME_TEMP = "index_redirect_routes_on_path_text_pattern_ops_where_temporary"

  OLD_INDEX_NAME_PATH_TPOPS = "index_redirect_routes_on_path_text_pattern_ops"
  OLD_INDEX_NAME_PATH_LOWER = "index_on_redirect_routes_lower_path"

  def up
    disable_statement_timeout do
      # this is a plain btree on a single boolean column. It'll never be
      # selective enough to be valuable.
      if index_exists?(:redirect_routes, :permanent)
        remove_concurrent_index(:redirect_routes, :permanent)
      end

      if_not_exists = Gitlab::Database.version.to_f >= 9.5 ? "IF NOT EXISTS" : ""

      # Unique index on lower(path) across both types of redirect_routes:
      execute("CREATE UNIQUE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_UNIQUE} ON redirect_routes (lower(path) varchar_pattern_ops);")

      # Make two indexes on path -- one for permanent and one for temporary routes:
      execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_PERM} ON redirect_routes (lower(path) varchar_pattern_ops) where (permanent);")
      execute("CREATE INDEX CONCURRENTLY #{if_not_exists} #{INDEX_NAME_TEMP} ON redirect_routes (lower(path) varchar_pattern_ops) where (not permanent or permanent is null) ;")

      # Remove the old indexes:

      # This one needed to be on lower(path) but wasn't so it's replaced with the two above
      execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_TPOPS};"

      # This one isn't needed because we only ever do = and LIKE on this
      # column so the varchar_pattern_ops index is sufficient
      execute "DROP INDEX CONCURRENTLY IF EXISTS #{OLD_INDEX_NAME_PATH_LOWER};"
    end
  end

  def down
    disable_statement_timeout do
      add_concurrent_index(:redirect_routes, :permanent)

      execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_TPOPS} ON redirect_routes (path varchar_pattern_ops);")
      execute("CREATE INDEX CONCURRENTLY #{OLD_INDEX_NAME_PATH_LOWER} ON redirect_routes (LOWER(path));")

      execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_UNIQUE};")
      execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_PERM};")
      execute("DROP INDEX CONCURRENTLY IF EXISTS #{INDEX_NAME_TEMP};")
    end
  end
end