rubocop-isucon 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -1
  3. data/README.md +17 -6
  4. data/config/default.yml +36 -0
  5. data/lib/rubocop/cop/isucon/correctors/{mysql2_n_plus_one_query_corrector → n_plus_one_query_corrector}/correctable_methods.rb +1 -1
  6. data/lib/rubocop/cop/isucon/correctors/{mysql2_n_plus_one_query_corrector → n_plus_one_query_corrector}/replace_methods.rb +1 -1
  7. data/lib/rubocop/cop/isucon/correctors/{mysql2_n_plus_one_query_corrector.rb → n_plus_one_query_corrector.rb} +15 -4
  8. data/lib/rubocop/cop/isucon/mixin/join_without_index_methods.rb +87 -0
  9. data/lib/rubocop/cop/isucon/mixin/many_join_table_methods.rb +39 -0
  10. data/lib/rubocop/cop/isucon/mixin/mysql2_xquery_methods.rb +7 -116
  11. data/lib/rubocop/cop/isucon/mixin/n_plus_one_query_methods.rb +153 -0
  12. data/lib/rubocop/cop/isucon/mixin/offense_location_methods.rb +130 -0
  13. data/lib/rubocop/cop/isucon/mixin/select_asterisk_methods.rb +148 -0
  14. data/lib/rubocop/cop/isucon/mixin/sqlite3_execute_methods.rb +67 -0
  15. data/lib/rubocop/cop/isucon/mixin/where_without_index_methods.rb +96 -0
  16. data/lib/rubocop/cop/isucon/mysql2/join_without_index.rb +4 -67
  17. data/lib/rubocop/cop/isucon/mysql2/many_join_table.rb +1 -26
  18. data/lib/rubocop/cop/isucon/mysql2/n_plus_one_query.rb +4 -114
  19. data/lib/rubocop/cop/isucon/mysql2/select_asterisk.rb +1 -135
  20. data/lib/rubocop/cop/isucon/mysql2/where_without_index.rb +5 -70
  21. data/lib/rubocop/cop/isucon/sqlite3/join_without_index.rb +37 -0
  22. data/lib/rubocop/cop/isucon/sqlite3/many_join_table.rb +61 -0
  23. data/lib/rubocop/cop/isucon/sqlite3/n_plus_one_query.rb +70 -0
  24. data/lib/rubocop/cop/isucon/sqlite3/select_asterisk.rb +37 -0
  25. data/lib/rubocop/cop/isucon/sqlite3/where_without_index.rb +40 -0
  26. data/lib/rubocop/cop/isucon_cops.rb +13 -1
  27. data/lib/rubocop/isucon/version.rb +1 -1
  28. metadata +17 -5
@@ -26,144 +26,10 @@ module RuboCop
26
26
  # db.xquery('SELECT users.id, users.name FROM users')
27
27
  #
28
28
  class SelectAsterisk < Base
29
- include Mixin::DatabaseMethods
30
29
  include Mixin::Mysql2XqueryMethods
30
+ include Mixin::SelectAsteriskMethods
31
31
 
32
32
  extend AutoCorrector
