KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2
3    Derby - Class org.apache.derby.impl.sql.execute.ScalarAggregateResultSet
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.monitor.Monitor;
25
26 import org.apache.derby.iapi.services.sanity.SanityManager;
27
28 import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
29 import org.apache.derby.iapi.services.stream.InfoStreams;
30
31 import org.apache.derby.iapi.services.io.Formatable;
32
33 import org.apache.derby.iapi.sql.execute.CursorResultSet;
34 import org.apache.derby.iapi.sql.ResultSet;
35 import org.apache.derby.iapi.sql.execute.ExecRow;
36 import org.apache.derby.iapi.sql.execute.ExecIndexRow;
37 import org.apache.derby.iapi.sql.execute.NoPutResultSet;
38
39 import org.apache.derby.iapi.sql.Activation;
40
41 import org.apache.derby.iapi.store.access.ColumnOrdering;
42 import org.apache.derby.iapi.store.access.TransactionController;
43
44 import org.apache.derby.iapi.services.loader.GeneratedMethod;
45
46 import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
47
48 import org.apache.derby.iapi.error.StandardException;
49
50 import org.apache.derby.iapi.types.RowLocation;
51
52 import org.apache.derby.iapi.services.io.FormatableArrayHolder;
53
54 import java.util.Properties JavaDoc;
55 import java.util.Vector JavaDoc;
56 import java.util.Enumeration JavaDoc;
57
58 /**
59  * This ResultSet evaluates scalar, non distinct aggregates.
60  * It will scan the entire source result set and calculate
61  * the scalar aggregates when scanning the source during the
62  * first call to next().
63  *
64  * @author jerry (broken out from SortResultSet)
65  */

