001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import org.hsqldb.lib.ArrayUtil;
034: import org.hsqldb.lib.HashSet;
035: import org.hsqldb.lib.Iterator;
036:
037: /**
038: * This class is used for grouping select results, especially for select
039: * statements that include group by clause and nested aggregate functions.
040: * It is used by the <b>Select</b> class regardless the existence of group by
041: * clause.
042: * <p>
043: * When a group by clause is defined, a <b>ResultGroup</b> is used to hold
044: * all column values and <b>AggregatingValue</b>s for each group. When a group
045: * by clause is not defined, one <b>ResultGroup</b> is used to hold all the
046: * results.<p>
047: *
048: * All <b>ResultGroup</b>s are placed in a <b>HashSet</b>. Adding a new row
049: * will first retrieve the corresponding group from the table, based on the
050: * values in the group by columns. If a group is found, then the row
051: * associated with the group will be returned. Otherwise a new group is
052: * created with the new row, and the new row is returned.
053: * <p>
054: * The <b>Select</b> can then update the values and <b>AggregatingValue</b>s
055: * in the returned row, rather than the original row. This approach enables
056: * nested aggregate functions, such as "count(id)+2, 20-count(id),
057: * max(id)-min(id)" support.
058: *
059: * @author Tony Lai
060: * @version 1.7.2
061: * @since 1.7.2
062: * @see Expression
063: * @see Select
064: */
065:
066: // fredt@users - patch 1.7.2 - mods to use new HashSet class and to separate addRow and getRow operations
067: class GroupedResult {
068:
069: /** @todo fredt - initialise results on first use */
070: private Result result;
071: int groupBegin;
072: int groupEnd;
073: private final boolean isGrouped;
074: private final boolean isAggregated;
075: private HashSet groups;
076: private ResultGroup currGroup;
077:
078: GroupedResult(Select select, Result.ResultMetaData meta) {
079:
080: result = new Result(meta);
081: groupBegin = select.iResultLen;
082: groupEnd = groupBegin + select.iGroupLen;
083: isGrouped = groupBegin != groupEnd;
084: isAggregated = select.isAggregated;
085:
086: if (isGrouped) {
087: groups = new HashSet();
088: }
089: }
090:
091: Object[] getRow(Object[] row) {
092:
093: if (isGrouped) {
094: ResultGroup newGroup = new ResultGroup(row);
095: ResultGroup group = (ResultGroup) groups.get(newGroup);
096:
097: if (group != null) {
098: ArrayUtil.copyArray(group.row, row, row.length);
099: }
100: } else if (isAggregated) {
101: if (currGroup != null) {
102: ArrayUtil.copyArray(currGroup.row, row, row.length);
103: }
104: }
105:
106: return row;
107: }
108:
109: void addRow(Object[] row) {
110:
111: if (isGrouped) {
112: ResultGroup newGroup = new ResultGroup(row);
113:
114: currGroup = (ResultGroup) groups.get(newGroup);
115:
116: if (currGroup == null) {
117: currGroup = newGroup;
118:
119: groups.add(currGroup);
120: result.add(row);
121: } else {
122: System.arraycopy(row, 0, currGroup.row, 0, row.length);
123: }
124: } else if (isAggregated) {
125: if (currGroup == null) {
126: currGroup = new ResultGroup(row);
127:
128: result.add(row);
129: } else {
130: System.arraycopy(row, 0, currGroup.row, 0, row.length);
131: }
132: } else {
133: result.add(row);
134: }
135: }
136:
137: int size() {
138: return result.getSize();
139: }
140:
141: Iterator iterator() {
142: return result.iterator();
143: }
144:
145: Result getResult() {
146: return result;
147: }
148:
149: class ResultGroup {
150:
151: Object[] row;
152: int hashCode;
153:
154: private ResultGroup(Object[] row) {
155:
156: this .row = row;
157: hashCode = 0;
158:
159: for (int i = groupBegin; i < groupEnd; i++) {
160: if (row[i] != null) {
161: hashCode += row[i].hashCode();
162: }
163: }
164: }
165:
166: public int hashCode() {
167: return hashCode;
168: }
169:
170: public boolean equals(Object obj) {
171:
172: if (obj == this ) {
173: return true;
174: }
175:
176: if (obj == null || !(obj instanceof ResultGroup)) {
177: return false;
178: }
179:
180: ResultGroup group = (ResultGroup) obj;
181:
182: for (int i = groupBegin; i < groupEnd; i++) {
183: if (!equals(row[i], group.row[i])) {
184: return false;
185: }
186: }
187:
188: return true;
189: }
190:
191: private boolean equals(Object o1, Object o2) {
192: return (o1 == null) ? o2 == null : o1.equals(o2);
193: }
194: }
195: }
|