bud 0.0.2
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/LICENSE +9 -0
- data/README +30 -0
- data/bin/budplot +134 -0
- data/bin/budvis +201 -0
- data/bin/rebl +4 -0
- data/docs/README.md +13 -0
- data/docs/bfs.md +379 -0
- data/docs/bfs.raw +251 -0
- data/docs/bfs_arch.png +0 -0
- data/docs/bloom-loop.png +0 -0
- data/docs/bust.md +83 -0
- data/docs/cheat.md +291 -0
- data/docs/deploy.md +96 -0
- data/docs/diffs +181 -0
- data/docs/getstarted.md +296 -0
- data/docs/intro.md +36 -0
- data/docs/modules.md +112 -0
- data/docs/operational.md +96 -0
- data/docs/rebl.md +99 -0
- data/docs/ruby_hooks.md +19 -0
- data/docs/visualizations.md +75 -0
- data/examples/README +1 -0
- data/examples/basics/hello.rb +12 -0
- data/examples/basics/out +1103 -0
- data/examples/basics/out.new +856 -0
- data/examples/basics/paths.rb +51 -0
- data/examples/bust/README.md +9 -0
- data/examples/bust/bustclient-example.rb +23 -0
- data/examples/bust/bustinspector.html +135 -0
- data/examples/bust/bustserver-example.rb +18 -0
- data/examples/chat/README.md +9 -0
- data/examples/chat/chat.rb +45 -0
- data/examples/chat/chat_protocol.rb +8 -0
- data/examples/chat/chat_server.rb +29 -0
- data/examples/deploy/tokenring-ec2.rb +26 -0
- data/examples/deploy/tokenring-local.rb +17 -0
- data/examples/deploy/tokenring.rb +39 -0
- data/lib/bud/aggs.rb +126 -0
- data/lib/bud/bud_meta.rb +185 -0
- data/lib/bud/bust/bust.rb +126 -0
- data/lib/bud/bust/client/idempotence.rb +10 -0
- data/lib/bud/bust/client/restclient.rb +49 -0
- data/lib/bud/collections.rb +937 -0
- data/lib/bud/depanalysis.rb +44 -0
- data/lib/bud/deploy/countatomicdelivery.rb +50 -0
- data/lib/bud/deploy/deployer.rb +67 -0
- data/lib/bud/deploy/ec2deploy.rb +200 -0
- data/lib/bud/deploy/localdeploy.rb +41 -0
- data/lib/bud/errors.rb +15 -0
- data/lib/bud/graphs.rb +405 -0
- data/lib/bud/joins.rb +300 -0
- data/lib/bud/rebl.rb +314 -0
- data/lib/bud/rewrite.rb +523 -0
- data/lib/bud/rtrace.rb +27 -0
- data/lib/bud/server.rb +43 -0
- data/lib/bud/state.rb +108 -0
- data/lib/bud/storage/tokyocabinet.rb +170 -0
- data/lib/bud/storage/zookeeper.rb +178 -0
- data/lib/bud/stratify.rb +83 -0
- data/lib/bud/viz.rb +65 -0
- data/lib/bud.rb +797 -0
- metadata +330 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'tokyocabinet'
|
2
|
+
|
3
|
+
module Bud
|
4
|
+
# Persistent table implementation based on TokyoCabinet.
|
5
|
+
class BudTcTable < BudCollection # :nodoc: all
|
6
|
+
def initialize(name, bud_instance, given_schema)
|
7
|
+
tc_dir = bud_instance.options[:tc_dir]
|
8
|
+
raise BudError, "TC support must be enabled via 'tc_dir'" unless tc_dir
|
9
|
+
unless File.exists?(tc_dir)
|
10
|
+
Dir.mkdir(tc_dir)
|
11
|
+
puts "Created directory: #{tc_dir}" unless bud_instance.options[:quiet]
|
12
|
+
end
|
13
|
+
|
14
|
+
dirname = "#{tc_dir}/bud_#{bud_instance.port}"
|
15
|
+
unless File.exists?(dirname)
|
16
|
+
Dir.mkdir(dirname)
|
17
|
+
puts "Created directory: #{dirname}" unless bud_instance.options[:quiet]
|
18
|
+
end
|
19
|
+
|
20
|
+
super(name, bud_instance, given_schema)
|
21
|
+
@to_delete = []
|
22
|
+
|
23
|
+
@hdb = TokyoCabinet::HDB.new
|
24
|
+
db_fname = "#{dirname}/#{name}.tch"
|
25
|
+
flags = TokyoCabinet::HDB::OWRITER | TokyoCabinet::HDB::OCREAT
|
26
|
+
if bud_instance.options[:tc_truncate] == true
|
27
|
+
flags |= TokyoCabinet::HDB::OTRUNC
|
28
|
+
end
|
29
|
+
if !@hdb.open(db_fname, flags)
|
30
|
+
raise BudError, "Failed to open TokyoCabinet DB '#{db_fname}': #{@hdb.errmsg}"
|
31
|
+
end
|
32
|
+
@hdb.tranbegin
|
33
|
+
end
|
34
|
+
|
35
|
+
def init_storage
|
36
|
+
# XXX: we can't easily use the @storage infrastructure provided by
|
37
|
+
# BudCollection; issue #33
|
38
|
+
@storage = nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](key)
|
42
|
+
key_s = MessagePack.pack(key)
|
43
|
+
val_s = @hdb[key_s]
|
44
|
+
if val_s
|
45
|
+
return make_tuple(key, MessagePack.unpack(val_s))
|
46
|
+
else
|
47
|
+
return @delta[key]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def has_key?(k)
|
52
|
+
key_s = MessagePack.pack(k)
|
53
|
+
return true if @hdb.has_key? key_s
|
54
|
+
return @delta.has_key? k
|
55
|
+
end
|
56
|
+
|
57
|
+
def include?(tuple)
|
58
|
+
key = @key_colnums.map{|k| tuple[k]}
|
59
|
+
value = self[key]
|
60
|
+
return (value == tuple)
|
61
|
+
end
|
62
|
+
|
63
|
+
def make_tuple(k_ary, v_ary)
|
64
|
+
t = Array.new(k_ary.length + v_ary.length)
|
65
|
+
@key_colnums.each_with_index do |k,i|
|
66
|
+
t[k] = k_ary[i]
|
67
|
+
end
|
68
|
+
val_cols.each_with_index do |c,i|
|
69
|
+
t[schema.index(c)] = v_ary[i]
|
70
|
+
end
|
71
|
+
tuple_accessors(t)
|
72
|
+
end
|
73
|
+
|
74
|
+
def each(&block)
|
75
|
+
each_from([@delta], &block)
|
76
|
+
each_storage(&block)
|
77
|
+
end
|
78
|
+
|
79
|
+
def each_from(bufs, &block)
|
80
|
+
bufs.each do |b|
|
81
|
+
if b == @storage then
|
82
|
+
each_storage(&block)
|
83
|
+
else
|
84
|
+
b.each_value do |v|
|
85
|
+
yield v
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def each_storage(&block)
|
92
|
+
@hdb.each do |k,v|
|
93
|
+
k_ary = MessagePack.unpack(k)
|
94
|
+
v_ary = MessagePack.unpack(v)
|
95
|
+
yield make_tuple(k_ary, v_ary)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def flush
|
100
|
+
@hdb.trancommit
|
101
|
+
end
|
102
|
+
|
103
|
+
def close
|
104
|
+
@hdb.close
|
105
|
+
end
|
106
|
+
|
107
|
+
def merge_to_hdb(buf)
|
108
|
+
buf.each do |key,tuple|
|
109
|
+
merge_tuple(key, tuple)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def merge_tuple(key, tuple)
|
114
|
+
val = val_cols.map{|c| tuple[schema.index(c)]}
|
115
|
+
key_s = MessagePack.pack(key)
|
116
|
+
val_s = MessagePack.pack(val)
|
117
|
+
if @hdb.putkeep(key_s, val_s) == false
|
118
|
+
old_tuple = self[key]
|
119
|
+
raise_pk_error(tuple, old_tuple) if tuple != old_tuple
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# move deltas to TC, and new_deltas to deltas
|
124
|
+
def tick_deltas
|
125
|
+
merge_to_hdb(@delta)
|
126
|
+
@delta = @new_delta
|
127
|
+
@new_delta = {}
|
128
|
+
end
|
129
|
+
|
130
|
+
superator "<-" do |o|
|
131
|
+
o.each do |tuple|
|
132
|
+
@to_delete << tuple unless tuple.nil?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def insert(tuple)
|
137
|
+
key = @key_colnums.map{|k| tuple[k]}
|
138
|
+
merge_tuple(key, tuple)
|
139
|
+
end
|
140
|
+
|
141
|
+
alias << insert
|
142
|
+
|
143
|
+
# Remove to_delete and then add pending to HDB
|
144
|
+
def tick
|
145
|
+
@to_delete.each do |tuple|
|
146
|
+
k = @key_colnums.map{|c| tuple[c]}
|
147
|
+
k_str = MessagePack.pack(k)
|
148
|
+
cols_str = @hdb[k_str]
|
149
|
+
unless cols_str.nil?
|
150
|
+
hdb_cols = MessagePack.unpack(cols_str)
|
151
|
+
delete_cols = val_cols.map{|c| tuple[schema.index(c)]}
|
152
|
+
if hdb_cols == delete_cols
|
153
|
+
@hdb.delete k_str
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
@to_delete = []
|
158
|
+
|
159
|
+
merge_to_hdb(@pending)
|
160
|
+
@pending = {}
|
161
|
+
|
162
|
+
@hdb.trancommit
|
163
|
+
@hdb.tranbegin
|
164
|
+
end
|
165
|
+
|
166
|
+
def method_missing(sym, *args, &block)
|
167
|
+
@hdb.send sym, *args, &block
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,178 @@
|
|
1
|
+
begin
|
2
|
+
require 'zookeeper'
|
3
|
+
Bud::HAVE_ZOOKEEPER = true
|
4
|
+
rescue LoadError
|
5
|
+
end
|
6
|
+
|
7
|
+
module Bud
|
8
|
+
# Persistent table implementation based on Zookeeper.
|
9
|
+
class BudZkTable < BudCollection # :nodoc: all
|
10
|
+
def initialize(name, zk_path, zk_addr, bud_instance)
|
11
|
+
unless defined? HAVE_ZOOKEEPER
|
12
|
+
raise BudError, "zookeeper gem is not installed: zktables cannot be used"
|
13
|
+
end
|
14
|
+
|
15
|
+
# schema = {[:key] => [:val]}
|
16
|
+
super(name, bud_instance, nil)
|
17
|
+
|
18
|
+
zk_path = zk_path.chomp("/") unless zk_path == "/"
|
19
|
+
@zk = Zookeeper.new(zk_addr)
|
20
|
+
@zk_path = zk_path
|
21
|
+
@base_path = @zk_path
|
22
|
+
@base_path += "/" unless @zk_path.end_with? "/"
|
23
|
+
@store_mutex = Mutex.new
|
24
|
+
@next_storage = {}
|
25
|
+
@saw_delta = false
|
26
|
+
@child_watch_id = nil
|
27
|
+
@stat_watch_id = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
# Since the watcher callbacks might invoke EventMachine, we wait until after
|
31
|
+
# EM startup to start watching for Zk events.
|
32
|
+
def start_watchers
|
33
|
+
# NB: Watcher callbacks are invoked in a separate Ruby thread.
|
34
|
+
@child_watcher = Zookeeper::WatcherCallback.new { get_and_watch }
|
35
|
+
@stat_watcher = Zookeeper::WatcherCallback.new { stat_and_watch }
|
36
|
+
stat_and_watch
|
37
|
+
end
|
38
|
+
|
39
|
+
def clone_empty
|
40
|
+
raise BudError
|
41
|
+
end
|
42
|
+
|
43
|
+
def stat_and_watch
|
44
|
+
r = @zk.stat(:path => @zk_path, :watcher => @stat_watcher)
|
45
|
+
@stat_watch_id = r[:req_id]
|
46
|
+
|
47
|
+
unless r[:stat].exists
|
48
|
+
cancel_child_watch
|
49
|
+
# The given @zk_path doesn't exist, so try to create it. Unclear
|
50
|
+
# whether this is always the best behavior.
|
51
|
+
r = @zk.create(:path => @zk_path)
|
52
|
+
if r[:rc] != Zookeeper::ZOK and r[:rc] != Zookeeper::ZNODEEXISTS
|
53
|
+
raise
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Make sure we're watching for children
|
58
|
+
get_and_watch unless @child_watch_id
|
59
|
+
end
|
60
|
+
|
61
|
+
def cancel_child_watch
|
62
|
+
if @child_watch_id
|
63
|
+
@zk.unregister_watcher(@child_watch_id)
|
64
|
+
@child_watch_id = nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def cancel_stat_watch
|
69
|
+
if @stat_watch_id
|
70
|
+
@zk.unregister_watcher(@stat_watch_id)
|
71
|
+
@stat_with_id = nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_and_watch
|
76
|
+
r = @zk.get_children(:path => @zk_path, :watcher => @child_watcher)
|
77
|
+
@child_watch_id = r[:req_id]
|
78
|
+
unless r[:stat].exists
|
79
|
+
cancel_child_watch
|
80
|
+
return
|
81
|
+
end
|
82
|
+
|
83
|
+
# XXX: can we easily get snapshot isolation?
|
84
|
+
new_children = {}
|
85
|
+
r[:children].each do |c|
|
86
|
+
child_path = @base_path + c
|
87
|
+
|
88
|
+
get_r = @zk.get(:path => child_path)
|
89
|
+
unless get_r[:stat].exists
|
90
|
+
puts "ZK: failed to fetch child: #{child_path}"
|
91
|
+
return
|
92
|
+
end
|
93
|
+
|
94
|
+
data = get_r[:data]
|
95
|
+
# XXX: For now, conflate empty string values with nil values
|
96
|
+
data ||= ""
|
97
|
+
new_children[c] = tuple_accessors([c, data])
|
98
|
+
end
|
99
|
+
|
100
|
+
# We successfully fetched all the children of @zk_path; arrange to install
|
101
|
+
# the new data into @storage at the next Bud tick
|
102
|
+
need_tick = false
|
103
|
+
@store_mutex.synchronize {
|
104
|
+
@next_storage = new_children
|
105
|
+
if @storage != @next_storage
|
106
|
+
need_tick = true
|
107
|
+
@saw_delta = true
|
108
|
+
end
|
109
|
+
}
|
110
|
+
|
111
|
+
# If we have new data, force a new Bud tick in the near future
|
112
|
+
if need_tick and not @bud_instance.lazy
|
113
|
+
EventMachine::schedule {
|
114
|
+
@bud_instance.tick
|
115
|
+
}
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def tick
|
120
|
+
@store_mutex.synchronize {
|
121
|
+
return unless @saw_delta
|
122
|
+
@storage = @next_storage
|
123
|
+
@next_storage = {}
|
124
|
+
@saw_delta = false
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def flush
|
129
|
+
each_from([@pending]) do |t|
|
130
|
+
path = @base_path + t.key
|
131
|
+
data = t.val
|
132
|
+
ephemeral = false
|
133
|
+
sequence = false
|
134
|
+
|
135
|
+
if t.length > 2
|
136
|
+
opts = t.last.first
|
137
|
+
if opts[:ephemeral] == true
|
138
|
+
ephemeral = true
|
139
|
+
end
|
140
|
+
if opts[:sequence] == true
|
141
|
+
sequence = true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
r = @zk.create(:path => path, :data => data,
|
146
|
+
:ephemeral => ephemeral, :sequence => sequence)
|
147
|
+
if r[:rc] == Zookeeper::ZNODEEXISTS
|
148
|
+
puts "Ignoring duplicate insert: #{t.inspect}"
|
149
|
+
elsif r[:rc] != Zookeeper::ZOK
|
150
|
+
puts "Failed create of #{path}: #{r.inspect}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
@pending.clear
|
154
|
+
end
|
155
|
+
|
156
|
+
def close
|
157
|
+
cancel_child_watch
|
158
|
+
cancel_stat_watch
|
159
|
+
@zk.close
|
160
|
+
end
|
161
|
+
|
162
|
+
superator "<~" do |o|
|
163
|
+
pending_merge(o)
|
164
|
+
end
|
165
|
+
|
166
|
+
superator "<+" do |o|
|
167
|
+
raise BudError, "Illegal use of <+ with zktable '#{@tabname}' on left"
|
168
|
+
end
|
169
|
+
|
170
|
+
def <=(o)
|
171
|
+
raise BudError, "Illegal use of <= with zktable '#{@tabname}' on left"
|
172
|
+
end
|
173
|
+
|
174
|
+
def <<(o)
|
175
|
+
raise BudError, "Illegal use of << with zktable '#{@tabname}' on left"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
data/lib/bud/stratify.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bud'
|
3
|
+
|
4
|
+
class Stratification # :nodoc: all
|
5
|
+
include Bud
|
6
|
+
|
7
|
+
state do
|
8
|
+
# Data inserted by client (Bud rewrite code)
|
9
|
+
table :depends, [:rule, :head, :op, :body, :neg]
|
10
|
+
|
11
|
+
scratch :depends_clean, [:head, :body, :neg, :temporal]
|
12
|
+
scratch :depends_tc, [:head, :body, :via, :neg, :temporal]
|
13
|
+
scratch :cycle, [:predicate, :via, :neg, :temporal]
|
14
|
+
table :stratum_base, [:predicate, :stratum]
|
15
|
+
table :stratum, [:predicate, :stratum]
|
16
|
+
table :top_strat, [:stratum]
|
17
|
+
end
|
18
|
+
|
19
|
+
def declaration
|
20
|
+
strata[0] = lambda {
|
21
|
+
depends_clean <= depends do |d|
|
22
|
+
is_temporal = (d.op.to_s =~ /<[\+\-\~]/)
|
23
|
+
[d.head, d.body, d.neg, is_temporal]
|
24
|
+
end
|
25
|
+
|
26
|
+
# Compute the transitive closure of "depends_clean" to detect cycles in
|
27
|
+
# the deductive fragment of the program.
|
28
|
+
depends_tc <= depends_clean do |d|
|
29
|
+
[d.head, d.body, d.body, d.neg, d.temporal]
|
30
|
+
end
|
31
|
+
depends_tc <= (depends_clean * depends_tc).pairs(:body => :head) do |b, r|
|
32
|
+
[b.head, r.body, b.body, (b.neg or r.neg), (b.temporal or r.temporal)]
|
33
|
+
end
|
34
|
+
|
35
|
+
cycle <= depends_tc do |d|
|
36
|
+
if d.head == d.body
|
37
|
+
if d.neg and !d.temporal
|
38
|
+
raise Bud::CompileError, "unstratifiable program: #{d.inspect}"
|
39
|
+
else
|
40
|
+
[d.head, d.via, d.neg, d.temporal]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# we initially assign all predicates to stratum 0
|
46
|
+
stratum_base <= depends {|d| [d.body, 0]}
|
47
|
+
}
|
48
|
+
|
49
|
+
strata[1] = lambda {
|
50
|
+
# classic stratification:
|
51
|
+
# if A depends on B, A is >= B.
|
52
|
+
# if A depends nonmonotonically on B, A > B.
|
53
|
+
# if A are B are co-dependent, give up.
|
54
|
+
# (don't need to do this, b/c we've ruled out deductive cycles)
|
55
|
+
# stratum choice will represent local evaluation order,
|
56
|
+
# so we need only consider 'synchronous' dependencies (<=)
|
57
|
+
|
58
|
+
# pass 1: assume no deductive cycles
|
59
|
+
# do "vanilla stratification" on deductive rules
|
60
|
+
stratum_base <= (depends * stratum_base).pairs(:body => :predicate) do |d, s|
|
61
|
+
if d.op.to_s == '<='
|
62
|
+
if d.neg
|
63
|
+
# BUMP
|
64
|
+
[d.head, s.stratum + 1]
|
65
|
+
else
|
66
|
+
# HOIST
|
67
|
+
[d.head, s.stratum]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
}
|
72
|
+
|
73
|
+
strata[2] = lambda {
|
74
|
+
stratum <= stratum_base.group([stratum_base.predicate], max(stratum_base.stratum))
|
75
|
+
}
|
76
|
+
|
77
|
+
strata[3] = lambda {
|
78
|
+
# there is no good reason that top_strat can't be computed in strata[3] over stratum_base.
|
79
|
+
# however, when it is deduced that way, it is empty after a tick
|
80
|
+
top_strat <= stratum.group([], max(stratum.stratum))
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
data/lib/bud/viz.rb
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'syntax/convertors/html'
|
3
|
+
require 'gchart'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'bud/state'
|
6
|
+
|
7
|
+
class VizOnline #:nodoc: all
|
8
|
+
def initialize(bud_instance)
|
9
|
+
@bud_instance = bud_instance
|
10
|
+
return if bud_instance.class == Stratification or @bud_instance.class == DepAnalysis
|
11
|
+
@meta_tables = {'t_rules' => 1, 't_depends' => 1, 't_table_info' => 1, 't_cycle' => 1, 't_stratum' => 1, 't_depends_tc' => 1, 't_table_schema' => 1}
|
12
|
+
@bud_instance.options[:tc_dir] = "TC_#{@bud_instance.class}_#{bud_instance.options[:tag]}_#{bud_instance.object_id}_#{bud_instance.port}"
|
13
|
+
@table_info = new_tab(:t_table_info, [:tab_name, :tab_type], @bud_instance)
|
14
|
+
@table_schema = new_tab(:t_table_schema, [:tab_name, :col_name, :ord], @bud_instance)
|
15
|
+
|
16
|
+
@logtab = {}
|
17
|
+
tmp_set = []
|
18
|
+
@bud_instance.tables.each do |name, tbl|
|
19
|
+
next if name.to_s =~ /_vizlog\z/
|
20
|
+
|
21
|
+
# Temp collections don't have a schema until a fact has been inserted into
|
22
|
+
# them; for now, we just include an empty schema for them in the viz
|
23
|
+
if tbl.schema.nil?
|
24
|
+
schema = []
|
25
|
+
else
|
26
|
+
schema = tbl.schema.clone
|
27
|
+
end
|
28
|
+
tmp_set << [name, schema, tbl.class.to_s]
|
29
|
+
end
|
30
|
+
|
31
|
+
tmp_set.each do |t|
|
32
|
+
news = [:c_bud_time]
|
33
|
+
@table_schema << [t[0], :c_bud_time, 0]
|
34
|
+
t[1].each_with_index do |s, i|
|
35
|
+
news << s
|
36
|
+
@table_schema << [t[0], s, i+1]
|
37
|
+
end
|
38
|
+
lt = "#{t[0]}_vizlog".to_sym
|
39
|
+
@logtab[t[0]] = new_tab(lt, news, @bud_instance)
|
40
|
+
@table_info << [t[0], t[2]]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def new_tab(name, schema, instance)
|
45
|
+
ret = Bud::BudTcTable.new(name, instance, schema)
|
46
|
+
instance.tables[name] = ret
|
47
|
+
return ret
|
48
|
+
end
|
49
|
+
|
50
|
+
def do_cards
|
51
|
+
return if @bud_instance.class == Stratification or @bud_instance.class == DepAnalysis
|
52
|
+
@bud_instance.tables.each do |t|
|
53
|
+
tab = t[0]
|
54
|
+
next if tab.to_s =~ /_vizlog\z/
|
55
|
+
next if @meta_tables[tab.to_s] and @bud_instance.budtime > 0
|
56
|
+
next unless @logtab[tab]
|
57
|
+
t[1].each do |row|
|
58
|
+
newrow = [@bud_instance.budtime]
|
59
|
+
row.each{ |r| newrow << r }
|
60
|
+
@logtab[tab] << newrow
|
61
|
+
end
|
62
|
+
@logtab[tab].tick
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|