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 +4 -4
- data/lib/hotdog/commands/down.rb +2 -1
- data/lib/hotdog/commands/hosts.rb +2 -1
- data/lib/hotdog/commands/pssh.rb +3 -1
- data/lib/hotdog/commands/ssh.rb +58 -24
- data/lib/hotdog/commands/tags.rb +3 -2
- data/lib/hotdog/expression/semantics.rb +61 -33
- data/lib/hotdog/version.rb +1 -1
- data/spec/optimizer/unary_expression_spec.rb +307 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5d13acaecbe1b4f13f6b9956f2e75cf69cfaff23
|
4
|
+
data.tar.gz: acdef611442f0a968b0b9810c962b25864c0b406
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6f4cc4380eea63cee183a0baff9b606e40e94cdc6522e5e16e33e44acb161987d6001ebf743fa6a7f054c4ed8cc034edd8494893a4c9736be58376efbd2ad7d
|
7
|
+
data.tar.gz: 950a18e1686c18591e474900353f738c78cc5f568c830dd41f43b231905eebdfe3bc415fd1662d710e9ec1072e4bba3ab050886f239a20aa35e8c237f7db75e0
|
data/lib/hotdog/commands/down.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
data/lib/hotdog/commands/pssh.rb
CHANGED
@@ -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
|
data/lib/hotdog/commands/ssh.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
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
|
data/lib/hotdog/commands/tags.rb
CHANGED
@@ -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(
|
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(
|
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
|
-
|
50
|
-
|
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
|
94
|
-
expression.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
-
|
159
|
-
|
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
|
-
|
186
|
-
|
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
|
-
|
213
|
-
|
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
|
-
|
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
|
-
|
339
|
-
|
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
|
-
|
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
|
}
|
data/lib/hotdog/version.rb
CHANGED
@@ -5,7 +5,7 @@ require "hotdog/commands/search"
|
|
5
5
|
require "parslet"
|
6
6
|
|
7
7
|
describe "unary expression" do
|
8
|
-
it "should
|
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
|
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.
|
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-
|
11
|
+
date: 2017-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|