bud 0.9.0 → 0.9.1

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/History.txt CHANGED
@@ -1,4 +1,13 @@
1
- == 0.9.0 / 2012-03-20
1
+ == 0.9.1 / 2012-04-10
2
+
3
+ * Reject attempts to insert a tuple into a collection with more fields than are
4
+ in the collection's schema
5
+ * Previous behavior was to ignore additional fields, but this was found to be
6
+ error-prone
7
+ * Remove builtin support for BUST (web services API); this avoids the need to
8
+ depend on the json, nestful and i18n gems.
9
+
10
+ == 0.9.0 / 2012-03-21
2
11
 
3
12
  * Major performance enhancements
4
13
  * Much, much faster: rewritten runtime that now uses a push-based dataflow
data/docs/cheat.md CHANGED
@@ -295,14 +295,14 @@ Like `pairs`, but implicitly includes a block that projects down to the left ite
295
295
  `rights(`*hash pairs*`)`:
296
296
  Like `pairs`, but implicitly includes a block that projects down to the right item in each pair.
297
297
 
298
+ `outer(`*hash pairs*`)`:<br>
299
+ Left Outer Join. Like `pairs`, but items in the first collection will be produced nil-padded if they have no match in the second collection.
300
+
298
301
  `flatten`:<br>
299
302
  `flatten` is a bit like SQL's `SELECT *`: it produces a collection of concatenated objects, with a schema that is the concatenation of the schemas in tablelist (with duplicate names disambiguated). Useful for chaining to operators that expect input collections with schemas, e.g., `group`:
300
303
 
301
304
  out <= (r * s).matches.flatten.group([:a], max(:b))
302
305
 
303
- `outer(`*hash pairs*`)`:<br>
304
- Left Outer Join. Like `pairs`, but items in the first collection will be produced nil-padded if they have no match in the second collection.
305
-
306
306
  ## Temp Collections ##
307
307
  `temp`<br>
308
308
  Temp collections are scratches defined within a `bloom` block:
File without changes
data/lib/bud/bud_meta.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'bud/rewrite'
2
- require 'pp'
3
2
 
4
3
 
5
4
  class BudMeta #:nodoc: all
@@ -16,9 +15,12 @@ class BudMeta #:nodoc: all
16
15
  if @bud_instance.toplevel == @bud_instance
17
16
  nodes, stratum_map, top_stratum = stratify_preds
18
17
 
19
- # stratum_map = {fully qualified pred => stratum}
18
+ # stratum_map = {fully qualified pred => stratum}. Copy stratum_map data
19
+ # into t_stratum format.
20
+ raise unless @bud_instance.t_stratum.to_a.empty?
21
+ @bud_instance.t_stratum <= stratum_map.to_a
20
22
 
21
- #slot each rule into the stratum corresponding to its lhs pred (from stratum_map)
23
+ # slot each rule into the stratum corresponding to its lhs pred (from stratum_map)
22
24
  stratified_rules = Array.new(top_stratum + 2) { [] } # stratum -> [ rules ]
23
25
  @bud_instance.t_rules.each do |rule|
24
26
  if rule.op.to_s == '<='
@@ -70,8 +72,11 @@ class BudMeta #:nodoc: all
70
72
  pt = klass.__bloom_asts__[block_name]
71
73
  return if pt.nil?
72
74
 
73
- pt = Marshal.load(Marshal.dump(pt)) #deep clone because RuleRewriter mucks up pt.
74
- pp pt if @bud_instance.options[:dump_ast]
75
+ pt = Marshal.load(Marshal.dump(pt)) # deep copy because RuleRewriter mucks up pt
76
+ if @bud_instance.options[:dump_ast]
77
+ require 'pp'
78
+ pp pt
79
+ end
75
80
  tmp_expander = TempExpander.new
76
81
  pt = tmp_expander.process(pt)
77
82
  tmp_expander.tmp_tables.each do |t|
@@ -104,9 +109,9 @@ class BudMeta #:nodoc: all
104
109
 
105
110
  def get_qual_name(pt)
106
111
  # expect to see a parse tree corresponding to a dotted name
