KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > h2 > expression > Aggregate


1 /*
2  * Copyright 2004-2006 H2 Group. Licensed under the H2 License, Version 1.0 (http://h2database.com/html/license.html).
3  * Initial Developer: H2 Group
4  */

5 package org.h2.expression;
6
7 import java.sql.SQLException JavaDoc;
8 import java.util.Comparator JavaDoc;
9 import java.util.HashMap JavaDoc;
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 /**
35  * @author Thomas
36  */

37
38 public class Aggregate extends Expression {
39     // TODO aggregates: make them 'pluggable'
40
// TODO incomatibility to hsqldb: aggregates: hsqldb uses automatic data type for sum if value is too big,
41
// h2 uses the same type as the data
42
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 JavaDoc aggregates = new HashMap JavaDoc();
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 JavaDoc name, int type) {
80         aggregates.put(name, new Integer JavaDoc(type));
81     }
82     
83     public static int getAggregateType(String JavaDoc name) {
84         Integer JavaDoc type = (Integer JavaDoc) 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 JavaDoc {
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 JavaDoc {
117         // TODO aggregates: check nested MIN(MAX(ID)) and so on
118
// if(on != null) {
119
// on.updateAggregate();
120
// }
121
HashMap JavaDoc 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 JavaDoc {
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 JavaDoc 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                     // TODO refactor: don't use built in comparator
178
list.sort(new Comparator JavaDoc() {
179                         public int compare(Object JavaDoc o1, Object JavaDoc o2) {
180                             try {
181                                 Value[] a1 = ((ValueArray)o1).getList();
182                                 Value[] a2 = ((ValueArray)o2).getList();
183                                 return sort.compare(a1, a2);
184                             } catch(SQLException JavaDoc e) {
185                                 throw Message.getInternalError("sort", e);
186                             }
187                         }
188                     });
189                 } catch(Error JavaDoc e) {
190                     throw Message.convert(e);
191                 }
192             }
193             StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
194             String JavaDoc 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 JavaDoc 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 JavaDoc {
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 JavaDoc {
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 JavaDoc getSQL() {
318         String JavaDoc text;
319         switch(type) {
320         case GROUP_CONCAT: {
321             StringBuffer JavaDoc buff = new StringBuffer JavaDoc();
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