Search in sources :

Example 11 with CostEstimate

use of org.apache.derby.iapi.sql.compile.CostEstimate in project derby by apache.

the class GroupByNode method optimizeIt.

/*
	 *  Optimizable interface
	 */
/**
 * @see Optimizable#optimizeIt
 *
 * @exception StandardException		Thrown on error
 */
@Override
public CostEstimate optimizeIt(Optimizer optimizer, OptimizablePredicateList predList, CostEstimate outerCost, RowOrdering rowOrdering) throws StandardException {
    // RESOLVE: NEED TO FACTOR IN THE COST OF GROUPING (SORTING) HERE
    ((Optimizable) childResult).optimizeIt(optimizer, predList, outerCost, rowOrdering);
    CostEstimate retval = super.optimizeIt(optimizer, predList, outerCost, rowOrdering);
    return retval;
}
Also used : CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate) Optimizable(org.apache.derby.iapi.sql.compile.Optimizable)

Example 12 with CostEstimate

use of org.apache.derby.iapi.sql.compile.CostEstimate in project derby by apache.

the class ProjectRestrictNode method optimizeIt.

/**
 * @see Optimizable#optimizeIt
 *
 * @exception StandardException		Thrown on error
 */
@Override
public CostEstimate optimizeIt(Optimizer optimizer, OptimizablePredicateList predList, CostEstimate outerCost, RowOrdering rowOrdering) throws StandardException {
    /*
		** RESOLVE: Most types of Optimizables only implement estimateCost(),
		** and leave it up to optimizeIt() in FromTable to figure out the
		** total cost of the join.  A ProjectRestrict can have a non-Optimizable
		** child, though, in which case we want to tell the child the
		** number of outer rows - it could affect the join strategy
		** significantly.  So we implement optimizeIt() here, which overrides
		** the optimizeIt() in FromTable.  This assumes that the join strategy
		** for which this join node is the inner table is a nested loop join,
		** which will not be a valid assumption when we implement other
		** strategies like materialization (hash join can work only on
		** base tables).  The join strategy for a base table under a
		** ProjectRestrict is set in the base table itself.
		*/
    CostEstimate childCost;
    setCostEstimate(getCostEstimate(optimizer));
    /*
		** Don't re-optimize a child result set that has already been fully
		** optimized.  For example, if the child result set is a SelectNode,
		** it will be changed to a ProjectRestrictNode, which we don't want
		** to re-optimized.
		*/
    // NOTE: TO GET THE RIGHT COST, THE CHILD RESULT MAY HAVE TO BE
    // OPTIMIZED MORE THAN ONCE, BECAUSE THE NUMBER OF OUTER ROWS
    // MAY BE DIFFERENT EACH TIME.
    // if (childResultOptimized)
    // return costEstimate;
    // It's possible that a call to optimize the left/right will cause
    // a new "truly the best" plan to be stored in the underlying base
    // tables.  If that happens and then we decide to skip that plan
    // (which we might do if the call to "considerCost()" below decides
    // the current path is infeasible or not the best) we need to be
    // able to revert back to the "truly the best" plans that we had
    // saved before we got here.  So with this next call we save the
    // current plans using "this" node as the key.  If needed, we'll
    // then make the call to revert the plans in OptimizerImpl's
    // getNextDecoratedPermutation() method.
    updateBestPlanMap(ADD_PLAN, this);
    /* If the childResult is instanceof Optimizable, then we optimizeIt.
		 * Otherwise, we are going into a new query block.  If the new query
		 * block has already had its access path modified, then there is
		 * nothing to do.  Otherwise, we must begin the optimization process
		 * anew on the new query block.
		 */
    if (childResult instanceof Optimizable) {
        childCost = ((Optimizable) childResult).optimizeIt(optimizer, restrictionList, outerCost, rowOrdering);
        /* Copy child cost to this node's cost */
        getCostEstimate().setCost(childCost.getEstimatedCost(), childCost.rowCount(), childCost.singleScanRowCount());
    // Note: we don't call "optimizer.considerCost()" here because
    // a) the child will make that call as part of its own
    // "optimizeIt()" work above, and b) the child might have
    // different criteria for "considering" (i.e. rejecting or
    // accepting) a plan's cost than this ProjectRestrictNode does--
    // and we don't want to override the child's decision.  So as
    // with most operations in this class, if the child is an
    // Optimizable, we just let it do its own work and make its
    // own decisions.
    } else if (!accessPathModified) {
        if (SanityManager.DEBUG) {
            if (!((childResult instanceof SelectNode) || (childResult instanceof RowResultSetNode))) {
                SanityManager.THROWASSERT("childResult is expected to be instanceof " + "SelectNode or RowResultSetNode - it is a " + childResult.getClass().getName());
            }
        }
        childResult = childResult.optimize(optimizer.getDataDictionary(), restrictionList, outerCost.rowCount());
        /* Copy child cost to this node's cost */
        childCost = childResult.getCostEstimate();
        getCostEstimate().setCost(childCost.getEstimatedCost(), childCost.rowCount(), childCost.singleScanRowCount());
        /* Note: Prior to the fix for DERBY-781 we had calls here
			 * to set the cost estimate for BestAccessPath and
			 * BestSortAvoidancePath to equal costEstimate.  That used
			 * to be okay because prior to DERBY-781 we would only
			 * get here once (per join order) for a given SelectNode/
			 * RowResultSetNode and thus we could safely say that the
			 * costEstimate from the most recent call to "optimize()"
			 * was the best one so far (because we knew that we would
			 * only call childResult.optimize() once).  Now that we
			 * support hash joins with subqueries, though, we can get
			 * here twice per join order: once when the optimizer is
			 * considering a nested loop join with this PRN, and once
			 * when it is considering a hash join.  This means we can't
			 * just arbitrarily use the cost estimate for the most recent
			 * "optimize()" as the best cost because that may not
			 * be accurate--it's possible that the above call to
			 * childResult.optimize() was for a hash join, but that
			 * we were here once before (namely for nested loop) and
			 * the cost of the nested loop is actually less than
			 * the cost of the hash join.  In that case it would
			 * be wrong to use costEstimate as the cost of the "best"
			 * paths because it (costEstimate) holds the cost of
			 * the hash join, not of the nested loop join.  So with
			 * DERBY-781 the following calls were removed:
			 *   getBestAccessPath().setCostEstimate(costEstimate);
			 *   getBestSortAvoidancePath().setCostEstimate(costEstimate);
			 * If costEstimate *does* actually hold the estimate for
			 * the best path so far, then we will set BestAccessPath
			 * and BestSortAvoidancePath as needed in the following
			 * call to "considerCost".
			 */
        // childResultOptimized = true;
        /* RESOLVE - ARBITRARYHASHJOIN - Passing restriction list here, as above, is correct.
			 * However,  passing predList makes the following work:
			 *	select * from t1, (select * from t2) c properties joinStrategy = hash where t1.c1 = c.c1;
			 * The following works with restrictionList:
			 *	select * from t1, (select c1 + 0 from t2) c(c1) properties joinStrategy = hash where t1.c1 = c.c1;
			 */
        optimizer.considerCost(this, restrictionList, getCostEstimate(), outerCost);
    }
    return getCostEstimate();
}
Also used : CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate) Optimizable(org.apache.derby.iapi.sql.compile.Optimizable)