107
- # a.b.c == s(:call, s1, :c, (:args))
108
- # where s1 == s(:call, s2, :b, (:args))
109
- # where s2 == s(:call, nil,:a, (:args))
112
+ # a.b.c == s(:call, s1, :c, (:args))
113
+ # where s1 == s(:call, s2, :b, (:args))
114
+ # where s2 == s(:call, nil, :a, (:args))
110
115
 
111
116
  tag, recv, name, args = pt
112
117
  return nil unless tag == :call or args.length == 1
@@ -114,7 +119,7 @@ class BudMeta #:nodoc: all
114
119
  if recv
115
120
  qn = get_qual_name(recv)
116
121
  return nil if qn.nil? or qn.size == 0
117
- qn = qn + "." + name.to_s
122
+ qn = "#{qn}.#{name}"
118
123
  else
119
124
  qn = name.to_s
120
125
  end
@@ -147,11 +152,12 @@ class BudMeta #:nodoc: all
147
152
  return pt unless n.sexp_type == :call and n.length == 4
148
153
 
149
154
  # Rule format: call tag, lhs, op, rhs
150
- tag, lhs, op, rhs = n
155
+ _, lhs, op, rhs = n
151
156
 
152
157
  # Check that LHS references a named collection
153
158
  lhs_name = get_qual_name(lhs)
154
- unless lhs_name and @bud_instance.tables.has_key? lhs_name.to_sym
159
+ return [n, "Unexpected lhs format: #{lhs}"] if lhs.nil?
160
+ unless @bud_instance.tables.has_key? lhs_name.to_sym
155
161
  return [n, "Collection does not exist: '#{lhs_name}'"]
156
162
  end
157
163
 
@@ -242,19 +248,21 @@ class BudMeta #:nodoc: all
242
248
  preds_in_body = nodes.select {|_, node| node.in_body}.map {|name, _| name}.to_set
243
249
 
244
250
  bud = @bud_instance
251
+ out = bud.options[:stdout]
252
+ out ||= $stdout
245
253
  bud.t_provides.each do |p|
246
254
  pred, input = p.interface, p.input
247
255
  if input
248
256
  unless preds_in_body.include? pred.to_s
249
257
  # input interface is underspecified if not used in any rule body
250
258
  bud.t_underspecified << [pred, true] # true indicates input mode
251
- puts "Warning: input interface #{pred} not used"
259
+ out.puts "Warning: input interface #{pred} not used"
252
260
  end
253
261
  else
254
262
  unless preds_in_lhs.include? pred.to_s
255
263
  # output interface underspecified if not in any rule's lhs
256
264
  bud.t_underspecified << [pred, false] #false indicates output mode.
257
- puts "Warning: output interface #{pred} not used"
265
+ out.puts "Warning: output interface #{pred} not used"
258
266
  end
259
267
  end
260
268
  end
@@ -21,10 +21,9 @@ module Bud
21
21
  attr_reader :cols, :key_cols # :nodoc: all
22
22
  attr_reader :struct
23
23
  attr_reader :storage, :delta, :new_delta, :pending, :tick_delta # :nodoc: all
24
- attr_accessor :qualified_tabname
25
- attr_accessor :invalidated, :to_delete, :rescan
24
+ attr_reader :wired_by, :to_delete
25
+ attr_accessor :invalidated, :rescan
26
26
  attr_accessor :is_source
27
- attr_accessor :wired_by
28
27
  attr_accessor :accumulate_tick_deltas # updated in bud.do_wiring
29
28
 
30
29
  def initialize(name, bud_instance, given_schema=nil, defer_schema=false) # :nodoc: all
@@ -50,6 +49,7 @@ module Bud
50
49
  given_schema ||= {[:key]=>[:val]}
51
50
  @given_schema = given_schema
52
51
  @cols, @key_cols = BudCollection.parse_schema(given_schema)
52
+
53
53
  # Check that no location specifiers appear in the schema. In the case of
54
54
  # channels, the location specifier has already been stripped from the
55
55
  # user-specified schema.
@@ -59,18 +59,19 @@ module Bud
59
59
  end
60
60
  end
61
61
 
62
- if @cols.size == 0
62
+ @key_colnums = @key_cols.map {|k| @cols.index(k)}
63
+
64
+ if @cols.empty?
63
65
  @cols = nil