66 class ScalarAggregateResultSet extends GenericAggregateResultSet
67     implements CursorResultSet
68 {
69
70     /* Run time statistics variables */
71     public int rowsInput;
72
73     // set in constructor and not altered during
74
// life of object.
75
public boolean singleInputRow;
76     protected ExecIndexRow sortTemplateRow;
77     protected boolean isInSortedOrder; // true if source results in sorted order
78

79     // Cache ExecIndexRow for scalar aggregates
80
protected ExecIndexRow sourceExecIndexRow;
81
82     // Remember whether or not a next() has been satisfied
83
private boolean nextSatisfied;
84
85     /**
86      * Constructor
87      *
88      * @param s input result set
89      * @param isInSortedOrder true if the source results are in sorted order
90      * @param aggregateItem indicates the number of the
91      * SavedObject off of the PreparedStatement that holds the
92      * AggregatorInfoList used by this routine.
93      * @param a activation
94      * @param ra generated method to build an empty
95      * output row
96      * @param resultSetNumber The resultSetNumber for this result set
97      *
98      * @exception StandardException Thrown on error
99      */

100     ScalarAggregateResultSet(NoPutResultSet s,
101                     boolean isInSortedOrder,
102                     int aggregateItem,
103                     Activation a,
104                     GeneratedMethod ra,
105                     int resultSetNumber,
106                     boolean singleInputRow,
107                     double optimizerEstimatedRowCount,
108                     double optimizerEstimatedCost) throws StandardException
109     {
110         super(s, aggregateItem, a, ra, resultSetNumber, optimizerEstimatedRowCount, optimizerEstimatedCost);
111         this.isInSortedOrder = isInSortedOrder;
112         // source expected to be non-null, mystery stress test bug
113
// - sometimes get NullPointerException in openCore().
114
if (SanityManager.DEBUG)
115         {
116             SanityManager.ASSERT(source != null,
117                 "SARS(), source expected to be non-null");
118         }
119         sortTemplateRow = getExecutionFactory().getIndexableRow((ExecRow) rowAllocator.invoke(activation));
120         this.singleInputRow = singleInputRow;
121
122         if (SanityManager.DEBUG)
123         {
124             SanityManager.DEBUG("AggregateTrace","execution time: "+
125                     a.getPreparedStatement().getSavedObject(aggregateItem));
126         }
127         constructorTime += getElapsedMillis(beginTime);
128     }
129
130
131     ///////////////////////////////////////////////////////////////////////////////
132
//
133
// ResultSet interface (leftover from NoPutResultSet)
134
//
135
///////////////////////////////////////////////////////////////////////////////
136

137     /**
138      * Open the scan. Load the sorter and prepare to get
139      * rows from it.
140      *
141      * @exception StandardException thrown if cursor finished.
142      */

143     public void openCore() throws StandardException
144     {
145         beginTime = getCurrentTimeMillis();
146
147         // source expected to be non-null, mystery stress test bug
148
// - sometimes get NullPointerException in openCore().
149
if (SanityManager.DEBUG)
150         {
151             SanityManager.ASSERT(source != null,
152                 "SARS.openCore(), source expected to be non-null");
153             SanityManager.ASSERT( ! isOpen, "ScalarAggregateResultSet already open");
154         }
155
156         sourceExecIndexRow = getExecutionFactory().getIndexableRow(sortTemplateRow);
157
158         source.openCore();
159
160         isOpen = true;
161         numOpens++;
162
163         openTime += getElapsedMillis(beginTime);
164     }
165
166
167     protected int countOfRows;
168
169     /* RESOLVE - THIS NEXT METHOD IS OVERRIDEN IN DistinctScalarResultSet
170      * BEACAUSE OF A JIT ERROR. THERE IS NO OTHER
171      * REASON TO OVERRIDE IT IN DistinctScalarAggregateResultSet. THE BUG WAS FOUND IN
172      * 1.1.6 WITH THE JIT.
173      */

174     /**
175      * Return the next row. If it is a scalar aggregate scan
176      *
177      * @exception StandardException thrown on failure.
178      * @exception StandardException ResultSetNotOpen thrown if not yet open.
179      *
180      * @return the next row in the result
181      */

182     public ExecRow getNextRowCore() throws StandardException
183     {
184         if (nextSatisfied)
185         {
186             clearCurrentRow();
187             return null;
188         }
189
190         ExecIndexRow sortResult = null;
191         ExecRow result = null;
192         ExecIndexRow execIndexRow = null;
193         ExecIndexRow aggResult = null;
194         //only care if it is a minAgg if we have a singleInputRow, then we know
195
//we are only looking at one aggregate
196
boolean minAgg = (singleInputRow && aggregates[0].aggInfo.aggregateName.equals("MIN"));
197         beginTime = getCurrentTimeMillis();
198         if (isOpen)
199         {
200             /*
201             ** We are dealing with a scalar aggregate.
202             ** Zip through each row and accumulate.
203             ** Accumulate into the first row. Only
204             ** the first row is cloned.
205             */

206             while ((execIndexRow = getRowFromResultSet(false)) != null)
207             {
208                 /*
209                 ** Use a clone of the first row as our result.
210                 ** We need to get a clone since we will be reusing
211                 ** the original as the wrapper of the source row.
212                 ** Turn cloning off since we wont be keeping any
213                 ** other rows.
214                 */

215                 if (aggResult == null)
216                 {
217                     /* No need to clone the row when doing the min/max
218                      * optimization for MIN, since we will not do another
219                      * next on the underlying result set.
220                      */

221                     aggResult = (singleInputRow && minAgg) ?
222                                 execIndexRow :
223                                 (ExecIndexRow) execIndexRow.getClone();
224                     
225                     initializeScalarAggregation(aggResult);
226                 }
227                 else
228                 {
229                     accumulateScalarAggregation(execIndexRow, aggResult, false);
230                 }
231
232                 /* Only need to look at first single row if
233                  * min/max optimization is on and operation is MIN
234                  * or if operation is MAX first non-null row since null sorts
235                  * as highest in btree
236                  * Note only 1 aggregate is allowed in a singleInputRow
237                  * optimization so we only need to look at the first aggregate
238                  */

239                 if (singleInputRow &&
240                     (minAgg ||
241                      !aggResult.getColumn(aggregates[0].aggregatorColumnId).isNull()))
242                 {
243                     break;
244                 }
245             }
246
247             /*
248             ** If we have aggregates, we need to generate a
249             ** value for them now. Only finish the aggregation
250             ** if we haven't yet (i.e. if countOfRows == 0).
251             ** If there weren't any input rows, we'll allocate
252             ** one here.
253             */

254             if (countOfRows == 0)
255             {
256                 aggResult = finishAggregation(aggResult);
257                 currentRow = aggResult;
258                 setCurrentRow(aggResult);
259                 countOfRows++;
260             }
261         }
262
263         nextSatisfied = true;
264         nextTime += getElapsedMillis(beginTime);
265         return aggResult;
266     }
267
268     /**
269      * If the result set has been opened,
270      * close the open scan.
271      *
272      * @exception StandardException thrown on error
273      */

274     public void close() throws StandardException
275     {
276         beginTime = getCurrentTimeMillis();
277         if ( isOpen )
278         {
279             // we don't want to keep around a pointer to the
280
// row ... so it can be thrown away.
281
// REVISIT: does this need to be in a finally
282
// block, to ensure that it is executed?
283
clearCurrentRow();
284
285             countOfRows = 0;
286             sourceExecIndexRow = null;
287             source.close();
288
289             super.close();
290         }
291         else
292             if (SanityManager.DEBUG)
293                 SanityManager.DEBUG("CloseRepeatInfo","Close of SortResultSet repeated");
294
295         closeTime += getElapsedMillis(beginTime);
296
297         nextSatisfied = false;
298         isOpen = false;
299     }
300
301     /**
302      * Return the total amount of time spent in this ResultSet
303      *
304      * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
305      * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
306      *
307      * @return long The total amount of time spent (in milliseconds).
308      */

309     public long getTimeSpent(int type)
310     {
311         long totTime = constructorTime + openTime + nextTime +
312                         closeTime;
313
314         if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)
315         {
316             return totTime - originalSource.getTimeSpent(ENTIRE_RESULTSET_TREE);
317         }
318         else
319         {
320             return totTime;
321         }
322     }
323
324     ///////////////////////////////////////////////////////////////////////////////
325
//
326
// CursorResultSet interface
327
//
328
///////////////////////////////////////////////////////////////////////////////
329