33
-
34
- MSG = "Use SELECT with column names. (e.g. `SELECT id, name FROM table_name`)"
35
-
36
- TODO = "# TODO: Remove needless columns if necessary\n"
37
-
38
- # @param node [RuboCop::AST::Node]
39
- def on_send(node)
40
- with_error_handling(node) do
41
- with_xquery(node) do |type, root_gda|
42
- next unless root_gda
43
-
44
- check_and_register_offence(type: type, root_gda: root_gda, node: node)
45
- end
46
- end
47
- end
48
-
49
- private
50
-
51
- # @param type [Symbol] Node type. one of `:str`, `:dstr`
52
- # @param root_gda [RuboCop::Isucon::GDA::Client]
53
- # @param node [RuboCop::AST::Node]
54
- def check_and_register_offence(type:, root_gda:, node:)
55
- root_gda.visit_all do |gda|
56
- next unless gda.ast.respond_to?(:expr_list)
57
-
58
- gda.ast.expr_list.each do |select_field_node|
59
- check_and_register_offence_for_select_field_node(
60
- type: type, node: node, gda: gda,
61
- select_field_node: select_field_node
62
- )
63
- end
64
- end
65
- end
66
-
67
- # @param type [Symbol] Node type. one of `:str`, `:dstr`
68
- # @param node [RuboCop::AST::Node]
69
- # @param gda [RuboCop::Isucon::GDA::Client]
70
- # @param select_field_node [GDA::Nodes::SelectField]
71
- def check_and_register_offence_for_select_field_node(type:, node:, gda:, select_field_node:)
72
- return unless select_field_node.respond_to?(:expr)
73
-
74
- select_field = parse_select_field_node(select_field_node)
75
-
76
- return unless select_field[:column_name] == "*"
77
-
78
- loc = offense_location(type: type, node: node, gda_location: select_field_node.expr.location)
79
- return unless loc
80
-
81
- add_offense(loc) do |corrector|
82
- perform_autocorrect(corrector: corrector, loc: loc, gda: gda, node: node,
83
- select_table_name: select_field[:table_name])
84
- end
85
- end
86
-
87
- # @param select_field_node [GDA::Nodes::SelectField]
88
- # @return [Hash<Symbol, String>] table_name, column_name
89
- def parse_select_field_node(select_field_node)
90
- column_elements = select_field_node.expr.value.split(".", 2)
91
-
92
- case column_elements.count
93
- when 1
94
- return { column_name: column_elements[0] }
95
- when 2
96
- return { table_name: column_elements[0], column_name: column_elements[1] }
97
- end
98
-
99
- {}
100
- end
101
-
102
- # @param corrector [RuboCop::Cop::Corrector]
103
- # @param loc [Parser::Source::Range]
104
- # @param gda [RuboCop::Isucon::GDA::Client]
105
- # @param node [RuboCop::AST::Node]
106
- # @param select_table_name [String,nil] table names included in the SELECT clause
107
- def perform_autocorrect(corrector:, loc:, gda:, node:, select_table_name:)
108
- return unless enabled_database?
109
- return if gda.table_names.empty?
110
-
111
- if select_table_name
112
- return unless gda.table_names.include?(select_table_name)
113
-
114
- replace_asterisk(corrector: corrector, loc: loc, table_name: select_table_name, table_prefix: true)
115
- else
116
- return unless gda.table_names.length == 1
117
-
118
- replace_asterisk(corrector: corrector, loc: loc, table_name: gda.table_names[0], table_prefix: false)
119
- end
120
-
121
- insert_todo_comment(corrector: corrector, node: node)
122
- end
123
-
124
- # @param corrector [RuboCop::Cop::Corrector]
125
- # @param loc [Parser::Source::Range]
126
- # @param table_name [String]
127
- # @param table_prefix [Boolean] Whether add table name to prefix (e.g. `users`.`name`)
128
- def replace_asterisk(corrector:, loc:, table_name:, table_prefix:)
129
- select_columns = columns_in_select_clause(table_name: table_name, table_prefix: table_prefix)
130
- corrector.replace(loc, select_columns)
131
- end
132
-
133
- # @param table_name [String]
134
- # @param table_prefix [Boolean] Whether add table name to prefix (e.g. `users`.`name`)
135
- # @return [String]
136
- def columns_in_select_clause(table_name:, table_prefix:)
137
- column_names = connection.column_names(table_name)
138
-
139
- column_names.map do |column|
140
- if table_prefix
141
- "`#{table_name}`.`#{column}`"
142
- else
143
- "`#{column}`"
144
- end
145
- end.join(", ")
146
- end
147
-
148
- # @param corrector [RuboCop::Cop::Corrector]
149
- # @param node [RuboCop::AST::Node]
150
- def insert_todo_comment(corrector:, node:)
151
- current_line = node.loc.expression.line
152
- current_line_range = node.loc.expression.source_buffer.line_range(current_line)
153
-
154
- indent = node_indent_level(node)
155
- comment_line = (" " * indent) + TODO
156
- corrector.insert_before(current_line_range, comment_line)
157
- end
158
-
159
- # @param node [RuboCop::AST::Node]
160
- # @return [Integer]
161
- def node_indent_level(node)
162
- node.loc.expression.source_line =~ /^(\s+)/
163
- return 0 unless Regexp.last_match(1)
164
-
165
- Regexp.last_match(1).length
166
- end
167
33
  end
168
34
  end
169
35
  end
@@ -19,84 +19,19 @@ module RuboCop
19
19
  # db.xquery('SELECT id, title FROM articles WHERE id = ?', id)
20
20
  #
