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.
Files changed (103) hide show
  1. data/.travis.yml +4 -2
  2. data/Gemfile +8 -9
  3. data/Guardfile +3 -2
  4. data/README.rdoc +55 -0
  5. data/Rakefile +2 -2
  6. data/TODO +100 -98
  7. data/config/flay.yml +2 -2
  8. data/config/flog.yml +1 -1
  9. data/config/roodi.yml +2 -2
  10. data/config/site.reek +2 -2
  11. data/lib/veritas/optimizer/algebra/difference.rb +1 -1
  12. data/lib/veritas/optimizer/algebra/intersection.rb +1 -1
  13. data/lib/veritas/optimizer/algebra/join.rb +117 -1
  14. data/lib/veritas/optimizer/algebra/product.rb +1 -1
  15. data/lib/veritas/optimizer/algebra/rename.rb +12 -1
  16. data/lib/veritas/optimizer/algebra/restriction.rb +148 -0
  17. data/lib/veritas/optimizer/algebra/union.rb +1 -1
  18. data/lib/veritas/optimizer/function/connective/binary.rb +61 -8
  19. data/lib/veritas/optimizer/function/connective/conjunction.rb +1 -1
  20. data/lib/veritas/optimizer/function/connective/disjunction.rb +1 -1
  21. data/lib/veritas/optimizer/function/predicate/comparable.rb +4 -4
  22. data/lib/veritas/optimizer/relation/operation/binary.rb +1 -1
  23. data/lib/veritas/optimizer/support/predicate_partition.rb +182 -0
  24. data/lib/veritas/optimizer/version.rb +1 -1
  25. data/lib/veritas/optimizer.rb +2 -0
  26. data/spec/integration/veritas/algebra/rename/optimize_spec.rb +30 -30
  27. data/spec/integration/veritas/algebra/restriction/optimize_spec.rb +3 -3
  28. data/spec/integration/veritas/relation/operation/limit/optimize_spec.rb +1 -1
  29. data/spec/integration/veritas/relation/operation/offset/optimize_spec.rb +1 -1
  30. data/spec/integration/veritas/relation/operation/order/optimize_spec.rb +6 -6
  31. data/spec/integration/veritas/relation/operation/reverse/optimize_spec.rb +3 -3
  32. data/spec/spec_helper.rb +2 -1
  33. data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimizable_spec.rb +1 -1
  34. data/spec/unit/veritas/optimizer/algebra/extension/order_operand/optimize_spec.rb +1 -1
  35. data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimizable_spec.rb +42 -0
  36. data/spec/unit/veritas/optimizer/algebra/join/left_materialized_operand/optimize_spec.rb +55 -0
  37. data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimizable_spec.rb +42 -0
  38. data/spec/unit/veritas/optimizer/algebra/join/right_materialized_operand/optimize_spec.rb +55 -0
  39. data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimizable_spec.rb +1 -1
  40. data/spec/unit/veritas/optimizer/algebra/rename/limit_operand/optimize_spec.rb +1 -1
  41. data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimizable_spec.rb +1 -1
  42. data/spec/unit/veritas/optimizer/algebra/rename/offset_operand/optimize_spec.rb +1 -1
  43. data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimizable_spec.rb +1 -1
  44. data/spec/unit/veritas/optimizer/algebra/rename/order_operand/optimize_spec.rb +1 -1
  45. data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimizable_spec.rb +1 -1
  46. data/spec/unit/veritas/optimizer/algebra/rename/reverse_operand/optimize_spec.rb +1 -1
  47. data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimizable_spec.rb +41 -0
  48. data/spec/unit/veritas/optimizer/algebra/restriction/combination_operand/optimize_spec.rb +35 -0
  49. data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimizable_spec.rb +51 -0
  50. data/spec/unit/veritas/optimizer/algebra/restriction/join_operand/optimize_spec.rb +48 -0
  51. data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimizable_spec.rb +1 -1
  52. data/spec/unit/veritas/optimizer/algebra/restriction/order_operand/optimize_spec.rb +1 -1
  53. data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimizable_spec.rb +44 -0
  54. data/spec/unit/veritas/optimizer/algebra/restriction/product_operand/optimize_spec.rb +48 -0
  55. data/spec/unit/veritas/optimizer/algebra/restriction/unoptimized_operand/optimize_spec.rb +4 -4
  56. data/spec/unit/veritas/optimizer/algebra/summarization/empty_operand/optimize_spec.rb +1 -1
  57. data/spec/unit/veritas/optimizer/algebra/summarization/empty_summarize_per/optimize_spec.rb +7 -7
  58. data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimizable_spec.rb +1 -1
  59. data/spec/unit/veritas/optimizer/algebra/summarization/order_operand/optimize_spec.rb +1 -1
  60. data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimizable_spec.rb +38 -6
  61. data/spec/unit/veritas/optimizer/function/connective/conjunction/optimizable_to_exclusion/optimize_spec.rb +44 -6
  62. data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimizable_spec.rb +38 -6
  63. data/spec/unit/veritas/optimizer/function/connective/disjunction/optimizable_to_inclusion/optimize_spec.rb +44 -6
  64. data/spec/unit/veritas/optimizer/predicate_partition/left_spec.rb +149 -0
  65. data/spec/unit/veritas/optimizer/predicate_partition/remainder_spec.rb +149 -0
  66. data/spec/unit/veritas/optimizer/predicate_partition/right_spec.rb +149 -0
  67. data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimizable_spec.rb +1 -1
  68. data/spec/unit/veritas/optimizer/relation/operation/binary/left_order_operand/optimize_spec.rb +1 -1
  69. data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimizable_spec.rb +1 -1
  70. data/spec/unit/veritas/optimizer/relation/operation/binary/{materialized_operand → materialized_operands}/optimize_spec.rb +1 -1
  71. data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimizable_spec.rb +1 -1
  72. data/spec/unit/veritas/optimizer/relation/operation/binary/right_order_operand/optimize_spec.rb +1 -1
  73. data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimizable_spec.rb +3 -3
  74. data/spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimize_spec.rb +4 -4
  75. data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimizable_spec.rb +3 -3
  76. data/spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimize_spec.rb +3 -3
  77. data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimizable_spec.rb +4 -4
  78. data/spec/unit/veritas/optimizer/relation/operation/limit/unoptimized_operand/optimize_spec.rb +4 -4
  79. data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimizable_spec.rb +3 -3
  80. data/spec/unit/veritas/optimizer/relation/operation/limit/zero_limit/optimize_spec.rb +4 -4
  81. data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimizable_spec.rb +3 -3
  82. data/spec/unit/veritas/optimizer/relation/operation/offset/offset_operand/optimize_spec.rb +3 -3
  83. data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimizable_spec.rb +4 -4
  84. data/spec/unit/veritas/optimizer/relation/operation/offset/unoptimized_operand/optimize_spec.rb +4 -4
  85. data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimizable_spec.rb +3 -3
  86. data/spec/unit/veritas/optimizer/relation/operation/offset/zero_offset/optimize_spec.rb +3 -3
  87. data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimizable_spec.rb +3 -3
  88. data/spec/unit/veritas/optimizer/relation/operation/order/one_limit_operand/optimize_spec.rb +2 -2
  89. data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimizable_spec.rb +2 -2
  90. data/spec/unit/veritas/optimizer/relation/operation/order/order_operand/optimize_spec.rb +2 -2
  91. data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimizable_spec.rb +1 -1
  92. data/spec/unit/veritas/optimizer/relation/operation/order/unoptimized_operand/optimize_spec.rb +1 -1
  93. data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimizable_spec.rb +2 -2
  94. data/spec/unit/veritas/optimizer/relation/operation/reverse/order_operand/optimize_spec.rb +1 -1
  95. data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimizable_spec.rb +3 -3
  96. data/spec/unit/veritas/optimizer/relation/operation/reverse/reverse_operand/optimize_spec.rb +1 -1
  97. data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimizable_spec.rb +3 -3
  98. data/spec/unit/veritas/optimizer/relation/operation/reverse/unoptimized_operand/optimize_spec.rb +1 -1
  99. data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimizable_spec.rb +1 -1
  100. data/spec/unit/veritas/optimizer/relation/operation/unary/order_operand/optimize_spec.rb +1 -1
  101. data/tasks/metrics/heckle.rake +1 -0
  102. data/veritas-optimizer.gemspec +33 -19
  103. metadata +37 -23