330     /**
331      * This result set has its row location from
332      * the last fetch done. Always returns null.
333      *
334      * @see CursorResultSet
335      *
336      * @return the row location of the current cursor row.
337      * @exception StandardException thrown on failure to get row location
338      */

339     public RowLocation getRowLocation() throws StandardException
340     {
341         return null;
342     }
343
344     /**
345      * This result set has its row from the last fetch done.
346      * If the cursor is closed, a null is returned.
347      *
348      * @see CursorResultSet
349      *
350      * @return the last row returned;
351      * @exception StandardException thrown on failure.
352      */

353     /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
354      * once there is such a method. (currentRow is redundant)
355      */

356     public ExecRow getCurrentRow() throws StandardException
357     {
358         if (SanityManager.DEBUG)
359             SanityManager.ASSERT(isOpen, "SortResultSet expected to be open");
360
361         return currentRow;
362     }
363
364     ///////////////////////////////////////////////////////////////////////////////
365
//
366
// SCAN ABSTRACTION UTILITIES
367
//
368
///////////////////////////////////////////////////////////////////////////////
369

370     /**
371      * Get a row from the input result set.
372      *
373      * @param doClone - true of the row should be cloned
374      *
375      * @exception StandardException Thrown on error
376      */

377     public ExecIndexRow getRowFromResultSet(boolean doClone)
378         throws StandardException
379     {
380         ExecRow sourceRow;
381         ExecIndexRow inputRow = null;
382
383         if ((sourceRow = source.getNextRowCore()) != null)
384         {
385             rowsInput++;
386             sourceExecIndexRow.execRowToExecIndexRow(
387                     doClone ? sourceRow.getClone() : sourceRow);
388             inputRow = sourceExecIndexRow;
389         }
390
391         return inputRow;
392     }
393
394     /**
395      * reopen a scan on the table. scan parameters are evaluated
396      * at each open, so there is probably some way of altering
397      * their values...
398      *
399      * @exception StandardException thrown if cursor finished.
400      */

