* state accordingly.
*/
private void pullOptimizableFromJoinOrder()
throws StandardException
{
Optimizable pullMe =
optimizableList.getOptimizable(proposedJoinOrder[joinPosition]);
/*
** Subtract the cost estimate of the optimizable being
** removed from the total cost estimate.
**
** 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.
*/
double prevRowCount;
double prevSingleScanRowCount;
int prevPosition = 0;
if (joinPosition == 0)
{
prevRowCount = outermostCostEstimate.rowCount();
prevSingleScanRowCount = outermostCostEstimate.singleScanRowCount();
}
else
{
prevPosition = proposedJoinOrder[joinPosition - 1];
CostEstimate localCE =
optimizableList.
getOptimizable(prevPosition).
getBestAccessPath().
getCostEstimate();
prevRowCount = localCE.rowCount();
prevSingleScanRowCount = localCE.singleScanRowCount();
}
/*
** If there is no feasible join order, the cost estimate
** in the best access path may never have been set.
** In this case, do not subtract anything from the
** current cost, since nothing was added to the current
** cost.
*/
double newCost = currentCost.getEstimatedCost();
double pullCost = 0.0;
CostEstimate pullCostEstimate =
pullMe.getBestAccessPath().getCostEstimate();
if (pullCostEstimate != null)
{
pullCost = pullCostEstimate.getEstimatedCost();
newCost -= pullCost;
/*
** It's possible for newCost to go negative here due to
** loss of precision--but that should ONLY happen if the
** optimizable we just pulled was at position 0. If we
** have a newCost that is <= 0 at any other time, then
** it's the result of a different kind of precision loss--
** namely, the estimated cost of pullMe was so large that
** we lost the precision of the accumulated cost as it
** existed prior to pullMe. Then when we subtracted
** pullMe's cost out, we ended up setting newCost to zero.
** That's an unfortunate side effect of optimizer cost
** estimates that grow too large. If that's what happened
** here,try to make some sense of things by adding up costs
** as they existed prior to pullMe...
*/
if (newCost <= 0.0)
{
if (joinPosition == 0)
newCost = 0.0;
else
newCost = recoverCostFromProposedJoinOrder(false);
}
}
/* If we are choosing a new outer table, then
* we rest the starting cost to the outermostCost.
* (Thus avoiding any problems with floating point
* accuracy and going negative.)
*/
if (joinPosition == 0)
{
if (outermostCostEstimate != null)
{
newCost = outermostCostEstimate.getEstimatedCost();
}
else
{
newCost = 0.0;
}
}
currentCost.setCost(
newCost,
prevRowCount,
prevSingleScanRowCount);
/*
** Subtract from the sort avoidance cost if there is a
** required row ordering.
**
** NOTE: It is not necessary here to check whether the
** best cost was ever set for the sort avoidance path,
** because it considerSortAvoidancePath() would not be
** set if there cost were not set.
*/
if (requiredRowOrdering != null)
{
if (pullMe.considerSortAvoidancePath())
{
AccessPath ap = pullMe.getBestSortAvoidancePath();
double prevEstimatedCost = 0.0d;
/*
** Subtract the sort avoidance cost estimate of the
** optimizable being removed from the total sort
** avoidance cost estimate.
**
** 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.
*/
if (joinPosition == 0)
{
prevRowCount = outermostCostEstimate.rowCount();
prevSingleScanRowCount =
outermostCostEstimate.singleScanRowCount();
/* If we are choosing a new outer table, then
* we rest the starting cost to the outermostCost.
* (Thus avoiding any problems with floating point
* accuracy and going negative.)
*/
prevEstimatedCost =
outermostCostEstimate.getEstimatedCost();
}
else
{
CostEstimate localCE =
optimizableList.
getOptimizable(prevPosition).
getBestSortAvoidancePath().
getCostEstimate();
prevRowCount = localCE.rowCount();
prevSingleScanRowCount = localCE.singleScanRowCount();
prevEstimatedCost =
currentSortAvoidanceCost.getEstimatedCost() -
ap.getCostEstimate().getEstimatedCost();
}
// See discussion above for "newCost"; same applies here.
if (prevEstimatedCost <= 0.0)
{
if (joinPosition == 0)
prevEstimatedCost = 0.0;
else
{
prevEstimatedCost =
recoverCostFromProposedJoinOrder(true);
}
}
currentSortAvoidanceCost.setCost(
prevEstimatedCost,
prevRowCount,
prevSingleScanRowCount);
/*
** Remove the table from the best row ordering.
** It should not be necessary to remove it from
** the current row ordering, because it is
** maintained as we step through the access paths
** for the current Optimizable.
*/
bestRowOrdering.removeOptimizable(pullMe.getTableNumber());
/*
** When removing a table from the join order,
** the best row ordering for the remaining outer tables
** becomes the starting point for the row ordering of
** the current table.
*/
bestRowOrdering.copy(currentRowOrdering);
}
}
/*
** Pull the predicates at from the optimizable and put
** them back in the predicate list.
**
** NOTE: This is a little inefficient because it pulls the
** single-table predicates, which are guaranteed to always
** be pushed to the same optimizable. We could make this
** leave the single-table predicates where they are.
*/
pullMe.pullOptPredicates(predicateList);
/*
** When we pull an Optimizable we need to go through and
** load whatever best path we found for that Optimizable
** with respect to this OptimizerImpl. The reason is that
** we could be pulling the Optimizable for the last time
** (before returning false), in which case we want it (the
** Optimizable) to be holding the best access path that it
** had at the time we found bestJoinOrder. This ensures
** that the access path which is generated and executed for
** the Optimizable matches the the access path decisions
** made by this OptimizerImpl for the best join order.
**
** NOTE: We we only reload the best plan if it's necessary
** to do so--i.e. if the best plans aren't already loaded.
** The plans will already be loaded if the last complete
** join order we had was the best one so far, because that
** means we called "rememberAsBest" on every Optimizable
** in the list and, as part of that call, we will run through
** and set trulyTheBestAccessPath for the entire subtree.
** So if we haven't tried any other plans since then,
** we know that every Optimizable (and its subtree) already
** has the correct best plan loaded in its trulyTheBest
** path field. It's good to skip the load in this case
** because 'reloading best plans' involves walking the
** entire subtree of _every_ Optimizable in the list, which
** can be expensive if there are deeply nested subqueries.
*/
if (reloadBestPlan)
pullMe.updateBestPlanMap(FromTable.LOAD_PLAN, this);
/* Mark current join position as unused */
proposedJoinOrder[joinPosition] = -1;
/* If we didn't advance the join position then the optimizable
* which currently sits at proposedJoinOrder[joinPosition]--call
* it PULL_ME--is *not* going to remain there. Instead, we're
* going to pull that optimizable from its position and attempt
* to put another one in its place. That said, when we try to
* figure out which of the other optimizables to place at
* joinPosition, we'll first do some "dependency checking", the
* result of which relies on the contents of assignedTableMap.
* Since assignedTableMap currently holds info about PULL_ME
* and since PULL_ME is *not* going to remain in the join order,
* we need to remove the info for PULL_ME from assignedTableMap.
* Otherwise an Optimizable which depends on PULL_ME could
* incorrectly be placed in the join order *before* PULL_ME,
* which would violate the dependency and lead to incorrect
* results. DERBY-3288.
*/
assignedTableMap.xor(pullMe.getReferencedTableMap());
}