active_record-sql_analyzer 0.1.0 → 0.2.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
2
  SHA1:
3
- metadata.gz: 48238f31887e49755dbac5b74348f9f580dc0b65
4
- data.tar.gz: 7c875b3aa9dfdb7e4449f12893ff1ddf2f7effff
3
+ metadata.gz: 7987c2a8d140ea3c6dc2b13effcad2f810562171
4
+ data.tar.gz: b62f71e7563c8e92d11ee5baac0ad8722d669780
5
5
  SHA512:
6
- metadata.gz: ceaaefc3ea471c547e81c4ce227a7fb1b1b13e6d61ea5caffd6d1391b1802fe35af2f5948d7624f1c376e75bfc0c4a89b8be6efb61667ddb5e1faac0bc34e33b
7
- data.tar.gz: a89a8a5ae0d6e38c08123a64b0e1a2ec63b8fcfd8641e5eaff55507c122717177c97fc84ab50f605e08ee3a0954ef98a711abc8fa6188253b76ae2ff359cd05c
6
+ metadata.gz: df3a2c7ea88fbc0b86cc9fdcc9fe94c52c9727ff436ade3f6f06869932f842c24101c33cbefe6bae77e381275572dcb6d4e724741c3bb18cd1ac5888d99da971
7
+ data.tar.gz: 158608b3e248858f41a0084a6787432932e4860f41d48f876fd6cbbf58c9cad8a2eb7bbbb877eca8092fe50af1c7cc3d5aaf38518771111c6183de0b741b920b
@@ -19,8 +19,12 @@ module ActiveRecord
19
19
  def process_queue
20
20
  event = @queue.pop
21
21
 
22
- event[:caller] = SqlAnalyzer.config[:backtrace_filter_proc].call(event[:caller])
23
- event[:sql] = SqlAnalyzer.config[:sql_redactor_complex_proc].call(event[:sql].dup)
22
+ event[:calls] = event[:calls].map do |call|
23
+ {
24
+ caller: SqlAnalyzer.config[:backtrace_filter_proc].call(call[:caller]),
25
+ sql: SqlAnalyzer.config[:sql_redactor_complex_proc].call(call[:sql].dup)
26
+ }
27
+ end
24
28
 
25
29
  logger = event.delete(:logger)
26
30
  logger.filter_event(event)
@@ -117,6 +117,12 @@ module ActiveRecord
117
117
  @options[:ambiguous_backtrace_lines] = lines
118
118
  end
119
119
 
120
+ # Disable transaction consolidation. With transaction consolidation enabled, the logger will log full transactions
121
+ # as single statements.
122
+ def disable_consolidate_transactions
123
+ @config[:consolidate_transactions] = false
124
+ end
125
+
120
126
  def [](key)
121
127
  @options[key]
122
128
  end
@@ -169,6 +175,7 @@ module ActiveRecord
169
175
  @options[:analyzers] = []
170
176
  @options[:logger_root_path] = Rails.root.join('log')
171
177
  @options[:backtrace_filter_proc] = BacktraceFilter.proc
178
+ @options[:consolidate_transactions] = true
172
179
  end
173
180
  end
174
181
  end
@@ -4,27 +4,27 @@ module ActiveRecord
4
4
  module SqlAnalyzer
5
5
  module Monkeypatches
6
6
  module Query
7
+ QueryAnalyzerCall = Struct.new(:sql, :caller)
8
+
7
9
  def execute(sql, *args)
8
10
  return super unless SqlAnalyzer.config
9
11
 
10
- safe_sql = nil
12
+ if @_query_analyzer_private_transaction_queue
13
+ @_query_analyzer_private_transaction_queue << QueryAnalyzerCall.new(sql, caller)
14
+ else
15
+ safe_sql = nil
16
+
17
+ SqlAnalyzer.config[:analyzers].each do |analyzer|
18
+ if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
19
+ # This is here rather than above intentionally.
20
+ # We assume we're not going to be analyzing 100% of queries and want to only re-encode
21
+ # when it's actually relevant.
22
+ safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
11
23
 
