hotdog 0.4.1 → 0.5.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/README.md +4 -0
- data/lib/hotdog/application.rb +21 -15
- data/lib/hotdog/commands/down.rb +10 -8
- data/lib/hotdog/commands/hosts.rb +0 -1
- data/lib/hotdog/commands/pssh.rb +27 -24
- data/lib/hotdog/commands/search.rb +640 -262
- data/lib/hotdog/commands/ssh.rb +30 -20
- data/lib/hotdog/commands/tags.rb +0 -1
- data/lib/hotdog/commands/up.rb +0 -1
- data/lib/hotdog/commands.rb +66 -22
- data/lib/hotdog/version.rb +1 -1
- data/spec/parser/parser_spec.rb +3 -3
- data/spec/parser/tag_expression_spec.rb +10 -8
- data/spec/parser/tag_glob_expression_spec.rb +10 -8
- data/spec/parser/tag_regexp_expression_spec.rb +10 -8
- metadata +3 -2
@@ -6,13 +6,15 @@ require "parslet"
|
|
6
6
|
module Hotdog
|
7
7
|
module Commands
|
8
8
|
class Search < BaseCommand
|
9
|
-
def
|
10
|
-
search_options = {
|
11
|
-
}
|
9
|
+
def define_options(optparse)
|
10
|
+
@search_options = @options.merge({
|
11
|
+
})
|
12
12
|
optparse.on("-n", "--limit LIMIT", "Limit result set to specified size at most", Integer) do |limit|
|
13
|
-
search_options[:limit] = limit
|
13
|
+
@search_options[:limit] = limit
|
14
14
|
end
|
15
|
-
|
15
|
+
end
|
16
|
+
|
17
|
+
def run(args=[])
|
16
18
|
expression = args.join(" ").strip
|
17
19
|
if expression.empty?
|
18
20
|
# return everything if given expression is empty
|
@@ -29,7 +31,7 @@ module Hotdog
|
|
29
31
|
result = evaluate(node, self)
|
30
32
|
if 0 < result.length
|
31
33
|
_result, fields = get_hosts_with_search_tags(result, node)
|
32
|
-
result = _result.take(search_options.fetch(:limit, _result.size))
|
34
|
+
result = _result.take(@search_options.fetch(:limit, _result.size))
|
33
35
|
STDOUT.print(format(result, fields: fields))
|
34
36
|
if _result.length == result.length
|
35
37
|
logger.info("found %d host(s)." % result.length)
|
@@ -67,14 +69,22 @@ module Hotdog
|
|
67
69
|
def parse(expression)
|
68
70
|
parser = ExpressionParser.new
|
69
71
|
parser.parse(expression).tap do |parsed|
|
70
|
-
logger.debug
|
72
|
+
logger.debug {
|
73
|
+
begin
|
74
|
+
JSON.pretty_generate(JSON.load(parsed.to_json))
|
75
|
+
rescue JSON::NestingError => error
|
76
|
+
error.message
|
77
|
+
end
|
78
|
+
}
|
71
79
|
end
|
72
80
|
end
|
73
81
|
|
74
82
|
def evaluate(data, environment)
|
75
83
|
node = ExpressionTransformer.new.apply(data)
|
76
84
|
optimized = node.optimize.tap do |optimized|
|
77
|
-
logger.debug
|
85
|
+
logger.debug {
|
86
|
+
JSON.pretty_generate(optimized.dump)
|
87
|
+
}
|
78
88
|
end
|
79
89
|
optimized.evaluate(environment)
|
80
90
|
end
|
@@ -133,9 +143,9 @@ module Hotdog
|
|
133
143
|
}
|
134
144
|
rule(:atom) {
|
135
145
|
( spacing.maybe >> str('(') >> expression >> str(')') >> spacing.maybe \
|
136
|
-
| spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> separator.as(:separator) >> attribute_regexp.as(:attribute_regexp) >> spacing.maybe \
|
137
|
-
| spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> separator.as(:separator) >> spacing.maybe \
|
138
|
-
| spacing.maybe >> identifier_regexp.as(:identifier_regexp) >> spacing.maybe \
|
146
|
+
| spacing.maybe >> str('/') >> identifier_regexp.as(:identifier_regexp) >> str('/') >> separator.as(:separator) >> str('/') >> attribute_regexp.as(:attribute_regexp) >> str('/') >> spacing.maybe \
|
147
|
+
| spacing.maybe >> str('/') >> identifier_regexp.as(:identifier_regexp) >> str('/') >> separator.as(:separator) >> spacing.maybe \
|
148
|
+
| spacing.maybe >> str('/') >> identifier_regexp.as(:identifier_regexp) >> str('/') >> spacing.maybe \
|
139
149
|
| spacing.maybe >> identifier_glob.as(:identifier_glob) >> separator.as(:separator) >> attribute_glob.as(:attribute_glob) >> spacing.maybe \
|
140
150
|
| spacing.maybe >> identifier_glob.as(:identifier_glob) >> separator.as(:separator) >> attribute.as(:attribute) >> spacing.maybe \
|
141
151
|
| spacing.maybe >> identifier_glob.as(:identifier_glob) >> separator.as(:separator) >> spacing.maybe \
|
@@ -144,16 +154,16 @@ module Hotdog
|
|
144
154
|
| spacing.maybe >> identifier.as(:identifier) >> separator.as(:separator) >> attribute.as(:attribute) >> spacing.maybe \
|
145
155
|
| spacing.maybe >> identifier.as(:identifier) >> separator.as(:separator) >> spacing.maybe \
|
146
156
|
| spacing.maybe >> identifier.as(:identifier) >> spacing.maybe \
|
147
|
-
| spacing.maybe >> separator.as(:separator) >> attribute_regexp.as(:attribute_regexp) >> spacing.maybe \
|
157
|
+
| spacing.maybe >> separator.as(:separator) >> str('/') >> attribute_regexp.as(:attribute_regexp) >> str('/') >> spacing.maybe \
|
148
158
|
| spacing.maybe >> separator.as(:separator) >> attribute_glob.as(:attribute_glob) >> spacing.maybe \
|
149
159
|
| spacing.maybe >> separator.as(:separator) >> attribute.as(:attribute) >> spacing.maybe \
|
150
|
-
| spacing.maybe >> attribute_regexp.as(:attribute_regexp) >> spacing.maybe \
|
160
|
+
| spacing.maybe >> str('/') >> attribute_regexp.as(:attribute_regexp) >> str('/') >> spacing.maybe \
|
151
161
|
| spacing.maybe >> attribute_glob.as(:attribute_glob) >> spacing.maybe \
|
152
162
|
| spacing.maybe >> attribute.as(:attribute) >> spacing.maybe \
|
153
163
|
)
|
154
164
|
}
|
155
165
|
rule(:identifier_regexp) {
|
156
|
-
(
|
166
|
+
( (str('/').absent? >> any).repeat(0) \
|
157
167
|
)
|
158
168
|
}
|
159
169
|
rule(:identifier_glob) {
|
@@ -170,7 +180,7 @@ module Hotdog
|
|
170
180
|
)
|
171
181
|
}
|
172
182
|
rule(:attribute_regexp) {
|
173
|
-
(
|
183
|
+
( (str('/').absent? >> any).repeat(0) \
|
174
184
|
)
|
175
185
|
}
|
176
186
|
rule(:attribute_glob) {
|
@@ -198,55 +208,75 @@ module Hotdog
|
|
198
208
|
UnaryExpressionNode.new(unary_op, expression)
|
199
209
|
}
|
200
210
|
rule(identifier_regexp: simple(:identifier_regexp), separator: simple(:separator), attribute_regexp: simple(:attribute_regexp)) {
|
201
|
-
|
211
|
+
if "host" == identifier_regexp
|
212
|
+
RegexpHostNode.new(attribute_regexp.to_s, separator)
|
213
|
+
else
|
214
|
+
RegexpTagNode.new(identifier_regexp.to_s, attribute_regexp.to_s, separator)
|
215
|
+
end
|
202
216
|
}
|
203
217
|
rule(identifier_regexp: simple(:identifier_regexp), separator: simple(:separator)) {
|
204
|
-
|
218
|
+
RegexpTagNameNode.new(identifier_regexp.to_s, separator)
|
205
219
|
}
|
206
220
|
rule(identifier_regexp: simple(:identifier_regexp)) {
|
207
|
-
|
221
|
+
RegexpNode.new(identifier_regexp.to_s)
|
208
222
|
}
|
209
223
|
rule(identifier_glob: simple(:identifier_glob), separator: simple(:separator), attribute_glob: simple(:attribute_glob)) {
|
210
|
-
|
224
|
+
if "host" == identifier_glob
|
225
|
+
GlobHostNode.new(attribute_glob.to_s, separator)
|
226
|
+
else
|
227
|
+
GlobTagNode.new(identifier_glob.to_s, attribute_glob.to_s, separator)
|
228
|
+
end
|
211
229
|
}
|
212
230
|
rule(identifier_glob: simple(:identifier_glob), separator: simple(:separator), attribute: simple(:attribute)) {
|
213
|
-
|
231
|
+
if "host" == identifier_glob
|
232
|
+
GlobHostNode.new(attribute.to_s, separator)
|
233
|
+
else
|
234
|
+
GlobTagNode.new(identifier.to_s, attribute.to_s, separator)
|
235
|
+
end
|
214
236
|
}
|
215
237
|
rule(identifier_glob: simple(:identifier_glob), separator: simple(:separator)) {
|
216
|
-
|
238
|
+
GlobTagNameNode.new(identifier_glob.to_s, separator)
|
217
239
|
}
|
218
240
|
rule(identifier_glob: simple(:identifier_glob)) {
|
219
|
-
|
241
|
+
GlobNode.new(identifier_glob.to_s)
|
220
242
|
}
|
221
243
|
rule(identifier: simple(:identifier), separator: simple(:separator), attribute_glob: simple(:attribute_glob)) {
|
222
|
-
|
244
|
+
if "host" == identifier
|
245
|
+
GlobHostNode.new(attribute_glob.to_s, separator)
|
246
|
+
else
|
247
|
+
GlobTagNode.new(identifier.to_s, attribute_glob.to_s, separator)
|
248
|
+
end
|
223
249
|
}
|
224
250
|
rule(identifier: simple(:identifier), separator: simple(:separator), attribute: simple(:attribute)) {
|
225
|
-
|
251
|
+
if "host" == identifier
|
252
|
+
StringHostNode.new(attribute.to_s, separator)
|
253
|
+
else
|
254
|
+
StringTagNode.new(identifier.to_s, attribute.to_s, separator)
|
255
|
+
end
|
226
256
|
}
|
227
257
|
rule(identifier: simple(:identifier), separator: simple(:separator)) {
|
228
|
-
|
258
|
+
StringTagNameNode.new(identifier.to_s, separator)
|
229
259
|
}
|
230
260
|
rule(identifier: simple(:identifier)) {
|
231
|
-
|
261
|
+
StringNode.new(identifier.to_s)
|
232
262
|
}
|
233
263
|
rule(separator: simple(:separator), attribute_regexp: simple(:attribute_regexp)) {
|
234
|
-
|
264
|
+
RegexpTagValueNode.new(attribute_regexp.to_s, separator)
|
235
265
|
}
|
236
266
|
rule(attribute_regexp: simple(:attribute_regexp)) {
|
237
|
-
|
267
|
+
RegexpTagValueNode.new(attribute_regexp.to_s)
|
238
268
|
}
|
239
269
|
rule(separator: simple(:separator), attribute_glob: simple(:attribute_glob)) {
|
240
|
-
|
270
|
+
GlobTagValueNode.new(attribute_glob.to_s, separator)
|
241
271
|
}
|
242
272
|
rule(attribute_glob: simple(:attribute_glob)) {
|
243
|
-
|
273
|
+
GlobTagValueNode.new(attribute_glob.to_s)
|
244
274
|
}
|
245
275
|
rule(separator: simple(:separator), attribute: simple(:attribute)) {
|
246
|
-
|
276
|
+
StringTagValueNode.new(attribute.to_s, separator)
|
247
277
|
}
|
248
278
|
rule(attribute: simple(:attribute)) {
|
249
|
-
|
279
|
+
StringTagValueNode.new(attribute.to_s)
|
250
280
|
}
|
251
281
|
end
|
252
282
|
|
@@ -262,6 +292,106 @@ module Hotdog
|
|
262
292
|
def dump(options={})
|
263
293
|
{}
|
264
294
|
end
|
295
|
+
|
296
|
+
def intermediates()
|
297
|
+
[]
|
298
|
+
end
|
299
|
+
|
300
|
+
def leafs()
|
301
|
+
[self]
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
class UnaryExpressionNode < ExpressionNode
|
306
|
+
attr_reader :op, :expression
|
307
|
+
|
308
|
+
def initialize(op, expression)
|
309
|
+
case op
|
310
|
+
when "!", "~", /\Anot\z/i
|
311
|
+
@op = :NOT
|
312
|
+
else
|
313
|
+
raise(SyntaxError.new("unknown unary operator: #{@op.inspect}"))
|
314
|
+
end
|
315
|
+
@expression = expression
|
316
|
+
end
|
317
|
+
|
318
|
+
def evaluate(environment, options={})
|
319
|
+
case @op
|
320
|
+
when :NOT
|
321
|
+
values = @expression.evaluate(environment, options).tap do |values|
|
322
|
+
environment.logger.debug("expr: #{values.length} value(s)")
|
323
|
+
end
|
324
|
+
if values.empty?
|
325
|
+
environment.execute("SELECT id FROM hosts").map { |row| row.first }.tap do |values|
|
326
|
+
environment.logger.debug("NOT expr: #{values.length} value(s)")
|
327
|
+
end
|
328
|
+
else
|
329
|
+
# workaround for "too many terms in compound SELECT"
|
330
|
+
min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts ORDER BY id LIMIT 1").first.to_a
|
331
|
+
(min / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).upto(max / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).flat_map { |i|
|
332
|
+
range = ((SQLITE_LIMIT_COMPOUND_SELECT - 2) * i)...((SQLITE_LIMIT_COMPOUND_SELECT - 2) * (i + 1))
|
333
|
+
selected = values.select { |n| range === n }
|
334
|
+
q = "SELECT id FROM hosts " \
|
335
|
+
"WHERE ? <= id AND id < ? AND id NOT IN (%s);"
|
336
|
+
environment.execute(q % selected.map { "?" }.join(", "), [range.first, range.last] + selected).map { |row| row.first }
|
337
|
+
}.tap do |values|
|
338
|
+
environment.logger.debug("NOT expr: #{values.length} value(s)")
|
339
|
+
end
|
340
|
+
end
|
341
|
+
else
|
342
|
+
[]
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def optimize(options={})
|
347
|
+
@expression = @expression.optimize(options)
|
348
|
+
case op
|
349
|
+
when :NOT
|
350
|
+
optimize1(options)
|
351
|
+
else
|
352
|
+
self
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def ==(other)
|
357
|
+
self.class === other and @op == other.op and @expression == other.expression
|
358
|
+
end
|
359
|
+
|
360
|
+
def dump(options={})
|
361
|
+
{unary_op: @op.to_s, expression: @expression.dump(options)}
|
362
|
+
end
|
363
|
+
|
364
|
+
def intermediates()
|
365
|
+
[self] + @expression.intermediates
|
366
|
+
end
|
367
|
+
|
368
|
+
def leafs()
|
369
|
+
@expression.leafs
|
370
|
+
end
|
371
|
+
|
372
|
+
private
|
373
|
+
def optimize1(options={})
|
374
|
+
case op
|
375
|
+
when :NOT
|
376
|
+
if UnaryExpressionNode === expression and expression.op == :NOT
|
377
|
+
expression.expression
|
378
|
+
else
|
379
|
+
if TagExpressionNode === expression
|
380
|
+
q = expression.maybe_query(options)
|
381
|
+
v = expression.condition_values(options)
|
382
|
+
if q and v.length <= SQLITE_LIMIT_COMPOUND_SELECT
|
383
|
+
QueryExpressionNode.new("SELECT id AS host_id FROM hosts EXCEPT #{q.sub(/\s*;\s*\z/, "")};", v)
|
384
|
+
else
|
385
|
+
self
|
386
|
+
end
|
387
|
+
else
|
388
|
+
self
|
389
|
+
end
|
390
|
+
end
|
391
|
+
else
|
392
|
+
self
|
393
|
+
end
|
394
|
+
end
|
265
395
|
end
|
266
396
|
|
267
397
|
class BinaryExpressionNode < ExpressionNode
|
@@ -386,10 +516,30 @@ module Hotdog
|
|
386
516
|
if left == right
|
387
517
|
left
|
388
518
|
else
|
389
|
-
|
519
|
+
if MultinaryExpressionNode === left
|
520
|
+
if left.op == op
|
521
|
+
left.merge(right, fallback: self)
|
522
|
+
else
|
523
|
+
optimize1(options)
|
524
|
+
end
|
525
|
+
else
|
526
|
+
if MultinaryExpressionNode === right
|
527
|
+
if right.op == op
|
528
|
+
right.merge(left, fallback: self)
|
529
|
+
else
|
530
|
+
optimize1(options)
|
531
|
+
end
|
532
|
+
else
|
533
|
+
MultinaryExpressionNode.new(op, [left, right], fallback: self)
|
534
|
+
end
|
535
|
+
end
|
390
536
|
end
|
391
537
|
when :XOR
|
392
|
-
|
538
|
+
if left == right
|
539
|
+
[]
|
540
|
+
else
|
541
|
+
optimize1(options)
|
542
|
+
end
|
393
543
|
else
|
394
544
|
self
|
395
545
|
end
|
@@ -400,30 +550,37 @@ module Hotdog
|
|
400
550
|
end
|
401
551
|
|
402
552
|
def dump(options={})
|
403
|
-
{left: @left.dump(options),
|
553
|
+
{left: @left.dump(options), binary_op: @op.to_s, right: @right.dump(options)}
|
554
|
+
end
|
555
|
+
|
556
|
+
def intermediates()
|
557
|
+
[self] + @left.intermediates + @right.intermediates
|
558
|
+
end
|
559
|
+
|
560
|
+
def leafs()
|
561
|
+
@left.leafs + @right.leafs
|
404
562
|
end
|
405
563
|
|
406
564
|
private
|
407
565
|
def optimize1(options)
|
408
566
|
if TagExpressionNode === left and TagExpressionNode === right
|
409
|
-
|
410
|
-
|
411
|
-
|
567
|
+
lq = left.maybe_query(options)
|
568
|
+
lv = left.condition_values(options)
|
569
|
+
rq = right.maybe_query(options)
|
570
|
+
rv = right.condition_values(options)
|
571
|
+
if lq and rq and lv.length + rv.length <= SQLITE_LIMIT_COMPOUND_SELECT
|
412
572
|
case op
|
413
573
|
when :AND
|
414
|
-
q = "
|
415
|
-
|
416
|
-
QueryExpressionNode.new(q, lhs[1] + rhs[1], fallback: self)
|
574
|
+
q = "#{lq.sub(/\s*;\s*\z/, "")} INTERSECT #{rq.sub(/\s*;\s*\z/, "")};"
|
575
|
+
QueryExpressionNode.new(q, lv + rv, fallback: self)
|
417
576
|
when :OR
|
418
|
-
q = "
|
419
|
-
|
420
|
-
QueryExpressionNode.new(q, lhs[1] + rhs[1], fallback: self)
|
577
|
+
q = "#{lq.sub(/\s*;\s*\z/, "")} UNION #{rq.sub(/\s*;\s*\z/, "")};"
|
578
|
+
QueryExpressionNode.new(q, lv + rv, fallback: self)
|
421
579
|
when :XOR
|
422
|
-
q = "
|
423
|
-
"
|
424
|
-
|
425
|
-
|
426
|
-
QueryExpressionNode.new(q, lhs[1] + rhs[1], fallback: self)
|
580
|
+
q = "#{lq.sub(/\s*;\s*\z/, "")} UNION #{rq.sub(/\s*;\s*\z/, "")} " \
|
581
|
+
"EXCEPT #{lq.sub(/\s*;\s*\z/, "")} " \
|
582
|
+
"INTERSECT #{rq.sub(/\s*;\s*\z/, "")};"
|
583
|
+
QueryExpressionNode.new(q, lv + rv, fallback: self)
|
427
584
|
else
|
428
585
|
self
|
429
586
|
end
|
@@ -436,88 +593,71 @@ module Hotdog
|
|
436
593
|
end
|
437
594
|
end
|
438
595
|
|
439
|
-
class
|
440
|
-
attr_reader :op, :
|
596
|
+
class MultinaryExpressionNode < ExpressionNode
|
597
|
+
attr_reader :op, :expressions
|
441
598
|
|
442
|
-
def initialize(op,
|
599
|
+
def initialize(op, expressions, options={})
|
443
600
|
case op
|
444
|
-
when
|
445
|
-
@op = :
|
601
|
+
when :OR
|
602
|
+
@op = :OR
|
446
603
|
else
|
447
|
-
raise(SyntaxError.new("unknown
|
604
|
+
raise(SyntaxError.new("unknown multinary operator: #{op.inspect}"))
|
605
|
+
end
|
606
|
+
if SQLITE_LIMIT_COMPOUND_SELECT < expressions.length
|
607
|
+
raise(ArgumentError.new("expressions limit exceeded: #{expressions.length} for #{SQLITE_LIMIT_COMPOUND_SELECT}"))
|
608
|
+
end
|
609
|
+
@expressions = expressions
|
610
|
+
@fallback = options[:fallback]
|
611
|
+
end
|
612
|
+
|
613
|
+
def merge(other, options={})
|
614
|
+
if MultinaryExpressionNode === other and op == other.op
|
615
|
+
MultinaryExpressionNode.new(op, expressions + other.expressions, options)
|
616
|
+
else
|
617
|
+
MultinaryExpressionNode.new(op, expressions + [other], options)
|
448
618
|
end
|
449
|
-
@expression = expression
|
450
619
|
end
|
451
620
|
|
452
621
|
def evaluate(environment, options={})
|
453
622
|
case @op
|
454
|
-
when :
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
623
|
+
when :OR
|
624
|
+
if expressions.all? { |expression| TagExpressionNode === expression }
|
625
|
+
if query_without_condition = expressions.first.maybe_query_without_condition(options)
|
626
|
+
q = query_without_condition.sub(/\s*;\s*\z/, "") + " WHERE " + expressions.map { |expression| "( %s )" % expression.condition(options) }.join(" OR ") + ";"
|
627
|
+
condition_length = expressions.first.condition_values(options).length
|
628
|
+
values = expressions.each_slice(SQLITE_LIMIT_COMPOUND_SELECT / condition_length).flat_map { |expressions|
|
629
|
+
environment.execute(q, expressions.flat_map { |expression| expression.condition_values(options) }).map { |row| row.first }
|
630
|
+
}
|
631
|
+
else
|
632
|
+
values = []
|
461
633
|
end
|
462
634
|
else
|
463
|
-
|
464
|
-
min, max = environment.execute("SELECT MIN(id), MAX(id) FROM hosts ORDER BY id LIMIT 1").first.to_a
|
465
|
-
(min / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).upto(max / (SQLITE_LIMIT_COMPOUND_SELECT - 2)).flat_map { |i|
|
466
|
-
range = ((SQLITE_LIMIT_COMPOUND_SELECT - 2) * i)...((SQLITE_LIMIT_COMPOUND_SELECT - 2) * (i + 1))
|
467
|
-
selected = values.select { |n| range === n }
|
468
|
-
q = "SELECT id FROM hosts " \
|
469
|
-
"WHERE ? <= id AND id < ? AND id NOT IN (%s);"
|
470
|
-
environment.execute(q % selected.map { "?" }.join(", "), [range.first, range.last] + selected).map { |row| row.first }
|
471
|
-
}.tap do |values|
|
472
|
-
environment.logger.debug("NOT expr: #{values.length} value(s)")
|
473
|
-
end
|
635
|
+
values = []
|
474
636
|
end
|
475
637
|
else
|
476
|
-
[]
|
638
|
+
values = []
|
477
639
|
end
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
optimize1(options)
|
640
|
+
if values.empty?
|
641
|
+
if @fallback
|
642
|
+
@fallback.evaluate(environment, options={})
|
643
|
+
else
|
644
|
+
[]
|
645
|
+
end
|
485
646
|
else
|
486
|
-
|
647
|
+
values
|
487
648
|
end
|
488
649
|
end
|
489
650
|
|
490
|
-
def
|
491
|
-
|
651
|
+
def dump(optinos={})
|
652
|
+
{multinary_op: @op.to_s, expressions: expressions.map { |expression| expression.dump }}
|
492
653
|
end
|
493
654
|
|
494
|
-
def
|
495
|
-
|
655
|
+
def intermediates()
|
656
|
+
[self] + @expression.flat_map { |expression| expression.intermediates }
|
496
657
|
end
|
497
658
|
|
498
|
-
|
499
|
-
|
500
|
-
case op
|
501
|
-
when :NOT
|
502
|
-
if UnaryExpressionNode === expression and expression.op == :NOT
|
503
|
-
expression.expression
|
504
|
-
else
|
505
|
-
if TagExpressionNode === expression
|
506
|
-
expr = expression.plan(options)
|
507
|
-
if expr and expr[1].length <= SQLITE_LIMIT_COMPOUND_SELECT
|
508
|
-
q = "SELECT id AS host_id FROM hosts " \
|
509
|
-
"EXCEPT #{expr[0].sub(/\s*;\s*\z/, "")};"
|
510
|
-
QueryExpressionNode.new(q, expr[1])
|
511
|
-
else
|
512
|
-
self
|
513
|
-
end
|
514
|
-
else
|
515
|
-
self
|
516
|
-
end
|
517
|
-
end
|
518
|
-
else
|
519
|
-
self
|
520
|
-
end
|
659
|
+
def leafs()
|
660
|
+
@expressions.flat_map { |expression| expression.leafs }
|
521
661
|
end
|
522
662
|
end
|
523
663
|
|
@@ -531,7 +671,11 @@ module Hotdog
|
|
531
671
|
def evaluate(environment, options={})
|
532
672
|
values = environment.execute(@query, @args).map { |row| row.first }
|
533
673
|
if values.empty? and @fallback
|
534
|
-
@fallback.evaluate(environment, options)
|
674
|
+
@fallback.evaluate(environment, options).tap do |values|
|
675
|
+
if values.empty?
|
676
|
+
environment.logger.info("no result: #{self.dump.inspect}")
|
677
|
+
end
|
678
|
+
end
|
535
679
|
else
|
536
680
|
values
|
537
681
|
end
|
@@ -567,49 +711,47 @@ module Hotdog
|
|
567
711
|
!(separator.nil? or separator.to_s.empty?)
|
568
712
|
end
|
569
713
|
|
570
|
-
def
|
571
|
-
if
|
572
|
-
|
573
|
-
case identifier
|
574
|
-
when /\Ahost\z/i
|
575
|
-
q = "SELECT hosts.id AS host_id FROM hosts " \
|
576
|
-
"WHERE hosts.name = ?;"
|
577
|
-
[q, [attribute]]
|
578
|
-
else
|
579
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
580
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
581
|
-
"WHERE tags.name = ? AND tags.value = ?;"
|
582
|
-
[q, [identifier, attribute]]
|
583
|
-
end
|
584
|
-
else
|
585
|
-
if separator?
|
586
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
587
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
588
|
-
"WHERE tags.name = ?;"
|
589
|
-
[q, [identifier]]
|
590
|
-
else
|
591
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
592
|
-
"INNER JOIN hosts ON hosts_tags.host_id = hosts.id " \
|
593
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
594
|
-
"WHERE hosts.name = ? OR tags.name = ? OR tags.value = ?;"
|
595
|
-
[q, [identifier, identifier, identifier]]
|
596
|
-
end
|
597
|
-
end
|
714
|
+
def maybe_query(options={})
|
715
|
+
if query_without_condition = maybe_query_without_condition(options)
|
716
|
+
query_without_condition.sub(/\s*;\s*\z/, "") + " WHERE " + condition(options) + ";"
|
598
717
|
else
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
718
|
+
nil
|
719
|
+
end
|
720
|
+
end
|
721
|
+
|
722
|
+
def maybe_query_without_condition(options={})
|
723
|
+
tables = condition_tables(options)
|
724
|
+
if tables.empty?
|
725
|
+
nil
|
726
|
+
else
|
727
|
+
case tables.sort
|
728
|
+
when [:hosts]
|
729
|
+
"SELECT hosts.id AS host_id FROM hosts;"
|
730
|
+
when [:hosts, :tags]
|
731
|
+
"SELECT DISTINCT hosts_tags.host_id FROM hosts_tags INNER JOIN hosts ON hosts_tags.host_id = hosts.id INNER JOIN tags ON hosts_tags.tag_id = tags.id;"
|
732
|
+
when [:tags]
|
733
|
+
"SELECT DISTINCT hosts_tags.host_id FROM hosts_tags INNER JOIN tags ON hosts_tags.tag_id = tags.id;"
|
604
734
|
else
|
605
|
-
|
735
|
+
raise(NotImplementedError.new("unknown tables: #{tables.join(", ")}"))
|
606
736
|
end
|
607
737
|
end
|
608
738
|
end
|
609
739
|
|
740
|
+
def condition(options={})
|
741
|
+
raise NotImplementedError
|
742
|
+
end
|
743
|
+
|
744
|
+
def condition_tables(options={})
|
745
|
+
raise NotImplementedError
|
746
|
+
end
|
747
|
+
|
748
|
+
def condition_values(options={})
|
749
|
+
raise NotImplementedError
|
750
|
+
end
|
751
|
+
|
610
752
|
def evaluate(environment, options={})
|
611
|
-
if q =
|
612
|
-
values = environment.execute(
|
753
|
+
if q = maybe_query(options)
|
754
|
+
values = environment.execute(q, condition_values(options)).map { |row| row.first }
|
613
755
|
if values.empty?
|
614
756
|
if options[:did_fallback]
|
615
757
|
[]
|
@@ -618,12 +760,28 @@ module Hotdog
|
|
618
760
|
# avoid optimizing @fallback to prevent infinite recursion
|
619
761
|
values = @fallback.evaluate(environment, options.merge(did_fallback: true))
|
620
762
|
if values.empty?
|
621
|
-
reload(environment, options)
|
763
|
+
if reload(environment, options)
|
764
|
+
evaluate(environment, options).tap do |values|
|
765
|
+
if values.empty?
|
766
|
+
environment.logger.info("no result: #{self.dump.inspect}")
|
767
|
+
end
|
768
|
+
end
|
769
|
+
else
|
770
|
+
[]
|
771
|
+
end
|
622
772
|
else
|
623
773
|
values
|
624
774
|
end
|
625
775
|
else
|
626
|
-
reload(environment, options)
|
776
|
+
if reload(environment, options)
|
777
|
+
evaluate(environment, options).tap do |values|
|
778
|
+
if values.empty?
|
779
|
+
environment.logger.info("no result: #{self.dump.inspect}")
|
780
|
+
end
|
781
|
+
end
|
782
|
+
else
|
783
|
+
[]
|
784
|
+
end
|
627
785
|
end
|
628
786
|
end
|
629
787
|
else
|
@@ -640,158 +798,378 @@ module Hotdog
|
|
640
798
|
|
641
799
|
def optimize(options={})
|
642
800
|
# fallback to glob expression
|
643
|
-
|
644
|
-
prefix = (identifier.start_with?("*")) ? "" : "*"
|
645
|
-
suffix = (identifier.end_with?("*")) ? "" : "*"
|
646
|
-
identifier_glob = prefix + identifier.gsub(/[-.\/_]/, "?") + suffix
|
647
|
-
else
|
648
|
-
identifier_glob = nil
|
649
|
-
end
|
650
|
-
if attribute?
|
651
|
-
prefix = (attribute.start_with?("*")) ? "" : "*"
|
652
|
-
suffix = (attribute.end_with?("*")) ? "" : "*"
|
653
|
-
attribute_glob = prefix + attribute.gsub(/[-.\/_]/, "?") + suffix
|
654
|
-
else
|
655
|
-
attribute_glob = nil
|
656
|
-
end
|
657
|
-
if (identifier? and identifier != identifier_glob) or (attribute? and attribute != attribute_glob)
|
658
|
-
@fallback = TagGlobExpressionNode.new(identifier_glob, attribute_glob, separator)
|
659
|
-
end
|
801
|
+
@fallback = maybe_fallback(options)
|
660
802
|
self
|
661
803
|
end
|
662
804
|
|
805
|
+
def to_glob(s)
|
806
|
+
(s.start_with?("*") ? "" : "*") + s.gsub(/[-.\/_]/, "?") + (s.end_with?("*") ? "" : "*")
|
807
|
+
end
|
808
|
+
|
809
|
+
def maybe_glob(s)
|
810
|
+
s ? to_glob(s.to_s) : nil
|
811
|
+
end
|
812
|
+
|
663
813
|
def reload(environment, options={})
|
664
|
-
|
665
|
-
if
|
814
|
+
$did_reload ||= false
|
815
|
+
if $did_reload
|
816
|
+
false
|
817
|
+
else
|
818
|
+
$did_reload = true
|
666
819
|
environment.logger.info("force reloading all hosts and tags.")
|
667
820
|
environment.reload(force: true)
|
668
|
-
|
669
|
-
else
|
670
|
-
[]
|
821
|
+
true
|
671
822
|
end
|
672
823
|
end
|
673
824
|
|
674
825
|
def dump(options={})
|
675
826
|
data = {}
|
676
|
-
data[:identifier] =
|
677
|
-
data[:separator] =
|
678
|
-
data[:attribute] =
|
827
|
+
data[:identifier] = identifier.to_s if identifier
|
828
|
+
data[:separator] = separator.to_s if separator
|
829
|
+
data[:attribute] = attribute.to_s if attribute
|
679
830
|
data[:fallback ] = @fallback.dump(options) if @fallback
|
680
831
|
data
|
681
832
|
end
|
833
|
+
|
834
|
+
def maybe_fallback(options={})
|
835
|
+
nil
|
836
|
+
end
|
682
837
|
end
|
683
838
|
|
684
|
-
class
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
691
|
-
|
692
|
-
|
693
|
-
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
700
|
-
|
701
|
-
|
702
|
-
|
703
|
-
|
704
|
-
|
705
|
-
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
end
|
713
|
-
else
|
714
|
-
if attribute?
|
715
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
716
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
717
|
-
"WHERE LOWER(tags.value) GLOB LOWER(?);"
|
718
|
-
[q, [attribute]]
|
719
|
-
else
|
720
|
-
nil
|
721
|
-
end
|
722
|
-
end
|
839
|
+
class StringExpressionNode < TagExpressionNode
|
840
|
+
end
|
841
|
+
|
842
|
+
class StringHostNode < StringExpressionNode
|
843
|
+
def initialize(attribute, separator=nil)
|
844
|
+
super("host", attribute, separator)
|
845
|
+
end
|
846
|
+
|
847
|
+
def condition(options={})
|
848
|
+
"hosts.name = ?"
|
849
|
+
end
|
850
|
+
|
851
|
+
def condition_tables(options={})
|
852
|
+
[:hosts]
|
853
|
+
end
|
854
|
+
|
855
|
+
def condition_values(options={})
|
856
|
+
[attribute]
|
857
|
+
end
|
858
|
+
|
859
|
+
def maybe_fallback(options={})
|
860
|
+
GlobHostNode.new(maybe_glob(attribute), separator)
|
861
|
+
end
|
862
|
+
end
|
863
|
+
|
864
|
+
class StringTagNode < StringExpressionNode
|
865
|
+
def initialize(identifier, attribute, separator=nil)
|
866
|
+
super(identifier, attribute, separator)
|
723
867
|
end
|
724
868
|
|
869
|
+
def condition(options={})
|
870
|
+
"tags.name = ? AND tags.value = ?"
|
871
|
+
end
|
872
|
+
|
873
|
+
def condition_tables(options={})
|
874
|
+
[:tags]
|
875
|
+
end
|
876
|
+
|
877
|
+
def condition_values(options={})
|
878
|
+
[identifier, attribute]
|
879
|
+
end
|
880
|
+
|
881
|
+
def maybe_fallback(options={})
|
882
|
+
GlobTagNode.new(maybe_glob(identifier), maybe_glob(attribute), separator)
|
883
|
+
end
|
884
|
+
end
|
885
|
+
|
886
|
+
class StringTagNameNode < StringExpressionNode
|
887
|
+
def initialize(identifier, separator=nil)
|
888
|
+
super(identifier, nil, separator)
|
889
|
+
end
|
890
|
+
|
891
|
+
def condition(options={})
|
892
|
+
"tags.name = ?"
|
893
|
+
end
|
894
|
+
|
895
|
+
def condition_tables(options={})
|
896
|
+
[:tags]
|
897
|
+
end
|
898
|
+
|
899
|
+
def condition_values(options={})
|
900
|
+
[identifier]
|
901
|
+
end
|
902
|
+
|
903
|
+
def maybe_fallback(options={})
|
904
|
+
GlobTagNameNode.new(maybe_glob(identifier), separator)
|
905
|
+
end
|
906
|
+
end
|
907
|
+
|
908
|
+
class StringTagValueNode < StringExpressionNode
|
909
|
+
def initialize(attribute, separator=nil)
|
910
|
+
super(nil, attribute, separator)
|
911
|
+
end
|
912
|
+
|
913
|
+
def condition(options={})
|
914
|
+
"tags.value = ?"
|
915
|
+
end
|
916
|
+
|
917
|
+
def condition_tables(options={})
|
918
|
+
[:tags]
|
919
|
+
end
|
920
|
+
|
921
|
+
def condition_values(options={})
|
922
|
+
[attribute]
|
923
|
+
end
|
924
|
+
|
925
|
+
def maybe_fallback(options={})
|
926
|
+
GlobTagValueNode.new(maybe_glob(attribute), separator)
|
927
|
+
end
|
928
|
+
end
|
929
|
+
|
930
|
+
class StringNode < StringExpressionNode
|
931
|
+
def initialize(identifier, separator=nil)
|
932
|
+
super(identifier, nil, separator)
|
933
|
+
end
|
934
|
+
|
935
|
+
def condition(options={})
|
936
|
+
"hosts.name = ? OR tags.name = ? OR tags.value = ?"
|
937
|
+
end
|
938
|
+
|
939
|
+
def condition_tables(options={})
|
940
|
+
[:hosts, :tags]
|
941
|
+
end
|
942
|
+
|
943
|
+
def condition_values(options={})
|
944
|
+
[identifier, identifier, identifier]
|
945
|
+
end
|
946
|
+
|
947
|
+
def maybe_fallback(options={})
|
948
|
+
GlobNode.new(maybe_glob(identifier), separator)
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
class GlobExpressionNode < TagExpressionNode
|
725
953
|
def dump(options={})
|
726
954
|
data = {}
|
727
|
-
data[:identifier_glob] =
|
728
|
-
data[:separator] =
|
729
|
-
data[:attribute_glob] =
|
955
|
+
data[:identifier_glob] = identifier.to_s if identifier
|
956
|
+
data[:separator] = separator.to_s if separator
|
957
|
+
data[:attribute_glob] = attribute.to_s if attribute
|
730
958
|
data[:fallback] = @fallback.dump(options) if @fallback
|
731
959
|
data
|
732
960
|
end
|
733
961
|
end
|
734
962
|
|
735
|
-
class
|
963
|
+
class GlobHostNode < GlobExpressionNode
|
964
|
+
def initialize(attribute, separator=nil)
|
965
|
+
super("host", attribute, separator)
|
966
|
+
end
|
967
|
+
|
968
|
+
def condition(options={})
|
969
|
+
"LOWER(hosts.name) GLOB LOWER(?)"
|
970
|
+
end
|
971
|
+
|
972
|
+
def condition_tables(options={})
|
973
|
+
[:hosts]
|
974
|
+
end
|
975
|
+
|
976
|
+
def condition_values(options={})
|
977
|
+
[attribute]
|
978
|
+
end
|
979
|
+
|
980
|
+
def maybe_fallback(options={})
|
981
|
+
GlobHostNode.new(maybe_glob(attribute), separator)
|
982
|
+
end
|
983
|
+
end
|
984
|
+
|
985
|
+
class GlobTagNode < GlobExpressionNode
|
736
986
|
def initialize(identifier, attribute, separator=nil)
|
737
|
-
identifier = identifier.sub(%r{\A/(.*)/\z}) { $1 } if identifier
|
738
|
-
attribute = attribute.sub(%r{\A/(.*)/\z}) { $1 } if attribute
|
739
987
|
super(identifier, attribute, separator)
|
740
988
|
end
|
741
989
|
|
742
|
-
def
|
743
|
-
|
744
|
-
if attribute?
|
745
|
-
case identifier
|
746
|
-
when /\Ahost\z/i
|
747
|
-
q = "SELECT hosts.id AS host_id FROM hosts " \
|
748
|
-
"WHERE hosts.name REGEXP ?;"
|
749
|
-
[q, [attribute]]
|
750
|
-
else
|
751
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
752
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
753
|
-
"WHERE tags.name REGEXP ? AND tags.value REGEXP ?;"
|
754
|
-
[q, [identifier, attribute]]
|
755
|
-
end
|
756
|
-
else
|
757
|
-
if separator?
|
758
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
759
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
760
|
-
"WHERE tags.name REGEXP ?;"
|
761
|
-
[q, [identifier]]
|
762
|
-
else
|
763
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
764
|
-
"INNER JOIN hosts ON hosts_tags.host_id = hosts.id " \
|
765
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
766
|
-
"WHERE hosts.name REGEXP ? OR tags.name REGEXP ? OR tags.value REGEXP ?;"
|
767
|
-
[q, [identifier, identifier, identifier]]
|
768
|
-
end
|
769
|
-
end
|
770
|
-
else
|
771
|
-
if attribute?
|
772
|
-
q = "SELECT DISTINCT hosts_tags.host_id FROM hosts_tags " \
|
773
|
-
"INNER JOIN tags ON hosts_tags.tag_id = tags.id " \
|
774
|
-
"WHERE tags.value REGEXP ?;"
|
775
|
-
[q, [attribute]]
|
776
|
-
else
|
777
|
-
nil
|
778
|
-
end
|
779
|
-
end
|
990
|
+
def condition(options={})
|
991
|
+
"LOWER(tags.name) GLOB LOWER(?) AND LOWER(tags.value) GLOB LOWER(?)"
|
780
992
|
end
|
781
993
|
|
782
|
-
def
|
783
|
-
|
994
|
+
def condition_tables(options={})
|
995
|
+
[:tags]
|
996
|
+
end
|
997
|
+
|
998
|
+
def condition_values(options={})
|
999
|
+
[identifier, attribute]
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
def maybe_fallback(options={})
|
1003
|
+
GlobTagNode.new(maybe_glob(identifier), maybe_glob(attribute), separator)
|
1004
|
+
end
|
1005
|
+
end
|
1006
|
+
|
1007
|
+
class GlobTagNameNode < GlobExpressionNode
|
1008
|
+
def initialize(identifier, separator=nil)
|
1009
|
+
super(identifier, nil, separator)
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
def condition(options={})
|
1013
|
+
"LOWER(tags.name) GLOB LOWER(?)"
|
1014
|
+
end
|
1015
|
+
|
1016
|
+
def condition_tables(options={})
|
1017
|
+
[:tags]
|
784
1018
|
end
|
785
1019
|
|
1020
|
+
def condition_values(options={})
|
1021
|
+
[identifier]
|
1022
|
+
end
|
1023
|
+
|
1024
|
+
def maybe_fallback(options={})
|
1025
|
+
GlobTagNameNode.new(maybe_glob(identifier), separator)
|
1026
|
+
end
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
class GlobTagValueNode < GlobExpressionNode
|
1030
|
+
def initialize(attribute, separator=nil)
|
1031
|
+
super(nil, attribute, separator)
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
def condition(options={})
|
1035
|
+
"LOWER(tags.value) GLOB LOWER(?)"
|
1036
|
+
end
|
1037
|
+
|
1038
|
+
def condition_tables(options={})
|
1039
|
+
[:tags]
|
1040
|
+
end
|
1041
|
+
|
1042
|
+
def condition_values(options={})
|
1043
|
+
[attribute]
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def maybe_fallback(options={})
|
1047
|
+
GlobTagValueNode.new(maybe_glob(attribute), separator)
|
1048
|
+
end
|
1049
|
+
end
|
1050
|
+
|
1051
|
+
class GlobNode < GlobExpressionNode
|
1052
|
+
def initialize(identifier, separator=nil)
|
1053
|
+
super(identifier, nil, separator)
|
1054
|
+
end
|
1055
|
+
|
1056
|
+
def condition(options={})
|
1057
|
+
"LOWER(hosts.name) GLOB LOWER(?) OR LOWER(tags.name) GLOB LOWER(?) OR LOWER(tags.value) GLOB LOWER(?)"
|
1058
|
+
end
|
1059
|
+
|
1060
|
+
def condition_tables(options={})
|
1061
|
+
[:hosts, :tags]
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def condition_values(options={})
|
1065
|
+
[identifier, identifier, identifier]
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def maybe_fallback(options={})
|
1069
|
+
GlobNode.new(maybe_glob(identifier), separator)
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1073
|
+
class RegexpExpressionNode < TagExpressionNode
|
786
1074
|
def dump(options={})
|
787
1075
|
data = {}
|
788
|
-
data[:identifier_regexp] =
|
789
|
-
data[:separator] =
|
790
|
-
data[:attribute_regexp] =
|
1076
|
+
data[:identifier_regexp] = identifier.to_s if identifier
|
1077
|
+
data[:separator] = separator.to_s if separator
|
1078
|
+
data[:attribute_regexp] = attribute.to_s if attribute
|
791
1079
|
data[:fallback] = @fallback.dump(options) if @fallback
|
792
1080
|
data
|
793
1081
|
end
|
794
1082
|
end
|
1083
|
+
|
1084
|
+
class RegexpHostNode < RegexpExpressionNode
|
1085
|
+
def initialize(attribute, separator=nil)
|
1086
|
+
super("host", attribute, separator)
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
def condition(options={})
|
1090
|
+
"hosts.name REGEXP ?"
|
1091
|
+
end
|
1092
|
+
|
1093
|
+
def condition_tables(options={})
|
1094
|
+
[:hosts]
|
1095
|
+
end
|
1096
|
+
|
1097
|
+
def condition_values(options={})
|
1098
|
+
[attribute]
|
1099
|
+
end
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
class RegexpTagNode < RegexpExpressionNode
|
1103
|
+
def initialize(identifier, attribute, separator=nil)
|
1104
|
+
super(identifier, attribute, separator)
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
def condition(options={})
|
1108
|
+
"tags.name REGEXP ? AND tags.value REGEXP ?"
|
1109
|
+
end
|
1110
|
+
|
1111
|
+
def condition_tables(options={})
|
1112
|
+
[:tags]
|
1113
|
+
end
|
1114
|
+
|
1115
|
+
def condition_values(options={})
|
1116
|
+
[identifier, attribute]
|
1117
|
+
end
|
1118
|
+
end
|
1119
|
+
|
1120
|
+
class RegexpTagNameNode < RegexpExpressionNode
|
1121
|
+
def initialize(identifier, separator=nil)
|
1122
|
+
super(identifier, nil, separator)
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
def condition(options={})
|
1126
|
+
"tags.name REGEXP ?"
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
def condition_tables(options={})
|
1130
|
+
[:tags]
|
1131
|
+
end
|
1132
|
+
|
1133
|
+
def condition_values(options={})
|
1134
|
+
[identifier]
|
1135
|
+
end
|
1136
|
+
end
|
1137
|
+
|
1138
|
+
class RegexpTagValueNode < RegexpExpressionNode
|
1139
|
+
def initialize(attribute, separator=nil)
|
1140
|
+
super(nil, attribute, separator)
|
1141
|
+
end
|
1142
|
+
|
1143
|
+
def condition(options={})
|
1144
|
+
"tags.value REGEXP ?"
|
1145
|
+
end
|
1146
|
+
|
1147
|
+
def condition_tables(options={})
|
1148
|
+
[:tags]
|
1149
|
+
end
|
1150
|
+
|
1151
|
+
def condition_values(options={})
|
1152
|
+
[attribute]
|
1153
|
+
end
|
1154
|
+
end
|
1155
|
+
|
1156
|
+
class RegexpNode < RegexpExpressionNode
|
1157
|
+
def initialize(identifier, separator=nil)
|
1158
|
+
super(identifier, separator)
|
1159
|
+
end
|
1160
|
+
|
1161
|
+
def condition(options={})
|
1162
|
+
"hosts.name REGEXP ? OR tags.name REGEXP ? OR tags.value REGEXP ?"
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
def condition_tables(options={})
|
1166
|
+
[:hosts, :tags]
|
1167
|
+
end
|
1168
|
+
|
1169
|
+
def condition_values(options={})
|
1170
|
+
[identifier, identifier, identifier]
|
1171
|
+
end
|
1172
|
+
end
|
795
1173
|
end
|
796
1174
|
end
|
797
1175
|
end
|