21
21
  class WhereWithoutIndex < Base
22
- include Mixin::DatabaseMethods
23
22
  include Mixin::Mysql2XqueryMethods
23
+ include Mixin::WhereWithoutIndexMethods
24
24
 
25
25
  MSG = "This where clause doesn't seem to have an index. " \
26
26
  "(e.g. `ALTER TABLE %<table_name>s ADD INDEX index_%<column_name>s (%<column_name>s)`)"
27
27
 
28
- # @param node [RuboCop::AST::Node]
29
- def on_send(node)
30
- with_error_handling(node) do
31
- return unless enabled_database?
32
-
33
- with_xquery(node) do |type, root_gda|
34
- next unless root_gda
35
- next if exists_index_in_where_clause_columns?(root_gda)
36
-
37
- register_offense(type: type, node: node, root_gda: root_gda)
38
- end
39
- end
40
- end
41
-
42
28
  private
43
29
 
44
- # @param type [Symbol] Node type. one of `:str`, `:dstr`
45
- # @param node [RuboCop::AST::Node]
46
- # @param root_gda [RuboCop::Isucon::GDA::Client]
47
- def register_offense(type:, node:, root_gda:)
48
- root_gda.visit_all do |gda|
49
- next if gda.where_nodes.empty?
50
-
51
- loc = offense_location(type: type, node: node, gda_location: gda.where_nodes.first.location)
52
- next unless loc
53
-
54
- message = offense_message(gda)
55
- add_offense(loc, message: message)
56
- end
57
- end
58
-
59
- # @param gda [RuboCop::Isucon::GDA::Client]
60
- def offense_message(gda)
61
- column_name = gda.where_conditions[0].column_operand
62
- table_name = find_table_name_from_column_name(table_names: gda.table_names, column_name: column_name)
63
- format(MSG, table_name: table_name, column_name: column_name)
64
- end
65
-
66
- # @param root_gda [RuboCop::Isucon::GDA::Client]
67
- # @return [Boolean]
68
- def exists_index_in_where_clause_columns?(root_gda)
69
- root_gda.visit_all do |gda|
70
- gda.table_names.each do |table_name|
71
- return true if covered_where_column_in_index?(gda: gda, table_name: table_name)
72
- return true if covered_where_column_in_primary_key?(gda: gda, table_name: table_name)
73
- end
74
- end
75
-
76
- false
77
- end
78
-
79
- # @param gda [RuboCop::Isucon::GDA::Client]
80
- # @param table_name [String]
81
- # @return [Boolean]
82
- def covered_where_column_in_index?(gda:, table_name:)
83
- indexes = connection.indexes(table_name)
84
- index_first_columns = indexes.map { |index| index.columns[0] }
85
-
86
- gda.where_conditions.any? do |condition|
87
- index_first_columns.include?(condition.column_operand)
88
- end
89
- end
90
-
91
- # @param gda [RuboCop::Isucon::GDA::Client]
92
30
  # @param table_name [String]
93
- # @return [Boolean]
94
- def covered_where_column_in_primary_key?(gda:, table_name:)
95
- primary_keys = connection.primary_keys(table_name)
96
- return false if primary_keys.empty?
97
-
98
- where_columns = gda.where_conditions.map(&:column_operand)
99
- primary_keys.all? { |primary_key| where_columns.include?(primary_key) }
31
+ # @param column_name [String]
32
+ # @return [String]
33
+ def generate_offense_message(table_name:, column_name:)
34
+ format(MSG, table_name: table_name, column_name: column_name)
100
35
  end
101
36
  end