Example 13 with CostEstimate

use of org.apache.derby.iapi.sql.compile.CostEstimate in project derby by apache.

the class SubqueryNode method generateExpression.

/**
 * Do code generation for this subquery.
 *
 * @param expressionBuilder	The ExpressionClassBuilder for the class being built
 * @param mbex	The method the expression will go into
 *
 * @exception StandardException		Thrown on error
 */
@Override
void generateExpression(ExpressionClassBuilder expressionBuilder, MethodBuilder mbex) throws StandardException {
    CompilerContext cc = getCompilerContext();
    String resultSetString;
    if (SanityManager.DEBUG) {
        SanityManager.ASSERT(expressionBuilder instanceof ActivationClassBuilder, "Expecting an ActivationClassBuilder");
    }
    ActivationClassBuilder acb = (ActivationClassBuilder) expressionBuilder;
    /* Generate the appropriate (Any or Once) ResultSet */
    if (subqueryType == EXPRESSION_SUBQUERY) {
        resultSetString = "getOnceResultSet";
    } else {
        resultSetString = "getAnyResultSet";
    }
    // Get cost estimate for underlying subquery
    CostEstimate costEstimate = resultSet.getFinalCostEstimate();
    /* Generate a new method.  It's only used within the other
		 * exprFuns, so it could be private. but since we don't
		 * generate the right bytecodes to invoke private methods,
		 * we just make it protected.  This generated class won't
		 * have any subclasses, certainly! (nat 12/97)
		 */
    String subqueryTypeString = getTypeCompiler().interfaceName();
    MethodBuilder mb = acb.newGeneratedFun(subqueryTypeString, Modifier.PROTECTED);
    /* Declare the field to hold the suquery's ResultSet tree */
    LocalField rsFieldLF = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.NoPutResultSet);
    ResultSetNode subNode = null;
    if (!isMaterializable()) {
        MethodBuilder executeMB = acb.getExecuteMethod();
        if (pushedNewPredicate && (!hasCorrelatedCRs())) {
            /* We try to materialize the subquery if it can fit in the memory.  We
				 * evaluate the subquery first.  If the result set fits in the memory,
				 * we replace the resultset with in-memory unions of row result sets.
				 * We do this trick by replacing the child result with a new node --
				 * MaterializeSubqueryNode, which essentially generates the suitable
				 * code to materialize the subquery if possible.  This may have big
				 * performance improvement.  See beetle 4373.
				 */
            if (SanityManager.DEBUG) {
                SanityManager.ASSERT(resultSet instanceof ProjectRestrictNode, "resultSet expected to be a ProjectRestrictNode!");
            }
            subNode = ((ProjectRestrictNode) resultSet).getChildResult();
            LocalField subRS = acb.newFieldDeclaration(Modifier.PRIVATE, ClassName.NoPutResultSet);
            mb.getField(subRS);
            mb.conditionalIfNull();
            ResultSetNode materialSubNode = new MaterializeSubqueryNode(subRS, getContextManager());
            // Propagate the resultSet's cost estimate to the new node.
            materialSubNode.setCostEstimate(resultSet.getFinalCostEstimate());
            ((ProjectRestrictNode) resultSet).setChildResult(materialSubNode);
            /* Evaluate subquery resultset here first.  Next time when we come to
				 * this subquery it may be replaced by a bunch of unions of rows.
				 */
            subNode.generate(acb, mb);
            mb.startElseCode();
            mb.getField(subRS);
            mb.completeConditional();
            mb.setField(subRS);
            executeMB.pushNull(ClassName.NoPutResultSet);
            executeMB.setField(subRS);
        }
        executeMB.pushNull(ClassName.NoPutResultSet);
        executeMB.setField(rsFieldLF);
        // now we fill in the body of the conditional
        mb.getField(rsFieldLF);
        mb.conditionalIfNull();
    }
    acb.pushGetResultSetFactoryExpression(mb);
    // start of args
    int nargs;
    /* Inside here is where subquery could already have been materialized. 4373
		 */
    resultSet.generate(acb, mb);
    /* Get the next ResultSet #, so that we can number the subquery's 
		 * empty row ResultColumnList and Once/Any ResultSet.
		 */
    int subqResultSetNumber = cc.getNextResultSetNumber();
    /* We will be reusing the RCL from the subquery's ResultSet for the 
		 * empty row function.  We need to reset the resultSetNumber in the
		 * RCL, before we generate that function.  Now that we've called
		 * generate() on the subquery's ResultSet, we can reset that
		 * resultSetNumber.
		 */
    resultSet.getResultColumns().setResultSetNumber(subqResultSetNumber);
    /* Generate code for empty row */
    resultSet.getResultColumns().generateNulls(acb, mb);
    /*
		 *	arg1: suqueryExpress - Expression for subquery's
		 *		  ResultSet
		 *  arg2: Activation
		 *  arg3: Method to generate Row with null(s) if subquery
		 *		  Result Set is empty
		 */
    if (subqueryType == EXPRESSION_SUBQUERY) {
        int cardinalityCheck;
        /* No need to do sort if subquery began life as a distinct expression subquery.
			 * (We simply check for a single unique value at execution time.)
			 * No need for cardinality check if we know that underlying
			 * ResultSet can contain at most 1 row.
			 * RESOLVE - Not necessary if we know we
			 * are getting a single row because of a unique index.
			 */
        if (distinctExpression) {
            cardinalityCheck = OnceResultSet.UNIQUE_CARDINALITY_CHECK;
        } else if (resultSet.returnsAtMostOneRow()) {
            cardinalityCheck = OnceResultSet.NO_CARDINALITY_CHECK;
        } else {
            cardinalityCheck = OnceResultSet.DO_CARDINALITY_CHECK;
        }
        /*  arg4: int - whether or not cardinality check is required
			 *				DO_CARDINALITY_CHECK - required
			 *				NO_CARDINALITY_CHECK - not required
			 *				UNIQUE_CARDINALITY_CHECK - verify single
			 *											unique value
			 */
        mb.push(cardinalityCheck);
        nargs = 8;
    } else {
        nargs = 7;
    }
    mb.push(subqResultSetNumber);
    mb.push(subqueryNumber);
    mb.push(pointOfAttachment);
    mb.push(costEstimate.rowCount());
    mb.push(costEstimate.getEstimatedCost());
    mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, resultSetString, ClassName.NoPutResultSet, nargs);
    if (!isMaterializable()) {
        /* put it back
			 */
        if (pushedNewPredicate && (!hasCorrelatedCRs()))
            ((ProjectRestrictNode) resultSet).setChildResult(subNode);
        // now we fill in the body of the conditional
        mb.startElseCode();
        mb.getField(rsFieldLF);
        mb.completeConditional();
    }
    mb.setField(rsFieldLF);
    /* rs.openCore() */
    mb.getField(rsFieldLF);
    mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "openCore", "void", 0);
    /* r = rs.next() */
    mb.getField(rsFieldLF);
    mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getNextRowCore", ClassName.ExecRow, 0);
    // mb.putVariable(rVar);
    // mb.endStatement();
    /* col = (<Datatype interface>) r.getColumn(1) */
    // mb.getVariable(rVar);
    // both the Row interface and columnId are 1-based
    mb.push(1);
    mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "getColumn", ClassName.DataValueDescriptor, 1);
    mb.cast(subqueryTypeString);
    /* Only generate the close() method for materialized
		 * subqueries.  All others will be closed when the
		 * close() method is called on the top ResultSet.
		 */
    if (isMaterializable()) {
        /* rs.close() */
        mb.getField(rsFieldLF);
        mb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.ResultSet, "close", "void", 0);
    }
    /* return col */
    // mb.getVariable(colVar);
    mb.methodReturn();
    mb.complete();
    /*
		** If we have an expression subquery, then we
		** can materialize it if it has no correlated
		** column references and is invariant.
		*/
    if (isMaterializable()) {
        LocalField lf = generateMaterialization(acb, mb, subqueryTypeString);
        mbex.getField(lf);
    } else {
        /* Generate the call to the new method */
        mbex.pushThis();
        mbex.callMethod(VMOpcode.INVOKEVIRTUAL, (String) null, mb.getName(), subqueryTypeString, 0);
    }
}
Also used : CompilerContext(org.apache.derby.iapi.sql.compile.CompilerContext) CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate) MethodBuilder(org.apache.derby.iapi.services.compiler.MethodBuilder) LocalField(org.apache.derby.iapi.services.compiler.LocalField)

