--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.GroupByList\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.sql.compile.C_NodeTypes;\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.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.reference.Limits;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * A GroupByList represents the list of expressions in a GROUP BY clause in\r
+ * a SELECT statement.\r
+ *\r
+ */\r
+\r
+public class GroupByList extends OrderedColumnList\r
+{\r
+ int numGroupingColsAdded = 0;\r
+\r
+ /**\r
+ Add a column to the list\r
+\r
+ @param column The column to add to the list\r
+ */\r
+ public void addGroupByColumn(GroupByColumn column)\r
+ {\r
+ addElement(column);\r
+ }\r
+\r
+ /**\r
+ Get a column from the list\r
+\r
+ @param position The column to get from the list\r
+ */\r
+ public GroupByColumn getGroupByColumn(int position)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(position >=0 && position < size(),\r
+ "position (" + position +\r
+ ") expected to be between 0 and " + size());\r
+ }\r
+ return (GroupByColumn) elementAt(position);\r
+ }\r
+\r
+ /**\r
+ Print the list.\r
+\r
+ @param depth The depth at which to indent the sub-nodes\r
+ */\r
+ public void printSubNodes(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ ( (GroupByColumn) elementAt(index) ).treePrint(depth);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Get the number of grouping columns that need to be added to the SELECT list.\r
+ *\r
+ * @return int The number of grouping columns that need to be added to\r
+ * the SELECT list.\r
+ */\r
+ public int getNumNeedToAddGroupingCols()\r
+ {\r
+ return numGroupingColsAdded;\r
+ }\r
+\r
+ /**\r
+ * Bind the group by list. Verify:\r
+ * o Number of grouping columns matches number of non-aggregates in\r
+ * SELECT's RCL.\r
+ * o Names in the group by list are unique\r
+ * o Names of grouping columns match names of non-aggregate\r
+ * expressions in SELECT's RCL.\r
+ *\r
+ * @param select The SelectNode\r
+ * @param aggregateVector The aggregate vector being built as we find AggregateNodes\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void bindGroupByColumns(SelectNode select,\r
+ Vector aggregateVector)\r
+ throws StandardException\r
+ {\r
+ FromList fromList = select.getFromList();\r
+ ResultColumnList selectRCL = select.getResultColumns();\r
+ SubqueryList dummySubqueryList =\r
+ (SubqueryList) getNodeFactory().getNode(\r
+ C_NodeTypes.SUBQUERY_LIST,\r
+ getContextManager());\r
+ int numColsAddedHere = 0;\r
+ int size = size();\r
+\r
+ /* Only 32677 columns allowed in GROUP BY clause */\r
+ if (size > Limits.DB2_MAX_ELEMENTS_IN_GROUP_BY)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TOO_MANY_ELEMENTS);\r
+ }\r
+\r
+ /* Bind the grouping column */\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ GroupByColumn groupByCol = (GroupByColumn) elementAt(index);\r
+ groupByCol.bindExpression(fromList,\r
+ dummySubqueryList, aggregateVector);\r
+ }\r
+\r
+ \r
+ int rclSize = selectRCL.size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ boolean matchFound = false;\r
+ GroupByColumn groupingCol = (GroupByColumn) elementAt(index);\r
+\r
+ /* Verify that this entry in the GROUP BY list matches a\r
+ * grouping column in the select list.\r
+ */\r
+ for (int inner = 0; inner < rclSize; inner++)\r
+ {\r
+ ResultColumn selectListRC = (ResultColumn) selectRCL.elementAt(inner);\r
+ if (!(selectListRC.getExpression() instanceof ColumnReference)) {\r
+ continue;\r
+ }\r
+ \r
+ ColumnReference selectListCR = (ColumnReference) selectListRC.getExpression();\r
+\r
+ if (selectListCR.isEquivalent(groupingCol.getColumnExpression())) { \r
+ /* Column positions for grouping columns are 0-based */\r
+ groupingCol.setColumnPosition(inner + 1);\r
+\r
+ /* Mark the RC in the SELECT list as a grouping column */\r
+ selectListRC.markAsGroupingColumn();\r
+ matchFound = true;\r
+ break;\r
+ }\r
+ }\r
+ /* If no match found in the SELECT list, then add a matching\r
+ * ResultColumn/ColumnReference pair to the SelectNode's RCL.\r
+ * However, don't add additional result columns if the query\r
+ * specified DISTINCT, because distinct processing considers\r
+ * the entire RCL and including extra columns could change the\r
+ * results: e.g. select distinct a,b from t group by a,b,c\r
+ * should not consider column c in distinct processing (DERBY-3613)\r
+ */\r
+ if (! matchFound && !select.hasDistinct() &&\r
+ groupingCol.getColumnExpression() instanceof ColumnReference) \r
+ {\r
+ // only add matching columns for column references not \r
+ // expressions yet. See DERBY-883 for details. \r
+ ResultColumn newRC;\r
+\r
+ /* Get a new ResultColumn */\r
+ newRC = (ResultColumn) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN,\r
+ groupingCol.getColumnName(),\r
+ groupingCol.getColumnExpression().getClone(),\r
+ getContextManager());\r
+ newRC.setVirtualColumnId(selectRCL.size() + 1);\r
+ newRC.markGenerated();\r
+ newRC.markAsGroupingColumn();\r
+\r
+ /* Add the new RC/CR to the RCL */\r
+ selectRCL.addElement(newRC);\r
+\r
+ /* Set the columnPosition in the GroupByColumn, now that it\r
+ * has a matching entry in the SELECT list.\r
+ */\r
+ groupingCol.setColumnPosition(selectRCL.size());\r
+ \r
+ // a new hidden or generated column is added to this RCL\r
+ // i.e. that the size() of the RCL != visibleSize(). \r
+ // Error checking done later should be aware of this \r
+ // special case.\r
+ selectRCL.setCountMismatchAllowed(true);\r
+\r
+ /*\r
+ ** Track the number of columns that we have added\r
+ ** in this routine. We track this separately\r
+ ** than the total number of columns added by this\r
+ ** object (numGroupingColsAdded) because we\r
+ ** might be bound (though not gagged) more than\r
+ ** once (in which case numGroupingColsAdded will\r
+ ** already be set).\r
+ */\r
+ numColsAddedHere++;\r
+ }\r
+ if (groupingCol.getColumnExpression() instanceof JavaToSQLValueNode) \r
+ {\r
+ // disallow any expression which involves native java computation. \r
+ // Not possible to consider java expressions for equivalence.\r
+ throw StandardException.newException( \r
+ SQLState.LANG_INVALID_GROUPED_SELECT_LIST);\r
+ }\r
+ }\r
+\r
+ /* Verify that no subqueries got added to the dummy list */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(dummySubqueryList.size() == 0,\r
+ "dummySubqueryList.size() is expected to be 0");\r
+ }\r
+\r
+ numGroupingColsAdded+= numColsAddedHere;\r
+ }\r
+\r
+ \r
+\r
+ /**\r
+ * Find the matching grouping column if any for the given expression\r
+ * \r
+ * @param node an expression for which we are trying to find a match\r
+ * in the group by list.\r
+ * \r
+ * @return the matching GroupByColumn if one exists, null otherwise.\r
+ * \r
+ * @throws StandardException\r
+ */\r
+ public GroupByColumn findGroupingColumn(ValueNode node)\r
+ throws StandardException\r
+ {\r
+ int sz = size();\r
+ for (int i = 0; i < sz; i++) \r
+ {\r
+ GroupByColumn gbc = (GroupByColumn)elementAt(i);\r
+ if (gbc.getColumnExpression().isEquivalent(node))\r
+ {\r
+ return gbc;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void remapColumnReferencesToExpressions() throws StandardException\r
+ {\r
+ GroupByColumn gbc;\r
+ int size = size();\r
+\r
+ /* This method is called when flattening a FromTable. We should\r
+ * not be flattening a FromTable if the underlying expression that\r
+ * will get returned out, after chopping out the redundant ResultColumns,\r
+ * is not a ColumnReference. (See ASSERT below.)\r
+ */\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ ValueNode retVN;\r
+ gbc = (GroupByColumn) elementAt(index);\r
+\r
+ retVN = gbc.getColumnExpression().remapColumnReferencesToExpressions();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(retVN instanceof ColumnReference,\r
+ "retVN expected to be instanceof ColumnReference, not " +\r
+ retVN.getClass().getName());\r
+ }\r
+\r
+ gbc.setColumnExpression(retVN);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Print it out, baby\r
+ */\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ StringBuffer buf = new StringBuffer();\r
+\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ GroupByColumn groupingCol = (GroupByColumn) elementAt(index);\r
+\r
+ buf.append(groupingCol.toString());\r
+ }\r
+ return buf.toString();\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ public void preprocess(\r
+ int numTables, FromList fromList, SubqueryList whereSubquerys, \r
+ PredicateList wherePredicates) throws StandardException \r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ GroupByColumn groupingCol = (GroupByColumn) elementAt(index);\r
+ groupingCol.setColumnExpression(\r
+ groupingCol.getColumnExpression().preprocess(\r
+ numTables, fromList, whereSubquerys, wherePredicates));\r
+ } \r
+ }\r
+}\r