bud 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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