@@ -168,7 +168,7 @@ describe Algebra::Restriction, '#optimize' do
168
168
  let(:predicate) { operand[:id].gte(1) }
169
169
 
170
170
  it 'pushes the object to each relation' do
171
- should eql(left.restrict { |r| r[:id].gte(1) }.union(right.restrict { |r| r[:id].gte(1) }))
171
+ should eql(left.restrict { |r| r.id.gte(1) }.union(right.restrict { |r| r.id.gte(1) }))
172
172
  end
173
173
 
174
174
  it 'returns an equivalent relation to the unoptimized operation' do
@@ -184,8 +184,8 @@ describe Algebra::Restriction, '#optimize' do
184
184
  end
185
185
 
186
186
  context 'with an order operation' do
187
- let(:operand) { relation.order }
188
- let(:predicate) { operand[:id].gte(1) }
187
+ let(:operand) { relation.sort_by { |r| r.id } }
188
+ let(:predicate) { operand[:id].gte(1) }
189
189
 
190
190
  it 'cancels out the order' do
191
191
  should eql(relation.restrict { predicate })
@@ -8,7 +8,7 @@ describe Relation::Operation::Limit, '#optimize' do
8
8
  let(:body) { [ [ 1 ], [ 2 ], [ 3 ] ].each }
