001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.IndexRowToBaseRowResultSet
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.execute.CursorResultSet;
032: import org.apache.derby.iapi.sql.execute.ExecRow;
033: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
034:
035: import org.apache.derby.iapi.types.DataValueDescriptor;
036:
037: import org.apache.derby.iapi.sql.Activation;
038: import org.apache.derby.iapi.sql.ResultSet;
039:
040: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
041: import org.apache.derby.iapi.sql.conn.StatementContext;
042:
043: import org.apache.derby.iapi.store.access.ConglomerateController;
044: import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;
045: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
046: import org.apache.derby.iapi.store.access.TransactionController;
047:
048: import org.apache.derby.iapi.services.loader.GeneratedMethod;
049:
050: import org.apache.derby.iapi.reference.SQLState;
051: import org.apache.derby.iapi.error.StandardException;
052:
053: import org.apache.derby.iapi.types.RowLocation;
054:
055: import org.apache.derby.iapi.services.io.FormatableBitSet;
056:
057: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
058:
059: /**
060: * Takes a result set with a RowLocation as the last column, and uses the
061: * RowLocation to get and return a row from the given base conglomerate.
062: * Normally, the input result set will be a TableScanResultSet scanning an
063: * index conglomerate.
064: *
065: * @author jeff
066: */
067: class IndexRowToBaseRowResultSet extends NoPutResultSetImpl implements
068: CursorResultSet {
069:
070: // set in constructor and not altered during
071: // life of object.
072: private long conglomId;
073: public NoPutResultSet source;
074: private GeneratedMethod resultRowAllocator;
075: private GeneratedMethod restriction;
076: private long baseConglomId;
077: public FormatableBitSet accessedHeapCols;
078: private FormatableBitSet accessedIndexCols;
079: //caching accessed columns (heap+index) beetle 3865
080: private FormatableBitSet accessedAllCols;
081: public String indexName;
082: private int[] indexCols;
083: private DynamicCompiledOpenConglomInfo dcoci;
084: private StaticCompiledOpenConglomInfo scoci;
085:
086: // set in open() and not changed after that
087: private ConglomerateController baseCC;
088: private boolean closeBaseCCHere;
089: private ExecRow resultRow;
090: private ExecRow compactRow;
091: private boolean forUpdate;
092: private DataValueDescriptor[] rowArray;
093:
094: // changed a whole bunch
095: RowLocation baseRowLocation;
096:
097: /* Remember whether or not we have copied any
098: * columns from the source row to our row yet.
099: */
100: boolean copiedFromSource;
101:
102: /* Run time statistics variables */
103: public long restrictionTime;
104:
105: protected boolean currentRowPrescanned;
106: private boolean sourceIsForUpdateIndexScan;
107:
108: //
109: // class interface
110: //
111: IndexRowToBaseRowResultSet(long conglomId, int scociItem,
112: Activation a, NoPutResultSet source,
113: GeneratedMethod resultRowAllocator, int resultSetNumber,
114: String indexName, int heapColRefItem, int indexColRefItem,
115: int indexColMapItem, GeneratedMethod restriction,
116: boolean forUpdate, double optimizerEstimatedRowCount,
117: double optimizerEstimatedCost) throws StandardException {
118: super (a, resultSetNumber, optimizerEstimatedRowCount,
119: optimizerEstimatedCost);
120: scoci = (StaticCompiledOpenConglomInfo) (activation
121: .getPreparedStatement().getSavedObject(scociItem));
122: TransactionController tc = activation
123: .getTransactionController();
124: dcoci = tc.getDynamicCompiledConglomInfo(conglomId);
125: this .source = source;
126: this .resultRowAllocator = resultRowAllocator;
127: this .indexName = indexName;
128: this .forUpdate = forUpdate;
129: this .restriction = restriction;
130:
131: /* RESOLVE - once we push Qualifiers into the store we
132: * need to clear their Orderable cache on each open/reopen.
133: */
134:
135: // retrieve the valid column list from
136: // the saved objects, if it exists
137: this .accessedHeapCols = null;
138: if (heapColRefItem != -1) {
139: this .accessedHeapCols = (FormatableBitSet) (a
140: .getPreparedStatement()
141: .getSavedObject(heapColRefItem));
142: }
143: if (indexColRefItem != -1) {
144: this .accessedIndexCols = (FormatableBitSet) (a
145: .getPreparedStatement()
146: .getSavedObject(indexColRefItem));
147: }
148: if (accessedIndexCols == null)
149: accessedAllCols = accessedHeapCols;
150: else {
151: accessedAllCols = new FormatableBitSet(accessedHeapCols);
152: accessedAllCols.or(accessedIndexCols);
153: }
154:
155: // retrieve the array of columns coming from the index
156: indexCols = ((ReferencedColumnsDescriptorImpl) (a
157: .getPreparedStatement().getSavedObject(indexColMapItem)))
158: .getReferencedColumnPositions();
159:
160: /* Get the result row template */
161: resultRow = (ExecRow) resultRowAllocator.invoke(activation);
162:
163: compactRow = getCompactRow(resultRow, accessedHeapCols,
164: accessedIndexCols, false);
165:
166: /* If there's no partial row bit map, then we want the entire
167: * row, otherwise we need to diddle with the row array so that
168: * we only get the columns coming from the heap on the fetch.
169: */
170: if (accessedHeapCols == null) {
171: rowArray = resultRow.getRowArray();
172: } else {
173: // Figure out how many columns are coming from the heap
174: int arraySize = accessedHeapCols.getNumBitsSet();
175: int accessedHeapColsSize = accessedHeapCols.size();
176:
177: rowArray = new DataValueDescriptor[accessedHeapColsSize];
178:
179: // Now, fill in rowArray with the desired columns
180: int partialIndex = 0;
181: int numFromIndex = 0;
182: for (int index = 0; index < accessedHeapColsSize; index++) {
183: if (accessedIndexCols != null
184: && accessedIndexCols.get(index)) {
185: numFromIndex++;
186: continue;
187: }
188: if (accessedHeapCols.get(index)) {
189: rowArray[index] = resultRow.getRowArray()[index];
190: partialIndex++;
191: }
192: }
193: }
194:
195: constructorTime += getElapsedMillis(beginTime);
196:
197: }
198:
199: //
200: // ResultSet interface (leftover from NoPutResultSet)
201: //
202:
203: /**
204: * open this ResultSet.
205: *
206: * @exception StandardException thrown if cursor finished.
207: */
208: public void openCore() throws StandardException {
209: boolean lockingRequired = false;
210: TransactionController tc;
211:
212: // REVISIT: through the direct DB API, this needs to be an
213: // error, not an ASSERT; users can open twice. Only through JDBC
214: // is access to open controlled and ensured valid.
215: if (SanityManager.DEBUG) {
216: SanityManager.ASSERT(!isOpen,
217: "IndexRowToBaseRowResultSet already open");
218: }
219:
220: beginTime = getCurrentTimeMillis();
221:
222: source.openCore();
223: if ((source instanceof TableScanResultSet)
224: && ((TableScanResultSet) source).indexCols != null)
225: sourceIsForUpdateIndexScan = true;
226:
227: /* Get a ConglomerateController for the base conglomerate
228: * NOTE: We only need to acquire locks on the data pages when
229: * going through the index when we are at READ COMMITTED and
230: * the source is a BulkTableScan or HashScan. (The underlying
231: * row will not be guaranteed to be locked.)
232: */
233: if (source.requiresRelocking()) {
234: lockingRequired = true;
235: }
236:
237: tc = activation.getTransactionController();
238:
239: int openMode;
240: int isolationLevel;
241:
242: if (forUpdate) {
243: openMode = TransactionController.OPENMODE_FORUPDATE;
244: } else {
245: openMode = 0;
246: }
247: isolationLevel = source.getScanIsolationLevel();
248:
249: if (!lockingRequired) {
250: // flag indicates that lock has already been acquired by access to
251: // the secondary index, and need not be gotten again in the base
252: // table.
253: openMode |= TransactionController.OPENMODE_SECONDARY_LOCKED;
254: }
255:
256: /* Try to get the ConglomerateController from the activation
257: * first, for the case that we are part of an update or delete.
258: * If so, then the RowChangerImpl did the correct locking.
259: * If not there, then we go off and open it ourself.
260: */
261: if (forUpdate) {
262: baseCC = activation.getHeapConglomerateController();
263: }
264:
265: if (baseCC == null) {
266: baseCC = tc.openCompiledConglomerate(activation
267: .getResultSetHoldability(), openMode,
268: // consistent with FromBaseTable's updateTargetLockMode
269: TransactionController.MODE_RECORD, isolationLevel,
270: scoci, dcoci);
271: closeBaseCCHere = true;
272: }
273:
274: isOpen = true;
275: numOpens++;
276: openTime += getElapsedMillis(beginTime);
277: }
278:
279: /**
280: * reopen this ResultSet.
281: *
282: * @exception StandardException thrown if cursor finished.
283: */
284: public void reopenCore() throws StandardException {
285: TransactionController tc;
286:
287: if (SanityManager.DEBUG) {
288: SanityManager.ASSERT(isOpen,
289: "IndexRowToBaseRowResultSet already open");
290: }
291:
292: beginTime = getCurrentTimeMillis();
293:
294: source.reopenCore();
295:
296: numOpens++;
297: openTime += getElapsedMillis(beginTime);
298: }
299:
300: /**
301: * Return the requested values computed
302: * from the next row (if any) for which
303: * the restriction evaluates to true.
304: * <p>
305: * restriction and projection parameters
306: * are evaluated for each row.
307: *
308: * @exception StandardException thrown on failure.
309: * @exception StandardException ResultSetNotOpen thrown if not yet open.
310: *
311: * @return the next row in the result
312: */
313: public ExecRow getNextRowCore() throws StandardException {
314:
315: ExecRow sourceRow = null;
316: ExecRow retval = null;
317: boolean restrict = false;
318: DataValueDescriptor restrictBoolean;
319: long beginRT = 0;
320:
321: beginTime = getCurrentTimeMillis();
322: if (!isOpen) {
323: throw StandardException.newException(
324: SQLState.LANG_RESULT_SET_NOT_OPEN, "next");
325: }
326:
327: /* beetle 3865, updateable cursor using index. When in-memory hash table was full, we
328: * read forward and saved future row id's in a virtual-memory-like temp table. So if
329: * we have rid's saved, and we are here, it must be non-covering index. Intercept it
330: * here, so that we don't have to go to underlying index scan. We get both heap cols
331: * and index cols together here for better performance.
332: */
333: if (sourceIsForUpdateIndexScan
334: && ((TableScanResultSet) source).futureForUpdateRows != null) {
335: currentRowPrescanned = false;
336: TableScanResultSet src = (TableScanResultSet) source;
337:
338: if (src.futureRowResultSet == null) {
339: src.futureRowResultSet = (TemporaryRowHolderResultSet) src.futureForUpdateRows
340: .getResultSet();
341: src.futureRowResultSet.openCore();
342: }
343:
344: ExecRow ridRow = src.futureRowResultSet.getNextRowCore();
345:
346: currentRow = null;
347:
348: if (ridRow != null) {
349: /* To maximize performance, we only use virtual memory style heap, no
350: * position index is ever created. And we save and retrieve rows from the
351: * in-memory part of the heap as much as possible. We can also insert after
352: * we start retrieving, the assumption is that we delete the current row right
353: * after we retrieve it.
354: */
355: src.futureRowResultSet.deleteCurrentRow();
356: baseRowLocation = (RowLocation) ridRow.getColumn(1);
357: baseCC.fetch(baseRowLocation, compactRow.getRowArray(),
358: accessedAllCols);
359:
360: currentRow = compactRow;
361: currentRowPrescanned = true;
362: } else if (src.sourceDrained)
363: currentRowPrescanned = true;
364:
365: if (currentRowPrescanned) {
366: setCurrentRow(currentRow);
367:
368: nextTime += getElapsedMillis(beginTime);
369: return currentRow;
370: }
371: }
372:
373: /* Loop until we get a row from the base page that qualifies or
374: * there's no more rows from the index that qualify. (If the RID
375: * returned by the index does not qualify, then we have to go back
376: * to the index to see if there is another RID to consider.)
377: */
378: do {
379: sourceRow = source.getNextRowCore();
380:
381: if (sourceRow != null) {
382:
383: if (SanityManager.DEBUG) {
384: SanityManager
385: .ASSERT(
386: sourceRow.getColumn(sourceRow
387: .nColumns()) instanceof RowLocation,
388: "Last column of source row is not a RowLocation");
389: }
390:
391: baseRowLocation = (RowLocation) sourceRow
392: .getColumn(sourceRow.nColumns());
393:
394: // Fetch the columns coming from the heap
395: boolean row_exists = baseCC.fetch(baseRowLocation,
396: rowArray, accessedHeapCols);
397:
398: if (row_exists) {
399: /* We only need to copy columns from the index row
400: * to our result row once as we will be reusing the
401: * wrappers in that case.
402: * NOTE: When the underlying ResultSet got an
403: * instantaneous lock (BulkTableScan or HashScan)
404: * then we will be getting all of the columns anew
405: * from the index (indexCols == null).
406: */
407: if (!copiedFromSource) {
408: copiedFromSource = true;
409:
410: // Copy the columns coming from the index into resultRow
411: for (int index = 0; index < indexCols.length; index++) {
412: if (indexCols[index] != -1) {
413: compactRow
414: .setColumn(
415: index + 1,
416: sourceRow
417: .getColumn(indexCols[index] + 1));
418: }
419: }
420: }
421:
422: setCurrentRow(compactRow);
423:
424: restrictBoolean = (DataValueDescriptor) ((restriction == null) ? null
425: : restriction.invoke(activation));
426:
427: restrictionTime += getElapsedMillis(beginRT);
428:
429: // if the result is null, we make it false --
430: // so the row won't be returned.
431: restrict = (restrictBoolean == null)
432: || ((!restrictBoolean.isNull()) && restrictBoolean
433: .getBoolean());
434: }
435:
436: if (!restrict || !row_exists) {
437: rowsFiltered++;
438: clearCurrentRow();
439: baseRowLocation = null;
440:
441: } else {
442: currentRow = compactRow;
443: }
444:
445: /* Update the run time statistics */
446: rowsSeen++;
447:
448: retval = currentRow;
449: } else {
450: clearCurrentRow();
451: baseRowLocation = null;
452:
453: retval = null;
454: }
455: } while ((sourceRow != null) && (!restrict));
456:
457: nextTime += getElapsedMillis(beginTime);
458: return retval;
459: }
460:
461: /**
462: * If the result set has been opened,
463: * close the open scan.
464: *
465: * @exception StandardException thrown on error
466: */
467: public void close() throws StandardException {
468: beginTime = getCurrentTimeMillis();
469: if (isOpen) {
470:
471: // we don't want to keep around a pointer to the
472: // row ... so it can be thrown away.
473: // REVISIT: does this need to be in a finally
474: // block, to ensure that it is executed?
475: clearCurrentRow();
476:
477: if (closeBaseCCHere) {
478: // This check should only be needed in the error case where
479: // we may call this close() routine as part of transaction
480: // backout cleanup if any of the following routines fail.
481: // If one of the subsequent statements gets an error, we
482: // will try to close this result set as part of transaction
483: // cleanup, and without this check we get a null pointer
484: // exception because we have null'd out baseCC.
485:
486: if (baseCC != null)
487: baseCC.close();
488: }
489:
490: /* Make sure to null out baseCC since
491: * we check for null baseCC after looking
492: * in the StatementContext.
493: */
494: baseCC = null;
495: source.close();
496:
497: super .close();
498: } else if (SanityManager.DEBUG) {
499: SanityManager.DEBUG("CloseRepeatInfo",
500: "Close of IndexRowToBaseRowResultSet repeated");
501: }
502:
503: closeTime += getElapsedMillis(beginTime);
504: }
505:
506: /**
507: * Return the total amount of time spent in this ResultSet
508: *
509: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
510: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
511: *
512: * @return long The total amount of time spent (in milliseconds).
513: */
514: public long getTimeSpent(int type) {
515: long totTime = constructorTime + openTime + nextTime
516: + closeTime;
517:
518: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
519: return totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);
520: } else {
521: return totTime;
522: }
523: }
524:
525: //
526: // CursorResultSet interface
527: //
528: /**
529: * Return the RowLocation of the base row.
530: *
531: * @see CursorResultSet
532: *
533: * @return the row location of the current cursor row.
534: * @exception StandardException thrown on failure.
535: */
536: public RowLocation getRowLocation() throws StandardException {
537: return baseRowLocation;
538: }
539:
540: /**
541: * @see NoPutResultSet#positionScanAtRowLocation
542: *
543: * Also remembers row location so that subsequent invocations of
544: * getCurrentRow will not read the index row to look up the row
545: * location base row, but reuse the saved row location.
546: */
547: public void positionScanAtRowLocation(RowLocation rl)
548: throws StandardException {
549: baseRowLocation = rl;
550: source.positionScanAtRowLocation(rl);
551: }
552:
553: /** * Gets last row returned.
554: *
555: * @see CursorResultSet
556: *
557: * @return the last row returned.
558: * @exception StandardException thrown on failure.
559: */
560: /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),
561: * once there is such a method. (currentRow is redundant)
562: */
563: public ExecRow getCurrentRow() throws StandardException {
564: ExecRow sourceRow = null;
565:
566: if (SanityManager.DEBUG) {
567: SanityManager
568: .ASSERT(isOpen,
569: "IndexRowToBaseRowResultSet is expected to be open");
570: }
571:
572: if (currentRowPrescanned)
573: return currentRow;
574:
575: /* Nothing to do if we're not currently on a row */
576: if (currentRow == null) {
577: return null;
578: }
579:
580: // We do not need to read the row from the index first, since we already
581: // have the rowLocation of the current row and can read it directly from
582: // the heap.
583: sourceRow = activation.getExecutionFactory().getValueRow(
584: indexCols.length);
585: sourceRow.setRowArray(rowArray);
586: // Fetch the columns coming from the heap
587: boolean row_exists = baseCC.fetch(baseRowLocation, rowArray,
588: (FormatableBitSet) null);
589: if (row_exists) {
590: setCurrentRow(sourceRow);
591: } else {
592: clearCurrentRow();
593: }
594: return currentRow;
595: }
596:
597: /**
598: * Is this ResultSet or it's source result set for update.
599: * beetle 3865: updateable cursor using index scan. We didn't need this function
600: * before because we couldn't use index for update cursor.
601: *
602: * @return Whether or not the result set is for update.
603: */
604: public boolean isForUpdate() {
605: return source.isForUpdate();
606: }
607:
608: }
|