groonga-query-log 1.3.7 → 1.3.8

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
  SHA256:
3
- metadata.gz: f15d15e5f0ca25ffbd6e5dee08c732a4cebb35b4ad4eaa44fcacca85e0e6b386
4
- data.tar.gz: 14b68832f85aff8c122477a5d5ed791a5545c370da51ed291588454216e3a6e9
3
+ metadata.gz: 661030b254c034e486074270e46e90f9ea593ceea13f1e648f9e7b76fe75bb3c
4
+ data.tar.gz: c8cae31736b8265e9b160019dbf0c72e0944e4e6113b94ad67a3a897e3bb240a
5
5
  SHA512:
6
- metadata.gz: b0240ddbc1584dc007f55ad7a742c6b6a7f7c643a6261f08f61bd8ba5a2359e240128d2614be6f7ce1c1be8fe034f1d22906a1222c7ceb507e929c093467b1a8
7
- data.tar.gz: a95c78d3864012a3711dd3ff2e0d54bab936fafdfd4a2fadafc04131e437272ee057221a16f07fa5f6e8478492ac97fa547a872fe075724800b43d0e34a2fd41
6
+ metadata.gz: 821547ca78bda0bc51ea23f12c0f1d72d3bbec079e96ed24884791b355279d9904f5a2a2e03b3f064bc3ca13d519df3be30153effaa530686659bfaf0235780c
7
+ data.tar.gz: fb253f2e4d5d50c6a4f0cc056b7b270647733d6c4b1f99e85316ac30f41f41944bcdfca74060d166f3f6da2094bf8727db6fafe91214aa4ee59abf33c22708c9
@@ -1,5 +1,41 @@
1
1
  # News
2
2
 
3
+ ## 1.3.8: 2018-10-18
4
+
5
+ ### Improvements
6
+
7
+ * `groonga-query-log-verify-server`:
8
+
9
+ * Changed to flush logs as soon as possible.
10
+
11
+ * Added `--vector-not-equal-empty-string` option.
12
+
13
+ * Added support an unification for `"null"` and `null` column types.
14
+
15
+ * `groonga-query-log-run-regression-test`:
16
+
17
+ * Changed to flush logs as soon as possible.
18
+
19
+ * Added support for rewriting `vector == ...` with `vector @ ...`.
20
+
21
+ * Added support for logging rewriting filters.
22
+
23
+ * Added `--vector-not-equal-empty-string` option.
24
+
25
+ * Added support an unification for `"null"` and `null` column types.
26
+
27
+ ### Fixes
28
+
29
+ * `groonga-query-log-verify-server`:
30
+
31
+ * Fixed a bug that logged contents are removed with `.tar.gz`
32
+ query log.
33
+
34
+ * `groonga-query-log-run-regression-test`:
35
+
36
+ * Fixed a bug that logged contents are removed with `.tar.gz`
37
+ query log.
38
+
3
39
  ## 1.3.7: 2018-09-11
4
40
 
5
41
  ### Improvements
@@ -45,6 +45,9 @@ module GroongaQueryLog
45
45
  @skip_finished_queries = false
46
46
  @output_query_log = false
47
47
  @stop_on_failure = false
48
+ @rewrite_vector_equal = false
49
+ @rewrite_vector_not_equal_empty_string = false
50
+ @vector_accessors = []
48
51
 
49
52
  @care_order = true
50
53
  @ignored_drilldown_keys = []
@@ -151,6 +154,22 @@ module GroongaQueryLog
151
154
  "(#{@stop_on_failure})") do |boolean|
152
155
  @stop_on_failure = boolean
153
156
  end
157
+ parser.on("--[no-]rewrite-vector-equal",
158
+ "Rewrite 'vector == ...' with 'vector @ ...'",
159
+ "(#{@rewrite_vector_equal})") do |boolean|
160
+ @rewrite_vector_equal = boolean
161
+ end
162
+ parser.on("--[no-]rewrite-vector-not-equal-empty-string",
163
+ "Rewrite 'vector != \"\"' with 'false'",
164
+ "(#{@rewrite_vector_not_equal_empty_string})") do |boolean|
165
+ @rewrite_vector_not_equal_empty_string = boolean
166
+ end
167
+ parser.on("--vector-accessor=ACCESSOR",
168
+ "Mark ACCESSOR as rewrite vector targets",
169
+ "You can specify multiple vector accessors by",
170
+ "specifying this option multiple times") do |accessor|
171
+ @vector_accessors << accessor
172
+ end
154
173
 
155
174
  parser.separator("")
156
175
  parser.separator("Comparisons:")