9
9
  let(:relation) { Relation.new([ [ :id, Integer ] ], body) }
10
10
  let(:directions) { [ relation[:id] ] }
11
- let(:order) { relation.order { directions } }
11
+ let(:order) { relation.sort_by { directions } }
12
12
  let(:operand) { order }
13
13
  let(:limit) { 1 }
14
14
  let(:object) { described_class.new(operand, limit) }
@@ -8,7 +8,7 @@ describe Relation::Operation::Offset, '#optimize' do
8
8
  let(:body) { [ [ 1 ], [ 2 ], [ 3 ] ].each }
9
9
  let(:relation) { Relation.new([ [ :id, Integer ] ], body) }
10
10
  let(:directions) { [ relation[:id] ] }
11
- let(:order) { relation.order { directions } }
11
+ let(:order) { relation.sort_by { directions } }
12
12
  let(:operand) { order }
13
13
  let(:offset) { 1 }
14
14
  let(:object) { described_class.new(operand, offset) }
@@ -25,7 +25,7 @@ describe Relation::Operation::Order, '#optimize' do
25
25
  context 'containing an optimizable relation' do
26
26
  let(:operand) { relation.project(relation.header) }
27
27
 
28
- it { should eql(relation.order { directions }) }
28
+ it { should eql(relation.sort_by { directions }) }
29
29
 
30
30
  it 'returns an equivalent relation to the unoptimized operation' do
31
31
  should == object
@@ -40,9 +40,9 @@ describe Relation::Operation::Order, '#optimize' do
40
40
  end
41
41
 
42
42
  context 'containing an object operation' do
43
- let(:operand) { relation.order { |r| [ r[:id].desc ] } }
43
+ let(:operand) { relation.sort_by { |r| [ r.id.desc ] } }
44
44
 
45
- it { should eql(relation.order { directions }) }
45
+ it { should eql(relation.sort_by { directions }) }
46
46
 
47
47
  it 'returns an equivalent relation to the unoptimized operation' do
48
48
  should == object
@@ -57,9 +57,9 @@ describe Relation::Operation::Order, '#optimize' do
57
57
  end
58
58
 
59
59
  context 'containing a reverse operation' do
60
- let(:operand) { relation.order.reverse }
60
+ let(:operand) { relation.sort_by { |r| r.id }.reverse }
61
61
 