12
- SqlAnalyzer.config[:analyzers].each do |analyzer|
13
- if _query_analyzer_private_should_sample_query(analyzer[:name])
14
- # This is here rather than above intentionally.
15
- # We assume we're not going to be analyzing 100% of queries and want to only re-encode
16
- # when it's actually relevant.
17
- safe_sql ||= sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace)
18
-
19
- if safe_sql =~ analyzer[:table_regex]
20
- SqlAnalyzer.background_processor << {
21
- sql: safe_sql,
22
- caller: caller,
23
- logger: analyzer[:logger_instance],
24
- tag: Thread.current[:_ar_analyzer_tag],
25
- request_path: Thread.current[:_ar_analyzer_request_path],
26
- transaction: @_query_analyzer_private_transaction_uuid
27
- }
24
+ if safe_sql =~ analyzer[:table_regex]
25
+ SqlAnalyzer.background_processor <<
26
+ _query_analyzer_private_query_stanza([QueryAnalyzerCall.new(safe_sql, caller)], analyzer)
27
+ end
28
28
  end
29
29
  end
30
30
  end
@@ -32,39 +32,67 @@ module ActiveRecord
32
32
  super
33
33
  end
34
34
 
35
- # Whether or not we should sample the query. If it's part of a transaction, check whether we
36
- # are sampling the transaction. Otherwise, run the sample proc.
37
- def _query_analyzer_private_should_sample_query(analyzer_name)
38
- if @_query_analyzer_private_transaction_uuid.present?
39
- @_query_analyzer_private_should_sample_transaction[analyzer_name]
40
- else
41
- SqlAnalyzer.config[:should_log_sample_proc].call(analyzer_name)
35
+ # Drain the transaction query queue. Log the current transaction out to any logger that samples it.
36
+ def _query_analyzer_private_drain_transaction_queue
37
+ reencoded_calls = nil
38
+
39
+ SqlAnalyzer.config[:analyzers].each do |analyzer|
40
+ if SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])
41
+ # Again, trying to only re-encode and join strings if the transaction is actually
42
+ # sampled.
43
+ reencoded_calls ||= @_query_analyzer_private_transaction_queue.map do |call|
44
+ QueryAnalyzerCall.new(call.sql.encode(Encoding::UTF_8, invalid: :replace, undef: :replace), call.caller)
45
+ end
46
+
47
+ matching_calls = reencoded_calls.select do |call|
48
+ (call.sql =~ /^(UPDATE|INSERT|DELETE)/) || (call.sql =~ analyzer[:table_regex])
49
+ end
50
+
51
+ if matching_calls.any?
52
+ # Add back in the begin and commit statements, using the correct call stack references
53
+ if reencoded_calls.first.sql == 'BEGIN'
54
+ matching_calls.unshift(reencoded_calls.first)
55
+ end
56
+
57
+ if ['COMMIT', 'ROLLBACK'].include? reencoded_calls.last.sql
58
+ matching_calls << reencoded_calls.last
59
+ end
60
+
61
+ SqlAnalyzer.background_processor <<
62
+ _query_analyzer_private_query_stanza(matching_calls, analyzer)
63
+ end
64
+ end
42
65
  end
43
66
  end
44
67
 
45
- # Calculate hash of analyzer_name -> boolean representing whether we should sample the
46
- # whole transaction for this analyzer.
47
- def _query_analyzer_private_calculate_sampling_for_all_analyzers
48
- SqlAnalyzer.config[:analyzers].map do |analyzer|
49
- [analyzer[:name], SqlAnalyzer.config[:should_log_sample_proc].call(analyzer[:name])]
50
- end.to_h
68
+ # Helper method to construct the event for a query or transaction.
69
+ # safe_sql [string]: SQL statement or combined SQL statement for transaction
70
+ # calls: A list of QueryAnalyzerCall objects to be turned into call hashes
71
+ def _query_analyzer_private_query_stanza(calls, analyzer)
72
+ {
73
+ # Calls are of the format {sql: String, caller: String}
74
+ calls: calls.map(&:to_h),
75
+ logger: analyzer[:logger_instance],
76
+ tag: Thread.current[:_ar_analyzer_tag],
77
+ request_path: Thread.current[:_ar_analyzer_request_path],
78
+ }
51
79
  end
52
80
 
