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 {
// we went through all of the available optimizables
// and none of them were legal in the current position;
// so we give up and fall back to normal processing.
// 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.
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 (optimizerTrace)
{
trace(SKIPPING_JOIN_ORDER, nextOptimizable, 0, 0.0, null);
}
/*
** If this is a user specified join order then it is illegal.
*/
if ( ! optimizableList.optimizeJoinOrder())
{
if (optimizerTrace)
{
trace(ILLEGAL_USER_JOIN_ORDER, 0, 0, 0.0, null);
}
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 (optimizerTrace)
{
trace(ILLEGAL_USER_JOIN_ORDER, 0, 0, 0.0, null);
}
throw StandardException.newException(SQLState.LANG_ILLEGAL_FORCED_JOIN_ORDER);
}
if (optimizerTrace)
{
trace(USER_JOIN_ORDER_OPTIMIZED, 0, 0, 0.0, null);
}
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)
{
permuteState = READY_TO_JUMP; //come again?
break;
}
rc[i] = ce.singleScanRowCount();
}
if (permuteState == JUMPING)
{
boolean doIt = false;
int temp;
for (int i = 0; i < numOptimizables; i++) //simple selection sort
{
int k = i;
for (int j = i+1; j < numOptimizables; j++)
if (rc[j] < rc[k]) k = j;
if (k != i)
{
rc[k] = rc[i]; //destroy the bridge
temp = firstLookOrder[i];
firstLookOrder[i] = firstLookOrder[k];
firstLookOrder[k] = temp;
doIt = true;
}
}
if (doIt)
{
joinPosition--;
rewindJoinOrder(); //jump from ground
continue;
}
else permuteState = NO_JUMP; //never
}
}
/*
** We have exhausted all the optimizables at this level.
** Go back up one level.
*/
/* Go back up one join position */
joinPosition--;
if (joinPosition < 0 && permuteState == WALK_HIGH) //reached peak
{
joinPosition = 0; //reset, fall down the hill
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 (proposedJoinOrder[i] > firstLookOrder[i]) //done
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 (optimizerTrace)
{
trace(CONSIDERING_JOIN_ORDER, 0, 0, 0.0, null);
}
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);