Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / sql / compile / JoinNode.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/JoinNode.java
new file mode 100644 (file)
index 0000000..88c5c02
--- /dev/null
@@ -0,0 +1,1963 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.JoinNode\r
+\r
+   Licensed to the Apache Software Foundation (ASF) under one or more\r
+   contributor license agreements.  See the NOTICE file distributed with\r
+   this work for additional information regarding copyright ownership.\r
+   The ASF licenses this file to you under the Apache License, Version 2.0\r
+   (the "License"); you may not use this file except in compliance with\r
+   the License.  You may obtain a copy of the License at\r
+\r
+      http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+   Unless required by applicable law or agreed to in writing, software\r
+   distributed under the License is distributed on an "AS IS" BASIS,\r
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+   See the License for the specific language governing permissions and\r
+   limitations under the License.\r
+\r
+ */\r
+\r
+package        org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.compile.Optimizable;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicate;\r
+import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;\r
+import org.apache.derby.iapi.sql.compile.Optimizer;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.CostEstimate;\r
+import org.apache.derby.iapi.sql.compile.RowOrdering;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.util.PropertyUtil;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.util.Properties;\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A JoinNode represents a join result set for either of the basic DML\r
+ * operations: SELECT and INSERT.  For INSERT - SELECT, any of the\r
+ * fields in a JoinNode can be used (the JoinNode represents\r
+ * the (join) SELECT statement in the INSERT - SELECT).  For INSERT,\r
+ * the resultColumns in the selectList will contain the names of the columns\r
+ * being inserted into or updated.\r
+ *\r
+ */\r
+\r
+public class JoinNode extends TableOperatorNode\r
+{\r
+       /* Join semantics */\r
+       public static final int INNERJOIN = 1;\r
+       public static final int CROSSJOIN = 2;\r
+       public static final int LEFTOUTERJOIN = 3;\r
+       public static final int RIGHTOUTERJOIN = 4;\r
+       public static final int FULLOUTERJOIN = 5;\r
+       public static final int UNIONJOIN = 6;\r
+\r
+       private boolean optimized;\r
+\r
+       private PredicateList leftPredicateList;\r
+       private PredicateList rightPredicateList;\r
+\r
+       protected boolean flattenableJoin = true;\r
+       Vector                          aggregateVector;\r
+       SubqueryList            subqueryList;\r
+       ValueNode                       joinClause;\r
+       boolean             joinClauseNormalized;\r
+       PredicateList           joinPredicates;\r
+       ResultColumnList        usingClause;\r
+       //User provided optimizer overrides\r
+       Properties joinOrderStrategyProperties;\r
+\r
+\r
+       /**\r
+        * Initializer for a JoinNode.\r
+        *\r
+        * @param leftResult    The ResultSetNode on the left side of this join\r
+        * @param rightResult   The ResultSetNode on the right side of this join\r
+        * @param onClause              The ON clause\r
+        * @param usingClause   The USING clause\r
+        * @param selectList    The result column list for the join\r
+        * @param tableProperties       Properties list associated with the table\r
+        * @param joinOrderStrategyProperties   User provided optimizer overrides\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void init(\r
+                                       Object leftResult,\r
+                                       Object rightResult,\r
+                                       Object onClause,\r
+                                       Object usingClause,\r
+                                       Object selectList,\r
+                                       Object tableProperties,\r
+                                       Object joinOrderStrategyProperties)\r
+                       throws StandardException\r
+       {\r
+               super.init(leftResult, rightResult, tableProperties);\r
+               resultColumns = (ResultColumnList) selectList;\r
+               joinClause = (ValueNode) onClause;\r
+               joinClauseNormalized = false;\r
+               this.usingClause = (ResultColumnList) usingClause;\r
+               this.joinOrderStrategyProperties = (Properties)joinOrderStrategyProperties;\r
+\r
+               /* JoinNodes can be generated in the parser or at the end of optimization.\r
+                * Those generated in the parser do not have resultColumns yet.\r
+                */\r
+               if (resultColumns != null)\r
+               {\r
+                       /* A longer term assertion */\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.ASSERT((leftResultSet.getReferencedTableMap() != null &&\r
+                                                                         rightResultSet.getReferencedTableMap() != null) ||\r
+                                                                        (leftResultSet.getReferencedTableMap() == null &&\r
+                                                                         rightResultSet.getReferencedTableMap() == null),\r
+                                       "left and right referencedTableMaps are expected to either both be non-null or both be null");\r
+                       }\r
+\r
+                       /* Build the referenced table map (left || right) */\r
+                       if (leftResultSet.getReferencedTableMap() != null)\r
+                       {\r
+                               referencedTableMap = (JBitSet) leftResultSet.getReferencedTableMap().clone();\r
+                               referencedTableMap.or((JBitSet) rightResultSet.getReferencedTableMap());\r
+                       }\r
+               }\r
+               joinPredicates = (PredicateList) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.PREDICATE_LIST,\r
+                                                                                       getContextManager());\r
+       }\r
+\r
+       /*\r
+        *  Optimizable interface\r
+        */\r
+\r
+       /**\r
+        * @see org.apache.derby.iapi.sql.compile.Optimizable#optimizeIt\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public CostEstimate optimizeIt(\r
+                                                       Optimizer optimizer,\r
+                                                       OptimizablePredicateList predList,\r
+                                                       CostEstimate outerCost,\r
+                                                       RowOrdering rowOrdering)\r
+                       throws StandardException\r
+       {\r
+               optimizer.trace(Optimizer.CALLING_ON_JOIN_NODE, 0, 0, 0.0, null);\r
+\r
+               // It's possible that a call to optimize the left/right will cause\r
+               // a new "truly the best" plan to be stored in the underlying base\r
+               // tables.  If that happens and then we decide to skip that plan\r
+               // (which we might do if the call to "considerCost()" below decides\r
+               // the current path is infeasible or not the best) we need to be\r
+               // able to revert back to the "truly the best" plans that we had\r
+               // saved before we got here.  So with this next call we save the\r
+               // current plans using "this" node as the key.  If needed, we'll\r
+               // then make the call to revert the plans in OptimizerImpl's\r
+               // getNextDecoratedPermutation() method.\r
+               updateBestPlanMap(ADD_PLAN, this);\r
+\r
+               /*\r
+               ** RESOLVE: Most types of Optimizables only implement estimateCost(),\r
+               ** and leave it up to optimizeIt() in FromTable to figure out the\r
+               ** total cost of the join.  For joins, though, we want to figure out\r
+               ** the best plan for the join knowing how many outer rows there are -\r
+               ** it could affect the join strategy significantly.  So we implement\r
+               ** optimizeIt() here, which overrides the optimizeIt() in FromTable.\r
+               ** This assumes that the join strategy for which this join node is\r
+               ** the inner table is a nested loop join, which will not be a valid\r
+               ** assumption when we implement other strategies like materialization\r
+               ** (hash join can work only on base tables).\r
+               */\r
+\r
+               /* RESOLVE - Need to figure out how to really optimize this node. */\r
+\r
+               // RESOLVE: NEED TO SET ROW ORDERING OF SOURCES IN THE ROW ORDERING\r
+               // THAT WAS PASSED IN.\r
+               leftResultSet = optimizeSource(\r
+                                                       optimizer,\r
+                                                       leftResultSet,\r
+                                                       getLeftPredicateList(),\r
+                                                       outerCost);\r
+\r
+               /* Move all joinPredicates down to the right.\r
+                * RESOLVE - When we consider the reverse join order then\r
+                * we will have to pull them back up and then push them\r
+                * down to the other side when considering the reverse\r
+                * join order.\r
+                * RESOLVE - This logic needs to be looked at when we\r
+                * implement full outer join.\r
+                */\r
+               // Walk joinPredicates backwards due to possible deletes\r
+               for (int index = joinPredicates.size() - 1; index >= 0; index --)\r
+               {\r
+                       JBitSet   curBitSet;\r
+                       Predicate predicate;\r
+\r
+                       predicate = (Predicate) joinPredicates.elementAt(index);\r
+                       if (! predicate.getPushable())\r
+                       {\r
+                               continue;\r
+                       }\r
+                       joinPredicates.removeElementAt(index);\r
+                       getRightPredicateList().addElement(predicate);\r
+               }\r
+\r
+               rightResultSet = optimizeSource(\r
+                                                       optimizer,\r
+                                                       rightResultSet,\r
+                                                       getRightPredicateList(),\r
+                                                       leftResultSet.getCostEstimate());\r
+\r
+               costEstimate = getCostEstimate(optimizer);\r
+\r
+               /*\r
+               ** We add the costs for the inner and outer table, but the number\r
+               ** of rows is that for the inner table only.\r
+               */\r
+               costEstimate.setCost(\r
+                       leftResultSet.getCostEstimate().getEstimatedCost() +\r
+                       rightResultSet.getCostEstimate().getEstimatedCost(),\r
+                       rightResultSet.getCostEstimate().rowCount(),\r
+                       rightResultSet.getCostEstimate().rowCount());\r
+\r
+               /*\r
+               ** Some types of joins (e.g. outer joins) will return a different\r
+               ** number of rows than is predicted by optimizeIt() in JoinNode.\r
+               ** So, adjust this value now. This method does nothing for most\r
+               ** join types.\r
+               */\r
+               adjustNumberOfRowsReturned(costEstimate);\r
+\r
+               /*\r
+               ** Get the cost of this result set in the context of the whole plan.\r
+               */\r
+               getCurrentAccessPath().\r
+                       getJoinStrategy().\r
+                               estimateCost(\r
+                                                       this,\r
+                                                       predList,\r
+                                                       (ConglomerateDescriptor) null,\r
+                                                       outerCost,\r
+                                                       optimizer,\r
+                                                       costEstimate\r
+                                                       );\r
+\r
+               optimizer.considerCost(this, predList, costEstimate, outerCost);\r
+\r
+               /* Optimize subqueries only once, no matter how many times we're called */\r
+               if ( (! optimized) && (subqueryList != null))\r
+               {\r
+                       /* RESOLVE - Need to figure out how to really optimize this node.\r
+                       * Also need to figure out the pushing of the joinClause.\r
+                       */\r
+                       subqueryList.optimize(optimizer.getDataDictionary(),\r
+                                                                       costEstimate.rowCount());\r
+                       subqueryList.modifyAccessPaths();\r
+               }\r
+\r
+               optimized = true;\r
+\r
+               return costEstimate;\r
+       }\r
+\r
+       /**\r
+        * @see Optimizable#pushOptPredicate\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)\r
+                       throws StandardException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(optimizablePredicate instanceof Predicate,\r
+                               "optimizablePredicate expected to be instanceof Predicate");\r
+                       SanityManager.ASSERT(! optimizablePredicate.hasSubquery() &&\r
+                                                                ! optimizablePredicate.hasMethodCall(),\r
+                               "optimizablePredicate either has a subquery or a method call");\r
+               }\r
+\r
+               /* Add the matching predicate to the joinPredicates */\r
+               joinPredicates.addPredicate((Predicate) optimizablePredicate);\r
+\r
+               /* Remap all of the ColumnReferences to point to the\r
+                * source of the values.\r
+                */\r
+               RemapCRsVisitor rcrv = new RemapCRsVisitor(true);\r
+               ((Predicate) optimizablePredicate).getAndNode().accept(rcrv);\r
+\r
+               return true;\r
+       }\r
+\r
+       /**\r
+        * @see Optimizable#modifyAccessPath\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public Optimizable modifyAccessPath(JBitSet outerTables) throws StandardException\r
+       {\r
+               super.modifyAccessPath(outerTables);\r
+\r
+               /* By the time we're done here, both the left and right\r
+                * predicate lists should be empty because we pushed everything\r
+                * down.\r
+                */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (getLeftPredicateList().size() != 0)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "getLeftPredicateList().size() expected to be 0, not " +\r
+                                       getLeftPredicateList().size());\r
+                       }\r
+                       if (getRightPredicateList().size() != 0)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "getRightPredicateList().size() expected to be 0, not " +\r
+                                       getRightPredicateList().size());\r
+                       }\r
+               }\r
+\r
+               return this;\r
+       }\r
+\r
+\r
+       /**\r
+        *  Some types of joins (e.g. outer joins) will return a different\r
+        *  number of rows than is predicted by optimizeIt() in JoinNode.\r
+        *  So, adjust this value now. This method does nothing for most\r
+        *  join types.\r
+        */\r
+       protected void adjustNumberOfRowsReturned(CostEstimate costEstimate)\r
+       {\r
+       }\r
+\r
+       /**\r
+        * Return a ResultColumnList with all of the columns in this table.\r
+        * (Used in expanding '*'s.)\r
+        * NOTE: Since this method is for expanding a "*" in the SELECT list,\r
+        * ResultColumn.expression will be a ColumnReference.\r
+        *\r
+        * @param allTableName          The qualifier on the "*"\r
+        *\r
+        * @return ResultColumnList     List of result columns from this table.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public ResultColumnList getAllResultColumns(TableName allTableName)\r
+                       throws StandardException\r
+       {\r
+               /* We need special processing when there is a USING clause.\r
+                * The resulting table will be the join columns from\r
+                * the outer table followed by the non-join columns from \r
+                * left side plus the non-join columns from the right side.\r
+                */\r
+               if (usingClause == null)\r
+               {\r
+                       return getAllResultColumnsNoUsing(allTableName);\r
+               }\r
+\r
+               /* Get the logical left side of the join.\r
+                * This is where the join columns come from.\r
+                * (For RIGHT OUTER JOIN, the left is the right\r
+                * and the right is the left and the JOIN is the NIOJ).\r
+                */\r
+               ResultSetNode   logicalLeftRS = getLogicalLeftResultSet();\r
+\r
+               // Get the join columns\r
+               ResultColumnList joinRCL = logicalLeftRS.getAllResultColumns(\r
+                                                                               null).\r
+                                                                                       getJoinColumns(usingClause);\r
+\r
+               // Get the left and right RCLs\r
+               ResultColumnList leftRCL = leftResultSet.getAllResultColumns(allTableName); \r
+               ResultColumnList rightRCL = rightResultSet.getAllResultColumns(allTableName); \r
+\r
+               /* Chop the join columns out of the both left and right.\r
+                * Thanks to the ANSI committee, the join columns \r
+                * do not belong to either table.\r
+                */\r
+               if (leftRCL != null)\r
+               {\r
+                       leftRCL.removeJoinColumns(usingClause);\r
+               }\r
+               if (rightRCL != null)\r
+               {\r
+                       rightRCL.removeJoinColumns(usingClause);\r
+               }\r
+\r
+               /* If allTableName is null, then we want to return the splicing\r
+                * of the join columns followed by the non-join columns from\r
+                * the left followed by the non-join columns from the right.\r
+                * If not, then at most 1 side should match.\r
+                * NOTE: We need to make sure that the RC's VirtualColumnIds\r
+                * are correct (1 .. size).\r
+                */\r
+               if (leftRCL == null)\r
+               {\r
+                       rightRCL.resetVirtualColumnIds();\r
+                       return rightRCL;\r
+               }\r
+               else if (rightRCL == null)\r
+               {\r
+                       leftRCL.resetVirtualColumnIds();\r
+                       return leftRCL;\r
+               }\r
+               else\r
+               {\r
+                       /* Both sides are non-null.  This should only happen\r
+                        * if allTableName is null.\r
+                        */\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (allTableName != null)\r
+                               {\r
+                                       SanityManager.THROWASSERT(\r
+                                               "allTableName (" + allTableName + \r
+                                               ") expected to be null");\r
+                               }\r
+                       }\r
+                       joinRCL.destructiveAppend(leftRCL);\r
+                       joinRCL.destructiveAppend(rightRCL);\r
+                       joinRCL.resetVirtualColumnIds();\r
+                       return joinRCL;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Return a ResultColumnList with all of the columns in this table.\r
+        * (Used in expanding '*'s.)\r
+        * NOTE: Since this method is for expanding a "*" in the SELECT list,\r
+        * ResultColumn.expression will be a ColumnReference.\r
+        * NOTE: This method is handles the case when there is no USING clause.\r
+        * The caller handles the case when there is a USING clause.\r
+        *\r
+        * @param allTableName          The qualifier on the "*"\r
+        *\r
+        * @return ResultColumnList     List of result columns from this table.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       private ResultColumnList getAllResultColumnsNoUsing(TableName allTableName)\r
+                       throws StandardException\r
+       {\r
+               ResultColumnList leftRCL = leftResultSet.getAllResultColumns(allTableName); \r
+               ResultColumnList rightRCL = rightResultSet.getAllResultColumns(allTableName); \r
+               /* If allTableName is null, then we want to return the spliced\r
+                * left and right RCLs.  If not, then at most 1 side should match.\r
+                */\r
+               if (leftRCL == null)\r
+               {\r
+                       return rightRCL;\r
+               }\r
+               else if (rightRCL == null)\r
+               {\r
+                       return leftRCL;\r
+               }\r
+               else\r
+               {\r
+                       /* Both sides are non-null.  This should only happen\r
+                        * if allTableName is null.\r
+                        */\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (allTableName != null)\r
+                               {\r
+                                       SanityManager.THROWASSERT(\r
+                                               "allTableName (" + allTableName + \r
+                                               ") expected to be null");\r
+                               }\r
+                       }\r
+\r
+                       // Return a spliced copy of the 2 lists\r
+                       ResultColumnList tempList =\r
+                                                               (ResultColumnList) getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                                               getContextManager());\r
+                       tempList.nondestructiveAppend(leftRCL);\r
+                       tempList.nondestructiveAppend(rightRCL);\r
+                       return tempList;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Try to find a ResultColumn in the table represented by this FromTable\r
+        * that matches the name in the given ColumnReference.\r
+        *\r
+        * @param columnReference       The columnReference whose name we're looking\r
+        *                              for in the given table.\r
+        *\r
+        * @return      A ResultColumn whose expression is the ColumnNode\r
+        *                      that matches the ColumnReference.\r
+        *              Returns null if there is no match.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultColumn getMatchingColumn(ColumnReference columnReference) throws StandardException\r
+       {\r
+               /* Get the logical left and right sides of the join.\r
+                * (For RIGHT OUTER JOIN, the left is the right\r
+                * and the right is the left and the JOIN is the NIOJ).\r
+                */\r
+               ResultSetNode   logicalLeftRS = getLogicalLeftResultSet();\r
+               ResultSetNode   logicalRightRS = getLogicalRightResultSet();\r
+               ResultColumn    leftRC = null;\r
+               ResultColumn    resultColumn = null;\r
+               ResultColumn    rightRC = null;\r
+               ResultColumn    usingRC = null;\r
+\r
+               leftRC = logicalLeftRS.getMatchingColumn(columnReference);\r
+\r
+               if (leftRC != null)\r
+               {\r
+                       resultColumn = leftRC;\r
+\r
+                       /* Find out if the column is in the using clause */\r
+                       if (usingClause != null)\r
+                       {\r
+                               usingRC = usingClause.getResultColumn(leftRC.getName());\r
+                       }\r
+               }\r
+\r
+               /* We only search on the right if the column isn't in the\r
+                * USING clause.\r
+                */\r
+               if (usingRC == null)\r
+               {\r
+                       rightRC = logicalRightRS.getMatchingColumn(columnReference);\r
+               }\r
+\r
+               if (rightRC != null)\r
+               {\r
+                       /* We must catch ambiguous column references for joins here,\r
+                        * since FromList only checks for ambiguous references between\r
+                        * nodes, not within a node.\r
+                        */\r
+                       if (leftRC != null)\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_AMBIGUOUS_COLUMN_NAME, \r
+                                                columnReference.getSQLColumnName());\r
+                       }\r
+                       resultColumn = rightRC;\r
+               }\r
+\r
+               /* Insert will bind the underlying result sets which have\r
+                * tables twice. On the 2nd bind, resultColumns != null,\r
+                * we must return the RC from the JoinNode's RCL which is above\r
+                * the RC that we just found above.  (Otherwise, the source\r
+                * for the ColumnReference will be from the wrong ResultSet\r
+                * at generate().)\r
+                */\r
+               if (resultColumns != null)\r
+               {\r
+                       int rclSize = resultColumns.size();\r
+                       for (int index = 0; index < rclSize; index++)\r
+                       {\r
+                               ResultColumn rc = (ResultColumn) resultColumns.elementAt(index);\r
+                               VirtualColumnNode vcn = (VirtualColumnNode) rc.getExpression();\r
+                               if (resultColumn == vcn.getSourceColumn())\r
+                               {\r
+                                       resultColumn = rc;\r
+                                       break;\r
+                               }\r
+                       }\r
+               }\r
+\r
+               return resultColumn;\r
+       }\r
+\r
+       /**\r
+        * Bind the result columns of this ResultSetNode when there is no\r
+        * base table to bind them to.  This is useful for SELECT statements,\r
+        * where the result columns get their types from the expressions that\r
+        * live under them.\r
+        *\r
+        * @param fromListParam         FromList to use/append to.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void bindResultColumns(FromList fromListParam)\r
+                                       throws StandardException\r
+       {\r
+               super.bindResultColumns(fromListParam);\r
+\r
+               /* Now we build our RCL */\r
+               buildRCL();\r
+\r
+               /* We cannot bind the join clause until after we've bound our\r
+                * result columns. This is because the resultColumns from the\r
+                * children are propagated and merged to create our resultColumns\r
+                * in super.bindRCs().  If we bind the join clause prior to that\r
+                * call, then the ColumnReferences in the join clause will point\r
+                * to the children's RCLs at the time that they are bound, but\r
+                * will end up pointing above themselves, to our resultColumns,\r
+                * after the call to super.bindRCS().\r
+                */\r
+               deferredBindExpressions(fromListParam);\r
+       }\r
+\r
+       /**\r
+        * Bind the result columns for this ResultSetNode to a base table.\r
+        * This is useful for INSERT and UPDATE statements, where the\r
+        * result columns get their types from the table being updated or\r
+        * inserted into.\r
+        * If a result column list is specified, then the verification that the \r
+        * result column list does not contain any duplicates will be done when\r
+        * binding them by name.\r
+        *\r
+        * @param targetTableDescriptor The TableDescriptor for the table being\r
+        *                              updated or inserted into\r
+        * @param targetColumnList      For INSERT statements, the user\r
+        *                                      does not have to supply column\r
+        *                                      names (for example, "insert into t\r
+        *                                      values (1,2,3)".  When this\r
+        *                                      parameter is null, it means that\r
+        *                                      the user did not supply column\r
+        *                                      names, and so the binding should\r
+        *                                      be done based on order.  When it\r
+        *                                      is not null, it means do the binding\r
+        *                                      by name, not position.\r
+        * @param statement                     Calling DMLStatementNode (Insert or Update)\r
+        * @param fromListParam         FromList to use/append to.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public void bindResultColumns(TableDescriptor targetTableDescriptor,\r
+                                       FromVTI targetVTI,\r
+                                       ResultColumnList targetColumnList,\r
+                                       DMLStatementNode statement,\r
+                                       FromList fromListParam)\r
+                               throws StandardException\r
+       {\r
+               super.bindResultColumns(targetTableDescriptor,\r
+                                                               targetVTI,\r
+                                                               targetColumnList, statement, \r
+                                                               fromListParam);\r
+\r
+               /* Now we build our RCL */\r
+               buildRCL();\r
+\r
+               /* We cannot bind the join clause until after we've bound our\r
+                * result columns. This is because the resultColumns from the\r
+                * children are propagated and merged to create our resultColumns\r
+                * in super.bindRCs().  If we bind the join clause prior to that\r
+                * call, then the ColumnReferences in the join clause will point\r
+                * to the children's RCLs at the time that they are bound, but\r
+                * will end up pointing above themselves, to our resultColumns,\r
+                * after the call to super.bindRCS().\r
+                */\r
+               deferredBindExpressions(fromListParam);\r
+       }\r
+\r
+       /**\r
+        * Build the RCL for this node.  We propagate the RCLs up from the\r
+        * children and splice them to form this node's RCL.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       private void buildRCL() throws StandardException\r
+       {\r
+               /* NOTE - we only need to build this list if it does not already \r
+                * exist.  This can happen in the degenerate case of an insert\r
+                * select with a join expression in a derived table within the select.\r
+                */\r
+               if (resultColumns != null)\r
+               {\r
+                       return;\r
+               }\r
+\r
+               ResultColumnList leftRCL;\r
+               ResultColumnList rightRCL;\r
+               ResultColumnList tmpRCL;\r
+\r
+               /* We get a shallow copy of the left's ResultColumnList and its \r
+                * ResultColumns.  (Copy maintains ResultColumn.expression for now.)\r
+                */\r
+               resultColumns = leftResultSet.getResultColumns();\r
+               leftRCL = resultColumns.copyListAndObjects();\r
+               leftResultSet.setResultColumns(leftRCL);\r
+\r
+               /* Replace ResultColumn.expression with new VirtualColumnNodes\r
+                * in the ProjectRestrictNode's ResultColumnList.  (VirtualColumnNodes include\r
+                * pointers to source ResultSetNode, this, and source ResultColumn.)\r
+                */\r
+               resultColumns.genVirtualColumnNodes(leftResultSet, leftRCL, false);\r
+\r
+               /*\r
+               ** If this is a right outer join, we can get nulls on the left side,\r
+               ** so change the types of the left result set to be nullable.\r
+               */\r
+               if (this instanceof HalfOuterJoinNode && ((HalfOuterJoinNode)this).isRightOuterJoin())\r
+               {\r
+                       resultColumns.setNullability(true);\r
+               }\r
+\r
+               /* Now, repeat the process with the right's RCL */\r
+               tmpRCL = rightResultSet.getResultColumns();\r
+               rightRCL = tmpRCL.copyListAndObjects();\r
+               rightResultSet.setResultColumns(rightRCL);\r
+\r
+               /* Replace ResultColumn.expression with new VirtualColumnNodes\r
+                * in the ProjectRestrictNode's ResultColumnList.  (VirtualColumnNodes include\r
+                * pointers to source ResultSetNode, this, and source ResultColumn.)\r
+                */\r
+               tmpRCL.genVirtualColumnNodes(rightResultSet, rightRCL, false);\r
+               tmpRCL.adjustVirtualColumnIds(resultColumns.size());\r
+\r
+               /*\r
+               ** If this is a left outer join, we can get nulls on the right side,\r
+               ** so change the types of the right result set to be nullable.\r
+               */\r
+               if (this instanceof HalfOuterJoinNode && !((HalfOuterJoinNode)this).isRightOuterJoin())\r
+               {\r
+                       tmpRCL.setNullability(true);\r
+               }\r
+               \r
+               /* Now we append the propagated RCL from the right to the one from\r
+                * the left and call it our own.\r
+                */\r
+               resultColumns.nondestructiveAppend(tmpRCL);\r
+       }\r
+\r
+       private void deferredBindExpressions(FromList fromListParam)\r
+                               throws StandardException\r
+       {\r
+               /* Bind the expressions in the join clause */\r
+               subqueryList = (SubqueryList) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.SUBQUERY_LIST,\r
+                                                                                       getContextManager());\r
+               aggregateVector = new Vector();\r
+\r
+               /* ON clause */\r
+               if (joinClause != null)\r
+               {\r
+                       /* Create a new fromList with only left and right children before\r
+                        * binding the join clause. Valid column references in the join clause\r
+                        * are limited to columns from the 2 tables being joined. This\r
+                        * algorithm enforces that.\r
+                        */\r
+                       FromList        fromList = (FromList) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.FROM_LIST,\r
+                                                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                                                       getContextManager());\r
+                       fromList.addElement((FromTable) leftResultSet);\r
+                       fromList.addElement((FromTable) rightResultSet);\r
+\r
+                       /* First bind with all tables in the from clause, to detect ambiguous\r
+                        * references. Push the left and right children to the front of the\r
+                        * fromListParam before binding the join clause.  (We will\r
+                        * remove it before returning.)  Valid column references in\r
+                        * the join clause are limited to columns from the 2 tables being\r
+                        * joined\r
+                        */\r
+                       fromListParam.insertElementAt(rightResultSet, 0);\r
+                       fromListParam.insertElementAt(leftResultSet, 0);\r
+                       joinClause = joinClause.bindExpression(\r
+                                                                         fromListParam, subqueryList,\r
+                                                                         aggregateVector);\r
+\r
+                       /* Now bind with two tables being joined. If this raises column not found exception,\r
+                        * then we have a reference to other tables in the from clause. Raise invalid\r
+                        * ON clause error to match DB2.\r
+                        */\r
+                       try {\r
+                               joinClause = joinClause.bindExpression(\r
+                                                                         fromList, subqueryList,\r
+                                                                         aggregateVector);\r
+                       } catch (StandardException se) {\r
+                               if (se.getSQLState().equals(SQLState.LANG_COLUMN_NOT_FOUND))\r
+                                       throw StandardException.newException(SQLState.LANG_DB2_ON_CLAUSE_INVALID); \r
+                               throw se;\r
+                       }\r
+\r
+                       /* DB2 doesn't allow subquerries in the ON clause */\r
+                       if (subqueryList.size() > 0)\r
+                               throw StandardException.newException(SQLState.LANG_DB2_ON_CLAUSE_INVALID); \r
+                       /*\r
+                       ** We cannot have aggregates in the ON clause.\r
+                       ** In the future, if we relax this, we'll need\r
+                       ** to be able to pass the aggregateVector up\r
+                       ** the tree.\r
+                       */\r
+                       if (aggregateVector.size() > 0)\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_NO_AGGREGATES_IN_ON_CLAUSE);\r
+                       }\r
+\r
+                       fromListParam.removeElementAt(0);\r
+                       fromListParam.removeElementAt(0);\r
+               }\r
+               /* USING clause */\r
+               else if (usingClause != null)\r
+               {\r
+                       /* Build a join clause from the usingClause, using the\r
+                        * exposed names in the left and right RSNs.\r
+                        * For each column in the list, we generate 2 ColumnReferences,\r
+                        * 1 for the left and 1 for the right.  We bind each of these\r
+                        * to the appropriate side and build an equality predicate\r
+                        * between the 2.  We bind the = and AND nodes by hand because\r
+                        * we have to bind the ColumnReferences a side at a time.\r
+                        * We need to bind the CRs a side at a time to ensure that\r
+                        * we don't find an bogus ambiguous column reference. (Bug 377)\r
+                        */\r
+                       joinClause = (AndNode) getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.AND_NODE,\r
+                                                                                               null,\r
+                                                                                               null,\r
+                                                                                               getContextManager());\r
+                       AndNode currAnd = (AndNode) joinClause;\r
+                       ValueNode trueNode = (ValueNode) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.BOOLEAN_CONSTANT_NODE,\r
+                                                                                       Boolean.TRUE,\r
+                                                                                       getContextManager());\r
+\r
+                       int usingSize = usingClause.size();\r
+                       for (int index = 0; index < usingSize; index++)\r
+                       {\r
+                               BinaryComparisonOperatorNode equalsNode;\r
+                               ColumnReference leftCR;\r
+                               ColumnReference rightCR;\r
+                               ResultColumn    rc = (ResultColumn) usingClause.elementAt(index);\r
+\r
+                               /* currAnd starts as first point of insertion (leftOperand == null)\r
+                                * and becomes last point of insertion.\r
+                                */\r
+                               if (currAnd.getLeftOperand() != null)\r
+                               {\r
+                                       currAnd.setRightOperand(\r
+                                               (AndNode) getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.AND_NODE,\r
+                                                                                               null,\r
+                                                                                               null,\r
+                                                                                               getContextManager()));\r
+                                       currAnd = (AndNode) currAnd.getRightOperand();\r
+                               }\r
+\r
+                               /* Create and bind the left CR */\r
+                               fromListParam.insertElementAt(leftResultSet, 0);\r
+                               leftCR = (ColumnReference) getNodeFactory().getNode(\r
+                                                       C_NodeTypes.COLUMN_REFERENCE,\r
+                                                       rc.getName(),\r
+                                                       ((FromTable) leftResultSet).getTableName(),\r
+                                                       getContextManager()); \r
+                               leftCR = (ColumnReference) leftCR.bindExpression(\r
+                                                                         fromListParam, subqueryList,\r
+                                                                         aggregateVector);\r
+                               fromListParam.removeElementAt(0);\r
+\r
+                               /* Create and bind the right CR */\r
+                               fromListParam.insertElementAt(rightResultSet, 0);\r
+                               rightCR = (ColumnReference) getNodeFactory().getNode(\r
+                                                       C_NodeTypes.COLUMN_REFERENCE,\r
+                                                       rc.getName(),\r
+                                                       ((FromTable) rightResultSet).getTableName(),\r
+                                                       getContextManager()); \r
+                               rightCR = (ColumnReference) rightCR.bindExpression(\r
+                                                                         fromListParam, subqueryList,\r
+                                                                         aggregateVector);\r
+                               fromListParam.removeElementAt(0);\r
+\r
+                               /* Create and insert the new = condition */\r
+                               equalsNode = (BinaryComparisonOperatorNode)\r
+                                                               getNodeFactory().getNode(\r
+                                                                               C_NodeTypes.BINARY_EQUALS_OPERATOR_NODE,\r
+                                                                               leftCR,\r
+                                                                               rightCR,\r
+                                                                               getContextManager());\r
+                               equalsNode.bindComparisonOperator();\r
+\r
+                               currAnd.setLeftOperand(equalsNode);\r
+                               /* The right deep chain of AndNodes ends in a BinaryTrueNode.\r
+                                * NOTE: We set it for every AndNode, even though we will\r
+                                * overwrite it if this is not the last column in the list,\r
+                                * because postBindFixup() expects both the AndNode to have\r
+                                * both the left and right operands.\r
+                                */\r
+                               currAnd.setRightOperand(trueNode);\r
+                               currAnd.postBindFixup();\r
+                        }\r
+               }\r
+\r
+               if (joinClause != null)\r
+               {\r
+                       /* If joinClause is a parameter, (where ?), then we assume\r
+                        * it will be a nullable boolean.\r
+                        */\r
+                       if (joinClause.requiresTypeFromContext())\r
+                       {\r
+                               joinClause.setType(new DataTypeDescriptor(TypeId.BOOLEAN_ID, true));\r
+                       }\r
+                       \r
+                       /*\r
+                       ** Is the datatype of the JOIN clause BOOLEAN?\r
+                       **\r
+                       ** NOTE: This test is not necessary in SQL92 entry level, because\r
+                       ** it is syntactically impossible to have a non-Boolean JOIN clause\r
+                       ** in that level of the standard.  But we intend to extend the\r
+                       ** language to allow Boolean user functions in the JOIN clause,\r
+                       ** so we need to test for the error condition.\r
+                       */\r
+                       TypeId joinTypeId = joinClause.getTypeId();\r
+\r
+                       /* If the where clause is not a built-in type, then generate a bound \r
+                        * conversion tree to a built-in type.\r
+                        */\r
+                       if (joinTypeId.userType())\r
+                       {\r
+                               joinClause = joinClause.genSQLJavaSQLTree();\r
+                       }\r
+\r
+                       if (! joinClause.getTypeServices().getTypeId().equals(\r
+                                       TypeId.BOOLEAN_ID))\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_NON_BOOLEAN_JOIN_CLAUSE, \r
+                                               joinClause.getTypeServices().getTypeId().getSQLTypeName()\r
+                                               );\r
+                       }\r
+               }\r
+       }\r
+\r
+\r
+       /** \r
+        * Put a ProjectRestrictNode on top of each FromTable in the FromList.\r
+        * ColumnReferences must continue to point to the same ResultColumn, so\r
+        * that ResultColumn must percolate up to the new PRN.  However,\r
+        * that ResultColumn will point to a new expression, a VirtualColumnNode, \r
+        * which points to the FromTable and the ResultColumn that is the source for\r
+        * the ColumnReference.  \r
+        * (The new PRN will have the original of the ResultColumnList and\r
+        * the ResultColumns from that list.  The FromTable will get shallow copies\r
+        * of the ResultColumnList and its ResultColumns.  ResultColumn.expression\r
+        * will remain at the FromTable, with the PRN getting a new \r
+        * VirtualColumnNode for each ResultColumn.expression.)\r
+        * We then project out the non-referenced columns.  If there are no referenced\r
+        * columns, then the PRN's ResultColumnList will consist of a single ResultColumn\r
+        * whose expression is 1.\r
+        *\r
+        * @param numTables                     Number of tables in the DML Statement\r
+        * @param gbl                           The group by list, if any\r
+        * @param fromList                      The from list, if any\r
+        *\r
+        * @return The generated ProjectRestrictNode atop the original FromTable.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public ResultSetNode preprocess(int numTables,\r
+                                                                       GroupByList gbl,\r
+                                                                       FromList fromList)\r
+                                                               throws StandardException\r
+       {\r
+               ResultSetNode newTreeTop;\r
+               \r
+               newTreeTop = super.preprocess(numTables, gbl, fromList);\r
+\r
+               /* Put the expression trees in conjunctive normal form.\r
+                * NOTE - This needs to occur before we preprocess the subqueries\r
+                * because the subquery transformations assume that any subquery operator \r
+                * negation has already occurred.\r
+                */\r
+               if (joinClause != null)\r
+               {\r
+                       normExpressions();\r
+\r
+                       /* Preprocess any subqueries in the join clause */\r
+                       if (subqueryList != null)\r
+                       {\r
+                               /* RESOLVE - In order to flatten a subquery in\r
+                                * the ON clause of an inner join we'd have to pass\r
+                                * the various lists from the outer select through to\r
+                                * ResultSetNode.preprocess() and overload\r
+                                * normExpressions in HalfOuterJoinNode.  That's not\r
+                                * worth the effort, so we say that the ON clause\r
+                                * is not under a top level AND in normExpressions()\r
+                                * to ensure that subqueries in the ON clause do not\r
+                                * get flattened.  That allows us to pass empty lists\r
+                                * to joinClause.preprocess() because we know that no\r
+                                * flattening will take place. (Bug #1206)\r
+                                */\r
+                               joinClause.preprocess(\r
+                                                               numTables,\r
+                                                               (FromList) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.FROM_LIST,\r
+                                                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                                                       getContextManager()),\r
+                                                               (SubqueryList) getNodeFactory().getNode(\r
+                                                                                                       C_NodeTypes.SUBQUERY_LIST,\r
+                                                                                                       getContextManager()),\r
+                                                               (PredicateList) getNodeFactory().getNode(\r
+                                                                                                       C_NodeTypes.PREDICATE_LIST,\r
+                                                                                                       getContextManager()));\r
+                       }\r
+\r
+                       /* Pull apart the expression trees */\r
+                       joinPredicates.pullExpressions(numTables, joinClause);\r
+                       joinPredicates.categorize();\r
+                       joinClause = null;\r
+               }\r
+\r
+               return newTreeTop;\r
+       }\r
+\r
+    /**\r
+     * Find the unreferenced result columns and project them out. This is used in pre-processing joins\r
+     * that are not flattened into the where clause.\r
+     */\r
+    void projectResultColumns() throws StandardException\r
+    {\r
+        leftResultSet.projectResultColumns();\r
+        rightResultSet.projectResultColumns();\r
+        resultColumns.pullVirtualIsReferenced();\r
+        super.projectResultColumns();\r
+    }\r
+    \r
+       /** Put the expression trees in conjunctive normal form \r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void normExpressions()\r
+                               throws StandardException\r
+       {\r
+               if (joinClauseNormalized == true) return;\r
+\r
+               /* For each expression tree:\r
+                *      o Eliminate NOTs (eliminateNots())\r
+                *      o Ensure that there is an AndNode on top of every\r
+                *        top level expression. (putAndsOnTop())\r
+                *      o Finish the job (changeToCNF())\r
+                */\r
+               joinClause = joinClause.eliminateNots(false);\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (!(joinClause.verifyEliminateNots()) )\r
+                       {\r
+                               joinClause.treePrint();\r
+                               SanityManager.THROWASSERT(\r
+                                       "joinClause in invalid form: " + joinClause); \r
+                       }\r
+               }\r
+               joinClause = joinClause.putAndsOnTop();\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (! ((joinClause instanceof AndNode) &&\r
+                                  (joinClause.verifyPutAndsOnTop())) )\r
+                       {\r
+                               joinClause.treePrint();\r
+                               SanityManager.THROWASSERT(\r
+                                       "joinClause in invalid form: " + joinClause); \r
+                       }\r
+               }\r
+               /* RESOLVE - ON clause is temporarily "not under a top\r
+                * top level AND" until we figure out how to deal with\r
+                * subqueries in the ON clause. (Bug 1206)\r
+                */\r
+               joinClause = joinClause.changeToCNF(false);\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (! ((joinClause instanceof AndNode) &&\r
+                                  (joinClause.verifyChangeToCNF())) )\r
+                       {\r
+                               joinClause.treePrint();\r
+                               SanityManager.THROWASSERT(\r
+                                       "joinClause in invalid form: " + joinClause); \r
+                       }\r
+               }\r
+\r
+               joinClauseNormalized = true;\r
+       }\r
+\r
+       /**\r
+        * Push expressions down to the first ResultSetNode which can do expression\r
+        * evaluation and has the same referenced table map.\r
+        * RESOLVE - This means only pushing down single table expressions to\r
+        * DistinctNodes today.  Once we have a better understanding of how\r
+        * the optimizer will work, we can push down join clauses.\r
+        *\r
+        * @param outerPredicateList    The PredicateList from the outer RS.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void pushExpressions(PredicateList outerPredicateList)\r
+                                       throws StandardException\r
+       {\r
+               FromTable               leftFromTable = (FromTable) leftResultSet;\r
+               FromTable               rightFromTable = (FromTable) rightResultSet;\r
+\r
+               /* OuterJoinNodes are responsible for overriding this\r
+                * method since they have different rules about where predicates\r
+                * can be applied.\r
+                */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (this instanceof HalfOuterJoinNode)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "JN.pushExpressions() not expected to be called for " +\r
+                                       getClass().getName());\r
+                       }\r
+               }\r
+\r
+               /* We try to push "pushable" \r
+                * predicates to 1 of 3 places:\r
+                *      o Predicates that only reference tables\r
+                *        on the left are pushed to the leftPredicateList.\r
+                *      o Predicates that only reference tables\r
+                *        on the right are pushed to the rightPredicateList.\r
+                *      o Predicates which reference tables on both\r
+                *        sides (and no others) are pushed to \r
+                *        the joinPredicates and may be pushed down\r
+                *        further during optimization.\r
+                */\r
+               // Left only\r
+               pushExpressionsToLeft(outerPredicateList);\r
+               leftFromTable.pushExpressions(getLeftPredicateList());\r
+               // Right only\r
+               pushExpressionsToRight(outerPredicateList);\r
+               rightFromTable.pushExpressions(getRightPredicateList());\r
+               // Join predicates\r
+               grabJoinPredicates(outerPredicateList);\r
+\r
+               /* By the time we're done here, both the left and right\r
+                * predicate lists should be empty because we pushed everything\r
+                * down.\r
+                */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (getLeftPredicateList().size() != 0)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "getLeftPredicateList().size() expected to be 0, not " +\r
+                                       getLeftPredicateList().size());\r
+                       }\r
+                       if (getRightPredicateList().size() != 0)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "getRightPredicateList().size() expected to be 0, not " +\r
+                                       getRightPredicateList().size());\r
+                       }\r
+               }\r
+       }\r
+\r
+       protected void pushExpressionsToLeft(PredicateList outerPredicateList)\r
+               throws StandardException\r
+       {\r
+               FromTable               leftFromTable = (FromTable) leftResultSet;\r
+\r
+               JBitSet         leftReferencedTableMap = leftFromTable.getReferencedTableMap();\r
+\r
+               /* Build a list of the single table predicates on left result set\r
+                * that we can push down \r
+                */\r
+               // Walk outerPredicateList backwards due to possible deletes\r
+               for (int index = outerPredicateList.size() - 1; index >= 0; index --)\r
+               {\r
+                       JBitSet   curBitSet;\r
+                       Predicate predicate;\r
+\r
+                       predicate = (Predicate) outerPredicateList.elementAt(index);\r
+                       if (! predicate.getPushable())\r
+                       {\r
+                               continue;\r
+                       }\r
+\r
+                       curBitSet = predicate.getReferencedSet();\r
+                       \r
+                       /* Do we have a match? */\r
+                       if (leftReferencedTableMap.contains(curBitSet))\r
+                       {\r
+                               /* Add the matching predicate to the push list */\r
+                               getLeftPredicateList().addPredicate(predicate);\r
+\r
+                               /* Remap all of the ColumnReferences to point to the\r
+                                * source of the values.\r
+                                * The tree is something like:\r
+                                *                      PRN1\r
+                                *                        |\r
+                                *                       JN (this)\r
+                                *                 /    \\r
+                                *              PRN2    PRN3\r
+                                *        |       |\r
+                                *              FBT1    FBT2\r
+                                *\r
+                                * The ColumnReferences start off pointing to the RCL off of\r
+                                * PRN1.  For optimization, we want them to point to the\r
+                                * RCL off of PRN2.  In order to do that, we remap them\r
+                                * twice here.  If optimization pushes them down to the\r
+                                * base table, it will remap them again.\r
+                                */\r
+                               RemapCRsVisitor rcrv = new RemapCRsVisitor(true);\r
+                               predicate.getAndNode().accept(rcrv);\r
+                               predicate.getAndNode().accept(rcrv);\r
+\r
+                               /* Remove the matching predicate from the outer list */\r
+                               outerPredicateList.removeElementAt(index);\r
+                       }\r
+               }\r
+       }\r
+\r
+       private void pushExpressionsToRight(PredicateList outerPredicateList)\r
+               throws StandardException\r
+       {\r
+               FromTable               rightFromTable = (FromTable) rightResultSet;\r
+\r
+               JBitSet         rightReferencedTableMap = rightFromTable.getReferencedTableMap();\r
+\r
+               /* Build a list of the single table predicates on right result set\r
+                * that we can push down \r
+                */\r
+               // Walk outerPredicateList backwards due to possible deletes\r
+               for (int index = outerPredicateList.size() - 1; index >= 0; index --)\r
+               {\r
+                       JBitSet   curBitSet;\r
+                       Predicate predicate;\r
+\r
+                       predicate = (Predicate) outerPredicateList.elementAt(index);\r
+                       if (! predicate.getPushable())\r
+                       {\r
+                               continue;\r
+                       }\r
+\r
+                       curBitSet = predicate.getReferencedSet();\r
+                       \r
+                       /* Do we have a match? */\r
+                       if (rightReferencedTableMap.contains(curBitSet))\r
+                       {\r
+                               /* Add the matching predicate to the push list */\r
+                               getRightPredicateList().addPredicate(predicate);\r
+\r
+                               /* Remap all of the ColumnReferences to point to the\r
+                                * source of the values.\r
+                                * The tree is something like:\r
+                                *                      PRN1\r
+                                *                        |\r
+                                *                       JN (this)\r
+                                *                 /    \\r
+                                *              PRN2    PRN3\r
+                                *        |       |\r
+                                *              FBT1    FBT2\r
+                                *\r
+                                * The ColumnReferences start off pointing to the RCL off of\r
+                                * PRN1.  For optimization, we want them to point to the\r
+                                * RCL off of PRN3.  In order to do that, we remap them\r
+                                * twice here.  If optimization pushes them down to the\r
+                                * base table, it will remap them again.\r
+                                */\r
+                               RemapCRsVisitor rcrv = new RemapCRsVisitor(true);\r
+                               predicate.getAndNode().accept(rcrv);\r
+                               predicate.getAndNode().accept(rcrv);\r
+\r
+                               /* Remove the matching predicate from the outer list */\r
+                               outerPredicateList.removeElementAt(index);\r
+                       }\r
+               }\r
+       }\r
+\r
+       private void grabJoinPredicates(PredicateList outerPredicateList)\r
+               throws StandardException\r
+       {\r
+               FromTable               leftFromTable = (FromTable) leftResultSet;\r
+               FromTable               rightFromTable = (FromTable) rightResultSet;\r
+\r
+               JBitSet         leftReferencedTableMap = leftFromTable.getReferencedTableMap();\r
+               JBitSet         rightReferencedTableMap = rightFromTable.getReferencedTableMap();\r
+\r
+               /* Build a list of the join predicates that we can push down */\r
+               // Walk outerPredicateList backwards due to possible deletes\r
+               for (int index = outerPredicateList.size() - 1; index >= 0; index --)\r
+               {\r
+                       JBitSet   curBitSet;\r
+                       Predicate predicate;\r
+\r
+                       predicate = (Predicate) outerPredicateList.elementAt(index);\r
+                       if (! predicate.getPushable())\r
+                       {\r
+                               continue;\r
+                       }\r
+\r
+                       curBitSet = predicate.getReferencedSet();\r
+                       \r
+                       /* Do we have a match? */\r
+                       JBitSet innerBitSet = (JBitSet) rightReferencedTableMap.clone();\r
+                       innerBitSet.or(leftReferencedTableMap);\r
+                       if (innerBitSet.contains(curBitSet))\r
+                       {\r
+                               /* Add the matching predicate to the push list */\r
+                               joinPredicates.addPredicate(predicate);\r
+\r
+                               /* Remap all of the ColumnReferences to point to the\r
+                                * source of the values.\r
+                                * The tree is something like:\r
+                                *                      PRN1\r
+                                *                        |\r
+                                *                       JN (this)\r
+                                *                 /    \\r
+                                *              PRN2    PRN3\r
+                                *        |       |\r
+                                *              FBT1    FBT2\r
+                                *\r
+                                * The ColumnReferences start off pointing to the RCL off of\r
+                                * PRN1.  For optimization, we want them to point to the\r
+                                * RCL off of PRN2 or PRN3.  In order to do that, we remap them\r
+                                * twice here.  If optimization pushes them down to the\r
+                                * base table, it will remap them again.\r
+                                */\r
+                               RemapCRsVisitor rcrv = new RemapCRsVisitor(true);\r
+                               predicate.getAndNode().accept(rcrv);\r
+                               predicate.getAndNode().accept(rcrv);\r
+\r
+                               /* Remove the matching predicate from the outer list */\r
+                               outerPredicateList.removeElementAt(index);\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Flatten this JoinNode into the outer query block. The steps in\r
+        * flattening are:\r
+        *      o  Mark all ResultColumns as redundant, so that they are "skipped over"\r
+        *         at generate().\r
+        *      o  Append the joinPredicates to the outer list.\r
+        *      o  Create a FromList from the tables being joined and return \r
+        *         that list so that the caller will merge the 2 lists \r
+        *\r
+        * @param rcl                           The RCL from the outer query\r
+        * @param outerPList            PredicateList to append wherePredicates to.\r
+        * @param sql                           The SubqueryList from the outer query\r
+        * @param gbl                           The group by list, if any\r
+        *\r
+        * @return FromList             The fromList from the underlying SelectNode.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public FromList flatten(ResultColumnList rcl,\r
+                                                       PredicateList outerPList,\r
+                                                       SubqueryList sql,\r
+                                                       GroupByList gbl)\r
+\r
+                       throws StandardException\r
+       {\r
+               /* OuterJoinNodes should never get here.\r
+                * (They can be transformed, but never\r
+                * flattened directly.)\r
+                */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (this instanceof HalfOuterJoinNode)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "JN.flatten() not expected to be called for " +\r
+                                       getClass().getName());\r
+                       }\r
+               }\r
+\r
+               /* Build a new FromList composed of left and right children \r
+                * NOTE: We must call FL.addElement() instead of FL.addFromTable()\r
+                * since there is no exposed name. (And even if there was,\r
+                * we could care less about unique exposed name checking here.)\r
+                */\r
+               FromList        fromList = (FromList) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.FROM_LIST,\r
+                                                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                                                       getContextManager());\r
+               fromList.addElement((FromTable) leftResultSet);\r
+               fromList.addElement((FromTable) rightResultSet);\r
+\r
+               /* Mark our RCL as redundant */\r
+               resultColumns.setRedundant();\r
+\r
+               /* Remap all ColumnReferences from the outer query to this node.\r
+                * (We replace those ColumnReferences with clones of the matching\r
+                * expression in the left and right's RCL.\r
+                */\r
+               rcl.remapColumnReferencesToExpressions();\r
+               outerPList.remapColumnReferencesToExpressions();\r
+               if (gbl != null)\r
+               {\r
+                       gbl.remapColumnReferencesToExpressions();\r
+               }\r
+\r
+               if (joinPredicates.size() > 0)\r
+               {\r
+                       outerPList.destructiveAppend(joinPredicates);\r
+               }\r
+\r
+               if (subqueryList != null && subqueryList.size() > 0)\r
+               {\r
+                       sql.destructiveAppend(subqueryList);\r
+               }\r
+\r
+               return fromList;\r
+       }\r
+\r
+       /**\r
+        * Currently we don't reordering any outer join w/ inner joins.\r
+        */\r
+       public boolean LOJ_reorderable(int numTables)\r
+                               throws StandardException\r
+       {\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Transform any Outer Join into an Inner Join where applicable.\r
+        * (Based on the existence of a null intolerant\r
+        * predicate on the inner table.)\r
+        *\r
+        * @param predicateTree The predicate tree for the query block\r
+        *\r
+        * @return The new tree top (OuterJoin or InnerJoin).\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public FromTable transformOuterJoins(ValueNode predicateTree, int numTables)\r
+               throws StandardException\r
+       {\r
+               /* Can't flatten if no predicates in where clause. */\r
+               if (predicateTree == null)\r
+               {\r
+                       return this;\r
+               }\r
+\r
+               /* See if left or right sides can be transformed */\r
+               leftResultSet = ((FromTable) leftResultSet).transformOuterJoins(predicateTree, numTables);\r
+               rightResultSet = ((FromTable) rightResultSet).transformOuterJoins(predicateTree, numTables);\r
+\r
+               return this;\r
+       }\r
+\r
+    /**\r
+     * For joins, the tree will be (nodes are left out if the clauses\r
+     * are empty):\r
+     *\r
+     *      ProjectRestrictResultSet -- for the having and the select list\r
+     *      SortResultSet -- for the group by list\r
+     *      ProjectRestrictResultSet -- for the where and the select list (if no group or having)\r
+     *      the result set for the fromList\r
+     *\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+     */\r
+       public void generate(ActivationClassBuilder acb,\r
+                                                               MethodBuilder mb)\r
+                                                       throws StandardException\r
+       {\r
+               generateCore(acb, mb, INNERJOIN, null, null);\r
+       }\r
+\r
+    /**\r
+     * Generate the code for a qualified join node.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+     */\r
+       public void generateCore(ActivationClassBuilder acb,\r
+                                                                  MethodBuilder mb,\r
+                                                                  int joinType) \r
+                       throws StandardException\r
+       {\r
+               generateCore(acb, mb, joinType, joinClause, subqueryList);\r
+       }\r
+\r
+       /**\r
+        * Do the generation work for the join node hierarchy.\r
+        *\r
+        * @param acb                   The ActivationClassBuilder\r
+        * @param mb the method the code is to go into\r
+        * @param joinType              The join type\r
+        * @param joinClause    The join clause, if any\r
+        * @param subquerys             The list of subqueries in the join clause, if any\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       protected void generateCore(ActivationClassBuilder acb,\r
+                                                                         MethodBuilder mb,\r
+                                                                         int joinType,\r
+                                                                         ValueNode joinClause,\r
+                                                                         SubqueryList subquerys)\r
+                       throws StandardException\r
+       {\r
+               /* Put the predicates back into the tree */\r
+               if (joinPredicates != null)\r
+               {\r
+                       joinClause = joinPredicates.restorePredicates();\r
+                       joinPredicates = null;\r
+               }\r
+\r
+               /* Get the next ResultSet #, so that we can number this ResultSetNode, its\r
+                * ResultColumnList and ResultSet.\r
+                */\r
+               assignResultSetNumber();\r
+\r
+               /* Set the point of attachment in all subqueries attached\r
+                * to this node.\r
+                */\r
+               if (subquerys != null && subquerys.size() > 0)\r
+               {\r
+                       subquerys.setPointOfAttachment(resultSetNumber);\r
+               }\r
+\r
+               // build up the tree.\r
+\r
+               /* Generate the JoinResultSet */\r
+               /* Nested loop and hash are the only join strategy currently supporteds.  \r
+                * Right outer joins are transformed into left outer joins.\r
+                */\r
+               String                  joinResultSetString;\r
+\r
+               if (joinType == LEFTOUTERJOIN)\r
+               {\r
+                       joinResultSetString = \r
+                               ((Optimizable) rightResultSet).getTrulyTheBestAccessPath().\r
+                                       getJoinStrategy().halfOuterJoinResultSetMethodName();\r
+               }\r
+               else\r
+               {\r
+                       joinResultSetString = \r
+                               ((Optimizable) rightResultSet).getTrulyTheBestAccessPath().\r
+                                       getJoinStrategy().joinResultSetMethodName();\r
+               }\r
+\r
+               acb.pushGetResultSetFactoryExpression(mb);\r
+               int nargs = getJoinArguments(acb, mb, joinClause);\r
+               mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, joinResultSetString, ClassName.NoPutResultSet, nargs);\r
+       }\r
+\r
+       /**\r
+        * Get the arguments to the join result set.\r
+        *\r
+        * @param acb   The ActivationClassBuilder for the class we're building.\r
+        * @param mb the method the generated code is going into\r
+        * @param joinClause    The join clause, if any\r
+        *\r
+        * @return      The array of arguments to the join result set\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       private int getJoinArguments(ActivationClassBuilder acb,\r
+                                                                                       MethodBuilder mb,\r
+                                                                                       ValueNode joinClause)\r
+                                                       throws StandardException\r
+       {\r
+               int numArgs = getNumJoinArguments();\r
+\r
+               leftResultSet.generate(acb, mb); // arg 1\r
+               mb.push(leftResultSet.resultColumns.size()); // arg 2\r
+               rightResultSet.generate(acb, mb); // arg 3\r
+               mb.push(rightResultSet.resultColumns.size()); // arg 4\r
+\r
+               // Get our final cost estimate based on child estimates.\r
+               costEstimate = getFinalCostEstimate();\r
+\r
+               // for the join clause, we generate an exprFun\r
+               // that evaluates the expression of the clause\r
+               // against the current row of the child's result.\r
+               // if the join clause is empty, we generate a function\r
+               // that just returns true. (Performance tradeoff: have\r
+               // this function for the empty join clause, or have\r
+               // all non-empty join clauses check for a null at runtime).\r
+\r
+               // generate the function and initializer:\r
+               // Note: Boolean lets us return nulls (boolean would not)\r
+               // private Boolean exprN()\r
+               // {\r
+               //   return <<joinClause.generate(ps)>>;\r
+               // }\r
+               // static Method exprN = method pointer to exprN;\r
+\r
+               // if there is no join clause, we just pass a null Expression.\r
+               if (joinClause == null)\r
+               {\r
+                       mb.pushNull(ClassName.GeneratedMethod); // arg 5\r
+               }\r
+               else\r
+               {\r
+                       // this sets up the method and the static field.\r
+                       // generates:\r
+                       //      Object userExprFun { }\r
+                       MethodBuilder userExprFun = acb.newUserExprFun();\r
+\r
+                       // join clause knows it is returning its value;\r
+\r
+                       /* generates:\r
+                        *    return <joinClause.generate(acb)>;\r
+                        * and adds it to userExprFun\r
+                        */\r
+                       joinClause.generate(acb, userExprFun);\r
+                       userExprFun.methodReturn();\r
+\r
+                       // we are done modifying userExprFun, complete it.\r
+                       userExprFun.complete();\r
+\r
+                       // join clause is used in the final result set as an access of the new static\r
+                       // field holding a reference to this new method.\r
+                       // generates:\r
+                       //      ActivationClass.userExprFun\r
+                       // which is the static field that "points" to the userExprFun\r
+                       // that evaluates the where clause.\r
+                       acb.pushMethodReference(mb, userExprFun); // arg 5\r
+               }\r
+\r
+               mb.push(resultSetNumber); // arg 6\r
+\r
+               addOuterJoinArguments(acb, mb);\r
+\r
+               // Does right side return a single row\r
+               oneRowRightSide(acb, mb);\r
+\r
+               // estimated row count\r
+               mb.push(costEstimate.rowCount());\r
+\r
+               // estimated cost\r
+               mb.push(costEstimate.getEstimatedCost());\r
+\r
+               //User may have supplied optimizer overrides in the sql\r
+               //Pass them onto execute phase so it can be shown in \r
+               //run time statistics.\r
+               if (joinOrderStrategyProperties != null)\r
+                       mb.push(PropertyUtil.sortProperties(joinOrderStrategyProperties));\r
+               else\r
+                       mb.pushNull("java.lang.String");\r
+\r
+               return numArgs;\r
+\r
+       }\r
+\r
+       /**\r
+        * @see ResultSetNode#getFinalCostEstimate\r
+        *\r
+        * Get the final CostEstimate for this JoinNode.\r
+        *\r
+        * @return      The final CostEstimate for this JoinNode, which is sum\r
+        *  the costs for the inner and outer table.  The number of rows,\r
+        *  though, is that for the inner table only.\r
+        */\r
+       public CostEstimate getFinalCostEstimate()\r
+               throws StandardException\r
+       {\r
+               // If we already found it, just return it.\r
+               if (finalCostEstimate != null)\r
+                       return finalCostEstimate;\r
+\r
+               CostEstimate leftCE = leftResultSet.getFinalCostEstimate();\r
+               CostEstimate rightCE = rightResultSet.getFinalCostEstimate();\r
+\r
+               finalCostEstimate = getNewCostEstimate();\r
+               finalCostEstimate.setCost(\r
+                       leftCE.getEstimatedCost() + rightCE.getEstimatedCost(),\r
+                       rightCE.rowCount(),\r
+                       rightCE.rowCount());\r
+\r
+               return finalCostEstimate;\r
+       }\r
+\r
+       protected void oneRowRightSide(ActivationClassBuilder acb,\r
+                                                                          MethodBuilder mb)\r
+               throws StandardException\r
+       {\r
+               mb.push(rightResultSet.isOneRowResultSet());\r
+               mb.push(rightResultSet.isNotExists());  //join is for NOT EXISTS\r
+       }\r
+\r
+       /**\r
+        * Return the number of arguments to the join result set.  This will\r
+        * be overridden for other types of joins (for example, outer joins).\r
+        */\r
+       protected int getNumJoinArguments()\r
+       {\r
+               return 11;\r
+       }\r
+\r
+       /**\r
+        * Generate     and add any arguments specifict to outer joins.\r
+        * (Expected to be overriden, where appropriate, in subclasses.)\r
+        *\r
+        * @param acb           The ActivationClassBuilder\r
+        * @param mb the method  the generated code is to go into\r
+        *\r
+        * return The number of args added\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+        protected int addOuterJoinArguments(ActivationClassBuilder acb,\r
+                                                                                       MethodBuilder mb)\r
+                throws StandardException\r
+        {\r
+                return 0;\r
+        }\r
+\r
+       /** \r
+        * Convert the joinType to a string.\r
+        *\r
+        * @param joinType                      The joinType as an int.\r
+        *\r
+        * @return String               The joinType as a String.\r
+        */\r
+       public static String joinTypeToString(int joinType)\r
+       {\r
+               switch(joinType)\r
+               {\r
+                       case INNERJOIN:\r
+                               return "INNER JOIN";\r
+\r
+                       case CROSSJOIN:\r
+                               return "CROSS JOIN";\r
+\r
+                       case LEFTOUTERJOIN:\r
+                               return "LEFT OUTER JOIN";\r
+\r
+                       case RIGHTOUTERJOIN:\r
+                               return "RIGHT OUTER JOIN";\r
+\r
+                       case FULLOUTERJOIN:\r
+                               return "FULL OUTER JOIN";\r
+\r
+                       case UNIONJOIN:\r
+                               return "UNION JOIN";\r
+\r
+                       default:\r
+                               if (SanityManager.DEBUG)\r
+                               {\r
+                                       SanityManager.ASSERT(false,     "Unexpected joinType");\r
+                               }\r
+                               return null;\r
+               }\r
+       }\r
+\r
+       protected PredicateList getLeftPredicateList() throws StandardException\r
+       {\r
+               if (leftPredicateList == null)\r
+                       leftPredicateList = (PredicateList) getNodeFactory().getNode(\r
+                                                                                                       C_NodeTypes.PREDICATE_LIST,\r
+                                                                                                       getContextManager());\r
+\r
+               return leftPredicateList;\r
+       }\r
+\r
+       protected PredicateList getRightPredicateList() throws StandardException\r
+       {\r
+               if (rightPredicateList == null)\r
+                       rightPredicateList = (PredicateList) getNodeFactory().getNode(\r
+                                                                                                       C_NodeTypes.PREDICATE_LIST,\r
+                                                                                                       getContextManager());\r
+\r
+               return rightPredicateList;\r
+       }\r
+\r
+       /**\r
+        * Get the lock mode for the target of an update statement\r
+        * (a delete or update).  The update mode will always be row for\r
+        * CurrentOfNodes.  It will be table if there is no where clause.\r
+        *\r
+        * @return      The lock mode\r
+        */\r
+       public int updateTargetLockMode()\r
+       {\r
+               /* Always use row locking if we have a join node.\r
+                * We can only have a join node if there is a subquery that\r
+                * got flattened, hence there is a restriction.\r
+                */\r
+               return TransactionController.MODE_RECORD;\r
+       }\r
+\r
+       /**\r
+        * Mark this node and its children as not being a flattenable join.\r
+        */\r
+       void notFlattenableJoin()\r
+       {\r
+               flattenableJoin = false;\r
+               leftResultSet.notFlattenableJoin();\r
+               rightResultSet.notFlattenableJoin();\r
+       }\r
+\r
+       /**\r
+        * Is this FromTable a JoinNode which can be flattened into \r
+        * the parents FromList.\r
+        *\r
+        * @return boolean              Whether or not this FromTable can be flattened.\r
+        */\r
+       public boolean isFlattenableJoinNode()\r
+       {\r
+               return flattenableJoin;\r
+       }\r
+\r
+       /**\r
+        * Return whether or not the underlying ResultSet tree\r
+        * is ordered on the specified columns.\r
+        * RESOLVE - This method currently only considers the outermost table \r
+        * of the query block.\r
+        *\r
+        * @param       crs                                     The specified ColumnReference[]\r
+        * @param       permuteOrdering         Whether or not the order of the CRs in the array can be permuted\r
+        * @param       fbtVector                       Vector that is to be filled with the FromBaseTable      \r
+        *\r
+        * @return      Whether the underlying ResultSet tree\r
+        * is ordered on the specified column.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering, Vector fbtVector)\r
+                               throws StandardException\r
+       {\r
+               /* RESOLVE - easiest thing for now is to only consider the leftmost child */\r
+               return leftResultSet.isOrderedOn(crs, permuteOrdering, fbtVector);\r
+       }\r
+\r
+       /**\r
+        * Prints the sub-nodes of this object.  See QueryTreeNode.java for\r
+        * how tree printing is supposed to work.\r
+        *\r
+        * @param depth         The depth of this node in the tree\r
+        */\r
+\r
+       public void printSubNodes(int depth)\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       super.printSubNodes(depth);\r
+\r
+                       if (subqueryList != null)\r
+                       {\r
+                               printLabel(depth, "subqueryList: ");\r
+                               subqueryList.treePrint(depth + 1);\r
+                       }\r
+\r
+                       if (joinClause != null)\r
+                       {\r
+                               printLabel(depth, "joinClause: ");\r
+                               joinClause.treePrint(depth + 1);\r
+                       }\r
+\r
+                       if (joinPredicates.size() != 0)\r
+                       {\r
+                               printLabel(depth, "joinPredicates: ");\r
+                               joinPredicates.treePrint(depth + 1);\r
+                       }\r
+\r
+                       if (usingClause != null)\r
+                       {\r
+                               printLabel(depth, "usingClause: ");\r
+                               usingClause.treePrint(depth + 1);\r
+                       }\r
+               }\r
+       }\r
+\r
+       void setSubqueryList(SubqueryList subqueryList)\r
+       {\r
+               this.subqueryList = subqueryList;\r
+       }\r
+\r
+       void setAggregateVector(Vector aggregateVector)\r
+       {\r
+               this.aggregateVector = aggregateVector;\r
+       }\r
+\r
+       /**\r
+        * Return the logical left result set for this qualified\r
+        * join node.\r
+        * (For RIGHT OUTER JOIN, the left is the right\r
+        * and the right is the left and the JOIN is the NIOJ).\r
+        */\r
+       ResultSetNode getLogicalLeftResultSet()\r
+       {\r
+               return leftResultSet;\r
+       }\r
+\r
+       /**\r
+        * Return the logical right result set for this qualified\r
+        * join node.\r
+        * (For RIGHT OUTER JOIN, the left is the right\r
+        * and the right is the left and the JOIN is the NIOJ).\r
+        */\r
+       ResultSetNode getLogicalRightResultSet()\r
+       {\r
+               return rightResultSet;\r
+       }\r
+\r
+       /**\r
+        * Accept a visitor, and call v.visit()\r
+        * on child nodes as necessary.  \r
+        * \r
+        * @param v the visitor\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public Visitable accept(Visitor v) \r
+               throws StandardException\r
+       {\r
+               if (v.skipChildren(this))\r
+               {\r
+                       return v.visit(this);\r
+               }\r
+\r
+               Visitable returnNode = super.accept(v);\r
+\r
+               if (resultColumns != null && !v.stopTraversal())\r
+               {\r
+                       resultColumns = (ResultColumnList)resultColumns.accept(v);\r
+               }\r
+\r
+               if (joinClause != null && !v.stopTraversal())\r
+               {\r
+                       joinClause = (ValueNode)joinClause.accept(v);\r
+               }\r
+\r
+               if (usingClause != null && !v.stopTraversal())\r
+               {\r
+                       usingClause = (ResultColumnList)usingClause.accept(v);\r
+               }\r
+\r
+               return returnNode;\r
+       }\r
+\r
+       // This method returns the table references in Join node, and this may be\r
+       // needed for LOJ reordering.  For example, we may have the following query:\r
+       //       (T JOIN S) LOJ (X LOJ Y) \r
+    // The top most LOJ may be a join betw T and X and thus we can reorder the\r
+       // LOJs.  However, as of 10/2002, we don't reorder LOJ mixed with join.\r
+       public JBitSet LOJgetReferencedTables(int numTables)\r
+                               throws StandardException\r
+       {\r
+               JBitSet map = new JBitSet(numTables);\r
+\r
+               map = (JBitSet) leftResultSet.LOJgetReferencedTables(numTables);\r
+               if (map == null) return null;\r
+               else map.or((JBitSet) rightResultSet.LOJgetReferencedTables(numTables));\r
+\r
+               return map;\r
+       }\r
+       \r
+}\r