bud 0.0.5 → 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/budplot +37 -12
- data/bin/budtimelines +20 -22
- data/bin/budvis +1 -1
- data/docs/cheat.md +54 -17
- data/docs/operational.md +1 -1
- data/lib/bud.rb +31 -18
- data/lib/bud/bud_meta.rb +7 -7
- data/lib/bud/bust/bust.rb +2 -2
- data/lib/bud/collections.rb +80 -68
- data/lib/bud/deploy/ec2deploy.rb +1 -1
- data/lib/bud/graphs.rb +9 -11
- data/lib/bud/joins.rb +29 -20
- data/lib/bud/monkeypatch.rb +8 -2
- data/lib/bud/rebl.rb +29 -13
- data/lib/bud/rewrite.rb +40 -39
- data/lib/bud/server.rb +1 -1
- data/lib/bud/state.rb +3 -3
- data/lib/bud/storage/dbm.rb +4 -4
- data/lib/bud/storage/tokyocabinet.rb +4 -4
- data/lib/bud/storage/zookeeper.rb +3 -3
- data/lib/bud/stratify.rb +6 -3
- data/lib/bud/viz.rb +1 -1
- data/lib/bud/viz_util.rb +11 -7
- metadata +10 -8
data/lib/bud/bud_meta.rb
CHANGED
@@ -148,8 +148,8 @@ class BudMeta #:nodoc: all
|
|
148
148
|
|
149
149
|
# Check that LHS references a named collection
|
150
150
|
return n if lhs.nil? or lhs.sexp_type != :call
|
151
|
-
lhs_name = lhs[2]
|
152
|
-
unless @bud_instance.tables.has_key? lhs_name
|
151
|
+
lhs_name = lhs[2].to_sym
|
152
|
+
unless @bud_instance.tables.has_key? lhs_name
|
153
153
|
return [n, "Table does not exist: '#{lhs_name}'"]
|
154
154
|
end
|
155
155
|
|
@@ -182,11 +182,11 @@ class BudMeta #:nodoc: all
|
|
182
182
|
strat.tick_internal
|
183
183
|
|
184
184
|
# Copy computed data back into Bud runtime
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
185
|
+
@bud_instance.stratum_collection_map = []
|
186
|
+
strat.stratum.each do |s|
|
187
|
+
@bud_instance.t_stratum << s
|
188
|
+
@bud_instance.stratum_collection_map[s[1]] ||= []
|
189
|
+
@bud_instance.stratum_collection_map[s[1]] << s[0].to_sym
|
190
190
|
end
|
191
191
|
strat.depends_tc.each {|d| @bud_instance.t_depends_tc << d}
|
192
192
|
strat.cycle.each {|c| @bud_instance.t_cycle << c}
|
data/lib/bud/bust/bust.rb
CHANGED
@@ -19,7 +19,7 @@ module Bust
|
|
19
19
|
# copied from peter's code; this should probably be in the Bud runtime or in
|
20
20
|
# some meta module
|
21
21
|
@tables.each do |t|
|
22
|
-
t_table_schema << [t[0], t[1].
|
22
|
+
t_table_schema << [t[0], t[1].cols.clone]
|
23
23
|
t_table_info << [t[0], t[1].class.to_s]
|
24
24
|
end
|
25
25
|
|
@@ -77,7 +77,7 @@ module Bust
|
|
77
77
|
# instantiate a new tuple
|
78
78
|
tuple_to_insert = []
|
79
79
|
@body.each do |k, v|
|
80
|
-
index = (eval "@bud." + table_name).
|
80
|
+
index = (eval "@bud." + table_name).cols.find_index(k.to_sym)
|
81
81
|
for i in (tuple_to_insert.size..index)
|
82
82
|
tuple_to_insert << nil
|
83
83
|
end
|
data/lib/bud/collections.rb
CHANGED
@@ -14,8 +14,10 @@ module Bud
|
|
14
14
|
class BudCollection
|
15
15
|
include Enumerable
|
16
16
|
|
17
|
-
|
18
|
-
|
17
|
+
# This needs to be an accessor to allow REBL to update it after cloning a
|
18
|
+
# Bud instance.
|
19
|
+
attr_accessor :bud_instance # :nodoc: all
|
20
|
+
attr_reader :cols, :key_cols, :tabname # :nodoc: all
|
19
21
|
attr_reader :storage, :delta, :new_delta, :pending # :nodoc: all
|
20
22
|
|
21
23
|
def initialize(name, bud_instance, given_schema=nil, defer_schema=false) # :nodoc: all
|
@@ -35,16 +37,26 @@ module Bud
|
|
35
37
|
private
|
36
38
|
def init_schema(given_schema)
|
37
39
|
given_schema ||= {[:key]=>[:val]}
|
40
|
+
|
41
|
+
# Check that no location specifiers appear in the schema. In the case of
|
42
|
+
# channels, the location specifier has already been stripped from the
|
43
|
+
# user-specified schema.
|
44
|
+
given_schema.each do |s|
|
45
|
+
if s.to_s.start_with? "@"
|
46
|
+
raise BudError, "illegal use of location specifier (@) in column #{s} of non-channel collection #{tabname}"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
38
50
|
@given_schema = given_schema
|
39
|
-
@
|
40
|
-
@key_colnums = key_cols.map {|k|
|
51
|
+
@cols, @key_cols = parse_schema(given_schema)
|
52
|
+
@key_colnums = key_cols.map {|k| @cols.index(k)}
|
41
53
|
setup_accessors
|
42
54
|
end
|
43
55
|
|
44
56
|
# The user-specified schema might come in two forms: a hash of Array =>
|
45
|
-
# Array (key_cols => remaining columns), or simply an Array of columns (if
|
46
|
-
# key_cols were specified). Return a pair: [list of columns
|
47
|
-
#
|
57
|
+
# Array (key_cols => remaining columns), or simply an Array of columns (if
|
58
|
+
# no key_cols were specified). Return a pair: [list of (all) columns, list
|
59
|
+
# of key columns]
|
48
60
|
private
|
49
61
|
def parse_schema(given_schema)
|
50
62
|
if given_schema.respond_to? :keys
|
@@ -56,17 +68,17 @@ module Bud
|
|
56
68
|
val_cols = []
|
57
69
|
end
|
58
70
|
|
59
|
-
|
60
|
-
|
61
|
-
if
|
62
|
-
raise BudError, "
|
71
|
+
cols = key_cols + val_cols
|
72
|
+
cols.each do |c|
|
73
|
+
if c.class != Symbol
|
74
|
+
raise BudError, "invalid schema element \"#{c}\", type \"#{c.class}\""
|
63
75
|
end
|
64
76
|
end
|
65
|
-
if
|
77
|
+
if cols.uniq.length < cols.length
|
66
78
|
raise BudError, "schema for #{tabname} contains duplicate names"
|
67
79
|
end
|
68
80
|
|
69
|
-
return [
|
81
|
+
return [cols, key_cols]
|
70
82
|
end
|
71
83
|
|
72
84
|
public
|
@@ -74,16 +86,18 @@ module Bud
|
|
74
86
|
self.class.new(tabname, bud_instance, @given_schema)
|
75
87
|
end
|
76
88
|
|
77
|
-
#
|
89
|
+
# produces the schema in a format that is useful as the schema specification for another table
|
78
90
|
public
|
79
|
-
def
|
80
|
-
@
|
91
|
+
def schema
|
92
|
+
return nil if @cols.nil?
|
93
|
+
return key_cols if val_cols.empty?
|
94
|
+
return { key_cols => val_cols }
|
81
95
|
end
|
82
96
|
|
83
|
-
#
|
97
|
+
# the columns of the collection's schema that are not part of the key
|
84
98
|
public
|
85
99
|
def val_cols # :nodoc: all
|
86
|
-
|
100
|
+
@cols - @key_cols
|
87
101
|
end
|
88
102
|
|
89
103
|
# define methods to turn 'table.col' into a [table,col] pair
|
@@ -91,7 +105,7 @@ module Bud
|
|
91
105
|
# j = join link, path, {link.to => path.from}
|
92
106
|
private
|
93
107
|
def setup_accessors
|
94
|
-
s = @
|
108
|
+
s = @cols
|
95
109
|
s.each do |colname|
|
96
110
|
reserved = eval "defined?(#{colname})"
|
97
111
|
unless (reserved.nil? or
|
@@ -129,7 +143,7 @@ module Bud
|
|
129
143
|
# generate a tuple with the schema of this collection and nil values in each attribute
|
130
144
|
public
|
131
145
|
def null_tuple
|
132
|
-
tuple_accessors(Array.new(@
|
146
|
+
tuple_accessors(Array.new(@cols.length))
|
133
147
|
end
|
134
148
|
|
135
149
|
# project the collection to its key attributes
|
@@ -141,13 +155,13 @@ module Bud
|
|
141
155
|
# project the collection to its non-key attributes
|
142
156
|
public
|
143
157
|
def values
|
144
|
-
self.map{|t| (self.key_cols.length..self.
|
158
|
+
self.map{|t| (self.key_cols.length..self.cols.length-1).map{|i| t[i]}}
|
145
159
|
end
|
146
160
|
|
147
161
|
# map each item in the collection into a string, suitable for placement in stdio
|
148
162
|
public
|
149
163
|
def inspected
|
150
|
-
self.map{|t|
|
164
|
+
[["#{@tabname}: [#{self.map{|t| "\n (#{t.map{|v| v.inspect}.join ", "})"}}]"]]
|
151
165
|
end
|
152
166
|
|
153
167
|
# akin to map, but modified for efficiency in Bloom statements
|
@@ -184,7 +198,7 @@ module Bud
|
|
184
198
|
bud_instance.metrics[:collections][{:addr=>addr, :tabname=>tabname, :strat_num=>strat_num, :rule_num=>rule_num}] ||= 0
|
185
199
|
bud_instance.metrics[:collections][{:addr=>addr, :tabname=>tabname, :strat_num=>strat_num, :rule_num=>rule_num}] += 1
|
186
200
|
end
|
187
|
-
|
201
|
+
|
188
202
|
private
|
189
203
|
def each_from(bufs, &block) # :nodoc: all
|
190
204
|
bufs.each do |b|
|
@@ -269,7 +283,7 @@ module Bud
|
|
269
283
|
private
|
270
284
|
def raise_pk_error(new_guy, old)
|
271
285
|
keycols = @key_colnums.map{|i| old[i]}
|
272
|
-
raise KeyConstraintError, "
|
286
|
+
raise KeyConstraintError, "key conflict inserting #{new_guy.inspect} into \"#{tabname}\": existing tuple #{old.inspect}, key_cols = #{keycols.inspect}"
|
273
287
|
end
|
274
288
|
|
275
289
|
private
|
@@ -281,15 +295,15 @@ module Bud
|
|
281
295
|
raise BudTypeError, "String value used as a fact inserted into \"#{tabname}\": #{o.inspect}"
|
282
296
|
end
|
283
297
|
|
284
|
-
if o.length <
|
298
|
+
if o.length < cols.length then
|
285
299
|
# if this tuple has too few fields, pad with nil's
|
286
300
|
old = o.clone
|
287
|
-
(o.length..
|
301
|
+
(o.length..cols.length-1).each{|i| o << nil}
|
288
302
|
# puts "in #{@tabname}, converted #{old.inspect} to #{o.inspect}"
|
289
|
-
elsif o.length >
|
303
|
+
elsif o.length > cols.length then
|
290
304
|
# if this tuple has more fields than usual, bundle up the
|
291
305
|
# extras into an array
|
292
|
-
o = (0..(
|
306
|
+
o = (0..(cols.length - 1)).map{|c| o[c]} << (cols.length..(o.length - 1)).map{|c| o[c]}
|
293
307
|
end
|
294
308
|
return o
|
295
309
|
end
|
@@ -322,18 +336,18 @@ module Bud
|
|
322
336
|
private
|
323
337
|
def check_enumerable(o)
|
324
338
|
unless o.nil? or o.class < Enumerable
|
325
|
-
raise BudTypeError, "
|
339
|
+
raise BudTypeError, "collection #{tabname} expected Enumerable value, not #{o.inspect} (class = #{o.class})"
|
326
340
|
end
|
327
341
|
end
|
328
342
|
|
329
343
|
# Assign self a schema, by hook or by crook. If +o+ is schemaless *and*
|
330
|
-
# empty, will leave @
|
344
|
+
# empty, will leave @cols as is.
|
331
345
|
private
|
332
346
|
def establish_schema(o)
|
333
347
|
# use o's schema if available
|
334
348
|
deduce_schema(o)
|
335
349
|
# else use arity of first non-nil tuple of o
|
336
|
-
if @
|
350
|
+
if @cols.nil?
|
337
351
|
o.each do |t|
|
338
352
|
next if t.nil?
|
339
353
|
fit_schema(t.size)
|
@@ -345,11 +359,11 @@ module Bud
|
|
345
359
|
# Copy over the schema from +o+ if available
|
346
360
|
private
|
347
361
|
def deduce_schema(o)
|
348
|
-
if @
|
362
|
+
if @cols.nil? and o.class <= Bud::BudCollection and not o.cols.nil?
|
349
363
|
# must have been initialized with defer_schema==true. take schema from rhs
|
350
|
-
init_schema(o.
|
364
|
+
init_schema(o.cols)
|
351
365
|
end
|
352
|
-
# if nothing available, leave @
|
366
|
+
# if nothing available, leave @cols unchanged
|
353
367
|
end
|
354
368
|
|
355
369
|
# manufacture schema of the form [:c0, :c1, ...] with width = +arity+
|
@@ -377,9 +391,9 @@ module Bud
|
|
377
391
|
public
|
378
392
|
def merge(o, buf=@new_delta) # :nodoc: all
|
379
393
|
unless o.nil?
|
380
|
-
o = o.uniq
|
394
|
+
o = o.uniq if o.respond_to?(:uniq)
|
381
395
|
check_enumerable(o)
|
382
|
-
establish_schema(o) if @
|
396
|
+
establish_schema(o) if @cols.nil?
|
383
397
|
|
384
398
|
# it's a pity that we are massaging the tuples that already exist in the head
|
385
399
|
o.each do |t|
|
@@ -402,7 +416,7 @@ module Bud
|
|
402
416
|
public
|
403
417
|
def pending_merge(o) # :nodoc: all
|
404
418
|
check_enumerable(o)
|
405
|
-
establish_schema(o) if @
|
419
|
+
establish_schema(o) if @cols.nil?
|
406
420
|
|
407
421
|
o.each {|i| do_insert(i, @pending)}
|
408
422
|
return self
|
@@ -412,7 +426,7 @@ module Bud
|
|
412
426
|
superator "<+" do |o|
|
413
427
|
pending_merge o
|
414
428
|
end
|
415
|
-
|
429
|
+
|
416
430
|
public
|
417
431
|
superator "<+-" do |o|
|
418
432
|
self <+ o
|
@@ -422,12 +436,12 @@ module Bud
|
|
422
436
|
end
|
423
437
|
end
|
424
438
|
end
|
425
|
-
|
426
|
-
public
|
439
|
+
|
440
|
+
public
|
427
441
|
superator "<-+" do |o|
|
428
442
|
self <+- o
|
429
443
|
end
|
430
|
-
|
444
|
+
|
431
445
|
# Called at the end of each timestep: prepare the collection for the next
|
432
446
|
# timestep.
|
433
447
|
public
|
@@ -494,11 +508,11 @@ module Bud
|
|
494
508
|
memo[pkey_cols][:agg], argflag = \
|
495
509
|
agg.send(:trans, memo[pkey_cols][:agg], p[colnum])
|
496
510
|
if argflag == :keep or agg.send(:tie, memo[pkey_cols][:agg], p[colnum])
|
497
|
-
memo[pkey_cols][:tups] << p
|
511
|
+
memo[pkey_cols][:tups] << p
|
498
512
|
elsif argflag == :replace
|
499
513
|
memo[pkey_cols][:tups] = [p]
|
500
514
|
elsif argflag.class <= Array and argflag[0] == :delete
|
501
|
-
memo[pkey_cols][:tups] -= argflag[1..-1]
|
515
|
+
memo[pkey_cols][:tups] -= argflag[1..-1]
|
502
516
|
end
|
503
517
|
end
|
504
518
|
memo
|
@@ -510,7 +524,7 @@ module Bud
|
|
510
524
|
tups.each do |k,v|
|
511
525
|
finalaggs[k] = agg.send(:final, v[:agg])
|
512
526
|
end
|
513
|
-
|
527
|
+
|
514
528
|
# and winnow the tups to match
|
515
529
|
finalaggs.each do |k,v|
|
516
530
|
tups[k][:tups].each do |t|
|
@@ -566,14 +580,12 @@ module Bud
|
|
566
580
|
def *(collection)
|
567
581
|
join([self, collection])
|
568
582
|
end
|
569
|
-
|
583
|
+
|
570
584
|
# AntiJoin
|
571
585
|
public
|
572
|
-
def notin(coll
|
573
|
-
|
574
|
-
|
575
|
-
return BudJoin.new([self,coll], @bud_instance).anti(*preds,&blk)
|
576
|
-
end
|
586
|
+
def notin(coll, *preds, &blk)
|
587
|
+
return BudJoin.new([self, coll], @bud_instance).anti(*preds, &blk)
|
588
|
+
end
|
577
589
|
|
578
590
|
# SQL-style grouping. first argument is an array of attributes to group by.
|
579
591
|
# Followed by a variable-length list of aggregates over attributes (e.g. +min(:x)+)
|
@@ -587,7 +599,7 @@ module Bud
|
|
587
599
|
elsif k[2] and k[2].class == Symbol
|
588
600
|
k[2]
|
589
601
|
else
|
590
|
-
raise Bud::CompileError, "
|
602
|
+
raise Bud::CompileError, "invalid grouping key"
|
591
603
|
end
|
592
604
|
end
|
593
605
|
aggcolsdups = aggpairs.map{|ap| ap[0].class.name.split("::").last}
|
@@ -671,16 +683,16 @@ module Bud
|
|
671
683
|
given_schema = Marshal.load(Marshal.dump(given_schema))
|
672
684
|
|
673
685
|
unless @is_loopback
|
674
|
-
|
675
|
-
spec_count =
|
686
|
+
the_cols, the_key_cols = parse_schema(given_schema)
|
687
|
+
spec_count = the_cols.count {|c| c.to_s.start_with? "@"}
|
676
688
|
if spec_count == 0
|
677
|
-
raise BudError, "
|
689
|
+
raise BudError, "missing location specifier for channel '#{name}'"
|
678
690
|
end
|
679
691
|
if spec_count > 1
|
680
|
-
raise BudError, "
|
692
|
+
raise BudError, "multiple location specifiers for channel '#{name}'"
|
681
693
|
end
|
682
694
|
|
683
|
-
the_val_cols =
|
695
|
+
the_val_cols = the_cols - the_key_cols
|
684
696
|
@locspec_idx = remove_at_sign!(the_key_cols)
|
685
697
|
if @locspec_idx.nil?
|
686
698
|
val_idx = remove_at_sign!(the_val_cols)
|
@@ -713,7 +725,7 @@ module Bud
|
|
713
725
|
lsplit[1] = lsplit[1].to_i
|
714
726
|
return lsplit
|
715
727
|
rescue Exception => e
|
716
|
-
raise BudError, "
|
728
|
+
raise BudError, "illegal location specifier in tuple #{t.inspect} for channel \"#{tabname}\": #{e.to_s}"
|
717
729
|
end
|
718
730
|
end
|
719
731
|
|
@@ -751,11 +763,11 @@ module Bud
|
|
751
763
|
def payloads
|
752
764
|
return self.pro if @is_loopback
|
753
765
|
|
754
|
-
if
|
766
|
+
if cols.size > 2
|
755
767
|
# bundle up each tuple's non-locspec fields into an array
|
756
768
|
retval = case @locspec_idx
|
757
769
|
when 0 then self.pro{|t| t[1..(t.size-1)]}
|
758
|
-
when (
|
770
|
+
when (cols.size - 1) then self.pro{|t| t[0..(t.size-2)]}
|
759
771
|
else self.pro{|t| t[0..(@locspec_idx-1)] + t[@locspec_idx+1..(t.size-1)]}
|
760
772
|
end
|
761
773
|
else
|
@@ -770,13 +782,13 @@ module Bud
|
|
770
782
|
end
|
771
783
|
|
772
784
|
superator "<+" do |o|
|
773
|
-
raise BudError, "
|
785
|
+
raise BudError, "illegal use of <+ with channel '#{@tabname}' on left"
|
774
786
|
end
|
775
787
|
|
776
788
|
undef merge
|
777
789
|
|
778
790
|
def <=(o)
|
779
|
-
raise BudError, "
|
791
|
+
raise BudError, "illegal use of <= with channel '#{@tabname}' on left"
|
780
792
|
end
|
781
793
|
end
|
782
794
|
|
@@ -837,7 +849,7 @@ module Bud
|
|
837
849
|
|
838
850
|
public
|
839
851
|
def <=(o) #:nodoc: all
|
840
|
-
raise BudError, "
|
852
|
+
raise BudError, "illegal use of <= with terminal '#{@tabname}' on left"
|
841
853
|
end
|
842
854
|
|
843
855
|
superator "<~" do |o|
|
@@ -855,19 +867,19 @@ module Bud
|
|
855
867
|
|
856
868
|
class BudPeriodic < BudCollection # :nodoc: all
|
857
869
|
def <=(o)
|
858
|
-
raise BudError, "
|
870
|
+
raise BudError, "illegal use of <= with periodic '#{tabname}' on left"
|
859
871
|
end
|
860
872
|
|
861
873
|
superator "<~" do |o|
|
862
|
-
raise BudError, "
|
874
|
+
raise BudError, "illegal use of <~ with periodic '#{tabname}' on left"
|
863
875
|
end
|
864
876
|
|
865
877
|
superator "<-" do |o|
|
866
|
-
raise BudError, "
|
878
|
+
raise BudError, "illegal use of <- with periodic '#{tabname}' on left"
|
867
879
|
end
|
868
880
|
|
869
881
|
superator "<+" do |o|
|
870
|
-
raise BudError, "
|
882
|
+
raise BudError, "illegal use of <+ with periodic '#{tabname}' on left"
|
871
883
|
end
|
872
884
|
end
|
873
885
|
|
@@ -907,11 +919,11 @@ module Bud
|
|
907
919
|
|
908
920
|
class BudReadOnly < BudScratch # :nodoc: all
|
909
921
|
superator "<+" do |o|
|
910
|
-
raise CompileError, "
|
922
|
+
raise CompileError, "illegal use of <+ with read-only collection '#{@tabname}' on left"
|
911
923
|
end
|
912
924
|
public
|
913
925
|
def merge(o) #:nodoc: all
|
914
|
-
raise CompileError, "
|
926
|
+
raise CompileError, "illegal use of <= with read-only collection '#{@tabname}' on left"
|
915
927
|
end
|
916
928
|
end
|
917
929
|
|
data/lib/bud/deploy/ec2deploy.rb
CHANGED
@@ -160,7 +160,7 @@ module EC2Deploy
|
|
160
160
|
# Update the Bud gem
|
161
161
|
channel = session.open_channel do |ch|
|
162
162
|
channel.request_pty do |_, success|
|
163
|
-
raise "
|
163
|
+
raise "couldn't open a PTY on #{t.node}" if !success
|
164
164
|
end
|
165
165
|
channel.exec("sudo gem update --no-ri --no-rdoc bud")
|
166
166
|
end
|