64
66
  else
65
67
  @struct = ($struct_classes[@cols] ||= Struct.new(*@cols))
66
68
  @structlen = @struct.members.length
67
69
  end
68
- @key_colnums = key_cols.map {|k| @cols.index(k)}
69
70
  setup_accessors
70
71
  end
71
72
 
72
73
  def qualified_tabname
73
- @qualified_tabname ||= @bud_instance.toplevel? ? tabname : (@bud_instance.qualified_name + "." + tabname.to_s).to_sym
74
+ @qualified_tabname ||= @bud_instance.toplevel? ? tabname : "#{@bud_instance.qualified_name}.#{tabname}".to_sym
74
75
  end
75
76
 
76
77
  # The user-specified schema might come in two forms: a hash of Array =>
@@ -168,7 +169,7 @@ module Bud
168
169
  # project the collection to its key attributes
169
170
  public
170
171
  def keys
171
- self.pro{|t| @key_colnums.map {|i| t[i]}}
172
+ self.pro{|t| get_key_vals(t)}
172
173
  end
173
174
 
174
175
  # project the collection to its non-key attributes
@@ -181,33 +182,19 @@ module Bud
181
182
  public
182
183
  def inspected
183
184
  self.pro{|t| [t.inspect]}
184
- # how about when this is called outside wiring?
185
- # [["#{@tabname}: [#{self.map{|t| "\n (#{t.map{|v| v.inspect}.join ", "})"}}]"]]
186
185
  end
187
186
 
188
187
  # projection
189
188
  public
190
189
  def pro(the_name=tabname, the_schema=schema, &blk)
191
- pusher = to_push_elem(the_name, the_schema)
192
- pusher_pro = pusher.pro(&blk)
193
- pusher_pro.elem_name = the_name
194
- pusher_pro.tabname = the_name
195
- pusher_pro
196
- end
197
-
198
- public
199
- def each_with_index(the_name=tabname, the_schema=schema, &blk)
200
- toplevel = @bud_instance.toplevel
201
- if not toplevel.done_wiring
202
- proj = pro(the_name, the_schema)
203
- elem = Bud::PushEachWithIndex.new('each_with_index' + object_id.to_s,
204
- toplevel.this_rule_context, tabname)
205
- elem.set_block(&blk)
206
- proj.wire_to(elem)
207
- toplevel.push_elems[[self.object_id, :each, blk]] = elem
208
- elem
190
+ if @bud_instance.wiring?
191
+ pusher = to_push_elem(the_name, the_schema)
192
+ pusher_pro = pusher.pro(&blk)
193
+ pusher_pro.elem_name = the_name
194
+ pusher_pro.tabname = the_name
195
+ pusher_pro
209
196
  else
210
- storage.each_with_index
197
+ @storage.map(&blk)
211
198
  end
212
199
  end
213
200
 
@@ -217,40 +204,42 @@ module Bud
217
204
  # accum.
218
205
  public
219
206
  def flat_map(&blk)
220
- pusher = self.pro(&blk)
221
- toplevel = @bud_instance.toplevel
222
- elem = Bud::PushElement.new(tabname, toplevel.this_rule_context, tabname)
223
- pusher.wire_to(elem)
224
- f = Proc.new do |t|
225
- t.each do |i|
226
- elem.push_out(i, false)
207
+ if @bud_instance.wiring?
208
+ pusher = self.pro(&blk)
209
+ toplevel = @bud_instance.toplevel
210
+ elem = Bud::PushElement.new(tabname, toplevel.this_rule_context, tabname)
211
+ pusher.wire_to(elem)
212
+ f = Proc.new do |t|
213
+ t.each do |i|
214
+ elem.push_out(i, false)
215
+ end
216
+ nil
227
217
  end
228
- nil
218
+ elem.set_block(&f)
219
+ toplevel.push_elems[[self.object_id, :flatten]] = elem
220
+ return elem
221
+ else
222
+ @storage.flat_map(&blk)
229
223
  end
230
- elem.set_block(&f)
231
- toplevel.push_elems[[self.object_id, :flatten]] = elem
232
- return elem
233
224
  end
234
225
 
235
226
  public
236
227
  def sort(&blk)