@@ -208,6 +227,10 @@ module GroongaQueryLog
208
227
  :skip_finished_queries => @skip_finished_queries,
209
228
  :ignored_drilldown_keys => @ignored_drilldown_keys,
210
229
  :stop_on_failure => @stop_on_failure,
230
+ :rewrite_vector_equal => @rewrite_vector_equal,
231
+ :rewrite_vector_not_equal_empty_string =>
232
+ @rewrite_vector_not_equal_empty_string,
233
+ :vector_accessors => @vector_accessors,
211
234
  :target_command_names => @target_command_names,
212
235
  :read_timeout => @read_timeout,
213
236
  }
@@ -228,7 +251,16 @@ module GroongaQueryLog
228
251
  server_options)
229
252
  end
230
253
 
254
+ module Loggable
255
+ def puts(*args)
256
+ $stdout.puts(*args)
257
+ $stdout.flush
258
+ end
259
+ end
260
+
231
261
  class GroongaServer
262
+ include Loggable
263
+
232
264
  attr_reader :host, :port
233
265
  def initialize(groonga, groonga_options, database_path, options)
234
266
  @input_directory = options[:input_directory] || Pathname.new(".")
@@ -369,6 +401,8 @@ module GroongaQueryLog
369
401
  end
370
402
 
371
403
  class Tester
404
+ include Loggable
405
+
372
406
  def initialize(old, new, options)
373
407
  @old = old
374
408
  @new = new
@@ -473,6 +507,17 @@ module GroongaQueryLog
473
507
  if @stop_on_failure
474
508
  command_line << "--stop-on-failure"
475
509
  end
510
+ if @options[:rewrite_vector_equal]
511
+ command_line << "--rewrite-vector-equal"
512
+ end
513
+ if @options[:rewrite_vector_not_equal_empty_string]
514
+ command_line << "--rewrite-vector-not-equal-empty-string"
515
+ end
516
+ vector_accessors = @options[:vector_accessors] || []
517
+ vector_accessors.each do |vector_accessor|
518
+ command_line << "--vector-accessor"
519
+ command_line << vector_accessor
520
+ end
476
521
  if @options[:target_command_names]
477
522
  command_line << "--target-command-names"
478
523
  command_line << @options[:target_command_names].join(",")
@@ -201,6 +201,25 @@ module GroongaQueryLog
201
201
  @options.stop_on_failure = boolean
202
202
  end
203
203
 
204
+ parser.on("--[no-]rewrite-vector-equal",
205
+ "Rewrite 'vector == ...' with 'vector @ ...'",
206
+ "(#{@options.rewrite_vector_equal?})") do |boolean|
207
+ @options.rewrite_vector_equal = boolean
208
+ end
209
+
210
+ parser.on("--[no-]rewrite-vector-not-equal-empty-string",
211
+ "Rewrite 'vector != \"\"' with 'false'",
212
+ "(#{@options.rewrite_vector_not_equal_empty_string?})") do |boolean|
213
+ @options.rewrite_vector_not_equal_empty_string = boolean
214
+ end
215
+
216
+ parser.on("--vector-accessor=ACCESSOR",
217
+ "Mark ACCESSOR as rewrite vector targets",
218
+ "You can specify multiple vector accessors by",
219
+ "specifying this option multiple times") do |accessor|
220
+ @options.vector_accessors << accessor
221
+ end
222
+
204
223
  parser.separator("Debug options:")
205
224
  parser.separator("")
206
225
 
@@ -0,0 +1,59 @@
1
+ # Copyright (C) 2018 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ module GroongaQueryLog
18
+ class FilterRewriter
19
+ def initialize(filter, options={})
20
+ @filter = filter
21
+ @options = options
22
+ @vector_accessors = @options[:vector_accessors] || []
23
+ end
24
+
25
+ def rewrite
26
+ rewritten = @filter
27
+ if @options[:rewrite_vector_equal]
28
+ rewritten = rewrite_vector_equal(rewritten)
29
+ end
30
+ if @options[:rewrite_vector_not_equal_empty_string]
31
+ rewritten = rewrite_vector_not_equal_empty_string(rewritten)
32
+ end
33
+ rewritten
34
+ end
35
+
36
+ private
37
+ def rewrite_vector_equal(filter)
38
+ filter.gsub(/([a-zA-Z0-9_.]+) *==/) do |matched|
39
+ variable = $1
40
+ if @vector_accessors.include?(variable)
41
+ "#{variable} @"
42
+ else
43
+ matched
44
+ end
45
+ end
46
+ end
47
+
48
+ def rewrite_vector_not_equal_empty_string(filter)
49
+ filter.gsub(/([a-zA-Z0-9_.]+) *!= *(?:''|"")/) do |matched|
50
+ variable = $1
51
+ if @vector_accessors.include?(variable)
52
+ "false"
53
+ else
54
+ matched
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -113,8 +113,8 @@ module GroongaQueryLog
113
113
  n_hits2 = records_result2[0]
