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;
}
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();
}
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);
}
}
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;
}
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();
}
Aggregations