62
- it { should eql(relation.order) }
62
+ it { should eql(relation.sort_by { |r| r.id }) }
63
63
 
64
64
  it 'returns an equivalent relation to the unoptimized operation' do
65
65
  should == object
@@ -74,7 +74,7 @@ describe Relation::Operation::Order, '#optimize' do
74
74
  end
75
75
 
76
76
  context 'containing a limit(1) operation' do
77
- let(:operand) { relation.order.take(1) }
77
+ let(:operand) { relation.sort_by { |r| r.id }.take(1) }
78
78
 
79
79
  it { should equal(operand) }
80
80
 
@@ -7,7 +7,7 @@ describe Relation::Operation::Reverse, '#optimize' do
7
7
 
8
8
  let(:body) { [ [ 1 ], [ 2 ], [ 3 ] ].each }
9
9
  let(:relation) { Relation.new([ [ :id, Integer ] ], body) }
10
- let(:order) { relation.order }
10
+ let(:order) { relation.sort_by { |r| r.id } }
11
11
  let(:operand) { order }
12
12
  let(:object) { described_class.new(operand) }
13
13
 
@@ -52,7 +52,7 @@ describe Relation::Operation::Reverse, '#optimize' do
52
52
  end
53
53
 
54
54
  context 'with an order operation' do
55
- it { should eql(relation.order { object.directions }) }
55
+ it { should eql(relation.sort_by { object.directions }) }
56
56
 
57
57
  it 'returns an equivalent relation to the unoptimized operation' do
58
58
  should == object
@@ -69,7 +69,7 @@ describe Relation::Operation::Reverse, '#optimize' do
69
69
  context 'with an order operation when optimized' do
70
70
  let(:operand) { order.rename({}) }
71
71
 
72
- it { should eql(relation.order { object.directions }) }
72
+ it { should eql(relation.sort_by { object.directions }) }
73
73
 
74
74
  it 'returns an equivalent relation to the unoptimized operation' do
75
75
  should == object
data/spec/spec_helper.rb CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  require 'rubygems'
4
4
  require 'backports'
5
- require 'veritas/optimizer'
5
+ require 'backports/basic_object'
6
+ require 'veritas-optimizer'
6
7
  require 'spec'
7
8
  require 'spec/autorun'
8
9
 
@@ -10,7 +10,7 @@ describe Optimizer::Algebra::Extension::OrderOperand, '#optimizable?' do
10
10
  let(:object) { described_class.new(relation) }
11
11
 
12
12
  context 'when operand is an order' do
13
- let(:operand) { base.order }
13
+ let(:operand) { base.sort_by { |r| r.id } }
14
14
 
15
15
  it { should be(true) }
16
16
  end
@@ -6,7 +6,7 @@ describe Optimizer::Algebra::Extension::OrderOperand, '#optimize' do
6
6
  subject { object.optimize }
7
7
 
8
8
  let(:base) { Relation.new([ [ :id, Integer ] ], [].each) }
9
- let(:operand) { base.order }
9
+ let(:operand) { base.sort_by { |r| r.id } }
10
10
  let(:relation) { operand.extend {} }
11
11
  let(:object) { described_class.new(relation) }