237
- pusher = self.pro
238
- pusher.sort("sort#{object_id}", @bud_instance, @cols, &blk)
228
+ if @bud_instance.wiring?
229
+ pusher = self.pro
230
+ pusher.sort("sort#{object_id}", @bud_instance, @cols, &blk)
231
+ else
232
+ @storage.sort
233
+ end
239
234
  end
240
235
 
241
- def rename(the_name, the_schema=nil)
236
+ def rename(the_name, the_schema=nil, &blk)
237
+ raise unless @bud_instance.wiring?
242
238
  # a scratch with this name should have been defined during rewriting
243
- raise(Bud::Error, "rename failed to define a scratch named #{the_name}") unless @bud_instance.respond_to? the_name
244
- retval = pro(the_name, the_schema)
245
- #retval.init_schema(the_schema)
246
- retval
239
+ raise Bud::Error, "rename failed to define a scratch named #{the_name}" unless @bud_instance.respond_to? the_name
240
+ pro(the_name, the_schema, &blk)
247
241
  end
248
242
 
249
- # def to_enum
250
- # pusher = self.pro
251
- # pusher.to_enum
252
- # end
253
-
254
243
  # By default, all tuples in any rhs are in storage or delta. Tuples in
255
244
  # new_delta will get transitioned to delta in the next iteration of the
256
245
  # evaluator (but within the current time tick).
@@ -377,9 +366,9 @@ module Bud
377
366
  end
378
367
 
379
368
  private
380
- def raise_pk_error(new_guy, old)
369
+ def raise_pk_error(new, old)
381
370
  key = get_key_vals(old)
382
- raise Bud::KeyConstraintError, "key conflict inserting #{new_guy.inspect} into \"#{tabname}\": existing tuple #{old.inspect}, key = #{key.inspect}"
371
+ raise Bud::KeyConstraintError, "key conflict inserting #{new.inspect} into \"#{tabname}\": existing tuple #{old.inspect}, key = #{key.inspect}"
383
372
  end
384
373
 
385
374
  private
@@ -396,7 +385,9 @@ module Bud
396
385
  raise Bud::TypeError, "array or struct type expected in \"#{qualified_tabname}\": #{o.inspect}"
397
386
  end
398
387
 
399
- o = o.take(@structlen) if o.length > @structlen
388
+ if o.length > @structlen
389
+ raise Bud::TypeError, "too many columns for \"#{qualified_tabname}\": #{o.inspect}"
390
+ end
400
391
  return @struct.new(*o)
401
392
  end
402
393
 
@@ -406,7 +397,7 @@ module Bud
406
397
  end
407
398
 
408
399
  public
409
- def do_insert(o, store)
400
+ def do_insert(t, store)
410
401
  if $BUD_DEBUG
411
402
  storetype = case store.object_id
412
403
  when @storage.object_id; "storage"
@@ -414,17 +405,22 @@ module Bud
414
405
  when @delta.object_id; "delta"
415
406
  when @new_delta.object_id; "new_delta"
416
407
  end
417
- puts "#{qualified_tabname}.#{storetype} ==> #{o}"
408
+ puts "#{qualified_tabname}.#{storetype} ==> #{t}"
418
409
  end
419
- return if o.nil? # silently ignore nils resulting from map predicates failing
420
- o = prep_tuple(o)
421
- key = get_key_vals(o)
410
+ return if t.nil? # silently ignore nils resulting from map predicates failing
411
+ t = prep_tuple(t)
412
+ key = get_key_vals(t)
413
+ merge_to_buf(store, key, t, store[key])
414
+ end
422
415
 
423
- old = store[key]
424
- if old.nil?
425
- store[key] = o
426
- else
427
- raise_pk_error(o, old) unless old == o
416
+ # Merge "tup" with key values "key" into "buf". "old" is an existing tuple
417
+ # with the same key columns as "tup" (if any such tuple exists).
418
+ private
419
+ def merge_to_buf(buf, key, tup, old)
420
+ if old.nil? # no matching tuple found
421
+ buf[key] = tup
422
+ elsif old != tup # ignore duplicates
423
+ raise_pk_error(tup, old)
428
424
  end
429
425
  end
430
426
 
