1 package com.quadcap.sql; 2 3 40 41 import java.io.Externalizable ; 42 import java.io.IOException ; 43 import java.io.ObjectInput ; 44 import java.io.ObjectOutput ; 45 46 import java.util.Enumeration ; 47 import java.util.Vector ; 48 49 import java.sql.SQLException ; 50 51 import com.quadcap.sql.types.Op; 52 import com.quadcap.sql.types.Type; 53 import com.quadcap.sql.types.TypeInt; 54 import com.quadcap.sql.types.Value; 55 import com.quadcap.sql.types.ValueDouble; 56 import com.quadcap.sql.types.ValueInteger; 57 import com.quadcap.sql.types.ValueNull; 58 59 import com.quadcap.sql.index.Btree; 60 61 import com.quadcap.util.Debug; 62 63 69 public class AggregateExpression 70 extends Expression implements Externalizable 71 { 72 public static final int AVG = 0; 73 public static final int SUM = 1; 74 public static final int MIN = 2; 75 public static final int MAX = 3; 76 public static final int COUNT = 4; 77 78 int op = -1; 79 boolean all = false; 80 Expression expr = null; 81 82 static String [] ops = {"AVG","SUM","MIN","MAX","COUNT"}; 83 84 static String toString(int op) { 85 try { 86 return ops[op]; 87 } catch (Throwable y) { 88 return "bad op<" + op + ">"; 89 } 90 } 91 92 static final byte[] aByte = { 0 }; 93 94 97 public AggregateExpression() {} 98 99 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() { return op == MIN; } 113 boolean isMax() { return op == MAX; } 114 boolean isCount() { return op == COUNT; } 115 116 119 class AggregateSessionState implements StatementContext { 120 Session session; 121 Btree distinct = null; 122 123 Value accum = null; 124 int count = 0; 125 126 public AggregateSessionState(Session session) { 127 this.session = session; 128 } 129 130 131 public int priority() { return 4; } 132 133 134 public void finish(boolean abort) throws IOException { 135 try { 136 if (distinct != null) { 137 distinct.free(); 138 } 139 } finally { 140 if (distinct != null) session.getDatabase().releaseTempFile(); 141 distinct = null; 142 } 143 } 144 145 146 public void reset() throws IOException { 147 count = 0; 148 accum = null; 149 finish(false); 150 } 151 } 152 153 public void reset(Session session) throws IOException { 154 AggregateSessionState s = getSessionState(session); 155 if (s != null) s.reset(); 156 } 157 158 AggregateSessionState getSessionState(Session session) { 159 return getSessionState(session, true); 160 } 161 162 AggregateSessionState getSessionState(Session session, boolean mk) { 163 AggregateSessionState s = (AggregateSessionState) 164 session.getContext(this, false); 165 if (s == null && mk) { 166 s = new AggregateSessionState(session); 167 session.putContext(this, false, s); 168 } 169 return s; 170 } 171 172 public int rank() { return 0; } 173 174 public Type getType(Session session, Cursor cursor) 175 throws SQLException 176 { 177 switch (op) { 178 case COUNT: 179 return TypeInt.typeInt; 180 default: 181 return expr.getType(session, cursor); 182 } 183 } 184 185 public Value getValue(Session session, Cursor cursor) 186 throws SQLException 187 { 188 AggregateSessionState s = getSessionState(session); 189 switch (op) { 190 case AVG: 191 if (s.accum == null) { 192 return ValueNull.valueNull; 193 } 194 return Value.binop(Op.DIVIDE, s.accum, 195 new ValueInteger(s.count)); 196 case SUM: 197 case MIN: 198 case MAX: 199 if (s.accum == null) return ValueNull.valueNull; 200 return s.accum; 201 case COUNT: 202 return new ValueInteger(s.count); 203 default: 204 throw new SQLException ("Bad aggregate type: " + op, "42000"); 205 } 206 } 207 208 public void updateAggregate(Session session, Cursor cursor) 209 throws SQLException 210 { 211 AggregateSessionState s = getSessionState(session); 212 Value v1 = null; 213 if (cursor != null) { 214 if (expr != null) { 215 v1 = expr.getValue(session, cursor); 216 } 217 if (!Value.isNull(v1)) { 218 if (all) { 219 s.count++; 220 } else { 221 try { 222 if (s.distinct == null) { 223 s.distinct = session.makeTempTree(); 224 } 225 byte[] key = Value.bytes(v1); 226 if (!s.distinct.set(key, key.length, 227 aByte, 0, 1)) { 228 s.count++; 229 } else { 230 v1 = ValueNull.valueNull; 231 } 232 } catch (IOException e) { 233 throw DbException.wrapThrowable(e); 234 } 235 } 236 } 237 } else { 238 v1 = ValueNull.valueNull; 239 } 240 switch (op) { 241 case AVG: 242 case SUM: 243 if (!Value.isNull(v1)) { 244 if (s.accum == null) { 245 s.accum = v1; 246 } else { 247 s.accum = Value.binop(Op.PLUS, v1, s.accum); 248 } 249 } 250 break; 251 case MIN: 252 if (!Value.isNull(v1)) { 253 if (s.accum == null || Value.boolOp(Op.LT, v1, s.accum)) { 254 s.accum = v1; 255 } 256 } 257 break; 258 case MAX: 259 if (!Value.isNull(v1)) { 260 if (s.accum == null || Value.boolOp(Op.GT, v1, s.accum)) { 261 s.accum = v1; 262 } 263 } 264 break; 265 case COUNT: 266 break; 267 default: 268 throw new SQLException ("Bad aggregate type: " + op, "42000"); 269 } 270 } 271 272 public void invert() { 273 } 274 275 public String toString() { 276 StringBuffer sb = new StringBuffer (toString(op)); 277 if (!all) sb.append(" DISTINCT"); 278 sb.append(' '); 279 sb.append(String.valueOf(expr)); 280 return sb.toString(); 281 } 282 283 public void visitSubExpressions(ExpressionVisitor ev) { 284 ev.visit(expr); 285 } 286 287 public void readExternal(ObjectInput in) 288 throws IOException , ClassNotFoundException 289 { 290 expr = (Expression)in.readObject(); 291 op = in.readInt(); 292 all = (in.read() == 1); 293 } 294 295 public void writeExternal(ObjectOutput out) throws IOException { 296 out.writeObject(expr); 297 out.writeInt(op); 298 out.write(all ? 1 : 0); 299 } 300 } 301 | Popular Tags |