401     public void reopenCore() throws StandardException
402     {
403         beginTime = getCurrentTimeMillis();
404         if (SanityManager.DEBUG)
405             SanityManager.ASSERT(isOpen, "NormalizeResultSet already open");
406
407         source.reopenCore();
408         numOpens++;
409         countOfRows = 0;
410         nextSatisfied = false;
411
412         openTime += getElapsedMillis(beginTime);
413     }
414
415     ///////////////////////////////////////////////////////////////////////////////
416
//
417
// AGGREGATION UTILITIES
418
//
419
///////////////////////////////////////////////////////////////////////////////
420

421     /**
422      * Run accumulation on every aggregate in this
423      * row. This method is useful when draining the source
424      * or sorter, depending on whether or not there were any
425      * distinct aggregates. Remember, if there are distinct
426      * aggregates, then the non-distinct aggregates were
427      * calculated on the way into the sorter and only the
428      * distinct aggregates will be accumulated here.
429      * Otherwise, all aggregates will be accumulated here.
430      *
431      * @param inputRow the input row
432      * @param accumulateRow the row with the accumulator (may be the same as the input row.
433      * @param hasDistinctAggregates does this scan have distinct
434      * aggregates. Used to figure out whether to merge
435      * or accumulate nondistinct aggregates.
436      *
437      * @exception StandardException Thrown on error
438      */

439     protected void accumulateScalarAggregation
440     (
441         ExecRow inputRow,
442         ExecRow accumulateRow,
443         boolean hasDistinctAggregates
444     )
445         throws StandardException
446     {
447         int size = aggregates.length;
448
449         if (SanityManager.DEBUG)
450         {
451             SanityManager.ASSERT((inputRow != null) && (accumulateRow != null),
452                     "Null row passed to accumulateScalarAggregation");
453         }
454         for (int i = 0; i < size; i++)
455         {
456             GenericAggregator currAggregate = aggregates[i];
457             if (hasDistinctAggregates &&
458                  !currAggregate.getAggregatorInfo().isDistinct())
459             {
460                 currAggregate.merge(inputRow, accumulateRow);
461             }
462             else
463             {
464                 currAggregate.accumulate(inputRow, accumulateRow);
465             }
466         }
467     }
468
469     ///////////////////////////////////////////////////////////////////////////////
470
//
471
// CLASS SPECIFIC
472
//
473
///////////////////////////////////////////////////////////////////////////////
474

475     /*
476     ** Run the aggregator initialization method for
477     ** each aggregator in the row.
478     **
479     ** @param row the row to initialize
480     **
481     ** @return Nothing.
482     **
483     ** @exception standard cloudscape exception
484     */

485     private void initializeScalarAggregation(ExecRow row)
486         throws StandardException
487     {
488         int size = aggregates.length;
489
490         if (SanityManager.DEBUG)
491         {
492             SanityManager.ASSERT(row != null,
493                     "Null row passed to initializeScalarAggregation");
494         }
495
496         for (int i = 0; i < size; i++)
497         {
498             GenericAggregator currAggregate = aggregates[i];
499             currAggregate.initialize(row);
500             currAggregate.accumulate(row, row);
501         }
502     }
503 }
504
Popular Tags