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/28 01:42:04 UTC
svn commit: r397682 [2/4] - in /db/derby/code/branches/10.1/java:
engine/org/apache/derby/iapi/sql/compile/
engine/org/apache/derby/iapi/store/access/
engine/org/apache/derby/impl/sql/compile/
testing/org/apache/derbyTesting/functionTests/master/ testi...
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SetOperatorNode.java Thu Apr 27 16:42:02 2006
@@ -24,7 +24,12 @@
import org.apache.derby.iapi.error.StandardException;
+import org.apache.derby.iapi.sql.compile.AccessPath;
import org.apache.derby.iapi.sql.compile.C_NodeTypes;
+import org.apache.derby.iapi.sql.compile.CostEstimate;
+import org.apache.derby.iapi.sql.compile.Optimizable;
+import org.apache.derby.iapi.sql.compile.OptimizablePredicate;
+import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
import org.apache.derby.iapi.sql.dictionary.DataDictionary;
import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
@@ -34,6 +39,8 @@
import org.apache.derby.iapi.util.JBitSet;
+import java.util.HashMap;
+
/**
* A SetOperatorNode represents a UNION, INTERSECT, or EXCEPT in a DML statement. Binding and optimization
* preprocessing is the same for all of these operations, so they share bind methods in this abstract class.
@@ -54,6 +61,18 @@
OrderByList orderByList;
+ // List of scoped predicates for pushing during optimization.
+ PredicateList leftOptPredicates;
+ PredicateList rightOptPredicates;
+
+ // List of original (unscoped) predicates that we tried to push
+ // during the most recent phase of optimization.
+ PredicateList pushedPredicates;
+
+ // Mapping of original predicates to scoped predicates, used to
+ // avoid re-scoping predicates unnecessarily.
+ HashMap leftScopedPreds;
+ HashMap rightScopedPreds;
/**
* Initializer for a SetOperatorNode.
@@ -86,6 +105,363 @@
}
/**
+ * @see Optimizable#modifyAccessPath
+ *
+ * @exception StandardException Thrown on error
+ */
+ public Optimizable modifyAccessPath(JBitSet outerTables,
+ PredicateList predList) throws StandardException
+ {
+ // When we optimized this node we attempted to push predicates down to
+ // the children, which means the best access path for the children
+ // 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. 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)))
+ predList.removeOptPredicate(i);
+ }
+
+ /*
+ * It's possible that we tried to push a predicate down to this node's
+ * children but failed to do so. This can happen if this node's
+ * children both match the criteria for pushing a predicate (namely,
+ * they reference base tables) but the children's children do not.
+ * Ex.
+ * select * from
+ * (select i,j from t2 UNION
+ * values (1,1),(2,2),(3,3),(4,4) UNION
+ * select i,j from t1
+ * ) x0 (i,j),
+ * t5 where x0.i = t5.i;
+ *
+ * This will yield a tree resembling the following:
+ *
+ * UNION
+ * / \
+ * UNION SELECT (T1)
+ * / \
+ * SELECT (T2) VALUES
+ *
+ * In this case the top UNION ("this") will push the predicate down,
+ * but the second UNION will _not_ push the predicate because
+ * it can't be pushed to the VALUES clause. This means that
+ * after we're done modifying the paths for "this" node (the top
+ * UNION), the predicate will still be sitting in our leftOptPredicates
+ * list. If that's the case, then we have to make sure the predicate,
+ * which was _not_ enforced in the left child, is enforced at this
+ * level. We do that by generating a ProjectRestrictNode above this
+ * node. Yes, this means the predicate will actually be applied
+ * twice to the right child (in this case), but that's okay as it
+ * won't affect the results.
+ */
+
+ // Get the cost estimate for this node so that we can put it in
+ // the new ProjectRestrictNode, if one is needed.
+ CostEstimate ce = getFinalCostEstimate();
+
+ // Modify this node's access paths.
+ ResultSetNode topNode = (ResultSetNode)modifyAccessPath(outerTables);
+
+ // Now see if there are any left over predicates; if so, then we
+ // have to generate a ProjectRestrictNode.
+ if (((leftOptPredicates != null) && (leftOptPredicates.size() > 0)) ||
+ ((rightOptPredicates != null) && (rightOptPredicates.size() > 0)))
+ {
+ // When we generate the project restrict node, we pass in the
+ // "pushedPredicates" list because that has the predicates in
+ // _unscoped_ form, which means they are intended for _this_
+ // node instead of this node's children. That's exactly what
+ // we want.
+ ResultSetNode prnRSN = (ResultSetNode) getNodeFactory().getNode(
+ C_NodeTypes.PROJECT_RESTRICT_NODE,
+ topNode, // Child ResultSet
+ topNode.getResultColumns(), // Projection
+ null, // Restriction
+ pushedPredicates, // Restriction as PredicateList
+ null, // Subquerys in Projection
+ null, // Subquerys in Restriction
+ null, // Table properties
+ getContextManager());
+ prnRSN.costEstimate = ce.cloneMe();
+ prnRSN.setReferencedTableMap(topNode.getReferencedTableMap());
+ topNode = prnRSN;
+ }
+
+ return (Optimizable)topNode;
+ }
+
+ /**
+ * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate
+ *
+ * Take a predicate and push it down to both the left AND right result
+ * sets. Return "true" if we successfully pushed it to both sides,
+ * and "false" otherwise. The assumption is that if we return "true",
+ * the caller will take the predicate and remove it from its own list
+ * of predicates to evaluate; if we return false, then the predicate
+ * will be evaluated at the level of the caller. So returning "false"
+ * means that the left and right result sets for this node will be fully
+ * returned, and then the predicate will be evaluated against the
+ * <set-operator> of those result sets (as of DERBY-805, the only set
+ * operator calling this method is UnionNode). If we can push the
+ * predicate down to both children, though, we can evaluate it closer
+ * to store, which means that each child result set returns only the
+ * correctly qualified rows, and thus the calling set operator will
+ * have a smaller result set on which to operate, which can boost
+ * performance.
+ *
+ * That said, if we can't push the predicate to _both_ sides, we don't
+ * push it at all. The reason is that if we push to one side but not
+ * to the other, we would have to ask the question of whether we should
+ * return "true" (meaning that the predicate would be removed from the
+ * caller's list and thus would _not_ be evaluated at the <set-operator>
+ * level) or "false" (meaning that the caller would keep the predicate
+ * and evaluate it at the <set-operator> level). Depending on the query
+ * in question, both answers could end up returning incorrect results.
+ *
+ * For example, if we push it to the right but not to the left, then
+ * leave it in the caller's list, the optimizer for the caller might
+ * decide to use the predicate to do a hash join with some outer result
+ * set (if the predicate is an equijoin predicate). That would result
+ * in materialization of the calling node and of its children--but since
+ * we pushed a predicate that depends on the outer table down into the
+ * right child, materialization of the right child will only return the
+ * rows that join with the _first_ row of the outer result set, which
+ * is wrong.
+ *
+ * If, on the other hand, we push the predicate to one side and then tell
+ * the caller to remove it from its list, the side to which we did _not_
+ * push the predicate could return rows that aren't qualified. Then,
+ * since the caller removed the predicate from its list, it (the caller)
+ * will not evaluate the predicate on its own result set--and thus we
+ * can end up returning rows that we weren't supposed to return.
+ *
+ * So all of that said, only push (and return "true") if we think we
+ * can push the predicate to both sides.
+ *
+ * @exception StandardException Thrown on error
+ */
+ public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)
+ throws StandardException
+ {
+ // This method was added to SetOperatorNode as part of DERBY-805,
+ // which was only targeted for UnionNodes. So for now, we don't
+ // do anything if "this" isn't a Union. This check can be removed
+ // when support for other SetOperators is added.
+ if (!(this instanceof UnionNode))
+ return false;
+
+ // We only handle certain types of predicates here; if the received
+ // predicate doesn't qualify, then don't push it.
+ Predicate pred = (Predicate)optimizablePredicate;
+ if (!pred.pushableToSubqueries())
+ return false;
+
+ // Check to see if the child nodes reference any base tables; if either
+ // child does not reference at least one base table, then we don't try
+ // to push the predicate.
+ boolean canPush = false;
+
+ JBitSet tableNums = new JBitSet(getReferencedTableMap().size());
+ BaseTableNumbersVisitor btnVis =
+ new BaseTableNumbersVisitor(tableNums);
+
+ // Check the left child.
+ leftResultSet.accept(btnVis);
+ canPush = (tableNums.getFirstSetBit() != -1);
+
+ /* If we can't push it to _both_ children, then we don't push at all.
+ * RESOLVE: We can add the ability to push a predicate to one side
+ * only by putting a ProjectRestrictNode between the union node and
+ * the child as a place to park the predicate. To make things simple,
+ * we might want to always put ProjectRestrictNodes under both sides
+ * of the union during preprocessing (i.e. after binding but before
+ * optimization). In some cases the extra nodes won't be needed, but
+ * PRNs (and the corresponding ProjectRestrictResultSets) are cheap.
+ * Also, we could eliminate unnecessary ProjectRestrictNodes at the
+ * end of optimization (possibly in modifyAccessPaths()). Until all
+ * of that is implemented, though, we only push if we can push to
+ * both sides...
+ */
+ if (!canPush)
+ return false;
+
+ // Check the right child.
+ tableNums.clearAll();
+ rightResultSet.accept(btnVis);
+ canPush = (tableNums.getFirstSetBit() != -1);
+ if (!canPush)
+ return false;
+
+ BinaryRelationalOperatorNode opNode =
+ (BinaryRelationalOperatorNode)pred.getAndNode().getLeftOperand();
+
+ // Note: we assume we only get here for predicates with col refs on
+ // both sides; if that ever changes, the following cast will need
+ // to be updated accordingly.
+ boolean opWasRemapped =
+ ((ColumnReference)opNode.getLeftOperand()).hasBeenRemapped();
+
+ /* If there is a ProjectRestrictNode directly above this node,
+ * then the predicate in question may have been remapped to this
+ * SetOperatorNode before we got here (see pushOptPredicate() in
+ * ProjectRestrictNode). If we leave it mapped when we try to
+ * get scoped predicates for the left and right result sets, the
+ * underlying column references of the scoped predicates will
+ * effectively be doubly-mapped (i.e. mapped more than once), which
+ * can cause problems at code generation time. So we 1) un-remap
+ * the predicate here, 2) get the scoped predicates, and then
+ * 3) remap the predicate again at the end of this method.
+ */
+ RemapCRsVisitor rcrv = null;
+ if (opWasRemapped)
+ {
+ rcrv = new RemapCRsVisitor(false);
+ pred.getAndNode().accept(rcrv);
+ }
+
+ // Get a list of all of the underlying base tables that this node
+ // references. We pass this down when scoping so that we can tell
+ // if the operands are actually supposed to be scoped to _this_
+ // node's children. Note that in order for the predicate to
+ // have been pushed this far, at least one of its operands must
+ // apply to this node--we don't know which one it is, though,
+ // so we use this tableNums info to figure that out.
+ tableNums.clearAll();
+ this.accept(btnVis);
+
+ /* What we want to do here is push the predicate to the left/right
+ * child. That means that we need to find the equivalent column(s)
+ * in each child.
+ * Ex:
+ *
+ * select * from
+ * (select i,j from t1 union select i,j from t2) X1,
+ * (select a,b from t3 union select a,b from t4) X2
+ * where X1.j = X2.b;
+ *
+ * In this example, X1.j maps to "t1" for the left side of the
+ * union (X1) and "t2" for the right side of the union. So we have
+ * to get versions of the predicate that are appropriate to each
+ * side. That's what the call to getPredScopedForResultSet()
+ * in the following code does.
+ */
+
+ // See if we already have a scoped version of the predicate cached,
+ // and if so just use that.
+ Predicate scopedPred = null;
+ if (leftScopedPreds == null)
+ leftScopedPreds = new HashMap();
+ else
+ scopedPred = (Predicate)leftScopedPreds.get(pred);
+ if (scopedPred == null)
+ {
+ scopedPred = pred.getPredScopedForResultSet(
+ tableNums, leftResultSet);
+ leftScopedPreds.put(pred, scopedPred);
+ }
+
+ // Add the scoped predicate to our list for the left child.
+ getLeftOptPredicateList().addOptPredicate(scopedPred);
+
+ scopedPred = null;
+ if (rightScopedPreds == null)
+ rightScopedPreds = new HashMap();
+ else
+ scopedPred = (Predicate)rightScopedPreds.get(pred);
+ if (scopedPred == null)
+ {
+ scopedPred = pred.getPredScopedForResultSet(
+ tableNums, rightResultSet);
+ rightScopedPreds.put(pred, scopedPred);
+ }
+
+ // Add the scoped predicate to our list for the right child.
+ getRightOptPredicateList().addOptPredicate(scopedPred);
+
+ // Restore the original predicate to the way it was before we got
+ // here--i.e. remap it again if needed.
+ if (opWasRemapped)
+ {
+ rcrv = new RemapCRsVisitor(true);
+ pred.getAndNode().accept(rcrv);
+ }
+
+ // Add the predicate (in its original form) to our list of predicates
+ // that we've pushed during this phase of optimization. We need to
+ // keep this list of pushed predicates around so that we can do
+ // a "pull" of them later, if needed. We also need this list for
+ // cases where predicates are not pushed all the way down; see
+ // modifyAccessPaths() in this class for more.
+ if (pushedPredicates == null)
+ pushedPredicates = new PredicateList();
+
+ pushedPredicates.addOptPredicate(pred);
+ return true;
+ }
+
+ /**
+ * @see Optimizable#pullOptPredicates
+ *
+ * @exception StandardException Thrown on error
+ */
+ public void pullOptPredicates(
+ OptimizablePredicateList optimizablePredicates)
+ throws StandardException
+ {
+ if (pushedPredicates == null)
+ // we didn't push anything, so nothing to pull.
+ return;
+
+ // It's possible that we tried to push a predicate down to this
+ // SetOperatorNode's children but weren't actually able to do so
+ // (see modifyAccessPaths() in this class for details on when that
+ // can happen). In that case the predicates will still be sitting
+ // in the left/right predicate list; we can ignore them here by
+ // just discarding them. When it comes time to modifyAccessPaths,
+ // though, we'll handle them correctly--i.e. we'll generate a
+ // ProjectRestrictNode over this node to ensure the predicates are
+ // enforced.
+
+ if (leftOptPredicates != null)
+ leftOptPredicates.removeAllElements();
+
+ if (rightOptPredicates != null)
+ rightOptPredicates.removeAllElements();
+
+ /* Note that predicates which have been explicitly scoped should
+ * not be pulled. The reason is that a scoped predicate can only
+ * be pushed to a specific, target result set. When it comes time
+ * to pull the predicate up, there's no need to pull the scoped
+ * predicate because it, by definition, was only intended for this
+ * specific result set and therefore cannot be pushed anywhere else.
+ * So at "pull" time, we can just discard the scoped predicates. We
+ * do, however, need to pull the original, unscoped predicate from
+ * which the scoped predicate was created because we can potentially
+ * push that predicate elsewhere
+ */
+ Predicate pred = null;
+ for (int i = 0; i < pushedPredicates.size(); i++)
+ {
+ pred = (Predicate)pushedPredicates.getOptPredicate(i);
+ if (pred.isScopedForPush())
+ continue;
+ optimizablePredicates.addOptPredicate(pred);
+ }
+
+ // We're done with the pushedPredicates list, so clear it out
+ // in preparation for another phase of optimization.
+ pushedPredicates.removeAllElements();
+ }
+
+ /**
* Convert this object to a String. See comments in QueryTreeNode.java
* for how this should be done for tree printing.
*
@@ -645,4 +1021,41 @@
* @return the operator name: "UNION", "INTERSECT", or "EXCEPT"
*/
abstract String getOperatorName();
+
+ /**
+ * Retrieve the list of optimizable predicates that are
+ * targeted for the left child. Create a new (empty)
+ * list if the list is null.
+ */
+ protected PredicateList getLeftOptPredicateList()
+ throws StandardException
+ {
+ if (leftOptPredicates == null) {
+ leftOptPredicates =
+ (PredicateList) getNodeFactory().getNode(
+ C_NodeTypes.PREDICATE_LIST,
+ getContextManager());
+ }
+
+ return leftOptPredicates;
+ }
+
+ /**
+ * Retrieve the list of optimizable predicates that are
+ * targeted for the right child. Create a new (empty)
+ * list if the list is null.
+ */
+ protected PredicateList getRightOptPredicateList()
+ throws StandardException
+ {
+ if (rightOptPredicates == null) {
+ rightOptPredicates =
+ (PredicateList) getNodeFactory().getNode(
+ C_NodeTypes.PREDICATE_LIST,
+ getContextManager());
+ }
+
+ return rightOptPredicates;
+ }
+
}
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SingleChildResultSetNode.java Thu Apr 27 16:42:02 2006
@@ -173,18 +173,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);
}
}
@@ -585,6 +592,7 @@
* the final cost estimate for the child node.
*/
public CostEstimate getFinalCostEstimate()
+ throws StandardException
{
/*
** The cost estimate will be set here if either optimize() or
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SubqueryNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SubqueryNode.java?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SubqueryNode.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/SubqueryNode.java Thu Apr 27 16:42:02 2006
@@ -1833,6 +1833,10 @@
mb.conditionalIfNull();
ResultSetNode materialSubNode = new MaterializeSubqueryNode(subRS);
+
+ // Propagate the resultSet's cost estimate to the new node.
+ materialSubNode.costEstimate = resultSet.getFinalCostEstimate();
+
((ProjectRestrictNode) resultSet).setChildResult(materialSubNode);
/* Evaluate subquery resultset here first. Next time when we come to
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/TableOperatorNode.java Thu Apr 27 16:42:02 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);
}
}
@@ -714,14 +721,36 @@
if (leftOptimizer != null)
leftOptimizer.modifyAccessPaths();
else
- leftResultSet = leftResultSet.modifyAccessPaths();
+ {
+ // If this is a SetOperatorNode then we may have pushed
+ // predicates down to the children. If that's the case
+ // then we need to pass those predicates down as part
+ // of the modifyAccessPaths call so that they can be
+ // pushed one last time, in prep for generation.
+ if (this instanceof SetOperatorNode)
+ {
+ SetOperatorNode setOp = (SetOperatorNode)this;
+ leftResultSet = leftResultSet.modifyAccessPaths(
+ setOp.getLeftOptPredicateList());
+ }
+ else
+ leftResultSet = leftResultSet.modifyAccessPaths();
+ }
}
if (!rightModifyAccessPathsDone)
{
if (rightOptimizer != null)
rightOptimizer.modifyAccessPaths();
else
- rightResultSet = rightResultSet.modifyAccessPaths();
+ {
+ if (this instanceof SetOperatorNode) {
+ SetOperatorNode setOp = (SetOperatorNode)this;
+ rightResultSet = rightResultSet.modifyAccessPaths(
+ setOp.getRightOptPredicateList());
+ }
+ else
+ rightResultSet = rightResultSet.modifyAccessPaths();
+ }
}
return this;
}
@@ -793,7 +822,7 @@
(RequiredRowOrdering) null,
getCompilerContext().getNumTables(),
lcc);
- ((OptimizerImpl)optimizer).prepForNextRound();
+ optimizer.prepForNextRound();
if (sourceResultSet == leftResultSet)
{
Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java (original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/sql/compile/UnionNode.java Thu Apr 27 16:42:02 2006
@@ -215,17 +215,49 @@
*/
/* optimize() both resultSets */
- /* RESOLVE - don't try to push predicates through for now */
+
+ // If we have predicates from an outer block, we want to try
+ // to push them down to this node's children. However, we can't
+ // just push the predicates down as they are; instead, we
+ // 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. 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,
leftResultSet,
- (PredicateList) null,
+ getLeftOptPredicateList(),
outerCost);
rightResultSet = optimizeSource(
optimizer,
rightResultSet,
- (PredicateList) null,
+ getRightOptPredicateList(),
outerCost);
CostEstimate costEstimate = getCostEstimate(optimizer);
@@ -551,6 +583,9 @@
*/
assignResultSetNumber();
+ // Get our final cost estimate based on the child estimates.
+ costEstimate = getFinalCostEstimate();
+
// build up the tree.
acb.pushGetResultSetFactoryExpression(mb); // instance for getUnionResultSet
@@ -601,6 +636,34 @@
closeMethodArgument(acb, mb);
mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getUnionResultSet", ClassName.NoPutResultSet, 7);
+ }
+
+ /**
+ * @see ResultSetNode#getFinalCostEstimate
+ *
+ * Get the final CostEstimate for this UnionNode.
+ *
+ * @return The final CostEstimate for this UnionNode, which is
+ * the sum of the two child costs.
+ */
+ public CostEstimate getFinalCostEstimate()
+ throws StandardException
+ {
+ // If we already found it, just return it.
+ if (finalCostEstimate != null)
+ return finalCostEstimate;
+
+ CostEstimate leftCE = leftResultSet.getFinalCostEstimate();
+ CostEstimate rightCE = rightResultSet.getFinalCostEstimate();
+
+ finalCostEstimate = getNewCostEstimate();
+ finalCostEstimate.setCost(leftCE.getEstimatedCost(),
+ leftCE.rowCount(),
+ leftCE.singleScanRowCount() +
+ rightCE.singleScanRowCount());
+
+ finalCostEstimate.add(rightCE, finalCostEstimate);
+ return finalCostEstimate;
}
String getOperatorName()
Modified: db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/derived.out
URL: http://svn.apache.org/viewcvs/db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/derived.out?rev=397682&r1=397681&r2=397682&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/derived.out (original)
+++ db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/derived.out Thu Apr 27 16:42:02 2006
@@ -323,12 +323,12 @@
B |A |B |A
-----------------------------------------------
0 |0 |0 |0
-0 |10 |0 |0
0 |0 |0 |10
+0 |10 |0 |0
0 |10 |0 |10
10 |0 |10 |0
-10 |10 |10 |0
10 |0 |10 |10
+10 |10 |10 |0
10 |10 |10 |10
ij> select * from (select (select 1 from s where 1 = 0), b.a from s a, s b) a (b, a),
(select (select 1 from s where 1 = 0), b.a from s a, s b) b (b, a) where a.b = b.b;