summaryrefslogtreecommitdiff
path: root/db/post_migrate/20210813151908_replace_external_wiki_triggers.rb
blob: d11baae42e26d9075cde243b24fce2bf1a979121 (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
# frozen_string_literal: true

class ReplaceExternalWikiTriggers < ActiveRecord::Migration[6.1]
  include Gitlab::Database::SchemaHelpers

  def up
    replace_triggers('type_new', 'Integrations::ExternalWiki')

    # we need an extra trigger to handle when type_new is updated by the
    # `integrations_set_type_new` trigger.
    # This can be removed when this trigger has been removed.
    execute(<<~SQL.squish)
      CREATE TRIGGER #{trigger_name(:type_new_updated)}
        AFTER UPDATE OF type_new ON integrations FOR EACH ROW
        WHEN ((new.type_new)::text = 'Integrations::ExternalWiki'::text AND new.project_id IS NOT NULL)
        EXECUTE FUNCTION set_has_external_wiki();
    SQL
  end

  def down
    execute("DROP TRIGGER IF EXISTS #{trigger_name(:type_new_updated)} ON integrations;")
    replace_triggers('type', 'ExternalWikiService')
  end

  private

  def replace_triggers(column_name, value)
    triggers(column_name, value).each do |event, condition|
      trigger = trigger_name(event)

      # create duplicate trigger, using the defined condition
      execute(<<~SQL.squish)
      CREATE TRIGGER #{trigger}_new AFTER #{event.upcase} ON integrations FOR EACH ROW
        WHEN (#{condition})
        EXECUTE FUNCTION set_has_external_wiki();
      SQL

      # Swap the triggers in place, so that the new trigger has the canonical name
      execute("ALTER TRIGGER #{trigger} ON integrations RENAME TO #{trigger}_old;")
      execute("ALTER TRIGGER #{trigger}_new ON integrations RENAME TO #{trigger};")

      # remove the old, now redundant trigger
      execute("DROP TRIGGER IF EXISTS #{trigger}_old ON integrations;")
    end
  end

  def trigger_name(event)
    "trigger_has_external_wiki_on_#{event}"
  end

  def triggers(column_name, value)
    {
      delete: "#{matches_value('old', column_name, value)} AND #{project_not_null('old')}",
      insert: "(new.active = true) AND #{matches_value('new', column_name, value)} AND #{project_not_null('new')}",
      update: "#{matches_value('new', column_name, value)} AND (old.active <> new.active) AND #{project_not_null('new')}"
    }
  end

  def project_not_null(row)
    "(#{row}.project_id IS NOT NULL)"
  end

  def matches_value(row, column_name, value)
    "((#{row}.#{column_name})::text = '#{value}'::text)"
  end
end