hotdog 0.25.0 → 0.26.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: a6cb5a21b306e4a1d4ba2ed3f56a32bccede9d3e
4
- data.tar.gz: db352cb8caf7b6204b5af04cae89ade3bb3225a3
3
+ metadata.gz: 5d13acaecbe1b4f13f6b9956f2e75cf69cfaff23
4
+ data.tar.gz: acdef611442f0a968b0b9810c962b25864c0b406
5
5
  SHA512:
6
- metadata.gz: 2f34769acd4326c2d965fee9e69587af995d1764e00318531e8d36fab9020f520aaa4cb57d5950a95938a895ac3359a4800cdae089673c59f5c280f1d16a4611
7
- data.tar.gz: cbcafc9311ea4f8384f57065324c104d0948bc8f95ad0e5108a4479d319e603e2501a8cc25b89c00057fbdd0777585b6b5621e43ed1695b07091e6faa27dc1b5
6
+ metadata.gz: a6f4cc4380eea63cee183a0baff9b606e40e94cdc6522e5e16e33e44acb161987d6001ebf743fa6a7f054c4ed8cc034edd8494893a4c9736be58376efbd2ad7d
7
+ data.tar.gz: 950a18e1686c18591e474900353f738c78cc5f568c830dd41f43b231905eebdfe3bc415fd1662d710e9ec1072e4bba3ab050886f239a20aa35e8c237f7db75e0
@@ -39,7 +39,8 @@ module Hotdog
39
39
  with_retry(error_handler: ->(error) { reload }) do
40
40
  if open_db
41
41
  @db.transaction do
42
- hosts.each_slice(SQLITE_LIMIT_COMPOUND_SELECT) do |hosts|
42
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
43
+ hosts.each_slice(sqlite_limit_compound_select) do |hosts|
43
44
  execute_db(@db, "DELETE FROM hosts_tags WHERE host_id IN ( SELECT id FROM hosts WHERE name IN (%s) );" % hosts.map { "?" }.join(", "), hosts)
44
45
  execute_db(@db, "DELETE FROM hosts WHERE name IN (%s);" % hosts.map { "?" }.join(", "), hosts)
45
46
  end
@@ -13,7 +13,8 @@ module Hotdog
13
13
  execute("SELECT id FROM hosts WHERE LOWER(name) GLOB LOWER(?);", [host_name]).to_a.reduce(:+) || []
14
14
  }
15
15
  else
16
- result = args.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |args|
16
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
17
+ result = args.each_slice(sqlite_limit_compound_select).flat_map { |args|
17
18
  execute("SELECT id FROM hosts WHERE name IN (%s);" % args.map { "?" }.join(", "), args).to_a.reduce(:+) || []
18
19
  }
19
20
  end
@@ -5,6 +5,7 @@ require "parallel"
5
5
  require "parslet"
6
6
  require "shellwords"
7
7
  require "tempfile"
8
+ require "thread"
8
9
  require "hotdog/commands/ssh"
9
10
 
10
11
  module Hotdog
@@ -43,9 +44,10 @@ module Hotdog
43
44
  true
44
45
  }
45
46
  else
