veritas-optimizer 0.0.4 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -2
- data/Gemfile +8 -9
- data/Guardfile +3 -2
- data/README.rdoc +55 -0
- data/Rakefile +2 -2
- data/TODO +100 -98
- data/config/flay.yml +2 -2
- data/config/flog.yml +1 -1
- data/config/roodi.yml +2 -2
- data/config/site.reek +2 -2
- data/lib/veritas/optimizer/algebra/difference.rb +1 -1
- data/lib/veritas/optimizer/algebra/intersection.rb +1 -1
- data/lib/veritas/optimizer/algebra/join.rb +117 -1
- data/lib/veritas/optimizer/algebra/product.rb +1 -1
- data/lib/veritas/optimizer/algebra/rename.rb +12 -1
- data/lib/veritas/optimizer/algebra/restriction.rb +148 -0
- data/lib/veritas/optimizer/algebra/union.rb +1 -1
- data/lib/veritas/optimizer/function/connective/binary.rb +61 -8
- data/lib/veritas/optimizer/function/connective/conjunction.rb +1 -1
- data/lib/veritas/optimizer/function/connective/disjunction.rb +1 -1
- data/lib/veritas/optimizer/function/predicate/comparable.rb +4 -4
- data/lib/veritas/optimizer/relation/operation/binary.rb +1 -1
- data/lib/veritas/optimizer/support/predicate_partition.rb +182 -0
- data/lib/veritas/optimizer/version.rb +1 -1
- data/lib/veritas/optimizer.rb +2 -0
- data/spec/integration/veritas/algebra/rename/optimize_spec.rb +30 -30
- data/spec/integration/veritas/algebra/restriction/optimize_spec.rb +3 -3
- data/spec/integration/veritas/relation/operation/limit/optimize_spec.rb +1 -1
- data/spec/integration/veritas/relation/operation/offset/optimize_spec.rb +1 -1
- data/spec/integration/veritas/relation/operation/order/optimize_spec.rb +6 -6
- data/spec/integration/veritas/relation/operation/reverse/optimize_spec.rb +3 -3
- data/spec/spec_helper.rb +2 -1
- data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimizable_spec.rb +42 -0
- data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimize_spec.rb +55 -0
- data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimizable_spec.rb +42 -0
- data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimize_spec.rb +55 -0
- data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimizable_spec.rb +41 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimize_spec.rb +35 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimizable_spec.rb +51 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimize_spec.rb +48 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimizable_spec.rb +44 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimize_spec.rb +48 -0
- data/spec/unit/veritas/optimizer/algebra/restriction/unoptimized_operand/optimize_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/algebra/summarization/empty_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/summarization/empty_summarize_per/optimize_spec.rb +7 -7
- data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimizable_spec.rb +38 -6
- data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimize_spec.rb +44 -6
- data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimizable_spec.rb +38 -6
- data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimize_spec.rb +44 -6
- data/spec/unit/veritas/optimizer/predicate_partition/left_spec.rb +149 -0
- data/spec/unit/veritas/optimizer/predicate_partition/remainder_spec.rb +149 -0
- data/spec/unit/veritas/optimizer/predicate_partition/right_spec.rb +149 -0
- data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimize_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimize_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimizable_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimize_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimize_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimize_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimizable_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimize_spec.rb +4 -4
- data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimize_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimize_spec.rb +2 -2
- data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimizable_spec.rb +2 -2
- data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimize_spec.rb +2 -2
- data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimizable_spec.rb +2 -2
- data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimizable_spec.rb +3 -3
- data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimize_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimizable_spec.rb +1 -1
- data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimize_spec.rb +1 -1
- data/tasks/metrics/heckle.rake +1 -0
- data/veritas-optimizer.gemspec +33 -19
- metadata +37 -23
@@ -137,6 +137,152 @@ module Veritas
|
|
137
137
|
|
138
138
|
end # class RestrictionOperand
|
139
139
|
|
140
|
+
# Optimize when the operand is a combine operation
|
141
|
+
class CombinationOperand < self
|
142
|
+
|
143
|
+
# Test if the restriction is commutative
|
144
|
+
#
|
145
|
+
# @return [Boolean]
|
146
|
+
#
|
147
|
+
# @api private
|
148
|
+
def optimizable?
|
149
|
+
restriction_commutative?
|
150
|
+
end
|
151
|
+
|
152
|
+
# Distribute the restriction across the operation and apply to the operands
|
153
|
+
#
|
154
|
+
# @return [Restriction]
|
155
|
+
#
|
156
|
+
# @api private
|
157
|
+
def optimize
|
158
|
+
left_restriction.send(relation_method, right_restriction).restrict { partition.remainder }
|
159
|
+
end
|
160
|
+
|
161
|
+
private
|
162
|
+
|
163
|
+
# Return a predicate partition for the restriction and operand headers
|
164
|
+
#
|
165
|
+
# @return [PredicatePartition]
|
166
|
+
#
|
167
|
+
# @api private
|
168
|
+
def partition
|
169
|
+
operand = self.operand
|
170
|
+
PredicatePartition.new(predicate, operand.left.header, operand.right.header)
|
171
|
+
end
|
172
|
+
|
173
|
+
# Test if the restriction can be distributed over the operation
|
174
|
+
#
|
175
|
+
# If the predicates for the left and right operands would match
|
176
|
+
# everything, there is no point in distributing the restriction
|
177
|
+
# across the operation, since it will not affect the result.
|
178
|
+
#
|
179
|
+
# @return [Boolean]
|
180
|
+
#
|
181
|
+
# @api private
|
182
|
+
def restriction_commutative?
|
183
|
+
!(partition_left_tautology? && partition_right_tautology?)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Test if the predicate for the left operand would match everything
|
187
|
+
#
|
188
|
+
# @return [Boolean]
|
189
|
+
#
|
190
|
+
# @api private
|
191
|
+
def partition_left_tautology?
|
192
|
+
partition.left.equal?(Veritas::Function::Proposition::Tautology.instance)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Test if the predicate for the right operand would match everything
|
196
|
+
#
|
197
|
+
# @return [Boolean]
|
198
|
+
#
|
199
|
+
# @api private
|
200
|
+
def partition_right_tautology?
|
201
|
+
partition.right.equal?(Veritas::Function::Proposition::Tautology.instance)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Abstract method to return the relation method name
|
205
|
+
#
|
206
|
+
# @raise [NotImplementedError]
|
207
|
+
# raised when the subclass does not implement the method
|
208
|
+
#
|
209
|
+
# @api private
|
210
|
+
def relation_method
|
211
|
+
raise NotImplementedError, "#{self.class}#relation_method must be implemented"
|
212
|
+
end
|
213
|
+
|
214
|
+
# Restrict the left operand with the left predicate partition
|
215
|
+
#
|
216
|
+
# @return [Restriction]
|
217
|
+
#
|
218
|
+
# @api private
|
219
|
+
def left_restriction
|
220
|
+
operand.left.restrict { partition.left }
|
221
|
+
end
|
222
|
+
|
223
|
+
# Restrict the right operand with the right predicate partition
|
224
|
+
#
|
225
|
+
# @return [Restriction]
|
226
|
+
#
|
227
|
+
# @api private
|
228
|
+
def right_restriction
|
229
|
+
operand.right.restrict { partition.right }
|
230
|
+
end
|
231
|
+
|
232
|
+
memoize :partition
|
233
|
+
end
|
234
|
+
|
235
|
+
# Optimize when the operand is a Join
|
236
|
+
class JoinOperand < CombinationOperand
|
237
|
+
|
238
|
+
# Test if the operand is a Join and the restriction is commutative
|
239
|
+
#
|
240
|
+
# @return [Boolean]
|
241
|
+
#
|
242
|
+
# @api private
|
243
|
+
def optimizable?
|
244
|
+
operand.kind_of?(Veritas::Algebra::Join) && super
|
245
|
+
end
|
246
|
+
|
247
|
+
private
|
248
|
+
|
249
|
+
# Return the relation method name for a Join operation
|
250
|
+
#
|
251
|
+
# @return [Symbol]
|
252
|
+
#
|
253
|
+
# @api private
|
254
|
+
def relation_method
|
255
|
+
:join
|
256
|
+
end
|
257
|
+
|
258
|
+
end # class JoinOperand
|
259
|
+
|
260
|
+
# Optimize when the operand is a Product
|
261
|
+
class ProductOperand < CombinationOperand
|
262
|
+
|
263
|
+
# Test if the operand is a Join and the restriction is commutative
|
264
|
+
#
|
265
|
+
# @return [Boolean]
|
266
|
+
#
|
267
|
+
# @api private
|
268
|
+
def optimizable?
|
269
|
+
operand.kind_of?(Veritas::Algebra::Product) && super
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
|
274
|
+
# Return the relation method name for a Product operation
|
275
|
+
#
|
276
|
+
# @return [Symbol]
|
277
|
+
#
|
278
|
+
# @api private
|
279
|
+
|
280
|
+
def relation_method
|
281
|
+
:product
|
282
|
+
end
|
283
|
+
|
284
|
+
end # class ProductOperand
|
285
|
+
|
140
286
|
# Optimize when the operand is a Set
|
141
287
|
class SetOperand < self
|
142
288
|
|
@@ -213,6 +359,8 @@ module Veritas
|
|
213
359
|
Tautology,
|
214
360
|
Contradiction,
|
215
361
|
RestrictionOperand,
|
362
|
+
JoinOperand,
|
363
|
+
ProductOperand,
|
216
364
|
SetOperand,
|
217
365
|
OrderOperand,
|
218
366
|
EmptyOperand,
|
@@ -11,27 +11,27 @@ module Veritas
|
|
11
11
|
|
12
12
|
private
|
13
13
|
|
14
|
-
# Test if the operands are equality predicates for the same attribute
|
14
|
+
# Test if the operands are equality/inclusion predicates for the same attribute
|
15
15
|
#
|
16
16
|
# @return [Boolean]
|
17
17
|
#
|
18
18
|
# @api private
|
19
19
|
def equality_with_same_attributes?
|
20
|
-
|
21
|
-
|
22
|
-
same_attribute?
|
20
|
+
left_equality? &&
|
21
|
+
right_equality? &&
|
22
|
+
same_attribute? &&
|
23
23
|
constant_value?
|
24
24
|
end
|
25
25
|
|
26
|
-
# Test if the operands are inequality predicates for the same attribute
|
26
|
+
# Test if the operands are inequality/exclusion predicates for the same attribute
|
27
27
|
#
|
28
28
|
# @return [Boolean]
|
29
29
|
#
|
30
30
|
# @api private
|
31
31
|
def inequality_with_same_attributes?
|
32
|
-
|
33
|
-
|
34
|
-
same_attribute?
|
32
|
+
left_inequality? &&
|
33
|
+
right_inequality? &&
|
34
|
+
same_attribute? &&
|
35
35
|
constant_value?
|
36
36
|
end
|
37
37
|
|
@@ -54,6 +54,50 @@ module Veritas
|
|
54
54
|
util.constant?(left.right) && util.constant?(right.right)
|
55
55
|
end
|
56
56
|
|
57
|
+
# Test if the left is an equality or inclusion
|
58
|
+
#
|
59
|
+
# @return [Boolean]
|
60
|
+
#
|
61
|
+
# @api private
|
62
|
+
def left_equality?
|
63
|
+
util = Veritas::Function::Predicate
|
64
|
+
left = self.left
|
65
|
+
left.kind_of?(util::Equality) || left.kind_of?(util::Inclusion)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Test if the right is an equality or inclusion
|
69
|
+
#
|
70
|
+
# @return [Boolean]
|
71
|
+
#
|
72
|
+
# @api private
|
73
|
+
def right_equality?
|
74
|
+
util = Veritas::Function::Predicate
|
75
|
+
right = self.right
|
76
|
+
right.kind_of?(util::Equality) || right.kind_of?(util::Inclusion)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Test if the left is an inequality or exclusion
|
80
|
+
#
|
81
|
+
# @return [Boolean]
|
82
|
+
#
|
83
|
+
# @api private
|
84
|
+
def left_inequality?
|
85
|
+
util = Veritas::Function::Predicate
|
86
|
+
left = self.left
|
87
|
+
left.kind_of?(util::Inequality) || left.kind_of?(util::Exclusion)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Test if the right is an inequality or exclusion
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
def right_inequality?
|
96
|
+
util = Veritas::Function::Predicate
|
97
|
+
right = self.right
|
98
|
+
right.kind_of?(util::Inequality) || right.kind_of?(util::Exclusion)
|
99
|
+
end
|
100
|
+
|
57
101
|
# Test if the left is a tautology
|
58
102
|
#
|
59
103
|
# @return [Boolean]
|
@@ -103,6 +147,15 @@ module Veritas
|
|
103
147
|
left.inverse.eql?(right)
|
104
148
|
end
|
105
149
|
|
150
|
+
# Merge the right enumerables from the operands
|
151
|
+
#
|
152
|
+
# @return [Array]
|
153
|
+
#
|
154
|
+
# @api private
|
155
|
+
def merged_right_enumerables
|
156
|
+
[ left.right, right.right ].flatten
|
157
|
+
end
|
158
|
+
|
106
159
|
# Optimize when the operands are constants
|
107
160
|
class ConstantOperands < self
|
108
161
|
include Function::Binary::ConstantOperands
|
@@ -73,7 +73,7 @@ module Veritas
|
|
73
73
|
# @api private
|
74
74
|
def optimize
|
75
75
|
left = self.left
|
76
|
-
Veritas::Function::Predicate::Exclusion.new(left.left,
|
76
|
+
Veritas::Function::Predicate::Exclusion.new(left.left, merged_right_enumerables).optimize
|
77
77
|
end
|
78
78
|
|
79
79
|
end # class OptimizableToExclusion
|
@@ -73,7 +73,7 @@ module Veritas
|
|
73
73
|
# @api private
|
74
74
|
def optimize
|
75
75
|
left = self.left
|
76
|
-
Veritas::Function::Predicate::Inclusion.new(left.left,
|
76
|
+
Veritas::Function::Predicate::Inclusion.new(left.left, merged_right_enumerables).optimize
|
77
77
|
end
|
78
78
|
|
79
79
|
end # class OptimizableToInclusion
|
@@ -45,7 +45,7 @@ module Veritas
|
|
45
45
|
if util.constant?(left) then left_invalid_constant?
|
46
46
|
elsif util.constant?(right) then right_invalid_constant?
|
47
47
|
else
|
48
|
-
|
48
|
+
!joinable?
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -69,14 +69,14 @@ module Veritas
|
|
69
69
|
!left.valid_value?(right)
|
70
70
|
end
|
71
71
|
|
72
|
-
# Test if the left and right operand are
|
72
|
+
# Test if the left and right operand are joinable
|
73
73
|
#
|
74
74
|
# @return [Boolean]
|
75
75
|
#
|
76
76
|
# @api private
|
77
|
-
def
|
77
|
+
def joinable?
|
78
78
|
left = self.left
|
79
|
-
|
79
|
+
left.respond_to?(:joinable?) && left.joinable?(right)
|
80
80
|
end
|
81
81
|
|
82
82
|
end # module NeverEquivalent
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Veritas
|
4
|
+
class Optimizer
|
5
|
+
|
6
|
+
# Partition a predicate to distribute it over binary operations
|
7
|
+
class PredicatePartition
|
8
|
+
include Immutable
|
9
|
+
|
10
|
+
TAUTOLOGY = Veritas::Function::Proposition::Tautology.instance
|
11
|
+
|
12
|
+
# Returns the predicate for the left header
|
13
|
+
#
|
14
|
+
# @return [Function]
|
15
|
+
#
|
16
|
+
# @api private
|
17
|
+
attr_reader :left
|
18
|
+
|
19
|
+
# Returns the predicate for the right header
|
20
|
+
#
|
21
|
+
# @return [Function]
|
22
|
+
#
|
23
|
+
# @api private
|
24
|
+
attr_reader :right
|
25
|
+
|
26
|
+
# Returns the remainder predicate
|
27
|
+
#
|
28
|
+
# @return [Function]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
attr_reader :remainder
|
32
|
+
|
33
|
+
# Initialize a Predication Partition
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# partition = PredicatePartition.new(predicate, left_header, right_header)
|
37
|
+
#
|
38
|
+
# @param [Function] predicate
|
39
|
+
#
|
40
|
+
# @param [Header] left_header
|
41
|
+
#
|
42
|
+
# @param [Header] right_header
|
43
|
+
#
|
44
|
+
# @return [undefined]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
def initialize(predicate, left_header, right_header)
|
48
|
+
@left = TAUTOLOGY
|
49
|
+
@right = TAUTOLOGY
|
50
|
+
@remainder = TAUTOLOGY
|
51
|
+
|
52
|
+
@left_header = left_header
|
53
|
+
@right_header = right_header
|
54
|
+
|
55
|
+
partition!(predicate)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Partition the predicate into a left, right and remainder predicates
|
61
|
+
#
|
62
|
+
# @param [Function] predicate
|
63
|
+
#
|
64
|
+
# @return [undefined]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
def partition!(predicate)
|
68
|
+
each_operand(predicate) do |operand|
|
69
|
+
case operand
|
70
|
+
when Veritas::Function::Binary then partition_binary!(operand)
|
71
|
+
when Veritas::Function::Unary then partition_unary!(operand)
|
72
|
+
when Veritas::Attribute::Boolean then partition_attribute!(operand)
|
73
|
+
else
|
74
|
+
partition_proposition!(operand)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Partition the binary function up into a left, right and remainder predicates
|
80
|
+
#
|
81
|
+
# @param [Function::Binary] function
|
82
|
+
#
|
83
|
+
# @return [undefined]
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
def partition_binary!(function)
|
87
|
+
operands = [ function.left, function.right ]
|
88
|
+
|
89
|
+
left_operands = @left_header & operands
|
90
|
+
right_operands = @right_header & operands
|
91
|
+
|
92
|
+
if (left_operands - right_operands).empty? || (right_operands - left_operands).empty?
|
93
|
+
@left &= function if left_operands.any?
|
94
|
+
@right &= function if right_operands.any?
|
95
|
+
else
|
96
|
+
@remainder &= function
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Partition the unary function up into the left and right predicates
|
101
|
+
#
|
102
|
+
# @param [Function::Unary] function
|
103
|
+
#
|
104
|
+
# @return [undefined]
|
105
|
+
#
|
106
|
+
# @api private
|
107
|
+
def partition_unary!(function)
|
108
|
+
operand = function.operand
|
109
|
+
@left &= function if @left_header.include?(operand)
|
110
|
+
@right &= function if @right_header.include?(operand)
|
111
|
+
end
|
112
|
+
|
113
|
+
# Partition the attribute up into the left and right predicates
|
114
|
+
#
|
115
|
+
# @param [Attribute] attribute
|
116
|
+
#
|
117
|
+
# @return [undefined]
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
def partition_attribute!(attribute)
|
121
|
+
@left &= attribute if @left_header.include?(attribute)
|
122
|
+
@right &= attribute if @right_header.include?(attribute)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Partition the proposition up into the left, right and remainder predicates
|
126
|
+
#
|
127
|
+
# @param [Function::Proposition] proposition
|
128
|
+
#
|
129
|
+
# @return [undefined]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def partition_proposition!(proposition)
|
133
|
+
@remainder &= proposition
|
134
|
+
@left &= proposition
|
135
|
+
@right &= proposition
|
136
|
+
end
|
137
|
+
|
138
|
+
# Yield each operand in the predicate recursively
|
139
|
+
#
|
140
|
+
# @param [Function] predicate
|
141
|
+
#
|
142
|
+
# @yield [operand]
|
143
|
+
#
|
144
|
+
# @yieldparam [Function] operand
|
145
|
+
# each operand in the predicate
|
146
|
+
#
|
147
|
+
# @yieldreturn [undefined]
|
148
|
+
#
|
149
|
+
# @return [undefined]
|
150
|
+
#
|
151
|
+
# @api private
|
152
|
+
def each_operand(predicate, &block)
|
153
|
+
case predicate
|
154
|
+
when Veritas::Function::Connective::Disjunction then each_operand(predicate.inverse.optimize, &block)
|
155
|
+
when Veritas::Function::Connective::Conjunction then each_conjunction(predicate, &block)
|
156
|
+
else
|
157
|
+
block.call(predicate)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Yield each operand of the conjunction
|
162
|
+
#
|
163
|
+
# @param [Function::Connective::Conjunction] conjunction
|
164
|
+
#
|
165
|
+
# @yield [operand]
|
166
|
+
#
|
167
|
+
# @yieldparam [Function] operand
|
168
|
+
# each operand in the conjunction
|
169
|
+
#
|
170
|
+
# @yieldreturn [undefined]
|
171
|
+
#
|
172
|
+
# @return [undefined]
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
def each_conjunction(conjunction, &block)
|
176
|
+
each_operand(conjunction.left, &block)
|
177
|
+
each_operand(conjunction.right, &block)
|
178
|
+
end
|
179
|
+
|
180
|
+
end # class PredicatePartition
|
181
|
+
end # class Optimizer
|
182
|
+
end # module Veritas
|
data/lib/veritas/optimizer.rb
CHANGED
@@ -183,7 +183,7 @@ describe Algebra::Rename, '#optimize' do
|
|
183
183
|
end
|
184
184
|
|
185
185
|
context 'containing a restriction' do
|
186
|
-
let(:operand) { relation.restrict { |r| r
|
186
|
+
let(:operand) { relation.restrict { |r| r.id.eq(1) } }
|
187
187
|
|
188
188
|
it { should be_kind_of(Algebra::Restriction) }
|
189
189
|
|
@@ -204,11 +204,11 @@ describe Algebra::Rename, '#optimize' do
|
|
204
204
|
end
|
205
205
|
|
206
206
|
context 'containing a restriction, containing a object that cancels out' do
|
207
|
-
let(:operand) { relation.rename(:id => :other_id).restrict { |r| r
|
208
|
-
let(:aliases) { { :other_id => :id }
|
207
|
+
let(:operand) { relation.rename(:id => :other_id).restrict { |r| r.other_id.eq(1) } }
|
208
|
+
let(:aliases) { { :other_id => :id } }
|
209
209
|
|
210
210
|
it 'pushes the object before the restriction, and then cancel it out' do
|
211
|
-
should eql(relation.restrict { |r| r
|
211
|
+
should eql(relation.restrict { |r| r.id.eq(1) })
|
212
212
|
end
|
213
213
|
|
214
214
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -267,11 +267,11 @@ describe Algebra::Rename, '#optimize' do
|
|
267
267
|
end
|
268
268
|
|
269
269
|
context 'containing a reverse operation' do
|
270
|
-
let(:limit) { relation.
|
271
|
-
let(:operand) { limit.reverse
|
270
|
+
let(:limit) { relation.sort_by { |r| [ r.id, r.name ] }.take(2) }
|
271
|
+
let(:operand) { limit.reverse }
|
272
272
|
|
273
273
|
it 'pushes the object under the order, limit and reverse' do
|
274
|
-
should eql(relation.rename(aliases).
|
274
|
+
should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.take(2).reverse)
|
275
275
|
end
|
276
276
|
|
277
277
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -287,12 +287,12 @@ describe Algebra::Rename, '#optimize' do
|
|
287
287
|
end
|
288
288
|
|
289
289
|
context 'containing a reverse operation, containing a object that cancels out' do
|
290
|
-
let(:limit) { relation.
|
291
|
-
let(:operand) { limit.rename(:id => :other_id).reverse
|
292
|
-
let(:aliases) { { :other_id => :id }
|
290
|
+
let(:limit) { relation.sort_by { |r| [ r.id, r.name ] }.take(2) }
|
291
|
+
let(:operand) { limit.rename(:id => :other_id).reverse }
|
292
|
+
let(:aliases) { { :other_id => :id } }
|
293
293
|
|
294
294
|
it 'pushes the object under the order, limit and reverse, and then cancel it out' do
|
295
|
-
should eql(relation.
|
295
|
+
should eql(relation.sort_by { |r| [ r.id, r.name ] }.take(2).reverse)
|
296
296
|
end
|
297
297
|
|
298
298
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -308,10 +308,10 @@ describe Algebra::Rename, '#optimize' do
|
|
308
308
|
end
|
309
309
|
|
310
310
|
context 'containing an order operation' do
|
311
|
-
let(:operand) { relation.
|
311
|
+
let(:operand) { relation.sort_by { |r| [ r.id, r.name ] } }
|
312
312
|
|
313
313
|
it 'pushes the object under the order' do
|
314
|
-
should eql(relation.rename(aliases).
|
314
|
+
should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] })
|
315
315
|
end
|
316
316
|
|
317
317
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -327,11 +327,11 @@ describe Algebra::Rename, '#optimize' do
|
|
327
327
|
end
|
328
328
|
|
329
329
|
context 'containing an order operation, containing a object that cancels out' do
|
330
|
-
let(:operand) { relation.rename(:id => :other_id).
|
331
|
-
let(:aliases) { { :other_id => :id }
|
330
|
+
let(:operand) { relation.rename(:id => :other_id).sort_by { |r| [ r.other_id, r.name ] } }
|
331
|
+
let(:aliases) { { :other_id => :id } }
|
332
332
|
|
333
333
|
it 'pushes the object under the order, and then cancel it out' do
|
334
|
-
should eql(relation.
|
334
|
+
should eql(relation.sort_by { |r| [ r.id, r.name ] })
|
335
335
|
end
|
336
336
|
|
337
337
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -347,11 +347,11 @@ describe Algebra::Rename, '#optimize' do
|
|
347
347
|
end
|
348
348
|
|
349
349
|
context 'containing a limit operation' do
|
350
|
-
let(:order) { relation.
|
351
|
-
let(:operand) { order.take(2)
|
350
|
+
let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
|
351
|
+
let(:operand) { order.take(2) }
|
352
352
|
|
353
353
|
it 'pushes the object under the limit and order' do
|
354
|
-
should eql(relation.rename(aliases).
|
354
|
+
should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.take(2))
|
355
355
|
end
|
356
356
|
|
357
357
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -367,12 +367,12 @@ describe Algebra::Rename, '#optimize' do
|
|
367
367
|
end
|
368
368
|
|
369
369
|
context 'containing a limit operation, containing a object that cancels out' do
|
370
|
-
let(:order) { relation.
|
371
|
-
let(:operand) { order.rename(:id => :other_id).take(2)
|
372
|
-
let(:aliases) { { :other_id => :id }
|
370
|
+
let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
|
371
|
+
let(:operand) { order.rename(:id => :other_id).take(2) }
|
372
|
+
let(:aliases) { { :other_id => :id } }
|
373
373
|
|
374
374
|
it 'pushes the object under the limit and order, and then cancel it out' do
|
375
|
-
should eql(relation.
|
375
|
+
should eql(relation.sort_by { |r| [ r.id, r.name ] }.take(2))
|
376
376
|
end
|
377
377
|
|
378
378
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -388,11 +388,11 @@ describe Algebra::Rename, '#optimize' do
|
|
388
388
|
end
|
389
389
|
|
390
390
|
context 'containing an offset operation' do
|
391
|
-
let(:order) { relation.
|
392
|
-
let(:operand) { order.drop(1)
|
391
|
+
let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
|
392
|
+
let(:operand) { order.drop(1) }
|
393
393
|
|
394
394
|
it 'pushes the object under the offset and order' do
|
395
|
-
should eql(relation.rename(aliases).
|
395
|
+
should eql(relation.rename(aliases).sort_by { |r| [ r.other_id, r.name ] }.drop(1))
|
396
396
|
end
|
397
397
|
|
398
398
|
it 'returns an equivalent relation to the unoptimized operation' do
|
@@ -408,12 +408,12 @@ describe Algebra::Rename, '#optimize' do
|
|
408
408
|
end
|
409
409
|
|
410
410
|
context 'containing an offset operation, containing a object that cancels out' do
|
411
|
-
let(:order) { relation.
|
412
|
-
let(:operand) { order.rename(:id => :other_id).drop(1)
|
413
|
-
let(:aliases) { { :other_id => :id }
|
411
|
+
let(:order) { relation.sort_by { |r| [ r.id, r.name ] } }
|
412
|
+
let(:operand) { order.rename(:id => :other_id).drop(1) }
|
413
|
+
let(:aliases) { { :other_id => :id } }
|
414
414
|
|
415
415
|
it 'pushes the object under the offset and order, and then cancel it out' do
|
416
|
-
should eql(relation.
|
416
|
+
should eql(relation.sort_by { |r| [ r.id, r.name ] }.drop(1))
|
417
417
|
end
|
418
418
|
|
419
419
|
it 'returns an equivalent relation to the unoptimized operation' do
|