bud 0.0.5 → 0.0.6
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.
- 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
|