53
81
  def transaction(requires_new: nil, isolation: nil, joinable: true)
54
- must_clear_uuid = false
82
+ must_clear_transaction = false
55
83
 
56
- if @_query_analyzer_private_transaction_uuid.nil? then
57
- must_clear_uuid = true
58
- @_query_analyzer_private_transaction_uuid = SecureRandom.uuid
59
- @_query_analyzer_private_should_sample_transaction =
60
- _query_analyzer_private_calculate_sampling_for_all_analyzers
84
+ if SqlAnalyzer.config[:consolidate_transactions]
85
+ if @_query_analyzer_private_transaction_queue.nil?
86
+ must_clear_transaction = true
87
+ @_query_analyzer_private_transaction_queue = []
88
+ end
61
89
  end
62
90
 
63
91
  super
64
92
  ensure
65
- if must_clear_uuid
66
- @_query_analyzer_private_transaction_uuid = nil
67
- @_query_analyzer_private_should_sample_transaction = nil
93
+ if must_clear_transaction
94
+ _query_analyzer_private_drain_transaction_queue
95
+ @_query_analyzer_private_transaction_queue = nil
68
96
  end
69
97
  end
70
98
  end
@@ -3,19 +3,43 @@ module ActiveRecord
3
3
  class RedactedLogger < CompactLogger
4
4
  def filter_event(event)
5
5
  # Determine if we're doing extended tracing or only the first
6
- if config[:ambiguous_tracers].any? { |regex| event[:caller].first =~ regex }
7
- event[:caller] = event[:caller][0, config[:ambiguous_backtrace_lines]].join(", ")
6
+ calls = event.delete(:calls).map do |call|
7
+ {sql: filter_sql(call[:sql]), caller: filter_caller(call[:caller])}
8
+ end
9
+
10
+ # De-duplicate redacted calls to avoid many transactions with looping "N+1" queries.
11
+ calls.uniq!
12
+
13
+ event[:sql] = calls.map { |call| call[:sql] }
14
+ event[:caller] = calls.map { |call| call[:caller] }.join(';; ')
15
+
16
+ if event[:sql].size == 1
17
+ event[:sql] = event[:sql].first
8
18
  else
9
- event[:caller] = event[:caller].first
19
+ event[:sql] = event[:sql].join('; ') + ';'
20
+ end
21
+ end
22
+
23
+ def filter_caller(kaller)
24
+ kaller = if config[:ambiguous_tracers].any? { |regex| kaller.first =~ regex }
25
+ kaller[0, config[:ambiguous_backtrace_lines]].join(", ")
26
+ else
27
+ kaller.first
10
28
  end
11
29
 
12
30
  config[:backtrace_redactors].each do |redactor|
13
- event[:caller].gsub!(redactor.search, redactor.replace) if event[:caller]
31
+ kaller.gsub!(redactor.search, redactor.replace)
14
32
  end
15
33
 
34
+ kaller
35
+ end
36
+
37
+ def filter_sql(sql)
16
38
  config[:sql_redactors].each do |redactor|
17
- event[:sql].gsub!(redactor.search, redactor.replace) if event[:sql]
39
+ sql.gsub!(redactor.search, redactor.replace)
18
40
  end
41
+
42
+ sql
19
43
  end
20
44
  end
21
45
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module SqlAnalyzer
3
- VERSION = '0.1.0'
3
+ VERSION = '0.2.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) { {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
@@ -34,10 +34,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::BackgroundProcessor do
34
34
 
35
35
  expect(logger.events).to eq(
36
36
  [
37
- {
37
+ calls: [{
38
38
  caller: "BFP CALLER",
39
39
  sql: "CSRP SQL"
40
- }
40
+ }]
41
41
  ]
42
42
  )
43
43
  end
@@ -55,6 +55,11 @@ RSpec.describe "End to End" do
55
55
  wait_for_pop
56
56
  end
57
57
 
58
+ def transaction
59
+ DBConnection.connection.transaction { yield }
60
+ wait_for_pop
61
+ end
62
+
58
63
  it "does not log with a non-matching table" do
59
64
  execute "SELECT * FROM nonmatching_table"
60
65
 
@@ -105,63 +110,135 @@ RSpec.describe "End to End" do
105
110
  end
