001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.LastIndexKeyResultSet
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.sql.execute.CursorResultSet;
025: import org.apache.derby.iapi.error.StandardException;
026:
027: import org.apache.derby.iapi.sql.ResultSet;
028: import org.apache.derby.iapi.sql.execute.ExecRow;
029: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
030: import org.apache.derby.iapi.sql.execute.ExecutionContext;
031: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
032:
033: import org.apache.derby.iapi.sql.Activation;
034:
035: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
036:
037: import org.apache.derby.iapi.store.access.ConglomerateController;
038: import org.apache.derby.iapi.store.access.GenericScanController;
039: import org.apache.derby.iapi.store.access.Qualifier;
040: import org.apache.derby.iapi.store.access.ScanController;
041: import org.apache.derby.iapi.store.access.TransactionController;
042:
043: import org.apache.derby.iapi.services.sanity.SanityManager;
044:
045: import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
046:
047: import org.apache.derby.iapi.services.loader.GeneratedMethod;
048:
049: import org.apache.derby.iapi.services.io.FormatableBitSet;
050:
051: import java.util.Properties;
052:
053: /**
054: * Return the last key in an index. Used to perform
055: * max().
056: *
057: * @author jamie
058: */
059: class LastIndexKeyResultSet extends NoPutResultSetImpl {
060: protected ExecRow candidate;
061:
062: // set in constructor and not altered during
063: // life of object.
064: protected long conglomId;
065: protected GeneratedMethod resultRowAllocator;
066: protected GeneratedMethod startKeyGetter;
067: protected int startSearchOperator;
068: protected GeneratedMethod stopKeyGetter;
069: protected int stopSearchOperator;
070: protected Qualifier[][] qualifiers;
071: public String tableName;
072: public String userSuppliedOptimizerOverrides;
073: public String indexName;
074: protected boolean runTimeStatisticsOn;
075: protected FormatableBitSet accessedCols;
076:
077: public int isolationLevel;
078: public int lockMode;
079:
080: // Run time statistics
081: public String stopPositionString;
082: public boolean coarserLock;
083: public boolean returnedRow;
084:
085: /**
086: * A last index key result set returns the last row from
087: * the index in question. It is used as an ajunct to max().
088: *
089: * @param activation the activation for this result set,
090: * which provides the context for the row allocation operation.
091: * @param resultSetNumber The resultSetNumber for the ResultSet
092: * @param resultRowAllocator a reference to a method in the activation
093: * that creates a holder for the result row of the scan. May
094: * be a partial row. <verbatim>
095: * ExecRow rowAllocator() throws StandardException; </verbatim>
096: * @param conglomId the conglomerate of the table to be scanned.
097: * @param tableName The full name of the table
098: * @param userSuppliedOptimizerOverrides Overrides specified by the user on the sql
099: * @param indexName The name of the index, if one used to access table.
100: * @param colRefItem An saved item for a bitSet of columns that
101: * are referenced in the underlying table. -1 if
102: * no item.
103: * @param lockMode The lock granularity to use (see
104: * TransactionController in access)
105: * @param tableLocked Whether or not the table is marked as using table locking
106: * (in sys.systables)
107: * @param isolationLevel Isolation level (specified or not) to use on scans
108: * @param optimizerEstimatedRowCount Estimated total # of rows by
109: * optimizer
110: * @param optimizerEstimatedCost Estimated total cost by optimizer
111: *
112: * @exception StandardException thrown when unable to create the
113: * result set
114: */
115: public LastIndexKeyResultSet(Activation activation,
116: int resultSetNumber, GeneratedMethod resultRowAllocator,
117: long conglomId, String tableName,
118: String userSuppliedOptimizerOverrides, String indexName,
119: int colRefItem, int lockMode, boolean tableLocked,
120: int isolationLevel, double optimizerEstimatedRowCount,
121: double optimizerEstimatedCost) throws StandardException {
122: super (activation, resultSetNumber, optimizerEstimatedRowCount,
123: optimizerEstimatedCost);
124:
125: this .conglomId = conglomId;
126:
127: if (SanityManager.DEBUG) {
128: SanityManager.ASSERT(activation != null,
129: "this scan must get activation context");
130: SanityManager.ASSERT(resultRowAllocator != null,
131: "this scan must get row allocator");
132: }
133:
134: this .resultRowAllocator = resultRowAllocator;
135: this .tableName = tableName;
136: this .userSuppliedOptimizerOverrides = userSuppliedOptimizerOverrides;
137: this .indexName = indexName;
138: this .lockMode = lockMode;
139: if (colRefItem != -1) {
140: this .accessedCols = (FormatableBitSet) (activation
141: .getPreparedStatement().getSavedObject(colRefItem));
142: }
143: /* Isolation level - translate from language to store */
144: // If not specified, get current isolation level
145: if (isolationLevel == ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) {
146: isolationLevel = lcc.getCurrentIsolationLevel();
147: }
148:
149: if (isolationLevel == ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL) {
150: this .isolationLevel = TransactionController.ISOLATION_SERIALIZABLE;
151: } else {
152: /* NOTE: always do row locking on READ COMMITTED/UNCOMMITTED
153: * and repeatable read scans unless the table is marked as
154: * table locked (in sys.systables).
155: *
156: * We always get instantaneous locks as we will complete
157: * the scan before returning any rows and we will fully
158: * requalify the row if we need to go to the heap on a next().
159: */
160:
161: if (!tableLocked) {
162: this .lockMode = TransactionController.MODE_RECORD;
163: }
164:
165: if (isolationLevel == ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL) {
166: this .isolationLevel = TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK;
167: } else if (isolationLevel == ExecutionContext.READ_UNCOMMITTED_ISOLATION_LEVEL) {
168: this .isolationLevel = TransactionController.ISOLATION_READ_UNCOMMITTED;
169: } else if (isolationLevel == ExecutionContext.REPEATABLE_READ_ISOLATION_LEVEL) {
170: this .isolationLevel = TransactionController.ISOLATION_REPEATABLE_READ;
171: }
172: }
173:
174: if (SanityManager.DEBUG) {
175: SanityManager
176: .ASSERT(
177: ((isolationLevel == ExecutionContext.READ_COMMITTED_ISOLATION_LEVEL)
178: || (isolationLevel == ExecutionContext.READ_UNCOMMITTED_ISOLATION_LEVEL)
179: || (isolationLevel == ExecutionContext.REPEATABLE_READ_ISOLATION_LEVEL) || (isolationLevel == ExecutionContext.SERIALIZABLE_ISOLATION_LEVEL)),
180:
181: "Invalid isolation level - "
182: + isolationLevel);
183: }
184:
185: runTimeStatisticsOn = getLanguageConnectionContext()
186: .getRunTimeStatisticsMode();
187:
188: /* Only call row allocators once */
189: candidate = (ExecRow) resultRowAllocator.invoke(activation);
190: constructorTime += getElapsedMillis(beginTime);
191:
192: /*
193: ** If scan tracing is turned on, print information about this
194: ** LastIndexKeyResultSet when it is first opened.
195: */
196: if (SanityManager.DEBUG) {
197: if (SanityManager.DEBUG_ON("ScanTrace")) {
198: //traceScanParameters();
199: }
200: }
201:
202: activation.informOfRowCount(this , 1);
203: }
204:
205: /////////////////////////////////////////////////////
206: //
207: // ResultSet interface (leftover from NoPutResultSet)
208: //
209: /////////////////////////////////////////////////////
210:
211: /**
212: * open a scan on the table. scan parameters are evaluated
213: * at each open, so there is probably some way of altering
214: * their values...
215: *
216: * @exception StandardException thrown on failure to open
217: */
218: public void openCore() throws StandardException {
219: ExecRow candidateCopy = candidate.getClone();
220:
221: beginTime = getCurrentTimeMillis();
222: if (SanityManager.DEBUG) {
223: SanityManager.ASSERT(!isOpen,
224: "LastIndexKeyResultSet already open");
225: }
226:
227: isOpen = true;
228: TransactionController tc = activation
229: .getTransactionController();
230:
231: /*
232: ** Grab the last row. Note that if there are deletes
233: ** left lying around and no real row to return, then
234: ** the row array gets set even though the scan doesn't
235: ** return a row, so be careful to handle this correctly.
236: */
237: if (tc.fetchMaxOnBtree(conglomId, // conglomerate to open
238: 0, // open mode
239: lockMode, isolationLevel, accessedCols, candidateCopy
240: .getRowArray())) {
241: currentRow = getCompactRow(candidateCopy, accessedCols,
242: (FormatableBitSet) null, true);
243: setCurrentRow(currentRow);
244: } else {
245: clearCurrentRow();
246: }
247:
248: numOpens++;
249: openTime += getElapsedMillis(beginTime);
250: }
251:
252: /**
253: * Return the next row (if any) from the scan (if open).
254: *
255: * @exception StandardException thrown on failure to get next row
256: */
257: public ExecRow getNextRowCore() throws StandardException {
258: if (returnedRow || !isOpen) {
259: clearCurrentRow();
260: } else {
261: returnedRow = true;
262: }
263: return currentRow;
264: }
265:
266: /**
267: * If the result set has been opened,
268: * close the open scan.
269: * @exception StandardException thrown on failure to close
270: */
271: public void close() throws StandardException {
272: beginTime = getCurrentTimeMillis();
273: if (isOpen) {
274: isOpen = false;
275: clearCurrentRow();
276:
277: super .close();
278: } else {
279: if (SanityManager.DEBUG) {
280: SanityManager.DEBUG("CloseRepeatInfo",
281: "Close of LastIndexKeyResultSet repeated");
282: }
283: }
284:
285: closeTime += getElapsedMillis(beginTime);
286: }
287:
288: /**
289: * Return the total amount of time spent in this ResultSet
290: *
291: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
292: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
293: *
294: * @return long The total amount of time spent (in milliseconds).
295: */
296: public long getTimeSpent(int type) {
297: long totTime = constructorTime + openTime + nextTime
298: + closeTime;
299:
300: /* RESOLVE - subtract out store time later, when available */
301: if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY) {
302: return totTime;
303: } else {
304: return totTime;
305: }
306: }
307:
308: /**
309: * This result set has its row from the last fetch done.
310: * If the cursor is closed, a null is returned.
311: *
312: * @see CursorResultSet
313: *
314: * @return the last row returned;
315: * @exception StandardException thrown on failure.
316: */
317: public ExecRow getCurrentRow() throws StandardException {
318: return currentRow;
319: }
320:
321: /**
322: * Print the parameters that constructed this result set to the
323: * trace stream.
324: */
325: /*
326: private final void traceScanParameters()
327: {
328: if (SanityManager.DEBUG)
329: {
330: HeaderPrintWriter traceStream = SanityManager.GET_DEBUG_STREAM();
331:
332: traceStream.println("");
333: traceStream.println("LastIndexKeyResultSet number " +
334: resultSetNumber +
335: " parameters:");
336:
337: traceStream.println("");
338: traceStream.println("\tTable name: " + tableName);
339: if (indexName != null)
340: {
341: traceStream.println("\tIndex name: " + indexName);
342: }
343: traceStream.println("");
344: }
345: }
346: */
347:
348: }
|