KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > sql > execute > AvgAggregator


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.AvgAggregator
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.sql.execute;
23
24 import org.apache.derby.iapi.services.sanity.SanityManager;
25 import org.apache.derby.iapi.types.NumberDataValue;
26 import org.apache.derby.iapi.error.StandardException;
27 import org.apache.derby.iapi.sql.execute.ExecAggregator;
28 import org.apache.derby.iapi.types.DataValueDescriptor;
29 import org.apache.derby.iapi.types.TypeId;
30
31 import org.apache.derby.iapi.services.io.StoredFormatIds;
32 import org.apache.derby.iapi.reference.SQLState;
33
34 import java.io.ObjectOutput JavaDoc;
35 import java.io.ObjectInput JavaDoc;
36 import java.io.IOException JavaDoc;
37
38 /**
39     Aggregator for AVG(). Extends the SumAggregator and
40     implements a count. Result is then sum()/count().
41     To handle overflow we catch the exception for
42     value out of range, then we swap the holder for
43     the current sum to one that can handle a larger
44     range. Eventually a sum may end up in a SQLDecimal
45     which can handle an infinite range. Once this
46     type promotion has happened, it will not revert back
47     to the original type, even if the sum would fit in
48     a lesser type.
49
50  */

51 public final class AvgAggregator extends SumAggregator
52 {
53     private long count;
54     private int scale;
55
56     protected void accumulate(DataValueDescriptor addend)
57         throws StandardException
58     {
59
60         if (count == 0) {
61
62             String JavaDoc typeName = addend.getTypeName();
63             if ( typeName.equals(TypeId.TINYINT_NAME)
64                 || typeName.equals(TypeId.SMALLINT_NAME)
65                 || typeName.equals(TypeId.INTEGER_NAME)
66                 || typeName.equals(TypeId.LONGINT_NAME)) {
67                 scale = 0;
68             } else if ( typeName.equals(TypeId.REAL_NAME)
69                 || typeName.equals(TypeId.DOUBLE_NAME)) {
70                 scale = TypeId.DECIMAL_SCALE;
71             } else {
72                 // DECIMAL
73
scale = ((NumberDataValue) addend).getDecimalValueScale();
74                 if (scale < NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE)
75                     scale = NumberDataValue.MIN_DECIMAL_DIVIDE_SCALE;
76             }
77         }
78
79         try {
80
81             super.accumulate(addend);
82             count++;
83             return;
84
85         } catch (StandardException se) {
86
87             if (!se.getMessageId().equals(SQLState.LANG_OUTSIDE_RANGE_FOR_DATATYPE))
88                 throw se;
89         }
90
91
92         /*
93             Sum is out of range so promote
94
95             TINYINT,SMALLINT -->> INTEGER
96
97             INTEGER -->> BIGINT
98
99             REAL -->> DOUBLE PRECISION
100
101             others -->> DECIMAL
102         */

103
104         // this code creates data type objects directly, it is anticipating
105
// the time they move into the defined api of the type system. (djd).
106
String JavaDoc typeName = value.getTypeName();
107         
108         DataValueDescriptor newValue;
109
110         if (typeName.equals(TypeId.INTEGER_NAME)) {
111             newValue = new org.apache.derby.iapi.types.SQLLongint();
112         } else if (typeName.equals(TypeId.TINYINT_NAME) ||
113                    typeName.equals(TypeId.SMALLINT_NAME)) {
114             newValue = new org.apache.derby.iapi.types.SQLInteger();
115         } else if (typeName.equals(TypeId.REAL_NAME)) {
116             newValue = new org.apache.derby.iapi.types.SQLDouble();
117         } else {
118             TypeId decimalTypeId = TypeId.getBuiltInTypeId(java.sql.Types.DECIMAL);
119             newValue = decimalTypeId.getNull();
120         }
121         
122         newValue.setValue(value);
123         value = newValue;
124         
125         accumulate(addend);
126     }
127
128     public void merge(ExecAggregator addend)
129         throws StandardException
130     {
131         AvgAggregator otherAvg = (AvgAggregator) addend;
132
133         // if I haven't been used take the other.
134
if (count == 0) {
135             count = otherAvg.count;
136             value = otherAvg.value;
137             scale = otherAvg.scale;
138             return;
139         }
140
141         // Don't bother merging if the other is a NULL value aggregate.
142
/* Note:Beetle:5346 fix change the sort to be High, that makes
143          * the neccessary for the NULL check because after the change
144          * addend could have a NULL value even on distincts unlike when
145          * NULLs were sort order Low, because by sorting NULLs Low
146          * they happens to be always first row which makes it as
147          * aggreagte result object instead of addends.
148          * Query that will fail without the following check:
149          * select avg(a) , count(distinct a) from t1;
150         */

151         if(otherAvg.value != null)
152         {
153             // subtract one here as the accumulate will add one back in
154
count += (otherAvg.count - 1);
155             accumulate(otherAvg.value);
156         }
157     }
158
159     /**
160      * Return the result of the aggregation. If the count
161      * is zero, then we haven't averaged anything yet, so
162      * we return null. Otherwise, return the running
163      * average as a double.
164      *
165      * @return null or the average as Double
166      */

167     public DataValueDescriptor getResult() throws StandardException
168     {
169         if (count == 0)
170         {
171             return null;
172         }
173
174         NumberDataValue sum = (NumberDataValue) value;
175         NumberDataValue avg = (NumberDataValue) value.getNewNull();
176
177         
178         if (count > (long) Integer.MAX_VALUE)
179         {
180             // TINYINT, SMALLINT, INTEGER implement arithmetic using integers
181
// If the sum is still represented as a TINYINT, SMALLINT or INTEGER
182
// we cannot let their int based arithmetic handle it, since they
183
// will perform a getInt() on the long value which will truncate the long value.
184
// One solution would be to promote the sum to a SQLLongint, but its value
185
// will be less than or equal to Integer.MAX_VALUE, so the average will be 0.
186
String JavaDoc typeName = sum.getTypeName();
187
188             if (typeName.equals(TypeId.INTEGER_NAME) ||
189                     typeName.equals(TypeId.TINYINT_NAME) ||
190                        typeName.equals(TypeId.SMALLINT_NAME))
191             {
192                 avg.setValue(0);
193                 return avg;
194             }
195         }
196
197         NumberDataValue countv = new org.apache.derby.iapi.types.SQLLongint(count);
198         sum.divide(sum, countv, avg, scale);
199                 
200         return avg;
201     }
202
203     /**
204      */

205     public ExecAggregator newAggregator()
206     {
207         return new AvgAggregator();
208     }
209
210     /////////////////////////////////////////////////////////////
211
//
212
// EXTERNALIZABLE INTERFACE
213
//
214
/////////////////////////////////////////////////////////////
215
/**
216      *
217      * @exception IOException on error
218      */

219     public void writeExternal(ObjectOutput JavaDoc out) throws IOException JavaDoc
220     {
221         super.writeExternal(out);
222         out.writeLong(count);
223         out.writeInt(scale);
224     }
225
226     /**
227      * @see java.io.Externalizable#readExternal
228      *
229      * @exception IOException on error
230      */

231     public void readExternal(ObjectInput JavaDoc in)
232         throws IOException JavaDoc, ClassNotFoundException JavaDoc
233     {
234         super.readExternal(in);
235         count = in.readLong();
236         scale = in.readInt();
237     }
238
239     /////////////////////////////////////////////////////////////
240
//
241
// FORMATABLE INTERFACE
242
//
243
/////////////////////////////////////////////////////////////
244
/**
245      * Get the formatID which corresponds to this class.
246      *
247      * @return the formatID of this class
248      */

249     public int getTypeFormatId() { return StoredFormatIds.AGG_AVG_V01_ID; }
250 }
251
Popular Tags