12
12
 
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Join::LeftMaterializedOperand, '#optimizable?' do
6
+ subject { object.optimizable? }
7
+
8
+ let(:relation) { left.join(right) }
9
+ let(:object) { described_class.new(relation) }
10
+
11
+ before do
12
+ object.operation.should be_kind_of(Algebra::Join)
13
+ end
14
+
15
+ context 'when the left is materialized and right is not a restriction' do
16
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ]) }
17
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each) }
18
+
19
+ it { should be(true) }
20
+ end
21
+
22
+ context 'when the left is materialized and right is a restriction not matching the left' do
23
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ]) }
24
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each).restrict { |r| r.age.eq(35) } }
25
+
26
+ it { should be(true) }
27
+ end
28
+
29
+ context 'when the left is materialized and right is a restriction matching the left' do
30
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ]) }
31
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each).restrict { |r| r.id.eq(1) } }
32
+
33
+ it { should be(false) }
34
+ end
35
+
36
+ context 'when the left is not materialized' do
37
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
38
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each) }
39
+
40
+ it { should be(false) }
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Join::LeftMaterializedOperand, '#optimize' do
6
+ subject { object.optimize }
7
+
8
+ let(:relation) { left.join(right) }
9
+ let(:object) { described_class.new(relation) }
10
+
11
+ before do
12
+ object.should be_optimizable
13
+ end
14
+
15
+ context 'with no joined tuples' do
16
+ let(:left) { Relation.new([ [ :id, Integer ], ], []) }
17
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [].each) }
18
+
19
+ it { should be_kind_of(Algebra::Join) }
20
+
21
+ its(:left) { should eql(Relation::Empty.new(left.header)) }
22
+
23
+ its(:right) { should eql(right.restrict { Function::Proposition::Contradiction.instance }) }
24
+ end
25
+
26
+ context 'with one joined tuple' do
27
+ let(:left) { Relation.new([ [ :id, Integer ], ], [ [ 1, ] ]) }
28
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each) }
29
+
30
+ it { should be_kind_of(Algebra::Join) }
31
+
32
+ its(:left) { should equal(left) }
33
+
34
+ its(:right) { should eql(right.restrict { |r| r.id.eq(1) }) }
35
+
36
+ it 'is not further optimizable' do
37
+ described_class.new(subject).should_not be_optimizable
38
+ end
39
+ end
40
+
41
+ context 'with two or more joined tuples' do
42
+ let(:left) { Relation.new([ [ :id, Integer ], ], [ [ 1, ], [ 2, ] ]) }
43
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ], [ 2, 25 ] ].each) }
44
+
45
+ it { should be_kind_of(Algebra::Join) }
46
+
47
+ its(:left) { should equal(left) }
48
+
49
+ its(:right) { should eql(right.restrict { |r| r.id.include([ 1, 2 ]) }) }
50
+
51
+ it 'is not further optimizable' do
52
+ described_class.new(subject).should_not be_optimizable
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,42 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Join::RightMaterializedOperand, '#optimizable?' do
6
+ subject { object.optimizable? }
7
+
8
+ let(:relation) { left.join(right) }
9
+ let(:object) { described_class.new(relation) }
10
+
11
+ before do
12
+ object.operation.should be_kind_of(Algebra::Join)
13
+ end
14
+
15
+ context 'when the right is materialized and left is not a restriction' do
16
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
17
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ]) }
18
+
19
+ it { should be(true) }
20
+ end
21
+
22
+ context 'when the right is materialized and left is a restriction not matching the right' do
23
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ].each).restrict { |r| r.name.eq('Dan Kubb') } }
24
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ]) }
25
+
26
+ it { should be(true) }
27
+ end
28
+
29
+ context 'when the right is materialized and left is a restriction matching the right' do
30
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ].each).restrict { |r| r.id.eq(1) } }
31
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ]) }
32
+
33
+ it { should be(false) }
34
+ end
35
+
36
+ context 'when the left is not materialized' do
37
+ let(:left) { Relation.new([ [ :id, Integer ], [ :name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
38
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ].each) }
39
+
40
+ it { should be(false) }
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Join::RightMaterializedOperand, '#optimize' do
6
+ subject { object.optimize }
7
+
8
+ let(:relation) { left.join(right) }
9
+ let(:object) { described_class.new(relation) }
10
+
11
+ before do
12
+ object.should be_optimizable
13
+ end
14
+
15
+ context 'with no joined tuples' do
16
+ let(:left) { Relation.new([ [ :id, Integer ], ], [].each) }
17
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], []) }
18
+
19
+ it { should be_kind_of(Algebra::Join) }
20
+
21
+ its(:left) { should eql(left.restrict { Function::Proposition::Contradiction.instance }) }
22
+
23
+ its(:right) { should eql(Relation::Empty.new(right.header)) }
24
+ end
25
+
26
+ context 'with one joined tuple' do
27
+ let(:left) { Relation.new([ [ :id, Integer ], ], [ [ 1, ] ].each) }
28
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ] ]) }
29
+
30
+ it { should be_kind_of(Algebra::Join) }
31
+
32
+ its(:left) { should eql(left.restrict { |r| r.id.eq(1) }) }
33
+
34
+ its(:right) { should equal(right) }
35
+
36
+ it 'is not further optimizable' do
37
+ described_class.new(subject).should_not be_optimizable
38
+ end
39
+ end
40
+
41
+ context 'with two or more joined tuples' do
42
+ let(:left) { Relation.new([ [ :id, Integer ], ], [ [ 1, ], [ 2, ] ].each) }
43
+ let(:right) { Relation.new([ [ :id, Integer ], [ :age, Integer ] ], [ [ 1, 35 ], [ 2, 25 ] ]) }
44
+
45
+ it { should be_kind_of(Algebra::Join) }
46
+
47
+ its(:left) { should eql(left.restrict { |r| r.id.include([ 1, 2 ]) }) }
48
+
49
+ its(:right) { should equal(right) }
50
+
51
+ it 'is not further optimizable' do
52
+ described_class.new(subject).should_not be_optimizable
53
+ end
54
+ end
55
+ end
@@ -15,7 +15,7 @@ describe Optimizer::Algebra::Rename::LimitOperand, '#optimizable?' do
15
15
  end
