/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.relational.cursors;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import xxl.core.cursors.MetaDataCursor;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.predicates.MetaDataPredicate;
import xxl.core.predicates.Predicate;
import xxl.core.predicates.Predicates;
import xxl.core.relational.JoinUtils;
import xxl.core.relational.metaData.MergedResultSetMetaData;
import xxl.core.relational.metaData.ResultSetMetaDatas;
import xxl.core.relational.tuples.Tuple;
import xxl.core.util.metaData.CompositeMetaData;
/**
* A nested-loops implementation of a flexible join operator.
*
* <p>The following types of joins are available. For a complete discussion of
* join operations read the adequate chapter of a good book on database
* systems, e.g. "Datenbanksysteme" from Kemper, Eickler.
* <ul>
* <li><tt>THETA_JOIN</tt></li>
* <li><tt>LEFT_OUTER_JOIN</tt></li>
* <li><tt>RIGHT_OUTER_JOIN</tt></li>
* <li><tt>OUTER_JOIN</tt></li>
* <li><tt>NATURAL_JOIN</tt></li>
* <li><tt>SEMI_JOIN</tt></li>
* <li><tt>CARTESIAN_PRODUCT</tt></li>
* </ul>
* Updates and removes are not supported.</p>
*/
public class NestedLoopsJoin extends xxl.core.cursors.joins.NestedLoopsJoin<Tuple, Tuple> implements MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> {
/**
* An enumeration of constants specifying the join types supported by this
* class.
*/
public static enum Type {
/**
* A constant specifying a theta join. Only the tuples for which the
* specified predicate is <code>true</code> will be returned.
*/
THETA_JOIN,
/**
* A constant specifying a left outer join. The tuples for which the
* specified predicate is <code>true</code> as well as all elements of
* the <code>cursor1</code> not qualifying concerning the predicate
* will be returned. The function <code>newResult</code> is called with
* arguments <code>cursor1.peek()</code> and <code>null</code>.
*/
LEFT_OUTER_JOIN,
/**
* A constant specifying a right outer join. The tuples for which the
* specified predicate is <code>true</code> as well as all elements of
* the <code>cursor2</code> not qualifying concerning the predicate
* will be returned. The function <code>newResult</code> is called with
* arguments <code>null</code> and <code>cursor2.peek()</code>.
*/
RIGHT_OUTER_JOIN,
/**
* A constant specifying a full outer join. The tuples for which the
* specified predicate is <code>true</code> as well as all tuples of
* the LEFT and RIGHT OUTER_JOIN will be returned.
*/
OUTER_JOIN,
/**
* A constant specifying a natural join. The tuples are compared using
* their common columns.
*/
NATURAL_JOIN,
/**
* A constant specifying a semi join. The tuples are compared using
* their common columns. The results become projected to the columns of
* the first input relation.
*/
SEMI_JOIN,
/**
* A constant specifying a Cartesian product. Every combination of
* tuples of the two relations is returned.
*/
CARTESIAN_PRODUCT
}
/**
* An internal variable used for storing the metadata information of this
* join operator.
*/
protected CompositeMetaData<Object, Object> globalMetaData;
/**
* Advanced constructor creating a new nested-loops join with lazy
* evaluation backed on two cursors using a metadata predicate to select
* the resulting tuples. This constructor also supports the handling of a
* non-resetable input metadata cursor, <code>cursor2</code>, because a
* parameterless function can be defined that returns this input metadata
* cursor. Furthermore a function can be specified that is invoked on each
* qualifying tuple before it is returned to the caller concerning a call
* to <code>next</code>. This function is a kind of factory-method to model
* the resulting object.
*
* <p>Handle with care! Do not use this constructor if you do not know
* exactly what you are doing.</p>
*
* @param cursor1 the input metadata cursor that is traversed in the
* "outer" loop.
* @param cursor2 the input metadata cursor that is traversed in the
* "inner" loop.
* @param newCursor a parameterless function that delivers a new metadata
* cursor, when the cursor <code>cursor2</code> cannot be reset,
* i.e., <code>cursor2.reset()</code> will cause a
* {@link java.lang.UnsupportedOperationException}.
* @param predicate the predicate evaluated for each tuple of elements
* backed on one element of each input-cursor in order to select
* them. Only these tuples where the predicate's evaluation result
* is <code>true</code> have been qualified to be a result of the
* join-operation.
* @param createTuple a factory method (function) that takes two parameters
* as arguments and is invoked on each tuple where the predicate's
* evaluation result is <code>true</code>, i.e. on each qualifying
* tuple before it is returned to the caller concerning a call to
* <code>next()</code>.
* @param type the type of the join operation. Possible values are the
* elements of the enumeration defined above in this class.
* @throws IllegalArgumentException if the specified type is not valid.
* @throws UnsupportedOperationException if the input metadata cursor
* <code>cursor1</code> or <code>cursor2</code> cannot be reseted.
*/
public NestedLoopsJoin(MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor1, MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor2, Function<?, ? extends MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>>> newCursor, MetaDataPredicate<? super Tuple, CompositeMetaData<Object, Object>> predicate, Function<Object, ? extends Tuple> createTuple, Type type) {
super(
cursor1,
cursor2,
newCursor,
predicate,
JoinUtils.genericJoinTupleFactory(
createTuple,
(MergedResultSetMetaData)ResultSetMetaDatas.getResultSetMetaData(predicate)
),
type == Type.LEFT_OUTER_JOIN ?
xxl.core.cursors.joins.NestedLoopsJoin.Type.LEFT_OUTER_JOIN :
type == Type.RIGHT_OUTER_JOIN ?
xxl.core.cursors.joins.NestedLoopsJoin.Type.RIGHT_OUTER_JOIN :
type == Type.OUTER_JOIN ?
xxl.core.cursors.joins.NestedLoopsJoin.Type.OUTER_JOIN :
xxl.core.cursors.joins.NestedLoopsJoin.Type.THETA_JOIN
);
globalMetaData = new CompositeMetaData<Object, Object>();
globalMetaData.add(ResultSetMetaDatas.RESULTSET_METADATA_TYPE, ResultSetMetaDatas.getResultSetMetaData(predicate));
}
/**
* Creates a new nested-loops join which performs a theta join. Lazy
* evaluation is used backed on two cursors using a predicate to select the
* resulting tuples. This constructor also supports the handling of a
* non-resetable input metadata cursor, <code>cursor2</code>, because a
* parameterless function can be defined that returns this input metadata
* cursor. Furthermore a function can be specified that is invoked on each
* qualifying tuple before it is returned to the caller concerning a call
* to <code>next</code>. This function is a kind of factory-method to model
* the resulting object.
*
* @param cursor1 the input metadata cursor that is traversed in the
* "outer" loop.
* @param cursor2 the input metadata cursor that is traversed in the
* "inner" loop.
* @param newCursor a parameterless function that delivers a new metadata
* cursor, when the cursor <code>cursor2</code> cannot be reset,
* i.e., <code>cursor2.reset()</code> will cause a
* {@link java.lang.UnsupportedOperationException}.
* @param theta the predicate evaluated for each tuple of elements backed
* on one element of each input-cursor in order to select them. Only
* these tuples where the predicate's evaluation result is
* <code>true</code> have been qualified to be a result of the
* join-operation.
* @param createTuple a factory method (function) that takes two parameters
* as arguments and is invoked on each tuple where the predicate's
* evaluation result is <code>true</code>, i.e. on each qualifying
* tuple before it is returned to the caller concerning a call to
* <code>next</code>.
* @throws IllegalArgumentException if the specified type is not valid.
* @throws UnsupportedOperationException if the input metadata cursor
* <code>cursor1</code> or <code>cursor2</code> cannot be reseted.
*/
public NestedLoopsJoin(MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor1, MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor2, Function<?, ? extends MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>>> newCursor, Predicate<? super Tuple> theta, Function<Object, ? extends Tuple> createTuple) {
this(
cursor1,
cursor2,
newCursor,
JoinUtils.thetaJoinMetaDataPredicate(
theta,
ResultSetMetaDatas.getResultSetMetaData(cursor1),
ResultSetMetaDatas.getResultSetMetaData(cursor2)
),
createTuple,
Type.THETA_JOIN
);
}
/**
* Creates a new nested-loops join with lazy evaluation backed on two
* cursors using a predicate to select the resulting tuples. This
* constructor also supports the handling of a non-resetable input metadata
* cursor, <code>cursor2</code>, because a parameterless function can be
* defined that returns this input metadata cursor. Furthermore a function
* can be specified that is invoked on each qualifying tuple before it is
* returned to the caller concerning a call to <code>next</code>. This
* function is a kind of factory-method to model the resulting object.
*
* @param cursor1 the input metadata cursor that is traversed in the
* "outer" loop.
* @param cursor2 the input metadata cursor that is traversed in the
* "inner" loop.
* @param newCursor a parameterless function that delivers a new metadata
* cursor, when the cursor <code>cursor2</code> cannot be reset,
* i.e., <code>cursor2.reset()</code> will cause a
* {@link java.lang.UnsupportedOperationException}.
* @param createTuple a factory method (function) that takes two parameters
* as arguments and is invoked on each tuple where the predicate's
* evaluation result is <code>true</code>, i.e. on each qualifying
* tuple before it is returned to the caller concerning a call to
* <code>next()</code>.
* @param type the type of the join operation. Possible values are the
* elements of the enumeration defined above in this class.
* @throws IllegalArgumentException if the specified type is not valid.
* @throws UnsupportedOperationException if input metadata cursor
* <code>cursor1</code> or <code>cursor2</code> cannot be reseted.
*/
public NestedLoopsJoin(MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor1, MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>> cursor2, Function<?, ? extends MetaDataCursor<? extends Tuple, CompositeMetaData<Object, Object>>> newCursor, Function<Object, ? extends Tuple> createTuple, Type type) {
this(
cursor1,
cursor2,
newCursor,
type == Type.THETA_JOIN || type == Type.CARTESIAN_PRODUCT ?
JoinUtils.thetaJoinMetaDataPredicate(
Predicates.TRUE,
ResultSetMetaDatas.getResultSetMetaData(cursor1),
ResultSetMetaDatas.getResultSetMetaData(cursor2)
) :
type == Type.SEMI_JOIN ?
JoinUtils.semiJoinMetaDataPredicate(
JoinUtils.naturalJoinMetaDataPredicate(
ResultSetMetaDatas.getResultSetMetaData(cursor1),
ResultSetMetaDatas.getResultSetMetaData(cursor2)
),
new ResultSetMetaData[] {
ResultSetMetaDatas.getResultSetMetaData(cursor1),
ResultSetMetaDatas.getResultSetMetaData(cursor2)
},
0
) :
JoinUtils.naturalJoinMetaDataPredicate(
ResultSetMetaDatas.getResultSetMetaData(cursor1),
ResultSetMetaDatas.getResultSetMetaData(cursor2)
),
createTuple,
type
);
}
/**
* Creates a new nested-loops join which performs a theta join. Lazy
* evaluation is used backed on two cursors using a predicate to select the
* resulting tuples. This constructor also supports the handling of a
* non-resetable input metadata cursor, <code>cursor2</code>, because a
* parameterless function can be defined that returns this input metadata
* cursor. Furthermore a function can be specified that is invoked on each
* qualifying tuple before it is returned to the caller concerning a call
* to <code>next</code>. This Function is a kind of factory-method to model
* the resulting object.
*
* @param resultSet1 the input result set that is traversed in the "outer"
* loop.
* @param resultSet2 the input result set that is traversed in the "inner"
* loop.
* @param newResultSet a function without parameters that delivers a new
* result set, when the iterating over the "inner" result set
* <code>resultSet2</code> cannot be reset, i.e.,
* <code>cursor2.reset()</code> will cause an
* {@link java.lang.UnsupportedOperationException}.
* @param predicate the predicate evaluated for each tuple of elements
* backed on one element of each input-cursor in order to select
* them. Only these tuples where the predicate's evaluation result
* is <code>true</code> have been qualified to be a result of the
* join-operation.
* @param createTuple a factory method (function) that takes two parameters
* as arguments and is invoked on each tuple where the predicate's
* evaluation result is <code>true</code>, i.e. on each qualifying
* tuple before it is returned to the caller concerning a call to
* <code>next</code>.
* @throws IllegalArgumentException if the specified type is not valid.
* @throws UnsupportedOperationException if the input metadata cursor
* <code>cursor1</code> or <code>cursor2</code> cannot be reseted.
*/
public NestedLoopsJoin(ResultSet resultSet1, ResultSet resultSet2, final Function<?, ? extends ResultSet> newResultSet, Predicate<? super Tuple> predicate, Function<Object, ? extends Tuple> createTuple) {
this(
new ResultSetMetaDataCursor(resultSet1),
new ResultSetMetaDataCursor(resultSet2),
new AbstractFunction<Object, MetaDataCursor<Tuple, CompositeMetaData<Object, Object>>>() {
@Override
public MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> invoke() {
return new ResultSetMetaDataCursor(newResultSet.invoke());
}
},
predicate,
createTuple
);
}
/**
* Creates a new nested-loops join with lazy evaluation backed on two
* cursors using a predicate to select the resulting tuples. This
* constructor also supports the handling of a non-resetable input metadata
* cursor, <code>cursor2</code>, because a parameterless function can be
* defined that returns this input metadata cursor. Furthermore a function
* can be specified that is invoked on each qualifying tuple before it is
* returned to the caller concerning a call to <code>next</code>. This
* function is a kind of factory-method to model the resulting object.
*
* @param resultSet1 the input result set that is traversed in the "outer"
* loop.
* @param resultSet2 the input result set that is traversed in the "inner"
* loop.
* @param newResultSet a function without parameters that delivers a new
* result set, when the iterating over the "inner" result set
* <code>resultSet2</code> cannot be reset, i.e.,
* <code>cursor2.reset()</code> will cause an
* {@link java.lang.UnsupportedOperationException}.
* @param createTuple a factory method (function) that takes two parameters
* as arguments and is invoked on each tuple where the predicate's
* evaluation result is <code>true</code>, i.e. on each qualifying
* tuple before it is returned to the caller concerning a call to
* <code>next</code>.
* @param type the type of the join operation. Possible values are the
* elements of the enumeration defined above in this class.
* @throws IllegalArgumentException if the specified type is not valid.
* @throws UnsupportedOperationException if the input metadata cursor
* <code>cursor1</code> or <code>cursor2</code> cannot be reseted.
*/
public NestedLoopsJoin(ResultSet resultSet1, ResultSet resultSet2, final Function<?, ? extends ResultSet> newResultSet, Function<Object, ? extends Tuple> createTuple, Type type) {
this(
new ResultSetMetaDataCursor(resultSet1),
new ResultSetMetaDataCursor(resultSet2),
new AbstractFunction<Object, MetaDataCursor<Tuple, CompositeMetaData<Object, Object>>>() {
@Override
public MetaDataCursor<Tuple, CompositeMetaData<Object, Object>> invoke() {
return new ResultSetMetaDataCursor(newResultSet.invoke());
}
},
createTuple,
type
);
}
/**
* Returns the metadata information for this metadata-cursor as a composite
* metadata ({@link CompositeMetaData}).
*
* @return the metadata information for this metadata-cursor as a composite
* metadata ({@link CompositeMetaData}).
*/
public CompositeMetaData<Object, Object> getMetaData() {
return globalMetaData;
}
}