*/
public boolean getNextDecoratedPermutation()
throws StandardException
{
boolean retval;
Optimizable curOpt =
optimizableList.getOptimizable(proposedJoinOrder[joinPosition]);
double originalRowCount = 0.0;
// RESOLVE: Should we step through the different join strategies here?
/* Returns true until all access paths are exhausted */
retval = curOpt.nextAccessPath(this,
(OptimizablePredicateList) null,
currentRowOrdering);
// If the previous path that we considered for curOpt was _not_ the best
// path for this round, then we need to revert back to whatever the
// best plan for curOpt was this round. Note that the cost estimate
// for bestAccessPath could be null here if the last path that we
// checked was the only one possible for this round.
if ((curOpt.getBestAccessPath().getCostEstimate() != null) &&
(curOpt.getCurrentAccessPath().getCostEstimate() != null))
{
// Note: we can't just check to see if bestCost is cheaper
// than currentCost because it's possible that currentCost
// is actually cheaper--but it may have been 'rejected' because
// it would have required too much memory. So we just check
// to see if bestCost and currentCost are different. If so
// then we know that the most recent access path (represented
// by "current" access path) was not the best.
if (curOpt.getBestAccessPath().getCostEstimate().compare(
curOpt.getCurrentAccessPath().getCostEstimate()) != 0)
{
curOpt.updateBestPlanMap(FromTable.LOAD_PLAN, curOpt);
}
else if (curOpt.getBestAccessPath().getCostEstimate().rowCount() <
curOpt.getCurrentAccessPath().getCostEstimate().rowCount())
{
// If currentCost and bestCost have the same cost estimate
// but currentCost has been rejected because of memory, we
// still need to revert the plans. In this case the row
// count for currentCost will be greater than the row count
// for bestCost, so that's what we just checked.
curOpt.updateBestPlanMap(FromTable.LOAD_PLAN, curOpt);
}
}
/* If we needed to revert plans for curOpt, we just did it above.
* So we no longer need to keep the previous best plan--and in fact,
* keeping it can lead to extreme memory usage for very large
* queries. So delete the stored plan for curOpt. DERBY-1315.
*/
curOpt.updateBestPlanMap(FromTable.REMOVE_PLAN, curOpt);
/*
** When all the access paths have been looked at, we know what the
** cheapest one is, so remember it. Only do this if a cost estimate
** has been set for the best access path - one will not have been
** set if no feasible plan has been found.
*/
CostEstimate ce = curOpt.getBestAccessPath().getCostEstimate();
if ( ( ! retval ) && (ce != null))
{
/*
** Add the cost of the current optimizable to the total cost.
** The total cost is the sum of all the costs, but the total
** number of rows is the number of rows returned by the innermost
** optimizable.
*/
currentCost.setCost(
currentCost.getEstimatedCost() + ce.getEstimatedCost(),
ce.rowCount(),
ce.singleScanRowCount());
if (curOpt.considerSortAvoidancePath() &&
requiredRowOrdering != null)
{
/* Add the cost for the sort avoidance path, if there is one */
ce = curOpt.getBestSortAvoidancePath().getCostEstimate();
currentSortAvoidanceCost.setCost(
currentSortAvoidanceCost.getEstimatedCost() +
ce.getEstimatedCost(),
ce.rowCount(),
ce.singleScanRowCount());
}
if (optimizerTrace)
{
trace(TOTAL_COST_NON_SA_PLAN, 0, 0, 0.0, null);
if (curOpt.considerSortAvoidancePath())
{
trace(TOTAL_COST_SA_PLAN, 0, 0, 0.0, null);
}
}
/* Do we have a complete join order? */
if ( joinPosition == (numOptimizables - 1) )
{
if (optimizerTrace)
{
trace(COMPLETE_JOIN_ORDER, 0, 0, 0.0, null);
}
/* Add cost of sorting to non-sort-avoidance cost */
if (requiredRowOrdering != null)
{
boolean gotSortCost = false;
/* Only get the sort cost once */
if (sortCost == null)
{
sortCost = newCostEstimate();
}
/* requiredRowOrdering records if the bestCost so far is
* sort-needed or not, as done in rememberBestCost. If
* the bestCost so far is sort-needed, and assume
* currentCost is also sort-needed, we want this comparison
* to be as accurate as possible. Different plans may
* produce different estimated row count (eg., heap scan
* vs. index scan during a join), sometimes the difference
* could be very big. However the actual row count should
* be only one value. So when comparing these two plans,
* we want them to have the same sort cost. We want to
* take the smaller row count, because a better estimation
* (eg. through index) would yield a smaller number. We
* adjust the bestCost here if it had a bigger rowCount
* estimate. The performance improvement of doing this
* sometimes is quite dramatic, eg. from 17 sec to 0.5 sec,
* see beetle 4353.
*/
else if (requiredRowOrdering.getSortNeeded())
{
if (bestCost.rowCount() > currentCost.rowCount())
{
// adjust bestCost
requiredRowOrdering.estimateCost(
bestCost.rowCount(),
bestRowOrdering,
sortCost
);
double oldSortCost = sortCost.getEstimatedCost();
requiredRowOrdering.estimateCost(
currentCost.rowCount(),
bestRowOrdering,
sortCost
);
gotSortCost = true;
bestCost.setCost(bestCost.getEstimatedCost() -
oldSortCost +
sortCost.getEstimatedCost(),
sortCost.rowCount(),
currentCost.singleScanRowCount());
}
else if (bestCost.rowCount() < currentCost.rowCount())
{
// adjust currentCost's rowCount
currentCost.setCost(currentCost.getEstimatedCost(),
bestCost.rowCount(),
currentCost.singleScanRowCount());
}
}
/* This does not figure out if sorting is necessary, just
* an asumption that sort is needed; if the assumption is
* wrong, we'll look at sort-avoidance cost as well, later
*/
if (! gotSortCost)
{
requiredRowOrdering.estimateCost(
currentCost.rowCount(),
bestRowOrdering,
sortCost
);
}
originalRowCount = currentCost.rowCount();
currentCost.setCost(currentCost.getEstimatedCost() +
sortCost.getEstimatedCost(),
sortCost.rowCount(),
currentCost.singleScanRowCount()
);
if (optimizerTrace)
{
trace(COST_OF_SORTING, 0, 0, 0.0, null);
trace(TOTAL_COST_WITH_SORTING, 0, 0, 0.0, null);
}
}
/*
** Is the cost of this join order lower than the best one we've
** found so far?
**
** NOTE: If the user has specified a join order, it will be the
** only join order the optimizer considers, so it is OK to use
** costing to decide that it is the "best" join order.
**
** For very deeply nested queries, it's possible that the optimizer
** will return an estimated cost of Double.INFINITY, which is
** greater than our uninitialized cost of Double.MAX_VALUE and
** thus the "compare" check below will return false. So we have
** to check to see if bestCost is uninitialized and, if so, we
** save currentCost regardless of what value it is--because we
** haven't found anything better yet.
**
** That said, it's also possible for bestCost to be infinity
** AND for current cost to be infinity, as well. In that case
** we can't really tell much by comparing the two, so for lack
** of better alternative we look at the row counts. See
** CostEstimateImpl.compare() for more.
*/
if ((! foundABestPlan) ||
(currentCost.compare(bestCost) < 0) ||
bestCost.isUninitialized())
{
rememberBestCost(currentCost, Optimizer.NORMAL_PLAN);
// Since we just remembered all of the best plans,
// no need to reload them when pulling Optimizables
// from this join order.
reloadBestPlan = false;
}
else
reloadBestPlan = true;
/* Subtract cost of sorting from non-sort-avoidance cost */
if (requiredRowOrdering != null)
{
/*
** The cost could go negative due to loss of precision.
*/
double newCost = currentCost.getEstimatedCost() -
sortCost.getEstimatedCost();
if (newCost < 0.0)
newCost = 0.0;
currentCost.setCost(newCost,
originalRowCount,
currentCost.singleScanRowCount()
);
}
/*
** This may be the best sort-avoidance plan if there is a
** required row ordering, and we are to consider a sort
** avoidance path on the last Optimizable in the join order.
*/
if (requiredRowOrdering != null &&
curOpt.considerSortAvoidancePath())
{
if (requiredRowOrdering.sortRequired(
bestRowOrdering, optimizableList) ==
RequiredRowOrdering.NOTHING_REQUIRED)
{