114
114
  return false if n_hits1 != n_hits2
115
115
 
116
- columns1 = records_result1[1]
117
- columns2 = records_result2[1]
116
+ columns1 = normalize_columns(records_result1[1])
117
+ columns2 = normalize_columns(records_result2[1])
118
118
  if all_output_columns?
119
119
  columns1.sort_by(&:first) == columns2.sort_by(&:first)
120
120
  else
@@ -137,8 +137,8 @@ module GroongaQueryLog
137
137
  n_hits2 = records_result2[0]
138
138
  return false if n_hits1 != n_hits2
139
139
 
140
- columns1 = records_result1[1]
141
- columns2 = records_result2[1]
140
+ columns1 = normalize_columns(records_result1[1])
141
+ columns2 = normalize_columns(records_result2[1])
142
142
  records1 = records_result1[2..-1]
143
143
  records2 = records_result2[2..-1]
144
144
 
@@ -185,8 +185,8 @@ module GroongaQueryLog
185
185
  n_hits2 = records_result2[0]
186
186
  return false if n_hits1 != n_hits2
187
187
 
188
- columns1 = records_result1[1]
189
- columns2 = records_result2[1]
188
+ columns1 = normalize_columns(records_result1[1])
189
+ columns2 = normalize_columns(records_result2[1])
190
190
  return false if columns1.sort_by(&:first) != columns2.sort_by(&:first)
191
191
 
192
192
  column_to_index1 = make_column_to_index_map(columns1)
@@ -223,8 +223,8 @@ module GroongaQueryLog
223
223
  n_hits2 = record_set2[0]
224
224
  return false if n_hits1 != n_hits2
225
225
 
226
- columns1 = record_set1[1]
227
- columns2 = record_set2[1]
226
+ columns1 = normalize_columns(record_set1[1])
227
+ columns2 = normalize_columns(record_set2[1])
228
228
  return false if columns1 != columns2
229
229
 
230
230
  records1 = record_set1[2..-1]
@@ -282,6 +282,13 @@ module GroongaQueryLog
282
282
  true
283
283
  end
284
284
 
285
+ def normalize_columns(columns)
286
+ columns.collect do |name, type|
287
+ type = nil if type == "null"
288
+ [name, type]
289
+ end
290
+ end
291
+
285
292
  def normalize_value(value, column)
286
293
  type = column[1]
287
294
  case type
@@ -19,6 +19,7 @@ require "thread"
19
19
 
20
20
  require "groonga/client"
21
21
 
22
+ require "groonga-query-log/filter-rewriter"
22
23
  require "groonga-query-log/parser"
23
24
  require "groonga-query-log/response-comparer"
24
25
 
@@ -118,7 +119,7 @@ module GroongaQueryLog
118
119
 
119
120
  def run_reporter
120
121
  Thread.new do
121
- @options.create_output do |output|
122
+ @options.open_output do |output|
122
123
  loop do
123
124
  result = @different_results.pop
124
125
  break if result.nil?
@@ -135,6 +136,8 @@ module GroongaQueryLog
135
136
  def verify_command(groonga1_client, groonga2_client, command)
136
137
  command["cache"] = "no" if @options.disable_cache?
137
138
  command["output_type"] = "json"
139
+ rewrite_filter(command, "filter")
140
+ rewrite_filter(command, "scorer")
138
141
  response1 = groonga1_client.execute(command)
139
142
  response2 = groonga2_client.execute(command)