106
111
 
107
112
  it "logs multiple queries in a transaction correctly" do
108
- DBConnection.connection.transaction do
109
- 4.times { execute "SELECT * FROM matching_table WHERE id = 4321" }
110
- 3.times { execute "SELECT * FROM matching_table WHERE test_string = 'abc'" }
113
+ transaction do
114
+ execute "SELECT * FROM matching_table WHERE id = 4321"
115
+ execute "SELECT * FROM matching_table WHERE test_string = 'abc'"
111
116
  end
112
117
 
113
- DBConnection.connection.transaction do
114
- 2.times { execute "SELECT * FROM matching_table WHERE test_string LIKE 'abc'" }
115
- execute "SELECT * FROM matching_table WHERE id > 2 and id < 4"
118
+ 2.times do
119
+ transaction do
120
+ execute "SELECT * FROM matching_table WHERE test_string = 'abc'"
121
+ execute "SELECT * FROM matching_table WHERE id = 4321"
122
+ end
116
123
  end
117
124
 
118
- id_eq_sha = log_reverse_hash[4]
119
- str_eq_sha = log_reverse_hash[3]
120
- str_like_sha = log_reverse_hash[2]
121
- id_gt_sha = log_reverse_hash[1]
122
-
123
- expect(log_def_hash[id_gt_sha]["sql"]).to include("id = [REDACTED] and id = [REDACTED]")
124
- expect(log_def_hash[str_like_sha]["sql"]).to include("test_string LIKE '[REDACTED]'")
125
- expect(log_def_hash[id_gt_sha]["transaction"]).to eq(log_def_hash[str_like_sha]["transaction"])
125
+ transaction_executed_once_sha = log_reverse_hash[1]
126
+ transaction_executed_twice_sha = log_reverse_hash[2]
126
127
 
127
- expect(log_def_hash[id_eq_sha]["sql"]).to include("id = [REDACTED]")
128
- expect(log_def_hash[str_eq_sha]["sql"]).to include("test_string = '[REDACTED]'")
129
- expect(log_def_hash[str_eq_sha]["transaction"]).to eq(log_def_hash[id_eq_sha]["transaction"])
128
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
129
+ "BEGIN; " \
130
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
131
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
132
+ "COMMIT;")
130
133
 
131
- expect(log_def_hash[str_like_sha]["transaction"]).to_not eq(log_def_hash[id_eq_sha]["transaction"])
134
+ 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;")
132
139
  end
133
140
 
134
141
  it "Logs nested transactions correctly" do
135
- DBConnection.connection.transaction do
142
+ transaction do
136
143
  execute "SELECT * FROM matching_table WHERE id = 4321"
137
- DBConnection.connection.transaction do
138
- 2.times { execute "SELECT * FROM matching_table WHERE test_string = 'abc'" }
144
+ transaction do
145
+ execute "SELECT * FROM matching_table WHERE test_string = 'abc'"
139
146
  end
140
147
  end
141
148
 
149
+ transaction_executed_once_sha = log_reverse_hash[1]
150
+ 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;")
155
+ end
142
156
 
143
- id_sha = log_reverse_hash[1]
144
- str_sha = log_reverse_hash[2]
157
+ it "Logs transactions with inserts correctly" do
158
+ transaction do
159
+ execute "INSERT INTO matching_table (test_string) VALUES ('test_value')"
160
+ execute "SELECT * FROM matching_table WHERE id = 4321"
161
+ end
145
162
 
146
- expect(log_def_hash[id_sha]["transaction"]).to eq(log_def_hash[str_sha]["transaction"])
147
- expect(log_def_hash[str_sha]["transaction"]).not_to be_nil
163
+ transaction_executed_once_sha = log_reverse_hash[1]
164
+ 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;")
148
169
  end
149
170
 
150
- it "Logs empty transactions correctly" do
151
- DBConnection.connection.transaction do
171
+ it "Logs mixed matching-nonmatching selects correctly" do
172
+ transaction do
152
173
  execute "SELECT * FROM matching_table WHERE id = 4321"
174
+ execute "SELECT * FROM nonmatching_table WHERE id = 4321"
153
175
  end
154
176
 
