KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > smallsql > database > GroupResult


1 /* =============================================================
2  * SmallSQL : a free Java DBMS library for the Java(tm) platform
3  * =============================================================
4  *
5  * (C) Copyright 2004-2006, by Volker Berlin.
6  *
7  * Project Info: http://www.smallsql.de/
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ---------------
28  * GroupResult.java
29  * ---------------
30  * Author: Volker Berlin
31  *
32  */

33 package smallsql.database;
34
35 import java.sql.*;
36
37 /**
38  * @author Volker Berlin
39  *
40  */

41 class GroupResult extends MemoryResult{
42
43     private Expression currentGroup; //Validate if the current row of cmd is part of the current group
44
private RowSource from;
45     private Expressions groupBy; // the list of Expressions in the GROUP BY clause
46
private Expressions expressions = new Expressions(); // List of Expression
47
private Expressions internalExpressions = new Expressions(); // a list of Aggregate Function and ColNames from SELECT, GROUP BY and HAVING
48

49     /**
50      * Constructor for Grouping a Result from a CommandSelect
51      */

52     GroupResult(CommandSelect cmd, RowSource from, Expressions groupBy, Expression having, Expressions orderBy) throws SQLException{
53         this.from = from;
54         this.groupBy = groupBy;
55         
56         if(groupBy != null){
57             for(int i=0; i<groupBy.size(); i++){
58                 Expression left = groupBy.get(i);
59                 int idx = addInternalExpressionFromGroupBy( left );
60                 ExpressionName right = new ExpressionName(null);
61                 right.setFrom(this, idx, new ColumnExpression(left));
62                 Expression expr = new ExpressionArithmetic( left, right, ExpressionArithmetic.EQUALS_NULL);
63                 currentGroup = (currentGroup == null) ?
64                                 expr :
65                                 new ExpressionArithmetic( currentGroup, expr, ExpressionArithmetic.AND );
66             }
67         }
68         expressions = internalExpressions;
69         for(int c=0; c<expressions.size(); c++){
70             addColumn(new ColumnExpression(expressions.get(c)));
71         }
72
73         patchExpressions( cmd.columnExpressions );
74         if(having != null) having = patchExpression( having );
75         patchExpressions( orderBy );
76     }
77     
78     /**
79      * Add a expression to the internal expression list if not exist in this list.
80      * It will be added named columns in the GROUP BY clause.
81      * @param expr The expression to added.
82      * @return the position in the internal list
83      */

84     final private int addInternalExpressionFromGroupBy(Expression expr) throws SQLException{
85         int type = expr.getType();
86         if(type >= Expression.GROUP_BEGIN){
87                 throw Utils.createSQLException("Aggregate function are not valid in the GROUP BY clause. ("+expr+")");
88         }else{
89             int idx = internalExpressions.indexOf(expr);
90             if(idx >= 0) return idx;
91             internalExpressions.add(expr);
92             return internalExpressions.size()-1;
93         }
94     }
95     
96     
97     /**
98      * Add a expression to the internal expression list if not exist in this list.
99      * It will be added aggregate functions from the SELECT, HAVING and ORDER BY clause.
100      * @param expr The expression to added.
101      * @return the position in the internal list
102      */

103     final private int addInternalExpressionFromSelect(Expression expr) throws SQLException{
104         int type = expr.getType();
105         if(type == Expression.NAME){
106             int idx = internalExpressions.indexOf(expr);
107             if(idx >= 0) return idx;
108             throw Utils.createSQLException("Expression '" + expr + "' is not part of a aggregate function or GROUP BY clause.");
109         }else
110         if(type >= Expression.GROUP_BEGIN){
111             int idx = internalExpressions.indexOf(expr);
112             if(idx >= 0) return idx;
113             internalExpressions.add(expr);
114             return internalExpressions.size()-1;
115         }else{
116             //if a function or aritmethik expresion is already in the group by the it is ok
117
int idx = internalExpressions.indexOf(expr);
118             if(idx >= 0) return idx;
119             Expression[] params = expr.getParams();
120             if(params != null){
121                 for(int p=0; p<params.length; p++){
122                     addInternalExpressionFromSelect( params[p]);
123                 }
124             }
125             return -1;
126         }
127     }
128     
129     
130     /**
131      * Patch all external ExpressionName in the list (SELECT clause)
132      * that it link to the the internal RowSource.
133      */

134     final private void patchExpressions(Expressions exprs) throws SQLException{
135         if(exprs == null) return;
136         for(int i=0; i<exprs.size(); i++){
137             exprs.set(i, patchExpression(exprs.get(i)));
138         }
139     }
140     
141     
142     final private void patchExpressions(Expression expression) throws SQLException{
143         Expression[] params = expression.getParams();
144         if(params == null) return;
145         for(int i=0; i<params.length; i++){
146             expression.setParamAt( patchExpression(params[i]), i);
147         }
148     }
149     
150     
151     /**
152      * Patch a single Expression. The caller need to replace the original Object
153      * if the return value return another object.
154      * @param expr the Expression to patch
155      * @return on simple columns and Aggregatfunction the original Expression is return as patch.
156      */

157     final private Expression patchExpression(Expression expr) throws SQLException{
158         //find the index in the internalExpression list
159
int idx = addInternalExpressionFromSelect( expr );
160         if(idx>=0){
161             Expression origExpression = expr;
162             ExpressionName exprName;
163             if(expr instanceof ExpressionName){
164                 exprName = (ExpressionName)expr;
165             }else{
166                 // this can only occur if in the GROUP BY clause are a function or arithmetic expression
167
// and a equals expression is used in SELECT, GROUP BY or HAVING
168
expr = exprName = new ExpressionName(expr.getAlias());
169             }
170             // patch the expression and set a new DataSource
171
Column column = exprName.getColumn();
172             if(column == null){
173                 column = new Column();
174                 exprName.setFrom(this, idx, column);
175                 switch(exprName.getType()){
176                     case Expression.MAX:
177                     case Expression.MIN:
178                     case Expression.FIRST:
179                     case Expression.LAST:
180                     case Expression.SUM:
181                         Expression baseExpression = exprName.getParams()[0];
182                         column.setPrecision(baseExpression.getPrecision());
183                         column.setScale(baseExpression.getScale());
184                         break;
185                     default:
186                         column.setPrecision(origExpression.getPrecision());
187                         column.setScale(origExpression.getScale());
188                 }
189                 column.setDataType(exprName.getDataType());
190             }else{
191                 exprName.setFrom(this, idx, column);
192             }
193         }else{
194             patchExpressions(expr);
195         }
196         return expr;
197     }
198
199     
200     
201     final void execute() throws Exception JavaDoc{
202         super.execute();
203         from.execute();
204         NextRow:
205         while(from.next()){
206             beforeFirst();
207             while(next()){
208                 if(currentGroup == null || currentGroup.getBoolean()){
209                     accumulateRow();
210                     continue NextRow;
211                 }
212             }
213             // add a new row to the GroupResult
214
addGroupRow();
215             accumulateRow();
216         }
217         
218         if(getRowCount() == 0 && groupBy == null){
219             //special handling for SELECT count(*) FROM table
220
//without GROUP BY and without any rows
221
addGroupRow();
222         }
223         // reset the row counter
224
beforeFirst();
225     }
226     
227     
228     
229     /**
230      * Add a new Row to the MemoryResult. This occur because the
231      * GROUP BY clause of the current row not relate to an exists row.
232      *
233      */

234     final private void addGroupRow(){
235         // add a new row to the GroupResult
236
ExpressionValue[] newRow = currentRow = new ExpressionValue[ expressions.size()];
237         for(int i=0; i<newRow.length; i++){
238             Expression expr = expressions.get(i);
239             int type = expr.getType();
240             if(type < Expression.GROUP_BEGIN) type = Expression.GROUP_BY;
241             newRow[i] = new ExpressionValue( type );
242         }
243         addRow(newRow);
244     }
245     
246     
247     final private void accumulateRow() throws Exception JavaDoc{
248         for(int i=0; i<currentRow.length; i++){
249             Expression src = expressions.get(i);
250             currentRow[i].accumulate(src);
251         }
252     }
253 }
254
Popular Tags