102
37
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Isucon
6
+ module Sqlite3
7
+ # Check for `JOIN` without index
8
+ #
9
+ # @note If `Database` isn't configured, this cop's feature (offense detection and auto-correct) will not be available.
10
+ #
11
+ # @example
12
+ # # bad (user_id is not indexed)
13
+ # db.execute('SELECT id, title FROM articles JOIN users ON users.id = articles.user_id')
14
+ #
15
+ # # good (user_id is indexed)
16
+ # db.execute('SELECT id, title FROM articles JOIN users ON users.id = articles.user_id')
17
+ #
18
+ class JoinWithoutIndex < Base
19
+ include Mixin::Sqlite3ExecuteMethods
20
+ include Mixin::JoinWithoutIndexMethods
21
+
22
+ MSG = "This join clause doesn't seem to have an index. " \
23
+ "(e.g. `CREATE INDEX index_%<table_name>s_%<column_name>s ON %<table_name>s (%<column_name>s)`)"
24
+
25
+ private
26
+
27
+ # @param table_name [String]
28
+ # @param column_name [String]
29
+ # @return [String]
30
+ def generate_offense_message(table_name:, column_name:)
31
+ format(MSG, table_name: table_name, column_name: column_name)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Isucon
6
+ module Sqlite3
7
+ # Check if SQL contains many JOINs
8
+ #
9
+ # @example CountTables: 3 (default)
10
+ # # bad
11
+ # totals = db.execute(
12
+ # "SELECT IFNULL(SUM(`submissions`.`score`), 0) AS `total_score`" \
13
+ # " FROM `users`" \
14
+ # " JOIN `registrations` ON `users`.`id` = `registrations`.`user_id`" \
15
+ # " JOIN `courses` ON `registrations`.`course_id` = `courses`.`id`" \
16
+ # " LEFT JOIN `classes` ON `courses`.`id` = `classes`.`course_id`" \
17
+ # " LEFT JOIN `submissions` ON `users`.`id` = `submissions`.`user_id` AND `submissions`.`class_id` = `classes`.`id`" \
18
+ # " WHERE `courses`.`id` = ?" \
19
+ # " GROUP BY `users`.`id`",
20
+ # [course[:id]]
21
+ # ).map { |_| _[:total_score] }
22
+ #
23
+ # # good
24
+ # registration_users_count =
25
+ # db.execute("SELECT COUNT(`user_id`) AS cnt FROM `registrations` WHERE `course_id` = ?", [course[:id]]).first[:cnt]
26
+ #
27
+ # totals = db.execute(<<~SQL, course[:id]).map { |_| _[:total_score] }
28
+ # SELECT IFNULL(SUM(`submissions`.`score`), 0) AS `total_score`
29
+ # FROM `submissions`
30
+ # JOIN `classes` ON `classes`.`id` = `submissions`.`class_id`
31
+ # WHERE `classes`.`course_id` = ?
32
+ # GROUP BY `submissions`.`user_id`
33
+ # SQL
34
+ #
35
+ # if totals.count < registration_users_count
36
+ # no_submissions_count = registration_users_count - totals.count
37
+ # totals += [0] * no_submissions_count
38
+ # end
39
+ #
40
+ # @example CountTables: 5
41
+ # # good
42
+ # totals = db.execute(
43
+ # "SELECT IFNULL(SUM(`submissions`.`score`), 0) AS `total_score`" \
44
+ # " FROM `users`" \
45
+ # " JOIN `registrations` ON `users`.`id` = `registrations`.`user_id`" \
46
+ # " JOIN `courses` ON `registrations`.`course_id` = `courses`.`id`" \
47
+ # " LEFT JOIN `classes` ON `courses`.`id` = `classes`.`course_id`" \
48
+ # " LEFT JOIN `submissions` ON `users`.`id` = `submissions`.`user_id` AND `submissions`.`class_id` = `classes`.`id`" \
49
+ # " WHERE `courses`.`id` = ?" \
50
+ # " GROUP BY `users`.`id`",
51
+ # [course[:id]]
52
+ # ).map { |_| _[:total_score] }
53
+ #
54
+ class ManyJoinTable < Base
55
+ include Mixin::Sqlite3ExecuteMethods
56
+ include Mixin::ManyJoinTableMethods
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Isucon
6
+ module Sqlite3
7
+ # rubocop:disable Layout/LineLength
8
+
9
+ # Checks that N+1 query is not used
10
+ #
11
+ # @note If `Database` isn't configured, auto-correct will not be available. (Only offense detection can be used)
12
+ #
13
+ # @note For the number of N+1 queries that can be detected by this cop, there are too few that can be corrected automatically
14
+ #
15
+ # @example
16
+ # # bad
17
+ # reservations = db.execute('SELECT * FROM `reservations` WHERE `schedule_id` = ?', [schedule_id]).map do |reservation|
18
+ # reservation[:user] = db.execute('SELECT * FROM `users` WHERE `id` = ? LIMIT 1', [id]).first
19
+ # reservation
20
+ # end
21
+ #
22
+ # # good
23
+ # rows = db.xquery(<<~SQL, [schedule_id])
24
+ # SELECT
25
+ # r.id AS reservation_id,
26
+ # r.schedule_id AS reservation_schedule_id,
27
+ # r.user_id AS reservation_user_id,
28
+ # r.created_at AS reservation_created_at,
29
+ # u.id AS user_id,
30
+ # u.email AS user_email,
31
+ # u.nickname AS user_nickname,
32
+ # u.staff AS user_staff,
33
+ # u.created_at AS user_created_at
34
+ # FROM `reservations` AS r
35
+ # INNER JOIN users u ON u.id = r.user_id
36
+ # WHERE r.schedule_id = ?
37
+ # SQL
38
+ #
39
+ # # bad
40
+ # courses.map do |course|
41
+ # teacher = db.execute('SELECT * FROM `users` WHERE `id` = ?', [course[:teacher_id]]).first
42
+ # end
43
+ #
44
+ # # good
45
+ # # This is similar to ActiveRecord's preload
46
+ # # c.f. https://guides.rubyonrails.org/active_record_querying.html#preload
47
+ # courses.map do |course|
48
+ # @users_by_id ||= db.execute('SELECT * FROM `users` WHERE `id` IN (?)', [courses.map { |course| course[:teacher_id] }]).each_with_object({}) { |v, hash| hash[v[:id]] = v }
49
+ # teacher = @users_by_id[course[:teacher_id]]
50
+ # end
51
+ #
52
+ class NPlusOneQuery < Base
53
+ # rubocop:enable Layout/LineLength
54
+
55
+ include Mixin::Sqlite3ExecuteMethods
56
+ include Mixin::NPlusOneQueryMethods
57
+
58
+ extend AutoCorrector
59
+
60
+ private
61
+
62
+ # [Boolean]
63
+ def array_arg?
64
+ true
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Isucon
6
+ module Sqlite3
7
+ # Avoid `SELECT *` in `db.execute`
8
+ #
9
+ # @note If `Database` isn't configured, auto-correct will not be available. (Only offense detection can be used)
10
+ #
11
+ # @note This cop replaces `SELECT *` with a `SELECT` by the columns present in the table (e.g. `SELECT id, name`),
12
+ # but does not check whether the columns are actually used.
13
+ # Please manually delete unused columns after auto corrected
14
+ #
15
+ # @example
16
+ # # bad
17
+ # db.execute('SELECT * FROM users')
18
+ #
19
+ # # bad
20
+ # db.execute('SELECT users.* FROM users')
21
+ #
22
+ # # good
23
+ # db.execute('SELECT id, name FROM users')
24
+ #
25
+ # # good
26
+ # db.execute('SELECT users.id, users.name FROM users')
27
+ #
28
+ class SelectAsterisk < Base
29
+ include Mixin::Sqlite3ExecuteMethods
30
+ include Mixin::SelectAsteriskMethods
31
+
32
+ extend AutoCorrector
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Isucon
6
+ module Sqlite3
7
+ # Check for `WHERE` without index
8
+ #
9
+ # @note If `Database` isn't configured, this cop's feature (offense detection and auto-correct) will not be available.
10
+ #
11
+ # @example
12
+ # # bad (user_id is not indexed)
13
+ # db.execute('SELECT id, title FROM articles WHERE used_id = ?', [user_id])
14
+ #
15
+ # # good (user_id is indexed)
16
+ # db.execute('SELECT id, title FROM articles WHERE used_id = ?', [user_id])
17
+ #
18
+ # # good (id is primary key)
19
+ # db.execute('SELECT id, title FROM articles WHERE id = ?', [id])
20
+ #
21
+ class WhereWithoutIndex < Base
22
+ include Mixin::Sqlite3ExecuteMethods
23
+ include Mixin::WhereWithoutIndexMethods
24
+
25
+ MSG = "This where clause doesn't seem to have an index. " \
26
+ "(e.g. `CREATE INDEX index_%<table_name>s_%<column_name>s ON %<table_name>s (%<column_name>s)`)"
27
+
28
+ private
29
+
30
+ # @param table_name [String]
31
+ # @param column_name [String]
32
+ # @return [String]
33
+ def generate_offense_message(table_name:, column_name:)
34
+ format(MSG, table_name: table_name, column_name: column_name)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,10 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "isucon/mixin/database_methods"
4
+ require_relative "isucon/mixin/join_without_index_methods"
5
+ require_relative "isucon/mixin/offense_location_methods"
6
+ require_relative "isucon/mixin/many_join_table_methods"
4
7
  require_relative "isucon/mixin/mysql2_xquery_methods"
