locomotive 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/locomotive.rb +6 -0
- data/lib/locomotive/core_extensions.rb +6 -0
- data/lib/locomotive/core_extensions/array.rb +22 -0
- data/lib/locomotive/core_extensions/class.rb +47 -0
- data/lib/locomotive/core_extensions/hash.rb +7 -0
- data/lib/locomotive/core_extensions/inflector.rb +29 -0
- data/lib/locomotive/core_extensions/module.rb +77 -0
- data/lib/locomotive/core_extensions/symbol.rb +17 -0
- data/lib/locomotive/misc.rb +1 -0
- data/lib/locomotive/misc/type_check.rb +129 -0
- data/lib/locomotive/relational_algebra.rb +8 -0
- data/lib/locomotive/relational_algebra/attributes.rb +171 -0
- data/lib/locomotive/relational_algebra/operators.rb +15 -0
- data/lib/locomotive/relational_algebra/operators/abstraction.rb +2 -0
- data/lib/locomotive/relational_algebra/operators/abstraction/lambda.rb +83 -0
- data/lib/locomotive/relational_algebra/operators/abstraction/variable.rb +65 -0
- data/lib/locomotive/relational_algebra/operators/aggregation.rb +2 -0
- data/lib/locomotive/relational_algebra/operators/aggregation/aggr_builtin.rb +26 -0
- data/lib/locomotive/relational_algebra/operators/aggregation/aggregation.rb +76 -0
- data/lib/locomotive/relational_algebra/operators/basic_operators.rb +144 -0
- data/lib/locomotive/relational_algebra/operators/boolean.rb +4 -0
- data/lib/locomotive/relational_algebra/operators/boolean/and.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/boolean/basic_boolean.rb +66 -0
- data/lib/locomotive/relational_algebra/operators/boolean/not.rb +56 -0
- data/lib/locomotive/relational_algebra/operators/boolean/or.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/builtins.rb +3 -0
- data/lib/locomotive/relational_algebra/operators/builtins/arith_builtin.rb +37 -0
- data/lib/locomotive/relational_algebra/operators/builtins/basic_builtin.rb +20 -0
- data/lib/locomotive/relational_algebra/operators/builtins/function.rb +69 -0
- data/lib/locomotive/relational_algebra/operators/comparisons.rb +6 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/basic_comparison.rb +65 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/equal.rb +13 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/greater.rb +13 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/greater_equal.rb +14 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/less.rb +21 -0
- data/lib/locomotive/relational_algebra/operators/comparisons/less_equal.rb +13 -0
- data/lib/locomotive/relational_algebra/operators/error.rb +1 -0
- data/lib/locomotive/relational_algebra/operators/error/error.rb +52 -0
- data/lib/locomotive/relational_algebra/operators/filter.rb +1 -0
- data/lib/locomotive/relational_algebra/operators/filter/select.rb +50 -0
- data/lib/locomotive/relational_algebra/operators/join.rb +5 -0
- data/lib/locomotive/relational_algebra/operators/join/basic_join.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/join/cross.rb +28 -0
- data/lib/locomotive/relational_algebra/operators/join/equi_join.rb +61 -0
- data/lib/locomotive/relational_algebra/operators/join/predicates.rb +95 -0
- data/lib/locomotive/relational_algebra/operators/join/theta_join.rb +53 -0
- data/lib/locomotive/relational_algebra/operators/projections.rb +2 -0
- data/lib/locomotive/relational_algebra/operators/projections/attach.rb +67 -0
- data/lib/locomotive/relational_algebra/operators/projections/projection.rb +106 -0
- data/lib/locomotive/relational_algebra/operators/ranking.rb +6 -0
- data/lib/locomotive/relational_algebra/operators/ranking/basic_ranking.rb +67 -0
- data/lib/locomotive/relational_algebra/operators/ranking/rank.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/ranking/rank_lists.rb +45 -0
- data/lib/locomotive/relational_algebra/operators/ranking/row_id.rb +24 -0
- data/lib/locomotive/relational_algebra/operators/ranking/row_number.rb +55 -0
- data/lib/locomotive/relational_algebra/operators/ranking/row_rank.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/serialization.rb +2 -0
- data/lib/locomotive/relational_algebra/operators/serialization/basic_serialize.rb +9 -0
- data/lib/locomotive/relational_algebra/operators/serialization/serialize_relation.rb +105 -0
- data/lib/locomotive/relational_algebra/operators/set.rb +4 -0
- data/lib/locomotive/relational_algebra/operators/set/basic_set.rb +24 -0
- data/lib/locomotive/relational_algebra/operators/set/difference.rb +11 -0
- data/lib/locomotive/relational_algebra/operators/set/distinct.rb +35 -0
- data/lib/locomotive/relational_algebra/operators/set/union.rb +11 -0
- data/lib/locomotive/relational_algebra/operators/tables.rb +3 -0
- data/lib/locomotive/relational_algebra/operators/tables/literal_table.rb +95 -0
- data/lib/locomotive/relational_algebra/operators/tables/nil.rb +13 -0
- data/lib/locomotive/relational_algebra/operators/tables/ref_table.rb +75 -0
- data/lib/locomotive/relational_algebra/operators/typeing.rb +1 -0
- data/lib/locomotive/relational_algebra/operators/typeing/cast.rb +59 -0
- data/lib/locomotive/relational_algebra/ordering.rb +30 -0
- data/lib/locomotive/relational_algebra/query_information.rb +666 -0
- data/lib/locomotive/relational_algebra/rel_alg_ast_node.rb +51 -0
- data/lib/locomotive/relational_algebra/rel_alg_exceptions.rb +15 -0
- data/lib/locomotive/relational_algebra/schema.rb +87 -0
- data/lib/locomotive/relational_algebra/types.rb +86 -0
- data/lib/locomotive/tree_helpers.rb +3 -0
- data/lib/locomotive/tree_helpers/annotations.rb +58 -0
- data/lib/locomotive/tree_helpers/ast.rb +99 -0
- data/lib/locomotive/tree_helpers/ast_traversal.rb +43 -0
- data/lib/locomotive/utils.rb +2 -0
- data/lib/locomotive/utils/relalg2xml.rb +239 -0
- data/lib/locomotive/utils/xml.rb +47 -0
- metadata +157 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
module Locomotive
|
2
|
+
|
3
|
+
module RelationalAlgebra
|
4
|
+
|
5
|
+
class RefTbl < Leaf
|
6
|
+
def_node :table, :properties, :_keys_, :key
|
7
|
+
attr_reader :name,
|
8
|
+
:attributes,
|
9
|
+
:keys
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def get_schema
|
14
|
+
mapping =
|
15
|
+
{ :integer => RInt.instance,
|
16
|
+
:float => RDec.instance,
|
17
|
+
:string => RStr.instance }
|
18
|
+
id = 0
|
19
|
+
Schema.new(
|
20
|
+
Hash[*@attributes.collect do |attr, ty|
|
21
|
+
[Item.new(id += 1), [ty]]
|
22
|
+
end.flatten_once])
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_name_mapping
|
26
|
+
id = 0
|
27
|
+
@attributes.collect do |attr, ty|
|
28
|
+
[ Item.new(id += 1), attr.clone ]
|
29
|
+
end.to_hash
|
30
|
+
end
|
31
|
+
|
32
|
+
public
|
33
|
+
|
34
|
+
attr_accessor :name
|
35
|
+
def_sig :name=, String
|
36
|
+
attr_reader :name_mapping
|
37
|
+
|
38
|
+
def initialize(name, attributes, keys)
|
39
|
+
@name = name
|
40
|
+
@attributes = attributes
|
41
|
+
@keys = keys
|
42
|
+
@name_mapping = get_name_mapping
|
43
|
+
self.schema = get_schema
|
44
|
+
end
|
45
|
+
|
46
|
+
def xml_kind
|
47
|
+
:ref_tbl
|
48
|
+
end
|
49
|
+
|
50
|
+
def xml_content
|
51
|
+
[properties do
|
52
|
+
_keys_ do
|
53
|
+
pos = -1
|
54
|
+
keys.map do |k|
|
55
|
+
key :name => k.to_xml, :position => pos += 1
|
56
|
+
end.join
|
57
|
+
end
|
58
|
+
end,
|
59
|
+
content do
|
60
|
+
table :name => name do
|
61
|
+
name_mapping.collect do |new,old|
|
62
|
+
column :name => new.to_xml, :tname => old.to_xml, :type => schema[new].first.to_xml
|
63
|
+
end.join
|
64
|
+
end
|
65
|
+
end].join
|
66
|
+
end
|
67
|
+
|
68
|
+
def clone
|
69
|
+
RefTbl.new(name.clone, attributes.clone, keys.clone)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'locomotive/relational_algebra/operators/typeing/cast'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Locomotive
|
2
|
+
|
3
|
+
module RelationalAlgebra
|
4
|
+
|
5
|
+
class Cast < Unary
|
6
|
+
def_node :_type_
|
7
|
+
|
8
|
+
attr_accessor :res,
|
9
|
+
:type,
|
10
|
+
:item
|
11
|
+
def_sig :res=, ConstAttribute
|
12
|
+
def_sig :type=, RType
|
13
|
+
def_sig :item=, ConstAttribute
|
14
|
+
|
15
|
+
def initialize(op, res, item, type)
|
16
|
+
self.res,
|
17
|
+
self.type,
|
18
|
+
self.item = res, type, item
|
19
|
+
super(op)
|
20
|
+
end
|
21
|
+
|
22
|
+
def child=(op)
|
23
|
+
unless op.schema.attributes?([item])
|
24
|
+
raise CorruptedSchema,
|
25
|
+
"Schema #{op.schema.attributes} does not " \
|
26
|
+
"contain all attributes of #{item}."
|
27
|
+
end
|
28
|
+
self.schema = op.schema + Schema.new({ self.res => [self.type] })
|
29
|
+
super(op)
|
30
|
+
end
|
31
|
+
|
32
|
+
def xml_content
|
33
|
+
content do
|
34
|
+
[column(:name => res.to_xml, :new => true),
|
35
|
+
column(:name => item.to_xml, :new => false),
|
36
|
+
_type_(:name => type.to_xml)].join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def clone
|
41
|
+
Cast.new(
|
42
|
+
child.clone,
|
43
|
+
res.clone,
|
44
|
+
item.clone,
|
45
|
+
type.clone)
|
46
|
+
end
|
47
|
+
|
48
|
+
def set(var,plan)
|
49
|
+
Cast.new(
|
50
|
+
child.set(var,plan),
|
51
|
+
res.clone,
|
52
|
+
item.clone,
|
53
|
+
type.clone)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Locomotive
|
2
|
+
|
3
|
+
module RelationalAlgebra
|
4
|
+
|
5
|
+
# Sortdirection specifies which
|
6
|
+
# order a column should follow
|
7
|
+
class SortDirection
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
class << self
|
11
|
+
alias :dir :instance
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_xml
|
15
|
+
self.class.to_s.split('::').last.downcase.to_sym
|
16
|
+
end
|
17
|
+
|
18
|
+
def clone
|
19
|
+
# singleton
|
20
|
+
self
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Sortdirection ascending
|
25
|
+
class Ascending < SortDirection; end
|
26
|
+
# Sortdirection descending
|
27
|
+
class Descending < SortDirection; end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,666 @@
|
|
1
|
+
module Locomotive
|
2
|
+
|
3
|
+
module RelationalAlgebra
|
4
|
+
|
5
|
+
# Forward declaration
|
6
|
+
class QueryInformationNode; end
|
7
|
+
|
8
|
+
class SurrogateList
|
9
|
+
public
|
10
|
+
delegate :[],
|
11
|
+
:to_a,
|
12
|
+
:each,
|
13
|
+
:keys,
|
14
|
+
:empty?,
|
15
|
+
:first,
|
16
|
+
:size,
|
17
|
+
:to => :surrogates
|
18
|
+
|
19
|
+
attr_accessor :surrogates
|
20
|
+
def_sig :surrogates=, { ConstAttribute => QueryInformationNode }
|
21
|
+
|
22
|
+
def initialize(hash)
|
23
|
+
self.surrogates = hash
|
24
|
+
end
|
25
|
+
|
26
|
+
def map(&block)
|
27
|
+
SurrogateList.new(
|
28
|
+
surrogates.map(&block).to_hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def +(sur)
|
32
|
+
SurrogateList.new(surrogates.merge(sur.surrogates))
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_if(&lambda)
|
36
|
+
SurrogateList.new(self.clone.surrogates.delete_if(&lambda))
|
37
|
+
end
|
38
|
+
|
39
|
+
def itapp(q_0, *itbls)
|
40
|
+
if itbls.any? { |i| self.keys != i.keys }
|
41
|
+
raise ITblsNotEqual,
|
42
|
+
"some itbls keys are not equal"
|
43
|
+
end
|
44
|
+
|
45
|
+
if self.empty? and
|
46
|
+
itbls.all? { |i| i.empty? }
|
47
|
+
return SurrogateList.new( {} )
|
48
|
+
end
|
49
|
+
|
50
|
+
c = self.first.first
|
51
|
+
|
52
|
+
q1_in = self[c]
|
53
|
+
qs_in = itbls.map { |itbl| itbl[c] }
|
54
|
+
|
55
|
+
q1 = q1_in.plan
|
56
|
+
qs = qs_in.map { |q| q.plan }
|
57
|
+
cols_q1, itbls_q1 = q1_in.column_structure, q1_in.surrogates
|
58
|
+
ord, ord_, item_, item__ = Iter.new(2), Iter.new(4),
|
59
|
+
Iter.new(3), Item.new(3)
|
60
|
+
|
61
|
+
# (1)
|
62
|
+
q1_ = q1.attach(AttachItem.new(ord, RAtomic.new(1, RNat.type)))
|
63
|
+
q = qs.zip(2..qs.size+1).reduce(q1_) do |p1,p2|
|
64
|
+
p1.union(
|
65
|
+
p2.first.attach(AttachItem.new(ord, RAtomic.new(p2.last, RNat.type))))
|
66
|
+
end.row_num(item_, [], [Iter.new(1), ord, Pos.new(1)])
|
67
|
+
|
68
|
+
#(2)
|
69
|
+
c_new = Iter.new(5)
|
70
|
+
q_ = q_0.project( ord => [ord_],
|
71
|
+
item_ => [item__],
|
72
|
+
c => [c_new] ).
|
73
|
+
theta_join(q, [Equivalence.new(ord_, ord),
|
74
|
+
Equivalence.new(c_new, Iter.new(1))] ).
|
75
|
+
project( { item__ => [Iter.new(1)],
|
76
|
+
Pos.new(1) => [Pos.new(1)],
|
77
|
+
item_ => itbls_q1.keys }.
|
78
|
+
merge(
|
79
|
+
(cols_q1 - itbls_q1.keys).items.collect do |col|
|
80
|
+
[col, [col]]
|
81
|
+
end.to_hash) )
|
82
|
+
|
83
|
+
# (3)
|
84
|
+
itbl_ = q1_in.surrogates.itapp(q, *qs_in.map { |q| q.surrogates })
|
85
|
+
# (4)
|
86
|
+
itbl__ = self.delete_if { |k,v| k == c}.
|
87
|
+
itapp(q_0, *itbls.map { |i| i.delete_if { |k,v| k == c} })
|
88
|
+
# (5)
|
89
|
+
SurrogateList.new( { c => QueryInformationNode.new(
|
90
|
+
q_, cols_q1, itbl_) } ) + itbl__
|
91
|
+
end
|
92
|
+
|
93
|
+
def itsel(q_0)
|
94
|
+
return SurrogateList.new({}) if self.empty?
|
95
|
+
|
96
|
+
c = self.first.first
|
97
|
+
cols, itbls, q = self[c].column_structure, self[c].surrogates, self[c].plan
|
98
|
+
c_ = (self.keys + cols.items).max.inc
|
99
|
+
|
100
|
+
# (1)
|
101
|
+
q_ = q.equi_join(q_0.project( c => [c_]), Iter.new(1), c_).
|
102
|
+
project( [Iter.new(1), Pos.new(1)] + cols.items )
|
103
|
+
|
104
|
+
itbls_ = itbls.itsel(q_)
|
105
|
+
itbls__ = self.delete_if { |k,v| k == c }.itsel(q_0)
|
106
|
+
|
107
|
+
SurrogateList.new(
|
108
|
+
{ c => QueryInformationNode.new(q_, cols, itbls_) }.
|
109
|
+
merge( itbls__.to_a.to_hash ) )
|
110
|
+
end
|
111
|
+
|
112
|
+
def join
|
113
|
+
itbls = surrogates.to_a
|
114
|
+
|
115
|
+
itbls.rest.reduce(itbls.first.last) do |qi,surr|
|
116
|
+
# the query_information node
|
117
|
+
qin_j = surr.last
|
118
|
+
|
119
|
+
cols_j = surr.last.column_structure
|
120
|
+
itbls_j = surr.last.surrogates
|
121
|
+
cols_c = qi.column_structure
|
122
|
+
itbls_c = qi.surrogates
|
123
|
+
|
124
|
+
# adapt cols
|
125
|
+
cols_j_ = cols_j.clone
|
126
|
+
cols_j_.items.each { |i| i.inc!(qi.column_structure.count) }
|
127
|
+
# adapt itbls
|
128
|
+
itbls_j_ = itbls_j.map { |k,q| [k.inc(qi.column_structure.count), q] }
|
129
|
+
|
130
|
+
# calculate new plan
|
131
|
+
q_j = qin_j.plan.project({ Iter.new(1) => [Iter.new(2)],
|
132
|
+
Pos.new(1) => [Pos.new(2)] }.
|
133
|
+
merge(
|
134
|
+
cols_j.items.zip(cols_j_.items).
|
135
|
+
map do |old,new|
|
136
|
+
[old,[new]]
|
137
|
+
end.to_hash))
|
138
|
+
|
139
|
+
q_ = q_j.theta_join(qi.plan, [Equivalence.new(Iter.new(2), Iter.new(1)),
|
140
|
+
Equivalence.new(Pos.new(2), Pos.new(1))]).
|
141
|
+
project([Iter.new(1), Pos.new(1)] + cols_c.items + cols_j_.items)
|
142
|
+
|
143
|
+
|
144
|
+
QueryInformationNode.new(q_, cols_c + cols_j_, itbls_c + itbls_j_)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def set(var, plan)
|
149
|
+
self.map do |item, itbl|
|
150
|
+
[item,QueryInformationNode.new(itbl.plan.set(var, plan),
|
151
|
+
itbl.column_structure,
|
152
|
+
itbl.surrogates.set(var,plan))]
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def clone
|
157
|
+
# only clone the attributes since we don't modify
|
158
|
+
# the plan
|
159
|
+
SurrogateList.new( surrogates.map { |k,v| [k.clone,v] }.to_hash )
|
160
|
+
end
|
161
|
+
|
162
|
+
def filter_and_adapt(items)
|
163
|
+
item_min = items.min
|
164
|
+
# we are modifying the structure itself
|
165
|
+
# so we have to clone it (sideeffect)
|
166
|
+
surr_new = self.delete_if do |it, itbl|
|
167
|
+
!items.member?(it)
|
168
|
+
end
|
169
|
+
# adapt the keys
|
170
|
+
surr_new.map { |k,p| [Item.new(k.id - (item_min.id - 1)), p] }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class ColumnStructureEntry
|
175
|
+
end
|
176
|
+
|
177
|
+
class OffsetType < ColumnStructureEntry
|
178
|
+
private
|
179
|
+
include Locomotive::XML
|
180
|
+
def_node :offset_
|
181
|
+
|
182
|
+
public
|
183
|
+
attr_reader :offset,
|
184
|
+
:type
|
185
|
+
|
186
|
+
def initialize(offset, type)
|
187
|
+
@offset = offset
|
188
|
+
@type = type
|
189
|
+
end
|
190
|
+
|
191
|
+
def items
|
192
|
+
[offset]
|
193
|
+
end
|
194
|
+
|
195
|
+
def offsets
|
196
|
+
[self]
|
197
|
+
end
|
198
|
+
|
199
|
+
def clone
|
200
|
+
OffsetType.new(offset.clone,
|
201
|
+
type.clone)
|
202
|
+
end
|
203
|
+
|
204
|
+
def to_xml
|
205
|
+
offset_ :item => offset.to_xml, :type => type.to_xml
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
class AttributeColumnStructure < ColumnStructureEntry
|
210
|
+
private
|
211
|
+
include Locomotive::XML
|
212
|
+
def_node :attribute_
|
213
|
+
|
214
|
+
public
|
215
|
+
attr_reader :attribute,
|
216
|
+
:column_structure
|
217
|
+
|
218
|
+
def initialize(attribute, cs)
|
219
|
+
@attribute = attribute
|
220
|
+
@column_structure = cs
|
221
|
+
end
|
222
|
+
|
223
|
+
def items
|
224
|
+
column_structure.items.flatten
|
225
|
+
end
|
226
|
+
|
227
|
+
def offsets
|
228
|
+
column_structure.offsets.flatten
|
229
|
+
end
|
230
|
+
|
231
|
+
def clone
|
232
|
+
AttributeColumnStructure.new(attribute.clone,
|
233
|
+
column_structure.clone)
|
234
|
+
end
|
235
|
+
|
236
|
+
def to_xml
|
237
|
+
attribute_ :name => attribute.to_xml do
|
238
|
+
column_structure.to_xml
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class ColumnStructure
|
244
|
+
private
|
245
|
+
include Locomotive::XML
|
246
|
+
def_node :column_structure
|
247
|
+
|
248
|
+
def to_cs_entry(entry)
|
249
|
+
return entry if OffsetType === entry or
|
250
|
+
AttributeColumnStructure === entry
|
251
|
+
|
252
|
+
if !(Array === entry and entry.size == 2) then
|
253
|
+
raise ArgumentError,
|
254
|
+
"entry is not a column_structure_entry"
|
255
|
+
end
|
256
|
+
|
257
|
+
case
|
258
|
+
when Item === entry.first,
|
259
|
+
RType === entry.last then
|
260
|
+
OffsetType.new(entry.first, entry.last)
|
261
|
+
when Attribute === entry.first then
|
262
|
+
if Array === entry.last then
|
263
|
+
AttributeColumnStructure.new(entry.first,
|
264
|
+
ColumnStructure.new(entry.last))
|
265
|
+
elsif ColumnStructure === entry.last then
|
266
|
+
AttributeColumnStructure.new(entry.first,
|
267
|
+
entry.last)
|
268
|
+
end
|
269
|
+
else
|
270
|
+
raise ArgumentError,
|
271
|
+
"entry is not a column_structure_entry"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def search_by_attribute(attribute)
|
276
|
+
entries.select do |entry|
|
277
|
+
entry.attribute == attribute
|
278
|
+
end.first
|
279
|
+
end
|
280
|
+
|
281
|
+
def search_by_item(offset)
|
282
|
+
entries.select do |entry|
|
283
|
+
entry.offset == offset
|
284
|
+
end.first
|
285
|
+
end
|
286
|
+
|
287
|
+
public
|
288
|
+
attr_reader :entries
|
289
|
+
|
290
|
+
delegate :first,
|
291
|
+
:map,
|
292
|
+
:collect,
|
293
|
+
:count,
|
294
|
+
:zip,
|
295
|
+
:to => :entries
|
296
|
+
|
297
|
+
def initialize(entries)
|
298
|
+
@entries = entries.map do |entry|
|
299
|
+
to_cs_entry(entry)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
def add(entries)
|
304
|
+
entries_ = entries
|
305
|
+
|
306
|
+
if ColumnStructure === entries then
|
307
|
+
entries_ = entries.entries
|
308
|
+
end
|
309
|
+
|
310
|
+
ColumnStructure.new(self.entries +
|
311
|
+
entries_.map { |e| to_cs_entry(e) })
|
312
|
+
end
|
313
|
+
alias :+ :add
|
314
|
+
|
315
|
+
def [](attribute_index)
|
316
|
+
# just look on the surface if you find the right attribute
|
317
|
+
case attribute_index
|
318
|
+
when Fixnum then
|
319
|
+
entries[attribute_index]
|
320
|
+
when Symbol then
|
321
|
+
# creating an attribute to be consistent in the signature
|
322
|
+
attribute = Attribute.new(attribute_index)
|
323
|
+
attr = search_by_attribute(attribute).
|
324
|
+
attr.nil? ? nil : attr.column_structure
|
325
|
+
when Attribute then
|
326
|
+
attr = search_by_attribute(attribute_index)
|
327
|
+
attr.nil? ? nil : attr.column_structure
|
328
|
+
when Item then
|
329
|
+
attr = search_by_attribute(attribute_index)
|
330
|
+
attr.nil? ? nil : ColumnStructure.new([search_by_item(attribute_index)])
|
331
|
+
else
|
332
|
+
raise ArgumentError,
|
333
|
+
"Argument should be a (Fixnum | Symbol | Attribute | Item)"
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def -(array)
|
338
|
+
ColumnStructure.new(
|
339
|
+
entries.clone.delete_if do |entry|
|
340
|
+
case
|
341
|
+
when AttributeColumnStructure === entry then
|
342
|
+
array.any? { |a| entry.items.member? a }
|
343
|
+
when OffsetType === entry
|
344
|
+
array.member? entry.offset
|
345
|
+
else
|
346
|
+
raise StandardError, "Not a cs-entry"
|
347
|
+
end
|
348
|
+
end)
|
349
|
+
end
|
350
|
+
|
351
|
+
def clone
|
352
|
+
ColumnStructure.new(
|
353
|
+
entries.map { |e| e.clone })
|
354
|
+
end
|
355
|
+
|
356
|
+
def items
|
357
|
+
entries.map do |entry|
|
358
|
+
entry.items
|
359
|
+
end.flatten
|
360
|
+
end
|
361
|
+
|
362
|
+
def offsets
|
363
|
+
entries.map do |entry|
|
364
|
+
entry.offsets
|
365
|
+
end.flatten
|
366
|
+
end
|
367
|
+
|
368
|
+
def adapt
|
369
|
+
item_min = self.items.min
|
370
|
+
# we are modifying the structure
|
371
|
+
# itself (sideeffect) so we have to
|
372
|
+
# do a clone of it
|
373
|
+
cs_new = self.clone
|
374
|
+
cs_new.items.each do |it|
|
375
|
+
it.dec!(item_min.id - 1)
|
376
|
+
end
|
377
|
+
cs_new
|
378
|
+
end
|
379
|
+
|
380
|
+
def to_xml
|
381
|
+
column_structure do
|
382
|
+
entries.collect do |e|
|
383
|
+
e.to_xml
|
384
|
+
end.join
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
class SideEffects
|
390
|
+
private
|
391
|
+
|
392
|
+
def to_side_effect(side)
|
393
|
+
case
|
394
|
+
when Array === side then
|
395
|
+
side
|
396
|
+
when SideEffects === side then
|
397
|
+
side.side
|
398
|
+
when Operator === side then
|
399
|
+
[side]
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
public
|
404
|
+
|
405
|
+
attr_reader :side
|
406
|
+
|
407
|
+
def initialize(side)
|
408
|
+
@side = to_side_effect side
|
409
|
+
end
|
410
|
+
|
411
|
+
def add(side_effect)
|
412
|
+
SideEffects.new(
|
413
|
+
@side + to_side_effect(side_effect))
|
414
|
+
end
|
415
|
+
alias :+ :add
|
416
|
+
|
417
|
+
|
418
|
+
def plan
|
419
|
+
@side.reduce(Nil.new) do |s1, s2|
|
420
|
+
s1.error(s2, Item.new(1))
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
class QueryInformationNode
|
426
|
+
private
|
427
|
+
|
428
|
+
def to_cs_structure(cs_structure)
|
429
|
+
case
|
430
|
+
when Array === cs_structure then
|
431
|
+
ColumnStructure.new(cs_structure)
|
432
|
+
when ColumnStructure === cs_structure then
|
433
|
+
cs_structure
|
434
|
+
else raise ArgumentError,
|
435
|
+
"#{cs_structure.class} is not a column_structure"
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def to_surrogates(surrogates)
|
440
|
+
case
|
441
|
+
when NilClass === surrogates then
|
442
|
+
SurrogateList.new({})
|
443
|
+
when Hash === surrogates then
|
444
|
+
SurrogateList.new(surrogates)
|
445
|
+
when SurrogateList === surrogates then
|
446
|
+
surrogates
|
447
|
+
else raise ArgumentError,
|
448
|
+
"surrogates doesn't seem to be a surrogate_list"
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def to_side_effects(side_effects)
|
453
|
+
case
|
454
|
+
when NilClass === side_effects then
|
455
|
+
SideEffects.new([])
|
456
|
+
when Array === side_effects then
|
457
|
+
SideEffects.new(side_effects)
|
458
|
+
when SideEffects === side_effects then
|
459
|
+
side_effects
|
460
|
+
else raise ArgumentError,
|
461
|
+
"side_effects doesn't seem to be a side_effect"
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
public
|
466
|
+
attr_accessor :plan,
|
467
|
+
:column_structure,
|
468
|
+
:surrogates,
|
469
|
+
:side_effects,
|
470
|
+
:methods
|
471
|
+
|
472
|
+
def_sig :plan=, Operator
|
473
|
+
def_sig :column_structure=, ColumnStructure
|
474
|
+
def_sig :surrogates=, SurrogateList
|
475
|
+
def_sig :side_effects=, SideEffects
|
476
|
+
def_sig :methods=, { Symbol => RelLambda }
|
477
|
+
|
478
|
+
def initialize(plan, cs_structure, surrogates=nil, side_effects=nil, methods={})
|
479
|
+
self.plan,
|
480
|
+
self.column_structure,
|
481
|
+
self.surrogates,
|
482
|
+
self.side_effects = plan, to_cs_structure(cs_structure),
|
483
|
+
to_surrogates(surrogates),
|
484
|
+
to_side_effects(side_effects)
|
485
|
+
|
486
|
+
unless self.plan.schema.attributes?(self.column_structure.items) then
|
487
|
+
raise StandardError, "Queryplan doesn't contain all attributes of" \
|
488
|
+
" #{self.column_structure.items.inspect}"
|
489
|
+
end
|
490
|
+
self.methods = methods
|
491
|
+
end
|
492
|
+
|
493
|
+
def frag
|
494
|
+
itbls_hash = {}
|
495
|
+
|
496
|
+
column_structure.
|
497
|
+
zip(1..column_structure.count).each do |c, i|
|
498
|
+
|
499
|
+
cols_c = column_structure[i-1].column_structure.adapt
|
500
|
+
itbls_c = surrogates.filter_and_adapt(c.items)
|
501
|
+
|
502
|
+
q_c = plan.project({ Iter.new(1) => [Iter.new(1)],
|
503
|
+
Pos.new(1) => [Pos.new(1)] }.
|
504
|
+
merge(
|
505
|
+
c.items.zip(cols_c.items).
|
506
|
+
map do |old,new|
|
507
|
+
[old,[new]]
|
508
|
+
end.to_hash))
|
509
|
+
|
510
|
+
itbls_hash = itbls_hash.merge(
|
511
|
+
{ Item.new(i) => QueryInformationNode.new(
|
512
|
+
q_c,
|
513
|
+
cols_c,
|
514
|
+
itbls_c) })
|
515
|
+
end
|
516
|
+
|
517
|
+
SurrogateList.new( itbls_hash )
|
518
|
+
end
|
519
|
+
|
520
|
+
def clone
|
521
|
+
QueryInformationNode.new(plan,
|
522
|
+
column_structure.clone,
|
523
|
+
surrogates.clone)
|
524
|
+
end
|
525
|
+
end
|
526
|
+
|
527
|
+
class ResultType
|
528
|
+
include Singleton
|
529
|
+
|
530
|
+
class << self
|
531
|
+
alias :type :instance
|
532
|
+
end
|
533
|
+
|
534
|
+
def to_xml
|
535
|
+
self.class.to_s.split("::").last.upcase
|
536
|
+
end
|
537
|
+
|
538
|
+
def clone
|
539
|
+
# singleton
|
540
|
+
self
|
541
|
+
end
|
542
|
+
end
|
543
|
+
|
544
|
+
class List < ResultType; end
|
545
|
+
class Atom < ResultType; end
|
546
|
+
|
547
|
+
class QueryPlan
|
548
|
+
private
|
549
|
+
|
550
|
+
include Locomotive::XML
|
551
|
+
def_node :query_plan,
|
552
|
+
:properties, :property
|
553
|
+
|
554
|
+
public
|
555
|
+
|
556
|
+
attr_reader :id,
|
557
|
+
:idref,
|
558
|
+
:colref,
|
559
|
+
:plan,
|
560
|
+
:cols,
|
561
|
+
:result_type
|
562
|
+
|
563
|
+
def initialize(plan, cols, id, result_type=nil, idref=nil, colref=nil)
|
564
|
+
@id = id
|
565
|
+
@idref = idref
|
566
|
+
@colref = colref
|
567
|
+
@result_type = result_type
|
568
|
+
@plan = plan
|
569
|
+
@cols = cols
|
570
|
+
end
|
571
|
+
|
572
|
+
def to_xml
|
573
|
+
attributes = { :id => id }
|
574
|
+
attributes.merge!({ :idref => idref }) if idref
|
575
|
+
attributes.merge!({ :colref => colref }) if colref
|
576
|
+
|
577
|
+
query_plan(attributes) do
|
578
|
+
p = []
|
579
|
+
p << properties do
|
580
|
+
property(:name => :overallResultType, :value => result_type.to_xml)
|
581
|
+
end if result_type
|
582
|
+
p << plan.serialize
|
583
|
+
p.join
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
|
589
|
+
class QueryPlanBundle
|
590
|
+
private
|
591
|
+
# def collect_surrogates(surr)
|
592
|
+
# lplans = []
|
593
|
+
# surr.each do |attr,q_in|
|
594
|
+
# lplans << [SerializeRelation.new(
|
595
|
+
# q_in.side_effects.plan, q_in.plan,
|
596
|
+
# Iter.new(1), Pos.new(1), q_in.column_structure.items),
|
597
|
+
# q_in.column_structure]
|
598
|
+
# lplans += collect_surrogates(q_in.surrogates)
|
599
|
+
# end
|
600
|
+
# lplans
|
601
|
+
# end
|
602
|
+
|
603
|
+
def collect_surrogates(items, last_id, surr)
|
604
|
+
next_id= last_id + 1
|
605
|
+
surr.surrogates.map do |attr,qin|
|
606
|
+
plan = qin.plan
|
607
|
+
cols = qin.column_structure
|
608
|
+
side = qin.side_effects.plan
|
609
|
+
surr = qin.surrogates
|
610
|
+
|
611
|
+
colref = items.index(attr) + 1
|
612
|
+
|
613
|
+
ser = SerializeRelation.new(
|
614
|
+
side, plan,
|
615
|
+
Iter.new(1), Pos.new(1), cols.items)
|
616
|
+
qp = QueryPlan.new(
|
617
|
+
ser, cols, next_id, nil, last_id, colref)
|
618
|
+
qps = collect_surrogates(cols.items, next_id, surr)
|
619
|
+
next_id += qps.flatten.size + 1
|
620
|
+
[qp] + qps
|
621
|
+
end
|
622
|
+
end
|
623
|
+
|
624
|
+
public
|
625
|
+
include Locomotive::XML
|
626
|
+
def_node :query_plan_bundle,
|
627
|
+
:csstructure
|
628
|
+
|
629
|
+
|
630
|
+
XML_Prolog = '<?xml version="1.0" encoding="UTF-8"?>'+"\n"
|
631
|
+
|
632
|
+
attr_accessor :query_plans
|
633
|
+
attr :cs_structure
|
634
|
+
|
635
|
+
def initialize(qin, type)
|
636
|
+
plan = qin.plan
|
637
|
+
cols = qin.column_structure
|
638
|
+
side = qin.side_effects.plan
|
639
|
+
surr = qin.surrogates
|
640
|
+
|
641
|
+
ser = SerializeRelation.new(
|
642
|
+
side, plan,
|
643
|
+
Iter.new(1), Pos.new(1),
|
644
|
+
cols.items)
|
645
|
+
|
646
|
+
qp = QueryPlan.new(ser, cols, 0, type)
|
647
|
+
self.query_plans = [qp, collect_surrogates(cols.items, 0, surr)].flatten
|
648
|
+
end
|
649
|
+
|
650
|
+
def to_xml
|
651
|
+
XML_Prolog +
|
652
|
+
query_plan_bundle do
|
653
|
+
query_plans.map do |qp|
|
654
|
+
qp.to_xml
|
655
|
+
end.join
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
def clone
|
660
|
+
QueryPlanBundle.new( logical_query_plans )
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
end
|
665
|
+
|
666
|
+
end
|