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

prevent_single_statement_with_disable_ddl_transaction.rb « migration « cop « rubocop - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 547dfb609451dc5d2fc918959dab0cf80a6b8718 (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
78
79
80
81
82
83
84
85
86
# frozen_string_literal: true

require_relative '../../migration_helpers'

module RuboCop
  module Cop
    module Migration
      # Cop that prevents usage of `disable_ddl_transaction!`
      # if the only statement being called in the migration is :validate_foreign_key.
      #
      # We do this because PostgreSQL will add an implicit transaction for single
      # statements. So there's no reason to add the disable_ddl_transaction!.
      #
      # This cop was introduced to clarify the need for disable_ddl_transaction!
      # and to avoid bike-shedding and review back-and-forth.
      #
      # @examples
      #
      #   # bad
      #   class SomeMigration < Gitlab::Database::Migration[2.1]
      #     disable_ddl_transaction!
      #     def up
      #       validate_foreign_key :emails, :user_id
      #     end
      #     def down
      #       # no-op
      #     end
      #   end
      #
      #   # good
      #   class SomeMigration < Gitlab::Database::Migration[2.1]
      #     def up
      #       validate_foreign_key :emails, :user_id
      #     end
      #     def down
      #       # no-op
      #     end
      #   end
      class PreventSingleStatementWithDisableDdlTransaction < RuboCop::Cop::Base
        include MigrationHelpers

        MSG = "PostgreSQL will add an implicit transaction for single statements. " \
              "So there's no reason to use `disable_ddl_transaction!`, if you're only " \
              "executing validate_foreign_key."

        def_node_matcher :disable_ddl_transaction?, <<~PATTERN
          (send _ :disable_ddl_transaction! ...)
        PATTERN

        def_node_matcher :validate_foreign_key?, <<~PATTERN
          (send :validate_foreign_key ...)
        PATTERN

        def on_begin(node)
          return unless in_migration?(node)

          disable_ddl_transaction_node = nil

          # Only perform cop if disable_ddl_transaction! is present
          node.each_descendant(:send) do |send_node|
            disable_ddl_transaction_node = send_node if disable_ddl_transaction?(send_node)
          end

          return unless disable_ddl_transaction_node

          # For each migration method, check if :validate_foreign_key is the only statement.
          node.each_descendant(:def) do |def_node|
            break unless [:up, :down, :change].include? def_node.children[0]

            statement_count = 0
            has_validate_foreign_key = false

            def_node.each_descendant(:send) do |send_node|
              has_validate_foreign_key = true if send_node.children[1] == :validate_foreign_key
              statement_count += 1
            end

            if disable_ddl_transaction_node && has_validate_foreign_key && statement_count == 1
              add_offense(disable_ddl_transaction_node, message: MSG)
            end
          end
        end
      end
    end
  end
end