Example 14 with CostEstimate

use of org.apache.derby.iapi.sql.compile.CostEstimate in project derby by apache.

the class UnionNode method optimizeIt.

/*
	 *  Optimizable interface
	 */
/**
 * @see org.apache.derby.iapi.sql.compile.Optimizable#optimizeIt
 *
 * @exception StandardException		Thrown on error
 */
@Override
public CostEstimate optimizeIt(Optimizer optimizer, OptimizablePredicateList predList, CostEstimate outerCost, RowOrdering rowOrdering) throws StandardException {
    // for the hash join.
    if ((predList != null) && !getCurrentAccessPath().getJoinStrategy().isHashJoin()) {
        for (int i = predList.size() - 1; i >= 0; i--) {
            if (pushOptPredicate(predList.getOptPredicate(i)))
                predList.removeOptPredicate(i);
        }
    }
    // It's possible that a call to optimize the left/right will cause
    // a new "truly the best" plan to be stored in the underlying base
    // tables.  If that happens and then we decide to skip that plan
    // (which we might do if the call to "considerCost()" below decides
    // the current path is infeasible or not the best) we need to be
    // able to revert back to the "truly the best" plans that we had
    // saved before we got here.  So with this next call we save the
    // current plans using "this" node as the key.  If needed, we'll
    // then make the call to revert the plans in OptimizerImpl's
    // getNextDecoratedPermutation() method.
    updateBestPlanMap(ADD_PLAN, this);
    leftResultSet = optimizeSource(optimizer, leftResultSet, getLeftOptPredicateList(), outerCost);
    rightResultSet = optimizeSource(optimizer, rightResultSet, getRightOptPredicateList(), outerCost);
    CostEstimate costEst = getCostEstimate(optimizer);
    /* The cost is the sum of the two child costs */
    costEst.setCost(leftResultSet.getCostEstimate().getEstimatedCost(), leftResultSet.getCostEstimate().rowCount(), leftResultSet.getCostEstimate().singleScanRowCount() + rightResultSet.getCostEstimate().singleScanRowCount());
    costEst.add(rightResultSet.getCostEstimate(), costEst);
    /*
		** Get the cost of this result set in the context of the whole plan.
		*/
    getCurrentAccessPath().getJoinStrategy().estimateCost(this, predList, (ConglomerateDescriptor) null, outerCost, optimizer, costEst);
    optimizer.considerCost(this, predList, costEst, outerCost);
    return costEst;
}
Also used : CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate)

