active_record-sql_analyzer 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 77bac67b331f9db59ff2dbc8f57ec2bca04505c0
4
- data.tar.gz: 870a62bd36006b4098d8e88a3a3af1cd3fa310b2
2
+ SHA256:
3
+ metadata.gz: 5d31d0e5425cf5bcfa0c272cda57fc401b65c76d2f995a76a86cbde30a7abd90
4
+ data.tar.gz: efc79b2899368bb3ab2aea189dd10b0ac3b183c93b2047ceef558bab8090e5d8
5
5
  SHA512:
6
- metadata.gz: 88f4ef3bbe57d34b89194e045e2845f4fa4bd9d5800e47943b8e9b2561fc2cb9025a870d976fc36e29c43564bc98c3c3baa7497ea6bd5ae2e894b505205ab3bd
7
- data.tar.gz: 7042bf53a81900be2c19a3bfa4e5564d2c2ca0d9fa23d3af22838e6322dd584e460db80236a5f35f1a755efba7a72781516d7c33ac9c7c3c24d2660518126cd8
6
+ metadata.gz: b0c27e575f069fc946d9b553f41c519d85017e7315d71f8b8b3b563720cd9ae2d7a9824edcfe0950dee8c4a5dc23b48bbdd993da3dfd03d4fe5ceeef86d9518f
7
+ data.tar.gz: 80cdcea62380e9f5397b98968a6f32cac07bf6f9872e72af7a99d82089af7fc1f3a7d3d054e94f6c61b0768c74f68b2757bdee90a6cc7ab794502438447e8bd0
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 / 2018-08-31
4
+ Breaking Changes
5
+ - Change hash method: use `json.hash` instead of `MD5.hexdigest(json)`.
6
+
7
+ Internal:
8
+ - Use `print` instead of `puts`.
data/Gemfile CHANGED
@@ -4,9 +4,9 @@ gem "pry-byebug"
4
4
  gem "rake"
5
5
 
6
6
  group :test do
7
- gem "mysql2", "~> 0.4", ">= 0.4.0"
7
+ gem "mysql2"
8
8
  gem "rspec", "~> 3.4"
9
- gem "rubocop", "~> 0.30"
9
+ gem "rubocop", "0.58.2"
10
10
  gem "sql-parser", git: "https://github.com/nerdrew/sql-parser.git"
11
11
  gem "timecop", "~> 0.8"
12
12
  end
@@ -1,4 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
+
2
3
  require File.expand_path('../lib/active_record/sql_analyzer/version', __FILE__)
3
4
  Gem::Specification.new do |s|
4
5
  s.authors = ['Zachary Anker', 'Gabriel Gilder']
@@ -15,7 +16,7 @@ Gem::Specification.new do |s|
15
16
  s.require_paths = ['lib']
16
17
  s.version = ActiveRecord::SqlAnalyzer::VERSION
17
18
 
18
- s.add_dependency 'activerecord', '~> 4.0', '>= 4.0.0'
19
+ s.add_dependency 'activerecord'
19
20
 
20
21
  s.add_development_dependency 'bundler', '~> 1.0'
21
22
  end
@@ -14,7 +14,7 @@ module ActiveRecord
14
14
  end
15
15
 
16
16
  def self.rails_root_regex
17
- @rails_root ||= %r{^#{Regexp.escape(Rails.root.to_s)}}
17
+ @rails_root_regex ||= %r{^#{Regexp.escape(Rails.root.to_s)}}
18
18
  end
19
19
 
20
20
  def self.proc
@@ -1,7 +1,7 @@
1
1
  module ActiveRecord
2
2
  module SqlAnalyzer
3
3
  class CLI
4
- attr_reader :options, :processor
4
+ attr_reader :options
5
5
 
6
6
  def initialize
7
7
  @options = {
@@ -23,7 +23,6 @@ module ActiveRecord
23
23
  end
24
24
 
25
25
  local_data
26
-
27
26
  rescue => ex
28
27
  puts "#{ex.class}: #{ex.message}"
29
28
  puts ex.backtrace
@@ -73,7 +72,7 @@ module ActiveRecord
73
72
  last_called, sha = line.split("|", 2)
74
73
  last_called = Time.at(last_called.to_i).utc
75
74
 
76
- local_usage[sha] ||= {"count" => 0}
75
+ local_usage[sha] ||= { "count" => 0 }
77
76
  local_usage[sha]["count"] += 1
78
77
 
79
78
  if !local_usage[sha]["last_called"] || local_usage[sha]["last_called"] < last_called
@@ -13,13 +13,14 @@ module ActiveRecord
13
13
  end
14
14
 
15
15
  def log(event)
16
- sha = Digest::MD5.hexdigest(event.to_s)
16
+ json = event.to_json
17
+ sha = json.hash
17
18
  unless logged_shas.include?(sha)
18
- definition_log_file.puts("#{sha}|#{event.to_json}")
19
+ definition_log_file.print("#{sha}|#{json}\n")
19
20
  logged_shas << sha
20
21
  end
21
22
 
22
- log_file.puts("#{Time.now.to_i}|#{sha}")
23
+ log_file.print("#{Time.now.to_i}|#{sha}\n")
23
24
  end
24
25
 
25
26
  def close
@@ -152,16 +152,16 @@ module ActiveRecord
152
152
  end
153
153
 
154
154
  def setup_defaults
155
- quotedValuePattern = %{('([^\\\\']|\\\\.|'')*'|"([^\\\\"]|\\\\.|"")*")}
155
+ quoted_value_pattern = %{('([^\\\\']|\\\\.|'')*'|"([^\\\\"]|\\\\.|"")*")}
156
156
  @options[:sql_redactors] = [
157
157
  Redactor.new(/\n/, " "),
158
158
  Redactor.new(/\s+/, " "),
159
159
  Redactor.new(/IN \([^)]+\)/i, "IN ('[REDACTED]')"),
160
160
  Redactor.new(/(\s|\b|`)(=|!=|>=|>|<=|<) ?(BINARY )?-?\d+(\.\d+)?/i, " = '[REDACTED]'"),
161
- Redactor.new(/(\s|\b|`)(=|!=|>=|>|<=|<) ?(BINARY )?x?#{quotedValuePattern}/i, " = '[REDACTED]'"),
161
+ Redactor.new(/(\s|\b|`)(=|!=|>=|>|<=|<) ?(BINARY )?x?#{quoted_value_pattern}/i, " = '[REDACTED]'"),
162
162
  Redactor.new(/VALUES \(.+\)$/i, "VALUES ('[REDACTED]')"),
163
- Redactor.new(/BETWEEN #{quotedValuePattern} AND #{quotedValuePattern}/i, "BETWEEN '[REDACTED]' AND '[REDACTED]'"),
164
- Redactor.new(/LIKE #{quotedValuePattern}/i, "LIKE '[REDACTED]'"),
163
+ Redactor.new(/BETWEEN #{quoted_value_pattern} AND #{quoted_value_pattern}/i, "BETWEEN '[REDACTED]' AND '[REDACTED]'"),
164
+ Redactor.new(/LIKE #{quoted_value_pattern}/i, "LIKE '[REDACTED]'"),
165
165
  Redactor.new(/ LIMIT \d+/i, ""),
166
166
  Redactor.new(/ OFFSET \d+/i, ""),
167
167
  Redactor.new(/INSERT INTO (`?\w+`?) \([^)]+\)/i, "INSERT INTO \\1 (REDACTED_COLUMNS)"),
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
 
32
32
  if safe_sql =~ analyzer[:table_regex]
33
33
  SqlAnalyzer.background_processor <<
34
- _query_analyzer_private_query_stanza([query_analyzer_call], analyzer)
34
+ _query_analyzer_private_query_stanza([query_analyzer_call], analyzer)
35
35
  end
36
36
  end
37
37
  end
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  def filter_event(event)
5
5
  # Determine if we're doing extended tracing or only the first
6
6
  calls = event.delete(:calls).map do |call|
7
- {sql: filter_sql(call[:sql]), caller: filter_caller(call[:caller])}
7
+ { sql: filter_sql(call[:sql]), caller: filter_caller(call[:caller]) }
8
8
  end
9
9
 
10
10
  # De-duplicate redacted calls to avoid many transactions with looping "N+1" queries.
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module SqlAnalyzer
3
- VERSION = '0.2.3'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
@@ -3,7 +3,7 @@ RSpec.describe ActiveRecord::SqlAnalyzer::BackgroundProcessor do
3
3
 
4
4
  let(:instance) { described_class.new }
5
5
 
6
- let(:event) { {calls: [{caller: "CALLER", sql: "SQL"}], logger: logger} }
6
+ let(:event) { { calls: [{ caller: "CALLER", sql: "SQL" }], logger: logger } }
7
7
 
8
8
  let(:logger) do
9
9
  Class.new do
@@ -23,8 +23,8 @@ RSpec.describe ActiveRecord::SqlAnalyzer::BackgroundProcessor do
23
23
 
24
24
  before do
25
25
  ActiveRecord::SqlAnalyzer.configure do |c|
26
- c.backtrace_filter_proc Proc.new { |lines| "BFP #{lines}" }
27
- c.complex_sql_redactor_proc Proc.new { |sql| "CSRP #{sql}" }
26
+ c.backtrace_filter_proc(Proc.new { |lines| "BFP #{lines}" })
27
+ c.complex_sql_redactor_proc(Proc.new { |sql| "CSRP #{sql}" })
28
28
  end
29
29
  end
30
30
 
@@ -29,20 +29,18 @@ RSpec.describe ActiveRecord::SqlAnalyzer::CLIProcessor do
29
29
 
30
30
  before do
31
31
  write_logs(:foo,
32
- {sql: "F-SQL1", caller: "CALLER1", tag: true},
33
- {sql: "F-SQL2", caller: "CALLER2", tag: true},
34
- {sql: "F-SQL1", caller: "CALLER1", tag: true},
35
- {sql: "F-SQL3", caller: "CALLER3", tag: true},
36
- {sql: "F-SQL2", caller: "CALLER2", tag: true}
37
- )
32
+ { sql: "F-SQL1", caller: "CALLER1", tag: true },
33
+ { sql: "F-SQL2", caller: "CALLER2", tag: true },
34
+ { sql: "F-SQL1", caller: "CALLER1", tag: true },
35
+ { sql: "F-SQL3", caller: "CALLER3", tag: true },
36
+ { sql: "F-SQL2", caller: "CALLER2", tag: true })
38
37
 
39
38
  write_logs(:bar,
40
- {sql: "B-SQL1", caller: "CALLER1"},
41
- {sql: "B-SQL2", caller: "CALLER2"},
42
- {sql: "B-SQL3", caller: "CALLER3"},
43
- {sql: "B-SQL2", caller: "CALLER2"},
44
- {sql: "B-SQL1", caller: "CALLER1"}
45
- )
39
+ { sql: "B-SQL1", caller: "CALLER1" },
40
+ { sql: "B-SQL2", caller: "CALLER2" },
41
+ { sql: "B-SQL3", caller: "CALLER3" },
42
+ { sql: "B-SQL2", caller: "CALLER2" },
43
+ { sql: "B-SQL1", caller: "CALLER1" })
46
44
  end
47
45
 
48
46
  subject(:process) do
@@ -36,7 +36,7 @@ RSpec.describe "End to End" do
36
36
  before do
37
37
  ActiveRecord::SqlAnalyzer.configure do |c|
38
38
  c.logger_root_path tmp_dir
39
- c.log_sample_proc Proc.new { |_name| true }
39
+ c.log_sample_proc(Proc.new { |_name| true })
40
40
 
41
41
  c.add_analyzer(
42
42
  name: :test_tag,
@@ -129,13 +129,15 @@ RSpec.describe "End to End" do
129
129
  "BEGIN; " \
130
130
  "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
131
131
  "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
132
- "COMMIT;")
132
+ "COMMIT;"
133
+ )
133
134
 
134
135
  expect(log_def_hash[transaction_executed_twice_sha]["sql"]).to eq(
135
- "BEGIN; " \
136
- "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
137
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
138
- "COMMIT;")
136
+ "BEGIN; " \
137
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
138
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
139
+ "COMMIT;"
140
+ )
139
141
  end
140
142
 
141
143
  it "Logs nested transactions correctly" do
@@ -148,10 +150,11 @@ RSpec.describe "End to End" do
148
150
 
149
151
  transaction_executed_once_sha = log_reverse_hash[1]
150
152
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
151
- "BEGIN; " \
152
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
153
- "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
154
- "COMMIT;")
153
+ "BEGIN; " \
154
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
155
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
156
+ "COMMIT;"
157
+ )
155
158
  end
156
159
 
157
160
  it "Logs transactions with inserts correctly" do
@@ -162,10 +165,11 @@ RSpec.describe "End to End" do
162
165
 
163
166
  transaction_executed_once_sha = log_reverse_hash[1]
164
167
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
165
- "BEGIN; " \
166
- "INSERT INTO matching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
167
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
168
- "COMMIT;")
168
+ "BEGIN; " \
169
+ "INSERT INTO matching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
170
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
171
+ "COMMIT;"
172
+ )
169
173
  end
170
174
 
171
175
  it "Logs mixed matching-nonmatching selects correctly" do
@@ -179,7 +183,8 @@ RSpec.describe "End to End" do
179
183
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
180
184
  "BEGIN; " \
181
185
  "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
182
- "COMMIT;")
186
+ "COMMIT;"
187
+ )
183
188
  end
184
189
 
185
190
  it "Logs transaction with repeated selects correctly" do
@@ -193,10 +198,11 @@ RSpec.describe "End to End" do
193
198
  transaction_executed_once_sha = log_reverse_hash[1]
194
199
 
195
200
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
196
- "BEGIN; " \
201
+ "BEGIN; " \
197
202
  "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
198
203
  "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
199
- "COMMIT;")
204
+ "COMMIT;"
205
+ )
200
206
  end
201
207
 
202
208
  it "Logs transaction with repeated inserts correctly" do
@@ -210,10 +216,11 @@ RSpec.describe "End to End" do
210
216
  transaction_executed_once_sha = log_reverse_hash[1]
211
217
 
212
218
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
213
- "BEGIN; " \
219
+ "BEGIN; " \
214
220
  "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
215
221
  "INSERT INTO matching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
216
- "COMMIT;")
222
+ "COMMIT;"
223
+ )
217
224
  end
218
225
 
219
226
  context "ActiveRecord generated transactions" do
@@ -240,10 +247,10 @@ RSpec.describe "End to End" do
240
247
  NonMatching.create
241
248
 
242
249
  expect(log_def_hash.map { |_hash, data| data["sql"] }).to match([
243
- "INSERT INTO `matching_table` VALUES ()",
244
- "BEGIN; INSERT INTO `matching_table` VALUES (); INSERT INTO `nonmatching_table` VALUES (); COMMIT;",
245
- "SELECT `matching_table`.* FROM `matching_table` ORDER BY `matching_table`.`id` DESC"
246
- ])
250
+ "INSERT INTO `matching_table` VALUES ()",
251
+ "BEGIN; INSERT INTO `matching_table` VALUES (); INSERT INTO `nonmatching_table` VALUES (); COMMIT;",
252
+ "SELECT `matching_table`.* FROM `matching_table` ORDER BY `matching_table`.`id` DESC"
253
+ ])
247
254
  end
248
255
  end
249
256
 
@@ -256,10 +263,11 @@ RSpec.describe "End to End" do
256
263
  transaction_executed_once_sha = log_reverse_hash[1]
257
264
 
258
265
  expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
259
- "BEGIN; " \
266
+ "BEGIN; " \
260
267
  "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
261
268
  "INSERT INTO nonmatching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
262
- "COMMIT;")
269
+ "COMMIT;"
270
+ )
263
271
  end
264
272
 
265
273
  it "Does not log nonmatching-only queries" do
@@ -284,7 +292,7 @@ RSpec.describe "End to End" do
284
292
  ActiveRecord::SqlAnalyzer.configure do |c|
285
293
  times_called = 0
286
294
  # Return true every other call, starting with the first call
287
- c.log_sample_proc Proc.new { |_name| (times_called += 1) % 2 == 1 }
295
+ c.log_sample_proc(Proc.new { |_name| (times_called += 1) % 2 == 1 })
288
296
  end
289
297
  end
290
298
 
@@ -294,7 +302,8 @@ RSpec.describe "End to End" do
294
302
 
295
303
  expect(log_def_hash.size).to eq(1)
296
304
  expect(log_def_hash.map { |_hash, query| query['sql'] }).to eq([
297
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'"])
305
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'"
306
+ ])
298
307
  end
299
308
 
300
309
  it "Samples some but not other whole transactions" do
@@ -309,20 +318,20 @@ RSpec.describe "End to End" do
309
318
  end
310
319
 
311
320
  expect(log_def_hash.map { |_hash, data| data["sql"] }).to match([
312
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'",
313
- "BEGIN; "\
314
- "SELECT * FROM matching_table WHERE id = '[REDACTED]'; "\
315
- "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; "\
316
- "COMMIT;",
317
- "SELECT * FROM matching_table WHERE id = '[REDACTED]' and id = '[REDACTED]'"
318
- ])
321
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'",
322
+ "BEGIN; "\
323
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; "\
324
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; "\
325
+ "COMMIT;",
326
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]' and id = '[REDACTED]'"
327
+ ])
319
328
  end
320
329
  end
321
330
 
322
331
  context "when sampling is disabled" do
323
332
  before do
324
333
  ActiveRecord::SqlAnalyzer.configure do |c|
325
- c.log_sample_proc Proc.new { |_name| false }
334
+ c.log_sample_proc(Proc.new { |_name| false })
326
335
  end
327
336
  end
328
337
 
@@ -267,9 +267,9 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
267
267
  let(:event) do
268
268
  {
269
269
  calls: [{
270
- caller: [],
271
- sql: "INSERT INTO `boom` (`bam`, `foo`) VALUES ('howdy', 'dowdy')",
272
- }]
270
+ caller: [],
271
+ sql: "INSERT INTO `boom` (`bam`, `foo`) VALUES ('howdy', 'dowdy')",
272
+ }]
273
273
  }
274
274
  end
275
275
 
@@ -19,7 +19,6 @@ class DBConnection
19
19
  end
20
20
 
21
21
  def self.setup_db
22
- ActiveRecord::Base.raise_in_transactional_callbacks = true
23
22
  conn = ActiveRecord::Base.establish_connection(configuration)
24
23
  conn.connection.execute <<-SQL
25
24
  CREATE DATABASE IF NOT EXISTS ar_sql_analyzer_test
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_record-sql_analyzer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zachary Anker
@@ -9,28 +9,22 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-12-16 00:00:00.000000000 Z
12
+ date: 2018-08-31 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
19
- - !ruby/object:Gem::Version
20
- version: '4.0'
21
18
  - - ">="
22
19
  - !ruby/object:Gem::Version
23
- version: 4.0.0
20
+ version: '0'
24
21
  type: :runtime
25
22
  prerelease: false
26
23
  version_requirements: !ruby/object:Gem::Requirement
27
24
  requirements:
28
- - - "~>"
29
- - !ruby/object:Gem::Version
30
- version: '4.0'
31
25
  - - ">="
32
26
  - !ruby/object:Gem::Version
33
- version: 4.0.0
27
+ version: '0'
34
28
  - !ruby/object:Gem::Dependency
35
29
  name: bundler
36
30
  requirement: !ruby/object:Gem::Requirement
@@ -58,6 +52,7 @@ files:
58
52
  - ".rspec"
59
53
  - ".rubocop.yml"
60
54
  - ".travis.yml"
55
+ - CHANGES.md
61
56
  - CONTRIBUTING.md
62
57
  - Gemfile
63
58
  - LICENSE.md
@@ -111,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
106
  version: '0'
112
107
  requirements: []
113
108
  rubyforge_project:
114
- rubygems_version: 2.5.1
109
+ rubygems_version: 2.7.6
115
110
  signing_key:
116
111
  specification_version: 4
117
112
  summary: Logs a subset of ActiveRecord queries and dumps them for analyses.