You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by ba...@apache.org on 2006/04/12 22:24:37 UTC
svn commit: r393593 [1/3] - in /db/derby/code/trunk/java:
engine/org/apache/derby/iapi/sql/compile/
engine/org/apache/derby/impl/sql/compile/
testing/org/apache/derbyTesting/functionTests/master/
testing/org/apache/derbyTesting/functionTests/tests/lang/
Author: bandaram
Date: Wed Apr 12 13:24:27 2006
New Revision: 393593
URL: http://svn.apache.org/viewcvs?rev=393593&view=rev
Log:
DERBY-805: Apply phase 4 changes to implement join-predicate pushdown. See attached write up in JIRA for details. Changes have been extensively documented there.
Submitted by Army Brown (qozinx@gmail.com)
Modified:
db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/Optimizable.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/BaseTableNumbersVisitor.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromTable.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java
db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/master/predicatePushdown.out
db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/predicatePushdown.sql
Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/Optimizable.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/Optimizable.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/Optimizable.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/compile/Optimizable.java Wed Apr 12 13:24:27 2006
@@ -237,26 +237,31 @@
* question, but in cases where there are nested subqueries, there will be
* one OptimizerImpl for every level of nesting, and each OptimizerImpl
* might have its own idea of what this Optimizable's "truly the best path"
- * access path really is. So whenever we save a "truly the best" path,
- * we take note of which Optimizer told us to do so. Then as each level
- * of subquery finishes optimization, the corresponding OptimizerImpl
- * can load its preferred access path into this Optimizable's
- * trulyTheBestAccessPath field and pass it up the tree, until eventually
- * the outer-most OptimizerImpl can choose to either use the best path
- * that it received from below (by calling "rememberAsBest()") or else
+ * access path really is. In addition, there could be Optimizables
+ * above this Optimizable that might need to override the best path
+ * chosen during optimization. So whenever we save a "truly the best" path,
+ * we take note of which Optimizer/Optimizable told us to do so. Then
+ * as each level of subquery finishes optimization, the corresponding
+ * OptimizerImpl/Optimizable can load its preferred access path into this
+ * Optimizable's trulyTheBestAccessPath field and pass it up the tree, until
+ * eventually the outer-most OptimizerImpl can choose to either use the best
+ * path that it received from below (by calling "rememberAsBest()") or else
* use the path that it found to be "best" for itself.
*
- * This method is what allows us to keep track of which OptimizerImpl
- * saved which "best plan", and allows us to load the appropriate plans
- * after each round of optimization.
+ * This method is what allows us to keep track of which OptimizerImpl or
+ * Optimizable saved which "best plan", and allows us to load the
+ * appropriate plans after each round of optimization.
*
- * @param doAdd True if we're saving a best plan for the OptimizerImpl,
- * false if we're loading/retrieving the best plan for the OptimizerImpl.
- * @param optimizer The OptimizerImpl for which we're saving/loading
- * the "truly the best" path.
+ * @param doAdd True if we're saving a best plan for the OptimizerImpl/
+ * Optimizable; false if we're loading/retrieving the best plan.
+ * @param planKey Object to use as the map key when adding/looking up
+ * a plan. If it is an instance of OptimizerImpl then it corresponds
+ * to an outer query; otherwise it's some Optimizable above this
+ * Optimizable that could potentially reject plans chosen by the
+ * OptimizerImpl to which this Optimizable belongs.
*/
public void addOrLoadBestPlanMapping(boolean doAdd,
- Optimizer optimizer) throws StandardException;
+ Object planKey) throws StandardException;
/**
* Remember the current access path as the best one (so far).
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/BaseTableNumbersVisitor.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/BaseTableNumbersVisitor.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/BaseTableNumbersVisitor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/BaseTableNumbersVisitor.java Wed Apr 12 13:24:27 2006
@@ -135,14 +135,14 @@
if (rcExpr instanceof ColumnReference)
// we found our column reference; recurse using that.
- return rcExpr.accept(this);
-
+ rcExpr.accept(this);
+ else {
// Else we must have found the table number someplace
// other than within a ColumnReference (ex. we may
// have pulled it from a VirtualColumnNode's source
// table); so just set the number.
- tableMap.set(baseTableNumber);
-
+ tableMap.set(baseTableNumber);
+ }
}
else if (node instanceof ColumnReference) {
// we couldn't find any other table numbers beneath the
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromTable.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromTable.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromTable.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/FromTable.java Wed Apr 12 13:24:27 2006
@@ -157,6 +157,18 @@
RowOrdering rowOrdering)
throws StandardException
{
+ // 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.
+ addOrLoadBestPlanMapping(true, this);
+
CostEstimate singleScanCost = estimateCost(predList,
(ConglomerateDescriptor) null,
outerCost,
@@ -507,25 +519,38 @@
/** @see Optimizable#addOrLoadBestPlanMapping */
public void addOrLoadBestPlanMapping(boolean doAdd,
- Optimizer optimizer) throws StandardException
+ Object planKey) throws StandardException
{
+ AccessPath bestPath = getTrulyTheBestAccessPath();
AccessPathImpl ap = null;
if (doAdd)
{
+ // If we get to this method before ever optimizing this node, then
+ // there will be no best path--so there's nothing to do.
+ if (bestPath == null)
+ return;
+
// If the optimizerToBestPlanMap already exists, search for an
- // AccessPath for the target optimizer and use that if we can.
+ // AccessPath for the received key and use that if we can.
if (optimizerToBestPlanMap == null)
optimizerToBestPlanMap = new HashMap();
else
- ap = (AccessPathImpl)optimizerToBestPlanMap.get(optimizer);
+ ap = (AccessPathImpl)optimizerToBestPlanMap.get(planKey);
- // If we don't already have an AccessPath for the optimizer,
- // create a new one.
+ // If we don't already have an AccessPath for the key,
+ // create a new one. If the key is an OptimizerImpl then
+ // we might as well pass it in to the AccessPath constructor;
+ // otherwise just pass null.
if (ap == null)
- ap = new AccessPathImpl(optimizer);
+ {
+ if (planKey instanceof Optimizer)
+ ap = new AccessPathImpl((Optimizer)planKey);
+ else
+ ap = new AccessPathImpl((Optimizer)null);
+ }
- ap.copy(getTrulyTheBestAccessPath());
- optimizerToBestPlanMap.put(optimizer, ap);
+ ap.copy(bestPath);
+ optimizerToBestPlanMap.put(planKey, ap);
return;
}
@@ -533,22 +558,21 @@
// into this Optimizable's trulyTheBestAccessPath field.
// If we don't have any plans saved, then there's nothing to load.
- // This can happen if the optimizer tried some join order for which
- // there was no valid plan.
+ // This can happen if the key is an OptimizerImpl that tried some
+ // join order for which there was no valid plan.
if (optimizerToBestPlanMap == null)
return;
- ap = (AccessPathImpl)optimizerToBestPlanMap.get(optimizer);
+ ap = (AccessPathImpl)optimizerToBestPlanMap.get(planKey);
- // Again, might be the case that there is no plan stored for
- // the optimizer if no valid plans have been discovered for
- // that optimizer's current join order.
- if (ap == null)
+ // It might be the case that there is no plan stored for
+ // the key, in which case there's nothing to load.
+ if ((ap == null) || (ap.getCostEstimate() == null))
return;
// We found a best plan in our map, so load it into this Optimizable's
// trulyTheBestAccessPath field.
- getTrulyTheBestAccessPath().copy(ap);
+ bestPath.copy(ap);
return;
}
@@ -582,7 +606,23 @@
// join order of the received optimizer, take note of what
// that path is, in case we need to "revert" back to this
// path later. See Optimizable.addOrLoadBestPlanMapping().
- addOrLoadBestPlanMapping(true, optimizer);
+ // Note: Since this call descends all the way down to base
+ // tables, it can be relatively expensive when we have deeply
+ // nested subqueries. So in an attempt to save some work, we
+ // skip the call if this node is a ProjectRestrictNode whose
+ // child is an Optimizable--in that case the ProjectRestrictNode
+ // will in turn call "rememberAsBest" on its child and so
+ // the required call to addOrLoadBestPlanMapping() will be
+ // made at that time. If we did it here, too, then we would
+ // just end up duplicating the work.
+ if (!(this instanceof ProjectRestrictNode))
+ addOrLoadBestPlanMapping(true, optimizer);
+ else
+ {
+ ProjectRestrictNode prn = (ProjectRestrictNode)this;
+ if (!(prn.getChildResult() instanceof Optimizable))
+ addOrLoadBestPlanMapping(true, optimizer);
+ }
/* also store the name of the access path; i.e index name/constraint
* name if we're using an index to access the base table.
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java Wed Apr 12 13:24:27 2006
@@ -176,6 +176,18 @@
{
optimizer.trace(Optimizer.CALLING_ON_JOIN_NODE, 0, 0, 0.0, null);
+ // 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.
+ addOrLoadBestPlanMapping(true, this);
+
/*
** RESOLVE: Most types of Optimizables only implement estimateCost(),
** and leave it up to optimizeIt() in FromTable to figure out the
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/OptimizerImpl.java Wed Apr 12 13:24:27 2006
@@ -1138,6 +1138,38 @@
(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.addOrLoadBestPlanMapping(false, 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.addOrLoadBestPlanMapping(false, 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
@@ -1810,15 +1842,21 @@
return;
}
+ // Before considering the cost, make sure we set the optimizable's
+ // "current" cost to be the one that we received. Doing this allows
+ // us to compare "current" with "best" later on to find out if
+ // the "current" plan is also the "best" one this round--if it's
+ // not then we'll have to revert back to whatever the best plan is.
+ // That check is performed in getNextDecoratedPermutation() of
+ // this class.
+ optimizable.getCurrentAccessPath().setCostEstimate(estimatedCost);
+
/*
** Skip this access path if it takes too much memory.
**
** NOTE: The default assumption here is that the number of rows in
** a single scan is the total number of rows divided by the number
** of outer rows. The optimizable may over-ride this assumption.
- **
- ** NOTE: This is probably not necessary here, because we should
- ** get here only for nested loop joins, which don't use memory.
*/
if( ! optimizable.memoryUsageOK( estimatedCost.rowCount() / outerCost.rowCount(),
maxMemoryPerTable))
@@ -2152,11 +2190,14 @@
* necessary.
*
* @param doAdd True if we're adding a mapping, false if we're loading.
- * @param outerOptimizer OptimizerImpl corresponding to an outer
- * query; we will use this as the key for the mapping.
+ * @param planKey Object to use as the map key when adding/looking up
+ * a plan. If this is an instance of OptimizerImpl then it corresponds
+ * to an outer query; otherwise it's some Optimizable above this
+ * OptimizerImpl that could potentially reject plans chosen by this
+ * OptimizerImpl.
*/
protected void addOrLoadBestPlanMappings(boolean doAdd,
- Optimizer outerOptimizer) throws StandardException
+ Object planKey) throws StandardException
{
// First we save this OptimizerImpl's best join order. If there's
// only one optimizable in the list, then there's only one possible
@@ -2171,7 +2212,7 @@
if (savedJoinOrders == null)
savedJoinOrders = new HashMap();
else
- joinOrder = (int[])savedJoinOrders.get(outerOptimizer);
+ joinOrder = (int[])savedJoinOrders.get(planKey);
// If we don't already have a join order array for the optimizer,
// create a new one.
@@ -2182,7 +2223,7 @@
for (int i = 0; i < bestJoinOrder.length; i++)
joinOrder[i] = bestJoinOrder[i];
- savedJoinOrders.put(outerOptimizer, joinOrder);
+ savedJoinOrders.put(planKey, joinOrder);
}
else
{
@@ -2192,16 +2233,17 @@
// If we don't have any join orders saved, then there's nothing to
// load. This can happen if the optimizer tried some join order
// for which there was no valid plan.
- if (savedJoinOrders == null)
- return;
-
- joinOrder = (int[])savedJoinOrders.get(outerOptimizer);
- if (joinOrder == null)
- return;
-
- // Load the join order we found into our bestJoinOrder array.
- for (int i = 0; i < joinOrder.length; i++)
- bestJoinOrder[i] = joinOrder[i];
+ if (savedJoinOrders != null)
+ {
+ joinOrder = (int[])savedJoinOrders.get(planKey);
+ if (joinOrder != null)
+ {
+ // Load the join order we found into our
+ // bestJoinOrder array.
+ for (int i = 0; i < joinOrder.length; i++)
+ bestJoinOrder[i] = joinOrder[i];
+ }
+ }
}
}
@@ -2211,7 +2253,7 @@
for (int i = optimizableList.size() - 1; i >= 0; i--)
{
optimizableList.getOptimizable(i).
- addOrLoadBestPlanMapping(doAdd, outerOptimizer);
+ addOrLoadBestPlanMapping(doAdd, planKey);
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ProjectRestrictNode.java Wed Apr 12 13:24:27 2006
@@ -293,6 +293,18 @@
// 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.
+ addOrLoadBestPlanMapping(true, 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
@@ -379,7 +391,25 @@
*/
if (childResult instanceof Optimizable)
{
- return ((Optimizable) childResult).feasibleJoinStrategy(restrictionList, optimizer);
+ // With DERBY-805 it's possible that, when considering a nested
+ // loop join with this PRN, we pushed predicates down into the
+ // child if the child is a UNION node. At this point, though, we
+ // may be considering doing a hash join with this PRN instead of a
+ // nested loop join, and if that's the case we need to pull any
+ // predicates back up so that they can be searched for equijoins
+ // that will in turn make the hash join possible. So that's what
+ // the next call does. Two things to note: 1) if no predicates
+ // were pushed, this call is a no-op; and 2) if we get here when
+ // considering a nested loop join, the predicates that we pull
+ // here (if any) will be re-pushed for subsequent costing/
+ // optimization as necessary (see OptimizerImpl.costPermutation(),
+ // which will call this class's optimizeIt() method and that's
+ // where the predicates are pushed down again).
+ if (childResult instanceof UnionNode)
+ ((UnionNode)childResult).pullOptPredicates(restrictionList);
+
+ return ((Optimizable) childResult).
+ feasibleJoinStrategy(restrictionList, optimizer);
}
else
{
@@ -544,6 +574,7 @@
** Do nothing if the child result set is not optimizable, as there
** can be nothing to modify.
*/
+ boolean alreadyPushed = false;
if ( ! (childResult instanceof Optimizable))
{
// Remember that the original child was not Optimizable
@@ -607,6 +638,17 @@
childResult = (ResultSetNode)
((SetOperatorNode) childResult).modifyAccessPath(
outerTables, restrictionList);
+
+ // Take note of the fact that we already pushed predicates
+ // as part of the modifyAccessPaths call. This is necessary
+ // because there may still be predicates in restrictionList
+ // that we intentionally decided not to push (ex. if we're
+ // going to do hash join then we chose to not push the join
+ // predicates). Whatever the reason for not pushing the
+ // predicates, we have to make sure we don't inadvertenly
+ // push them later (esp. as part of the "pushUsefulPredicates"
+ // call below).
+ alreadyPushed = true;
}
else {
childResult =
@@ -615,7 +657,8 @@
}
}
- if (restrictionList != null)
+
+ if ((restrictionList != null) && !alreadyPushed)
{
restrictionList.pushUsefulPredicates((Optimizable) childResult);
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java Wed Apr 12 13:24:27 2006
@@ -1636,18 +1636,20 @@
}
/**
- * Get the optimizer for this result set; assumption is that
- * this.optimizer has already been created by the getOptimizer()
- * method above.
+ * Get the optimizer for this result set.
+ *
+ * @return If this.optimizer has has already been created by the
+ * getOptimizer() method above, then return it; otherwise,
+ * return null.
*/
- protected OptimizerImpl getOptimizerImpl() {
-
- if (SanityManager.DEBUG) {
- SanityManager.ASSERT(optimizer != null,
- "Tried to retrieve optimizer for a result set, but no such " +
- "optimizer existed.");
- }
-
+ protected OptimizerImpl getOptimizerImpl()
+ {
+ // Note that the optimizer might be null because it's possible that
+ // we'll get here before any calls to getOptimizer() were made, which
+ // can happen if we're trying to save a "best path" but we haven't
+ // actually found one yet. In that case we just return the "null"
+ // value; the caller must check for it and behave appropriately.
+ // Ex. see TableOperatorNode.addOrLoadBestPlanMapping().
return (OptimizerImpl)optimizer;
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java Wed Apr 12 13:24:27 2006
@@ -117,8 +117,12 @@
// might depend on those predicates. So now that we're preparing
// to generate the best paths, we have to push those same predicates
// down again (this is the last time) so that the children can use
- // them as appropriate.
- if (predList != null)
+ // them as appropriate. NOTE: If our final choice for join strategy
+ // is a hash join, then we do not push the predicates because we'll
+ // need them to be at this level in order to find out which of them
+ // is the equijoin predicate that is required by hash join.
+ if ((predList != null) &&
+ !getTrulyTheBestAccessPath().getJoinStrategy().isHashJoin())
{
for (int i = predList.size() - 1; i >= 0; i--)
if (pushOptPredicate(predList.getOptPredicate(i)))
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java Wed Apr 12 13:24:27 2006
@@ -171,18 +171,25 @@
* child, in order to ensure that we have a full plan mapped.
*/
public void addOrLoadBestPlanMapping(boolean doAdd,
- Optimizer optimizer) throws StandardException
+ Object planKey) throws StandardException
{
- super.addOrLoadBestPlanMapping(doAdd, optimizer);
+ super.addOrLoadBestPlanMapping(doAdd, planKey);
+
+ // Now walk the child. Note that if the child is not an
+ // Optimizable and the call to child.getOptimizerImpl()
+ // returns null, then that means we haven't tried to optimize
+ // the child yet. So in that case there's nothing to
+ // add/load.
+
if (childResult instanceof Optimizable)
{
((Optimizable)childResult).
- addOrLoadBestPlanMapping(doAdd, optimizer);
+ addOrLoadBestPlanMapping(doAdd, planKey);
}
- else
+ else if (childResult.getOptimizerImpl() != null)
{
childResult.getOptimizerImpl().
- addOrLoadBestPlanMappings(doAdd, optimizer);
+ addOrLoadBestPlanMappings(doAdd, planKey);
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java Wed Apr 12 13:24:27 2006
@@ -164,29 +164,36 @@
* full plan mapped.
*/
public void addOrLoadBestPlanMapping(boolean doAdd,
- Optimizer optimizer) throws StandardException
+ Object planKey) throws StandardException
{
- super.addOrLoadBestPlanMapping(doAdd, optimizer);
+ super.addOrLoadBestPlanMapping(doAdd, planKey);
+
+ // Now walk the children. Note that if either child is not
+ // an Optimizable and the call to child.getOptimizerImpl()
+ // returns null, then that means we haven't tried to optimize
+ // the child yet. So in that case there's nothing to
+ // add/load.
+
if (leftResultSet instanceof Optimizable)
{
((Optimizable)leftResultSet).
- addOrLoadBestPlanMapping(doAdd, optimizer);
+ addOrLoadBestPlanMapping(doAdd, planKey);
}
- else
+ else if (leftResultSet.getOptimizerImpl() != null)
{
leftResultSet.getOptimizerImpl().
- addOrLoadBestPlanMappings(doAdd, optimizer);
+ addOrLoadBestPlanMappings(doAdd, planKey);
}
if (rightResultSet instanceof Optimizable)
{
((Optimizable)rightResultSet).
- addOrLoadBestPlanMapping(doAdd, optimizer);
+ addOrLoadBestPlanMapping(doAdd, planKey);
}
- else
+ else if (rightResultSet.getOptimizerImpl() != null)
{
rightResultSet.getOptimizerImpl().
- addOrLoadBestPlanMappings(doAdd, optimizer);
+ addOrLoadBestPlanMappings(doAdd, planKey);
}
}
Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java?rev=393593&r1=393592&r2=393593&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java Wed Apr 12 13:24:27 2006
@@ -220,14 +220,31 @@
// need to scope them for the child result sets first, and
// then push the scoped versions. This is all done in the
// call to pushOptPredicate() here; for more, see that method's
- // definition in SetOperatorNode.
- if (predList != null)
+ // definition in SetOperatorNode. NOTE: If we're considering a
+ // hash join then we do not push the predicates because we'll
+ // need the predicates to be at this level in order to find
+ // out if one of them is an equijoin predicate that can be used
+ // 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.
+ addOrLoadBestPlanMapping(true, this);
leftResultSet = optimizeSource(
optimizer,