001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.GenericAggregator
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.execute;
023:
024: import org.apache.derby.iapi.types.NumberDataValue;
025: import org.apache.derby.iapi.types.UserDataValue;
026:
027: import org.apache.derby.iapi.reference.SQLState;
028:
029: import org.apache.derby.iapi.sql.execute.ExecRow;
030: import org.apache.derby.iapi.store.access.TransactionController;
031: import org.apache.derby.iapi.services.io.Storable;
032:
033: import org.apache.derby.iapi.types.DataValueDescriptor;
034: import org.apache.derby.iapi.types.DataValueDescriptor;
035: import org.apache.derby.iapi.sql.ResultDescription;
036:
037: import org.apache.derby.iapi.services.sanity.SanityManager;
038: import org.apache.derby.iapi.services.loader.ClassFactory;
039:
040: import org.apache.derby.iapi.sql.execute.ExecAggregator;
041: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
042: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
043:
044: import org.apache.derby.iapi.error.StandardException;
045: import org.apache.derby.iapi.jdbc.ConnectionContext;
046: import java.sql.Statement;
047:
048: /**
049: * Adaptor that sits between execution layer and aggregates.
050: *
051: * @author jamie
052: */
053: class GenericAggregator {
054: AggregatorInfo aggInfo;
055: int aggregatorColumnId;
056: private int inputColumnId;
057: private int resultColumnId;
058: private ResultDescription inputColumnResultDescription;
059:
060: private ExecRow[] rowArray;
061: private TemporaryRowHolderResultSet execResultSet;
062: private final ClassFactory cf;
063:
064: /*
065: ** We cache an aggregator to speed up
066: ** the instantiation of lots of aggregators.
067: */
068: private ExecAggregator cachedAggregator;
069:
070: /**
071: * Constructor:
072: *
073: * @param aggInfo information about the user aggregate
074: * @param cf the class factory.
075: */
076: GenericAggregator(AggregatorInfo aggInfo, ClassFactory cf) {
077: this .aggInfo = aggInfo;
078: aggregatorColumnId = aggInfo.getAggregatorColNum();
079: inputColumnId = aggInfo.getInputColNum();
080: resultColumnId = aggInfo.getOutputColNum();
081: int[] colArray = new int[1];
082: inputColumnResultDescription = aggInfo.getResultDescription();
083: this .cf = cf;
084: }
085:
086: /**
087: * Initialize the aggregator
088: *
089: * @param row the row with the aggregator to be initialized
090: *
091: * @exception StandardException on error
092: */
093: void initialize(ExecRow row) throws StandardException {
094: if (SanityManager.DEBUG) {
095: SanityManager.ASSERT(row != null, "row is null");
096: }
097:
098: UserDataValue aggregatorColumn = (UserDataValue) row
099: .getColumn(aggregatorColumnId + 1);
100:
101: ExecAggregator ua = (ExecAggregator) aggregatorColumn
102: .getObject();
103: if (ua == null) {
104: ua = getAggregatorInstance();
105: aggregatorColumn.setValue(ua);
106: }
107: }
108:
109: /**
110: * Accumulate the aggregate results. This is the
111: * guts of the aggregation. We will call the user aggregate
112: * on itself to do the aggregation.
113: *
114: * @param inputRow the row with the input colum
115: * @param accumulateRow the row with the aggregator
116: *
117: * @exception StandardException on error
118: */
119: void accumulate(ExecRow inputRow, ExecRow accumulateRow)
120: throws StandardException {
121: DataValueDescriptor inputColumn = null;
122:
123: if (SanityManager.DEBUG) {
124: SanityManager.ASSERT((inputRow != null)
125: && (accumulateRow != null), "bad accumulate call");
126: }
127:
128: DataValueDescriptor aggregatorColumn = accumulateRow
129: .getColumn(aggregatorColumnId + 1);
130:
131: inputColumn = inputRow.getColumn(inputColumnId + 1);
132:
133: accumulate(inputColumn, aggregatorColumn);
134: }
135:
136: /**
137: * Accumulate the aggregate results. This is the
138: * guts of the aggregation. We will call the user aggregate
139: * on itself to do the aggregation.
140: *
141: * @param inputRow the row with the input colum
142: * @param accumulateRow the row with the aggregator
143: *
144: * @exception StandardException on error
145: */
146: void accumulate(Object[] inputRow, Object[] accumulateRow)
147: throws StandardException {
148: DataValueDescriptor inputColumn = null;
149:
150: if (SanityManager.DEBUG) {
151: SanityManager.ASSERT((inputRow != null)
152: && (accumulateRow != null), "bad accumulate call");
153: }
154:
155: DataValueDescriptor aggregatorColumn = (DataValueDescriptor) accumulateRow[aggregatorColumnId];
156: inputColumn = (DataValueDescriptor) inputRow[inputColumnId];
157:
158: accumulate(inputColumn, aggregatorColumn);
159: }
160:
161: /**
162: * Accumulate the aggregate results. This is the
163: * guts of the aggregation. We will call the user aggregate
164: * on itself to do the aggregation.
165: *
166: * @param inputColumn
167: * @param aggregatorColumn
168: *
169: * @exception StandardException on error
170: */
171: void accumulate(DataValueDescriptor inputColumn,
172: DataValueDescriptor aggregatorColumn)
173: throws StandardException {
174: ExecAggregator ua;
175:
176: if (SanityManager.DEBUG) {
177: /*
178: ** Just to be on the safe side, confirm that we actually
179: ** have a Aggregator in this column.
180: */
181: if (!(aggregatorColumn instanceof UserDataValue)) {
182: SanityManager
183: .THROWASSERT("accumlator column is not a UserDataValue as "
184: + "expected, it is a "
185: + aggregatorColumn.getClass().getName());
186: }
187: }
188: ua = (ExecAggregator) aggregatorColumn.getObject();
189:
190: /*
191: ** If we don't have an aggregator, then we have to
192: ** create one now. This happens when the input result
193: ** set is null.
194: */
195: if (ua == null) {
196: ua = getAggregatorInstance();
197: }
198:
199: ua.accumulate(inputColumn, this );
200: }
201:
202: /**
203: * Merge the aggregate results. This is the
204: * guts of the aggregation. We will call the user aggregate
205: * on itself to do the aggregation.
206: *
207: * @param inputRow the row with the input colum
208: * @param mergeRow the row with the aggregator
209: *
210: * @exception StandardException on error
211: */
212: void merge(ExecRow inputRow, ExecRow mergeRow)
213: throws StandardException {
214:
215: DataValueDescriptor mergeColumn = mergeRow
216: .getColumn(aggregatorColumnId + 1);
217: DataValueDescriptor inputColumn = inputRow
218: .getColumn(aggregatorColumnId + 1);
219:
220: merge(inputColumn, mergeColumn);
221: }
222:
223: /**
224: * Merge the aggregate results. This is the
225: * guts of the aggregation. We will call the user aggregate
226: * on itself to do the aggregation.
227: *
228: * @param inputRow the row with the input colum
229: * @param mergeRow the row with the aggregator
230: *
231: * @exception StandardException on error
232: */
233: void merge(Object[] inputRow, Object[] mergeRow)
234: throws StandardException {
235: DataValueDescriptor mergeColumn = (DataValueDescriptor) mergeRow[aggregatorColumnId];
236: DataValueDescriptor inputColumn = (DataValueDescriptor) inputRow[aggregatorColumnId];
237:
238: merge(inputColumn, mergeColumn);
239: }
240:
241: /**
242: * Get the results of the aggregation and put it
243: * in the result column.
244: *
245: * @param row the row with the result and the aggregator
246: *
247: * @exception StandardException on error
248: */
249: boolean finish(ExecRow row) throws StandardException {
250: DataValueDescriptor outputColumn = row
251: .getColumn(resultColumnId + 1);
252: DataValueDescriptor aggregatorColumn = row
253: .getColumn(aggregatorColumnId + 1);
254: /*
255: ** Just to be on the safe side, confirm that we actually
256: ** have a Aggregator in aggregatorColumn.
257: */
258: if (SanityManager.DEBUG) {
259: SanityManager.ASSERT(aggregatorColumn != null,
260: "aggregatorColumn is null");
261: SanityManager.ASSERT(outputColumn != null,
262: "otuputColumn is null");
263: SanityManager
264: .ASSERT(aggregatorColumn instanceof UserDataValue,
265: "accumlator column is not a UserDataValue as expected");
266: }
267:
268: ExecAggregator ua = (ExecAggregator) aggregatorColumn
269: .getObject();
270:
271: /*
272: ** If we don't have an aggregator, then we have to
273: ** create one now. This happens when the input result
274: ** set is null.
275: */
276: if (ua == null) {
277: ua = getAggregatorInstance();
278: }
279:
280: /*
281: **
282: ** We are going to copy
283: ** then entire DataValueDescriptor into the result column.
284: ** We could call setValue(result.setObject()), but we
285: ** might loose state (e.g. SQLBit.getObject() returns a
286: ** byte[] which looses the precision of the bit.
287: **
288: */
289:
290: DataValueDescriptor result = ua.getResult();
291: if (result == null)
292: outputColumn.setToNull();
293: else
294: outputColumn.setValue(result);
295:
296: return ua.didEliminateNulls();
297: }
298:
299: /**
300: * Get a new instance of the aggregator and initialize it.
301: *
302: * @return an exec aggregator
303: *
304: * @exception StandardException on error
305: */
306: ExecAggregator getAggregatorInstance() throws StandardException {
307: ExecAggregator aggregatorInstance;
308: if (cachedAggregator == null) {
309: try {
310: Class aggregatorClass = cf.loadApplicationClass(aggInfo
311: .getAggregatorClassName());
312: Object agg = aggregatorClass.newInstance();
313: aggregatorInstance = (ExecAggregator) agg;
314: cachedAggregator = aggregatorInstance;
315: aggregatorInstance.setup(aggInfo.getAggregateName());
316:
317: } catch (Exception e) {
318: throw StandardException.unexpectedUserException(e);
319: }
320: } else {
321: aggregatorInstance = cachedAggregator.newAggregator();
322: }
323:
324: return aggregatorInstance;
325: }
326:
327: /////////////////////////////////////////////////////////////
328: //
329: /////////////////////////////////////////////////////////////
330:
331: /**
332: * Return the column id that is being aggregated
333: */
334: int getColumnId() {
335: // Every sort has to have at least one column.
336: return aggregatorColumnId;
337: }
338:
339: /**
340: * Merge two partial aggregations. This is how the
341: * sorter merges partial aggregates.
342: *
343: * @exception StandardException on error
344: */
345: void merge(Storable aggregatorColumnIn, Storable aggregatorColumnOut)
346: throws StandardException {
347: ExecAggregator uaIn;
348: ExecAggregator uaOut;
349:
350: if (SanityManager.DEBUG) {
351: /*
352: ** Just to be on the safe side, confirm that we actually
353: ** have a Aggregator in this column.
354: */
355: if (!(aggregatorColumnIn instanceof UserDataValue)) {
356: SanityManager
357: .THROWASSERT("aggregatorColumnOut column is not "
358: + "a UserAggreator as expected, "
359: + "it is a "
360: + aggregatorColumnIn.getClass()
361: .getName());
362: }
363: if (!(aggregatorColumnOut instanceof UserDataValue)) {
364: SanityManager
365: .THROWASSERT("aggregatorColumnIn column is not"
366: + " a UserAggreator as expected, "
367: + "it is a "
368: + aggregatorColumnOut.getClass()
369: .getName());
370: }
371: }
372: uaIn = (ExecAggregator) (((UserDataValue) aggregatorColumnIn)
373: .getObject());
374: uaOut = (ExecAggregator) (((UserDataValue) aggregatorColumnOut)
375: .getObject());
376:
377: uaOut.merge(uaIn);
378: }
379:
380: //////////////////////////////////////////////////////
381: //
382: // MISC
383: //
384: //////////////////////////////////////////////////////
385: AggregatorInfo getAggregatorInfo() {
386: return aggInfo;
387: }
388:
389: }
|