bud 0.0.8 → 0.1.0.pre1
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -10
- data/bin/budplot +1 -2
- data/docs/cheat.md +2 -15
- data/examples/basics/paths.rb +7 -7
- data/lib/bud/aggs.rb +15 -19
- data/lib/bud/bud_meta.rb +165 -77
- data/lib/bud/bust/bust.rb +11 -4
- data/lib/bud/collections.rb +643 -280
- data/lib/bud/depanalysis.rb +50 -25
- data/lib/bud/executor/elements.rb +592 -0
- data/lib/bud/executor/group.rb +104 -0
- data/lib/bud/executor/join.rb +638 -0
- data/lib/bud/graphs.rb +12 -11
- data/lib/bud/joins.rb +2 -1
- data/lib/bud/meta_algebra.rb +5 -4
- data/lib/bud/metrics.rb +9 -3
- data/lib/bud/monkeypatch.rb +131 -23
- data/lib/bud/rebl.rb +41 -28
- data/lib/bud/rewrite.rb +112 -440
- data/lib/bud/server.rb +3 -2
- data/lib/bud/source.rb +109 -0
- data/lib/bud/state.rb +16 -9
- data/lib/bud/storage/dbm.rb +62 -16
- data/lib/bud/storage/zookeeper.rb +2 -2
- data/lib/bud/viz.rb +8 -4
- data/lib/bud/viz_util.rb +10 -9
- data/lib/bud.rb +413 -199
- metadata +40 -55
- data/examples/deploy/tokenring-ec2.rb +0 -26
- data/examples/deploy/tokenring-fork.rb +0 -15
- data/examples/deploy/tokenring-thread.rb +0 -15
- data/examples/deploy/tokenring.rb +0 -47
- data/lib/bud/deploy/deployer.rb +0 -67
- data/lib/bud/deploy/ec2deploy.rb +0 -199
- data/lib/bud/deploy/forkdeploy.rb +0 -90
- data/lib/bud/deploy/threaddeploy.rb +0 -38
- data/lib/bud/storage/tokyocabinet.rb +0 -190
- data/lib/bud/stratify.rb +0 -85
data/lib/bud/depanalysis.rb
CHANGED
@@ -5,41 +5,66 @@ class DepAnalysis #:nodoc: all
|
|
5
5
|
include Bud
|
6
6
|
|
7
7
|
state do
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
# Data inserted by client, usually from t_depends and t_provides
|
9
|
+
scratch :depends, [:lhs, :op, :body, :neg]
|
10
|
+
scratch :providing, [:pred, :input]
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
# Intermediate state
|
13
|
+
scratch :depends_clean, [:lhs, :body, :neg, :temporal]
|
14
|
+
|
15
|
+
scratch :depends_tc, [:lhs, :body, :via, :neg, :temporal]
|
16
|
+
scratch :cycle, [:pred, :via, :neg, :temporal]
|
17
|
+
scratch :underspecified, [:pred, :input]
|
18
|
+
scratch :source, [:pred]
|
19
|
+
scratch :sink, [:pred]
|
15
20
|
end
|
16
21
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
bloom :analysis do
|
23
|
+
depends_clean <= depends do |d|
|
24
|
+
is_temporal = (d.op.to_s =~ /<[\+\-\~]/)
|
25
|
+
[d.lhs, d.body, d.neg, is_temporal]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Compute the transitive closure of "depends_clean" to detect cycles in
|
29
|
+
# the deductive fragment of the program.
|
30
|
+
depends_tc <= depends_clean do |d|
|
31
|
+
[d.lhs, d.body, d.body, d.neg, d.temporal]
|
32
|
+
end
|
33
|
+
depends_tc <= (depends_clean * depends_tc).pairs(:body => :lhs) do |b, r|
|
34
|
+
[b.lhs, r.body, b.body, (b.neg or r.neg), (b.temporal or r.temporal)]
|
35
|
+
end
|
36
|
+
|
37
|
+
cycle <= depends_tc do |d|
|
38
|
+
if d.lhs == d.body
|
39
|
+
unless d.neg and !d.temporal
|
40
|
+
[d.lhs, d.via, d.neg, d.temporal]
|
22
41
|
end
|
23
42
|
end
|
43
|
+
end
|
24
44
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
45
|
+
source <= providing do |p|
|
46
|
+
if p.input and !depends_tc.map{|d| d.lhs}.include? p.pred
|
47
|
+
[p.pred]
|
29
48
|
end
|
49
|
+
end
|
30
50
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
51
|
+
sink <= providing do |p|
|
52
|
+
if !p.input and !depends_tc.map{|d| d.body}.include? p.pred
|
53
|
+
[p.pred]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
underspecified <= providing do |p|
|
58
|
+
if p.input
|
59
|
+
unless depends_tc.map{|d| d.body if d.lhs != d.body}.include? p.pred
|
60
|
+
[p.pred, true]
|
61
|
+
end
|
62
|
+
else
|
63
|
+
unless depends_tc.map{|d| d.lhs if d.lhs != d.body}.include? p.pred
|
64
|
+
[p.pred, false]
|
40
65
|
end
|
41
66
|
end
|
42
|
-
|
67
|
+
end
|
43
68
|
end
|
44
69
|
end
|
45
70
|
|
@@ -0,0 +1,592 @@
|
|
1
|
+
require "set"
|
2
|
+
require 'bud/collections'
|
3
|
+
ELEMENT_BUFSIZE = 1
|
4
|
+
|
5
|
+
module Bud
|
6
|
+
# Usage example:
|
7
|
+
# p = PushElement.new(:r) do |inp|
|
8
|
+
# puts "in block"
|
9
|
+
# [inp] if inp.class <= Numeric and inp%2 == 0
|
10
|
+
# end
|
11
|
+
|
12
|
+
# p.insert(1)
|
13
|
+
# p.insert(nil)
|
14
|
+
class PushElement < BudCollection
|
15
|
+
attr_accessor :elem_name
|
16
|
+
attr_accessor :rescan, :invalidated
|
17
|
+
attr_reader :arity, :inputs, :found_delta, :refcount, :wired_by, :outputs
|
18
|
+
|
19
|
+
def initialize(name_in, bud_instance, collection_name=nil, given_schema=nil, defer_schema=false, &blk)
|
20
|
+
super(name_in, bud_instance, given_schema, defer_schema)
|
21
|
+
@blk = blk
|
22
|
+
@outputs = []
|
23
|
+
@pendings = []
|
24
|
+
@deletes = []
|
25
|
+
@delete_keys = []
|
26
|
+
@wired_by = []
|
27
|
+
@elem_name = name_in
|
28
|
+
@found_delta = false
|
29
|
+
@refcount = 1
|
30
|
+
@each_index = 0
|
31
|
+
@collection_name = collection_name
|
32
|
+
@invalidated = true
|
33
|
+
@rescan = true
|
34
|
+
end
|
35
|
+
|
36
|
+
def wiring?
|
37
|
+
@bud_instance.toplevel.done_wiring == false
|
38
|
+
end
|
39
|
+
|
40
|
+
def wirings
|
41
|
+
@wirings ||= @outputs + @pendings + @deletes + @delete_keys
|
42
|
+
end
|
43
|
+
|
44
|
+
public
|
45
|
+
def print_wiring(depth=0, accum = "")
|
46
|
+
depth.times {print " "}
|
47
|
+
puts "#{accum} #{(self.object_id*2).to_s(16)}: #{qualified_tabname} (#{self.class})"
|
48
|
+
|
49
|
+
[@outputs, @pendings, @deletes, @delete_keys].each do |kind|
|
50
|
+
case kind.object_id
|
51
|
+
when @outputs.object_id
|
52
|
+
next_accum = "=> "
|
53
|
+
when @pendings.object_id
|
54
|
+
next_accum = "+> "
|
55
|
+
when @deletes.object_id, @delete_keys.object_id
|
56
|
+
next_accum = "-> "
|
57
|
+
end
|
58
|
+
|
59
|
+
kind.each do |o|
|
60
|
+
if o.respond_to?(:print_wiring)
|
61
|
+
o.print_wiring(depth+1, next_accum)
|
62
|
+
else
|
63
|
+
(depth+1).times {print " "}
|
64
|
+
print "#{next_accum} "
|
65
|
+
if o.class <= Bud::BudCollection
|
66
|
+
puts "#{(o.object_id*2).to_s(16)}: #{o.qualified_tabname} (#{o.class})"
|
67
|
+
else
|
68
|
+
puts "#{(o.object_id*2).to_s(16)}: (#{o.class.name})"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def check_wiring
|
76
|
+
if @blk.nil? and @outputs.empty? and @pendings.empty? and @deletes.empty? and @delete_keys.empty?
|
77
|
+
raise "no output specified for PushElement #{@qualified_tabname}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def set_block(&blk)
|
82
|
+
@blk = blk
|
83
|
+
end
|
84
|
+
def wire_to(element)
|
85
|
+
unless element.methods.include? :insert or element.methods.include? "insert"
|
86
|
+
raise Bud::Error, "attempt to wire_to element without insert method"
|
87
|
+
end
|
88
|
+
# elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
|
89
|
+
# puts "wiring #{self.elem_name} to #{elem_name}"
|
90
|
+
@outputs << element
|
91
|
+
element.wired_by << self if element.respond_to? :wired_by
|
92
|
+
end
|
93
|
+
def wire_to_pending(element)
|
94
|
+
raise Bud::Error, "attempt to wire_to_pending element without pending_merge method" unless element.methods.include? "pending_merge" or element.methods.include? :pending_merge
|
95
|
+
elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
|
96
|
+
# puts "wiring #{self.elem_name} to #{elem_name}(pending)"
|
97
|
+
@pendings << element
|
98
|
+
element.wired_by << self if element.respond_to? :wired_by
|
99
|
+
end
|
100
|
+
def wire_to_delete(element)
|
101
|
+
raise Bud::Error, "attempt to wire_to_delete element without pending_delete method" unless element.methods.include? "pending_delete" or element.methods.include? :pending_delete
|
102
|
+
elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
|
103
|
+
# puts "wiring #{self.elem_name} to #{elem_name}(delete)"
|
104
|
+
@deletes << element
|
105
|
+
element.wired_by << self if element.respond_to? :wired_by
|
106
|
+
end
|
107
|
+
def wire_to_delete_by_key(element)
|
108
|
+
raise Bud::Error, "attempt to wire_to_delete_by_key element without pending_delete_keys method" unless element.methods.include? "pending_delete_keys" or element.methods.include? :pending_delete_keys
|
109
|
+
elem_name = element.respond_to?(:tabname) ? element.tabname : element.elem_name
|
110
|
+
# puts "wiring #{self.elem_name} to #{elem_name}(delete)"
|
111
|
+
@delete_keys << element
|
112
|
+
element.wired_by << self if element.respond_to? :wired_by
|
113
|
+
end
|
114
|
+
|
115
|
+
def rescan_at_tick
|
116
|
+
false
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
def insert(item, source=nil)
|
121
|
+
push_out(item)
|
122
|
+
end
|
123
|
+
|
124
|
+
def tick
|
125
|
+
invalidate_cache if @invalidated
|
126
|
+
end
|
127
|
+
|
128
|
+
def tick_deltas
|
129
|
+
@found_delta = false
|
130
|
+
end
|
131
|
+
|
132
|
+
def push_out(item, do_block=true)
|
133
|
+
if item
|
134
|
+
blk = @blk if do_block
|
135
|
+
if blk
|
136
|
+
item = item.to_a if blk.arity > 1
|
137
|
+
begin
|
138
|
+
item = blk.call item
|
139
|
+
rescue Exception
|
140
|
+
raise
|
141
|
+
end
|
142
|
+
end
|
143
|
+
@outputs.each do |ou|
|
144
|
+
if ou.class <= Bud::PushElement
|
145
|
+
#the_name = ou.elem_name
|
146
|
+
# puts "#{self.object_id%10000} (#{elem_name}) -> #{ou.object_id%10000} (#{the_name}): #{item.inspect}"
|
147
|
+
ou.insert(item,self)
|
148
|
+
elsif ou.class <= Bud::BudCollection
|
149
|
+
# the_name = ou.tabname
|
150
|
+
# puts "#{self.object_id%10000} (#{elem_name}) -> #{ou.object_id%10000} (#{the_name}): #{item.inspect}"
|
151
|
+
ou.do_insert(item,ou.new_delta)
|
152
|
+
else
|
153
|
+
raise "Expected either a PushElement or a BudCollection"
|
154
|
+
end
|
155
|
+
end unless item.nil?
|
156
|
+
# for all the following, o is a BudCollection
|
157
|
+
@deletes.each{|o| o.pending_delete([item])} unless item.nil?
|
158
|
+
@delete_keys.each{|o| o.pending_delete_keys([item])} unless item.nil?
|
159
|
+
@pendings.each{|o| o.pending_merge([item])} unless item.nil?
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
# default for stateless elements
|
165
|
+
public
|
166
|
+
def add_rescan_invalidate(rescan, invalidate)
|
167
|
+
# if any of the source elements are in rescan mode, then put this node in rescan.
|
168
|
+
srcs = non_temporal_predecessors
|
169
|
+
if srcs.any?{|p| rescan.member? p}
|
170
|
+
rescan << self
|
171
|
+
end
|
172
|
+
|
173
|
+
# pass the current state to the non-element outputs, and see if they end up marking this node for rescan
|
174
|
+
invalidate_tables(rescan, invalidate)
|
175
|
+
|
176
|
+
# finally, if this node is in rescan, pass the request on to all source elements
|
177
|
+
if rescan.member? self
|
178
|
+
srcs.each{|e| rescan << e} # propagate a rescan request to all sources.
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def invalidate_tables(rescan, invalidate)
|
183
|
+
# exchange rescan and invalidate information with tables. If this node is in rescan, it may invalidate a target
|
184
|
+
# table (if it is a scratch). And if the target node is invalidated, this node marks itself for rescan to
|
185
|
+
# enable a refill of that table at run-time
|
186
|
+
|
187
|
+
@outputs.each do |o|
|
188
|
+
unless o.class <= PushElement
|
189
|
+
o.add_rescan_invalidate(rescan, invalidate) unless o.class <= PushElement
|
190
|
+
rescan << self if invalidate.member? o
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def <<(i)
|
196
|
+
insert(i, nil)
|
197
|
+
end
|
198
|
+
public
|
199
|
+
def flush
|
200
|
+
end
|
201
|
+
|
202
|
+
def invalidate_cache
|
203
|
+
#override to get rid of cached information.
|
204
|
+
end
|
205
|
+
public
|
206
|
+
def stratum_end
|
207
|
+
end
|
208
|
+
#public
|
209
|
+
#def set_schema(schema)
|
210
|
+
# @schema=schema
|
211
|
+
# setup_accessors
|
212
|
+
#end
|
213
|
+
|
214
|
+
|
215
|
+
####
|
216
|
+
# and now, the Bloom-facing methods
|
217
|
+
public
|
218
|
+
def pro(the_name = @elem_name, the_schema = schema, &blk)
|
219
|
+
toplevel = @bud_instance.toplevel
|
220
|
+
elem = Bud::PushElement.new('project' + object_id.to_s, toplevel.this_rule_context, @collection_name, the_schema)
|
221
|
+
#elem.init_schema(the_schema) unless the_schema.nil?
|
222
|
+
self.wire_to(elem)
|
223
|
+
elem.set_block(&blk)
|
224
|
+
toplevel.push_elems[[self.object_id,:pro,blk]] = elem
|
225
|
+
return elem
|
226
|
+
end
|
227
|
+
|
228
|
+
alias each pro
|
229
|
+
|
230
|
+
public
|
231
|
+
def each_with_index(the_name = elem_name, the_schema = schema, &blk)
|
232
|
+
toplevel = @bud_instance.toplevel
|
233
|
+
elem = Bud::PushEachWithIndex.new('each_with_index' + object_id.to_s, toplevel.this_rule_context, @collection_name)
|
234
|
+
elem.set_block(&blk)
|
235
|
+
self.wire_to(elem)
|
236
|
+
toplevel.push_elems[[self.object_id,:each,blk]] = elem
|
237
|
+
end
|
238
|
+
|
239
|
+
def join(elem2, &blk)
|
240
|
+
# cached = @bud_instance.push_elems[[self.object_id,:join,[self,elem2], @bud_instance, blk]]
|
241
|
+
# if cached.nil?
|
242
|
+
elem2 = elem2.to_push_elem unless elem2.class <= PushElement
|
243
|
+
toplevel = @bud_instance.toplevel
|
244
|
+
join = Bud::PushSHJoin.new([self,elem2], toplevel.this_rule_context, [])
|
245
|
+
self.wire_to(join)
|
246
|
+
elem2.wire_to(join)
|
247
|
+
toplevel.push_elems[[self.object_id,:join,[self,elem2], toplevel, blk]] = join
|
248
|
+
toplevel.push_joins[toplevel.this_stratum] << join
|
249
|
+
# else
|
250
|
+
# cached.refcount += 1
|
251
|
+
# end
|
252
|
+
return toplevel.push_elems[[self.object_id,:join,[self,elem2], toplevel, blk]]
|
253
|
+
end
|
254
|
+
def *(elem2, &blk)
|
255
|
+
join(elem2, &blk)
|
256
|
+
end
|
257
|
+
|
258
|
+
def notin(elem2, preds=nil, &blk)
|
259
|
+
toplevel = @bud_instance.toplevel
|
260
|
+
notin_elem = Bud::PushNotIn.new([self, elem2], toplevel.this_rule_context, preds, &blk)
|
261
|
+
self.wire_to(notin_elem)
|
262
|
+
elem2.wire_to(notin_elem)
|
263
|
+
toplevel.push_elems[[self.object_id, :notin, collection, toplevel, blk]] == notin_elem
|
264
|
+
return notin_elem
|
265
|
+
end
|
266
|
+
|
267
|
+
def merge(source)
|
268
|
+
if source.class <= PushElement and wiring?
|
269
|
+
source.wire_to(self)
|
270
|
+
else
|
271
|
+
source.each{|i| self << i}
|
272
|
+
end
|
273
|
+
end
|
274
|
+
alias <= merge
|
275
|
+
superator "<~" do |o|
|
276
|
+
raise Bud::Error, "Illegal use of <~ with pusher '#{tabname}' on left"
|
277
|
+
end
|
278
|
+
|
279
|
+
superator "<-" do |o|
|
280
|
+
raise Bud::Error, "Illegal use of <- with pusher '#{tabname}' on left"
|
281
|
+
end
|
282
|
+
|
283
|
+
superator "<+" do |o|
|
284
|
+
raise Bud::Error, "Illegal use of <+ with pusher '#{tabname}' on left"
|
285
|
+
end
|
286
|
+
|
287
|
+
def group(keycols, *aggpairs, &blk)
|
288
|
+
# establish schema
|
289
|
+
keycols = [] if keycols.nil?
|
290
|
+
keycols = keycols.map{|c| canonicalize_col(c)}
|
291
|
+
keynames = keycols.map{|k| k[2]}
|
292
|
+
aggcolsdups = aggpairs.map{|ap| ap[0].class.name.split("::").last}
|
293
|
+
aggcols = []
|
294
|
+
aggcolsdups.each_with_index do |n, i|
|
295
|
+
aggcols << "#{n.downcase}_#{i}".to_sym
|
296
|
+
end
|
297
|
+
if aggcols.empty?
|
298
|
+
the_schema = keynames
|
299
|
+
else
|
300
|
+
the_schema = { keynames => aggcols }
|
301
|
+
end
|
302
|
+
|
303
|
+
aggpairs = aggpairs.map{|ap| ap[1].nil? ? [ap[0]] : [ap[0], canonicalize_col(ap[1])]}
|
304
|
+
toplevel = @bud_instance.toplevel
|
305
|
+
# if @bud_instance.push_elems[[self.object_id, :group, keycols, aggpairs, blk]].nil?
|
306
|
+
g = Bud::PushGroup.new('grp'+Time.new.tv_usec.to_s, toplevel.this_rule_context, @collection_name, keycols, aggpairs, the_schema, &blk)
|
307
|
+
self.wire_to(g)
|
308
|
+
toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]] = g
|
309
|
+
# end
|
310
|
+
# toplevel.push_elems[[self.object_id, :group, keycols, aggpairs, blk]]
|
311
|
+
return g
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
def argagg(aggname, gbkey_cols, collection, &blk)
|
316
|
+
gbkey_cols = gbkey_cols.map{|c| canonicalize_col(c)}
|
317
|
+
collection = canonicalize_col(collection)
|
318
|
+
toplevel = @bud_instance.toplevel
|
319
|
+
agg = toplevel.send(aggname, collection)[0]
|
320
|
+
raise Bud::Error, "#{aggname} not declared exemplary" unless agg.class <= Bud::ArgExemplary
|
321
|
+
keynames = gbkey_cols.map do |k|
|
322
|
+
if k.class == Symbol
|
323
|
+
k.to_s
|
324
|
+
else
|
325
|
+
k[2]
|
326
|
+
end
|
327
|
+
end
|
328
|
+
aggpairs = [[agg,collection]]
|
329
|
+
# if toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]].nil?
|
330
|
+
aa = Bud::PushArgAgg.new('argagg'+Time.new.tv_usec.to_s, toplevel.this_rule_context, @collection_name, gbkey_cols, aggpairs, schema, &blk)
|
331
|
+
self.wire_to(aa)
|
332
|
+
toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]] = aa
|
333
|
+
# end
|
334
|
+
# return toplevel.push_elems[[self.object_id,:argagg, gbkey_cols, aggpairs, blk]]
|
335
|
+
return aa
|
336
|
+
end
|
337
|
+
def argmax(gbcols, col, &blk)
|
338
|
+
argagg(gbcols, Bud::max(col), blk)
|
339
|
+
end
|
340
|
+
def argmin(gbcols, col, &blk)
|
341
|
+
argagg(gbcols, Bud::min(col), blk)
|
342
|
+
end
|
343
|
+
def sort(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
344
|
+
elem = Bud::PushSort.new(name, bud_instance, the_schema, &blk)
|
345
|
+
wire_to(elem)
|
346
|
+
elem
|
347
|
+
end
|
348
|
+
def push_predicate(pred_symbol, name=nil, bud_instance=nil, the_schema=nil, &blk)
|
349
|
+
elem = Bud::PushPredicate.new(pred_symbol, name, bud_instance, the_schema, &blk)
|
350
|
+
wire_to(elem)
|
351
|
+
elem
|
352
|
+
end
|
353
|
+
def all?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
354
|
+
push_predicate(:all?, name, bud_instance, the_schema, &blk)
|
355
|
+
end
|
356
|
+
def any?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
357
|
+
push_predicate(:any?, name, bud_instance, the_schema, &blk)
|
358
|
+
end
|
359
|
+
def include?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
360
|
+
push_predicate(:include?, name, bud_instance, the_schema, &blk)
|
361
|
+
end
|
362
|
+
def member?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
363
|
+
push_predicate(:member?, name, bud_instance, the_schema, &blk)
|
364
|
+
end
|
365
|
+
def none?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
366
|
+
push_predicate(:none?, name, bud_instance, the_schema, &blk)
|
367
|
+
end
|
368
|
+
def one?(name=nil, bud_instance=nil, the_schema=nil, &blk)
|
369
|
+
push_predicate(:one?, name, bud_instance, the_schema, &blk)
|
370
|
+
end
|
371
|
+
|
372
|
+
def reduce(initial, &blk)
|
373
|
+
@memo = initial
|
374
|
+
retval = Bud::PushReduce.new('reduce'+Time.new.tv_usec.to_s, @bud_instance, @collection_name, schema, initial, &blk)
|
375
|
+
self.wire_to(retval)
|
376
|
+
retval
|
377
|
+
end
|
378
|
+
|
379
|
+
alias on_exists? pro
|
380
|
+
def on_include?(item, &blk)
|
381
|
+
toplevel = @bud_instance.toplevel
|
382
|
+
if toplevel.push_elems[[self.object_id,:on_include?, item, blk]].nil?
|
383
|
+
inc = pro{|i| blk.call(item) if i == item and not blk.nil?}
|
384
|
+
wire_to(inc)
|
385
|
+
toplevel.push_elems[[self.object_id,:on_include?, item, blk]] = inc
|
386
|
+
end
|
387
|
+
toplevel.push_elems[[self.object_id,:on_include?, item, blk]]
|
388
|
+
end
|
389
|
+
def inspected
|
390
|
+
toplevel = @bud_instance.toplevel
|
391
|
+
if toplevel.push_elems[[self.object_id,:inspected]].nil?
|
392
|
+
ins = pro{|i| [i.inspect]}
|
393
|
+
self.wire_to(ins)
|
394
|
+
toplevel.push_elems[[self.object_id,:inspected]] = ins
|
395
|
+
end
|
396
|
+
toplevel.push_elems[[self.object_id,:inspected]]
|
397
|
+
end
|
398
|
+
|
399
|
+
def to_enum
|
400
|
+
# scr = @bud_instance.scratch(("scratch_" + Process.pid.to_s + "_" + object_id.to_s + "_" + rand(10000).to_s).to_sym, schema)
|
401
|
+
scr = []
|
402
|
+
self.wire_to(scr)
|
403
|
+
scr
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
class PushStatefulElement < PushElement
|
408
|
+
|
409
|
+
def rescan_at_tick
|
410
|
+
true
|
411
|
+
end
|
412
|
+
|
413
|
+
def rescan
|
414
|
+
true # always gives an entire dump of its contents
|
415
|
+
end
|
416
|
+
|
417
|
+
def add_rescan_invalidate(rescan, invalidate)
|
418
|
+
# If an upstream node is set to rescan, a stateful node invalidates its cache
|
419
|
+
# In addition, a stateful node always rescans its own contents (doesn't need to pass a rescan request to its
|
420
|
+
# its source nodes
|
421
|
+
|
422
|
+
rescan << self
|
423
|
+
srcs = non_temporal_predecessors
|
424
|
+
if srcs.any? {|p| rescan.member? p}
|
425
|
+
invalidate << self
|
426
|
+
end
|
427
|
+
|
428
|
+
invalidate_tables(rescan, invalidate)
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
class PushPredicate < PushStatefulElement
|
433
|
+
def initialize(pred_symbol, elem_name=nil, collection_name=nil, bud_instance=nil, schema_in=nil, &blk)
|
434
|
+
@pred_symbol = pred_symbol
|
435
|
+
@in_buf = []
|
436
|
+
super(elem_name, bud_instance, collection_name, schema_in, &blk)
|
437
|
+
end
|
438
|
+
|
439
|
+
def insert(item, source)
|
440
|
+
@in_buf << item
|
441
|
+
end
|
442
|
+
|
443
|
+
public
|
444
|
+
def flush
|
445
|
+
# always rescans
|
446
|
+
@in_buf.send(@pred_symbol, @blk)
|
447
|
+
end
|
448
|
+
|
449
|
+
def invalidate_cache
|
450
|
+
@in_buf.clear
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
class PushSort < PushStatefulElement
|
455
|
+
def initialize(elem_name=nil, bud_instance=nil, collection_name=nil, schema_in=nil, &blk)
|
456
|
+
@sortbuf = []
|
457
|
+
super(elem_name, bud_instance, collection_name, schema_in, &blk)
|
458
|
+
end
|
459
|
+
|
460
|
+
def insert(item, source)
|
461
|
+
@sortbuf << item
|
462
|
+
end
|
463
|
+
|
464
|
+
def flush
|
465
|
+
unless @sortbuf.empty?
|
466
|
+
@sortbuf.sort!(&@blk)
|
467
|
+
@sortbuf.each do |t|
|
468
|
+
push_out(t, false)
|
469
|
+
end
|
470
|
+
@sortbuf = []
|
471
|
+
end
|
472
|
+
nil
|
473
|
+
end
|
474
|
+
|
475
|
+
def invalidate_cache
|
476
|
+
@sortbuf = []
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
class ScannerElement < PushElement
|
481
|
+
attr_reader :collection
|
482
|
+
attr_reader :rescan_set, :invalidate_set
|
483
|
+
def initialize(elem_name, bud_instance, collection_in, the_schema=collection_in.schema, &blk)
|
484
|
+
# puts self.class
|
485
|
+
super(elem_name, bud_instance, collection_in.qualified_tabname, the_schema)
|
486
|
+
@collection = collection_in
|
487
|
+
@rescan_set = []
|
488
|
+
@invalidate_set = []
|
489
|
+
end
|
490
|
+
|
491
|
+
def rescan
|
492
|
+
@rescan || @collection.invalidated
|
493
|
+
end
|
494
|
+
|
495
|
+
def rescan_at_tick
|
496
|
+
@collection.invalidate_at_tick # need to scan afresh if collection invalidated.
|
497
|
+
end
|
498
|
+
|
499
|
+
def invalidate_at_tick(rescan, invalidate)
|
500
|
+
# collection of others to rescan/invalidate if this scanner's collection were to be invalidated.
|
501
|
+
@rescan_set = rescan
|
502
|
+
@invalidate_set = invalidate
|
503
|
+
end
|
504
|
+
|
505
|
+
public
|
506
|
+
def add_rescan_invalidate(rescan, invalidate)
|
507
|
+
# scanner elements are never directly connected to tables.
|
508
|
+
rescan << self if invalidate.member? @collection
|
509
|
+
|
510
|
+
# Note also that this node can be nominated for rescan by a target node; in other words, a scanner element
|
511
|
+
# can be set to rescan even if the collection is not invalidated.
|
512
|
+
end
|
513
|
+
|
514
|
+
def scan(first_iter)
|
515
|
+
if (first_iter)
|
516
|
+
if rescan
|
517
|
+
# scan entire storage
|
518
|
+
@collection.each_raw {|item|
|
519
|
+
push_out(item)
|
520
|
+
}
|
521
|
+
else
|
522
|
+
# In the first iteration, tick_delta would be non-null IFF the collection has grown in an earlier stratum
|
523
|
+
@collection.tick_delta.each {|item| push_out(item)}
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
# send deltas out in all cases
|
528
|
+
@collection.delta.each_value {|item| push_out(item)}
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
class PushReduce < PushStatefulElement
|
533
|
+
def initialize(elem_name, bud_instance, collection_name, schema_in, initial, &blk)
|
534
|
+
@memo = initial
|
535
|
+
@blk = blk
|
536
|
+
super(elem_name, bud_instance, collection_name, schema)
|
537
|
+
end
|
538
|
+
|
539
|
+
def insert(i, source=nil)
|
540
|
+
@memo = @blk.call(@memo,i)
|
541
|
+
end
|
542
|
+
|
543
|
+
def invalidate_cache
|
544
|
+
@memo.clear
|
545
|
+
end
|
546
|
+
|
547
|
+
public
|
548
|
+
def flush
|
549
|
+
@memo.each do |k,v|
|
550
|
+
push_out([k,v], false)
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
class PushEachWithIndex < PushStatefulElement
|
556
|
+
def initialize(elem_name, bud_instance, collection_name)
|
557
|
+
super(elem_name, bud_instance, collection_name)
|
558
|
+
@each_index = 0
|
559
|
+
end
|
560
|
+
|
561
|
+
def add_rescan_invalidate(rescan, invalidate)
|
562
|
+
srcs = non_temporal_predecessors
|
563
|
+
if srcs.any? {|p| rescan.member? p}
|
564
|
+
invalidate << self
|
565
|
+
rescan << self
|
566
|
+
end
|
567
|
+
|
568
|
+
invalidate_tables(rescan, invalidate)
|
569
|
+
|
570
|
+
# This node has some state (@each_index), but not the tuples. If it is in rescan mode, then it must ask its
|
571
|
+
# sources to rescan, and restart its index.
|
572
|
+
if rescan.member? self
|
573
|
+
invalidate << self
|
574
|
+
srcs.each {|e| rescan << e}
|
575
|
+
end
|
576
|
+
end
|
577
|
+
|
578
|
+
def invalidate_cache
|
579
|
+
@each_index = 0
|
580
|
+
end
|
581
|
+
|
582
|
+
def stratum_end
|
583
|
+
@each_index = 0
|
584
|
+
end
|
585
|
+
|
586
|
+
def insert(item, source=nil)
|
587
|
+
ix = @each_index
|
588
|
+
@each_index = ix + 1
|
589
|
+
push_out([item, ix])
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|