8
+ require_relative "isucon/mixin/n_plus_one_query_methods"
9
+ require_relative "isucon/mixin/select_asterisk_methods"
5
10
  require_relative "isucon/mixin/sinatra_methods"
11
+ require_relative "isucon/mixin/sqlite3_execute_methods"
12
+ require_relative "isucon/mixin/where_without_index_methods"
6
13
 
7
- require_relative "isucon/correctors/mysql2_n_plus_one_query_corrector"
14
+ require_relative "isucon/correctors/n_plus_one_query_corrector"
8
15
 
9
16
  require_relative "isucon/mysql2/join_without_index"
10
17
  require_relative "isucon/mysql2/many_join_table"
@@ -18,3 +25,8 @@ require_relative "isucon/sinatra/rack_logger"
18
25
  require_relative "isucon/sinatra/serve_static_file"
19
26
  require_relative "isucon/shell/backtick"
20
27
  require_relative "isucon/shell/system"
28
+ require_relative "isucon/sqlite3/join_without_index"
29
+ require_relative "isucon/sqlite3/many_join_table"
30
+ require_relative "isucon/sqlite3/n_plus_one_query"
31
+ require_relative "isucon/sqlite3/select_asterisk"
32
+ require_relative "isucon/sqlite3/where_without_index"
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Isucon
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-isucon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - sue445
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-07-23 00:00:00.000000000 Z
11
+ date: 2022-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -221,12 +221,19 @@ files:
221
221
  - gemfiles/activerecord_6_1.gemfile