Example 15 with CostEstimate

use of org.apache.derby.iapi.sql.compile.CostEstimate in project derby by apache.

the class UnionNode method getFinalCostEstimate.

/**
 * @see ResultSetNode#getFinalCostEstimate
 *
 * Get the final CostEstimate for this UnionNode.
 *
 * @return	The final CostEstimate for this UnionNode, which is
 *  the sum of the two child costs.
 */
@Override
CostEstimate getFinalCostEstimate() throws StandardException {
    // If we already found it, just return it.
    if (getCandidateFinalCostEstimate() != null) {
        return getCandidateFinalCostEstimate();
    }
    CostEstimate leftCE = leftResultSet.getFinalCostEstimate();
    CostEstimate rightCE = rightResultSet.getFinalCostEstimate();
    setCandidateFinalCostEstimate(getNewCostEstimate());
    getCandidateFinalCostEstimate().setCost(leftCE.getEstimatedCost(), leftCE.rowCount(), leftCE.singleScanRowCount() + rightCE.singleScanRowCount());
    getCandidateFinalCostEstimate().add(rightCE, getCandidateFinalCostEstimate());
    return getCandidateFinalCostEstimate();
}
Also used : CostEstimate(org.apache.derby.iapi.sql.compile.CostEstimate)

Aggregations

