Browse files

Add Insertion::ProjectionLeft

* Handles push-down of an insertion into a projection operator
* Only applies if the removed attributes are optional, since the right relation
  needs to be extended with those attributes and the value defaults to nil.
  • Loading branch information...
1 parent ead1c84 commit 85aaa6998f765e737ee8e19121455c777edb7785 @dkubb committed May 22, 2012
View
3 config/site.reek
@@ -50,7 +50,8 @@ NestedIterators:
# none of these use nested iterators, they use nested blocks only
Veritas::Optimizer::Algebra::Extension#wrap_operand,
Veritas::Optimizer::Algebra::Summarization#wrap_operand,
- Veritas::Optimizer::Algebra::Summarization::EmptyOperand#optimize
+ Veritas::Optimizer::Algebra::Summarization::EmptyOperand#optimize,
+ Veritas::Optimizer::Relation::Operation::Insertion::ProjectionLeft#extend_right
]
enabled: true
max_allowed_nesting: 1
View
62 lib/veritas/optimizer/relation/operation/insertion.rb
@@ -117,9 +117,71 @@ def tuple_predicate
end # class RestrictionLeft
+ class ProjectionLeft < self
+
+ # Test if the left operand is a Projection
+ #
+ # @return [Boolean]
+ #
+ # @api private
+ def optimizable?
+ left.kind_of?(Veritas::Algebra::Projection) &&
+ ! required_attributes?
+ end
+
+ # An Insertion into a Projection applies to its operand
+ #
+ # Push-down the insertion to apply to the projection operand
+ # only if the projected-away properties are not required. The right
+ # operand will be extended with same named attributes.
+ #
+ # @return [Veritas::Algebra::Projection]
+ #
+ # @api private
+ def optimize
+ unwrap_left.insert(extend_right).project(operation.header)
+ end
+
+ private
+
+ # Test if all attributes are required
+ #
+ # @return [Boolean]
+ #
+ # @api private
+ def required_attributes?
+ removed_attributes.all? { |attribute| attribute.required? }
+ end
+
+ # Returns the attributes removed from the left projection
+ #
+ # @return [#all?]
+ #
+ # @api private
+ def removed_attributes
+ left = self.left
+ left.operand.header - left.header
+ end
+
+ # Extend the right relation with the removed attributes
+ #
+ # @return [Veritas::Algebra::Extension]
+ #
+ # @api private
+ def extend_right
+ right.extend do |context|
+ removed_attributes.each do |attribute|
+ context.add(attribute, nil)
+ end
+ end
+ end
+
+ end # class ProjectionLeft
+
Veritas::Relation::Operation::Insertion.optimizer = chain(
RenameLeft,
RestrictionLeft,
+ ProjectionLeft,
OrderLeft,
OrderRight,
MaterializedOperands,
View
22 spec/integration/veritas/relation/operation/insertion/optimize_spec.rb
@@ -63,4 +63,26 @@
it_should_behave_like 'an optimize method'
end
+
+ context 'left is a projection relation' do
+ let(:original_left) { Relation.new([ [ :id, Integer ], [ :name, String, { :required => false } ] ], [ [ 1, 'John Doe' ] ].each) }
+ let(:left) { original_left.project([ :id ]) }
+ let(:right) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ].each) }
+
+ it 'returns an equivalent relation to the unoptimized operation' do
+ should == object
+ end
+
+ it 'does not execute left_body#each' do
+ left_body.should_not_receive(:each)
+ subject
+ end
+
+ it 'does not execute right_body#each' do
+ right_body.should_not_receive(:each)
+ subject
+ end
+
+ it_should_behave_like 'an optimize method'
+ end
end
View
35 spec/unit/veritas/optimizer/relation/operation/insertion/projection_left/optimizable_spec.rb
@@ -0,0 +1,35 @@
+# encoding: utf-8
+
+require 'spec_helper'
+
+describe Optimizer::Relation::Operation::Insertion::ProjectionLeft, '#optimizable?' do
+ subject { object.optimizable? }
+
+ let(:object) { described_class.new(relation) }
+ let(:relation) { left.insert(right) }
+ let(:right) { Relation.new([ [ :id, Integer ] ], [ [ 2 ] ].each) }
+
+ before do
+ object.operation.should be_kind_of(Relation::Operation::Insertion)
+ end
+
+ context 'when the left is a projection, and removed attributes are optional' do
+ let(:header) { [ [ :id, Integer ], [ :name, String, { :required => false } ] ] }
+ let(:left) { Relation.new(header, [ [ 1, 'John Doe' ] ].each).project([ :id ]) }
+
+ it { should be(true) }
+ end
+
+ context 'when the left is a projection, and removed attributes are required' do
+ let(:header) { [ [ :id, Integer ], [ :name, String ] ] }
+ let(:left) { Relation.new(header, [ [ 1, 'John Doe' ] ].each).project([ :id ]) }
+
+ it { should be(false) }
+ end
+
+ context 'when the left is not a projection' do
+ let(:left) { Relation.new([ [ :id, Integer ] ], [ [ 1 ] ].each) }
+
+ it { should be(false) }
+ end
+end
View
29 spec/unit/veritas/optimizer/relation/operation/insertion/projection_left/optimize_spec.rb
@@ -0,0 +1,29 @@
+# encoding: utf-8
+
+require 'spec_helper'
+
+describe Optimizer::Relation::Operation::Insertion::ProjectionLeft, '#optimize' do
+ subject { object.optimize }
+
+ let(:object) { described_class.new(relation) }
+ let(:relation) { left.insert(right) }
+ let(:base_left) { Relation.new(header, [ [ 1, 'John Doe' ] ].each) }
+ let(:left) { base_left.project([ :id ]) }
+ let(:right) { Relation.new([ [ :id, Integer ] ], [ [ 2 ] ].each) }
+ let(:header) { [ [ :id, Integer ], [ :name, String, { :required => false } ] ] }
+
+ before do
+ object.should be_optimizable
+ end
+
+ it { should be_instance_of(Algebra::Projection) }
+
+ # the operand is the base left and the right is extended with the removed attributes
+ its(:operand) do
+ should eql(base_left.insert(right.extend { |r|
+ r.add([ :name, String, { :required => false } ], nil)
+ }))
+ end
+
+ its(:header) { should == [ [ :id, Integer ] ] }
+end
View
6 veritas-optimizer.gemspec
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["Dan Kubb"]
- s.date = "2012-05-21"
+ s.date = "2012-05-22"
s.description = "Optimizes veritas relations"
s.email = "dan.kubb@gmail.com"
s.extra_rdoc_files = [
@@ -316,8 +316,12 @@ Gem::Specification.new do |s|
"spec/unit/veritas/optimizer/relation/operation/binary/unoptimized_operands/optimizable_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/binary/unoptimized_operands/optimize_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/combination/optimize_spec.rb",
+ "spec/unit/veritas/optimizer/relation/operation/insertion/projection_left/optimizable_spec.rb",
+ "spec/unit/veritas/optimizer/relation/operation/insertion/projection_left/optimize_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/insertion/rename_left/optimizable_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/insertion/rename_left/optimize_spec.rb",
+ "spec/unit/veritas/optimizer/relation/operation/insertion/restriction_left/optimizable_spec.rb",
+ "spec/unit/veritas/optimizer/relation/operation/insertion/restriction_left/optimize_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimizable_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/limit/equal_limit_operand/optimize_spec.rb",
"spec/unit/veritas/optimizer/relation/operation/limit/limit_operand/optimizable_spec.rb",

0 comments on commit 85aaa69

Please sign in to comment.