222
222
  - gemfiles/activerecord_7_0.gemfile
223
223
  - lib/rubocop-isucon.rb
224
- - lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector.rb
225
- - lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector/correctable_methods.rb
226
- - lib/rubocop/cop/isucon/correctors/mysql2_n_plus_one_query_corrector/replace_methods.rb
224
+ - lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector.rb
225
+ - lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/correctable_methods.rb
226
+ - lib/rubocop/cop/isucon/correctors/n_plus_one_query_corrector/replace_methods.rb
227
227
  - lib/rubocop/cop/isucon/mixin/database_methods.rb
228
+ - lib/rubocop/cop/isucon/mixin/join_without_index_methods.rb
229
+ - lib/rubocop/cop/isucon/mixin/many_join_table_methods.rb
228
230
  - lib/rubocop/cop/isucon/mixin/mysql2_xquery_methods.rb
231
+ - lib/rubocop/cop/isucon/mixin/n_plus_one_query_methods.rb
232
+ - lib/rubocop/cop/isucon/mixin/offense_location_methods.rb
233
+ - lib/rubocop/cop/isucon/mixin/select_asterisk_methods.rb
229
234
  - lib/rubocop/cop/isucon/mixin/sinatra_methods.rb
235
+ - lib/rubocop/cop/isucon/mixin/sqlite3_execute_methods.rb
236
+ - lib/rubocop/cop/isucon/mixin/where_without_index_methods.rb
230
237
  - lib/rubocop/cop/isucon/mysql2/join_without_index.rb
231
238
  - lib/rubocop/cop/isucon/mysql2/many_join_table.rb
232
239
  - lib/rubocop/cop/isucon/mysql2/n_plus_one_query.rb
@@ -239,6 +246,11 @@ files:
239
246
  - lib/rubocop/cop/isucon/sinatra/logger.rb
240
247
  - lib/rubocop/cop/isucon/sinatra/rack_logger.rb
241
248
  - lib/rubocop/cop/isucon/sinatra/serve_static_file.rb
249
+ - lib/rubocop/cop/isucon/sqlite3/join_without_index.rb
250
+ - lib/rubocop/cop/isucon/sqlite3/many_join_table.rb
251
+ - lib/rubocop/cop/isucon/sqlite3/n_plus_one_query.rb
252
+ - lib/rubocop/cop/isucon/sqlite3/select_asterisk.rb
253
+ - lib/rubocop/cop/isucon/sqlite3/where_without_index.rb
242
254
  - lib/rubocop/cop/isucon_cops.rb
243
255
  - lib/rubocop/isucon.rb
244
256
  - lib/rubocop/isucon/database_connection.rb