Skip to content

Commit

Permalink
Add Insertion::ProjectionLeft
Browse files Browse the repository at this point in the history
* 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
dkubb committed May 22, 2012
1 parent ead1c84 commit 85aaa69
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 2 deletions.
3 changes: 2 additions & 1 deletion config/site.reek
Expand Up @@ -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
Expand Down
62 changes: 62 additions & 0 deletions lib/veritas/optimizer/relation/operation/insertion.rb
Expand Up @@ -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,
Expand Down
Expand Up @@ -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
@@ -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
@@ -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
6 changes: 5 additions & 1 deletion veritas-optimizer.gemspec
Expand Up @@ -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 = [
Expand Down Expand Up @@ -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",
Expand Down

0 comments on commit 85aaa69

Please sign in to comment.