155
- 2.times { execute "SELECT * FROM matching_table WHERE test_string = 'abc'" }
177
+ transaction_executed_once_sha = log_reverse_hash[1]
156
178
 
157
- id_sha = log_reverse_hash[1]
158
- str_sha = log_reverse_hash[2]
179
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
180
+ "BEGIN; " \
181
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
182
+ "COMMIT;")
183
+ end
159
184
 
160
- expect(log_def_hash[id_sha]["sql"]).to include("id = [REDACTED]")
161
- expect(log_def_hash[str_sha]["sql"]).to include("test_string = '[REDACTED]'")
185
+ it "Logs transaction with repeated selects correctly" do
186
+ transaction do
187
+ execute "SELECT * FROM matching_table WHERE id = 4321"
188
+ ['blah', 'bloo'].each do |s|
189
+ execute "SELECT * FROM matching_table WHERE test_string = '#{s}'"
190
+ end
191
+ end
192
+
193
+ transaction_executed_once_sha = log_reverse_hash[1]
162
194
 
163
- expect(log_def_hash[id_sha]["transaction"]).not_to be_nil
164
- expect(log_def_hash[str_sha]["transaction"]).to be_nil
195
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
196
+ "BEGIN; " \
197
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
198
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
199
+ "COMMIT;")
200
+ end
201
+
202
+ it "Logs transaction with repeated inserts correctly" do
203
+ transaction do
204
+ execute "SELECT * FROM matching_table WHERE id = 4321"
205
+ 2.times do
206
+ execute "INSERT INTO matching_table (test_string) VALUES ('test_value')"
207
+ end
208
+ end
209
+
210
+ transaction_executed_once_sha = log_reverse_hash[1]
211
+
212
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
213
+ "BEGIN; " \
214
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
215
+ "INSERT INTO matching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
216
+ "COMMIT;")
217
+ end
218
+
219
+
220
+ it "Logs mixed matching-nonmatching with inserts correctly" do
221
+ transaction do
222
+ execute "SELECT * FROM matching_table WHERE id = 4321"
223
+ execute "INSERT INTO nonmatching_table (id) VALUES (1)"
224
+ end
225
+
226
+ transaction_executed_once_sha = log_reverse_hash[1]
227
+
228
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
229
+ "BEGIN; " \
230
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
231
+ "INSERT INTO nonmatching_table (REDACTED_COLUMNS) VALUES ('[REDACTED]'); " \
232
+ "COMMIT;")
233
+ end
234
+
235
+ it "Does not log nonmatching-only queries" do
236
+ transaction do
237
+ execute "SELECT * FROM nonmatching_table WHERE id = 4321"
238
+ execute "SELECT * FROM nonmatching_table WHERE id = 4321"
239
+ end
240
+
241
+ expect(log_def_hash.size).to eq(0)
165
242
  end
166
243
 
167
244
  context "Selectively sampling" do
@@ -179,24 +256,29 @@ RSpec.describe "End to End" do
179
256
 
180
257
  expect(log_def_hash.size).to eq(1)
181
258
  expect(log_def_hash.map { |_hash, query| query['sql'] }).to eq([
182
- "SELECT * FROM matching_table WHERE id = [REDACTED]"])
259
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'"])
183
260
  end
184
261
 
185
262
  it "Samples some but not other whole transactions" do
186
- DBConnection.connection.transaction do
263
+ transaction do
187
264
  execute "SELECT * FROM matching_table WHERE id = 1"
188
265
  execute "SELECT * FROM matching_table WHERE test_string = 'abc'"
189
266
  end
190
267
 
191
- DBConnection.connection.transaction do
268
+ transaction do
192
269
  execute "SELECT * FROM matching_table WHERE id = 1 and test_string = 'abc'"
193
270
  execute "SELECT * FROM matching_table WHERE id > 4 and id < 8"
194
271
  end
195
272
 
