001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.MaterializedResultSet
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.services.monitor.Monitor;
025:
026: import org.apache.derby.iapi.services.sanity.SanityManager;
027:
028: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
029: import org.apache.derby.iapi.services.stream.InfoStreams;
030:
031: import org.apache.derby.iapi.sql.ResultSet;
032:
033: import org.apache.derby.iapi.types.RowLocation;
034:
035: import org.apache.derby.iapi.sql.execute.CursorResultSet;
036: import org.apache.derby.iapi.sql.execute.ExecRow;
037: import org.apache.derby.iapi.sql.execute.ExecutionContext;
038: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
039: import org.apache.derby.iapi.sql.execute.ExecutionFactory;
040:
041: import org.apache.derby.iapi.sql.Activation;
042:
043: import org.apache.derby.iapi.services.loader.GeneratedMethod;
044:
045: import org.apache.derby.iapi.store.access.ConglomerateController;
046: import org.apache.derby.iapi.store.access.ScanController;
047: import org.apache.derby.iapi.store.access.TransactionController;
048:
049: import org.apache.derby.iapi.error.StandardException;
050: import org.apache.derby.iapi.reference.SQLState;
051:
052: import org.apache.derby.iapi.services.io.FormatableBitSet;
053:
054: /**
055: * Materialize the underlying ResultSet tree into a temp table on the 1st open.
056: * Return rows from temp table on subsequent scans.
057: */
058:
059: class MaterializedResultSet extends NoPutResultSetImpl implements
060: CursorResultSet {
061: /*
062: ** Set in constructor and not altered during life of object.
063: */
064:
065: public NoPutResultSet source;
066:
067: private ExecRow materializedRowBuffer;
068: protected long materializedCID;
069: public boolean materializedCreated;
070: private boolean fromSource = true;
071: protected ConglomerateController materializedCC;
072: protected ScanController materializedScan;
073: private TransactionController tc;
074: private boolean sourceDrained;
075:
076: public long createTCTime;
077: public long fetchTCTime;
078:
079: /**
080: * Constructor for a MaterializedResultSet
081: *
082: * @param source The NoPutResultSet from which to get rows
083: * to be materialized
084: * @param activation The activation for this execution
085: * @param resultSetNumber The resultSetNumber
086: *
087: * @exception StandardException on error
088: */
089:
090: public MaterializedResultSet(NoPutResultSet source,
091: Activation activation, int resultSetNumber,
092: double optimizerEstimatedRowCount,
093: double optimizerEstimatedCost) throws StandardException {
094: super (activation, resultSetNumber, optimizerEstimatedRowCount,
095: optimizerEstimatedCost);
096: this .source = source;
097:
098: // Get the current transaction controller
099: tc = activation.getTransactionController();
100:
101: constructorTime += getElapsedMillis(beginTime);
102: }
103:
104: //
105: // ResultSet interface (leftover from NoPutResultSet)
106: //
107:
108: /**
109: * open a scan on the source. scan parameters are evaluated
110: * at each open, so there is probably some way of altering
111: * their values...
112: *
113: * @exception StandardException thrown on failure
114: */
115: public void openCore() throws StandardException {
116: beginTime = getCurrentTimeMillis();
117: if (SanityManager.DEBUG)
118: SanityManager.ASSERT(!isOpen,
119: "MaterializedResultSet already open");
120:
121: source.openCore();
122: isOpen = true;
123: numOpens++;
124:
125: openTime += getElapsedMillis(beginTime);
126: }
127:
128: /**
129: * reopen a scan on the table. scan parameters are evaluated
130: * at each open, so there is probably some way of altering
131: * their values...
132: *
133: * @exception StandardException thrown if cursor finished.
134: */
135: public void reopenCore() throws StandardException {
136: boolean constantEval = true;
137:
138: beginTime = getCurrentTimeMillis();
139:
140: if (SanityManager.DEBUG)
141: SanityManager.ASSERT(isOpen,
142: "MaterializedResultSet already open");
143:
144: // Finish draining the source into the temp table
145: while (!sourceDrained) {
146: getNextRowFromSource();
147: }
148:
149: // Results will now come from the temp table
150: fromSource = false;
151:
152: // Close the temp table if open
153: if (materializedScan != null) {
154: materializedScan.close();
155: }
156:
157: /* Open a scan on the temp conglomerate,
158: * if one exists.
159: */
160: if (materializedCID != 0) {
161: materializedScan = tc.openScan(
162: materializedCID,
163: false, // hold
164: 0, // for update
165: TransactionController.MODE_TABLE,
166: TransactionController.ISOLATION_SERIALIZABLE,
167: (FormatableBitSet) null, // all fields as objects
168: null, // start key value
169: 0, // start operator
170: null, // qualifier
171: null, // stop key value
172: 0); // stop operator
173:
174: isOpen = true;
175: }
176:
177: numOpens++;
178:
179: openTime += getElapsedMillis(beginTime);
180: }
181:
182: /**
183: *
184: * @exception StandardException thrown on failure
185: */
186: public ExecRow getNextRowCore() throws StandardException {
187: ExecRow result = null;
188:
189: beginTime = getCurrentTimeMillis();
190: if (!isOpen)
191: throw StandardException.newException(
192: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
193:
194: /* Should we get the next row from the source or the materialized result set? */
195: if (fromSource) {
196: result = getNextRowFromSource();
197: } else {
198: result = getNextRowFromTempTable();
199: }
200:
201: if (result != null) {
202: rowsSeen++;
203: }
204:
205: currentRow = result;
206: setCurrentRow(currentRow);
207:
208: nextTime += getElapsedMillis(beginTime);
209:
210: return result;
211: }
212:
213: /* Get the next row from the source ResultSet tree and insert into the temp table */
214: private ExecRow getNextRowFromSource() throws StandardException {
215: // Nothing to do if source is already drained
216: if (sourceDrained) {
217: return null;
218: }
219:
220: ExecRow sourceRow = null;
221: ExecRow result = null;
222:
223: sourceRow = source.getNextRowCore();
224:
225: if (sourceRow != null) {
226: long beginTCTime = getCurrentTimeMillis();
227: /* If this is the first row from the source then clone it as our own
228: * for use when fetching from temp table.
229: * This is also the place where we create the temp table.
230: */
231: if (materializedRowBuffer == null) {
232: materializedRowBuffer = sourceRow.getClone();
233:
234: tc = activation.getTransactionController();
235:
236: materializedCID = tc.createConglomerate("heap",
237: materializedRowBuffer.getRowArray(), null,
238: null, TransactionController.IS_TEMPORARY
239: | TransactionController.IS_KEPT);
240: materializedCreated = true;
241: materializedCC = tc.openConglomerate(materializedCID,
242: false,
243: TransactionController.OPENMODE_FORUPDATE,
244: TransactionController.MODE_TABLE,
245: TransactionController.ISOLATION_SERIALIZABLE);
246: }
247: materializedCC.insert(sourceRow.getRowArray());
248:
249: createTCTime += getElapsedMillis(beginTCTime);
250: }
251: // Remember whether or not we've drained the source
252: else {
253: sourceDrained = true;
254: }
255:
256: return sourceRow;
257: }
258:
259: /* Get the next Row from the temp table */
260: private ExecRow getNextRowFromTempTable() throws StandardException {
261: long beginTCTime = getCurrentTimeMillis();
262: /* Get and return the next row from the temp conglomerate,
263: * if one exists.
264: */
265: if (materializedScan != null
266: && materializedScan.fetchNext(materializedRowBuffer
267: .getRowArray())) {
268: fetchTCTime += getElapsedMillis(beginTCTime);
269: return materializedRowBuffer;
270: } else {
271: return null;
272: }
273: }
274:
275: /**
276: * If the result set has been opened,
277: * close the open scan.
278: *
279: * @exception StandardException thrown on error
280: */
281: public void close() throws StandardException {
282: beginTime = getCurrentTimeMillis();
283: if (isOpen) {
284: currentRow = null;
285: source.close();
286:
287: if (materializedScan != null)
288: materializedScan.close();
289: materializedScan = null;
290:
291: if (materializedCC != null)
292: materializedCC.close();
293: materializedCC = null;
294:
295: if (materializedCreated)
296: tc.dropConglomerate(materializedCID);
297:
298: materializedCreated = false;
299:
300: super .close();
301: } else if (SanityManager.DEBUG)
302: SanityManager.DEBUG("CloseRepeatInfo",
303: "Close of MaterializedResultSet repeated");
304:
305: closeTime += getElapsedMillis(beginTime);
306: }
307:
308: /**
309: * Return the total amount of time spent in this ResultSet
310: *
311: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
312: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
313: *
314: * @return long The total amount of time spent (in milliseconds).
315: */
316: public long getTimeSpent(int type) {
317: long totTime = constructorTime + openTime + nextTime
318: + closeTime;
319:
320: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
321: return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);
322: } else {
323: return totTime;
324: }
325: }
326:
327: //
328: // CursorResultSet interface
329: //
330:
331: /**
332: * Gets information from its source. We might want
333: * to have this take a CursorResultSet in its constructor some day,
334: * instead of doing a cast here?
335: *
336: * @see CursorResultSet
337: *
338: * @return the row location of the current cursor row.
339: *
340: * @exception StandardException thrown on failure
341: */
342: public RowLocation getRowLocation() throws StandardException {
343: if (SanityManager.DEBUG)
344: SanityManager.ASSERT(source instanceof CursorResultSet,
345: "source not CursorResultSet");
346: return ((CursorResultSet) source).getRowLocation();
347: }
348:
349: /**
350: * Gets information from last getNextRow call.
351: *
352: * @see CursorResultSet
353: *
354: * @return the last row returned.
355: */
356: /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
357: * once there is such a method. (currentRow is redundant)
358: */
359: public ExecRow getCurrentRow() {
360: return currentRow;
361: }
362:
363: //
364: // class implementation
365: //
366: }
|