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
|