Skip to content

Commit

Permalink
Add Insertion::RestrictionLeft
Browse files Browse the repository at this point in the history
* Handles push-down of an insertion into a restriction operator
* Creates a disjunction of the predicates for the left restriction and each
  right tuple, and then wraps the insertion in the new restriction. This is
  equivalent to the original operation, except that the insertion now wraps the
  original restriction operand. Also, the right hand side is materialized, which
  may be expensive, but it probably would've happened when the tuples were
  inserted into the base relation.
  • Loading branch information
dkubb committed May 21, 2012
1 parent da4180a commit ae0c209
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 16 deletions.
2 changes: 1 addition & 1 deletion config/flay.yml
@@ -1,3 +1,3 @@
---
threshold: 113
total_score: 862
total_score: 858
72 changes: 63 additions & 9 deletions lib/veritas/optimizer/relation/operation/insertion.rb
Expand Up @@ -8,6 +8,17 @@ module Operation
# Abstract base class representing Difference optimizations
class Insertion < Relation::Operation::Binary

private

# Unwrap the operand from the left relation
#
# @return [Relation]
#
# @api private
def unwrap_left
left.operand
end

# Optimize when the left operand is a rename
class RenameLeft < self

Expand Down Expand Up @@ -35,15 +46,6 @@ def optimize

private

# Unwrap the operand from the left relation
#
# @return [Relation]
#
# @api private
def unwrap_left
left.operand
end

# Wrap the right relation in a Rename
#
# @return [Relation]
Expand All @@ -64,8 +66,60 @@ def aliases

end # class RenameLeft

# Optimize when the left operand is a restriction
class RestrictionLeft < self

# Test if the left operand is a Restriction
#
# @return [Boolean]
#
# @api private
def optimizable?
left.kind_of?(Veritas::Algebra::Restriction)
end

# An Insertion into a Restriction applies to its operand
#
# Push-down the insertion to apply to the restriction operand,
# and make sure the inserted tuples match the predicate since
# they must be included in the new relation.
#
# @return [Veritas::Algebra::Restriction]
#
# @api private
def optimize
unwrap_left.insert(materialize_right).restrict { tuple_predicate }
end

private

# Materialize the right operand
#
# @return [Relation::Materialized]
#
# @api private
def materialize_right
right.materialize
end

# Predicate matching the left or right tuples
#
# @return [Function]
#
# @api private
def tuple_predicate
materialize_right.reduce(left.predicate) do |predicate, tuple|
predicate.or(tuple.predicate)
end
end

memoize :materialize_right

end # class RestrictionLeft

Veritas::Relation::Operation::Insertion.optimizer = chain(
RenameLeft,
RestrictionLeft,
OrderLeft,
OrderRight,
MaterializedOperands,
Expand Down
Expand Up @@ -8,7 +8,8 @@
let(:object) { described_class.new(left, right) }
let(:original_left) { Relation.new(header, left_body) }
let(:original_right) { Relation.new(header, right_body) }
let(:header) { [ [ :id, Integer ] ] }
let(:header) { [ attribute ] }
let(:attribute) { Attribute::Integer.new(:id) }
let(:left_body) { [ [ 1 ] ].each }
let(:right_body) { [ [ 2 ] ].each }

Expand All @@ -30,9 +31,36 @@
subject
end

# check to make sure the right rename is factored out
# check to make sure the insertion is pushed-down, and the right rename
# is factored out (since it is a rename, then an inverted rename)
its(:operand) { should eql(original_left.insert(original_right)) }

it_should_behave_like 'an optimize method'
end

context 'left is a restriction relation' do
let(:left) { original_left.restrict { |r| r.id.eq(1) } }
let(:right) { original_right }

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 'executes right_body#each' do
right_body.should_receive(:each)
subject
end

# check to make sure the insertion is pushed-down, and the right relation is
# materialized, and the predicate matches tuples from the left and right
its(:operand) { should eql(original_left.insert(right.materialize)) }
its(:predicate) { should eql(attribute.include([ 1, 2 ])) }

it_should_behave_like 'an optimize method'
end
end
Expand Up @@ -18,10 +18,6 @@

it { should be_instance_of(Algebra::Rename) }

it { should == Relation.new([ [ :other_id, Integer ] ], [ [ 1 ], [ 2 ] ]) }

its(:operand) { should be_instance_of(Relation::Operation::Insertion) }

# the operand is unwrapped from the left, and right becomes an inverted rename
its(:operand) { should eql(base_left.insert(right.rename(aliases.invert))) }
end
@@ -0,0 +1,29 @@
# encoding: utf-8

require 'spec_helper'

describe Optimizer::Relation::Operation::Insertion::RestrictionLeft, '#optimizable?' do
subject { object.optimizable? }

let(:object) { described_class.new(relation) }
let(:relation) { left.insert(right) }
let(:base_left) { Relation.new(header, [ [ 1 ] ].each) }
let(:right) { Relation.new(header, [ [ 2 ] ].each) }
let(:header) { Relation::Header.new([ [ :id, Integer ] ]) }

before do
object.operation.should be_kind_of(Relation::Operation::Insertion)
end

context 'when the left is a restriction' do
let(:left) { base_left.restrict { |r| r.id.eq(1) } }

it { should be(true) }
end

context 'when the left is not a rename' do
let(:left) { base_left }

it { should be(false) }
end
end
@@ -0,0 +1,27 @@
# encoding: utf-8

require 'spec_helper'

describe Optimizer::Relation::Operation::Insertion::RestrictionLeft, '#optimize' do
subject { object.optimize }

let(:object) { described_class.new(relation) }
let(:relation) { left.insert(right) }
let(:base_left) { Relation.new(header, [ [ 1 ] ].each) }
let(:left) { base_left.restrict { |r| r.id.eq(1) } }
let(:right) { Relation.new(header, [ [ 2 ] ].each) }
let(:header) { Relation::Header.new([ attribute ]) }
let(:attribute) { Attribute::Integer.new(:id) }

before do
object.should be_optimizable
end

it { should be_instance_of(Algebra::Restriction) }

# the operand is unwrapped from the left, and right is materialized
its(:operand) { should eql(base_left.insert(right.materialize)) }

# the predicate is a disjunction of the original and each tuple predicate
its(:predicate) { should eql(attribute.eq(1).or(attribute.eq(2))) }
end

0 comments on commit ae0c209

Please sign in to comment.