196
- expect(log_def_hash.size).to eq(2)
197
- expect(Set.new(log_def_hash.map { |_hash, query| query['sql'] })).to eq(Set.new([
198
- "SELECT * FROM matching_table WHERE id = [REDACTED]",
199
- "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'"]))
273
+
274
+ transaction_executed_once_sha = log_reverse_hash[1]
275
+
276
+ expect(log_def_hash.size).to eq(1)
277
+ expect(log_def_hash[transaction_executed_once_sha]["sql"]).to eq(
278
+ "BEGIN; " \
279
+ "SELECT * FROM matching_table WHERE id = '[REDACTED]'; " \
280
+ "SELECT * FROM matching_table WHERE test_string = '[REDACTED]'; " \
281
+ "COMMIT;")
200
282
  end
201
283
  end
202
284
 
@@ -21,8 +21,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
21
21
  [/erb_erb_[0-9]+_[0-9]+/, ""]
22
22
  ]
23
23
  end
24
+
24
25
  # All raw SQL should be valid :)
25
- expect { parser.scan_str(event[:sql]) }.not_to raise_exception if event[:sql].present?
26
+ event_sql_list = event[:calls].map { |call| call[:sql] }
27
+ expect { event_sql_list.map { |sql| parser.scan_str sql } }.not_to raise_exception if event_sql_list.any?(&:present?)
26
28
  end
27
29
 
28
30
  after do
@@ -33,8 +35,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
33
35
  context "ambiguous backtraces" do
34
36
  let(:event) do
35
37
  {
36
- caller: %w(ambiguous foo bar),
37
- sql: ""
38
+ calls: [{
39
+ caller: %w(ambiguous foo bar),
40
+ sql: "",
41
+ }]
38
42
  }
39
43
  end
40
44
 
@@ -52,8 +56,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
52
56
  context "backtrace" do
53
57
  let(:event) do
54
58
  {
55
- caller: %w(foo-bar-erb_erb_1_5),
56
- sql: ""
59
+ calls: [{
60
+ caller: %w(foo-bar-erb_erb_1_5),
61
+ sql: "",
62
+ }]
57
63
  }
58
64
  end
59
65
 
@@ -65,8 +71,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
65
71
  context "sql quoted" do
66
72
  let(:event) do
67
73
  {
68
- caller: [""],
69
- sql: "SELECT * FROM foo WHERE name = 'hello\\'s name'"
74
+ calls: [{
75
+ caller: [""],
76
+ sql: "SELECT * FROM foo WHERE name = 'hello\\'s name'",
77
+ }]
70
78
  }
71
79
  end
72
80
 
@@ -78,8 +86,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
78
86
  context "sql quoted multiple WHERE" do
79
87
  let(:event) do
80
88
  {
81
- caller: [""],
82
- sql: "SELECT * FROM foo WHERE name = 'hello\\'s name' AND age = '21'"
89
+ calls: [{
90
+ caller: [""],
91
+ sql: "SELECT * FROM foo WHERE name = 'hello\\'s name' AND age = '21'",
92
+ }]
83
93
  }
84
94
  end
85
95
 
@@ -92,8 +102,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
92
102
  context "sql escaped and quoted" do
93
103
  let(:event) do
94
104
  {
95
- caller: [""],
96
- sql: "SELECT * FROM foo WHERE name = 'hello\\\'s name'"
105
+ calls: [{
106
+ caller: [""],
107
+ sql: "SELECT * FROM foo WHERE name = 'hello\\\'s name'",
108
+ }]
97
109
  }
98
110
  end
99
111
 
@@ -105,8 +117,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
105
117
  context "sql case insensitivity" do
106
118
  let(:event) do
107
119
  {
108
- caller: [""],
109
- sql: "SELECT * FROM foo WHERE name lIkE 'hello'"
120
+ calls: [{
121
+ caller: [""],
122
+ sql: "SELECT * FROM foo WHERE name lIkE 'hello'",
123
+ }]
110
124
  }
111
125
  end
112
126
 
@@ -118,8 +132,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
118
132
  context "sql" do
119
133
  let(:event) do
120
134
  {
121
- caller: [""],
122
- sql: "SELECT * FROM foo WHERE id = 1234"
135
+ calls: [{
136
+ caller: [""],
137
+ sql: "SELECT * FROM foo WHERE id = 1234",
138
+ }]
123
139
  }
124
140
  end
125
141
 
@@ -131,8 +147,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
131
147
  context "like quoted" do
132
148
  let(:event) do
