001: package com.quadcap.sql;
002:
003: /* Copyright 1999 - 2003 Quadcap Software. All rights reserved.
004: *
005: * This software is distributed under the Quadcap Free Software License.
006: * This software may be used or modified for any purpose, personal or
007: * commercial. Open Source redistributions are permitted. Commercial
008: * redistribution of larger works derived from, or works which bundle
009: * this software requires a "Commercial Redistribution License"; see
010: * http://www.quadcap.com/purchase.
011: *
012: * Redistributions qualify as "Open Source" under one of the following terms:
013: *
014: * Redistributions are made at no charge beyond the reasonable cost of
015: * materials and delivery.
016: *
017: * Redistributions are accompanied by a copy of the Source Code or by an
018: * irrevocable offer to provide a copy of the Source Code for up to three
019: * years at the cost of materials and delivery. Such redistributions
020: * must allow further use, modification, and redistribution of the Source
021: * Code under substantially the same terms as this license.
022: *
023: * Redistributions of source code must retain the copyright notices as they
024: * appear in each source code file, these license terms, and the
025: * disclaimer/limitation of liability set forth as paragraph 6 below.
026: *
027: * Redistributions in binary form must reproduce this Copyright Notice,
028: * these license terms, and the disclaimer/limitation of liability set
029: * forth as paragraph 6 below, in the documentation and/or other materials
030: * provided with the distribution.
031: *
032: * The Software is provided on an "AS IS" basis. No warranty is
033: * provided that the Software is free of defects, or fit for a
034: * particular purpose.
035: *
036: * Limitation of Liability. Quadcap Software shall not be liable
037: * for any damages suffered by the Licensee or any third party resulting
038: * from use of the Software.
039: */
040:
041: import java.io.Externalizable;
042: import java.io.IOException;
043: import java.io.ObjectInput;
044: import java.io.ObjectOutput;
045:
046: import java.util.Enumeration;
047: import java.util.Vector;
048:
049: import java.sql.SQLException;
050:
051: import com.quadcap.sql.types.Op;
052: import com.quadcap.sql.types.Type;
053: import com.quadcap.sql.types.TypeInt;
054: import com.quadcap.sql.types.Value;
055: import com.quadcap.sql.types.ValueDouble;
056: import com.quadcap.sql.types.ValueInteger;
057: import com.quadcap.sql.types.ValueNull;
058:
059: import com.quadcap.sql.index.Btree;
060:
061: import com.quadcap.util.Debug;
062:
063: /**
064: * Expression implementing one of <b>AVG</b>, <b>SUM</b>,
065: * <b>MIN</b>, or <b>MAX</b>.
066: *
067: * @author Stan Bailes
068: */
069: public class AggregateExpression extends Expression implements
070: Externalizable {
071: public static final int AVG = 0;
072: public static final int SUM = 1;
073: public static final int MIN = 2;
074: public static final int MAX = 3;
075: public static final int COUNT = 4;
076:
077: int op = -1;
078: boolean all = false;
079: Expression expr = null;
080:
081: static String[] ops = { "AVG", "SUM", "MIN", "MAX", "COUNT" };
082:
083: static String toString(int op) {
084: try {
085: return ops[op];
086: } catch (Throwable y) {
087: return "bad op<" + op + ">";
088: }
089: }
090:
091: static final byte[] aByte = { 0 };
092:
093: /**
094: * Default constructor
095: */
096: public AggregateExpression() {
097: }
098:
099: /**
100: * Explicit constructor
101: */
102: public AggregateExpression(int op, boolean all, Expression expr) {
103: this .op = op;
104: this .all = all;
105: this .expr = expr;
106: }
107:
108: Expression getInnerExpression() {
109: return expr;
110: }
111:
112: boolean isMin() {
113: return op == MIN;
114: }
115:
116: boolean isMax() {
117: return op == MAX;
118: }
119:
120: boolean isCount() {
121: return op == COUNT;
122: }
123:
124: /**
125: * Private class to maintain per-session state for this expression
126: */
127: class AggregateSessionState implements StatementContext {
128: Session session;
129: Btree distinct = null;
130:
131: Value accum = null;
132: int count = 0;
133:
134: public AggregateSessionState(Session session) {
135: this .session = session;
136: }
137:
138: /** Whenever you get around to finishing me is fine. */
139: public int priority() {
140: return 4;
141: }
142:
143: /** Girl I believe I got to move on down the line. */
144: public void finish(boolean abort) throws IOException {
145: try {
146: if (distinct != null) {
147: distinct.free();
148: }
149: } finally {
150: if (distinct != null)
151: session.getDatabase().releaseTempFile();
152: distinct = null;
153: }
154: }
155:
156: /** Get ready for another day */
157: public void reset() throws IOException {
158: count = 0;
159: accum = null;
160: finish(false);
161: }
162: }
163:
164: public void reset(Session session) throws IOException {
165: AggregateSessionState s = getSessionState(session);
166: if (s != null)
167: s.reset();
168: }
169:
170: AggregateSessionState getSessionState(Session session) {
171: return getSessionState(session, true);
172: }
173:
174: AggregateSessionState getSessionState(Session session, boolean mk) {
175: AggregateSessionState s = (AggregateSessionState) session
176: .getContext(this , false);
177: if (s == null && mk) {
178: s = new AggregateSessionState(session);
179: session.putContext(this , false, s);
180: }
181: return s;
182: }
183:
184: public int rank() {
185: return 0;
186: }
187:
188: public Type getType(Session session, Cursor cursor)
189: throws SQLException {
190: switch (op) {
191: case COUNT:
192: return TypeInt.typeInt;
193: default:
194: return expr.getType(session, cursor);
195: }
196: }
197:
198: public Value getValue(Session session, Cursor cursor)
199: throws SQLException {
200: AggregateSessionState s = getSessionState(session);
201: switch (op) {
202: case AVG:
203: if (s.accum == null) {
204: return ValueNull.valueNull;
205: }
206: return Value.binop(Op.DIVIDE, s.accum, new ValueInteger(
207: s.count));
208: case SUM:
209: case MIN:
210: case MAX:
211: if (s.accum == null)
212: return ValueNull.valueNull;
213: return s.accum;
214: case COUNT:
215: return new ValueInteger(s.count);
216: default:
217: throw new SQLException("Bad aggregate type: " + op, "42000");
218: }
219: }
220:
221: public void updateAggregate(Session session, Cursor cursor)
222: throws SQLException {
223: AggregateSessionState s = getSessionState(session);
224: Value v1 = null;
225: if (cursor != null) {
226: if (expr != null) {
227: v1 = expr.getValue(session, cursor);
228: }
229: if (!Value.isNull(v1)) {
230: if (all) {
231: s.count++;
232: } else {
233: try {
234: if (s.distinct == null) {
235: s.distinct = session.makeTempTree();
236: }
237: byte[] key = Value.bytes(v1);
238: if (!s.distinct.set(key, key.length, aByte, 0,
239: 1)) {
240: s.count++;
241: } else {
242: v1 = ValueNull.valueNull;
243: }
244: } catch (IOException e) {
245: throw DbException.wrapThrowable(e);
246: }
247: }
248: }
249: } else {
250: v1 = ValueNull.valueNull;
251: }
252: switch (op) {
253: case AVG:
254: case SUM:
255: if (!Value.isNull(v1)) {
256: if (s.accum == null) {
257: s.accum = v1;
258: } else {
259: s.accum = Value.binop(Op.PLUS, v1, s.accum);
260: }
261: }
262: break;
263: case MIN:
264: if (!Value.isNull(v1)) {
265: if (s.accum == null || Value.boolOp(Op.LT, v1, s.accum)) {
266: s.accum = v1;
267: }
268: }
269: break;
270: case MAX:
271: if (!Value.isNull(v1)) {
272: if (s.accum == null || Value.boolOp(Op.GT, v1, s.accum)) {
273: s.accum = v1;
274: }
275: }
276: break;
277: case COUNT:
278: break;
279: default:
280: throw new SQLException("Bad aggregate type: " + op, "42000");
281: }
282: }
283:
284: public void invert() {
285: }
286:
287: public String toString() {
288: StringBuffer sb = new StringBuffer(toString(op));
289: if (!all)
290: sb.append(" DISTINCT");
291: sb.append(' ');
292: sb.append(String.valueOf(expr));
293: return sb.toString();
294: }
295:
296: public void visitSubExpressions(ExpressionVisitor ev) {
297: ev.visit(expr);
298: }
299:
300: public void readExternal(ObjectInput in) throws IOException,
301: ClassNotFoundException {
302: expr = (Expression) in.readObject();
303: op = in.readInt();
304: all = (in.read() == 1);
305: }
306:
307: public void writeExternal(ObjectOutput out) throws IOException {
308: out.writeObject(expr);
309: out.writeInt(op);
310: out.write(all ? 1 : 0);
311: }
312: }
|