@@ -487,9 +483,13 @@ module Bud
487
483
  end
488
484
  end
489
485
 
486
+ # This is used for two quite different purposes. If given a Bud collection
487
+ # or dataflow element as an input, we assume we're being called to wire up
488
+ # the push-based dataflow. If given an Enumerable consisting of Bud tuples,
489
+ # we assume we're being called to insert the tuples (e.g., to support direct
490
+ # insertion of tuples into Bud collections in a sync_do block).
490
491
  public
491
492
  def merge(o, buf=@delta) # :nodoc: all
492
- toplevel = @bud_instance.toplevel
493
493
  if o.class <= Bud::PushElement
494
494
  add_merge_target
495
495
  deduce_schema(o) if @cols.nil?
@@ -498,7 +498,7 @@ module Bud
498
498
  add_merge_target
499
499
  deduce_schema(o) if @cols.nil?
500
500
  o.pro.wire_to self
501
- elsif o.class <= Proc and toplevel.done_bootstrap and not toplevel.done_wiring and not o.nil?
501
+ elsif o.class <= Proc
502
502
  add_merge_target
503
503
  tbl = register_coll_expr(o)
504
504
  tbl.pro.wire_to self
@@ -510,15 +510,13 @@ module Bud
510
510
  o.each {|i| do_insert(i, buf)}
511
511
  end
512
512
  end
513
- return self
514
513
  end
515
514
 
516
515
  def register_coll_expr(expr)
517
516
  coll_name = "expr_#{expr.object_id}"
518
517
  cols = (1..@cols.length).map{|i| "c#{i}".to_sym} unless @cols.nil?
519
518
  @bud_instance.coll_expr(coll_name.to_sym, expr, cols)
520
- coll = @bud_instance.send(coll_name)
521
- coll
519
+ @bud_instance.send(coll_name)
522
520
  end
523
521
 
524
522
  public
@@ -530,26 +528,12 @@ module Bud
530
528
  # buffer items to be merged atomically at end of this timestep
531
529
  public
532
530
  def pending_merge(o) # :nodoc: all
533
- toplevel = @bud_instance.toplevel
534
- if o.class <= Bud::PushElement
535
- add_merge_target
536
- o.wire_to_pending self
537
- elsif o.class <= Bud::BudCollection
538
- add_merge_target
539
- o.pro.wire_to_pending self
540
- elsif o.class <= Proc and toplevel.done_bootstrap and not toplevel.done_wiring
541
- add_merge_target
542
- tbl = register_coll_expr(o) unless o.nil?
543
- tbl.pro.wire_to_pending self
544
- else
545
- unless o.nil?
546
- o = o.uniq.compact if o.respond_to?(:uniq)
547
- check_enumerable(o)
548
- establish_schema(o) if @cols.nil?
549
- o.each{|i| self.do_insert(i, @pending)}
550
- end
531
+ unless o.nil?
532
+ o = o.uniq.compact if o.respond_to?(:uniq)
533
+ check_enumerable(o)
534
+ establish_schema(o) if @cols.nil?
535
+ o.each{|i| self.do_insert(i, @pending)}
551
536
  end
552
- return self
553
537
  end
554
538
 
555
539
  public
@@ -557,7 +541,19 @@ module Bud
557
541
 
558
542
  public
559
543
  superator "<+" do |o|
560
- pending_merge o
544
+ if o.class <= Bud::PushElement
545
+ add_merge_target
546
+ o.wire_to(self, :pending)
547
+ elsif o.class <= Bud::BudCollection
548
+ add_merge_target
549
+ o.pro.wire_to(self, :pending)
550
+ elsif o.class <= Proc
551
+ add_merge_target
552
+ tbl = register_coll_expr(o)
553
+ tbl.pro.wire_to(self, :pending)
554
+ else
555
+ pending_merge(o)
556
+ end
561
557
  end
562
558
 
563
559
  def tick
@@ -578,15 +574,10 @@ module Bud
578
574
  unless @new_delta.empty?
579
575
  puts "#{qualified_tabname}.tick_delta new_delta --> delta (#{@new_delta.size} elems)" if $BUD_DEBUG
580
576
 