140
143
  compare_options = {
@@ -148,6 +151,21 @@ module GroongaQueryLog
148
151
  end
149
152
  end
150
153
 
154
+ def rewrite_filter(command, name)
155
+ target = command[name]
156
+ return if target.nil?
157
+ return unless @options.need_filter_rewrite?
158
+
159
+ rewriter = FilterRewriter.new(target, @options.to_filter_rewriter_options)
160
+ rewritten_target = rewriter.rewrite
161
+ return if target == rewritten_target
162
+
163
+ $stderr.puts("Rewritten #{name}")
164
+ $stderr.puts(" Before: #{target}")
165
+ $stderr.puts(" After: #{rewritten_target}")
166
+ command[name] = rewritten_target
167
+ end
168
+
151
169
  def report_result(output, result)
152
170
  @same = false
153
171
  command, response1, response2 = result
@@ -155,6 +173,7 @@ module GroongaQueryLog
155
173
  output.puts("command: #{command_source}")
156
174
  output.puts("response1: #{response1.body.to_json}")
157
175
  output.puts("response2: #{response2.body.to_json}")
176
+ output.flush
158
177
  end
159
178
 
160
179
  def log_client_error(error)
@@ -181,6 +200,9 @@ module GroongaQueryLog
181
200
  attr_writer :verify_cache
182
201
  attr_accessor :ignored_drilldown_keys
183
202
  attr_writer :stop_on_failure
203
+ attr_writer :rewrite_vector_equal
204
+ attr_writer :rewrite_vector_not_equal_empty_string
205
+ attr_accessor :vector_accessors
184
206
  def initialize
185
207
  @groonga1 = GroongaOptions.new
186
208
  @groonga2 = GroongaOptions.new
@@ -188,6 +210,7 @@ module GroongaQueryLog
188
210
  @request_queue_size = nil
189
211
  @disable_cache = false
190
212
  @output_path = nil
213
+ @output_opened = false
191
214
  @target_command_names = [
192
215
  "io_flush",
193
216
  "logical_count",
@@ -203,6 +226,9 @@ module GroongaQueryLog
203
226
  @verify_cache = false
204
227
  @ignored_drilldown_keys = []
205
228
  @stop_on_failure = false
229
+ @rewrite_vector_equal = false
230
+ @rewrite_vector_not_equal_empty_string = false
231
+ @vector_accessors = []
206
232
  end
207
233
 
208
234
  def request_queue_size
@@ -221,6 +247,14 @@ module GroongaQueryLog
221
247
  @stop_on_failure
222
248
  end
223
249
 
250
+ def rewrite_vector_equal?
251
+ @rewrite_vector_equal
252
+ end
253
+
254
+ def rewrite_vector_not_equal_empty_string?
255
+ @rewrite_vector_not_equal_empty_string
256
+ end
257
+
224
258
  def target_command_name?(name)
225
259
  return false if name.nil?
226
260
 
@@ -231,14 +265,34 @@ module GroongaQueryLog
231
265
  end
232
266
  end
233
267
 
234
- def create_output(&block)
268
+ def open_output(&block)
235
269
  if @output_path
236
- FileUtils.mkdir_p(File.dirname(@output_path))
237
- File.open(@output_path, "w", &block)
270
+ if @output_opened
271
+ mode = "a"
272
+ else
273
+ FileUtils.mkdir_p(File.dirname(@output_path))
274
+ mode = "w"
275
+ @output_opened = true
276
+ end
277
+ File.open(@output_path, mode, &block)
238
278
  else
239
279
  yield($stdout)
240
280
  end
241
281
  end
282
+
283
+ def need_filter_rewrite?
284
+ rewrite_vector_equal? or
285
+ rewrite_vector_not_equal_empty_string?
286
+ end
287
+
288
+ def to_filter_rewriter_options
289
+ {
290
+ :rewrite_vector_equal => rewrite_vector_equal?,
291
+ :rewrite_vector_not_equal_empty_string =>
292
+ rewrite_vector_not_equal_empty_string?,
293
+ :vector_accessors => vector_accessors,
294
+ }
295
+ end
242
296
  end
243
297
 
244
298
  class GroongaOptions
@@ -15,5 +15,5 @@
15
15
  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
  module GroongaQueryLog
18
- VERSION = "1.3.7"
18
+ VERSION = "1.3.8"
19
19
  end
@@ -0,0 +1,75 @@
1
+ # Copyright (C) 2018 Kouhei Sutou <kou@clear-code.com>
2
+ #
3
+ # This library is free software; you can redistribute it and/or
4
+ # modify it under the terms of the GNU Lesser General Public
5
+ # License as published by the Free Software Foundation; either
6
+ # version 2.1 of the License, or (at your option) any later version.
7
+ #
8
+ # This library is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11
+ # Lesser General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU Lesser General Public
14
+ # License along with this library; if not, write to the Free Software
15
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+
17
+ class FilterRewriterTest < Test::Unit::TestCase
18
+ private
19
+ def rewrite(filter, options)
20
+ rewriter = GroongaQueryLog::FilterRewriter.new(filter, options)
21
+ rewriter.rewrite
22
+ end
23
+
24
+ class VectorEqualTest < self
25
+ def rewrite(filter, vector_accessors)
26
+ super(filter,
27
+ :rewrite_vector_equal => true,
28
+ :vector_accessors => vector_accessors)
29
+ end
30
+
31
+ def test_not_target_accessor
32
+ assert_equal("vector == \"value\"",
33
+ rewrite("vector == \"value\"",
34
+ ["nonexistent"]))
35
+ end
36
+
37
+ def test_parenthesis
38
+ assert_equal("((vector @ \"value\"))",
39
+ rewrite("((vector == \"value\"))",
40
+ ["vector"]))
41
+ end
42
+
43
+ def test_under_score
44
+ assert_equal("vector_column @ \"value\"",
45
+ rewrite("vector_column == \"value\"",
46
+ ["vector_column"]))
47
+ end
48
+ end
49
+
50
+ class VectorNotEqualEmptyStringTest < self
51
+ def rewrite(filter, vector_accessors)
52
+ super(filter,
53
+ :rewrite_vector_not_equal_empty_string => true,
54
+ :vector_accessors => vector_accessors)
55
+ end
56
+
57
+ def test_not_target_accessor
58
+ assert_equal("vector != \"\"",
59
+ rewrite("vector != \"\"",
60
+ ["nonexistent"]))
61
+ end
62
+
63
+ def test_parenthesis
64
+ assert_equal("((false))",
65
+ rewrite("((vector != \"\"))",
66
+ ["vector"]))
67
+ end
68
+
69
+ def test_under_score
70
+ assert_equal("false",
71
+ rewrite("vector_column != \"\"",
72
+ ["vector_column"]))
73
+ end
74
+ end
75
+ end
@@ -363,6 +363,54 @@ class ResponseComparerTest < Test::Unit::TestCase
363
363
  end
364
364
  end
365
365
 
366
+ class ColumnType < self
367
+ class Null < self
368
+ def create_response(type)
369
+ [
370
+ [
371
+ [1],
372
+ [["snippet_html", type]],
373
+ ["...snippet..."],
374
+ ]
375
+ ]
376
+ end
377
+
378
+ def test_all_output_columns
379
+ response1 = create_response("null")
380
+ response2 = create_response(nil)
381
+ assert do
382
+ same?(response1, response2)
383
+ end
384
+ end
385
+
386
+ def test_unary_minus_output_column
387
+ @command["output_columns"] = "-value, snippet_html(body)"
388
+ response1 = create_response("null")
389
+ response2 = create_response(nil)
390
+ assert do
391
+ same?(response1, response2)
392
+ end
393
+ end
394
+
395
+ def test_specific_output_column
396
+ @command["output_columns"] = "snippet_html(body)"
397
+ response1 = create_response("null")
398
+ response2 = create_response(nil)
399
+ assert do
400
+ same?(response1, response2)
401
+ end
402
+ end
403
+
404
+ def test_not_care_order
405
+ response1 = create_response("null")
406
+ response2 = create_response(nil)
407
+ assert do
408
+ same?(response1, response2, care_order: false)
409
+ end
410
+ end
411
+ end
412
+ end
413
+
366
414
  class DrilldownTest < self
367
415
  def create_response(drilldown)
368
416
  [
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: groonga-query-log
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.7
4
+ version: 1.3.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kouhei Sutou
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-09-11 00:00:00.000000000 Z
11
+ date: 2018-10-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: groonga-command-parser
@@ -224,6 +224,7 @@ files:
224
224
  - lib/groonga-query-log/command/run-regression-test.rb
225
225
  - lib/groonga-query-log/command/show-running-queries.rb
226
226
  - lib/groonga-query-log/command/verify-server.rb
227
+ - lib/groonga-query-log/filter-rewriter.rb
227
228
  - lib/groonga-query-log/incompatibility-detector.rb
228
229
  - lib/groonga-query-log/memory-leak-detector.rb
229
230
  - lib/groonga-query-log/parser.rb
@@ -289,6 +290,7 @@ files:
289
290
  - test/fixtures/target-tables.expected
290
291
  - test/helper.rb
291
292
  - test/run-test.rb
293
+ - test/test-filter-rewriter.rb
292
294
  - test/test-incompatibility-detector.rb
293
295
  - test/test-parser.rb
294
296
  - test/test-replayer.rb
@@ -321,6 +323,7 @@ summary: Groonga-query-log is a collection of library and tools to process [Groo
321
323
  as a library. You can analyze your Groonga's queries and test with your Groonga's
322
324
  query log by using groonga-query-log as a tool.
323
325
  test_files:
326
+ - test/test-filter-rewriter.rb
324
327
  - test/fixtures/multi.expected
325
328
  - test/fixtures/target-tables.expected
326
329
  - test/fixtures/target-commands.expected