CostEstimate (org.apache.derby.iapi.sql.compile.CostEstimate)25 Optimizable (org.apache.derby.iapi.sql.compile.Optimizable)9 AccessPath (org.apache.derby.iapi.sql.compile.AccessPath)5 OptimizablePredicateList (org.apache.derby.iapi.sql.compile.OptimizablePredicateList)3 CompilerContext (org.apache.derby.iapi.sql.compile.CompilerContext)2 ConglomerateDescriptor (org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor)2 LocalField (org.apache.derby.iapi.services.compiler.LocalField)1 MethodBuilder (org.apache.derby.iapi.services.compiler.MethodBuilder)1 FormatableArrayHolder (org.apache.derby.iapi.services.io.FormatableArrayHolder)1 FormatableBitSet (org.apache.derby.iapi.services.io.FormatableBitSet)1 FormatableIntHolder (org.apache.derby.iapi.services.io.FormatableIntHolder)1 JoinStrategy (org.apache.derby.iapi.sql.compile.JoinStrategy)1 OptimizablePredicate (org.apache.derby.iapi.sql.compile.OptimizablePredicate)1 OptimizerPlan (org.apache.derby.iapi.sql.compile.OptimizerPlan)1 IndexRowGenerator (org.apache.derby.iapi.sql.dictionary.IndexRowGenerator)1 StaticCompiledOpenConglomInfo (org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo)1 StoreCostController (org.apache.derby.iapi.store.access.StoreCostController)1 DataValueDescriptor (org.apache.derby.iapi.types.DataValueDescriptor)1 JBitSet (org.apache.derby.iapi.util.JBitSet)1