16
16
 
17
17
  context 'when the operand is an limit operation' do
18
- let(:operand) { base.order.take(1) }
18
+ let(:operand) { base.sort_by { |r| r.id }.take(1) }
19
19
 
20
20
  it { should be(true) }
21
21
  end
@@ -7,7 +7,7 @@ describe Optimizer::Algebra::Rename::LimitOperand, '#optimize' do
7
7
 
8
8
  let(:header) { Relation::Header.new([ [ :id, Integer ] ]) }
9
9
  let(:base) { Relation.new(header, [ [ 1 ] ].each) }
10
- let(:order) { base.order }
10
+ let(:order) { base.sort_by { |r| r.id } }
11
11
  let(:relation) { order.take(2).rename(:id => :other_id) }
12
12
  let(:object) { described_class.new(relation) }
13
13
 
@@ -15,7 +15,7 @@ describe Optimizer::Algebra::Rename::OffsetOperand, '#optimizable?' do
15
15
  end
16
16
 
17
17
  context 'when the operand is an offset operation' do
18
- let(:operand) { base.order.drop(1) }
18
+ let(:operand) { base.sort_by { |r| r.id }.drop(1) }
19
19
 
20
20
  it { should be(true) }
21
21
  end
@@ -7,7 +7,7 @@ describe Optimizer::Algebra::Rename::OffsetOperand, '#optimize' do
7
7
 
8
8
  let(:header) { Relation::Header.new([ [ :id, Integer ] ]) }
9
9
  let(:base) { Relation.new(header, [ [ 1 ] ].each) }
10
- let(:order) { base.order }
10
+ let(:order) { base.sort_by { |r| r.id } }
11
11
  let(:relation) { order.drop(1).rename(:id => :other_id) }
12
12
  let(:object) { described_class.new(relation) }
13
13
 
@@ -15,7 +15,7 @@ describe Optimizer::Algebra::Rename::OrderOperand, '#optimizable?' do
15
15
  end
16
16
 
17
17
  context 'when the operand is an order operation' do
18
- let(:operand) { base.order }
18
+ let(:operand) { base.sort_by { |r| r.id } }
19
19
 
20
20
  it { should be(true) }
21
21
  end
@@ -6,7 +6,7 @@ describe Optimizer::Algebra::Rename::OrderOperand, '#optimize' do
6
6
  subject { object.optimize }
7
7
 
8
8
  let(:base) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ].each) }
9
- let(:operand) { base.order }
9
+ let(:operand) { base.sort_by { |r| r.id } }
10
10
  let(:relation) { operand.rename(:id => :other_id) }
11
11
  let(:object) { described_class.new(relation) }