581
- # XXX: what about multiple delta tuples produced in the same tick that
582
- # conflict on the PK?
583
- @new_delta.each_pair do |k, v|
584
- sv = @storage[k]
585
- if sv.nil?
586
- @delta[k] = v
587
- else
588
- raise_pk_error(v, sv) unless v == sv
589
- end
577
+ # NB: key conflicts between different new_delta tuples are detected in
578
+ # do_insert().
579
+ @new_delta.each_pair do |key, tup|
580
+ merge_to_buf(@delta, key, tup, @storage[key])
590
581
  end
591
582
  @new_delta.clear
592
583
  return !(@delta.empty?)
@@ -633,8 +624,10 @@ module Bud
633
624
  this_stratum = toplevel.this_stratum
634
625
  oid = self.object_id
635
626
  unless toplevel.scanners[this_stratum][[oid, the_name]]
636
- toplevel.scanners[this_stratum][[oid, the_name]] = Bud::ScannerElement.new(the_name, self.bud_instance, self, the_schema)
637
- toplevel.push_sources[this_stratum][[oid, the_name]] = toplevel.scanners[this_stratum][[oid, the_name]]
627
+ scanner = Bud::ScannerElement.new(the_name, @bud_instance,
628
+ self, the_schema)
629
+ toplevel.scanners[this_stratum][[oid, the_name]] = scanner
630
+ toplevel.push_sources[this_stratum][[oid, the_name]] = scanner
638
631
  end
639
632
  return toplevel.scanners[this_stratum][[oid, the_name]]
640
633
  end
@@ -709,11 +702,8 @@ module Bud
709
702
  col.class <= Symbol ? self.send(col) : col
710
703
  end
711
704
 
712
- # alias reduce inject
713
705
  def reduce(initial, &blk)
714
- elem1 = to_push_elem
715
- red_elem = elem1.reduce(initial, &blk)
716
- return red_elem
706
+ return to_push_elem.reduce(initial, &blk)
717
707
  end
718
708
 
719
709
  public
@@ -867,17 +857,18 @@ module Bud
867
857
  public
868
858
  def flush # :nodoc: all
869
859
  toplevel = @bud_instance.toplevel
870
- ip = toplevel.ip
871
- port = toplevel.port
872
860
  @pending.each_value do |t|
873
861
  if @is_loopback
862
+ ip = toplevel.ip
863
+ port = toplevel.port
874
864
  the_locspec = [ip, port]
875
865
  else
876
866
  the_locspec = split_locspec(t, @locspec_idx)
877
867
  raise Bud::Error, "'#{t[@locspec_idx]}', channel '#{@tabname}'" if the_locspec[0].nil? or the_locspec[1].nil? or the_locspec[0] == '' or the_locspec[1] == ''
878
868
  end
879
869
  puts "channel #{qualified_tabname}.send: #{t}" if $BUD_DEBUG
880
- toplevel.dsock.send_datagram([qualified_tabname.to_s, t].to_msgpack, the_locspec[0], the_locspec[1])
870
+ toplevel.dsock.send_datagram([qualified_tabname.to_s, t].to_msgpack,
871
+ the_locspec[0], the_locspec[1])
881
872
  end
882
873
  @pending.clear
883
874
  end
@@ -903,7 +894,12 @@ module Bud
903
894
 
904
895
  superator "<~" do |o|
905
896
  if o.class <= Bud::PushElement
906
- o.wire_to_pending self
897
+ o.wire_to(self, :pending)
898
+ elsif o.class <= Bud::BudCollection
899
+ o.pro.wire_to(self, :pending)
900
+ elsif o.class <= Proc
901
+ tbl = register_coll_expr(o)
902
+ tbl.pro.wire_to(self, :pending)
907
903
  else
908
904
  pending_merge(o)
909
905
  end
@@ -1001,7 +997,12 @@ module Bud
1001
997
 
1002
998
  superator "<~" do |o|
1003
999
  if o.class <= Bud::PushElement
1004
- o.wire_to_pending self
1000
+ o.wire_to(self, :pending)
1001
+ elsif o.class <= Bud::BudCollection
1002
+ o.pro.wire_to(self, :pending)
1003
+ elsif o.class <= Proc
1004
+ tbl = register_coll_expr(o)
1005
+ tbl.pro.wire_to(self, :pending)
1005
1006
  else