133
149
  {
134
- caller: [""],
135
- sql: %{SELECT * FROM foo WHERE name LIKE 'A \\'quoted\\' value.' OR name LIKE "another ""quoted"" \\"value\\""}
150
+ calls: [{
151
+ caller: [""],
152
+ sql: %{SELECT * FROM foo WHERE name LIKE 'A \\'quoted\\' value.' OR name LIKE "another ""quoted"" \\"value\\""},
153
+ }]
136
154
  }
137
155
  end
138
156
 
@@ -144,8 +162,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
144
162
  context "like escaped and quoted" do
145
163
  let(:event) do
146
164
  {
147
- caller: [""],
148
- sql: "SELECT * FROM foo WHERE name LIKE 'A \\\'quoted\\\' value.'"
165
+ calls: [{
166
+ caller: [""],
167
+ sql: "SELECT * FROM foo WHERE name LIKE 'A \\\'quoted\\\' value.'",
168
+ }]
149
169
  }
150
170
  end
151
171
 
@@ -157,8 +177,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
157
177
  context "in quoted" do
158
178
  let(:event) do
159
179
  {
160
- caller: [""],
161
- sql: "SELECT * FROM foo WHERE name IN ('A ''quoted'' value.')"
180
+ calls: [{
181
+ caller: [""],
182
+ sql: "SELECT * FROM foo WHERE name IN ('A ''quoted'' value.')",
183
+ }]
162
184
  }
163
185
  end
164
186
 
@@ -170,8 +192,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
170
192
  context "in escaped and quoted" do
171
193
  let(:event) do
172
194
  {
173
- caller: [""],
174
- sql: %{SELECT * FROM foo WHERE name IN ('A ''quoted'' value.', "another ""quoted"" \\"value\\"")}
195
+ calls: [{
196
+ caller: [""],
197
+ sql: %{SELECT * FROM foo WHERE name IN ('A ''quoted'' value.', "another ""quoted"" \\"value\\"")},
198
+ }]
175
199
  }
176
200
  end
177
201
 
@@ -183,8 +207,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
183
207
  context "between strings" do
184
208
  let(:event) do
185
209
  {
186
- caller: [""],
187
- sql: "SELECT * FROM foo WHERE name BETWEEN 'A value.' AND 'Another value'"
210
+ calls: [{
211
+ caller: [""],
212
+ sql: "SELECT * FROM foo WHERE name BETWEEN 'A value.' AND 'Another value'",
213
+ }]
188
214
  }
189
215
  end
190
216
 
@@ -196,8 +222,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
196
222
  context "between strings with escaped quotes" do
197
223
  let(:event) do
198
224
  {
199
- caller: [""],
200
- sql: "SELECT * FROM foo WHERE name BETWEEN 'A ''quoted'' value.' AND 'Another \\'value\\''"
225
+ calls: [{
226
+ caller: [""],
227
+ sql: "SELECT * FROM foo WHERE name BETWEEN 'A ''quoted'' value.' AND 'Another \\'value\\''",
228
+ }]
201
229
  }
202
230
  end
203
231
 
@@ -209,8 +237,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
209
237
  context "in with = and other where clauses" do
210
238
  let(:event) do
211
239
  {
212
- caller: [""],
213
- sql: "SELECT * FROM foo WHERE name IN ('value=') AND name = 'value'"
240
+ calls: [{
241
+ caller: [""],
242
+ sql: "SELECT * FROM foo WHERE name IN ('value=') AND name = 'value'",
243
+ }]
214
244
  }
215
245
  end
216
246
 
@@ -222,8 +252,10 @@ RSpec.describe ActiveRecord::SqlAnalyzer::RedactedLogger do
222
252
  context "insert" do
223
253
  let(:event) do
224
254
  {
225
- caller: [""],
226
- sql: "INSERT INTO `boom` (`bam`, `foo`) VALUES ('howdy', 'dowdy')"
255
+ calls: [{
256
+ caller: [""],
257
+ sql: "INSERT INTO `boom` (`bam`, `foo`) VALUES ('howdy', 'dowdy')",
258
+ }]
227
259
  }
228
260
  end
229
261
 
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.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zachary Anker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2016-08-09 00:00:00.000000000 Z
12
+ date: 2016-08-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord