001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.GroupByList
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.error.StandardException;
029:
030: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
031:
032: import org.apache.derby.iapi.reference.SQLState;
033: import org.apache.derby.iapi.reference.Limits;
034:
035: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
036:
037: import org.apache.derby.iapi.util.ReuseFactory;
038:
039: import java.util.Vector;
040:
041: /**
042: * A GroupByList represents the list of expressions in a GROUP BY clause in
043: * a SELECT statement.
044: *
045: * @author Jeff Lichtman
046: */
047:
048: public class GroupByList extends OrderedColumnList {
049: int numGroupingColsAdded = 0;
050:
051: /**
052: Add a column to the list
053:
054: @param column The column to add to the list
055: */
056: public void addGroupByColumn(GroupByColumn column) {
057: addElement(column);
058: }
059:
060: /**
061: Get a column from the list
062:
063: @param position The column to get from the list
064: */
065: public GroupByColumn getGroupByColumn(int position) {
066: if (SanityManager.DEBUG) {
067: SanityManager.ASSERT(position >= 0 && position < size(),
068: "position (" + position
069: + ") expected to be between 0 and "
070: + size());
071: }
072: return (GroupByColumn) elementAt(position);
073: }
074:
075: /**
076: Print the list.
077:
078: @param depth The depth at which to indent the sub-nodes
079: */
080: public void printSubNodes(int depth) {
081: if (SanityManager.DEBUG) {
082: for (int index = 0; index < size(); index++) {
083: ((GroupByColumn) elementAt(index)).treePrint(depth);
084: }
085: }
086: }
087:
088: /**
089: * Get the number of grouping columns that need to be added to the SELECT list.
090: *
091: * @return int The number of grouping columns that need to be added to
092: * the SELECT list.
093: */
094: public int getNumNeedToAddGroupingCols() {
095: return numGroupingColsAdded;
096: }
097:
098: /**
099: * Bind the group by list. Verify:
100: * o Number of grouping columns matches number of non-aggregates in
101: * SELECT's RCL.
102: * o Names in the group by list are unique
103: * o Names of grouping columns match names of non-aggregate
104: * expressions in SELECT's RCL.
105: *
106: * @param select The SelectNode
107: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
108: *
109: * @exception StandardException Thrown on error
110: */
111: public void bindGroupByColumns(SelectNode select,
112: Vector aggregateVector) throws StandardException {
113: FromList fromList = select.getFromList();
114: ResultColumnList selectRCL = select.getResultColumns();
115: SubqueryList dummySubqueryList = (SubqueryList) getNodeFactory()
116: .getNode(C_NodeTypes.SUBQUERY_LIST, getContextManager());
117: int numColsAddedHere = 0;
118: int size = size();
119:
120: /* Only 32677 columns allowed in GROUP BY clause */
121: if (size > Limits.DB2_MAX_ELEMENTS_IN_GROUP_BY) {
122: throw StandardException
123: .newException(SQLState.LANG_TOO_MANY_ELEMENTS);
124: }
125:
126: /* Bind the grouping column */
127: for (int index = 0; index < size; index++) {
128: GroupByColumn groupByCol = (GroupByColumn) elementAt(index);
129: groupByCol.bindExpression(fromList, dummySubqueryList,
130: aggregateVector);
131: }
132:
133: int rclSize = selectRCL.size();
134: for (int index = 0; index < size; index++) {
135: boolean matchFound = false;
136: GroupByColumn groupingCol = (GroupByColumn) elementAt(index);
137:
138: /* Verify that this entry in the GROUP BY list matches a
139: * grouping column in the select list.
140: */
141: for (int inner = 0; inner < rclSize; inner++) {
142: ResultColumn selectListRC = (ResultColumn) selectRCL
143: .elementAt(inner);
144: if (!(selectListRC.getExpression() instanceof ColumnReference)) {
145: continue;
146: }
147:
148: ColumnReference selectListCR = (ColumnReference) selectListRC
149: .getExpression();
150:
151: if (selectListCR.isEquivalent(groupingCol
152: .getColumnExpression())) {
153: /* Column positions for grouping columns are 0-based */
154: groupingCol.setColumnPosition(inner + 1);
155:
156: /* Mark the RC in the SELECT list as a grouping column */
157: selectListRC.markAsGroupingColumn();
158: matchFound = true;
159: break;
160: }
161: }
162: /* If no match found in the SELECT list, then add a matching
163: * ResultColumn/ColumnReference pair to the SelectNode's RCL.
164: */
165: if (!matchFound
166: && groupingCol.getColumnExpression() instanceof ColumnReference) {
167: // only add matching columns for column references not
168: // expressions yet. See DERBY-883 for details.
169: ResultColumn newRC;
170:
171: /* Get a new ResultColumn */
172: newRC = (ResultColumn) getNodeFactory().getNode(
173: C_NodeTypes.RESULT_COLUMN,
174: groupingCol.getColumnName(),
175: groupingCol.getColumnExpression().getClone(),
176: getContextManager());
177: newRC.setVirtualColumnId(selectRCL.size() + 1);
178: newRC.markGenerated();
179: newRC.markAsGroupingColumn();
180:
181: /* Add the new RC/CR to the RCL */
182: selectRCL.addElement(newRC);
183:
184: /* Set the columnPosition in the GroupByColumn, now that it
185: * has a matching entry in the SELECT list.
186: */
187: groupingCol.setColumnPosition(selectRCL.size());
188:
189: /*
190: ** Track the number of columns that we have added
191: ** in this routine. We track this separately
192: ** than the total number of columns added by this
193: ** object (numGroupingColsAdded) because we
194: ** might be bound (though not gagged) more than
195: ** once (in which case numGroupingColsAdded will
196: ** already be set).
197: */
198: numColsAddedHere++;
199: }
200: }
201:
202: /* Verify that no subqueries got added to the dummy list */
203: if (SanityManager.DEBUG) {
204: SanityManager.ASSERT(dummySubqueryList.size() == 0,
205: "dummySubqueryList.size() is expected to be 0");
206: }
207:
208: numGroupingColsAdded += numColsAddedHere;
209: }
210:
211: /**
212: * Find the matching grouping column if any for the given expression
213: *
214: * @param node an expression for which we are trying to find a match
215: * in the group by list.
216: *
217: * @return the matching GroupByColumn if one exists, null otherwise.
218: *
219: * @throws StandardException
220: */
221: public GroupByColumn findGroupingColumn(ValueNode node)
222: throws StandardException {
223: int sz = size();
224: for (int i = 0; i < sz; i++) {
225: GroupByColumn gbc = (GroupByColumn) elementAt(i);
226: if (gbc.getColumnExpression().isEquivalent(node)) {
227: return gbc;
228: }
229: }
230: return null;
231: }
232:
233: /**
234: * Remap all ColumnReferences in this tree to be clones of the
235: * underlying expression.
236: *
237: * @exception StandardException Thrown on error
238: */
239: public void remapColumnReferencesToExpressions()
240: throws StandardException {
241: GroupByColumn gbc;
242: int size = size();
243:
244: /* This method is called when flattening a FromTable. We should
245: * not be flattening a FromTable if the underlying expression that
246: * will get returned out, after chopping out the redundant ResultColumns,
247: * is not a ColumnReference. (See ASSERT below.)
248: */
249: for (int index = 0; index < size; index++) {
250: ValueNode retVN;
251: gbc = (GroupByColumn) elementAt(index);
252:
253: retVN = gbc.getColumnExpression()
254: .remapColumnReferencesToExpressions();
255:
256: if (SanityManager.DEBUG) {
257: SanityManager.ASSERT(retVN instanceof ColumnReference,
258: "retVN expected to be instanceof ColumnReference, not "
259: + retVN.getClass().getName());
260: }
261:
262: gbc.setColumnExpression(retVN);
263: }
264: }
265:
266: /**
267: * Print it out, baby
268: */
269: public String toString() {
270: if (SanityManager.DEBUG) {
271: StringBuffer buf = new StringBuffer();
272:
273: for (int index = 0; index < size(); index++) {
274: GroupByColumn groupingCol = (GroupByColumn) elementAt(index);
275:
276: buf.append(groupingCol.toString());
277: }
278: return buf.toString();
279: } else {
280: return "";
281: }
282: }
283:
284: public void preprocess(int numTables, FromList fromList,
285: SubqueryList whereSubquerys, PredicateList wherePredicates)
286: throws StandardException {
287: for (int index = 0; index < size(); index++) {
288: GroupByColumn groupingCol = (GroupByColumn) elementAt(index);
289: groupingCol.setColumnExpression(groupingCol
290: .getColumnExpression().preprocess(numTables,
291: fromList, whereSubquerys, wherePredicates));
292: }
293: }
294: }
|