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 java.lang.Math;
034: import java.math.BigDecimal;
035: import java.math.BigInteger;
036:
037: import org.hsqldb.lib.HashSet;
038: import org.hsqldb.store.ValuePool;
039:
040: /**
041: * Implementation of SQL set functions (currently only aggregate functions).
042: * This reduces temporary Object creation by SUM and AVG functions for
043: * INTEGER and narrower types.
044: *
045: * @author fredt@users
046: * @author boucherb@users
047: * @version 1.7.3
048: * @since 1.7.2
049: *
050: */
051: public class SetFunction {
052:
053: private HashSet distinctValues;
054: private boolean isDistinct;
055:
056: //
057: private int setType;
058: private int type;
059:
060: //
061: private int count;
062:
063: //
064: private boolean hasNull;
065: private boolean every = true;
066: private boolean some = false;
067: private long currentLong;
068: private double currentDouble;
069: private BigDecimal currentBigDecimal;
070: private Object currentValue;
071:
072: SetFunction(int setType, int type, boolean isDistinct) {
073:
074: this .setType = setType;
075: this .type = type;
076:
077: if (isDistinct) {
078: this .isDistinct = true;
079: distinctValues = new HashSet();
080: }
081:
082: if (setType == Expression.VAR_SAMP
083: || setType == Expression.STDDEV_SAMP) {
084: this .sample = true;
085: }
086: }
087:
088: void add(Session session, Object item) throws HsqlException {
089:
090: if (item == null) {
091: hasNull = true;
092:
093: return;
094: }
095:
096: if (isDistinct && !distinctValues.add(item)) {
097: return;
098: }
099:
100: count++;
101:
102: switch (setType) {
103:
104: case Expression.COUNT:
105: return;
106:
107: case Expression.AVG:
108: case Expression.SUM: {
109: switch (type) {
110:
111: case Types.TINYINT:
112: case Types.SMALLINT:
113: case Types.INTEGER:
114: currentLong += ((Number) item).intValue();
115:
116: return;
117:
118: case Types.BIGINT:
119: addLong(((Number) item).longValue());
120:
121: return;
122:
123: case Types.REAL:
124: case Types.FLOAT:
125: case Types.DOUBLE:
126: currentDouble += ((Number) item).doubleValue();
127:
128: return;
129:
130: case Types.NUMERIC:
131: case Types.DECIMAL:
132: if (currentBigDecimal == null) {
133: currentBigDecimal = (BigDecimal) item;
134: } else {
135: currentBigDecimal = currentBigDecimal
136: .add((BigDecimal) item);
137: }
138:
139: return;
140:
141: default:
142: throw Trace.error(Trace.SUM_OF_NON_NUMERIC);
143: }
144: }
145: case Expression.MIN: {
146: if (currentValue == null) {
147: currentValue = item;
148:
149: return;
150: }
151:
152: if (Column.compare(session.database.collation,
153: currentValue, item, type) > 0) {
154: currentValue = item;
155: }
156:
157: return;
158: }
159: case Expression.MAX: {
160: if (currentValue == null) {
161: currentValue = item;
162:
163: return;
164: }
165:
166: if (Column.compare(session.database.collation,
167: currentValue, item, type) < 0) {
168: currentValue = item;
169: }
170:
171: return;
172: }
173: case Expression.EVERY:
174: if (!(item instanceof Boolean)) {
175: throw Trace.error(Trace.WRONG_DATA_TYPE);
176: }
177:
178: every &= ((Boolean) item).booleanValue();
179:
180: return;
181:
182: case Expression.SOME:
183: if (!(item instanceof Boolean)) {
184: throw Trace.error(Trace.WRONG_DATA_TYPE);
185: }
186:
187: some |= ((Boolean) item).booleanValue();
188:
189: return;
190:
191: case Expression.STDDEV_POP:
192: case Expression.STDDEV_SAMP:
193: case Expression.VAR_POP:
194: case Expression.VAR_SAMP:
195: if (!(item instanceof Number)) {
196: throw Trace.error(Trace.WRONG_DATA_TYPE);
197: }
198:
199: addDataPoint((Number) item);
200:
201: return;
202: }
203: }
204:
205: Object getValue() throws HsqlException {
206:
207: if (setType == Expression.COUNT) {
208: return ValuePool.getInt(count);
209: }
210:
211: if (count == 0) {
212: return null;
213: }
214:
215: switch (setType) {
216:
217: case Expression.AVG: {
218: switch (type) {
219:
220: case Types.TINYINT:
221: case Types.SMALLINT:
222: case Types.INTEGER:
223: return new Long(currentLong / count);
224:
225: case Types.BIGINT: {
226: long value = getLongSum().divide(
227: BigInteger.valueOf(count)).longValue();
228:
229: return new Long(value);
230: }
231: case Types.REAL:
232: case Types.FLOAT:
233: case Types.DOUBLE:
234: return new Double(currentDouble / count);
235:
236: case Types.NUMERIC:
237: case Types.DECIMAL:
238: return currentBigDecimal.divide(new BigDecimal(count),
239: BigDecimal.ROUND_HALF_DOWN);
240:
241: default:
242: throw Trace.error(Trace.SUM_OF_NON_NUMERIC);
243: }
244: }
245: case Expression.SUM: {
246: switch (type) {
247:
248: case Types.TINYINT:
249: case Types.SMALLINT:
250: case Types.INTEGER:
251: return new Long(currentLong);
252:
253: case Types.BIGINT:
254: return new BigDecimal(getLongSum());
255:
256: case Types.REAL:
257: case Types.FLOAT:
258: case Types.DOUBLE:
259: return new Double(currentDouble);
260:
261: case Types.NUMERIC:
262: case Types.DECIMAL:
263: return currentBigDecimal;
264:
265: default:
266: throw Trace.error(Trace.SUM_OF_NON_NUMERIC);
267: }
268: }
269: case Expression.MIN:
270: case Expression.MAX:
271: return currentValue;
272:
273: case Expression.EVERY:
274: return every ? Boolean.TRUE : Boolean.FALSE;
275:
276: case Expression.SOME:
277: return some ? Boolean.TRUE : Boolean.FALSE;
278:
279: case Expression.STDDEV_POP:
280: case Expression.STDDEV_SAMP:
281: return getStdDev();
282:
283: case Expression.VAR_POP:
284: case Expression.VAR_SAMP:
285: return getVariance();
286:
287: default:
288: throw Trace.error(Trace.INVALID_CONVERSION);
289: }
290: }
291:
292: /**
293: * During parsing and before an instance of SetFunction is created,
294: * getType is called with type parameter set to correct type when main
295: * SELECT statements contain aggregates. It is called with Types.NULL
296: * when SELECT statements within INSERT or UPDATE contian aggregates.
297: *
298: */
299: static int getType(int setType, int type) throws HsqlException {
300:
301: switch (setType) {
302:
303: case Expression.COUNT:
304: return Types.INTEGER;
305:
306: case Expression.AVG: {
307: switch (type) {
308:
309: case Types.TINYINT:
310: case Types.SMALLINT:
311: case Types.INTEGER:
312: case Types.BIGINT:
313: return Types.BIGINT;
314:
315: case Types.REAL:
316: case Types.FLOAT:
317: case Types.DOUBLE:
318: return Types.DOUBLE;
319:
320: case Types.NUMERIC:
321: case Types.DECIMAL:
322: return Types.DECIMAL;
323:
324: default:
325: return Types.NULL;
326: }
327: }
328: case Expression.SUM: {
329: switch (type) {
330:
331: case Types.TINYINT:
332: case Types.SMALLINT:
333: case Types.INTEGER:
334: return Types.BIGINT;
335:
336: case Types.BIGINT:
337: return Types.DECIMAL;
338:
339: case Types.REAL:
340: case Types.FLOAT:
341: case Types.DOUBLE:
342: return Types.DOUBLE;
343:
344: case Types.NUMERIC:
345: case Types.DECIMAL:
346: return Types.DECIMAL;
347:
348: default:
349: return Types.NULL;
350: }
351: }
352: case Expression.MIN:
353: case Expression.MAX:
354: return type;
355:
356: case Expression.EVERY:
357: case Expression.SOME:
358: return Types.BOOLEAN;
359:
360: case Expression.STDDEV_POP:
361: case Expression.STDDEV_SAMP:
362: case Expression.VAR_POP:
363: case Expression.VAR_SAMP:
364: return Types.DOUBLE;
365:
366: default:
367: throw Trace.error(Trace.INVALID_CONVERSION);
368: }
369: }
370:
371: // long sum - originally a separate class
372:
373: /**
374: * Maintain the sum of multiple long values without creating a new
375: * BigInteger object for each addition.
376: */
377: static BigInteger multiplier = BigInteger
378: .valueOf(0x0000000100000000L);
379:
380: // BigInteger bigint = BigInteger.ZERO;
381: long hi;
382: long lo;
383:
384: void addLong(long value) {
385:
386: if (value == 0) {
387: } else if (value > 0) {
388: hi += value >> 32;
389: lo += value & 0x00000000ffffffffL;
390: } else {
391: if (value == Long.MIN_VALUE) {
392: hi -= 0x000000080000000L;
393: } else {
394: long temp = ~value + 1;
395:
396: hi -= temp >> 32;
397: lo -= temp & 0x00000000ffffffffL;
398: }
399: }
400:
401: // bigint = bigint.add(BigInteger.valueOf(value));
402: }
403:
404: BigInteger getLongSum() throws HsqlException {
405:
406: BigInteger biglo = BigInteger.valueOf(lo);
407: BigInteger bighi = BigInteger.valueOf(hi);
408: BigInteger result = (bighi.multiply(multiplier)).add(biglo);
409:
410: /*
411: if ( result.compareTo(bigint) != 0 ){
412: throw Trace.error(Trace.GENERAL_ERROR, "longSum mismatch");
413: }
414: */
415: return result;
416: }
417:
418: // end long sum
419: // statistics support - written by Campbell
420: // this section was orginally an independent class
421: private double sk;
422: private double vk;
423: private long n;
424: private boolean initialized;
425: private boolean sample;
426:
427: private void addDataPoint(Number x) { // optimized
428:
429: double xi;
430: double xsi;
431: long nm1;
432:
433: if (x == null) {
434: return;
435: }
436:
437: xi = x.doubleValue();
438:
439: if (!initialized) {
440: n = 1;
441: sk = xi;
442: vk = 0.0;
443: initialized = true;
444:
445: return;
446: }
447:
448: n++;
449:
450: nm1 = (n - 1);
451: xsi = (sk - (xi * nm1));
452: vk += ((xsi * xsi) / n) / nm1;
453: sk += xi;
454: }
455:
456: private Number getVariance() {
457:
458: if (!initialized) {
459: return null;
460: }
461:
462: return sample ? (n == 1) ? null // NULL (not NaN) is correct in this case
463: : new Double(vk / (double) (n - 1)) : new Double(vk
464: / (double) (n));
465: }
466:
467: private Number getStdDev() {
468:
469: if (!initialized) {
470: return null;
471: }
472:
473: return sample ? (n == 1) ? null // NULL (not NaN) is correct in this case
474: : new Double(Math.sqrt(vk / (double) (n - 1)))
475: : new Double(Math.sqrt(vk / (double) (n)));
476: }
477:
478: // end statistics support
479: }
|