Search in sources :

Example 1 with JBitSet

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;
}
Also used : OptimizablePredicateList(org.apache.derby.iapi.sql.compile.OptimizablePredicateList) JBitSet(org.apache.derby.iapi.util.JBitSet) OptimizablePredicate(org.apache.derby.iapi.sql.compile.OptimizablePredicate)

Example 2 with JBitSet

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);
        }
    }
}
Also used : OptimizablePredicate(org.apache.derby.iapi.sql.compile.OptimizablePredicate) OptimizablePredicateList(org.apache.derby.iapi.sql.compile.OptimizablePredicateList) JBitSet(org.apache.derby.iapi.util.JBitSet) DataValueDescriptor(org.apache.derby.iapi.types.DataValueDescriptor)

Example 3 with JBitSet

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.
                }
            }
        }
    }
}
Also used : OptimizablePredicateList(org.apache.derby.iapi.sql.compile.OptimizablePredicateList) JBitSet(org.apache.derby.iapi.util.JBitSet) ArrayList(java.util.ArrayList) OptimizablePredicate(org.apache.derby.iapi.sql.compile.OptimizablePredicate)

Example 4 with JBitSet

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);
    }
}
Also used : OptimizablePredicate(org.apache.derby.iapi.sql.compile.OptimizablePredicate) JBitSet(org.apache.derby.iapi.util.JBitSet)

Example 5 with JBitSet

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;
}
Also used : JBitSet(org.apache.derby.iapi.util.JBitSet) CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate) Optimizable(org.apache.derby.iapi.sql.compile.Optimizable)

Aggregations

JBitSet (org.apache.derby.iapi.util.JBitSet)39 OptimizablePredicate (org.apache.derby.iapi.sql.compile.OptimizablePredicate)12 OptimizablePredicateList (org.apache.derby.iapi.sql.compile.OptimizablePredicateList)7 Optimizable (org.apache.derby.iapi.sql.compile.Optimizable)2 ConglomerateDescriptor (org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor)2 ArrayList (java.util.ArrayList)1 HashMap (java.util.HashMap)1 IndexDescriptor (org.apache.derby.catalog.IndexDescriptor)1 ContextManager (org.apache.derby.iapi.services.context.ContextManager)1 FormatableBitSet (org.apache.derby.iapi.services.io.FormatableBitSet)1 CostEstimate (org.apache.derby.iapi.sql.compile.CostEstimate)1 DataValueDescriptor (org.apache.derby.iapi.types.DataValueDescriptor)1 StandardException (org.apache.derby.shared.common.error.StandardException)1