12
12
 
@@ -14,7 +14,7 @@ describe Optimizer::Algebra::Rename::ReverseOperand, '#optimizable?' do
14
14
  end
15
15
 
16
16
  context 'when the operand is an reverse operation' do
17
- let(:operand) { base.order.take(2).reverse }
17
+ let(:operand) { base.sort_by { |r| r.id }.take(2).reverse }
18
18
 
19
19
  it { should be(true) }
20
20
  end
@@ -6,7 +6,7 @@ describe Optimizer::Algebra::Rename::ReverseOperand, '#optimize' do
6
6
  subject { object.optimize }
7
7
 
8
8
  let(:base) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ].each) }
9
- let(:limit) { base.order.take(2) }
9
+ let(:limit) { base.sort_by { |r| r.id }.take(2) }
10
10
  let(:operand) { limit.rename({}).reverse }
11
11
  let(:relation) { operand.rename(:id => :other_id) }
12
12
  let(:object) { described_class.new(relation) }
@@ -0,0 +1,41 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::CombinationOperand, '#optimizable?' do
6
+ subject { object.optimizable? }
7
+
8
+ let(:left) { Relation.new([ [ :id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :id, Integer ], [ :employee_name, String ] ], [ [ 2, 'Dan Kubb' ] ].each) }
10
+ let(:operand) { left.join(right) }
11
+ let(:relation) { operand.restrict { predicate } }
12
+ let(:object) { described_class.new(relation) }
13
+
14
+ before do
15
+ object.operation.should be_kind_of(Algebra::Restriction)
16
+ end
17
+
18
+ context 'when the predicate distributes to the left' do
19
+ let(:predicate) { left[:user_name].eq('Dan Kubb') }
20
+
21
+ it { should be(true) }
22
+ end
23
+
24
+ context 'when the predicate distributes to the right' do
25
+ let(:predicate) { right[:employee_name].eq('Dan Kubb') }
26
+
27
+ it { should be(true) }
28
+ end
29
+
30
+ context 'when the predicate distributes to the left and right' do
31
+ let(:predicate) { left[:id].eq(1) }
32
+
33
+ it { should be(true) }
34
+ end
35
+
36
+ context 'when the predicate does not distribute to the left or right' do
37
+ let(:predicate) { left[:user_name].eq(right[:employee_name]) }
38
+
39
+ it { should be(false) }
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::CombinationOperand, '#optimize' do
6
+ subject { object.optimize }
7
+
8
+ let(:left) { Relation.new([ [ :id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :id, Integer ], [ :employee_name, String ] ], [ [ 2, 'Dan Kubb' ] ].each) }
10
+ let(:operand) { left.join(right) }
11
+ let(:relation) { operand.restrict { false } }
12
+ let(:object) { described_class.new(relation) }
13
+
14
+ before do
15
+ object.should be_optimizable
16
+ end
17
+
18
+ specify { expect { subject }.to raise_error(NotImplementedError, 'Veritas::Optimizer::Algebra::Restriction::CombinationOperand#relation_method must be implemented') }
19
+
20
+ context 'with a defined relation_method' do
21
+ let(:described_class) { Class.new(Optimizer::Algebra::Restriction::CombinationOperand) }
22
+
23
+ before do
24
+ described_class.class_eval do
25
+ def relation_method
26
+ :join
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'returns an join with a distributed restriction' do
32
+ should eql(left.restrict { false }.join(right.restrict { false }).restrict { false })
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::JoinOperand, '#optimizable?' do
6
+ subject { object.optimizable? }
7
+
8
+ let(:left) { Relation.new([ [ :id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :id, Integer ], [ :employee_name, String ] ], [ [ 2, 'Dan Kubb' ] ].each) }
10
+ let(:relation) { operand.restrict { predicate } }
11
+ let(:object) { described_class.new(relation) }
12
+
13
+ before do
14
+ object.operation.should be_kind_of(Algebra::Restriction)
15
+ end
16
+
17
+ context 'when the operand is a join operation and the predicate distributes to the left' do
18
+ let(:operand) { left.join(right) }
19
+ let(:predicate) { left[:user_name].eq('Dan Kubb') }
20
+
21
+ it { should be(true) }
22
+ end
23
+
24
+ context 'when the operand is a join operation and the predicate distributes to the right' do
25
+ let(:operand) { left.join(right) }
26
+ let(:predicate) { right[:employee_name].eq('Dan Kubb') }
27
+
28
+ it { should be(true) }
29
+ end
30
+
31
+ context 'when the operand is a join operation and the predicate distributes to the left and right' do
32
+ let(:operand) { left.join(right) }
33
+ let(:predicate) { left[:id].eq(1) }
34
+
35
+ it { should be(true) }
36
+ end
37
+
38
+ context 'when the operand is a join operation and the predicate does not distribute to the left or right' do
39
+ let(:operand) { left.join(right) }
40
+ let(:predicate) { left[:user_name].eq(right[:employee_name]) }
41
+
42
+ it { should be(false) }
43
+ end
44
+
45
+ context 'when the operand is not a join operation' do
46
+ let(:operand) { left }
47
+ let(:predicate) { true }
48
+
49
+ it { should be(false) }
50
+ end
51
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::JoinOperand, '#optimize' do
6
+ subject { object.optimize }
7
+
8
+ let(:left) { Relation.new([ [ :id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :id, Integer ], [ :employee_name, String ] ], [ [ 2, 'Dan Kubb' ] ].each) }
10
+ let(:operand) { left.join(right) }
11
+ let(:left_predicate) { left[:user_name].eq('Dan Kubb') }
12
+ let(:right_predicate) { right[:employee_name].eq('Dan Kubb') }
13
+ let(:remainder_predicate) { left[:user_name].eq(right[:employee_name]) }
14
+ let(:predicate) { left_predicate.and(right_predicate).and(remainder_predicate) }
15
+ let(:relation) { operand.restrict { predicate } }
16
+ let(:object) { described_class.new(relation) }
17
+
18
+ before do
19
+ object.should be_optimizable
20
+ end
21
+
22
+ it { should be_kind_of(Algebra::Restriction) }
23
+
24
+ its(:operand) { should be_kind_of(Algebra::Join) }
25
+
26
+ it 'wraps the left operand of the join in a restriction' do
27
+ left_operand = subject.operand.left
28
+ left_operand.operand.should equal(left)
29
+ left_operand.should be_kind_of(Algebra::Restriction)
30
+ end
31
+
32
+ it 'wraps the right operand of the join in a restriction' do
33
+ right_operand = subject.operand.right
34
+ right_operand.operand.should equal(right)
35
+ right_operand.should be_kind_of(Algebra::Restriction)
36
+ end
37
+
38
+ it 'distributes the applicable part of the predicate to the left' do
39
+ subject.operand.left.predicate.should equal(left_predicate)
40
+ end
41
+
42
+ it 'distributes the applicable part of the predicate to the right' do
43
+ subject.operand.right.predicate.should equal(right_predicate)
44
+ end
45
+
46
+ # keeps the remainder that applies to the left and right
47
+ its(:predicate) { should equal(remainder_predicate) }
48
+ end
@@ -15,7 +15,7 @@ describe Optimizer::Algebra::Restriction::OrderOperand, '#optimizable?' do
15
15
  end
16
16
 
17
17
  context 'when the operand is an order operation' do
18
- let(:operand) { base.order }
18
+ let(:operand) { base.sort_by { |r| r.id } }
19
19
 
20
20
  it { should be(true) }
21
21
  end
@@ -7,7 +7,7 @@ describe Optimizer::Algebra::Restriction::OrderOperand, '#optimize' do
7
7
 
8
8
  let(:base) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ].each) }
9
9
  let(:predicate) { base[:id].eq(1) }
10
- let(:relation) { base.order.restrict { predicate } }
10
+ let(:relation) { base.sort_by { |r| r.id }.restrict { predicate } }
11
11
  let(:object) { described_class.new(relation) }
12
12
 
13
13
  before do