Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / compile / GroupByNode.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/GroupByNode.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/GroupByNode.java
new file mode 100644 (file)
index 0000000..156411a
--- /dev/null
@@ -0,0 +1,1303 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.GroupByNode\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 java.util.Iterator;\r
+import java.util.Vector;\r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.Collections;\r
+\r
+import org.apache.derby.catalog.IndexDescriptor;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+import org.apache.derby.iapi.services.io.FormatableArrayHolder;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.LanguageFactory;\r
+import org.apache.derby.iapi.sql.ResultColumnDescriptor;\r
+import org.apache.derby.iapi.sql.compile.AccessPath;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+import org.apache.derby.iapi.sql.compile.CostEstimate;\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.RequiredRowOrdering;\r
+import org.apache.derby.iapi.sql.compile.RowOrdering;\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.store.access.ColumnOrdering;\r
+import org.apache.derby.impl.sql.execute.AggregatorInfo;\r
+import org.apache.derby.impl.sql.execute.AggregatorInfoList;\r
+\r
+\r
+/**\r
+ * A GroupByNode represents a result set for a grouping operation\r
+ * on a select.  Note that this includes a SELECT with aggregates\r
+ * and no grouping columns (in which case the select list is null)\r
+ * It has the same description as its input result set.\r
+ * <p>\r
+ * For the most part, it simply delegates operations to its bottomPRSet,\r
+ * which is currently expected to be a ProjectRestrictResultSet generated\r
+ * for a SelectNode.\r
+ * <p>\r
+ * NOTE: A GroupByNode extends FromTable since it can exist in a FromList.\r
+ * <p>\r
+ * There is a lot of room for optimizations here: <UL>\r
+ * <LI> agg(distinct x) group by x => agg(x) group by x (for min and max) </LI>\r
+ * <LI> min()/max() use index scans if possible, no sort may \r
+ *             be needed. </LI>\r
+ * </UL>\r
+ *\r
+ *\r
+ */\r
+public class GroupByNode extends SingleChildResultSetNode\r
+{\r
+       /**\r
+        * The GROUP BY list\r
+        */\r
+       GroupByList groupingList;\r
+\r
+       /**\r
+        * The list of all aggregates in the query block\r
+        * that contains this group by.\r
+        */\r
+       Vector  aggregateVector;\r
+\r
+       /**\r
+        * Information that is used at execution time to\r
+        * process aggregates.\r
+        */\r
+       private AggregatorInfoList      aggInfo;\r
+\r
+       /**\r
+        * The parent to the GroupByNode.  If we need to\r
+        * generate a ProjectRestrict over the group by\r
+        * then this is set to that node.  Otherwise it\r
+        * is null.\r
+        */\r
+       FromTable       parent;\r
+\r
+       private boolean addDistinctAggregate;\r
+       private boolean singleInputRowOptimization;\r
+       private int             addDistinctAggregateColumnNum;\r
+\r
+       // Is the source in sorted order\r
+       private boolean isInSortedOrder;\r
+\r
+       private ValueNode havingClause;\r
+       \r
+       private SubqueryList havingSubquerys;\r
+       \r
+       /**\r
+        * Intializer for a GroupByNode.\r
+        *\r
+        * @param bottomPR      The child FromTable\r
+        * @param groupingList  The groupingList\r
+        * @param aggregateVector       The vector of aggregates from\r
+        *              the query block.  Since aggregation is done\r
+        *              at the same time as grouping, we need them\r
+        *              here.\r
+        * @param havingClause The having clause.\r
+        * @param havingSubquerys subqueries in the having clause.\r
+        * @param tableProperties       Properties list associated with the table\r
+        * @param nestingLevel nestingLevel of this group by node. This is used for \r
+        *     error checking of group by queries with having clause.\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void init(\r
+                                               Object bottomPR,\r
+                                               Object groupingList,\r
+                                               Object aggregateVector,\r
+                                               Object havingClause,\r
+                                               Object havingSubquerys,\r
+                                               Object tableProperties,\r
+                                               Object nestingLevel)\r
+                       throws StandardException\r
+       {\r
+               super.init(bottomPR, tableProperties);\r
+               setLevel(((Integer)nestingLevel).intValue());\r
+               this.havingClause = (ValueNode)havingClause;\r
+               this.havingSubquerys = (SubqueryList)havingSubquerys;\r
+               /* Group by without aggregates gets xformed into distinct */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+//                     Aggregage vector can be null if we have a having clause.\r
+//          select c1 from t1 group by c1 having c1 > 1;                       \r
+//                     SanityManager.ASSERT(((Vector) aggregateVector).size() > 0,\r
+//                     "aggregateVector expected to be non-empty");\r
+                       if (!(childResult instanceof Optimizable))\r
+                       {\r
+                               SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() +\r
+                                       ", expected to be instanceof Optimizable");\r
+                       }\r
+                       if (!(childResult instanceof FromTable))\r
+                       {\r
+                               SanityManager.THROWASSERT("childResult, " + childResult.getClass().getName() +\r
+                                       ", expected to be instanceof FromTable");\r
+                       }\r
+               }\r
+\r
+               ResultColumnList newBottomRCL;\r
+               this.groupingList = (GroupByList) groupingList;\r
+               this.aggregateVector = (Vector) aggregateVector;\r
+               this.parent = this;\r
+\r
+               /*\r
+               ** The first thing we do is put ourselves on\r
+               ** top of the SELECT.  The select becomes the\r
+               ** childResult.  So our RCL becomes its RCL (so\r
+               ** nodes above it now point to us).  Map our\r
+               ** RCL to its columns.\r
+               */\r
+               newBottomRCL = childResult.getResultColumns().copyListAndObjects();\r
+               resultColumns = childResult.getResultColumns();\r
+               childResult.setResultColumns(newBottomRCL);\r
+\r
+               /*\r
+               ** We have aggregates, so we need to add\r
+               ** an extra PRNode and we also have to muck around\r
+               ** with our trees a might.\r
+               */\r
+               addAggregates();\r
+\r
+               /* We say that the source is never in sorted order if there is a distinct aggregate.\r
+                * (Not sure what happens if it is, so just skip it for now.)\r
+                * Otherwise, we check to see if the source is in sorted order on any permutation\r
+                * of the grouping columns.)\r
+                */\r
+               if (! addDistinctAggregate && groupingList != null)\r
+               {\r
+                       ColumnReference[] crs =\r
+                                                               new ColumnReference[this.groupingList.size()];\r
+\r
+                       // Now populate the CR array and see if ordered\r
+                       int glSize = this.groupingList.size();\r
+                       int index;\r
+                       for (index = 0; index < glSize; index++)\r
+                       {\r
+                               GroupByColumn gc =\r
+                                               (GroupByColumn) this.groupingList.elementAt(index);\r
+                               if (gc.getColumnExpression() instanceof ColumnReference) \r
+                               {\r
+                                       crs[index] = (ColumnReference)gc.getColumnExpression();\r
+                               } \r
+                               else \r
+                               {\r
+                                       isInSortedOrder = false;\r
+                                       break;\r
+                               }\r
+                               \r
+                       }\r
+                       if (index == glSize) {\r
+                               isInSortedOrder = childResult.isOrderedOn(crs, true, (Vector)null);\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Get whether or not the source is in sorted order.\r
+        *\r
+        * @return Whether or not the source is in sorted order.\r
+        */\r
+       boolean getIsInSortedOrder()\r
+       {\r
+               return isInSortedOrder;\r
+       }\r
+\r
+       /**\r
+        * Add the extra result columns required by the aggregates\r
+        * to the result list.\r
+        * \r
+        * @exception standard exception\r
+        */\r
+       private void addAggregates()\r
+               throws StandardException\r
+       {\r
+               addNewPRNode();\r
+               addNewColumnsForAggregation();\r
+               addDistinctAggregatesToOrderBy();\r
+       }\r
+\r
+       /**\r
+        * Add any distinct aggregates to the order by list.\r
+        * Asserts that there are 0 or more distincts.\r
+        */\r
+       private void addDistinctAggregatesToOrderBy()\r
+       {\r
+               int numDistinct = numDistinctAggregates(aggregateVector);\r
+               if (numDistinct != 0)\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.ASSERT(numDistinct == 1,\r
+                                       "Should not have more than 1 distinct aggregate per Group By node");\r
+                       }\r
+                       \r
+                       AggregatorInfo agg = null;\r
+                       int count = aggInfo.size();\r
+                       for (int i = 0; i < count; i++)\r
+                       {\r
+                               agg = (AggregatorInfo) aggInfo.elementAt(i);\r
+                               if (agg.isDistinct())\r
+                               {\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.ASSERT(agg != null && agg.isDistinct());\r
+                       }\r
+\r
+                       addDistinctAggregate = true;\r
+                       addDistinctAggregateColumnNum = agg.getInputColNum();\r
+               }\r
+       }\r
+       \r
+       /**\r
+        * Add a new PR node for aggregation.  Put the\r
+        * new PR under the sort.\r
+        *              \r
+        * @exception standard exception\r
+        */\r
+       private void addNewPRNode()\r
+               throws StandardException\r
+       {\r
+               /*\r
+               ** Get the new PR, put above the GroupBy.  \r
+               */\r
+               ResultColumnList rclNew = (ResultColumnList)getNodeFactory().getNode(\r
+                                                                                C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                                getContextManager());\r
+               int sz = resultColumns.size();\r
+               for (int i = 0; i < sz; i++) \r
+               {\r
+                       ResultColumn rc = (ResultColumn) resultColumns.elementAt(i);\r
+                       if (!rc.isGenerated()) {\r
+                               rclNew.addElement(rc);\r
+                       }\r
+               }\r
+\r
+               // if any columns in the source RCL were generated for an order by\r
+               // remember it in the new RCL as well. After the sort is done it will\r
+               // have to be projected out upstream.\r
+               rclNew.copyOrderBySelect(resultColumns);\r
+               \r
+               parent = (FromTable) getNodeFactory().getNode(\r
+                                                                               C_NodeTypes.PROJECT_RESTRICT_NODE,\r
+                                                                               this,   // child\r
+                                                                               rclNew,\r
+                                                                               null, //havingClause,\r
+                                                                               null,                           // restriction list\r
+                                                                               null,                           // project subqueries\r
+                                                                               havingSubquerys,\r
+                                                                               tableProperties,\r
+                                                                               getContextManager());\r
+\r
+\r
+               /*\r
+               ** Reset the bottom RCL to be empty.\r
+               */\r
+               childResult.setResultColumns((ResultColumnList)\r
+                                                                                       getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                                               getContextManager()));\r
+\r
+               /*\r
+               ** Set the group by RCL to be empty\r
+               */\r
+               resultColumns = (ResultColumnList) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                                                       getContextManager());\r
+\r
+       }\r
+\r
+       /**\r
+        * In the query rewrite for group by, add the columns on which\r
+        * we are doing the group by.\r
+\r
+        * @see #addNewColumnsForAggregation\r
+        */\r
+       private void addUnAggColumns() throws StandardException\r
+       {\r
+               ResultColumnList bottomRCL  = childResult.getResultColumns();\r
+               ResultColumnList groupByRCL = resultColumns;\r
+\r
+               ArrayList referencesToSubstitute = new ArrayList();\r
+               ArrayList havingRefsToSubstitute = null;\r
+               if (havingClause != null)\r
+                       havingRefsToSubstitute = new ArrayList();\r
+               int sz = groupingList.size();\r
+               for (int i = 0; i < sz; i++) \r
+               {\r
+                       GroupByColumn gbc = (GroupByColumn) groupingList.elementAt(i);\r
+                       ResultColumn newRC = (ResultColumn) getNodeFactory().getNode(\r
+                                       C_NodeTypes.RESULT_COLUMN,\r
+                                       "##UnaggColumn",\r
+                                       gbc.getColumnExpression(),\r
+                                       getContextManager());\r
+\r
+                       // add this result column to the bottom rcl\r
+                       bottomRCL.addElement(newRC);\r
+                       newRC.markGenerated();\r
+                       newRC.bindResultColumnToExpression();\r
+                       newRC.setVirtualColumnId(bottomRCL.size());\r
+                       \r
+                       // now add this column to the groupbylist\r
+                       ResultColumn gbRC = (ResultColumn) getNodeFactory().getNode(\r
+                                       C_NodeTypes.RESULT_COLUMN,\r
+                                       "##UnaggColumn",\r
+                                       gbc.getColumnExpression(),\r
+                                       getContextManager());\r
+                       groupByRCL.addElement(gbRC);\r
+                       gbRC.markGenerated();\r
+                       gbRC.bindResultColumnToExpression();\r
+                       gbRC.setVirtualColumnId(groupByRCL.size());\r
+\r
+                       /*\r
+                        ** Reset the original node to point to the\r
+                        ** Group By result set.\r
+                        */\r
+                       VirtualColumnNode vc = (VirtualColumnNode) getNodeFactory().getNode(\r
+                                       C_NodeTypes.VIRTUAL_COLUMN_NODE,\r
+                                       this, // source result set.\r
+                                       gbRC,\r
+                                       new Integer(groupByRCL.size()),\r
+                                       getContextManager());\r
+\r
+                       // we replace each group by expression \r
+                       // in the projection list with a virtual column node\r
+                       // that effectively points to a result column \r
+                       // in the result set doing the group by\r
+                       //\r
+                       // Note that we don't perform the replacements\r
+                       // immediately, but instead we accumulate them\r
+                       // until the end of the loop. This allows us to\r
+                       // sort the expressions and process them in\r
+                       // descending order of complexity, necessary\r
+                       // because a compound expression may contain a\r
+                       // reference to a simple grouped column, but in\r
+                       // such a case we want to process the expression\r
+                       // as an expression, not as individual column\r
+                       // references. E.g., if the statement was:\r
+                       //   SELECT ... GROUP BY C1, C1 * (C2 / 100), C3\r
+                       // then we don't want the replacement of the\r
+                       // simple column reference C1 to affect the\r
+                       // compound expression C1 * (C2 / 100). DERBY-3094.\r
+                       //\r
+                       ValueNode vn = gbc.getColumnExpression();\r
+                       SubstituteExpressionVisitor vis =\r
+                               new SubstituteExpressionVisitor(vn, vc,\r
+                                               AggregateNode.class);\r
+                       referencesToSubstitute.add(vis);\r
+                       \r
+                       // Since we always need a PR node on top of the GB \r
+                       // node to perform projection we can use it to perform \r
+                       // the having clause restriction as well. \r
+                       // To evaluate the having clause correctly, we need to \r
+                       // convert each aggregate and expression to point \r
+                       // to the appropriate result column in the group by node. \r
+                       // This is no different from the transformations we do to \r
+                       // correctly evaluate aggregates and expressions in the \r
+                       // projection list. \r
+                       // \r
+                       //\r
+                       // For this query:\r
+                       // SELECT c1, SUM(c2), MAX(c3)\r
+                       //    FROM t1 \r
+                       //    HAVING c1+max(c3) > 0;\r
+\r
+                       // PRSN RCL -> (ptr(gbn:rcl[0]), ptr(gbn:rcl[1]), ptr(gbn:rcl[4]))\r
+                       // Restriction: (> (+ ptr(gbn:rcl[0]) ptr(gbn:rcl[4])) 0)\r
+                       //              |\r
+                       // GBN (RCL) -> (C1, SUM(C2), <input>, <aggregator>, MAX(C3), <input>, <aggregator>\r
+                       //              |\r
+                       //       FBT (C1, C2)\r
+                       if (havingClause != null)\r
+                       {\r
+                               SubstituteExpressionVisitor havingSE =\r
+                                       new SubstituteExpressionVisitor(vn,vc,null);\r
+                               havingRefsToSubstitute.add(havingSE);\r
+                       }\r
+                       gbc.setColumnPosition(bottomRCL.size());\r
+               }\r
+               Comparator sorter = new ExpressionSorter();\r
+               Collections.sort(referencesToSubstitute,sorter);\r
+               for (int r = 0; r < referencesToSubstitute.size(); r++)\r
+                       parent.getResultColumns().accept(\r
+                               (SubstituteExpressionVisitor)referencesToSubstitute.get(r));\r
+               if (havingRefsToSubstitute != null)\r
+               {\r
+                       Collections.sort(havingRefsToSubstitute,sorter);\r
+                       for (int r = 0; r < havingRefsToSubstitute.size(); r++)\r
+                               havingClause.accept(\r
+                                       (SubstituteExpressionVisitor)havingRefsToSubstitute.get(r));\r
+}\r
+       }\r
+\r
+       /**\r
+        * Add a whole slew of columns needed for \r
+        * aggregation. Basically, for each aggregate we add\r
+        * 3 columns: the aggregate input expression\r
+        * and the aggregator column and a column where the aggregate \r
+        * result is stored.  The input expression is\r
+        * taken directly from the aggregator node.  The aggregator\r
+        * is the run time aggregator.  We add it to the RC list\r
+        * as a new object coming into the sort node.\r
+        * <P>\r
+        * At this point this is invoked, we have the following\r
+        * tree: <UL>\r
+        *      PR - (PARENT): RCL is the original select list\r
+        *       |\r
+        *      PR - GROUP BY:  RCL is empty\r
+        *       |\r
+        *      PR - FROM TABLE: RCL is empty </UL> <P>\r
+        *\r
+        * For each ColumnReference in PR RCL <UL>\r
+        *      <LI> clone the ref </LI>\r
+        *      <LI> create a new RC in the bottom RCL and set it \r
+        *               to the col ref </LI>\r
+        *      <LI> create a new RC in the GROUPBY RCL and set it to \r
+        *               point to the bottom RC </LI>\r
+        *      <LI> reset the top PR ref to point to the new GROUPBY\r
+        *               RC</LI></UL>   \r
+        *\r
+        * For each aggregate in aggregateVector <UL>\r
+        *      <LI> create RC in FROM TABLE.  Fill it with \r
+        *              aggs Operator.\r
+        *      <LI> create RC in FROM TABLE for agg result</LI>\r
+        *      <LI> create RC in FROM TABLE for aggregator</LI>\r
+        *      <LI> create RC in GROUPBY for agg input, set it\r
+        *              to point to FROM TABLE RC </LI>\r
+        *      <LI> create RC in GROUPBY for agg result</LI>\r
+        *      <LI> create RC in GROUPBY for aggregator</LI>\r
+        *      <LI> replace Agg with reference to RC for agg result </LI></UL>.\r
+        * <P>\r
+        * For a query like,\r
+        * <pre>\r
+         select c1, sum(c2), max(c3)\r
+         from t1 \r
+         group by c1;\r
+         </pre>\r
+        * the query tree ends up looking like this:\r
+          <pre>\r
+           ProjectRestrictNode RCL -> (ptr to GBN(column[0]), ptr to GBN(column[1]), ptr to GBN(column[4]))\r
+                     |\r
+           GroupByNode RCL->(C1, SUM(C2), <agg-input>, <aggregator>, MAX(C3), <agg-input>, <aggregator>)\r
+                     |\r
+           ProjectRestrict RCL->(C1, C2, C3)\r
+                     |\r
+           FromBaseTable\r
+           </pre>\r
+        * \r
+        * The RCL of the GroupByNode contains all the unagg (or grouping columns)\r
+        * followed by 3 RC's for each aggregate in this order: the final computed\r
+        * aggregate value, the aggregate input and the aggregator function.\r
+        * <p>\r
+        * The Aggregator function puts the results in the first of the 3 RC's \r
+        * and the PR resultset in turn picks up the value from there.\r
+        * <p>\r
+        * The notation (ptr to GBN(column[0])) basically means that it is\r
+        * a pointer to the 0th RC in the RCL of the GroupByNode. \r
+        * <p>\r
+        * The addition of these unagg and agg columns to the GroupByNode and \r
+        * to the PRN is performed in addUnAggColumns and addAggregateColumns. \r
+        * <p>\r
+        * Note that that addition of the GroupByNode is done after the\r
+        * query is optimized (in SelectNode#modifyAccessPaths) which means a \r
+        * fair amount of patching up is needed to account for generated group by columns.\r
+        * @exception standard exception\r
+        */\r
+       private void addNewColumnsForAggregation()\r
+               throws StandardException\r
+       {\r
+               aggInfo = new AggregatorInfoList();\r
+               if (groupingList != null)\r
+               {\r
+                       addUnAggColumns();\r
+               }\r
+               if (havingClause != null) {\r
+                       // we have replaced group by expressions in the having clause.\r
+                       // there should be no column references in the having clause \r
+                       // referencing this table. Skip over aggregate nodes.\r
+                       //   select a, sum(b) from t group by a having a+c > 1 \r
+                       //  is not valid because of column c.\r
+                       // \r
+                       // it is allright to have columns from parent or child subqueries;\r
+                       //   select * from p where p.p1 in \r
+                       //      (select c.c1 from c group by c.c1 having count(*) = p.p2\r
+                       CollectNodesVisitor collectNodesVisitor = \r
+                               new CollectNodesVisitor(ColumnReference.class, AggregateNode.class);\r
+                       havingClause.accept(collectNodesVisitor);\r
+                       for (Iterator it = collectNodesVisitor.getList().iterator();\r
+                            it.hasNext(); ) \r
+                       {\r
+                               ColumnReference cr = (ColumnReference)it.next();\r
+                               \r
+                               if (!cr.getGeneratedToReplaceAggregate() && \r
+                                               cr.getSourceLevel() == level) {\r
+                                       throw StandardException.newException(\r
+                                                       SQLState.LANG_INVALID_COL_HAVING_CLAUSE, \r
+                                                       cr.getSQLColumnName());                                         \r
+                               }\r
+                       }\r
+               }\r
+               addAggregateColumns();\r
+       }\r
+       \r
+       /**\r
+        * In the query rewrite involving aggregates, add the columns for\r
+        * aggregation.\r
+        *\r
+        * @see #addNewColumnsForAggregation\r
+        */\r
+       private void addAggregateColumns() throws StandardException\r
+       {\r
+               DataDictionary                  dd = getDataDictionary();\r
+               AggregateNode   aggregate = null;\r
+               ColumnReference newColumnRef;\r
+               ResultColumn    newRC;\r
+               ResultColumn    tmpRC;\r
+               ResultColumn    aggInputRC;\r
+               ResultColumnList bottomRCL  = childResult.getResultColumns();\r
+               ResultColumnList groupByRCL = resultColumns;\r
+               ResultColumnList aggRCL;\r
+               int                             aggregatorVColId;\r
+               int                             aggInputVColId;\r
+               int                             aggResultVColId;\r
+               \r
+               /*\r
+                ** Now process all of the aggregates.  Replace\r
+                ** every aggregate with an RC.  We toss out\r
+                ** the list of RCs, we need to get each RC\r
+                ** as we process its corresponding aggregate.\r
+                */\r
+               LanguageFactory lf = getLanguageConnectionContext().getLanguageFactory();\r
+               \r
+               ReplaceAggregatesWithCRVisitor replaceAggsVisitor = \r
+                       new ReplaceAggregatesWithCRVisitor(\r
+                                       (ResultColumnList) getNodeFactory().getNode(\r
+                                                       C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                       getContextManager()),\r
+                               ((FromTable) childResult).getTableNumber(),\r
+                               ResultSetNode.class);\r
+               parent.getResultColumns().accept(replaceAggsVisitor);\r
+\r
+               \r
+               if (havingClause != null) \r
+               {\r
+                       // replace aggregates in the having clause with column references.\r
+                       replaceAggsVisitor = new ReplaceAggregatesWithCRVisitor(\r
+                                       (ResultColumnList) getNodeFactory().getNode(\r
+                                                       C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                                       getContextManager()),                                   \r
+                                       ((FromTable)childResult).getTableNumber());\r
+                       havingClause.accept(replaceAggsVisitor);\r
+                       // make having clause a restriction list in the parent \r
+                       // project restrict node.\r
+                       ProjectRestrictNode parentPRSN = (ProjectRestrictNode)parent;\r
+                       parentPRSN.setRestriction(havingClause);\r
+               }\r
+\r
+               \r
+               /*\r
+               ** For each aggregate\r
+               */\r
+               int alSize = aggregateVector.size();\r
+               for (int index = 0; index < alSize; index++)\r
+               {\r
+                       aggregate = (AggregateNode) aggregateVector.elementAt(index);\r
+\r
+                       /*\r
+                       ** AGG RESULT: Set the aggregate result to null in the\r
+                       ** bottom project restrict.\r
+                       */\r
+                       newRC = (ResultColumn) getNodeFactory().getNode(\r
+                                       C_NodeTypes.RESULT_COLUMN,\r
+                                       "##aggregate result",\r
+                                       aggregate.getNewNullResultExpression(),\r
+                                       getContextManager());\r
+                       newRC.markGenerated();\r
+                       newRC.bindResultColumnToExpression();\r
+                       bottomRCL.addElement(newRC);\r
+                       newRC.setVirtualColumnId(bottomRCL.size());\r
+                       aggResultVColId = newRC.getVirtualColumnId();\r
+\r
+                       /*\r
+                       ** Set the GB aggregrate result column to\r
+                       ** point to this.  The GB aggregate result\r
+                       ** was created when we called\r
+                       ** ReplaceAggregatesWithColumnReferencesVisitor()\r
+                       */\r
+                       newColumnRef = (ColumnReference) getNodeFactory().getNode(\r
+                                       C_NodeTypes.COLUMN_REFERENCE,\r
+                                       newRC.getName(),\r
+                                       null,\r
+                                       getContextManager());\r
+                       newColumnRef.setSource(newRC);\r
+                       newColumnRef.setType(newRC.getExpressionType());\r
+                       newColumnRef.setNestingLevel(this.getLevel());\r
+                       newColumnRef.setSourceLevel(this.getLevel());\r
+                       tmpRC = (ResultColumn) getNodeFactory().getNode(\r
+                                       C_NodeTypes.RESULT_COLUMN,\r
+                                       newRC.getColumnName(),\r
+                                       newColumnRef,\r
+                                       getContextManager());\r
+                       tmpRC.markGenerated();\r
+                       tmpRC.bindResultColumnToExpression();\r
+                       groupByRCL.addElement(tmpRC);\r
+                       tmpRC.setVirtualColumnId(groupByRCL.size());\r
+\r
+                       /*\r
+                       ** Set the column reference to point to\r
+                       ** this.\r
+                       */\r
+                       newColumnRef = aggregate.getGeneratedRef();\r
+                       newColumnRef.setSource(tmpRC);\r
+\r
+                       /*\r
+                       ** AGG INPUT: Create a ResultColumn in the bottom \r
+                       ** project restrict that has the expression that is\r
+                       ** to be aggregated\r
+                       */\r
+                       newRC = aggregate.getNewExpressionResultColumn(dd);\r
+                       newRC.markGenerated();\r
+                       newRC.bindResultColumnToExpression();\r
+                       bottomRCL.addElement(newRC);\r
+                       newRC.setVirtualColumnId(bottomRCL.size());\r
+                       aggInputVColId = newRC.getVirtualColumnId();\r
+                       aggInputRC = newRC;\r
+       \r
+                       /*\r
+                       ** Add a reference to this column into the\r
+                       ** group by columns.\r
+                       */\r
+                       tmpRC = getColumnReference(newRC, dd);\r
+                       groupByRCL.addElement(tmpRC);\r
+                       tmpRC.setVirtualColumnId(groupByRCL.size());\r
+\r
+                       /*\r
+                       ** AGGREGATOR: Add a getAggregator method call \r
+                       ** to the bottom result column list.\r
+                       */\r
+                       newRC = aggregate.getNewAggregatorResultColumn(dd);\r
+                       newRC.markGenerated();\r
+                       newRC.bindResultColumnToExpression();\r
+                       bottomRCL.addElement(newRC);\r
+                       newRC.setVirtualColumnId(bottomRCL.size());\r
+                       aggregatorVColId = newRC.getVirtualColumnId();\r
+\r
+                       /*\r
+                       ** Add a reference to this column in the Group By result\r
+                       ** set.\r
+                       */\r
+                       tmpRC = getColumnReference(newRC, dd);\r
+                       groupByRCL.addElement(tmpRC);\r
+                       tmpRC.setVirtualColumnId(groupByRCL.size());\r
+\r
+                       /*\r
+                       ** Piece together a fake one column rcl that we will use\r
+                       ** to generate a proper result description for input\r
+                       ** to this agg if it is a user agg.\r
+                       */\r
+                       aggRCL = (ResultColumnList) getNodeFactory().getNode(\r
+                                       C_NodeTypes.RESULT_COLUMN_LIST,\r
+                                       getContextManager());\r
+                       aggRCL.addElement(aggInputRC);\r
+\r
+                       /*\r
+                       ** Note that the column ids in the row are 0 based\r
+                       ** so we have to subtract 1.\r
+                       */\r
+                       aggInfo.addElement(new AggregatorInfo(\r
+                                       aggregate.getAggregateName(),\r
+                                       aggregate.getAggregatorClassName(),\r
+                                       aggInputVColId - 1,                     // aggregate input column\r
+                                       aggResultVColId -1,                     // the aggregate result column\r
+                                       aggregatorVColId - 1,           // the aggregator column        \r
+                                       aggregate.isDistinct(),\r
+                                       lf.getResultDescription(aggRCL.makeResultDescriptors(), "SELECT")\r
+                       ));\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Return the parent node to this one, if there is\r
+        * one.  It will return 'this' if there is no generated\r
+        * node above this one.\r
+        *\r
+        * @return the parent node\r
+        */\r
+       public FromTable getParent()\r
+       {\r
+               return parent;\r
+       }\r
+\r
+\r
+       /*\r
+        *  Optimizable interface\r
+        */\r
+\r
+       /**\r
+        * @see 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
+               // RESOLVE: NEED TO FACTOR IN THE COST OF GROUPING (SORTING) HERE\r
+               CostEstimate childCost = ((Optimizable) childResult).optimizeIt(\r
+                                                                                                       optimizer,\r
+                                                                                                       predList,\r
+                                                                                                       outerCost,\r
+                                                                                                       rowOrdering);\r
+\r
+               CostEstimate retval = super.optimizeIt(\r
+                                                                                               optimizer,\r
+                                                                                               predList,\r
+                                                                                               outerCost,\r
+                                                                                               rowOrdering\r
+                                                                                         );\r
+\r
+               return retval;\r
+       }\r
+\r
+       /**\r
+        * @see Optimizable#estimateCost\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public CostEstimate estimateCost(OptimizablePredicateList predList,\r
+                                                                               ConglomerateDescriptor cd,\r
+                                                                               CostEstimate outerCost,\r
+                                                                               Optimizer optimizer,\r
+                                                                               RowOrdering rowOrdering\r
+                                                                               )\r
+                       throws StandardException\r
+       {\r
+               // RESOLVE: NEED TO FACTOR IN THE COST OF GROUPING (SORTING) HERE\r
+               //\r
+               CostEstimate childCost = ((Optimizable) childResult).estimateCost(\r
+                                                                                                       predList,\r
+                                                                                                       cd,\r
+                                                                                                       outerCost,\r
+                                                                                                       optimizer,\r
+                                                                                                       rowOrdering);\r
+\r
+               CostEstimate costEstimate = getCostEstimate(optimizer);\r
+               costEstimate.setCost(childCost.getEstimatedCost(),\r
+                                                       childCost.rowCount(),\r
+                                                       childCost.singleScanRowCount());\r
+\r
+               return costEstimate;\r
+       }\r
+\r
+       /**\r
+        * @see org.apache.derby.iapi.sql.compile.Optimizable#pushOptPredicate\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public boolean pushOptPredicate(OptimizablePredicate optimizablePredicate)\r
+                       throws StandardException\r
+       {\r
+               return ((Optimizable) childResult).pushOptPredicate(optimizablePredicate);\r
+       }\r
+\r
+       /**\r
+        * Convert this object to a String.  See comments in QueryTreeNode.java\r
+        * for how this should be done for tree printing.\r
+        *\r
+        * @return      This object as a String\r
+        */\r
+\r
+       public String toString()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       return "singleInputRowOptimization: " + singleInputRowOptimization + "\n" +\r
+                               childResult.toString() + "\n" + super.toString();\r
+               }\r
+               else\r
+               {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Evaluate whether or not the subquery in a FromSubquery is flattenable.\r
+        * Currently, a FSqry is flattenable if all of the following are true:\r
+        *              o  Subquery is a SelectNode.\r
+        *              o  It contains no top level subqueries.  (RESOLVE - we can relax this)\r
+        *              o  It does not contain a group by or having clause\r
+        *              o  It does not contain aggregates.\r
+        *\r
+        * @param fromList      The outer from list\r
+        *\r
+        * @return boolean      Whether or not the FromSubquery is flattenable.\r
+        */\r
+       public boolean flattenableInFromSubquery(FromList fromList)\r
+       {\r
+               /* Can't flatten a GroupByNode */\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Optimize this GroupByNode.\r
+        *\r
+        * @param dataDictionary        The DataDictionary to use for optimization\r
+        * @param predicates            The PredicateList to optimize.  This should\r
+        *                                                      be a join predicate.\r
+        * @param outerRows                     The number of outer joining rows\r
+        *\r
+        * @return      ResultSetNode   The top of the optimized subtree\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+\r
+       public ResultSetNode optimize(DataDictionary dataDictionary,\r
+                                                                 PredicateList predicates,\r
+                                                                 double outerRows)\r
+                                       throws StandardException\r
+       {\r
+               /* We need to implement this method since a PRN can appear above a\r
+                * SelectNode in a query tree.\r
+                */\r
+               childResult = (FromTable) childResult.optimize(\r
+                                                                                       dataDictionary,\r
+                                                                                       predicates,\r
+                                                                                       outerRows);\r
+               Optimizer optimizer = getOptimizer(\r
+                                               (FromList) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.FROM_LIST,\r
+                                                                       getNodeFactory().doJoinOrderOptimization(),\r
+                                                                       getContextManager()),\r
+                                               predicates,\r
+                                               dataDictionary,\r
+                                               (RequiredRowOrdering) null);\r
+\r
+               // RESOLVE: NEED TO FACTOR IN COST OF SORTING AND FIGURE OUT HOW\r
+               // MANY ROWS HAVE BEEN ELIMINATED.\r
+               costEstimate = optimizer.newCostEstimate();\r
+\r
+               costEstimate.setCost(childResult.getCostEstimate().getEstimatedCost(),\r
+                                                       childResult.getCostEstimate().rowCount(),\r
+                                                       childResult.getCostEstimate().singleScanRowCount());\r
+\r
+               return this;\r
+       }\r
+\r
+       ResultColumnDescriptor[] makeResultDescriptors()\r
+       {\r
+           return childResult.makeResultDescriptors();\r
+       }\r
+\r
+       /**\r
+        * Return whether or not the underlying ResultSet tree will return\r
+        * a single row, at most.\r
+        * This is important for join nodes where we can save the extra next\r
+        * on the right side if we know that it will return at most 1 row.\r
+        *\r
+        * @return Whether or not the underlying ResultSet tree will return a single row.\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public boolean isOneRowResultSet()      throws StandardException\r
+       {\r
+               // Only consider scalar aggregates for now\r
+               return ((groupingList == null) ||  (groupingList.size() == 0));\r
+       }\r
+\r
+    /**\r
+     * generate the sort result set operating over the source\r
+        * resultset.  Adds distinct aggregates to the sort if\r
+        * necessary.\r
+     *\r
+        * @exception StandardException         Thrown on error\r
+     */\r
+       public void generate(ActivationClassBuilder acb,\r
+                                                               MethodBuilder mb)\r
+                                                       throws StandardException\r
+       {\r
+               int                                     orderingItem = 0;\r
+               int                                     aggInfoItem = 0;\r
+               FormatableArrayHolder   orderingHolder;\r
+\r
+               /* Get the next ResultSet#, so we can number this ResultSetNode, its\r
+                * ResultColumnList and ResultSet.\r
+                */\r
+               assignResultSetNumber();\r
+\r
+               // Get the final cost estimate from the child.\r
+               costEstimate = childResult.getFinalCostEstimate();\r
+\r
+               /*\r
+               ** Get the column ordering for the sort.  Note that\r
+               ** for a scalar aggegate we may not have any ordering\r
+               ** columns (if there are no distinct aggregates).\r
+               ** WARNING: if a distinct aggregate is passed to\r
+               ** SortResultSet it assumes that the last column\r
+               ** is the distinct one.  If this assumption changes\r
+               ** then SortResultSet will have to change.\r
+               */\r
+               orderingHolder = acb.getColumnOrdering(groupingList);\r
+               if (addDistinctAggregate)\r
+               {\r
+                       orderingHolder = acb.addColumnToOrdering(\r
+                                                                       orderingHolder,\r
+                                                                       addDistinctAggregateColumnNum);\r
+               }\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (SanityManager.DEBUG_ON("AggregateTrace"))\r
+                       {\r
+                               StringBuffer s = new StringBuffer();\r
+                                       \r
+                               s.append("Group by column ordering is (");\r
+                               ColumnOrdering[] ordering = \r
+                                               (ColumnOrdering[])orderingHolder.getArray(ColumnOrdering.class);\r
+\r
+                               for (int i = 0; i < ordering.length; i++)       \r
+                               {\r
+                                       s.append(ordering[i].getColumnId());\r
+                                       s.append(" ");\r
+                               }\r
+                               s.append(")");\r
+                               SanityManager.DEBUG("AggregateTrace", s.toString());\r
+                       }\r
+               }\r
+\r
+               orderingItem = acb.addItem(orderingHolder);\r
+\r
+               /*\r
+               ** We have aggregates, so save the aggInfo\r
+               ** struct in the activation and store the number\r
+               */\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(aggInfo != null,\r
+                                       "aggInfo not set up as expected");\r
+               }\r
+               aggInfoItem = acb.addItem(aggInfo);\r
+\r
+               acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+               // Generate the child ResultSet\r
+               childResult.generate(acb, mb);\r
+               mb.push(isInSortedOrder);\r
+               mb.push(aggInfoItem);\r
+               mb.push(orderingItem);\r
+\r
+               resultColumns.generateHolder(acb, mb);\r
+\r
+               mb.push(resultColumns.getTotalColumnSize());\r
+               mb.push(resultSetNumber);\r
+\r
+               /* Generate a (Distinct)ScalarAggregateResultSet if scalar aggregates */\r
+               if ((groupingList == null) ||  (groupingList.size() == 0))\r
+               {\r
+                       genScalarAggregateResultSet(acb, mb);\r
+               }\r
+               /* Generate a (Distinct)GroupedAggregateResultSet if grouped aggregates */\r
+               else\r
+               {\r
+                       genGroupedAggregateResultSet(acb, mb);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Generate the code to evaluate scalar aggregates.\r
+        *\r
+        */\r
+       private void genScalarAggregateResultSet(ActivationClassBuilder acb,\r
+                                                                                                  MethodBuilder mb)\r
+       {\r
+               /* Generate the (Distinct)ScalarAggregateResultSet:\r
+                *      arg1: childExpress - Expression for childResult\r
+                *  arg2: isInSortedOrder - true if source result set in sorted order\r
+                *  arg3: aggregateItem - entry in saved objects for the aggregates,\r
+                *  arg4: orderItem - entry in saved objects for the ordering\r
+                *  arg5: Activation\r
+                *  arg6: rowAllocator - method to construct rows for fetching\r
+                *                      from the sort\r
+                *  arg7: row size\r
+                *  arg8: resultSetNumber\r
+                *  arg9: Whether or not to perform min optimization.\r
+                */\r
+               String resultSet = (addDistinctAggregate) ? "getDistinctScalarAggregateResultSet" : "getScalarAggregateResultSet";\r
+\r
+               mb.push(singleInputRowOptimization);\r
+               mb.push(costEstimate.rowCount());\r
+               mb.push(costEstimate.getEstimatedCost());\r
+\r
+               mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, resultSet,\r
+                ClassName.NoPutResultSet, 10);\r
+       }\r
+\r
+       /**\r
+        * Generate the code to evaluate grouped aggregates.\r
+        *\r
+        */\r
+       private void genGroupedAggregateResultSet(ActivationClassBuilder acb,\r
+                                                                                                  MethodBuilder mb)\r
+                               throws StandardException\r
+       {\r
+               /* Generate the (Distinct)GroupedAggregateResultSet:\r
+                *      arg1: childExpress - Expression for childResult\r
+                *  arg2: isInSortedOrder - true if source result set in sorted order\r
+                *  arg3: aggregateItem - entry in saved objects for the aggregates,\r
+                *  arg4: orderItem - entry in saved objects for the ordering\r
+                *  arg5: Activation\r
+                *  arg6: rowAllocator - method to construct rows for fetching\r
+                *                      from the sort\r
+                *  arg7: row size\r
+                *  arg8: resultSetNumber\r
+                */\r
+               String resultSet = (addDistinctAggregate) ? "getDistinctGroupedAggregateResultSet" : "getGroupedAggregateResultSet";\r
+    \r
+               mb.push(costEstimate.rowCount());\r
+               mb.push(costEstimate.getEstimatedCost());\r
+\r
+               mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, resultSet,\r
+                ClassName.NoPutResultSet, 9);\r
+\r
+       }\r
+\r
+       ///////////////////////////////////////////////////////////////\r
+       //\r
+       // UTILITIES\r
+       //\r
+       ///////////////////////////////////////////////////////////////\r
+       /**\r
+        * Method for creating a new result column referencing\r
+        * the one passed in.\r
+        *\r
+        * @param       targetRC        the source\r
+        * @param       dd\r
+        *\r
+        * @return the new result column\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       private ResultColumn getColumnReference(ResultColumn targetRC, \r
+                                                               DataDictionary          dd)\r
+               throws StandardException\r
+       {\r
+               ColumnReference tmpColumnRef;\r
+               ResultColumn    newRC;\r
+       \r
+               tmpColumnRef = (ColumnReference) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.COLUMN_REFERENCE,\r
+                                                                                       targetRC.getName(),\r
+                                                                                       null,\r
+                                                                                       getContextManager());\r
+               tmpColumnRef.setSource(targetRC);\r
+               tmpColumnRef.setType(targetRC.getExpressionType());\r
+               tmpColumnRef.setNestingLevel(this.getLevel());\r
+               tmpColumnRef.setSourceLevel(this.getLevel());\r
+               newRC = (ResultColumn) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.RESULT_COLUMN,\r
+                                                                       targetRC.getColumnName(),\r
+                                                                       tmpColumnRef,\r
+                                                                       getContextManager());\r
+               newRC.markGenerated();\r
+               newRC.bindResultColumnToExpression();\r
+               return newRC;\r
+       }\r
+\r
+       /**\r
+        * Consider any optimizations after the optimizer has chosen a plan.\r
+        * Optimizations include:\r
+        *      o  min optimization for scalar aggregates\r
+        *      o  max optimization for scalar aggregates\r
+        *\r
+        * @param selectHasPredicates true if SELECT containing this\r
+        *              vector/scalar aggregate has a restriction\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       void considerPostOptimizeOptimizations(boolean selectHasPredicates)\r
+               throws StandardException\r
+       {\r
+               /* Consider the optimization for min with asc index on that column or\r
+                * max with desc index on that column:\r
+                *      o  No group by\r
+                *  o  One of:\r
+                *              o  min/max(ColumnReference) is only aggregate && source is \r
+                *                 ordered on the ColumnReference\r
+                *              o  min/max(ConstantNode)\r
+                * The optimization of the other way around (min with desc index or\r
+                * max with asc index) has the same restrictions with the additional\r
+                * temporary restriction of no qualifications at all (because\r
+                * we don't have true backward scans).\r
+                */\r
+               if (groupingList == null)\r
+               {\r
+                       if (aggregateVector.size() == 1)\r
+                       {\r
+                               AggregateNode an = (AggregateNode) aggregateVector.elementAt(0);\r
+                               AggregateDefinition ad = an.getAggregateDefinition();\r
+                               if (ad instanceof MaxMinAggregateDefinition)\r
+                               {\r
+                                       if (an.getOperand() instanceof ColumnReference)\r
+                                       {\r
+                                               /* See if the underlying ResultSet tree\r
+                                                * is ordered on the ColumnReference.\r
+                                                */\r
+                                               ColumnReference[] crs = new ColumnReference[1];\r
+                                               crs[0] = (ColumnReference) an.getOperand();\r
+                                               \r
+                                               Vector tableVector = new Vector();\r
+                                               boolean minMaxOptimizationPossible = isOrderedOn(crs, false, tableVector);\r
+                                               if (SanityManager.DEBUG)\r
+                                               {\r
+                                                       SanityManager.ASSERT(tableVector.size() <= 1, "bad number of FromBaseTables returned by isOrderedOn() -- "+tableVector.size());\r
+                                               }\r
+\r
+                                               if (minMaxOptimizationPossible)\r
+                                               {\r
+                                                       boolean ascIndex = true;\r
+                                                       int colNum = crs[0].getColumnNumber();\r
+                                                       \r
+                                                       /* Check if we have an access path, this will be\r
+                                                        * null in a join case (See Beetle 4423)\r
+                                                        */\r
+                                                       AccessPath accessPath= getTrulyTheBestAccessPath();\r
+                                                       if (accessPath == null)\r
+                                                               return;\r
+                                                       IndexDescriptor id = accessPath.\r
+                                                                                               getConglomerateDescriptor().\r
+                                                                                               getIndexDescriptor();\r
+                                                       int[] keyColumns = id.baseColumnPositions();\r
+                                                       boolean[] isAscending = id.isAscending();\r
+                                                       for (int i = 0; i < keyColumns.length; i++)\r
+                                                       {\r
+                                                               /* in such a query: select min(c3) from\r
+                                                                * tab1 where c1 = 2 and c2 = 5, if prefix keys\r
+                                                                * have equality operator, then we can still use\r
+                                                                * the index.  The checking of equality operator\r
+                                                                * has been done in isStrictlyOrderedOn.\r
+                                                                */\r
+                                                               if (colNum == keyColumns[i])\r
+                                                               {\r
+                                                                       if (! isAscending[i])\r
+                                                                               ascIndex = false;\r
+                                                                       break;\r
+                                                               }\r
+                                                       }\r
+                                                       FromBaseTable fbt = (FromBaseTable)tableVector.firstElement();\r
+                                                       MaxMinAggregateDefinition temp = (MaxMinAggregateDefinition)ad;\r
+\r
+                                                       /*  MAX   ASC      NULLABLE \r
+                             *  ----  ----------\r
+                                                        *  TRUE  TRUE      TRUE/FALSE  =  Special Last Key Scan (ASC Index Last key with null skips)\r
+                                                        *  TRUE  FALSE     TRUE/FALSE  =  JustDisableBulk(DESC index 1st key with null skips)\r
+                                                        *  FALSE TRUE      TRUE/FALSE  = JustDisableBulk(ASC index 1st key)\r
+                                                        *  FALSE FALSE     TRUE/FALSE  = Special Last Key Scan(Desc Index Last Key)\r
+                                                        */\r
+\r
+                                                       if (((!temp.isMax()) && ascIndex) || \r
+                                                               ((temp.isMax()) && !ascIndex))\r
+                                                       {\r
+                                                               fbt.disableBulkFetch();\r
+                                                               singleInputRowOptimization = true;\r
+                                                       }\r
+                                                       /*\r
+                                                       ** Max optimization with asc index or min with\r
+                                                       ** desc index is currently more\r
+                                                       ** restrictive than otherwise.\r
+                                                       ** We are getting the store to return the last\r
+                                                       ** row from an index (for the time being, the\r
+                                                       ** store cannot do real backward scans).  SO\r
+                                                       ** we cannot do this optimization if we have\r
+                                                       ** any predicates at all.\r
+                                                       */\r
+                                                       else if (!selectHasPredicates && \r
+                                                                        ((temp.isMax() && ascIndex) || \r
+                                                                         (!temp.isMax() && !ascIndex )))\r
+                                                       {\r
+                                                               fbt.disableBulkFetch();\r
+                                                               fbt.doSpecialMaxScan();\r
+                                                               singleInputRowOptimization = true;\r
+                                                       }\r
+                                               }\r
+                                       }\r
+                                       else if (an.getOperand() instanceof ConstantNode)\r
+                                       {\r
+                                               singleInputRowOptimization = true;\r
+                                       }\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Comparator class for GROUP BY expression substitution.\r
+        *\r
+        * This class enables the sorting of a collection of\r
+        * SubstituteExpressionVisitor instances. We sort the visitors\r
+        * during the tree manipulation processing in order to process\r
+        * expressions of higher complexity prior to expressions of\r
+        * lower complexity. Processing the expressions in this order ensures\r
+        * that we choose the best match for an expression, and thus avoids\r
+        * problems where we substitute a sub-expression instead of the\r
+        * full expression. For example, if the statement is:\r
+        *   ... GROUP BY a+b, a, a*(a+b), a+b+c\r
+        * we'll process those expressions in the order: a*(a+b),\r
+        * a+b+c, a+b, then a.\r
+        */\r
+       private static final class ExpressionSorter implements Comparator\r
+       {\r
+               public int compare(Object o1, Object o2)\r
+               {\r
+                       try {\r
+                               ValueNode v1 = ((SubstituteExpressionVisitor)o1).getSource();\r
+                               ValueNode v2 = ((SubstituteExpressionVisitor)o2).getSource();\r
+                               int refCount1, refCount2;\r
+                               CollectNodesVisitor vis = new CollectNodesVisitor(\r
+                               ColumnReference.class);\r
+                               v1.accept(vis);\r
+                               refCount1 = vis.getList().size();\r
+                               vis = new CollectNodesVisitor(ColumnReference.class);\r
+                               v2.accept(vis);\r
+                               refCount2 = vis.getList().size();\r
+                               // The ValueNode with the larger number of refs\r
+                               // should compare lower. That way we are sorting\r
+                               // the expressions in descending order of complexity.\r
+                               return refCount2 - refCount1;\r
+                       }\r
+                       catch (StandardException e)\r
+                       {\r
+                               throw new RuntimeException(e);\r
+                       }\r
+               }\r
+       }\r
+}\r