bud 0.0.3 → 0.0.4
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/README +33 -16
- data/bin/budplot +42 -65
- data/bin/budtimelines +235 -0
- data/bin/budvis +24 -122
- data/bin/rebl +1 -0
- data/docs/README.md +21 -10
- data/docs/bfs.md +4 -6
- data/docs/c.html +251 -0
- data/docs/cheat.md +45 -30
- data/docs/deploy.md +26 -26
- data/docs/getstarted.md +6 -4
- data/docs/visualizations.md +43 -31
- data/examples/chat/chat.rb +4 -9
- data/examples/chat/chat_server.rb +1 -8
- data/examples/deploy/deploy_ip_port +1 -0
- data/examples/deploy/keys.rb +5 -0
- data/examples/deploy/tokenring-ec2.rb +9 -9
- data/examples/deploy/{tokenring-local.rb → tokenring-fork.rb} +3 -5
- data/examples/deploy/tokenring-thread.rb +15 -0
- data/examples/deploy/tokenring.rb +25 -17
- data/lib/bud/aggs.rb +87 -25
- data/lib/bud/bud_meta.rb +48 -31
- data/lib/bud/bust/bust.rb +16 -15
- data/lib/bud/collections.rb +207 -232
- data/lib/bud/depanalysis.rb +1 -0
- data/lib/bud/deploy/countatomicdelivery.rb +8 -20
- data/lib/bud/deploy/deployer.rb +16 -16
- data/lib/bud/deploy/ec2deploy.rb +34 -35
- data/lib/bud/deploy/forkdeploy.rb +90 -0
- data/lib/bud/deploy/threaddeploy.rb +38 -0
- data/lib/bud/graphs.rb +103 -199
- data/lib/bud/joins.rb +190 -41
- data/lib/bud/monkeypatch.rb +84 -0
- data/lib/bud/rebl.rb +8 -1
- data/lib/bud/rewrite.rb +152 -49
- data/lib/bud/server.rb +1 -0
- data/lib/bud/state.rb +24 -10
- data/lib/bud/storage/dbm.rb +170 -0
- data/lib/bud/storage/tokyocabinet.rb +5 -1
- data/lib/bud/stratify.rb +6 -7
- data/lib/bud/viz.rb +31 -17
- data/lib/bud/viz_util.rb +204 -0
- data/lib/bud.rb +271 -244
- data/lib/bud.rb.orig +806 -0
- metadata +43 -22
- data/docs/bfs.raw +0 -251
- data/docs/diffs +0 -181
- data/examples/basics/out +0 -1103
- data/examples/basics/out.new +0 -856
- data/lib/bud/deploy/localdeploy.rb +0 -53
data/lib/bud/collections.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'msgpack'
|
2
2
|
|
3
3
|
module Bud
|
4
|
-
########
|
4
|
+
########
|
5
5
|
#--
|
6
6
|
# the collection types
|
7
7
|
# each collection is partitioned into 4:
|
@@ -16,7 +16,7 @@ module Bud
|
|
16
16
|
|
17
17
|
attr_accessor :bud_instance, :locspec_idx # :nodoc: all
|
18
18
|
attr_reader :schema, :tabname # :nodoc: all
|
19
|
-
attr_reader :storage, :delta, :new_delta # :nodoc: all
|
19
|
+
attr_reader :storage, :delta, :new_delta, :pending # :nodoc: all
|
20
20
|
|
21
21
|
def initialize(name, bud_instance, given_schema=nil, defer_schema=false) # :nodoc: all
|
22
22
|
@tabname = name
|
@@ -27,7 +27,6 @@ module Bud
|
|
27
27
|
|
28
28
|
private
|
29
29
|
def init_buffers
|
30
|
-
@sealed = false
|
31
30
|
init_storage
|
32
31
|
init_pending
|
33
32
|
init_deltas
|
@@ -70,7 +69,7 @@ module Bud
|
|
70
69
|
return [schema, key_cols]
|
71
70
|
end
|
72
71
|
|
73
|
-
public
|
72
|
+
public
|
74
73
|
def clone_empty #:nodoc: all
|
75
74
|
self.class.new(tabname, bud_instance, @given_schema)
|
76
75
|
end
|
@@ -80,7 +79,7 @@ module Bud
|
|
80
79
|
def key_cols
|
81
80
|
@key_cols
|
82
81
|
end
|
83
|
-
|
82
|
+
|
84
83
|
# subset of the schema (i.e. an array of attribute names) that is not in the key
|
85
84
|
public
|
86
85
|
def val_cols # :nodoc: all
|
@@ -155,25 +154,21 @@ module Bud
|
|
155
154
|
public
|
156
155
|
def pro(&blk)
|
157
156
|
if @bud_instance.stratum_first_iter
|
158
|
-
return map(&blk)
|
157
|
+
return map(&blk)
|
159
158
|
else
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
retval
|
164
|
-
each_from([@delta]) do |t|
|
165
|
-
newitem = blk.call(t)
|
166
|
-
retval << newitem unless newitem.nil?
|
167
|
-
end
|
168
|
-
return retval
|
159
|
+
retval = []
|
160
|
+
each_from([@delta]) do |t|
|
161
|
+
newitem = blk.call(t)
|
162
|
+
retval << newitem unless newitem.nil?
|
169
163
|
end
|
170
|
-
|
164
|
+
return retval
|
165
|
+
end
|
171
166
|
end
|
172
167
|
|
173
168
|
# By default, all tuples in any rhs are in storage or delta. Tuples in
|
174
169
|
# new_delta will get transitioned to delta in the next iteration of the
|
175
170
|
# evaluator (but within the current time tick).
|
176
|
-
public
|
171
|
+
public
|
177
172
|
def each(&block) # :nodoc: all
|
178
173
|
each_from([@storage, @delta], &block)
|
179
174
|
end
|
@@ -223,7 +218,8 @@ module Bud
|
|
223
218
|
# checks for key +k+ in the key columns
|
224
219
|
public
|
225
220
|
def has_key?(k)
|
226
|
-
|
221
|
+
check_enumerable(k)
|
222
|
+
return false if k.nil? or self[k].nil?
|
227
223
|
return true
|
228
224
|
end
|
229
225
|
|
@@ -232,7 +228,8 @@ module Bud
|
|
232
228
|
def [](k)
|
233
229
|
# assumes that key is in storage or delta, but not both
|
234
230
|
# is this enforced in do_insert?
|
235
|
-
|
231
|
+
t = @storage[k]
|
232
|
+
return t.nil? ? @delta[k] : t
|
236
233
|
end
|
237
234
|
|
238
235
|
# checks for +item+ in the collection
|
@@ -257,15 +254,18 @@ module Bud
|
|
257
254
|
end
|
258
255
|
|
259
256
|
private
|
260
|
-
def raise_pk_error(
|
257
|
+
def raise_pk_error(new_guy, old)
|
261
258
|
keycols = key_cols.map{|k| old[schema.index(k)]}
|
262
|
-
raise KeyConstraintError, "Key conflict inserting #{
|
259
|
+
raise KeyConstraintError, "Key conflict inserting #{new_guy.inspect} into \"#{tabname}\": existing tuple #{old.inspect}, key_cols = #{keycols.inspect}"
|
263
260
|
end
|
264
261
|
|
265
262
|
private
|
266
263
|
def prep_tuple(o)
|
267
264
|
unless o.respond_to?(:length) and o.respond_to?(:[])
|
268
|
-
raise BudTypeError, "non-indexable type inserted into
|
265
|
+
raise BudTypeError, "non-indexable type inserted into \"#{tabname}\": #{o.inspect}"
|
266
|
+
end
|
267
|
+
if o.class <= String
|
268
|
+
raise BudTypeError, "String value used as a fact inserted into \"#{tabname}\": #{o.inspect}"
|
269
269
|
end
|
270
270
|
|
271
271
|
if o.length < schema.length then
|
@@ -308,8 +308,8 @@ module Bud
|
|
308
308
|
|
309
309
|
private
|
310
310
|
def check_enumerable(o)
|
311
|
-
unless
|
312
|
-
raise BudTypeError, "
|
311
|
+
unless o.nil? or o.class < Enumerable
|
312
|
+
raise BudTypeError, "Collection #{tabname} expected Enumerable value, not #{o.inspect} (class = #{o.class})"
|
313
313
|
end
|
314
314
|
end
|
315
315
|
|
@@ -318,10 +318,15 @@ module Bud
|
|
318
318
|
private
|
319
319
|
def establish_schema(o)
|
320
320
|
# use o's schema if available
|
321
|
-
deduce_schema(o)
|
322
|
-
# else use arity of first tuple of o
|
323
|
-
|
324
|
-
|
321
|
+
deduce_schema(o)
|
322
|
+
# else use arity of first non-nil tuple of o
|
323
|
+
if @schema.nil?
|
324
|
+
o.each do |t|
|
325
|
+
next if t.nil?
|
326
|
+
fit_schema(t.size)
|
327
|
+
break
|
328
|
+
end
|
329
|
+
end
|
325
330
|
end
|
326
331
|
|
327
332
|
# Copy over the schema from +o+ if available
|
@@ -331,8 +336,7 @@ module Bud
|
|
331
336
|
# must have been initialized with defer_schema==true. take schema from rhs
|
332
337
|
init_schema(o.schema)
|
333
338
|
end
|
334
|
-
#
|
335
|
-
return @schema
|
339
|
+
# if nothing available, leave @schema unchanged
|
336
340
|
end
|
337
341
|
|
338
342
|
# manufacture schema of the form [:c0, :c1, ...] with width = +arity+
|
@@ -340,24 +344,36 @@ module Bud
|
|
340
344
|
def fit_schema(arity)
|
341
345
|
# rhs is schemaless. create schema from first tuple merged
|
342
346
|
init_schema((0..arity-1).map{|indx| ("c"+indx.to_s).to_sym})
|
343
|
-
|
347
|
+
end
|
348
|
+
|
349
|
+
private
|
350
|
+
def include_any_buf?(t, key_vals)
|
351
|
+
bufs = [self, @delta, @new_delta]
|
352
|
+
bufs.each do |b|
|
353
|
+
old = b[key_vals]
|
354
|
+
next if old.nil?
|
355
|
+
if old != t
|
356
|
+
raise_pk_error(t, old)
|
357
|
+
else
|
358
|
+
return true
|
359
|
+
end
|
360
|
+
end
|
361
|
+
return false
|
344
362
|
end
|
345
363
|
|
346
364
|
public
|
347
365
|
def merge(o, buf=@new_delta) # :nodoc: all
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
else
|
360
|
-
buf[key_vals] = tuple_accessors(i)
|
366
|
+
unless o.nil?
|
367
|
+
o = o.uniq.compact if o.respond_to?(:uniq)
|
368
|
+
check_enumerable(o)
|
369
|
+
establish_schema(o) if @schema.nil?
|
370
|
+
|
371
|
+
# it's a pity that we are massaging the tuples that already exist in the head
|
372
|
+
o.each do |t|
|
373
|
+
next if t.nil? or t == []
|
374
|
+
t = prep_tuple(t)
|
375
|
+
key_vals = @key_colnums.map{|k| t[k]}
|
376
|
+
buf[key_vals] = tuple_accessors(t) unless include_any_buf?(t, key_vals)
|
361
377
|
end
|
362
378
|
end
|
363
379
|
return self
|
@@ -365,7 +381,7 @@ module Bud
|
|
365
381
|
|
366
382
|
public
|
367
383
|
# instantaneously merge items from collection +o+ into +buf+
|
368
|
-
def <=(collection)
|
384
|
+
def <=(collection)
|
369
385
|
merge(collection)
|
370
386
|
end
|
371
387
|
|
@@ -373,7 +389,7 @@ module Bud
|
|
373
389
|
public
|
374
390
|
def pending_merge(o) # :nodoc: all
|
375
391
|
check_enumerable(o)
|
376
|
-
|
392
|
+
establish_schema(o) if @schema.nil?
|
377
393
|
|
378
394
|
o.each {|i| do_insert(i, @pending)}
|
379
395
|
return self
|
@@ -395,7 +411,7 @@ module Bud
|
|
395
411
|
end
|
396
412
|
|
397
413
|
# move deltas to storage, and new_deltas to deltas.
|
398
|
-
public
|
414
|
+
public
|
399
415
|
def tick_deltas # :nodoc: all
|
400
416
|
# assertion: intersect(@storage, @delta) == nil
|
401
417
|
@storage.merge!(@delta)
|
@@ -421,7 +437,7 @@ module Bud
|
|
421
437
|
return []
|
422
438
|
end
|
423
439
|
end
|
424
|
-
|
440
|
+
|
425
441
|
|
426
442
|
# a generalization of argmin/argmax to arbitrary exemplary aggregates.
|
427
443
|
# for each distinct value in the grouping key columns, return the item in that group
|
@@ -447,30 +463,42 @@ module Bud
|
|
447
463
|
if memo[pkey_cols].nil?
|
448
464
|
memo[pkey_cols] = {:agg=>agg.send(:init, p[colnum]), :tups => [p]}
|
449
465
|
else
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
466
|
+
memo[pkey_cols][:agg], argflag = \
|
467
|
+
agg.send(:trans, memo[pkey_cols][:agg], p[colnum])
|
468
|
+
if argflag == :keep or agg.send(:tie, memo[pkey_cols][:agg], p[colnum])
|
469
|
+
memo[pkey_cols][:tups] << p
|
470
|
+
elsif argflag == :replace
|
471
|
+
memo[pkey_cols][:tups] = [p]
|
472
|
+
elsif argflag.class <= Array and argflag[0] == :delete
|
473
|
+
memo[pkey_cols][:tups] -= argflag[1..-1]
|
457
474
|
end
|
458
475
|
end
|
459
476
|
memo
|
460
477
|
end
|
461
478
|
|
479
|
+
# now we need to finalize the agg per group
|
480
|
+
finalaggs = {}
|
462
481
|
finals = []
|
463
|
-
|
464
|
-
|
465
|
-
|
482
|
+
tups.each do |k,v|
|
483
|
+
finalaggs[k] = agg.send(:final, v[:agg])
|
484
|
+
end
|
485
|
+
|
486
|
+
# and winnow the tups to match
|
487
|
+
finalaggs.each do |k,v|
|
488
|
+
tups[k][:tups].each do |t|
|
489
|
+
finals << t if (t[colnum] == v)
|
466
490
|
end
|
467
491
|
end
|
468
492
|
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
493
|
+
if block_given?
|
494
|
+
finals.map{|r| yield r}
|
495
|
+
else
|
496
|
+
# merge directly into retval.storage, so that the temp tuples get picked up
|
497
|
+
# by the lhs of the rule
|
498
|
+
retval = BudScratch.new('argagg_temp', bud_instance, @given_schema)
|
499
|
+
retval.uniquify_tabname
|
500
|
+
retval.merge(finals, retval.storage)
|
501
|
+
end
|
474
502
|
end
|
475
503
|
|
476
504
|
# for each distinct value in the grouping key columns, return the item in that group
|
@@ -487,14 +515,29 @@ module Bud
|
|
487
515
|
argagg(:max, gbkey_cols, col)
|
488
516
|
end
|
489
517
|
|
518
|
+
private
|
519
|
+
def wrap_map(j, &blk)
|
520
|
+
if blk.nil?
|
521
|
+
return j
|
522
|
+
else
|
523
|
+
return j.map(&blk)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
def join(collections, *preds, &blk)
|
528
|
+
# since joins are stateful, we want to allocate them once and store in this Bud instance
|
529
|
+
# we ID them on their tablenames, preds, and block
|
530
|
+
return wrap_map(BudJoin.new(collections, @bud_instance, preds), &blk)
|
531
|
+
end
|
532
|
+
|
490
533
|
# form a collection containing all pairs of items in +self+ and items in
|
491
534
|
# +collection+
|
492
535
|
public
|
493
536
|
def *(collection)
|
494
|
-
|
537
|
+
join([self, collection])
|
495
538
|
end
|
496
539
|
|
497
|
-
# SQL-style grouping. first argument is an array of attributes to group by.
|
540
|
+
# SQL-style grouping. first argument is an array of attributes to group by.
|
498
541
|
# Followed by a variable-length list of aggregates over attributes (e.g. +min(:x)+)
|
499
542
|
# Attributes can be referenced as symbols, or as +collection_name.attribute_name+
|
500
543
|
public
|
@@ -514,21 +557,24 @@ module Bud
|
|
514
557
|
aggcolsdups.each_with_index do |n, i|
|
515
558
|
aggcols << "#{n.downcase}_#{i}".to_sym
|
516
559
|
end
|
560
|
+
aggpairs = aggpairs.map do |ap|
|
561
|
+
if ap[1].class == Symbol
|
562
|
+
colnum = ap[1].nil? ? nil : self.send(ap[1].to_s)[1]
|
563
|
+
else
|
564
|
+
colnum = ap[1].nil? ? nil : ap[1][1]
|
565
|
+
end
|
566
|
+
[ap[0], colnum]
|
567
|
+
end
|
517
568
|
tups = agg_in.inject({}) do |memo, p|
|
518
569
|
pkey_cols = keynames.map{|n| p.send(n)}
|
519
570
|
memo[pkey_cols] = [] if memo[pkey_cols].nil?
|
520
571
|
aggpairs.each_with_index do |ap, i|
|
521
572
|
agg = ap[0]
|
522
|
-
|
523
|
-
colnum = ap[1].nil? ? nil : self.send(ap[1].to_s)[1]
|
524
|
-
else
|
525
|
-
colnum = ap[1].nil? ? nil : ap[1][1]
|
526
|
-
end
|
527
|
-
colval = colnum.nil? ? nil : p[colnum]
|
573
|
+
colval = ap[1].nil? ? nil : p[ap[1]]
|
528
574
|
if memo[pkey_cols][i].nil?
|
529
575
|
memo[pkey_cols][i] = agg.send(:init, colval)
|
530
576
|
else
|
531
|
-
memo[pkey_cols][i] = agg.send(:trans, memo[pkey_cols][i], colval)
|
577
|
+
memo[pkey_cols][i], ignore = agg.send(:trans, memo[pkey_cols][i], colval)
|
532
578
|
end
|
533
579
|
end
|
534
580
|
memo
|
@@ -559,132 +605,6 @@ module Bud
|
|
559
605
|
|
560
606
|
alias reduce inject
|
561
607
|
|
562
|
-
# methods that work on nested collections (resulting from joins)
|
563
|
-
|
564
|
-
|
565
|
-
# given a * expression over n collections, form all combinations of items
|
566
|
-
# subject to an array of predicates, pred
|
567
|
-
# currently supports two options for equijoin predicates:
|
568
|
-
# general form: an array of arrays capturing a conjunction of equiv. classes
|
569
|
-
# [[table1.col1, table2.col2, table3.col3], [table1.col2, table2.col3]]
|
570
|
-
# common form: a hash capturing equality of a column on left with one on right.
|
571
|
-
# :col1 => :col2 (same as lefttable.col1 => righttable.col2)
|
572
|
-
public
|
573
|
-
def pairs(*preds, &blk)
|
574
|
-
setup_preds(preds) unless (preds.nil? or preds.empty?)
|
575
|
-
# given new preds, the state for the join will be different. set it up again.
|
576
|
-
setup_state if self.class <= Bud::BudJoin
|
577
|
-
blk.nil? ? self : map(&blk)
|
578
|
-
end
|
579
|
-
|
580
|
-
alias combos pairs
|
581
|
-
|
582
|
-
# the natural join: given a * expression over 2 collections, form all
|
583
|
-
# combinations of items that have the same values in matching fiels
|
584
|
-
public
|
585
|
-
def matches(&blk)
|
586
|
-
preds = BudJoin::natural_preds(@bud_instance, @rels)
|
587
|
-
pairs(*preds, &blk)
|
588
|
-
end
|
589
|
-
|
590
|
-
# given a * expression over 2 collections, form all
|
591
|
-
# combinations of items that satisfy the predicates +preds+,
|
592
|
-
# and project only onto the attributes of the first collection
|
593
|
-
public
|
594
|
-
def lefts(*preds)
|
595
|
-
@localpreds = disambiguate_preds(preds)
|
596
|
-
map{ |l,r| l }
|
597
|
-
end
|
598
|
-
|
599
|
-
# given a * expression over 2 collections, form all
|
600
|
-
# combinations of items that satisfy the predicates +preds+,
|
601
|
-
# and project only onto the attributes of the second item
|
602
|
-
public
|
603
|
-
def rights(*preds)
|
604
|
-
@localpreds = disambiguate_preds(preds)
|
605
|
-
map{ |l,r| r }
|
606
|
-
end
|
607
|
-
|
608
|
-
# extract predicates on rellist[0] and recurse to right side with remainder
|
609
|
-
protected
|
610
|
-
def setup_preds(preds) # :nodoc: all
|
611
|
-
allpreds = disambiguate_preds(preds)
|
612
|
-
allpreds = canonicalize_localpreds(@rels, allpreds)
|
613
|
-
@localpreds = allpreds.reject { |p| p[0][0] != @rels[0].tabname }
|
614
|
-
otherpreds = allpreds - @localpreds
|
615
|
-
otherpreds = nil if otherpreds.empty?
|
616
|
-
unless otherpreds.nil?
|
617
|
-
unless @rels[1].class <= Bud::BudJoin
|
618
|
-
raise BudError, "join predicates don't match tables being joined: #{otherpreds.inspect}"
|
619
|
-
end
|
620
|
-
@rels[1].setup_preds(otherpreds)
|
621
|
-
end
|
622
|
-
end
|
623
|
-
|
624
|
-
protected
|
625
|
-
def disambiguate_preds(preds) # :nodoc: all
|
626
|
-
if preds.size == 1 and preds[0].class <= Hash
|
627
|
-
predarray = preds[0].map do |k,v|
|
628
|
-
if k.class != v.class
|
629
|
-
raise Bud::CompileError, "inconsistent attribute ref style #{k.inspect} => #{v.inspect}"
|
630
|
-
elsif k.class <= Array
|
631
|
-
[k,v]
|
632
|
-
elsif k.class <= Symbol
|
633
|
-
if @origrels and @origrels.length == 2
|
634
|
-
[find_attr_match(k,@origrels[0]), find_attr_match(v,@origrels[1])]
|
635
|
-
else
|
636
|
-
[find_attr_match(k), find_attr_match(v)]
|
637
|
-
end
|
638
|
-
else
|
639
|
-
raise Bud::CompileError, "invalid attribute ref in #{k.inspect} => #{v.inspect}"
|
640
|
-
end
|
641
|
-
end
|
642
|
-
return decomp_preds(*predarray)
|
643
|
-
else
|
644
|
-
return decomp_preds(*preds)
|
645
|
-
end
|
646
|
-
end
|
647
|
-
|
648
|
-
# find element in @origrels that contains this aname method
|
649
|
-
# if 2nd arg is non-nil, only check that collection.
|
650
|
-
# after found, return the result of invoking aname from chosen collection
|
651
|
-
protected
|
652
|
-
def find_attr_match(aname, rel=nil) # :nodoc: all
|
653
|
-
dorels = (rel.nil? ? @origrels : [rel])
|
654
|
-
match = nil
|
655
|
-
dorels.each do |r|
|
656
|
-
match ||= r if r.respond_to?(aname)
|
657
|
-
if r.respond_to?(aname) and match != r
|
658
|
-
raise Bud::CompileError, "ambiguous attribute :#{aname} in both #{match.tabname} and #{r.tabname}"
|
659
|
-
end
|
660
|
-
end
|
661
|
-
if match.nil?
|
662
|
-
raise Bud::CompileError, "attribute :#{aname} not found in any of #{dorels.map{|t| t.tabname}.inspect}"
|
663
|
-
end
|
664
|
-
match.send(aname)
|
665
|
-
end
|
666
|
-
|
667
|
-
protected
|
668
|
-
def decomp_preds(*preds) # :nodoc:all
|
669
|
-
# decompose each pred into a binary pred
|
670
|
-
return nil if preds.nil? or preds.empty? or preds == [nil]
|
671
|
-
newpreds = []
|
672
|
-
preds.each do |p|
|
673
|
-
p.each_with_index do |c, i|
|
674
|
-
newpreds << [p[i], p[i+1]] unless p[i+1].nil?
|
675
|
-
end
|
676
|
-
end
|
677
|
-
newpreds
|
678
|
-
end
|
679
|
-
|
680
|
-
protected
|
681
|
-
def canonicalize_localpreds(rel_list, preds) # :nodoc:all
|
682
|
-
return if preds.nil?
|
683
|
-
retval = preds.map do |p|
|
684
|
-
p[1][0] == rel_list[0].tabname ? p.reverse : p
|
685
|
-
end
|
686
|
-
end
|
687
|
-
|
688
608
|
public
|
689
609
|
def uniquify_tabname # :nodoc: all
|
690
610
|
# just append current number of microseconds
|
@@ -701,18 +621,25 @@ module Bud
|
|
701
621
|
class BudChannel < BudCollection
|
702
622
|
attr_reader :locspec_idx # :nodoc: all
|
703
623
|
|
704
|
-
def initialize(name, bud_instance, given_schema=nil) # :nodoc: all
|
624
|
+
def initialize(name, bud_instance, given_schema=nil, loopback=false) # :nodoc: all
|
705
625
|
given_schema ||= [:@address, :val]
|
706
|
-
|
707
|
-
|
708
|
-
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
626
|
+
@is_loopback = loopback
|
627
|
+
@locspec_idx = nil
|
628
|
+
|
629
|
+
unless @is_loopback
|
630
|
+
the_schema, the_key_cols = parse_schema(given_schema)
|
631
|
+
the_val_cols = the_schema - the_key_cols
|
632
|
+
@locspec_idx = remove_at_sign!(the_key_cols)
|
633
|
+
@locspec_idx = remove_at_sign!(the_schema) if @locspec_idx.nil?
|
634
|
+
if @locspec_idx.nil?
|
635
|
+
raise BudError, "Missing location specifier for channel '#{name}'"
|
636
|
+
end
|
637
|
+
|
638
|
+
# We mutate the hash key above, so we need to recreate the hash
|
639
|
+
# XXX: ugh, hacky
|
640
|
+
if given_schema.respond_to? :keys
|
641
|
+
given_schema = {the_key_cols => the_val_cols}
|
642
|
+
end
|
716
643
|
end
|
717
644
|
|
718
645
|
super(name, bud_instance, given_schema)
|
@@ -728,37 +655,38 @@ module Bud
|
|
728
655
|
end
|
729
656
|
|
730
657
|
private
|
731
|
-
def split_locspec(
|
732
|
-
|
733
|
-
|
734
|
-
|
658
|
+
def split_locspec(t, idx)
|
659
|
+
begin
|
660
|
+
lsplit = t[idx].split(':')
|
661
|
+
lsplit[1] = lsplit[1].to_i
|
662
|
+
return lsplit
|
663
|
+
rescue Exception => e
|
664
|
+
raise BudError, "Illegal location specifier in tuple #{t.inspect} for channel \"#{tabname}\": #{e.to_s}"
|
665
|
+
end
|
735
666
|
end
|
736
667
|
|
737
668
|
public
|
738
669
|
def clone_empty
|
739
|
-
|
740
|
-
retval.locspec_idx = @locspec_idx
|
741
|
-
retval
|
670
|
+
self.class.new(tabname, bud_instance, @given_schema, @is_loopback)
|
742
671
|
end
|
743
672
|
|
744
|
-
public
|
673
|
+
public
|
745
674
|
def tick # :nodoc: all
|
746
|
-
@sealed = false
|
747
675
|
@storage = {}
|
748
676
|
# Note that we do not clear @pending here: if the user inserted into the
|
749
677
|
# channel manually (e.g., via <~ from inside a sync_do block), we send the
|
750
678
|
# message at the end of the current tick.
|
751
679
|
end
|
752
680
|
|
753
|
-
public
|
681
|
+
public
|
754
682
|
def flush # :nodoc: all
|
755
683
|
ip = @bud_instance.ip
|
756
684
|
port = @bud_instance.port
|
757
685
|
each_from([@pending]) do |t|
|
758
|
-
if @
|
686
|
+
if @is_loopback
|
759
687
|
the_locspec = [ip, port]
|
760
688
|
else
|
761
|
-
the_locspec = split_locspec(t
|
689
|
+
the_locspec = split_locspec(t, @locspec_idx)
|
762
690
|
raise BudError, "'#{t[@locspec_idx]}', channel '#{@tabname}'" if the_locspec[0].nil? or the_locspec[1].nil? or the_locspec[0] == '' or the_locspec[1] == ''
|
763
691
|
end
|
764
692
|
@bud_instance.dsock.send_datagram([@tabname, t].to_msgpack, the_locspec[0], the_locspec[1])
|
@@ -769,6 +697,8 @@ module Bud
|
|
769
697
|
public
|
770
698
|
# project to the non-address fields
|
771
699
|
def payloads
|
700
|
+
return self.pro if @is_loopback
|
701
|
+
|
772
702
|
if schema.size > 2
|
773
703
|
# bundle up each tuple's non-locspec fields into an array
|
774
704
|
retval = case @locspec_idx
|
@@ -792,7 +722,7 @@ module Bud
|
|
792
722
|
end
|
793
723
|
|
794
724
|
undef merge
|
795
|
-
|
725
|
+
|
796
726
|
def <=(o)
|
797
727
|
raise BudError, "Illegal use of <= with channel '#{@tabname}' on left"
|
798
728
|
end
|
@@ -804,15 +734,18 @@ module Bud
|
|
804
734
|
@prompt = prompt
|
805
735
|
end
|
806
736
|
|
807
|
-
public
|
737
|
+
public
|
808
738
|
def start_stdin_reader # :nodoc: all
|
809
739
|
# XXX: Ugly hack. Rather than sending terminal data to EM via UDP,
|
810
740
|
# we should add the terminal file descriptor to the EM event loop.
|
811
741
|
@reader = Thread.new do
|
812
742
|
begin
|
813
743
|
while true
|
814
|
-
|
815
|
-
|
744
|
+
out_io = get_out_io
|
745
|
+
out_io.print("#{tabname} > ") if @prompt
|
746
|
+
|
747
|
+
in_io = @bud_instance.options[:stdin]
|
748
|
+
s = in_io.gets
|
816
749
|
break if s.nil? # Hit EOF
|
817
750
|
s = s.chomp if s
|
818
751
|
tup = [s]
|
@@ -834,9 +767,10 @@ module Bud
|
|
834
767
|
|
835
768
|
public
|
836
769
|
def flush #:nodoc: all
|
770
|
+
out_io = get_out_io
|
837
771
|
@pending.each do |p|
|
838
|
-
|
839
|
-
|
772
|
+
out_io.puts p[0]
|
773
|
+
out_io.flush
|
840
774
|
end
|
841
775
|
@pending = {}
|
842
776
|
end
|
@@ -857,9 +791,35 @@ module Bud
|
|
857
791
|
superator "<~" do |o|
|
858
792
|
pending_merge(o)
|
859
793
|
end
|
794
|
+
|
795
|
+
private
|
796
|
+
def get_out_io
|
797
|
+
rv = @bud_instance.options[:stdout]
|
798
|
+
rv ||= $stdout
|
799
|
+
rv
|
800
|
+
end
|
860
801
|
end
|
861
802
|
|
862
803
|
class BudPeriodic < BudCollection # :nodoc: all
|
804
|
+
def <=(o)
|
805
|
+
raise BudError, "Illegal use of <= with periodic '#{tabname}' on left"
|
806
|
+
end
|
807
|
+
|
808
|
+
superator "<~" do |o|
|
809
|
+
raise BudError, "Illegal use of <~ with periodic '#{tabname}' on left"
|
810
|
+
end
|
811
|
+
|
812
|
+
superator "<-" do |o|
|
813
|
+
raise BudError, "Illegal use of <- with periodic '#{tabname}' on left"
|
814
|
+
end
|
815
|
+
|
816
|
+
superator "<+" do |o|
|
817
|
+
raise BudError, "Illegal use of <+ with periodic '#{tabname}' on left"
|
818
|
+
end
|
819
|
+
|
820
|
+
def add_periodic_tuple(id)
|
821
|
+
pending_merge([[id, Time.now]])
|
822
|
+
end
|
863
823
|
end
|
864
824
|
|
865
825
|
class BudTable < BudCollection # :nodoc: all
|
@@ -876,16 +836,22 @@ module Bud
|
|
876
836
|
@storage.delete keycols
|
877
837
|
end
|
878
838
|
end
|
879
|
-
@
|
839
|
+
@pending.each do |keycols, tuple|
|
840
|
+
old = @storage[keycols]
|
841
|
+
if old.nil?
|
842
|
+
@storage[keycols] = tuple
|
843
|
+
else
|
844
|
+
raise_pk_error(tuple, old) unless tuple == old
|
845
|
+
end
|
846
|
+
end
|
880
847
|
@to_delete = []
|
881
848
|
@pending = {}
|
882
849
|
end
|
883
850
|
|
884
851
|
superator "<-" do |o|
|
885
|
-
o.each do |
|
886
|
-
next if
|
887
|
-
|
888
|
-
@to_delete << tuple
|
852
|
+
o.each do |t|
|
853
|
+
next if t.nil?
|
854
|
+
@to_delete << prep_tuple(t)
|
889
855
|
end
|
890
856
|
end
|
891
857
|
end
|
@@ -910,6 +876,15 @@ module Bud
|
|
910
876
|
@linenum = 0
|
911
877
|
end
|
912
878
|
|
879
|
+
public
|
880
|
+
def pro(&blk)
|
881
|
+
if @bud_instance.stratum_first_iter
|
882
|
+
return map(&blk)
|
883
|
+
else
|
884
|
+
return []
|
885
|
+
end
|
886
|
+
end
|
887
|
+
|
913
888
|
public
|
914
889
|
def each(&block) # :nodoc: all
|
915
890
|
while (l = @fd.gets)
|