47
+ output_lock = Mutex.new
46
48
  stats = Parallel.map(hosts_cmdlines.each_with_index.to_a, in_threads: parallelism(hosts)) { |(host, cmdline), i|
47
49
  identifier = options[:show_identifier] ? host : nil
48
- success = exec_command(identifier, cmdline, index: i, output: true, infile: (infile ? infile.path : nil))
50
+ success = exec_command(identifier, cmdline, index: i, output: true, infile: (infile ? infile.path : nil), output_lock: output_lock)
49
51
  if !success && options[:stop_on_error]
50
52
  raise StopException.new
51
53
  end
@@ -3,6 +3,7 @@
3
3
  require "json"
4
4
  require "parslet"
5
5
  require "shellwords"
6
+ require "thread"
6
7
  require "hotdog/commands/search"
7
8
 
8
9
  module Hotdog
@@ -152,42 +153,75 @@ module Hotdog
152
153
 
153
154
  def exec_command(identifier, cmdline, options={})
154
155
  output = options.fetch(:output, true)
156
+ output_lock = options[:output_lock] || Mutex.new
155
157
  logger.debug("execute: #{cmdline}")
156
158
  if use_color?
157
- if options.key?(:index)
158
- color = 31 + (options[:index] % 6)
159
- else
160
- color = 36
161
- end
159
+ color = color_code(options[:index])
162
160
  else
163
161
  color = nil
164
162
  end
165
163
  if options[:infile]
166
- stdin = IO.popen("cat #{Shellwords.shellescape(options[:infile])}")
167
- else
168
- stdin = :close
169
- end
170
- IO.popen(cmdline, in: stdin) do |io|
171
- io.each_with_index do |s, i|
172
- if output
173
- if identifier
174
- if color
175
- STDOUT.write("\e[0;#{color}m")
176
- end
177
- STDOUT.write(identifier)
178
- STDOUT.write(":")
179
- STDOUT.write(i.to_s)
180
- STDOUT.write(":")
181
- if color
182
- STDOUT.write("\e[0m")
183
- end
164
+ cmdline = "cat #{Shellwords.shellescape(options[:infile])} | #{cmdline}"
165
+ end
166
+ cmderr, child_cmderr = IO.pipe
167
+ IO.popen(cmdline, in: :close, err: child_cmderr) do |cmdout|
168
+ i = 0
169
+ each_readable([cmderr, cmdout]) do |readable|
170
+ raw = readable.readline
171
+ output_lock.synchronize do
172
+ if readable == cmdout
173
+ STDOUT.puts(prettify_output(raw, i, color, identifier))
174
+ i += 1
175
+ else
176
+ STDERR.puts(raw)
184
177
  end
185
- STDOUT.puts(s)
186
178
  end
187
179
  end
188
180
  end
189
181
  $?.success? # $? is thread-local variable
190
182
  end
183
+
184
+ private
185
+ def each_readable(read_list, timeout=1)
186
+ loop do
187
+ # we cannot look until IO#eof? since it will block for pipes
188
+ # http://ruby-doc.org/core-2.4.0/IO.html#method-i-eof-3F
189
+ rs = Array(IO.select(read_list, [], [], timeout)).first
190
+ if r = Array(rs).first
191
+ begin
192
+ yield r
193
+ rescue EOFError => error
194
+ break
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ def color_code(index)
201
+ if index
202
+ color = 31 + (index % 6)
203
+ else
204
+ color = 36
205
+ end
206
+ end
207
+
208
+ def prettify_output(raw, i, color, identifier)
209
+ buf = []
210
+ if identifier
211
+ if color
212
+ buf << ("\e[0;#{color}m")
213
+ end
214
+ buf << identifier
215
+ buf << ":"
216
+ buf << i.to_s
217
+ buf << ":"
218
+ if color
219
+ buf << "\e[0m"
220
+ end
221
+ end
222
+ buf << raw
223
+ buf.join
224
+ end
191
225
  end
192
226
 
193
227
  class SingularSshAlike < SshAlike
@@ -9,14 +9,15 @@ module Hotdog
9
9
  show_tags(result)
10
10
  else
11
11
  tags = args.map { |tag| split_tag(tag) }
12
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
12
13
  if tags.all? { |_tagname, tagvalue| tagvalue.empty? }
13
- result = tags.each_slice(SQLITE_LIMIT_COMPOUND_SELECT).flat_map { |tags|
14
+ result = tags.each_slice(sqlite_limit_compound_select).flat_map { |tags|
14
15
  q = "SELECT value FROM tags " \
15
16
  "WHERE %s;" % tags.map { |tagname, _tagvalue| glob?(tagname) ? "LOWER(name) GLOB LOWER(?)" : "name = ?" }.join(" OR ")
16
17
  execute(q, tags.map { |tagname, _tagvalue| tagname }).map { |value| [value] }
17
18
  }
18
19
  else
19
- result = tags.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / 2).flat_map { |tags|
20
+ result = tags.each_slice(sqlite_limit_compound_select / 2).flat_map { |tags|
20
21
  q = "SELECT value FROM tags " \
21
22
  "WHERE %s;" % tags.map { |tagname, tagvalue| (glob?(tagname) or glob?(tagvalue)) ? "( LOWER(name) GLOB LOWER(?) AND LOWER(value) GLOB LOWER(?) )" : "( name = ? AND value = ? )" }.join(" OR ")
22
23
  execute(q, tags).map { |value| [value] }
@@ -25,6 +25,8 @@ module Hotdog
25
25
 
26
26
  def initialize(op, expression)
27
27
  case (op || "not").to_s
28
+ when "NOOP", "noop"
29
+ @op = :NOOP
28
30
  when "!", "~", "NOT", "not"
29
31
  @op = :NOT
30
32
  else
@@ -35,6 +37,8 @@ module Hotdog
35
37
 
36
38
  def evaluate(environment, options={})
37
39
  case @op
40
+ when :NOOP
41
+ @expression.evaluate(environment, options)
38
42
  when :NOT
39
43
  values = @expression.evaluate(environment, options).tap do |values|
40
44
  environment.logger.debug("expr: #{values.length} value(s)")
@@ -46,8 +50,9 @@ module Hotdog
46
50
  else
47
51
  # workaround for "too many terms in compound SELECT"
48
52
  min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a
49
- (min / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).upto(max / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).flat_map { |i|
50
- range = ((SQLITE_LIMIT_COMPOUND_SELECT - 2) * i)...((SQLITE_LIMIT_COMPOUND_SELECT - 2) * (i + 1))
53
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
54
+ (min / (sqlite_limit_compound_select - 2)).upto(max / (sqlite_limit_compound_select - 2)).flat_map { |i|
55
+ range = ((sqlite_limit_compound_select - 2) * i)...((sqlite_limit_compound_select - 2) * (i + 1))
51
56
  selected = values.select { |n| range === n }
52
57
  q = "SELECT id FROM hosts " \
53
58
  "WHERE ? <= id AND id < ? AND id NOT IN (%s);"
@@ -62,8 +67,9 @@ module Hotdog
62
67
  end
63
68
 
64
69
  def optimize(options={})
65
- @expression = @expression.optimize(options)
66
70
  case op
71
+ when :NOOP
72
+ optimize1(options)
67
73
  when :NOT
68
74
  case expression
69
75
  when EverythingNode
@@ -74,6 +80,7 @@ module Hotdog
74
80
  optimize1(options)
75
81
  end
76
82
  else
83
+ @expression = expression.optimize(options)
77
84
  self
78
85
  end
79
86
  end
@@ -89,30 +96,45 @@ module Hotdog
89
96
  private
90
97
  def optimize1(options={})
91
98
  case op
99
+ when :NOOP
100
+ expression.optimize(options)
92
101
  when :NOT
93
- if UnaryExpressionNode === expression and expression.op == :NOT
94
- expression.expression
95
- else
96
- case expression
97
- when QueryExpressionNode
98
- q = expression.query
99
- v = expression.values
100
- if q and v.length <= SQLITE_LIMIT_COMPOUND_SELECT
101
- QueryExpressionNode.new("SELECT id AS host_id FROM hosts EXCEPT #{q.sub(/\s*;\s*\z/, "")};", v)
102
- else
103
- self
104
- end
105
- when TagExpressionNode
106
- q = expression.maybe_query(options)
107
- v = expression.condition_values(options)
108
- if q and v.length <= SQLITE_LIMIT_COMPOUND_SELECT
109
- QueryExpressionNode.new("SELECT id AS host_id FROM hosts EXCEPT #{q.sub(/\s*;\s*\z/, "")};", v)
110
- else
111
- self
112
- end
102
+ if UnaryExpressionNode === expression
103
+ case expression.op
104
+ when :NOOP
105
+ @expression = expression.optimize(options)
106
+ optimize2(options)
107
+ when :NOT
108
+ expression.expression.optimize(options)
113
109
  else
114
110
  self
115
111
  end
112
+ else
113
+ optimize2(options)
114
+ end
115
+ else
116
+ self
117
+ end
118
+ end
119
+
120
+ def optimize2(options={})
121
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
122
+ case expression
123
+ when QueryExpressionNode
124
+ q = expression.query
125
+ v = expression.values
126
+ if q and v.length <= sqlite_limit_compound_select
127
+ QueryExpressionNode.new("SELECT id AS host_id FROM hosts EXCEPT #{q.sub(/\s*;\s*\z/, "")};", v)
128
+ else
129
+ self
130
+ end
131
+ when TagExpressionNode
132
+ q = expression.maybe_query(options)
133
+ v = expression.condition_values(options)
134
+ if q and v.length <= sqlite_limit_compound_select
135
+ QueryExpressionNode.new("SELECT id AS host_id FROM hosts EXCEPT #{q.sub(/\s*;\s*\z/, "")};", v)
136
+ else
137
+ self
116
138
  end
117
139
  else
118
140
  self
@@ -155,8 +177,9 @@ module Hotdog
155
177
  else
156
178
  # workaround for "too many terms in compound SELECT"
157
179
  min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a
158
- (min / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2)).upto(max / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2)).flat_map { |i|
159
- range = (((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2) * i)...(((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2) * (i + 1))
180
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
181
+ (min / ((sqlite_limit_compound_select - 2) / 2)).upto(max / ((sqlite_limit_compound_select - 2) / 2)).flat_map { |i|
182
+ range = (((sqlite_limit_compound_select - 2) / 2) * i)...(((sqlite_limit_compound_select - 2) / 2) * (i + 1))
160
183
  left_selected = left_values.select { |n| range === n }
161
184
  right_selected = right_values.select { |n| range === n }
162
185
  q = "SELECT id FROM hosts " \
@@ -182,8 +205,9 @@ module Hotdog
182
205
  else
183
206
  # workaround for "too many terms in compound SELECT"
184
207
  min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a
185
- (min / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2)).upto(max / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2)).flat_map { |i|
186
- range = (((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2) * i)...(((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 2) * (i + 1))
208
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
209
+ (min / ((sqlite_limit_compound_select - 2) / 2)).upto(max / ((sqlite_limit_compound_select - 2) / 2)).flat_map { |i|
210
+ range = (((sqlite_limit_compound_select - 2) / 2) * i)...(((sqlite_limit_compound_select - 2) / 2) * (i + 1))
187
211
  left_selected = left_values.select { |n| range === n }
188
212
  right_selected = right_values.select { |n| range === n }
189
213
  q = "SELECT id FROM hosts " \
@@ -209,8 +233,9 @@ module Hotdog
209
233
  else
210
234
  # workaround for "too many terms in compound SELECT"
211
235
  min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts LIMIT 1;").first.to_a
212
- (min / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 4)).upto(max / ((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 4)).flat_map { |i|
213
- range = (((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 4) * i)...(((SQLITE_LIMIT_COMPOUND_SELECT - 2) / 4) * (i + 1))
236
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
237
+ (min / ((sqlite_limit_compound_select - 2) / 4)).upto(max / ((sqlite_limit_compound_select - 2) / 4)).flat_map { |i|
238
+ range = (((sqlite_limit_compound_select - 2) / 4) * i)...(((sqlite_limit_compound_select - 2) / 4) * (i + 1))
214
239
  left_selected = left_values.select { |n| range === n }
215
240
  right_selected = right_values.select { |n| range === n }
216
241
  q = "SELECT id FROM hosts " \
@@ -300,7 +325,8 @@ module Hotdog
300
325
  lv = left.condition_values(options)
301
326
  rq = right.maybe_query(options)
302
327
  rv = right.condition_values(options)
303
- if lq and rq and lv.length + rv.length <= SQLITE_LIMIT_COMPOUND_SELECT
328
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
329
+ if lq and rq and lv.length + rv.length <= sqlite_limit_compound_select
304
330
  case op
305
331
  when :AND
306
332
  q = "#{lq.sub(/\s*;\s*\z/, "")} INTERSECT #{rq.sub(/\s*;\s*\z/, "")};"
@@ -335,8 +361,9 @@ module Hotdog
335
361
  else
336
362
  raise(SyntaxError.new("unknown multinary operator: #{op.inspect}"))
337
363
  end
338
- if SQLITE_LIMIT_COMPOUND_SELECT < expressions.length
339
- raise(ArgumentError.new("expressions limit exceeded: #{expressions.length} for #{SQLITE_LIMIT_COMPOUND_SELECT}"))
364
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
365
+ if sqlite_limit_compound_select < expressions.length
366
+ raise(ArgumentError.new("expressions limit exceeded: #{expressions.length} for #{sqlite_limit_compound_select}"))
340
367
  end
341
368
  @expressions = expressions
342
369
  @fallback = options[:fallback]
@@ -358,7 +385,8 @@ module Hotdog
358
385
  query_without_condition = expressions.first.maybe_query_without_condition(options)
359
386
  if query_without_condition
360
387
  condition_length = expressions.map { |expression| expression.condition_values(options).length }.max
361
- expressions.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / condition_length).flat_map { |expressions|
388
+ sqlite_limit_compound_select = options[:sqlite_limit_compound_select] || SQLITE_LIMIT_COMPOUND_SELECT
389
+ expressions.each_slice(sqlite_limit_compound_select / condition_length).flat_map { |expressions|
362
390
  q = query_without_condition.sub(/\s*;\s*\z/, " WHERE #{expressions.map { |expression| "( %s )" % expression.condition(options) }.join(" OR ")};")
363
391
  environment.execute(q, expressions.flat_map { |expression| expression.condition_values(options) }).map { |row| row.first }
364
392
  }
@@ -1,3 +1,3 @@
1
1
  module Hotdog
2
- VERSION = "0.25.0"
2
+ VERSION = "0.26.0"
3
3
  end
@@ -5,7 +5,7 @@ require "hotdog/commands/search"
5
5
  require "parslet"
6
6
 
7
7
  describe "unary expression" do
8
- it "should be everything" do
8
+ it "NOT nothing should return everything" do
9
9
  expr = Hotdog::Expression::UnaryExpressionNode.new("NOT", Hotdog::Expression::NothingNode.new())
10
10
  expect(expr.optimize.dump).to eq({
11
11
  query: "SELECT id AS host_id FROM hosts;",
@@ -13,11 +13,316 @@ describe "unary expression" do
13
13
  })
14
14
  end
15
15
 
16
- it "should be nothing" do
16
+ it "NOT everything should return nothing" do
17
17
  expr = Hotdog::Expression::UnaryExpressionNode.new("NOT", Hotdog::Expression::EverythingNode.new())
18
18
  expect(expr.optimize.dump).to eq({
19
19
  query: "SELECT NULL AS host_id WHERE host_id NOT NULL;",
20
20
  values: [],
21
21
  })
22
22
  end
23
+
24
+ it "NOT NOT nothing should return nothing" do
25
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
26
+ "NOT",
27
+ Hotdog::Expression::UnaryExpressionNode.new(
28
+ "NOT",
29
+ Hotdog::Expression::NothingNode.new(),
30
+ ),
31
+ )
32
+ expect(expr.optimize.dump).to eq({
33
+ query: "SELECT NULL AS host_id WHERE host_id NOT NULL;",
34
+ values: [],
35
+ })
36
+ end
37
+
38
+ it "NOT NOT everything should return everything" do
39
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
40
+ "NOT",
41
+ Hotdog::Expression::UnaryExpressionNode.new(
42
+ "NOT",
43
+ Hotdog::Expression::EverythingNode.new(),
44
+ ),
45
+ )
46
+ expect(expr.optimize.dump).to eq({
47
+ query: "SELECT id AS host_id FROM hosts;",
48
+ values: [],
49
+ })
50
+ end
51
+
52
+ it "NOT NOT NOT nothing should return everything" do
53
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
54
+ "NOT",
55
+ Hotdog::Expression::UnaryExpressionNode.new(
56
+ "NOT",
57
+ Hotdog::Expression::UnaryExpressionNode.new(
58
+ "NOT",
59
+ Hotdog::Expression::NothingNode.new(),
60
+ ),
61
+ ),
62
+ )
63
+ expect(expr.optimize.dump).to eq({
64
+ query: "SELECT id AS host_id FROM hosts;",
65
+ values: [],
66
+ })
67
+ end
68
+
69
+ it "NOT NOT NOT everything should return nothing" do
70
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
71
+ "NOT",
72
+ Hotdog::Expression::UnaryExpressionNode.new(
73
+ "NOT",
74
+ Hotdog::Expression::UnaryExpressionNode.new(
75
+ "NOT",
76
+ Hotdog::Expression::EverythingNode.new(),
77
+ ),
78
+ ),
79
+ )
80
+ expect(expr.optimize.dump).to eq({
81
+ query: "SELECT NULL AS host_id WHERE host_id NOT NULL;",
82
+ values: [],
83
+ })
84
+ end
85
+
86
+ it "NOT host should return everything except the host" do
87
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
88
+ "NOT",
89
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
90
+ )
91
+ expect(expr.optimize.dump).to eq({
92
+ query: "SELECT id AS host_id FROM hosts EXCEPT SELECT hosts.id AS host_id FROM hosts WHERE hosts.name = ?;",
93
+ values: ["foo"],
94
+ })
95
+ end
96
+
97
+ it "NOT NOT host should return the host" do
98
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
99
+ "NOT",
100
+ Hotdog::Expression::UnaryExpressionNode.new(
101
+ "NOT",
102
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
103
+ ),
104
+ )
105
+ expect(expr.optimize.dump).to eq({
106
+ tagname: "host",
107
+ separator: ":",
108
+ tagvalue: "foo",
109
+ fallback: {
110
+ query: [
111
+ "SELECT hosts.id AS host_id FROM hosts",
112
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
113
+ ].join(" "),
114
+ values: ["*foo*"],
115
+ },
116
+ })
117
+ end
118
+
119
+ it "NOT NOT NOT host should return everything except the host" do
120
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
121
+ "NOT",
122
+ Hotdog::Expression::UnaryExpressionNode.new(
123
+ "NOT",
124
+ Hotdog::Expression::UnaryExpressionNode.new(
125
+ "NOT",
126
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
127
+ ),
128
+ ),
129
+ )
130
+ expect(expr.optimize.dump).to eq({
131
+ query: "SELECT id AS host_id FROM hosts EXCEPT SELECT hosts.id AS host_id FROM hosts WHERE hosts.name = ?;",
132
+ values: ["foo"],
133
+ })
134
+ end
135
+
136
+ it "NOOP host should return the host" do
137
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
138
+ "NOOP",
139
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
140
+ )
141
+ expect(expr.optimize.dump).to eq({
142
+ tagname: "host",
143
+ separator: ":",
144
+ tagvalue: "foo",
145
+ fallback: {
146
+ query: [
147
+ "SELECT hosts.id AS host_id FROM hosts",
148
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
149
+ ].join(" "),
150
+ values: ["*foo*"],
151
+ },
152
+ })
153
+ end
154
+
155
+ it "NOOP NOOP host should return the host" do
156
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
157
+ "NOOP",
158
+ Hotdog::Expression::UnaryExpressionNode.new(
159
+ "NOOP",
160
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
161
+ ),
162
+ )
163
+ expect(expr.optimize.dump).to eq({
164
+ tagname: "host",
165
+ separator: ":",
166
+ tagvalue: "foo",
167
+ fallback: {
168
+ query: [
169
+ "SELECT hosts.id AS host_id FROM hosts",
170
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
171
+ ].join(" "),
172
+ values: ["*foo*"],
173
+ },
174
+ })
175
+ end
176
+
177
+ it "NOOP NOOP NOOP host should return the host" do
178
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
179
+ "NOOP",
180
+ Hotdog::Expression::UnaryExpressionNode.new(
181
+ "NOOP",
182
+ Hotdog::Expression::UnaryExpressionNode.new(
183
+ "NOOP",
184
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
185
+ ),
186
+ ),
187
+ )
188
+ expect(expr.optimize.dump).to eq({
189
+ tagname: "host",
190
+ separator: ":",
191
+ tagvalue: "foo",
192
+ fallback: {
193
+ query: [
194
+ "SELECT hosts.id AS host_id FROM hosts",
195
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
196
+ ].join(" "),
197
+ values: ["*foo*"],
198
+ },
199
+ })
200
+ end
201
+
202
+ it "NOT NOOP NOOP host should return everything except the host" do
203
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
204
+ "NOT",
205
+ Hotdog::Expression::UnaryExpressionNode.new(
206
+ "NOOP",
207
+ Hotdog::Expression::UnaryExpressionNode.new(
208
+ "NOOP",
209
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
210
+ ),
211
+ ),
212
+ )
213
+ expect(expr.optimize.dump).to eq({
214
+ query: "SELECT id AS host_id FROM hosts EXCEPT SELECT hosts.id AS host_id FROM hosts WHERE hosts.name = ?;",
215
+ values: ["foo"],
216
+ })
217
+ end
218
+
219
+ it "NOOP NOT NOOP host should return everything except the host" do
220
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
221
+ "NOOP",
222
+ Hotdog::Expression::UnaryExpressionNode.new(
223
+ "NOT",
224
+ Hotdog::Expression::UnaryExpressionNode.new(
225
+ "NOOP",
226
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
227
+ ),
228
+ ),
229
+ )
230
+ expect(expr.optimize.dump).to eq({
231
+ query: "SELECT id AS host_id FROM hosts EXCEPT SELECT hosts.id AS host_id FROM hosts WHERE hosts.name = ?;",
232
+ values: ["foo"],
233
+ })
234
+ end
235
+
236
+ it "NOOP NOOP NOT host should return everything except the host" do
237
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
238
+ "NOOP",
239
+ Hotdog::Expression::UnaryExpressionNode.new(
240
+ "NOOP",
241
+ Hotdog::Expression::UnaryExpressionNode.new(
242
+ "NOT",
243
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
244
+ ),
245
+ ),
246
+ )
247
+ expect(expr.optimize.dump).to eq({
248
+ query: "SELECT id AS host_id FROM hosts EXCEPT SELECT hosts.id AS host_id FROM hosts WHERE hosts.name = ?;",
249
+ values: ["foo"],
250
+ })
251
+ end
252
+
253
+ it "NOOP NOT NOT host should return everything except the host" do
254
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
255
+ "NOT",
256
+ Hotdog::Expression::UnaryExpressionNode.new(
257
+ "NOT",
258
+ Hotdog::Expression::UnaryExpressionNode.new(
259
+ "NOOP",
260
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
261
+ ),
262
+ ),
263
+ )
264
+ expect(expr.optimize.dump).to eq({
265
+ tagname: "host",
266
+ separator: ":",
267
+ tagvalue: "foo",
268
+ fallback: {
269
+ query: [
270
+ "SELECT hosts.id AS host_id FROM hosts",
271
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
272
+ ].join(" "),
273
+ values: ["*foo*"],
274
+ },
275
+ })
276
+ end
277
+
278
+ it "NOT NOOP NOT host should return everything except the host" do
279
+ pending("optimization of 2+ depth unary expression is not yet supported")
280
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
281
+ "NOT",
282
+ Hotdog::Expression::UnaryExpressionNode.new(
283
+ "NOOP",
284
+ Hotdog::Expression::UnaryExpressionNode.new(
285
+ "NOT",
286
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
287
+ ),
288
+ ),
289
+ )
290
+ expect(expr.optimize.dump).to eq({
291
+ tagname: "host",
292
+ separator: ":",
293
+ tagvalue: "foo",
294
+ fallback: {
295
+ query: [
296
+ "SELECT hosts.id AS host_id FROM hosts",
297
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
298
+ ].join(" "),
299
+ values: ["*foo*"],
300
+ },
301
+ })
302
+ end
303
+
304
+ it "NOT NOT NOOP host should return everything except the host" do
305
+ expr = Hotdog::Expression::UnaryExpressionNode.new(
306
+ "NOT",
307
+ Hotdog::Expression::UnaryExpressionNode.new(
308
+ "NOT",
309
+ Hotdog::Expression::UnaryExpressionNode.new(
310
+ "NOOP",
311
+ Hotdog::Expression::StringHostNode.new("foo", ":"),
312
+ ),
313
+ ),
314
+ )
315
+ expect(expr.optimize.dump).to eq({
316
+ tagname: "host",
317
+ separator: ":",
318
+ tagvalue: "foo",
319
+ fallback: {
320
+ query: [
321
+ "SELECT hosts.id AS host_id FROM hosts",
322
+ "WHERE LOWER(hosts.name) GLOB LOWER(?);",
323
+ ].join(" "),
324
+ values: ["*foo*"],
325
+ },
326
+ })
327
+ end
23
328
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hotdog
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.25.0
4
+ version: 0.26.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yamashita Yuu
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-22 00:00:00.000000000 Z
11
+ date: 2017-08-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler