active_record-sql_analyzer 0.1.0 → 0.2.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
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