active_record-sql_analyzer 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.