1006
1007
  pending_merge(o)
1007
1008
  end
@@ -1072,27 +1073,22 @@ module Bud
1072
1073
  @tick_delta.clear
1073
1074
  deleted = nil
1074
1075
  @to_delete.each do |tuple|
1075
- keycols = @key_colnums.map{|k| tuple[k]}
1076
+ keycols = get_key_vals(tuple)
1076
1077
  if @storage[keycols] == tuple
1077
1078
  v = @storage.delete keycols
1078
1079
  deleted ||= v
1079
1080
  end
1080
1081
  end
1081
1082
  @to_delete_by_key.each do |tuple|
1082
- v = @storage.delete @key_colnums.map{|k| tuple[k]}
1083
+ v = @storage.delete(get_key_vals(tuple))
1083
1084
  deleted ||= v
1084
1085
  end
1085
1086
 
1086
1087
  @invalidated = (not deleted.nil?)
1087
1088
  puts "table #{qualified_tabname} invalidated" if $BUD_DEBUG and @invalidated
1088
1089
 
1089
- @pending.each do |keycols, tuple|
1090
- old = @storage[keycols]
1091
- if old.nil?
1092
- @delta[keycols] = tuple
1093
- else
1094
- raise_pk_error(tuple, old) unless tuple == old
1095
- end
1090
+ @pending.each do |key, tup|
1091
+ merge_to_buf(@delta, key, tup, @storage[key])
1096
1092
  end
1097
1093
  @to_delete = []
1098
1094
  @to_delete_by_key = []
@@ -1100,21 +1096,20 @@ module Bud
1100
1096
  end
1101
1097
 
1102
1098
  def invalidated=(val)
1103
- raise "Internal error: must not set invalidate on tables"
1099
+ raise Bud::Error, "internal error: must not set invalidate on tables"
1104
1100
  end
1105
1101
 
1106
1102
  def pending_delete(o)
1107
- toplevel = @bud_instance.toplevel
1108
1103
  if o.class <= Bud::PushElement
1109
1104
  add_merge_target
1110
- o.wire_to_delete self
1105
+ o.wire_to(self, :delete)
1111
1106
  elsif o.class <= Bud::BudCollection
1112
1107
  add_merge_target
1113
- o.pro.wire_to_delete self
1114
- elsif o.class <= Proc and @bud_instance.toplevel.done_bootstrap and not toplevel.done_wiring
1108
+ o.pro.wire_to(self, :delete)
1109
+ elsif o.class <= Proc
1115
1110
  add_merge_target
1116
1111
  tbl = register_coll_expr(o)
1117
- tbl.pro.wire_to_delete self
1112
+ tbl.pro.wire_to(self, :delete)
1118
1113
  else
1119
1114
  unless o.nil?
1120
1115
  o = o.uniq.compact if o.respond_to?(:uniq)
@@ -1130,14 +1125,13 @@ module Bud
1130
1125
 
1131
1126
  public
1132
1127
  def pending_delete_keys(o)
1133
- toplevel = @bud_instance.toplevel
1134
1128
  if o.class <= Bud::PushElement
1135
- o.wire_to_delete_by_key self
1129
+ o.wire_to(self, :delete_by_key)
1136
1130
  elsif o.class <= Bud::BudCollection
1137
- o.pro.wire_to_delete_by_key self
1138
- elsif o.class <= Proc and @bud_instance.toplevel.done_bootstrap and not @bud_instance.toplevel.done_wiring
1131
+ o.pro.wire_to(self, :delete_by_key)
1132
+ elsif o.class <= Proc
1139
1133
  tbl = register_coll_expr(o)
1140
- tbl.pro.wire_to_delete_by_key self
1134
+ tbl.pro.wire_to(self, :delete_by_key)
1141
1135
  else
1142
1136
  unless o.nil?
1143
1137
  o = o.uniq.compact if o.respond_to?(:uniq)
@@ -1146,7 +1140,6 @@ module Bud
1146
1140
  o.each{|i| @to_delete_by_key << prep_tuple(i)}
1147
1141
  end
1148
1142
  end
1149
- o
1150
1143
  end
1151
1144
 
1152
1145
  public