Index: java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java =================================================================== --- java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java (revision 419922) +++ java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java (working copy) @@ -452,9 +452,30 @@ ** just pick the next table in the current position. */ boolean joinPosAdvanced = false; + + /* Determine if the current plan is still less expensive than + * the best plan so far. If bestCost is uninitialized then + * we want to return false here; if we didn't, then in the (rare) + * case where the current cost is greater than Double.MAX_VALUE + * (esp. if it's Double.POSITIVE_INFINITY, which can occur + * for very deeply nested queries with long FromLists) we would + * give up on the current plan even though we didn't have a + * best plan yet, which would be wrong. Also note: if we have + * a required row ordering then we might end up using the + * sort avoidance plan--but we don't know at this point + * which plan (sort avoidance or "normal") we're going to + * use, so we error on the side of caution and only short- + * circuit if both currentCost and currentSortAvoidanceCost + * (if the latter is applicable) are greater than bestCost. + */ + boolean alreadyCostsMore = + !bestCost.isUninitialized() && + (currentCost.compare(bestCost) > 0) && + ((requiredRowOrdering == null) || + (currentSortAvoidanceCost.compare(bestCost) > 0)); + if ((joinPosition < (numOptimizables - 1)) && - ((currentCost.compare(bestCost) < 0) || - (currentSortAvoidanceCost.compare(bestCost) < 0)) && + !alreadyCostsMore && ( ! timeExceeded ) ) { @@ -480,16 +501,29 @@ bestRowOrdering.copy(currentRowOrdering); } } - else if (optimizerTrace) + else { - /* - ** Not considered short-circuiting if all slots in join - ** order are taken. - */ + if (optimizerTrace) + { + /* + ** Not considered short-circuiting if all slots in join + ** order are taken. + */ + if (joinPosition < (numOptimizables - 1)) + { + trace(SHORT_CIRCUITING, 0, 0, 0.0, null); + } + } + + // If we short-circuited the current join order then we need + // to make sure that, when we start pulling optimizables to find + // a new join order, we reload the best plans for those + // optimizables as we pull them. Otherwise we could end up + // generating a plan for an optimizable even though that plan + // was part of a short-circuited (and thus rejected) join + // order. if (joinPosition < (numOptimizables - 1)) - { - trace(SHORT_CIRCUITING, 0, 0, 0.0, null); - } + reloadBestPlan = true; } if (permuteState == JUMPING && !joinPosAdvanced && joinPosition >= 0) Index: java/testing/org/apache/derbyTesting/functionTests/master/predicatePushdown.out =================================================================== --- java/testing/org/apache/derbyTesting/functionTests/master/predicatePushdown.out (revision 419922) +++ java/testing/org/apache/derbyTesting/functionTests/master/predicatePushdown.out (working copy) @@ -2246,15 +2246,15 @@ null stop position: null qualifiers: Column[0][0] Id: 0 +Operator: <= +Ordered nulls: false +Unknown return value: false +Negate comparison result: false +Column[0][1] Id: 0 Operator: < Ordered nulls: false Unknown return value: true Negate comparison result: true -Column[0][1] Id: 0 -Operator: <= -Ordered nulls: false -Unknown return value: false -Negate comparison result: false Right result set: Project-Restrict ResultSet (8): Number of opens = 1 @@ -2290,15 +2290,15 @@ null stop position: null qualifiers: Column[0][0] Id: 0 +Operator: <= +Ordered nulls: false +Unknown return value: false +Negate comparison result: false +Column[0][1] Id: 0 Operator: < Ordered nulls: false Unknown return value: true Negate comparison result: true -Column[0][1] Id: 0 -Operator: <= -Ordered nulls: false -Unknown return value: false -Negate comparison result: false Right result set: Sort ResultSet: Number of opens = 4