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
@@ -0,0 +1,44 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::ProductOperand, '#optimizable?' do
6
+ subject { object.optimizable? }
7
+
8
+ let(:left) { Relation.new([ [ :user_id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :employee_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 product operation and the predicate distributes to the left' do
18
+ let(:operand) { left.product(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 product operation and the predicate distributes to the right' do
25
+ let(:operand) { left.product(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 product operation and the predicate does not distribute to the left or right' do
32
+ let(:operand) { left.product(right) }
33
+ let(:predicate) { left[:user_name].eq(right[:employee_name]) }
34
+
35
+ it { should be(false) }
36
+ end
37
+
38
+ context 'when the operand is not a product operation' do
39
+ let(:operand) { left }
40
+ let(:predicate) { true }
41
+
42
+ it { should be(false) }
43
+ end
44
+ end
@@ -0,0 +1,48 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::Algebra::Restriction::ProductOperand, '#optimize' do
6
+ subject { object.optimize }
7
+
8
+ let(:left) { Relation.new([ [ :user_id, Integer ], [ :user_name, String ] ], [ [ 1, 'Dan Kubb' ] ].each) }
9
+ let(:right) { Relation.new([ [ :employee_id, Integer ], [ :employee_name, String ] ], [ [ 2, 'Dan Kubb' ] ].each) }
10
+ let(:operand) { left.product(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::Product) }
25
+
26
+ it 'wraps the left operand of the product 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 product 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
@@ -5,10 +5,10 @@ require 'spec_helper'
5
5
  describe Optimizer::Algebra::Restriction::UnoptimizedOperand, '#optimize' do
6
6
  subject { object.optimize }
7
7
 
8
- let(:header) { Relation::Header.new([ [ :id, Integer ] ]) }
9
- let(:base) { Relation.new(header, [ [ 1 ] ].each) }
10
- let(:relation) { base.rename({}).restrict { |r| r[:id].eq(1) } }
11
- let(:object) { described_class.new(relation) }
8
+ let(:header) { Relation::Header.new([ [ :id, Integer ] ]) }
9
+ let(:base) { Relation.new(header, [ [ 1 ] ].each) }
10
+ let(:relation) { base.rename({}).restrict { |r| r.id.eq(1) } }
11
+ let(:object) { described_class.new(relation) }
12
12
 
13
13
  before do
14
14
  object.should be_optimizable
@@ -13,7 +13,7 @@ describe Optimizer::Algebra::Summarization::EmptyOperand, '#optimize' do
13
13
  let(:object) { described_class.new(relation) }
14
14
 
15
15
  context 'when the function has a default' do
16
- let(:relation) { operand.summarize(summarize_per) { |r| r.add(attribute, r[:id].count) } }
16
+ let(:relation) { operand.summarize(summarize_per) { |r| r.add(attribute, r.id.count) } }
17
17
 
18
18
  before do
19
19
  object.should be_optimizable
@@ -5,13 +5,13 @@ require 'spec_helper'
5
5
  describe Optimizer::Algebra::Summarization::EmptySummarizePer, '#optimize' do
6
6
  subject { object.optimize }
7
7
 
8
- let(:header) { Relation::Header.new([ [ :id, Integer ], [ :name, String ] ]) }
9
- let(:base) { Relation.new(header, [ [ 1, 'Dan Kubb' ] ].each) }
10
- let(:attribute) { Attribute::Object.new(:test) }
11
- let(:operand) { base }
12
- let(:summarize_per) { base.project([ :id ]).restrict { false } }
13
- let(:relation) { operand.summarize(summarize_per) { |r| r.add(attribute, r[:name].count) } }
14
- let(:object) { described_class.new(relation) }
8
+ let(:header) { Relation::Header.new([ [ :id, Integer ], [ :name, String ] ]) }
9
+ let(:base) { Relation.new(header, [ [ 1, 'Dan Kubb' ] ].each) }
10
+ let(:attribute) { Attribute::Object.new(:test) }
11
+ let(:operand) { base }
12
+ let(:summarize_per) { base.project([ :id ]).restrict { false } }
13
+ let(:relation) { operand.summarize(summarize_per) { |r| r.add(attribute, r.name.count) } }
14
+ let(:object) { described_class.new(relation) }
15
15
 
16
16
  before do
17
17
  object.should be_optimizable
@@ -10,7 +10,7 @@ describe Optimizer::Algebra::Summarization::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::Summarization::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.summarize([]) {} }
11
11
  let(:object) { described_class.new(relation) }
12
12
 
@@ -14,16 +14,48 @@ describe Optimizer::Function::Connective::Conjunction::OptimizableToExclusion, '
14
14
  end
15
15
 
16
16
  context 'when the operands are optimizable' do
17
- let(:left) { attribute.ne(2) }
18
- let(:right) { attribute.ne(1) }
17
+ context 'when the left and right are equality predicates' do
18
+ let(:left) { attribute.ne(2) }
19
+ let(:right) { attribute.ne(1) }
19
20
 
20
- it { should be(true) }
21
+ it { should be(true) }
22
+ end
23
+
24
+ context 'when the left is an equality predicate and the right is an inclusion predicate' do
25
+ let(:left) { attribute.ne(2) }
26
+ let(:right) { attribute.exclude([ 1, 3 ]) }
27
+
28
+ it { should be(true) }
29
+ end
30
+
31
+ context 'when the left is an inclusion predicate and the right is an equality predicate' do
32
+ let(:left) { attribute.exclude([ 2, 3 ]) }
33
+ let(:right) { attribute.ne(1) }
34
+
35
+ it { should be(true) }
36
+ end
37
+
38
+ context 'when the left and right are inclusion predicatess' do
39
+ let(:left) { attribute.exclude([ 2, 3 ]) }
40
+ let(:right) { attribute.exclude([ 1, 3 ]) }
41
+
42
+ it { should be(true) }
43
+ end
21
44
  end
22
45
 
23
46
  context 'when the operands are not optimizable' do
24
- let(:left) { attribute.ne(1) }
25
- let(:right) { attribute.eq(2) }
47
+ context 'when the left is an inequality predicate and the right is an equality predicate' do
48
+ let(:left) { attribute.ne(1) }
49
+ let(:right) { attribute.eq(2) }
50
+
51
+ it { should be(false) }
52
+ end
53
+
54
+ context 'when the left is an exclusion predicate and the right is an equality predicate' do
55
+ let(:left) { attribute.exclude([ 1, 3 ]) }
56
+ let(:right) { attribute.eq(2) }
26
57
 
27
- it { should be(false) }
58
+ it { should be(false) }
59
+ end
28
60
  end
29
61
  end
@@ -14,15 +14,53 @@ describe Optimizer::Function::Connective::Conjunction::OptimizableToExclusion, '
14
14
  end
15
15
 
16
16
  context 'when the right operands are optimizable' do
17
- let(:left) { attribute.ne(2) }
18
- let(:right) { attribute.ne(1) }
17
+ context 'when the left and right are equality predicates' do
18
+ let(:left) { attribute.ne(2) }
19
+ let(:right) { attribute.ne(1) }
19
20
 
20
- it { should be_kind_of(Function::Predicate::Exclusion) }
21
+ it { should be_kind_of(Function::Predicate::Exclusion) }
21
22
 
22
- its(:left) { should equal(attribute) }
23
+ its(:left) { should equal(attribute) }
23
24
 
24
- # enumerable order is normalized
25
- its(:right) { should == [ 1, 2 ] }
25
+ # enumerable order is normalized
26
+ its(:right) { should == [ 1, 2 ] }
27
+ end
28
+
29
+ context 'when the left is an equality predicate and the right is an inclusion predicate' do
30
+ let(:left) { attribute.ne(2) }
31
+ let(:right) { attribute.exclude([ 1 ]) }
32
+
33
+ it { should be_kind_of(Function::Predicate::Exclusion) }
34
+
35
+ its(:left) { should equal(attribute) }
36
+
37
+ # enumerable order is normalized
38
+ its(:right) { should == [ 1, 2 ] }
39
+ end
40
+
41
+ context 'when the left is an inclusion predicate and the right is an equality predicate' do
42
+ let(:left) { attribute.exclude([ 2 ]) }
43
+ let(:right) { attribute.ne(1) }
44
+
45
+ it { should be_kind_of(Function::Predicate::Exclusion) }
46
+
47
+ its(:left) { should equal(attribute) }
48
+
49
+ # enumerable order is normalized
50
+ its(:right) { should == [ 1, 2 ] }
51
+ end
52
+
53
+ context 'when the left and right are inclusion predicatess' do
54
+ let(:left) { attribute.exclude([ 2 ]) }
55
+ let(:right) { attribute.exclude([ 1 ]) }
56
+
57
+ it { should be_kind_of(Function::Predicate::Exclusion) }
58
+
59
+ its(:left) { should equal(attribute) }
60
+
61
+ # enumerable order is normalized
62
+ its(:right) { should == [ 1, 2 ] }
63
+ end
26
64
  end
27
65
 
28
66
  context 'when the right operands are not optimizable' do
@@ -14,16 +14,48 @@ describe Optimizer::Function::Connective::Disjunction::OptimizableToInclusion, '
14
14
  end
15
15
 
16
16
  context 'when the operands are optimizable' do
17
- let(:left) { attribute.eq(2) }
18
- let(:right) { attribute.eq(1) }
17
+ context 'when the left and right are equality predicates' do
18
+ let(:left) { attribute.eq(2) }
19
+ let(:right) { attribute.eq(1) }
19
20
 
20
- it { should be(true) }
21
+ it { should be(true) }
22
+ end
23
+
24
+ context 'when the left is an equality predicate and the right is an inclusion predicate' do
25
+ let(:left) { attribute.eq(2) }
26
+ let(:right) { attribute.include([ 1, 3 ]) }
27
+
28
+ it { should be(true) }
29
+ end
30
+
31
+ context 'when the left is an inclusion predicate and the right is an equality predicate' do
32
+ let(:left) { attribute.include([ 2, 3 ]) }
33
+ let(:right) { attribute.eq(1) }
34
+
35
+ it { should be(true) }
36
+ end
37
+
38
+ context 'when the left and right are inclusion predicatess' do
39
+ let(:left) { attribute.include([ 2, 3 ]) }
40
+ let(:right) { attribute.include([ 1, 3 ]) }
41
+
42
+ it { should be(true) }
43
+ end
21
44
  end
22
45
 
23
46
  context 'when the operands are not optimizable' do
24
- let(:left) { attribute.ne(1) }
25
- let(:right) { attribute.eq(2) }
47
+ context 'when the left is an inequality predicate and the right is an equality predicate' do
48
+ let(:left) { attribute.ne(1) }
49
+ let(:right) { attribute.eq(2) }
50
+
51
+ it { should be(false) }
52
+ end
53
+
54
+ context 'when the left is an exclusion predicate and the right is an equality predicate' do
55
+ let(:left) { attribute.exclude([ 1, 3 ]) }
56
+ let(:right) { attribute.eq(2) }
26
57
 
27
- it { should be(false) }
58
+ it { should be(false) }
59
+ end
28
60
  end
29
61
  end
@@ -14,15 +14,53 @@ describe Optimizer::Function::Connective::Disjunction::OptimizableToInclusion, '
14
14
  end
15
15
 
16
16
  context 'when the right operands are optimizable' do
17
- let(:left) { attribute.eq(2) }
18
- let(:right) { attribute.eq(1) }
17
+ context 'when the left and right are equality predicates' do
18
+ let(:left) { attribute.eq(2) }
19
+ let(:right) { attribute.eq(1) }
19
20
 
20
- it { should be_kind_of(Function::Predicate::Inclusion) }
21
+ it { should be_kind_of(Function::Predicate::Inclusion) }
21
22
 
22
- its(:left) { should equal(attribute) }
23
+ its(:left) { should equal(attribute) }
23
24
 
24
- # enumerable order is normalized
25
- its(:right) { should == [ 1, 2 ] }
25
+ # enumerable order is normalized
26
+ its(:right) { should == [ 1, 2 ] }
27
+ end
28
+
29
+ context 'when the left is an equality predicate and the right is an inclusion predicate' do
30
+ let(:left) { attribute.eq(2) }
31
+ let(:right) { attribute.include([ 1 ]) }
32
+
33
+ it { should be_kind_of(Function::Predicate::Inclusion) }
34
+
35
+ its(:left) { should equal(attribute) }
36
+
37
+ # enumerable order is normalized
38
+ its(:right) { should == [ 1, 2 ] }
39
+ end
40
+
41
+ context 'when the left is an inclusion predicate and the right is an equality predicate' do
42
+ let(:left) { attribute.include([ 2 ]) }
43
+ let(:right) { attribute.eq(1) }
44
+
45
+ it { should be_kind_of(Function::Predicate::Inclusion) }
46
+
47
+ its(:left) { should equal(attribute) }
48
+
49
+ # enumerable order is normalized
50
+ its(:right) { should == [ 1, 2 ] }
51
+ end
52
+
53
+ context 'when the left and right are inclusion predicatess' do
54
+ let(:left) { attribute.include([ 2 ]) }
55
+ let(:right) { attribute.include([ 1 ]) }
56
+
57
+ it { should be_kind_of(Function::Predicate::Inclusion) }
58
+
59
+ its(:left) { should equal(attribute) }
60
+
61
+ # enumerable order is normalized
62
+ its(:right) { should == [ 1, 2 ] }
63
+ end
26
64
  end
27
65
 
28
66
  context 'when the right operands are not optimizable' do
@@ -0,0 +1,149 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Optimizer::PredicatePartition, '#left' do
6
+ subject { object.left }
7
+
8
+ let(:left_header) { Relation::Header.new([ [ :id, Integer ], [ :user_name, String ], Attribute::Boolean.new(:active_user), Attribute::Boolean.new(:active) ]) }
9
+ let(:right_header) { Relation::Header.new([ [ :id, Integer ], [ :employee_name, String ], Attribute::Boolean.new(:active_employee), Attribute::Boolean.new(:active) ]) }
10
+ let(:object) { described_class.new(predicate, left_header, right_header) }
11
+
12
+ context 'when the predicate is a tautology' do
13
+ let(:predicate) { Function::Proposition::Tautology.instance }
14
+
15
+ it 'partitions the predicate to the left' do
16
+ should equal(Function::Proposition::Tautology.instance)
17
+ end
18
+ end
19
+
20
+ context 'when the predicate is a contradiction' do
21
+ let(:predicate) { Function::Proposition::Contradiction.instance }
22
+
23
+ it 'partitions the predicate to the left' do
24
+ should equal(Function::Proposition::Contradiction.instance)
25
+ end
26
+ end
27
+
28
+ context 'when the predicate is an attribute' do
29
+ context 'with an attribute from the left header, but not the right header' do
30
+ let(:predicate) { left_header[:active_user] }
31
+
32
+ it 'partitions the predicate to the left' do
33
+ should equal(predicate)
34
+ end
35
+ end
36
+
37
+ context 'with an attribute from the right header, but not the left header' do
38
+ let(:predicate) { right_header[:active_employee] }
39
+
40
+ it 'does not partition the predicate to the left' do
41
+ should equal(Function::Proposition::Tautology.instance)
42
+ end
43
+ end
44
+
45
+ context 'with an attribute from the left and right header' do
46
+ let(:predicate) { left_header[:active] }
47
+
48
+ it 'partitions the predicate to the left' do
49
+ should equal(predicate)
50
+ end
51
+ end
52
+ end
53
+
54
+ context 'when the predicate is a binary predicate' do
55
+ context 'with an attribute from the left header, but not the right header' do
56
+ let(:predicate) { left_header[:user_name].eq('Dan Kubb') }
57
+
58
+ it 'partitions the predicate to the left' do
59
+ should equal(predicate)
60
+ end
61
+ end
62
+
63
+ context 'with an attribute from the right header, but not the left header' do
64
+ let(:predicate) { right_header[:employee_name].eq('Dan Kubb') }
65
+
66
+ it 'does not partition the predicate to the left' do
67
+ should equal(Function::Proposition::Tautology.instance)
68
+ end
69
+ end
70
+
71
+ context 'with an attribute from the left and right header' do
72
+ let(:predicate) { left_header[:id].eq(1) }
73
+
74
+ it 'partitions the predicate to the left' do
75
+ should equal(predicate)
76
+ end
77
+ end
78
+
79
+ context 'with an attribute from the left header, and an attribute from the left and right header' do
80
+ let(:predicate) { left_header[:active_user].eq(left_header[:active]) }
81
+
82
+ it 'partitions the predicate to the left' do
83
+ should equal(predicate)
84
+ end
85
+ end
86
+
87
+ context 'with an attribute from the right header, and an attribute from the left and right header' do
88
+ let(:predicate) { right_header[:active_employee].eq(left_header[:active]) }
89
+
90
+ it 'partitions the predicate to the left' do
91
+ should equal(predicate)
92
+ end
93
+ end
94
+
95
+ context 'with disjoint attributes from the left and right header' do
96
+ let(:predicate) { left_header[:user_name].eq(right_header[:employee_name]) }
97
+
98
+ it 'does not partition the predicate to the left' do
99
+ should equal(Function::Proposition::Tautology.instance)
100
+ end
101
+ end
102
+ end
103
+
104
+ context 'when the predicate is a negation' do
105
+ context 'with an attribute from the left header, but not the right header' do
106
+ let(:predicate) { Function::Connective::Negation.new(left_header[:active_user]) }
107
+
108
+ it 'partitions the predicate to the left' do
109
+ should equal(predicate)
110
+ end
111
+ end
112
+
113
+ context 'with an attribute from the right header, but not the left header' do
114
+ let(:predicate) { Function::Connective::Negation.new(right_header[:active_employee]) }
115
+
116
+ it 'does not partition the predicate to the left' do
117
+ should equal(Function::Proposition::Tautology.instance)
118
+ end
119
+ end
120
+
121
+ context 'with an attribute from the left and right header' do
122
+ let(:predicate) { Function::Connective::Negation.new(left_header[:active]) }
123
+
124
+ it 'partitions the predicate to the left' do
125
+ should equal(predicate)
126
+ end
127
+ end
128
+ end
129
+
130
+ context 'when the predicate is a conjunction' do
131
+ let(:left_predicate) { left_header[:user_name].eq('Dan Kubb') }
132
+ let(:right_predicate) { right_header[:employee_name].eq('Dan Kubb') }
133
+ let(:predicate) { left_predicate.and(right_predicate) }
134
+
135
+ it 'partitions the predicate to the left that reference the left header' do
136
+ should equal(left_predicate)
137
+ end
138
+ end
139
+
140
+ context 'when the predicate is a disjunction' do
141
+ let(:left_predicate) { left_header[:user_name].eq('Dan Kubb') }
142
+ let(:right_predicate) { right_header[:employee_name].eq('Dan Kubb') }
143
+ let(:predicate) { left_predicate.or(right_predicate) }
144
+
145
+ it 'partitions the predicate to the left that reference the left header, transforming it with De Morgan\'s Law' do
146
+ should eql(left_header[:user_name].ne('Dan Kubb'))
147
+ end
148
+ end
149
+ end