00001
package com.quadcap.sql;
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
import java.io.Externalizable;
00042
import java.io.IOException;
00043
import java.io.ObjectInput;
00044
import java.io.ObjectOutput;
00045
00046
import java.util.Enumeration;
00047
import java.util.Vector;
00048
00049
import java.sql.SQLException;
00050
00051
import com.quadcap.sql.types.Op;
00052
import com.quadcap.sql.types.Type;
00053
import com.quadcap.sql.types.TypeInt;
00054
import com.quadcap.sql.types.Value;
00055
import com.quadcap.sql.types.ValueDouble;
00056
import com.quadcap.sql.types.ValueInteger;
00057
import com.quadcap.sql.types.ValueNull;
00058
00059
import com.quadcap.sql.index.Btree;
00060
00061
import com.quadcap.util.Debug;
00062
00063
00064
00065
00066
00067
00068
00069 public class AggregateExpression
00070
extends Expression implements
Externalizable
00071 {
00072 public static final int AVG = 0;
00073 public static final int SUM = 1;
00074 public static final int MIN = 2;
00075 public static final int MAX = 3;
00076 public static final int COUNT = 4;
00077
00078 int op = -1;
00079 boolean all =
false;
00080 Expression expr = null;
00081
00082 static String[]
ops = {
"AVG",
"SUM",
"MIN",
"MAX",
"COUNT"};
00083
00084 static String
toString(
int op) {
00085
try {
00086
return ops[op];
00087 }
catch (Throwable y) {
00088
return "bad op<" + op +
">";
00089 }
00090 }
00091
00092 static final byte[]
aByte = { 0 };
00093
00094
00095
00096
00097 public AggregateExpression() {}
00098
00099
00100
00101
00102 public AggregateExpression(
int op,
boolean all,
Expression expr) {
00103
this.op = op;
00104
this.all = all;
00105
this.expr = expr;
00106 }
00107
00108 Expression getInnerExpression() {
00109
return expr;
00110 }
00111
00112 boolean isMin() {
return op ==
MIN; }
00113 boolean isMax() {
return op ==
MAX; }
00114 boolean isCount() {
return op ==
COUNT; }
00115
00116
00117
00118
00119 class AggregateSessionState implements StatementContext {
00120 Session session;
00121 Btree
distinct = null;
00122
00123 Value accum = null;
00124 int count = 0;
00125
00126 public AggregateSessionState(
Session session) {
00127
this.session = session;
00128 }
00129
00130
00131 public int priority() {
return 4; }
00132
00133
00134 public void finish(
boolean abort)
throws IOException {
00135
try {
00136
if (
distinct != null) {
00137
distinct.free();
00138 }
00139 } finally {
00140
if (
distinct != null)
session.
getDatabase().releaseTempFile();
00141
distinct = null;
00142 }
00143 }
00144
00145
00146 public void reset() throws IOException {
00147
count = 0;
00148
accum = null;
00149 finish(
false);
00150 }
00151 }
00152
00153 public void reset(
Session session)
throws IOException {
00154
AggregateSessionState s =
getSessionState(session);
00155
if (s != null) s.
reset();
00156 }
00157
00158 AggregateSessionState getSessionState(
Session session) {
00159
return getSessionState(session,
true);
00160 }
00161
00162 AggregateSessionState getSessionState(
Session session,
boolean mk) {
00163
AggregateSessionState s = (
AggregateSessionState)
00164 session.
getContext(
this,
false);
00165
if (s == null && mk) {
00166 s =
new AggregateSessionState(session);
00167 session.
putContext(
this,
false, s);
00168 }
00169
return s;
00170 }
00171
00172 public int rank() {
return 0; }
00173
00174 public Type getType(
Session session,
Cursor cursor)
00175
throws SQLException
00176 {
00177
switch (
op) {
00178
case COUNT:
00179
return TypeInt.typeInt;
00180
default:
00181
return expr.
getType(session, cursor);
00182 }
00183 }
00184
00185 public Value getValue(
Session session,
Cursor cursor)
00186
throws SQLException
00187 {
00188
AggregateSessionState s = getSessionState(session);
00189
switch (
op) {
00190
case AVG:
00191
if (s.
accum == null) {
00192
return ValueNull.valueNull;
00193 }
00194
return Value.binop(
Op.DIVIDE, s.
accum,
00195
new ValueInteger(s.
count));
00196
case SUM:
00197
case MIN:
00198
case MAX:
00199
if (s.
accum == null)
return ValueNull.valueNull;
00200
return s.
accum;
00201
case COUNT:
00202
return new ValueInteger(s.
count);
00203
default:
00204
throw new SQLException(
"Bad aggregate type: " +
op,
"42000");
00205 }
00206 }
00207
00208 public void updateAggregate(
Session session,
Cursor cursor)
00209
throws SQLException
00210 {
00211
AggregateSessionState s = getSessionState(session);
00212
Value v1 = null;
00213
if (cursor != null) {
00214
if (
expr != null) {
00215 v1 =
expr.
getValue(session, cursor);
00216 }
00217
if (!
Value.isNull(v1)) {
00218
if (
all) {
00219 s.
count++;
00220 }
else {
00221
try {
00222
if (s.
distinct == null) {
00223 s.
distinct = session.makeTempTree();
00224 }
00225 byte[] key =
Value.bytes(v1);
00226
if (!s.
distinct.
set(key, key.length,
00227
aByte, 0, 1)) {
00228 s.
count++;
00229 }
else {
00230 v1 =
ValueNull.valueNull;
00231 }
00232 }
catch (IOException e) {
00233
throw DbException.wrapThrowable(e);
00234 }
00235 }
00236 }
00237 }
else {
00238 v1 =
ValueNull.valueNull;
00239 }
00240
switch (
op) {
00241
case AVG:
00242
case SUM:
00243
if (!
Value.isNull(v1)) {
00244
if (s.
accum == null) {
00245 s.
accum = v1;
00246 }
else {
00247 s.
accum =
Value.
binop(
Op.PLUS, v1, s.
accum);
00248 }
00249 }
00250
break;
00251
case MIN:
00252
if (!
Value.isNull(v1)) {
00253
if (s.
accum == null ||
Value.
boolOp(
Op.LT, v1, s.
accum)) {
00254 s.
accum = v1;
00255 }
00256 }
00257
break;
00258
case MAX:
00259
if (!
Value.isNull(v1)) {
00260
if (s.
accum == null ||
Value.
boolOp(
Op.GT, v1, s.
accum)) {
00261 s.
accum = v1;
00262 }
00263 }
00264
break;
00265
case COUNT:
00266
break;
00267
default:
00268
throw new SQLException(
"Bad aggregate type: " +
op,
"42000");
00269 }
00270 }
00271
00272 public void invert() {
00273 }
00274
00275 public String
toString() {
00276 StringBuffer sb =
new StringBuffer(
toString(
op));
00277
if (!
all) sb.append(
" DISTINCT");
00278 sb.append(
' ');
00279 sb.append(String.valueOf(
expr));
00280
return sb.toString();
00281 }
00282
00283 public void visitSubExpressions(
ExpressionVisitor ev) {
00284 ev.
visit(
expr);
00285 }
00286
00287 public void readExternal(ObjectInput in)
00288
throws IOException, ClassNotFoundException
00289 {
00290
expr = (
Expression)in.readObject();
00291
op = in.readInt();
00292
all = (in.read() == 1);
00293 }
00294
00295 public void writeExternal(ObjectOutput out)
throws IOException {
00296 out.writeObject(
expr);
00297 out.writeInt(
op);
00298 out.write(
all ? 1 : 0);
00299 }
00300 }