1 5 package org.h2.expression; 6 7 import java.sql.SQLException ; 8 import java.util.Comparator ; 9 import java.util.HashMap ; 10 11 import org.h2.command.dml.Select; 12 import org.h2.command.dml.SelectOrderBy; 13 import org.h2.engine.Constants; 14 import org.h2.engine.Database; 15 import org.h2.engine.Session; 16 import org.h2.index.Index; 17 import org.h2.message.Message; 18 import org.h2.result.SortOrder; 19 import org.h2.table.Column; 20 import org.h2.table.ColumnResolver; 21 import org.h2.table.Table; 22 import org.h2.table.TableFilter; 23 import org.h2.util.ObjectArray; 24 import org.h2.util.StringUtils; 25 import org.h2.value.DataType; 26 import org.h2.value.Value; 27 import org.h2.value.ValueArray; 28 import org.h2.value.ValueBoolean; 29 import org.h2.value.ValueDouble; 30 import org.h2.value.ValueInt; 31 import org.h2.value.ValueNull; 32 import org.h2.value.ValueString; 33 34 37 38 public class Aggregate extends Expression { 39 public static final int COUNT_ALL = 0, COUNT = 1, SUM = 2, MIN = 3, MAX = 4, AVG = 5; 43 public static final int GROUP_CONCAT = 6, STDDEV_POP = 7, STDDEV_SAMP = 8; 44 public static final int VAR_POP = 9, VAR_SAMP = 10, SOME = 11, EVERY = 12, SELECTIVITY = 13; 45 private int type; 46 private Expression on; 47 private Expression separator; 48 private ObjectArray orderList; 49 private SortOrder sort; 50 private int dataType, scale; 51 private long precision; 52 private Select select; 53 private Database database; 54 private boolean distinct; 55 56 private static HashMap aggregates = new HashMap (); 57 58 static { 59 addAggregate("COUNT", COUNT); 60 addAggregate("SUM", SUM); 61 addAggregate("MIN", MIN); 62 addAggregate("MAX", MAX); 63 addAggregate("AVG", AVG); 64 addAggregate("GROUP_CONCAT", GROUP_CONCAT); 65 addAggregate("STDDEV_SAMP", STDDEV_SAMP); 66 addAggregate("STDDEV", STDDEV_SAMP); 67 addAggregate("STDDEV_POP", STDDEV_POP); 68 addAggregate("STDDEVP", STDDEV_POP); 69 addAggregate("VAR_POP", VAR_POP); 70 addAggregate("VARP", VAR_POP); 71 addAggregate("VAR_SAMP", VAR_SAMP); 72 addAggregate("VAR", VAR_SAMP); 73 addAggregate("VARIANCE", VAR_SAMP); 74 addAggregate("SOME", SOME); 75 addAggregate("EVERY", EVERY); 76 addAggregate("SELECTIVITY", SELECTIVITY); 77 } 78 79 private static void addAggregate(String name, int type) { 80 aggregates.put(name, new Integer (type)); 81 } 82 83 public static int getAggregateType(String name) { 84 Integer type = (Integer ) aggregates.get(name); 85 return type == null ? -1 : type.intValue(); 86 } 87 88 public Aggregate(Database database, int type, Expression on, Select select, boolean distinct) { 89 this.database = database; 90 this.type = type; 91 this.on = on; 92 this.select = select; 93 this.distinct = distinct; 94 } 95 96 public void setOrder(ObjectArray orderBy) { 97 this.orderList = orderBy; 98 } 99 100 public void setSeparator(Expression separator) { 101 this.separator = separator; 102 } 103 104 private SortOrder initOrder(Session session) throws SQLException { 105 int[] index = new int[orderList.size()]; 106 int[] sortType = new int[orderList.size()]; 107 for(int i=0; i<orderList.size(); i++) { 108 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 109 index[i] = i+1; 110 int type = o.descending ? SortOrder.DESCENDING : SortOrder.ASCENDING; 111 sortType[i] = type; 112 } 113 return new SortOrder(session.getDatabase(), index, sortType); 114 } 115 116 public void updateAggregate(Session session) throws SQLException { 117 HashMap group = select.getCurrentGroup(); 122 AggregateData data = (AggregateData) group.get(this); 123 if(data == null) { 124 data = new AggregateData(type); 125 group.put(this, data); 126 } 127 Value v = on == null ? null : on.getValue(session); 128 if(type == GROUP_CONCAT) { 129 if(v != ValueNull.INSTANCE) { 130 v = v.convertTo(Value.STRING); 131 if(orderList != null) { 132 Value[] array = new Value[1 + orderList.size()]; 133 array[0] = v; 134 for(int i=0; i<orderList.size(); i++) { 135 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 136 array[i+1] = o.expression.getValue(session); 137 } 138 v = ValueArray.get(array); 139 } 140 } 141 } 142 data.add(database, distinct, v); 143 } 144 145 public Value getValue(Session session) throws SQLException { 146 if(select.isQuickQuery()) { 147 switch(type) { 148 case COUNT_ALL: 149 Table table = select.getTopTableFilter().getTable(); 150 return ValueInt.get(table.getRowCount()); 151 case MIN: 152 case MAX: 153 boolean first = type == MIN; 154 Index index = getColumnIndex(first); 155 Value v = index.findFirstOrLast(session, first); 156 return v; 157 default: 158 throw Message.getInternalError("type="+type); 159 } 160 } 161 HashMap group = select.getCurrentGroup(); 162 if(group == null) { 163 throw Message.getSQLException(Message.INVALID_USE_OF_AGGREGATE_FUNCTION_1, getSQL()); 164 } 165 AggregateData data = (AggregateData) group.get(this); 166 if(data == null) { 167 data = new AggregateData(type); 168 } 169 Value v = data.getValue(database, distinct); 170 if(type == GROUP_CONCAT) { 171 ObjectArray list = data.getList(); 172 if(list == null || list.size()==0) { 173 return ValueNull.INSTANCE; 174 } 175 if(orderList != null) { 176 try { 177 list.sort(new Comparator () { 179 public int compare(Object o1, Object o2) { 180 try { 181 Value[] a1 = ((ValueArray)o1).getList(); 182 Value[] a2 = ((ValueArray)o2).getList(); 183 return sort.compare(a1, a2); 184 } catch(SQLException e) { 185 throw Message.getInternalError("sort", e); 186 } 187 } 188 }); 189 } catch(Error e) { 190 throw Message.convert(e); 191 } 192 } 193 StringBuffer buff = new StringBuffer (); 194 String sep = separator == null ? "," : separator.getValue(session).getString(); 195 for(int i=0; i<list.size(); i++) { 196 Value val = (Value)list.get(i); 197 String s; 198 if(val.getType() == Value.ARRAY) { 199 s = ((ValueArray)val).getList()[0].getString(); 200 } else { 201 s = val.convertTo(Value.STRING).getString(); 202 } 203 if(s == null) { 204 continue; 205 } 206 if(i > 0 && sep != null) { 207 buff.append(sep); 208 } 209 buff.append(s); 210 } 211 v = ValueString.get(buff.toString()); 212 } 213 return v; 214 } 215 216 public int getType() { 217 return dataType; 218 } 219 220 public void mapColumns(ColumnResolver resolver, int level) throws SQLException { 221 if(on != null) { 222 on.mapColumns(resolver, level); 223 } 224 if(orderList != null) { 225 for(int i=0; i<orderList.size(); i++) { 226 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 227 o.expression.mapColumns(resolver, level); 228 } 229 } 230 if(separator != null) { 231 separator.mapColumns(resolver, level); 232 } 233 } 234 235 public Expression optimize(Session session) throws SQLException { 236 if(on != null) { 237 on = on.optimize(session); 238 dataType = on.getType(); 239 scale = on.getScale(); 240 precision = on.getPrecision(); 241 } 242 if(orderList != null) { 243 for(int i=0; i<orderList.size(); i++) { 244 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 245 o.expression = o.expression.optimize(session); 246 } 247 sort = initOrder(session); 248 } 249 if(separator != null) { 250 separator = separator.optimize(session); 251 } 252 switch(type) { 253 case GROUP_CONCAT: 254 dataType = Value.STRING; 255 scale = 0; 256 precision = 0; 257 break; 258 case COUNT_ALL: 259 case COUNT: 260 case SELECTIVITY: 261 dataType = Value.INT; 262 scale = 0; 263 precision = 0; 264 break; 265 case SUM: 266 case AVG: 267 if(!DataType.supportsAdd(dataType)) { 268 throw Message.getSQLException(Message.SUM_OR_AVG_ON_WRONG_DATATYPE_1, getSQL()); 269 } 270 break; 271 case MIN: 272 case MAX: 273 break; 274 case STDDEV_POP: 275 case STDDEV_SAMP: 276 case VAR_POP: 277 case VAR_SAMP: 278 dataType = Value.DOUBLE; 279 precision = ValueDouble.PRECISION; 280 scale = 0; 281 break; 282 case EVERY: 283 case SOME: 284 dataType = Value.BOOLEAN; 285 precision = ValueBoolean.PRECISION; 286 scale = 0; 287 break; 288 default: 289 throw Message.getInternalError("type="+type); 290 } 291 return this; 292 } 293 294 public void setEvaluatable(TableFilter tableFilter, boolean b) { 295 if(on != null) { 296 on.setEvaluatable(tableFilter, b); 297 } 298 if(orderList != null) { 299 for(int i=0; i<orderList.size(); i++) { 300 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 301 o.expression.setEvaluatable(tableFilter, b); 302 } 303 } 304 if(separator != null) { 305 separator.setEvaluatable(tableFilter, b); 306 } 307 } 308 309 public int getScale() { 310 return scale; 311 } 312 313 public long getPrecision() { 314 return precision; 315 } 316 317 public String getSQL() { 318 String text; 319 switch(type) { 320 case GROUP_CONCAT: { 321 StringBuffer buff = new StringBuffer (); 322 buff.append("GROUP_CONCAT("); 323 buff.append(on.getSQL()); 324 if(orderList != null) { 325 buff.append(" ORDER BY "); 326 if(orderList != null) { 327 for(int i=0; i<orderList.size(); i++) { 328 SelectOrderBy o = (SelectOrderBy) orderList.get(i); 329 if(i > 0) { 330 buff.append(", "); 331 } 332 buff.append(o.expression.getSQL()); 333 if(o.descending) { 334 buff.append(" DESC"); 335 } 336 } 337 } 338 } 339 if(separator != null) { 340 buff.append(" SEPARATOR "); 341 buff.append(separator.getSQL()); 342 } 343 buff.append(")"); 344 return buff.toString(); 345 } 346 case COUNT_ALL: 347 return "COUNT(*)"; 348 case COUNT: 349 text = "COUNT"; 350 break; 351 case SELECTIVITY: 352 text = "SELECTIVITY"; 353 break; 354 case SUM: 355 text = "SUM"; 356 break; 357 case MIN: 358 text = "MIN"; 359 break; 360 case MAX: 361 text = "MAX"; 362 break; 363 case AVG: 364 text="AVG"; 365 break; 366 case STDDEV_POP: 367 text="STDDEV_POP"; 368 break; 369 case STDDEV_SAMP: 370 text="STDDEV_SAMP"; 371 break; 372 case VAR_POP: 373 text="VAR_POP"; 374 break; 375 case VAR_SAMP: 376 text="VAR_SAMP"; 377 break; 378 case EVERY: 379 text="EVERY"; 380 break; 381 case SOME: 382 text="SOME"; 383 break; 384 default: 385 throw Message.getInternalError("type="+type); 386 } 387 if(distinct) { 388 return text + "(DISTINCT " + on.getSQL()+")"; 389 } else{ 390 return text + StringUtils.enclose(on.getSQL()); 391 } 392 } 393 394 public int getAggregateType() { 395 return type; 396 } 397 398 private Index getColumnIndex(boolean first) { 399 if(on instanceof ExpressionColumn) { 400 ExpressionColumn col = (ExpressionColumn)on; 401 Column column = col.getColumn(); 402 Table table = col.getTableFilter().getTable(); 403 Index index = table.getIndexForColumn(column, first); 404 return index; 405 } 406 return null; 407 } 408 409 public boolean isEverything(ExpressionVisitor visitor) { 410 if(visitor.type == ExpressionVisitor.OPTIMIZABLE_MIN_MAX_COUNT_ALL) { 411 switch(type) { 412 case COUNT_ALL: 413 return visitor.table.canGetRowCount(); 414 case MIN: 415 case MAX: 416 if(!Constants.OPTIMIZE_MIN_MAX) { 417 return false; 418 } 419 boolean first = type == MIN; 420 Index index = getColumnIndex(first); 421 return index != null; 422 default: 423 return false; 424 } 425 } 426 return (on == null || on.isEverything(visitor)) && (separator == null || separator.isEverything(visitor)); 427 } 428 429 public int getCost() { 430 return (on == null) ? 1 : on.getCost() + 1; 431 } 432 433 } 434 | Popular Tags |