bud 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +23 -0
- data/bin/budlabel +63 -0
- data/bin/budtimelines +1 -1
- data/docs/cheat.md +1 -1
- data/docs/getstarted.md +8 -8
- data/examples/chat/README.md +2 -0
- data/examples/chat/chat.rb +3 -2
- data/examples/chat/chat_protocol.rb +1 -1
- data/examples/chat/chat_server.rb +3 -2
- data/lib/bud/aggs.rb +16 -2
- data/lib/bud/bud_meta.rb +19 -28
- data/lib/bud/collections.rb +157 -39
- data/lib/bud/depanalysis.rb +3 -4
- data/lib/bud/executor/elements.rb +62 -57
- data/lib/bud/executor/group.rb +35 -32
- data/lib/bud/executor/join.rb +0 -11
- data/lib/bud/graphs.rb +1 -1
- data/lib/bud/labeling/bloomgraph.rb +47 -0
- data/lib/bud/labeling/budplot_style.rb +53 -0
- data/lib/bud/labeling/labeling.rb +288 -0
- data/lib/bud/lattice-core.rb +563 -0
- data/lib/bud/lattice-lib.rb +367 -0
- data/lib/bud/monkeypatch.rb +18 -8
- data/lib/bud/rewrite.rb +314 -139
- data/lib/bud/server.rb +13 -2
- data/lib/bud/source.rb +34 -18
- data/lib/bud/state.rb +90 -1
- data/lib/bud/storage/zookeeper.rb +38 -33
- data/lib/bud/viz.rb +0 -1
- data/lib/bud.rb +55 -15
- metadata +15 -8
data/lib/bud/depanalysis.rb
CHANGED
@@ -6,7 +6,7 @@ class DepAnalysis #:nodoc: all
|
|
6
6
|
|
7
7
|
state do
|
8
8
|
# Data inserted by client, usually from t_depends and t_provides
|
9
|
-
scratch :depends, [:lhs, :op, :body, :neg]
|
9
|
+
scratch :depends, [:lhs, :op, :body, :neg, :in_body]
|
10
10
|
scratch :providing, [:pred, :input]
|
11
11
|
|
12
12
|
# Intermediate state
|
@@ -36,7 +36,7 @@ class DepAnalysis #:nodoc: all
|
|
36
36
|
|
37
37
|
cycle <= depends_tc do |d|
|
38
38
|
if d.lhs == d.body
|
39
|
-
unless
|
39
|
+
unless d.neg and !d.temporal
|
40
40
|
[d.lhs, d.via, d.neg, d.temporal]
|
41
41
|
end
|
42
42
|
end
|
@@ -60,11 +60,10 @@ class DepAnalysis #:nodoc: all
|
|
60
60
|
[p.pred, true]
|
61
61
|
end
|
62
62
|
else
|
63
|
-
unless depends_tc.map{|
|
63
|
+
unless depends_tc.map{|dt| dt.lhs if dt.lhs != dt.body}.include? p.pred
|
64
64
|
[p.pred, false]
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
69
|
end
|
70
|
-
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'set'
|
2
1
|
require 'bud/collections'
|
3
2
|
|
4
3
|
module Bud
|
@@ -13,7 +12,7 @@ module Bud
|
|
13
12
|
class PushElement < BudCollection
|
14
13
|
attr_accessor :rescan, :invalidated
|
15
14
|
attr_accessor :elem_name
|
16
|
-
attr_reader :found_delta, :
|
15
|
+
attr_reader :found_delta, :wired_by, :outputs
|
17
16
|
|
18
17
|
def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
|
19
18
|
super(name_in, bud_instance, given_schema, defer_schema)
|
@@ -25,7 +24,6 @@ module Bud
|
|
25
24
|
@wired_by = []
|
26
25
|
@elem_name = name_in
|
27
26
|
@found_delta = false
|
28
|
-
@refcount = 1
|
29
27
|
@collection_name = collection_name
|
30
28
|
@invalidated = true
|
31
29
|
@rescan = true
|
@@ -58,6 +56,8 @@ module Bud
|
|
58
56
|
print "#{next_accum} "
|
59
57
|
if o.class <= Bud::BudCollection
|
60
58
|
puts "#{(o.object_id*2).to_s(16)}: #{o.qualified_tabname} (#{o.class})"
|
59
|
+
elsif o.class <= Bud::LatticeWrapper
|
60
|
+
puts "#{o.inspect}"
|
61
61
|
else
|
62
62
|
puts "#{(o.object_id*2).to_s(16)}: (#{o.class.name})"
|
63
63
|
end
|
@@ -83,17 +83,15 @@ module Bud
|
|
83
83
|
|
84
84
|
case kind
|
85
85
|
when :output
|
86
|
-
raise Bud::Error unless element.respond_to? :insert
|
87
86
|
@outputs << element
|
88
87
|
when :pending
|
89
|
-
raise Bud::Error unless element.respond_to? :pending_merge
|
90
88
|
@pendings << element
|
91
89
|
when :delete
|
92
|
-
raise Bud::Error unless element.respond_to? :pending_delete
|
93
90
|
@deletes << element
|
94
91
|
when :delete_by_key
|
95
|
-
raise Bud::Error unless element.respond_to? :pending_delete_keys
|
96
92
|
@delete_keys << element
|
93
|
+
else
|
94
|
+
raise Bud::Error, "unrecognized wiring kind: #{kind}"
|
97
95
|
end
|
98
96
|
|
99
97
|
element.wired_by << self if element.respond_to? :wired_by
|
@@ -116,27 +114,36 @@ module Bud
|
|
116
114
|
end
|
117
115
|
|
118
116
|
def push_out(item, do_block=true)
|
119
|
-
if item
|
120
|
-
|
121
|
-
|
122
|
-
|
117
|
+
return if item.nil?
|
118
|
+
|
119
|
+
if do_block && @blk
|
120
|
+
item = item.to_a if @blk.arity > 1
|
121
|
+
item = @blk.call item
|
122
|
+
return if item.nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
@outputs.each do |ou|
|
126
|
+
if ou.class <= Bud::PushElement
|
127
|
+
ou.insert(item, self)
|
128
|
+
elsif ou.class <= Bud::BudCollection
|
129
|
+
ou.do_insert(item, ou.new_delta)
|
130
|
+
elsif ou.class <= Bud::LatticeWrapper
|
131
|
+
ou.insert(item, self)
|
132
|
+
else
|
133
|
+
raise Bud::Error, "expected output target: #{ou.class}"
|
123
134
|
end
|
135
|
+
end
|
124
136
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
ou.insert(item, self)
|
129
|
-
elsif ou.class <= Bud::BudCollection
|
130
|
-
ou.do_insert(item, ou.new_delta)
|
131
|
-
else
|
132
|
-
raise Bud::Error, "expected either a PushElement or a BudCollection"
|
133
|
-
end
|
134
|
-
end
|
137
|
+
# for the following, o is a BudCollection
|
138
|
+
@deletes.each{|o| o.pending_delete([item])}
|
139
|
+
@delete_keys.each{|o| o.pending_delete_keys([item])}
|
135
140
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
141
|
+
# o is a LatticeWrapper or a BudCollection
|
142
|
+
@pendings.each do |o|
|
143
|
+
if o.class <= Bud::LatticeWrapper
|
144
|
+
o <+ item
|
145
|
+
else
|
146
|
+
o.pending_merge([item])
|
140
147
|
end
|
141
148
|
end
|
142
149
|
end
|
@@ -213,34 +220,30 @@ module Bud
|
|
213
220
|
@collection_name)
|
214
221
|
elem.set_block(&blk)
|
215
222
|
self.wire_to(elem)
|
216
|
-
toplevel.push_elems[[self.object_id, :
|
223
|
+
toplevel.push_elems[[self.object_id, :each_with_index, blk]] = elem
|
217
224
|
end
|
218
225
|
|
219
226
|
def join(elem2, &blk)
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
toplevel.push_joins[toplevel.this_stratum] << join
|
229
|
-
# else
|
230
|
-
# cached.refcount += 1
|
231
|
-
# end
|
232
|
-
return toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]]
|
227
|
+
elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
|
228
|
+
toplevel = @bud_instance.toplevel
|
229
|
+
join = Bud::PushSHJoin.new([self, elem2], toplevel.this_rule_context, [])
|
230
|
+
self.wire_to(join)
|
231
|
+
elem2.wire_to(join)
|
232
|
+
toplevel.push_elems[[self.object_id, :join, [self, elem2], toplevel, blk]] = join
|
233
|
+
toplevel.push_joins[toplevel.this_stratum] << join
|
234
|
+
return join
|
233
235
|
end
|
234
236
|
def *(elem2, &blk)
|
235
237
|
join(elem2, &blk)
|
236
238
|
end
|
237
239
|
|
238
|
-
def notin(elem2, preds
|
240
|
+
def notin(elem2, *preds, &blk)
|
241
|
+
elem2 = elem2.to_push_elem unless elem2.kind_of? PushElement
|
239
242
|
toplevel = @bud_instance.toplevel
|
240
243
|
notin_elem = Bud::PushNotIn.new([self, elem2], toplevel.this_rule_context, preds, &blk)
|
241
244
|
self.wire_to(notin_elem)
|
242
245
|
elem2.wire_to(notin_elem)
|
243
|
-
toplevel.push_elems[[self.object_id, :notin,
|
246
|
+
toplevel.push_elems[[self.object_id, :notin, [self, elem2], toplevel, blk]] = notin_elem
|
244
247
|
return notin_elem
|
245
248
|
end
|
246
249
|
|
@@ -280,14 +283,12 @@ module Bud
|
|
280
283
|
the_schema = { keynames => aggcols }
|
281
284
|
end
|
282
285
|
|
283
|
-
aggpairs = aggpairs
|
286
|
+
aggpairs = prep_aggpairs(aggpairs)
|
284
287
|
toplevel = @bud_instance.toplevel
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
# end
|
290
|
-
# toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]]
|
288
|
+
g = Bud::PushGroup.new('grp'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
|
289
|
+
@collection_name, keycols, aggpairs, the_schema, &blk)
|
290
|
+
self.wire_to(g)
|
291
|
+
toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
|
291
292
|
return g
|
292
293
|
end
|
293
294
|
|
@@ -305,19 +306,17 @@ module Bud
|
|
305
306
|
end
|
306
307
|
end
|
307
308
|
aggpairs = [[agg, collection]]
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
# end
|
313
|
-
# return toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]]
|
309
|
+
aa = Bud::PushArgAgg.new('argagg'+Time.new.tv_usec.to_s, toplevel.this_rule_context,
|
310
|
+
@collection_name, gbkey_cols, aggpairs, schema, &blk)
|
311
|
+
self.wire_to(aa)
|
312
|
+
toplevel.push_elems[[self.object_id, :argagg, gbkey_cols, aggpairs, blk]] = aa
|
314
313
|
return aa
|
315
314
|
end
|
316
315
|
def argmax(gbcols, col, &blk)
|
317
|
-
argagg(gbcols,
|
316
|
+
argagg(:max, gbcols, col, &blk)
|
318
317
|
end
|
319
318
|
def argmin(gbcols, col, &blk)
|
320
|
-
argagg(gbcols,
|
319
|
+
argagg(:min, gbcols, col, &blk)
|
321
320
|
end
|
322
321
|
def sort(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
323
322
|
elem = Bud::PushSort.new(name, bud_instance, the_schema, &blk)
|
@@ -447,6 +446,7 @@ module Bud
|
|
447
446
|
class ScannerElement < PushElement
|
448
447
|
attr_reader :collection
|
449
448
|
attr_reader :rescan_set, :invalidate_set
|
449
|
+
attr_accessor :force_rescan
|
450
450
|
|
451
451
|
def initialize(elem_name, bud_instance, collection_in,
|
452
452
|
the_schema=collection_in.schema, &blk)
|
@@ -454,6 +454,7 @@ module Bud
|
|
454
454
|
@collection = collection_in
|
455
455
|
@rescan_set = []
|
456
456
|
@invalidate_set = []
|
457
|
+
@force_rescan = false
|
457
458
|
end
|
458
459
|
|
459
460
|
def rescan
|
@@ -485,7 +486,11 @@ module Bud
|
|
485
486
|
end
|
486
487
|
|
487
488
|
def scan(first_iter)
|
488
|
-
if
|
489
|
+
if @force_rescan
|
490
|
+
# Scan entire storage
|
491
|
+
@collection.each_raw {|item| push_out(item)}
|
492
|
+
@force_rescan = false
|
493
|
+
elsif first_iter
|
489
494
|
if rescan
|
490
495
|
# Scan entire storage
|
491
496
|
@collection.each_raw {|item| push_out(item)}
|
data/lib/bud/executor/group.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'bud/executor/elements'
|
2
|
-
require 'set'
|
3
2
|
|
4
3
|
module Bud
|
5
4
|
class PushGroup < PushStatefulElement
|
@@ -10,21 +9,25 @@ module Bud
|
|
10
9
|
else
|
11
10
|
@keys = keys_in.map{|k| k[1]}
|
12
11
|
end
|
13
|
-
# An aggpair is an array: [agg class instance,
|
14
|
-
#
|
15
|
-
@aggpairs = aggpairs_in.map
|
12
|
+
# An aggpair is an array: [agg class instance, array of indexes of input
|
13
|
+
# agg input columns]. The second field is nil for Count.
|
14
|
+
@aggpairs = aggpairs_in.map do |ap|
|
15
|
+
agg, *rest = ap
|
16
|
+
if rest.empty?
|
17
|
+
[agg, nil]
|
18
|
+
else
|
19
|
+
[agg, rest.map {|r| r[1]}]
|
20
|
+
end
|
21
|
+
end
|
16
22
|
@groups = {}
|
17
23
|
|
18
24
|
# Check whether we need to eliminate duplicates from our input (we might
|
19
25
|
# see duplicates because of the rescan/invalidation logic, as well as
|
20
26
|
# because we don't do duplicate elimination on the output of a projection
|
21
27
|
# operator). We don't need to dupelim if all the args are exemplary.
|
22
|
-
@elim_dups = @aggpairs.any? {|
|
23
|
-
if @elim_dups
|
24
|
-
@input_cache = Set.new
|
25
|
-
end
|
28
|
+
@elim_dups = @aggpairs.any? {|ap| not ap[0].kind_of? ArgExemplary}
|
29
|
+
@input_cache = Set.new if @elim_dups
|
26
30
|
|
27
|
-
@seen_new_data = false
|
28
31
|
super(elem_name, bud_instance, collection_name, schema_in, &blk)
|
29
32
|
end
|
30
33
|
|
@@ -34,19 +37,25 @@ module Bud
|
|
34
37
|
@input_cache << item
|
35
38
|
end
|
36
39
|
|
37
|
-
@seen_new_data = true
|
38
40
|
key = item.values_at(*@keys)
|
39
41
|
group_state = @groups[key]
|
40
42
|
if group_state.nil?
|
41
43
|
@groups[key] = @aggpairs.map do |ap|
|
42
|
-
|
43
|
-
|
44
|
+
if ap[1].nil?
|
45
|
+
ap[0].init(item)
|
46
|
+
else
|
47
|
+
ap[0].init(*item.values_at(*ap[1]))
|
48
|
+
end
|
44
49
|
end
|
45
50
|
else
|
46
51
|
@aggpairs.each_with_index do |ap, agg_ix|
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
state_val = group_state[agg_ix]
|
53
|
+
if ap[1].nil?
|
54
|
+
trans_rv = ap[0].trans(state_val, item)
|
55
|
+
else
|
56
|
+
trans_rv = ap[0].trans(state_val, *item.values_at(*ap[1]))
|
57
|
+
end
|
58
|
+
group_state[agg_ix] = trans_rv[0]
|
50
59
|
end
|
51
60
|
end
|
52
61
|
end
|
@@ -62,14 +71,12 @@ module Bud
|
|
62
71
|
puts "#{self.class}/#{self.tabname} invalidated" if $BUD_DEBUG
|
63
72
|
@groups.clear
|
64
73
|
@input_cache.clear if @elim_dups
|
65
|
-
@seen_new_data = false
|
66
74
|
end
|
67
75
|
|
68
76
|
def flush
|
69
|
-
#
|
70
|
-
|
71
|
-
|
72
|
-
@seen_new_data = false
|
77
|
+
# Don't emit fresh output unless a rescan is needed
|
78
|
+
return unless @rescan
|
79
|
+
@rescan = false
|
73
80
|
|
74
81
|
@groups.each do |key, group_state|
|
75
82
|
rv = key.clone
|
@@ -87,7 +94,6 @@ module Bud
|
|
87
94
|
raise Bud::Error, "multiple aggpairs #{aggpairs_in.map{|a| a.class.name}} in ArgAgg; only one allowed"
|
88
95
|
end
|
89
96
|
super(elem_name, bud_instance, collection_name, keys_in, aggpairs_in, schema_in, &blk)
|
90
|
-
@agg, @aggcol = @aggpairs[0]
|
91
97
|
@winners = {}
|
92
98
|
end
|
93
99
|
|
@@ -101,18 +107,16 @@ module Bud
|
|
101
107
|
key = @keys.map{|k| item[k]}
|
102
108
|
group_state = @groups[key]
|
103
109
|
if group_state.nil?
|
104
|
-
@seen_new_data = true
|
105
110
|
@groups[key] = @aggpairs.map do |ap|
|
106
111
|
@winners[key] = [item]
|
107
|
-
|
108
|
-
ap[0].init(
|
112
|
+
input_vals = item.values_at(*ap[1])
|
113
|
+
ap[0].init(*input_vals)
|
109
114
|
end
|
110
115
|
else
|
111
116
|
@aggpairs.each_with_index do |ap, agg_ix|
|
112
|
-
|
113
|
-
state_val, flag, *rest = ap[0].trans(group_state[agg_ix],
|
117
|
+
input_vals = item.values_at(*ap[1])
|
118
|
+
state_val, flag, *rest = ap[0].trans(group_state[agg_ix], *input_vals)
|
114
119
|
group_state[agg_ix] = state_val
|
115
|
-
@seen_new_data = true unless flag == :ignore
|
116
120
|
|
117
121
|
case flag
|
118
122
|
when :ignore
|
@@ -133,14 +137,13 @@ module Bud
|
|
133
137
|
end
|
134
138
|
|
135
139
|
def flush
|
136
|
-
#
|
137
|
-
|
138
|
-
|
139
|
-
@seen_new_data = false
|
140
|
+
# Don't emit fresh output unless a rescan is needed
|
141
|
+
return unless @rescan
|
142
|
+
@rescan = false
|
140
143
|
|
141
144
|
@groups.each_key do |g|
|
142
145
|
@winners[g].each do |t|
|
143
|
-
push_out(t
|
146
|
+
push_out(t)
|
144
147
|
end
|
145
148
|
end
|
146
149
|
end
|
data/lib/bud/executor/join.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'bud/executor/elements'
|
2
|
-
require 'set'
|
3
2
|
|
4
3
|
$EMPTY = []
|
5
4
|
module Bud
|
@@ -58,12 +57,6 @@ module Bud
|
|
58
57
|
super(@tabname, @bud_instance, nil, @cols)
|
59
58
|
end
|
60
59
|
|
61
|
-
public
|
62
|
-
def copy_on_write
|
63
|
-
@refcount -= 1
|
64
|
-
return Bud::PushSHJoin.new(@all_rels_below, @bud_instance, [])
|
65
|
-
end
|
66
|
-
|
67
60
|
public
|
68
61
|
def state_id # :nodoc: all
|
69
62
|
object_id
|
@@ -366,10 +359,6 @@ module Bud
|
|
366
359
|
# :col1 => :col2 (same as lefttable.col1 => righttable.col2)
|
367
360
|
public
|
368
361
|
def pairs(*preds, &blk)
|
369
|
-
## XXX Need to do this for all the join modifiers
|
370
|
-
unless @refcount == 1
|
371
|
-
return self.copy_on_write.pairs(preds, blk)
|
372
|
-
end
|
373
362
|
@origpreds = preds
|
374
363
|
setup_preds(preds) unless preds.empty?
|
375
364
|
# given new preds, the state for the join will be different. set it up again.
|
data/lib/bud/graphs.rb
CHANGED
@@ -75,7 +75,7 @@ class GraphGen #:nodoc: all
|
|
75
75
|
# its name is "CYC" + concat(sort(predicate names))
|
76
76
|
depends.each do |d|
|
77
77
|
# b/c bud_obj was pruned before serialization...
|
78
|
-
|
78
|
+
bud_obj, rule_id, lhs, op, body, nm, in_body = d.to_a
|
79
79
|
head = lhs
|
80
80
|
body = body
|
81
81
|
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
require 'graphviz'
|
4
|
+
|
5
|
+
# A simple interface between graphviz and bud
|
6
|
+
module BudGraph
|
7
|
+
state do
|
8
|
+
interface input, :bnode, [:name] => [:meta]
|
9
|
+
interface input, :bedge, [:from, :to, :meta]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module BloomGraph
|
14
|
+
include BudGraph
|
15
|
+
|
16
|
+
state do
|
17
|
+
table :nodes, bnode.schema
|
18
|
+
table :edges, bedge.schema
|
19
|
+
end
|
20
|
+
|
21
|
+
bloom do
|
22
|
+
nodes <= bnode
|
23
|
+
edges <= bedge
|
24
|
+
end
|
25
|
+
|
26
|
+
def finish(ignore, name, fmt=:pdf)
|
27
|
+
it = ignore.to_set
|
28
|
+
tick
|
29
|
+
nodes.to_a.each do |n|
|
30
|
+
unless it.include? n.name.to_sym
|
31
|
+
@graph.add_nodes(n.name, n.meta)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
edges.to_a.each do |e|
|
36
|
+
unless it.include? e.from.to_sym or it.include? e.to.to_sym
|
37
|
+
@graph.add_edges(e.from, e.to, e.meta)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
@graph.output(fmt => name)
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(opts={:type => :digraph})
|
44
|
+
@graph = GraphViz.new(:G, opts)
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|