use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class PredicateList method getPushablePredicates.
/**
* Build a list of pushable predicates, if any,
* that satisfy the referencedTableMap.
*
* @param referencedTableMap The referenced table map
*
* @return A list of pushable predicates, if any,
* that satisfy the referencedTableMap.
*
* @exception StandardException Thrown on error
*/
PredicateList getPushablePredicates(JBitSet referencedTableMap) throws StandardException {
PredicateList pushPList = null;
// Walk the list backwards because of possible deletes
for (int index = size() - 1; index >= 0; index--) {
Predicate predicate = elementAt(index);
if (!predicate.getPushable()) {
continue;
}
JBitSet curBitSet = predicate.getReferencedSet();
/* Do we have a match? */
if (referencedTableMap.contains(curBitSet)) {
/* Add the matching predicate to the push list */
if (pushPList == null) {
pushPList = new PredicateList(getContextManager());
}
pushPList.addPredicate(predicate);
/* Remap all of the ColumnReferences to point to the
* source of the values.
*/
RemapCRsVisitor rcrv = new RemapCRsVisitor(true);
predicate.getAndNode().accept(rcrv);
/* Remove the matching predicate from the outer list */
removeElementAt(index);
}
}
return pushPList;
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class PredicateList method searchClauseTransitiveClosure.
/**
* Perform transitive closure on search clauses. We build a
* list of search clauses of the form:
* <ColumnReference> <RelationalOperator> [<ConstantNode>]
* We also build a list of equijoin conditions of form:
* <ColumnReference1> = <ColumnReference2>
* where both columns are from different tables in the same query block.
* For each search clause in the list, we search the equijoin list to see
* if there is an equijoin clause on the same column. If so, then we
* search the search clause list for a search condition on the column
* being joined against with the same relation operator and constant. If
* a match is found, then there is no need to add a new predicate.
* Otherwise, we add a new search condition on the column being joined
* with. In either case, if the relational operator in the search
* clause is an "=" then we mark the equijoin clause as being redundant.
* Redundant equijoin clauses will be removed at the end of the search as
* they are * unnecessary.
*
* @param numTables The number of tables in the query
* @param hashJoinSpecified Whether or not user specified a hash join
*
* @exception StandardException Thrown on error
*/
void searchClauseTransitiveClosure(int numTables, boolean hashJoinSpecified) throws StandardException {
PredicateList equijoinClauses = new PredicateList(getContextManager());
PredicateList searchClauses = new PredicateList(getContextManager());
RelationalOperator equalsNode = null;
int size = size();
for (int index = 0; index < size; index++) {
AndNode andNode;
Predicate predicate;
predicate = elementAt(index);
andNode = predicate.getAndNode();
// Skip anything that's not a RelationalOperator
if (!predicate.isRelationalOpPredicate()) {
continue;
}
RelationalOperator operator = (RelationalOperator) andNode.getLeftOperand();
// Is this an equijoin?
if (((ValueNode) operator).isBinaryEqualsOperatorNode()) {
BinaryRelationalOperatorNode equals = (BinaryRelationalOperatorNode) operator;
// Remember any equals node for redundancy check at end
equalsNode = equals;
ValueNode left = equals.getLeftOperand();
ValueNode right = equals.getRightOperand();
if ((left instanceof ColumnReference && right instanceof ColumnReference)) {
ColumnReference leftCR = (ColumnReference) left;
ColumnReference rightCR = (ColumnReference) right;
if (leftCR.getSourceLevel() == rightCR.getSourceLevel() && leftCR.getTableNumber() != rightCR.getTableNumber()) {
equijoinClauses.addElement(predicate);
}
continue;
}
}
// Is this a usable search clause?
if (operator instanceof UnaryComparisonOperatorNode) {
if (((UnaryComparisonOperatorNode) operator).getOperand() instanceof ColumnReference) {
searchClauses.addElement(predicate);
}
continue;
} else if (operator instanceof BinaryComparisonOperatorNode) {
BinaryComparisonOperatorNode bcon = (BinaryComparisonOperatorNode) operator;
ValueNode left = bcon.getLeftOperand();
ValueNode right = bcon.getRightOperand();
// ConstantNode or ParameterNode in the future.
if (left instanceof ColumnReference && isConstantOrParameterNode(right)) {
searchClauses.addElement(predicate);
} else if (isConstantOrParameterNode(left) && right instanceof ColumnReference) {
// put the ColumnReference on the left to simplify things
andNode.setLeftOperand(bcon.getSwappedEquivalent());
searchClauses.addElement(predicate);
}
continue;
}
}
// Nothing to do if no search clauses or equijoin clauses
if (equijoinClauses.size() == 0 || searchClauses.size() == 0) {
return;
}
/* Now we do the real work.
* NOTE: We can append to the searchClauses while walking
* them, thus we cannot cache the value of size().
*/
for (int scIndex = 0; scIndex < searchClauses.size(); scIndex++) {
ColumnReference searchCR;
DataValueDescriptor searchODV = null;
RelationalOperator ro = (RelationalOperator) (searchClauses.elementAt(scIndex).getAndNode()).getLeftOperand();
// Find the ColumnReference and constant value, if any, in the search clause
if (ro instanceof UnaryComparisonOperatorNode) {
searchCR = (ColumnReference) ((UnaryComparisonOperatorNode) ro).getOperand();
} else {
searchCR = (ColumnReference) ((BinaryComparisonOperatorNode) ro).getLeftOperand();
// Don't get value for parameterNode since not known yet.
if (((BinaryComparisonOperatorNode) ro).getRightOperand() instanceof ConstantNode) {
ConstantNode currCN = (ConstantNode) ((BinaryComparisonOperatorNode) ro).getRightOperand();
searchODV = currCN.getValue();
} else
searchODV = null;
}
// Cache the table and column numbers of searchCR
int tableNumber = searchCR.getTableNumber();
int colNumber = searchCR.getColumnNumber();
// Look for any equijoin clauses of interest
for (Predicate predicate : equijoinClauses) {
/* Skip the current equijoin clause if it has already been used
* when adding a new search clause of the same type
* via transitive closure.
* NOTE: We check the type of the search clause instead of just the
* fact that a search clause was added because multiple search clauses
* can get added when preprocessing LIKE and BETWEEN.
*/
if (predicate.transitiveSearchClauseAdded(ro)) {
continue;
}
BinaryRelationalOperatorNode equals = (BinaryRelationalOperatorNode) predicate.getAndNode().getLeftOperand();
ColumnReference leftCR = (ColumnReference) equals.getLeftOperand();
ColumnReference rightCR = (ColumnReference) equals.getRightOperand();
ColumnReference otherCR;
if (leftCR.getTableNumber() == tableNumber && leftCR.getColumnNumber() == colNumber) {
otherCR = rightCR;
} else if (rightCR.getTableNumber() == tableNumber && rightCR.getColumnNumber() == colNumber) {
otherCR = leftCR;
} else {
// this is not a matching equijoin clause
continue;
}
/* At this point we've found a search clause and an equijoin that
* are candidates for adding a new search clause via transitive
* closure. Look to see if a matching search clause already
* exists on the other table. If not, then add one.
* NOTE: In either case we mark the join clause has having added
* a search clause of this type to short circuit any future searches
*/
predicate.setTransitiveSearchClauseAdded(ro);
boolean match = false;
ColumnReference searchCR2;
RelationalOperator ro2;
for (Predicate sc : searchClauses) {
DataValueDescriptor currODV = null;
ro2 = (RelationalOperator) sc.getAndNode().getLeftOperand();
// Find the ColumnReference in the search clause
if (ro2 instanceof UnaryComparisonOperatorNode) {
searchCR2 = (ColumnReference) ((UnaryComparisonOperatorNode) ro2).getOperand();
} else {
searchCR2 = (ColumnReference) ((BinaryComparisonOperatorNode) ro2).getLeftOperand();
if (((BinaryComparisonOperatorNode) ro2).getRightOperand() instanceof ConstantNode) {
ConstantNode currCN = (ConstantNode) ((BinaryComparisonOperatorNode) ro2).getRightOperand();
currODV = currCN.getValue();
} else
currODV = null;
}
/* Is this a match? A match is a search clause with
* the same operator on the same column with a comparison against
* the same value.
*/
if (searchCR2.getTableNumber() == otherCR.getTableNumber() && searchCR2.getColumnNumber() == otherCR.getColumnNumber() && ((currODV != null && searchODV != null && currODV.compare(searchODV) == 0) || (currODV == null && searchODV == null)) && ro2.getOperator() == ro.getOperator() && ro2.getClass().getName().equals(ro.getClass().getName())) {
match = true;
break;
}
}
// Add the new search clause if no match found
if (!match) {
// Build a new predicate
OperatorNode roClone = (OperatorNode) ro.getTransitiveSearchClause((ColumnReference) otherCR.getClone());
/* Set type info for the operator node */
if (roClone instanceof BinaryComparisonOperatorNode) {
((BinaryComparisonOperatorNode) roClone).bindComparisonOperator();
} else {
((UnaryComparisonOperatorNode) roClone).bindComparisonOperator();
}
// Convert the roClone into CNF:
//
// AND
// / \
// roClone true
//
ValueNode trueNode = new BooleanConstantNode(true, getContextManager());
AndNode newAnd = new AndNode(roClone, trueNode, getContextManager());
newAnd.postBindFixup();
// Add a new predicate to both the search clauses and this list
JBitSet tableMap = new JBitSet(numTables);
newAnd.categorize(tableMap, false);
Predicate newPred = new Predicate(newAnd, tableMap, getContextManager());
addPredicate(newPred);
searchClauses.addElement(newPred);
}
}
}
/* Finally, we eliminate any equijoin clauses made redundant by
* transitive closure, but only if the user did not specify a hash join
* in the current query block.
*/
if (hashJoinSpecified) {
return;
}
/* Walk list backwards since we can delete while
* traversing the list.
*/
for (int index = size() - 1; index >= 0; index--) {
Predicate predicate = elementAt(index);
if (predicate.transitiveSearchClauseAdded(equalsNode)) {
removeElementAt(index);
}
}
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class PredicateList method joinClauseTransitiveClosure.
/**
* Perform transitive closure on join clauses. For each table in the query,
* we build a list of equijoin clauses of the form:
* ColumnReference relop ColumnReference
* Each join clause is put on 2 lists since it joins 2 tables.
*
* We then walk the array of lists. We first walk it as the outer list.
* For each equijoin predicate, we assign an equivalence class if it does
* not yet have one. We then walk the predicate list (as middle) for the
* other table, searching for other equijoins with the middle table number
* and column number. All such predicates are assigned the same
* equivalence class. We then walk the predicate list (as inner) for the
* other side of the middle predicate to see if we can find an equijoin
* between outer and inner. If so, then we simply assign it to the same
* equivalence class. If not, then we add the new equijoin clause.
*
* Note that an equijoin predicate between two tables CANNOT be
* used for transitive closure, if either of the tables is in the
* fromlist for NOT EXISTS. In that case, the join predicate
* actually specifies that the rows from the indicated table must
* NOT exist, and therefore those non-existent rows cannot be
* transitively joined to the other matching tables. See DERBY-3033
* for a description of a situation in which this actually arises.
*
* @param numTables The number of tables in the query
* @param fromList The FromList in question.
* @param cc The CompilerContext to use
*
* @exception StandardException Thrown on error
*/
void joinClauseTransitiveClosure(int numTables, FromList fromList, CompilerContext cc) throws StandardException {
// Nothing to do if < 3 tables
if (fromList.size() < 3) {
return;
}
/* Create an array of numTables PredicateLists to hold the join clauses. */
PredicateList[] joinClauses = new PredicateList[numTables];
for (int index = 0; index < numTables; index++) {
joinClauses[index] = new PredicateList(getContextManager());
}
/* Pull the equijoin clauses, putting each one in the list for
* each of the tables being joined.
*/
for (Predicate predicate : this) {
ValueNode vn = predicate.getAndNode().getLeftOperand();
if (!(vn.isBinaryEqualsOperatorNode())) {
continue;
}
/* Is this an equijoin clause between 2 ColumnReferences? */
BinaryRelationalOperatorNode equals = (BinaryRelationalOperatorNode) vn;
ValueNode left = equals.getLeftOperand();
ValueNode right = equals.getRightOperand();
if ((left instanceof ColumnReference && right instanceof ColumnReference)) {
ColumnReference leftCR = (ColumnReference) left;
ColumnReference rightCR = (ColumnReference) right;
if (leftCR.getSourceLevel() == rightCR.getSourceLevel() && leftCR.getTableNumber() != rightCR.getTableNumber() && !fromList.tableNumberIsNotExists(leftCR.getTableNumber()) && !fromList.tableNumberIsNotExists(rightCR.getTableNumber())) {
// Add the equijoin clause to each of the lists
joinClauses[leftCR.getTableNumber()].addElement(predicate);
joinClauses[rightCR.getTableNumber()].addElement(predicate);
}
continue;
}
}
/* Walk each of the PredicateLists, using each 1 as the starting point
* of an equivalence class.
*/
for (int index = 0; index < numTables; index++) {
PredicateList outerJCL = joinClauses[index];
// Skip the empty lists
if (outerJCL.size() == 0) {
continue;
}
/* Put all of the join clauses that already have an equivalence
* class at the head of the outer list to optimize search.
*/
ArrayList<Predicate> movePreds = new ArrayList<Predicate>();
for (int jcIndex = outerJCL.size() - 1; jcIndex >= 0; jcIndex--) {
Predicate predicate = outerJCL.elementAt(jcIndex);
if (predicate.getEquivalenceClass() != -1) {
outerJCL.removeElementAt(jcIndex);
movePreds.add(predicate);
}
}
for (int mpIndex = 0; mpIndex < movePreds.size(); mpIndex++) {
outerJCL.insertElementAt(movePreds.get(mpIndex), 0);
}
// Walk this list as the outer
for (int outerIndex = 0; outerIndex < outerJCL.size(); outerIndex++) {
ColumnReference innerCR = null;
ColumnReference outerCR;
int outerTableNumber = index;
int middleTableNumber;
int outerColumnNumber;
int middleColumnNumber;
int outerEC;
/* Assign an equivalence class to those Predicates
* that have not already been assigned an equivalence class.
*/
Predicate outerP = outerJCL.elementAt(outerIndex);
if (outerP.getEquivalenceClass() == -1) {
outerP.setEquivalenceClass(cc.getNextEquivalenceClass());
}
outerEC = outerP.getEquivalenceClass();
// Get the table and column numbers
BinaryRelationalOperatorNode equals = (BinaryRelationalOperatorNode) outerP.getAndNode().getLeftOperand();
ColumnReference leftCR = (ColumnReference) equals.getLeftOperand();
ColumnReference rightCR = (ColumnReference) equals.getRightOperand();
if (leftCR.getTableNumber() == outerTableNumber) {
outerColumnNumber = leftCR.getColumnNumber();
middleTableNumber = rightCR.getTableNumber();
middleColumnNumber = rightCR.getColumnNumber();
outerCR = leftCR;
} else {
outerColumnNumber = rightCR.getColumnNumber();
middleTableNumber = leftCR.getTableNumber();
middleColumnNumber = leftCR.getColumnNumber();
outerCR = rightCR;
}
/* Walk the other list as the middle to find other join clauses
* in the chain/equivalence class
*/
PredicateList middleJCL = joinClauses[middleTableNumber];
for (Predicate middleP : middleJCL) {
/* Skip those Predicates that have already been
* assigned a different equivalence class.
*/
if (middleP.getEquivalenceClass() != -1 && middleP.getEquivalenceClass() != outerEC) {
continue;
}
int innerTableNumber;
int innerColumnNumber;
// Get the table and column numbers
BinaryRelationalOperatorNode middleEquals = (BinaryRelationalOperatorNode) middleP.getAndNode().getLeftOperand();
ColumnReference mLeftCR = (ColumnReference) middleEquals.getLeftOperand();
ColumnReference mRightCR = (ColumnReference) middleEquals.getRightOperand();
/* Find the other side of the equijoin, skipping this predicate if
* not on middleColumnNumber.
*/
if (mLeftCR.getTableNumber() == middleTableNumber) {
if (mLeftCR.getColumnNumber() != middleColumnNumber) {
continue;
}
innerTableNumber = mRightCR.getTableNumber();
innerColumnNumber = mRightCR.getColumnNumber();
} else {
if (mRightCR.getColumnNumber() != middleColumnNumber) {
continue;
}
innerTableNumber = mLeftCR.getTableNumber();
innerColumnNumber = mLeftCR.getColumnNumber();
}
// Skip over outerTableNumber.outerColumnNumber = middleTableNumber.middleColumnNumber
if (outerTableNumber == innerTableNumber && outerColumnNumber == innerColumnNumber) {
continue;
}
// Put this predicate into the outer equivalence class
middleP.setEquivalenceClass(outerEC);
/* Now go to the inner list and see if there is an equijoin
* between inner and outer on innerColumnNumber and outerColumnNumber.
* If so, then we continue our walk across middle, otherwise we
* add a new equijoin to both the inner and outer lists before
* continuing to walk across middle.
*/
int newTableNumber;
int newColumnNumber;
Predicate innerP = null;
PredicateList innerJCL = joinClauses[innerTableNumber];
int innerIndex = 0;
for (; innerIndex < innerJCL.size(); innerIndex++) {
innerP = innerJCL.elementAt(innerIndex);
// Skip over predicates with other equivalence classes
if (innerP.getEquivalenceClass() != -1 && innerP.getEquivalenceClass() != outerEC) {
continue;
}
/* Now we see if the inner predicate completes the loop.
* If so, then add it to the outer equivalence class
* and stop.
*/
// Get the table and column numbers
BinaryRelationalOperatorNode innerEquals = (BinaryRelationalOperatorNode) innerP.getAndNode().getLeftOperand();
ColumnReference iLeftCR = (ColumnReference) innerEquals.getLeftOperand();
ColumnReference iRightCR = (ColumnReference) innerEquals.getRightOperand();
if (iLeftCR.getTableNumber() == innerTableNumber) {
if (iLeftCR.getColumnNumber() != innerColumnNumber) {
continue;
}
newTableNumber = iRightCR.getTableNumber();
newColumnNumber = iRightCR.getColumnNumber();
innerCR = iLeftCR;
} else {
if (iRightCR.getColumnNumber() != innerColumnNumber) {
continue;
}
newTableNumber = iLeftCR.getTableNumber();
newColumnNumber = iLeftCR.getColumnNumber();
innerCR = iRightCR;
}
// Did we find the equijoin between inner and outer
if (newTableNumber == outerTableNumber && newColumnNumber == outerColumnNumber) {
break;
}
}
// Did we find an equijoin on inner and outer
if (innerIndex != innerJCL.size()) {
// match found
// Put this predicate into the outer equivalence class
innerP.setEquivalenceClass(outerEC);
continue;
}
// No match, add new equijoin
// Build a new predicate
BinaryRelationalOperatorNode newEquals = new BinaryRelationalOperatorNode(BinaryRelationalOperatorNode.K_EQUALS, outerCR.getClone(), innerCR.getClone(), false, getContextManager());
newEquals.bindComparisonOperator();
// Create new predicate into CNF:
//
// AND
// / \
// newEquals true
//
ValueNode trueNode = new BooleanConstantNode(true, getContextManager());
AndNode newAnd = new AndNode(newEquals, trueNode, getContextManager());
newAnd.postBindFixup();
// Add a new predicate to both the equijoin clauses and this list
JBitSet tableMap = new JBitSet(numTables);
newAnd.categorize(tableMap, false);
Predicate newPred = new Predicate(newAnd, tableMap, getContextManager());
newPred.setEquivalenceClass(outerEC);
addPredicate(newPred);
/* Add the new predicate right after the outer position
* so that we can follow all of the predicates in equivalence
* classes before those that do not yet have equivalence classes.
*/
if (outerIndex != outerJCL.size() - 1) {
outerJCL.insertElementAt(newPred, outerIndex + 1);
} else {
outerJCL.addElement(newPred);
}
if (outerJCL != innerJCL) {
innerJCL.addElement(newPred);
} else {
// DERBY-4387: Avoid adding <t1>.a = <t1>.b twice to
// the same predicate list, so do nothing since we
// already added predicate to outerJCL above.
}
}
}
}
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class PredicateList method pushExpressionsIntoSelect.
/**
* Push all predicates, which can be pushed, into the underlying select.
* A predicate can be pushed into an underlying select if the source of
* every ColumnReference in the predicate is itself a ColumnReference.
*
* This is useful when attempting to push predicates into non-flattenable
* views or derived tables or into unions.
*
* @param select The underlying SelectNode.
* @param copyPredicate Whether to make a copy of the predicate
* before pushing
*
* @exception StandardException Thrown on error
*/
void pushExpressionsIntoSelect(SelectNode select, boolean copyPredicate) throws StandardException {
/* Walk list backwards since we can delete while
* traversing the list.
*/
for (int index = size() - 1; index >= 0; index--) {
Predicate predicate = elementAt(index);
CollectNodesVisitor<ColumnReference> getCRs = new CollectNodesVisitor<ColumnReference>(ColumnReference.class);
predicate.getAndNode().accept(getCRs);
List<ColumnReference> colRefs = getCRs.getList();
/* state doesn't become true until we find the 1st
* ColumnReference. (We probably will always find
* at least 1 CR, but just to be safe, ...)
*/
boolean state = colRefs.size() > 0;
if (state) {
for (ColumnReference ref : colRefs) {
if (!ref.pointsToColumnReference()) {
state = false;
break;
}
}
}
if (!state)
continue;
if (copyPredicate) {
// Copy this predicate and push this instead
AndNode andNode = predicate.getAndNode();
ValueNode leftOperand;
ColumnReference crNode;
BinaryRelationalOperatorNode opNode = null;
InListOperatorNode inNode = null;
if (andNode.getLeftOperand() instanceof BinaryRelationalOperatorNode) {
opNode = (BinaryRelationalOperatorNode) andNode.getLeftOperand();
// Investigate using invariant interface to check rightOperand
if (!(opNode.getLeftOperand() instanceof ColumnReference) || !isConstantOrParameterNode(opNode.getRightOperand()))
continue;
crNode = (ColumnReference) opNode.getLeftOperand();
} else if (andNode.getLeftOperand() instanceof InListOperatorNode) {
inNode = (InListOperatorNode) andNode.getLeftOperand();
if (!(inNode.getLeftOperand() instanceof ColumnReference)) {
continue;
} else if (!(inNode.getRightOperandList().isConstantExpression())) {
continue;
}
crNode = (ColumnReference) inNode.getLeftOperand();
} else {
continue;
}
// Remap this crNode to underlying column reference in the select, if possible.
ColumnReference newCRNode = select.findColumnReferenceInResult(crNode.getColumnName());
if (newCRNode == null)
continue;
// <column> <relop> <value> AND TRUE
if (andNode.getLeftOperand() instanceof BinaryRelationalOperatorNode) {
/* If the operator is a binary relational operator that was
* created for a probe predicate then we have to make a
* copy of the underlying IN-list as well, so that we can
* give it the correct left operand (i.e. the new Column
* Reference node). Then we pass that copy into the new
* relational operator node.
*/
inNode = opNode.getInListOp();
if (inNode != null) {
inNode = inNode.shallowCopy();
inNode.setLeftOperand(newCRNode);
}
BinaryRelationalOperatorNode newRelop = new BinaryRelationalOperatorNode(opNode.kind, newCRNode, opNode.getRightOperand(), inNode, opNode.getForQueryRewrite(), getContextManager());
newRelop.bindComparisonOperator();
leftOperand = newRelop;
} else {
InListOperatorNode newInNode = new InListOperatorNode(newCRNode, inNode.getRightOperandList(), getContextManager());
newInNode.setType(inNode.getTypeServices());
leftOperand = newInNode;
}
// Convert the predicate into CNF form:
//
// AND
// / \
// leftOperand true
//
ValueNode trueNode = new BooleanConstantNode(true, getContextManager());
AndNode newAnd = new AndNode(leftOperand, trueNode, getContextManager());
newAnd.postBindFixup();
JBitSet tableMap = new JBitSet(select.getReferencedTableMap().size());
// Use newly constructed predicate
predicate = new Predicate(newAnd, tableMap, getContextManager());
} else {
// keep the counters up to date when removing a predicate
if (predicate.isStartKey())
numberOfStartPredicates--;
if (predicate.isStopKey())
numberOfStopPredicates--;
if (predicate.isQualifier())
numberOfQualifiers--;
/* Clear all of the scan flags since they may be different
* due to the splitting of the list.
*/
predicate.clearScanFlags();
// Remove this predicate from the list
removeElementAt(index);
}
// Push it into the select
select.pushExpressionsIntoSelect(predicate);
}
}
use of org.apache.derby.iapi.util.JBitSet in project derby by apache.
the class OptimizerImpl method getNextPermutation.
/**
* @see Optimizer#getNextPermutation
*
* @exception StandardException Thrown on error
*/
public boolean getNextPermutation() throws StandardException {
/* Don't get any permutations if there is nothing to optimize */
if (numOptimizables < 1) {
if (tracingIsOn()) {
tracer().traceVacuous();
}
endOfRoundCleanup();
return false;
}
/* Make sure that all Optimizables init their access paths.
* (They wait until optimization since the access path
* references the optimizer.)
*/
optimizableList.initAccessPaths(this);
/*
** Experiments show that optimization time only starts to
** become a problem with seven tables, so only check for
** too much time if there are more than seven tables.
** Also, don't check for too much time if user has specified
** no timeout.
*/
if ((!timeExceeded) && (numTablesInQuery > 6) && (!noTimeout)) {
/*
** Stop optimizing if the time spent optimizing is greater than
** the current best cost.
*/
currentTime = System.currentTimeMillis();
timeExceeded = (currentTime - timeOptimizationStarted) > timeLimit;
if (tracingIsOn() && timeExceeded) {
tracer().traceTimeout(currentTime, bestCost);
}
}
if (bestCost.isUninitialized() && foundABestPlan && ((!usingPredsPushedFromAbove && !bestJoinOrderUsedPredsFromAbove) || timeExceeded)) {
/* We can get here if this OptimizerImpl is for a subquery
* that timed out for a previous permutation of the outer
* query, but then the outer query itself did _not_ timeout.
* In that case we'll end up back here for another round of
* optimization, but our timeExceeded flag will be true.
* We don't want to reset all of the timeout state here
* because that could lead to redundant work (see comments
* in prepForNextRound()), but we also don't want to return
* without having a plan, because then we'd return an unfairly
* high "bestCost" value--i.e. Double.MAX_VALUE. Note that
* we can't just revert back to whatever bestCost we had
* prior to this because that cost is for some previous
* permutation of the outer query--not the current permutation--
* and thus would be incorrect. So instead we have to delay
* the timeout until we find a complete (and valid) join order,
* so that we can return a valid cost estimate. Once we have
* a valid cost we'll then go through the timeout logic
* and stop optimizing.
*
* All of that said, instead of just trying the first possible
* join order, we jump to the join order that gave us the best
* cost in previous rounds. We know that such a join order exists
* because that's how our timeout value was set to begin with--so
* if there was no best join order, we never would have timed out
* and thus we wouldn't be here.
*
* We can also get here if we've already optimized the list
* of optimizables once (in a previous round of optimization)
* and now we're back to do it again. If that's true AND
* we did *not* receive any predicates pushed from above AND
* the bestJoinOrder from the previous round did *not* depend
* on predicates pushed from above, then we'll jump to the
* previous join order and start there. NOTE: if after jumping
* to the previous join order and calculating the cost we haven't
* timed out, we will continue looking at other join orders (as
* usual) until we exhaust them all or we time out.
*/
if (permuteState != JUMPING) {
// proceed with normal timeout logic.
if (firstLookOrder == null)
firstLookOrder = new int[numOptimizables];
System.arraycopy(bestJoinOrder, 0, firstLookOrder, 0, numOptimizables);
permuteState = JUMPING;
/* If we already assigned at least one position in the
* join order when this happened (i.e. if joinPosition
* is greater than *or equal* to zero; DERBY-1777), then
* reset the join order before jumping. The call to
* rewindJoinOrder() here will put joinPosition back
* to 0. But that said, we'll then end up incrementing
* joinPosition before we start looking for the next
* join order (see below), which means we need to set
* it to -1 here so that it gets incremented to "0" and
* then processing can continue as normal from there.
* Note: we don't need to set reloadBestPlan to true
* here because we only get here if we have *not* found
* a best plan yet.
*/
if (joinPosition >= 0) {
rewindJoinOrder();
joinPosition = -1;
}
}
// Reset the timeExceeded flag so that we'll keep going
// until we find a complete join order. NOTE: we intentionally
// do _not_ reset the timeOptimizationStarted value because we
// we want to go through this timeout logic for every
// permutation, to make sure we timeout as soon as we have
// our first complete join order.
timeExceeded = false;
}
/*
** Pick the next table in the join order, if there is an unused position
** in the join order, and the current plan is less expensive than
** the best plan so far, and the amount of time spent optimizing is
** still less than the cost of the best plan so far, and a best
** cost has been found in the current join position. Otherwise,
** just pick the next table in the current position.
*/
boolean joinPosAdvanced = false;
/* Determine if the current plan is still less expensive than
* the best plan so far. If bestCost is uninitialized then
* we want to return false here; if we didn't, then in the (rare)
* case where the current cost is greater than Double.MAX_VALUE
* (esp. if it's Double.POSITIVE_INFINITY, which can occur
* for very deeply nested queries with long FromLists) we would
* give up on the current plan even though we didn't have a
* best plan yet, which would be wrong. Also note: if we have
* a required row ordering then we might end up using the
* sort avoidance plan--but we don't know at this point
* which plan (sort avoidance or "normal") we're going to
* use, so we error on the side of caution and only short-
* circuit if both currentCost and currentSortAvoidanceCost
* (if the latter is applicable) are greater than bestCost.
*/
boolean alreadyCostsMore = !bestCost.isUninitialized() && (currentCost.compare(bestCost) > 0) && ((requiredRowOrdering == null) || (currentSortAvoidanceCost.compare(bestCost) > 0));
if ((joinPosition < (numOptimizables - 1)) && !alreadyCostsMore && (!timeExceeded)) {
/*
** Are we either starting at the first join position (in which
** case joinPosition will be -1), or has a best cost been found
** in the current join position? The latter case might not be
** true if there is no feasible join order.
*/
if ((joinPosition < 0) || optimizableList.getOptimizable(proposedJoinOrder[joinPosition]).getBestAccessPath().getCostEstimate() != null) {
joinPosition++;
joinPosAdvanced = true;
/*
** When adding a table to the join order, the best row
** row ordering for the outer tables becomes the starting
** point for the row ordering of the current table.
*/
bestRowOrdering.copy(currentRowOrdering);
}
} else {
// order.
if (joinPosition < (numOptimizables - 1)) {
if (tracingIsOn()) {
tracer().traceShortCircuiting(timeExceeded, optimizableList.getOptimizable(proposedJoinOrder[joinPosition]), joinPosition);
}
reloadBestPlan = true;
}
}
if (permuteState == JUMPING && !joinPosAdvanced && joinPosition >= 0) {
// not feeling well in the middle of jump
// Note: we have to make sure we reload the best plans
// as we rewind since they may have been clobbered
// (as part of the current join order) before we gave
// up on jumping.
reloadBestPlan = true;
// fall
rewindJoinOrder();
// give up
permuteState = NO_JUMP;
}
/*
** The join position becomes < 0 when all the permutations have been
** looked at.
*/
while (joinPosition >= 0) {
int nextOptimizable = proposedJoinOrder[joinPosition] + 1;
if (proposedJoinOrder[joinPosition] >= 0) {
/* We are either going to try another table at the current
* join order position, or we have exhausted all the tables
* at the current join order position. In either case, we
* need to pull the table at the current join order position
* and remove it from the join order. Do this BEFORE we
* search for the next optimizable so that assignedTableMap,
* which is updated to reflect the PULL, has the correct
* information for enforcing join order depdendencies.
* DERBY-3288.
*/
pullOptimizableFromJoinOrder();
}
if (desiredJoinOrderFound || timeExceeded) {
/*
** If the desired join order has been found (which will happen
** if the user specifies a join order), pretend that there are
** no more optimizables at this join position. This will cause
** us to back out of the current join order.
**
** Also, don't look at any more join orders if we have taken
** too much time with this optimization.
*/
nextOptimizable = numOptimizables;
} else if (// still jumping
permuteState == JUMPING) {
/* We're "jumping" to a join order that puts the optimizables
** with the lowest estimated costs first (insofar as it
** is legal to do so). The "firstLookOrder" array holds the
** ideal join order for position <joinPosition> up thru
** position <numOptimizables-1>. So here, we look at the
** ideal optimizable to place at <joinPosition> and see if
** it's legal; if it is, then we're done. Otherwise, we
** swap it with <numOptimizables-1> and see if that gives us
** a legal join order w.r.t <joinPosition>. If not, then we
** swap it with <numOptimizables-2> and check, and if that
** fails, then we swap it with <numOptimizables-3>, and so
** on. For example, assume we have 6 optimizables whose
** order from least expensive to most expensive is 2, 1, 4,
** 5, 3, 0. Assume also that we've already verified the
** legality of the first two positions--i.e. that joinPosition
** is now "2". That means that "firstLookOrder" currently
** contains the following:
**
** [ pos ] 0 1 2 3 4 5
** [ opt ] 2 1 4 5 3 0
**
** Then at this point, we do the following:
**
** -- Check to see if the ideal optimizable "4" is valid
** at its current position (2)
** -- If opt "4" is valid, then we're done; else we
** swap it with the value at position _5_:
**
** [ pos ] 0 1 2 3 4 5
** [ opt ] 2 1 0 5 3 4
**
** -- Check to see if optimizable "0" is valid at its
** new position (2).
** -- If opt "0" is valid, then we're done; else we
** put "0" back in its original position and swap
** the ideal optimizer ("4") with the value at
** position _4_:
**
** [ pos ] 0 1 2 3 4 5
** [ opt ] 2 1 3 5 4 0
**
** -- Check to see if optimizable "3" is valid at its
** new position (2).
** -- If opt "3" is valid, then we're done; else we
** put "3" back in its original position and swap
** the ideal optimizer ("4") with the value at
** position _3_:
**
** [ pos ] 0 1 2 3 4 5
** [ opt ] 2 1 5 4 3 0
**
** -- Check to see if optimizable "5" is valid at its
** new position (2).
** -- If opt "5" is valid, then we're done; else we've
** tried all the available optimizables and none
** of them are legal at position 2. In this case,
** we give up on "JUMPING" and fall back to normal
** join-order processing.
*/
int idealOptimizable = firstLookOrder[joinPosition];
nextOptimizable = idealOptimizable;
int lookPos = numOptimizables;
int lastSwappedOpt = -1;
Optimizable nextOpt;
for (nextOpt = optimizableList.getOptimizable(nextOptimizable); !(nextOpt.legalJoinOrder(assignedTableMap)); nextOpt = optimizableList.getOptimizable(nextOptimizable)) {
// Undo last swap, if we had one.
if (lastSwappedOpt >= 0) {
firstLookOrder[joinPosition] = idealOptimizable;
firstLookOrder[lookPos] = lastSwappedOpt;
}
if (lookPos > joinPosition + 1) {
// we still have other possibilities; get the next
// one by "swapping" it into the current position.
lastSwappedOpt = firstLookOrder[--lookPos];
firstLookOrder[joinPosition] = lastSwappedOpt;
firstLookOrder[lookPos] = idealOptimizable;
nextOptimizable = lastSwappedOpt;
} else {
// here.
if (joinPosition > 0) {
joinPosition--;
reloadBestPlan = true;
rewindJoinOrder();
}
permuteState = NO_JUMP;
break;
}
}
if (permuteState == NO_JUMP)
continue;
if (joinPosition == numOptimizables - 1) {
// we just set the final position within our
// "firstLookOrder" join order; now go ahead
// and search for the best join order, starting from
// the join order stored in "firstLookOrder". This
// is called walking "high" because we're searching
// the join orders that are at or "above" (after) the
// order found in firstLookOrder. Ex. if we had three
// optimizables and firstLookOrder was [1 2 0], then
// the "high" would be [1 2 0], [2 0 1] and [2 1 0];
// the "low" would be [0 1 2], [0 2 1], and [1 0 2].
// We walk the "high" first, then fall back and
// walk the "low".
permuteState = WALK_HIGH;
}
} else {
/* Find the next unused table at this join position */
for (; nextOptimizable < numOptimizables; nextOptimizable++) {
boolean found = false;
for (int posn = 0; posn < joinPosition; posn++) {
/*
** Is this optimizable already somewhere
** in the join order?
*/
if (proposedJoinOrder[posn] == nextOptimizable) {
found = true;
break;
}
}
/* No need to check the dependencies if the optimizable
* is already in the join order--because we should have
* checked its dependencies before putting it there.
*/
if (found) {
if (SanityManager.DEBUG) {
// Doesn't hurt to check in SANE mode, though...
if ((nextOptimizable < numOptimizables) && !joinOrderMeetsDependencies(nextOptimizable)) {
SanityManager.THROWASSERT("Found optimizable '" + nextOptimizable + "' in current join order even though " + "its dependencies were NOT satisfied.");
}
}
continue;
}
/* Check to make sure that all of the next optimizable's
* dependencies have been satisfied.
*/
if ((nextOptimizable < numOptimizables) && !joinOrderMeetsDependencies(nextOptimizable)) {
if (tracingIsOn()) {
tracer().traceSkippingJoinOrder(nextOptimizable, joinPosition, ArrayUtil.copy(proposedJoinOrder), (JBitSet) assignedTableMap.clone());
}
/*
** If this is a user specified join order then it is illegal.
*/
if (!optimizableList.optimizeJoinOrder()) {
if (tracingIsOn()) {
tracer().traceIllegalUserJoinOrder();
}
throw StandardException.newException(SQLState.LANG_ILLEGAL_FORCED_JOIN_ORDER);
}
continue;
}
break;
}
}
/* Have we exhausted all the optimizables at this join position? */
if (nextOptimizable >= numOptimizables) {
/*
** If we're not optimizing the join order, remember the first
** join order.
*/
if (!optimizableList.optimizeJoinOrder()) {
// Verify that the user specified a legal join order
if (!optimizableList.legalJoinOrder(numTablesInQuery)) {
if (tracingIsOn()) {
tracer().traceIllegalUserJoinOrder();
}
throw StandardException.newException(SQLState.LANG_ILLEGAL_FORCED_JOIN_ORDER);
}
if (tracingIsOn()) {
tracer().traceUserJoinOrderOptimized();
}
desiredJoinOrderFound = true;
}
if (permuteState == READY_TO_JUMP && joinPosition > 0 && joinPosition == numOptimizables - 1) {
permuteState = JUMPING;
/* A simple heuristics is that the row count we got
* indicates a potentially good join order. We'd like row
* count to get big as late as possible, so
* that less load is carried over.
*/
double[] rc = new double[numOptimizables];
for (int i = 0; i < numOptimizables; i++) {
firstLookOrder[i] = i;
CostEstimate ce = optimizableList.getOptimizable(i).getBestAccessPath().getCostEstimate();
if (ce == null) {
// come again?
permuteState = READY_TO_JUMP;
break;
}
rc[i] = ce.singleScanRowCount();
}
if (permuteState == JUMPING) {
boolean doIt = false;
int temp;
for (// simple selection sort
int i = 0; // simple selection sort
i < numOptimizables; // simple selection sort
i++) {
int k = i;
for (int j = i + 1; j < numOptimizables; j++) if (rc[j] < rc[k])
k = j;
if (k != i) {
// destroy the bridge
rc[k] = rc[i];
temp = firstLookOrder[i];
firstLookOrder[i] = firstLookOrder[k];
firstLookOrder[k] = temp;
doIt = true;
}
}
if (doIt) {
joinPosition--;
// jump from ground
rewindJoinOrder();
continue;
} else
// never
permuteState = NO_JUMP;
}
}
/*
** We have exhausted all the optimizables at this level.
** Go back up one level.
*/
/* Go back up one join position */
joinPosition--;
if (// reached peak
joinPosition < 0 && permuteState == WALK_HIGH) {
// reset, fall down the hill
joinPosition = 0;
permuteState = WALK_LOW;
}
continue;
}
/*
** We have found another optimizable to try at this join position.
*/
proposedJoinOrder[joinPosition] = nextOptimizable;
if (permuteState == WALK_LOW) {
boolean finishedCycle = true;
for (int i = 0; i < numOptimizables; i++) {
if (proposedJoinOrder[i] < firstLookOrder[i]) {
finishedCycle = false;
break;
} else if (// done
proposedJoinOrder[i] > firstLookOrder[i])
break;
}
if (finishedCycle) {
// We just set proposedJoinOrder[joinPosition] above, so
// if we're done we need to put it back to -1 to indicate
// that it's an empty slot. Then we rewind and pull any
// other Optimizables at positions < joinPosition.
// Note: we have to make sure we reload the best plans
// as we rewind since they may have been clobbered
// (as part of the current join order) before we got
// here.
proposedJoinOrder[joinPosition] = -1;
joinPosition--;
if (joinPosition >= 0) {
reloadBestPlan = true;
rewindJoinOrder();
joinPosition = -1;
}
permuteState = READY_TO_JUMP;
endOfRoundCleanup();
return false;
}
}
/* Re-init (clear out) the cost for the best access path
* when placing a table.
*/
optimizableList.getOptimizable(nextOptimizable).getBestAccessPath().setCostEstimate((CostEstimate) null);
if (tracingIsOn()) {
tracer().traceJoinOrderConsideration(joinPosition, ArrayUtil.copy(proposedJoinOrder), (JBitSet) assignedTableMap.clone());
}
Optimizable nextOpt = optimizableList.getOptimizable(nextOptimizable);
/* Update the assigned table map to include the newly-placed
* Optimizable in the current join order. Assumption is that
* this OR can always be undone using an XOR, which will only
* be true if none of the Optimizables have overlapping table
* maps. The XOR itself occurs as part of optimizable "PULL"
* processing.
*/
if (SanityManager.DEBUG) {
JBitSet optMap = (JBitSet) nextOpt.getReferencedTableMap().clone();
optMap.and(assignedTableMap);
if (optMap.getFirstSetBit() != -1) {
SanityManager.THROWASSERT("Found multiple optimizables that share one or " + "more referenced table numbers (esp: '" + optMap + "'), but that should not be the case.");
}
}
assignedTableMap.or(nextOpt.getReferencedTableMap());
nextOpt.startOptimizing(this, currentRowOrdering);
pushPredicates(optimizableList.getOptimizable(nextOptimizable), assignedTableMap);
return true;
}
endOfRoundCleanup();
return false;
}
Aggregations