001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.execute.CurrentOfResultSet
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: import org.apache.derby.iapi.reference.SQLState;
027:
028: import org.apache.derby.iapi.sql.execute.CursorActivation;
029: import org.apache.derby.iapi.sql.execute.ExecRow;
030: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
031:
032: import org.apache.derby.iapi.sql.Activation;
033: import org.apache.derby.iapi.sql.ResultSet;
034: import org.apache.derby.iapi.sql.PreparedStatement;
035:
036: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
037:
038: import org.apache.derby.iapi.types.RowLocation;
039:
040: import org.apache.derby.iapi.services.sanity.SanityManager;
041: import org.apache.derby.iapi.sql.depend.DependencyManager;
042:
043: /**
044: * Takes a cursor name and returns the current row
045: * of the cursor; for use in generating the source
046: * row and row location for positioned update/delete operations.
047: * <p>
048: * This result set returns only one row.
049: *
050: * @author ames
051: */
052: public class CurrentOfResultSet extends NoPutResultSetImpl implements
053: CursorResultSet {
054:
055: private boolean next;
056: private RowLocation rowLocation;
057:
058: private CursorResultSet cursor;
059: private CursorResultSet target;
060: private ExecRow sparseRow;
061:
062: // set in constructor and not altered during
063: // life of object.
064: private final String cursorName;
065: private final String psName;
066:
067: //
068: // class interface
069: //
070: public CurrentOfResultSet(String cursorName, Activation activation,
071: int resultSetNumber, String psName) {
072: super (activation, resultSetNumber, 0.0d, 0.0d);
073: if (SanityManager.DEBUG)
074: SanityManager.ASSERT(cursorName != null,
075: "current of scan must get cursor name");
076: this .cursorName = cursorName;
077: this .psName = psName;
078: }
079:
080: //
081: // ResultSet interface (leftover from NoPutResultSet)
082: //
083: /**
084: * open a scan on the table. scan parameters are evaluated
085: * at each open, so there is probably some way of altering
086: * their values...
087: *
088: * @exception StandardException thrown on failure to open
089: */
090: public void openCore() throws StandardException {
091: if (SanityManager.DEBUG)
092: SanityManager.ASSERT(!isOpen,
093: "CurrentOfResultSet already open");
094:
095: // get the cursor
096: getCursor();
097:
098: next = false;
099: isOpen = true;
100: }
101:
102: /**
103: * If open and not returned yet, returns the row.
104: *
105: * @exception StandardException thrown on failure.
106: */
107: public ExecRow getNextRowCore() throws StandardException {
108:
109: if (isOpen) {
110: if (!next) {
111: next = true;
112: if (SanityManager.DEBUG)
113: SanityManager.ASSERT(!cursor.isClosed(),
114: "cursor closed");
115:
116: ExecRow cursorRow = cursor.getCurrentRow();
117:
118: // requalify the current row
119: if (cursorRow == null) {
120: throw StandardException
121: .newException(SQLState.NO_CURRENT_ROW);
122: }
123: // we know it will be requested, may as well get it now.
124: rowLocation = cursor.getRowLocation();
125:
126: // get the row from the base table, which is the real result
127: // row for the CurrentOfResultSet
128: currentRow = target.getCurrentRow();
129:
130: // if the source result set is a ScrollInsensitiveResultSet, and
131: // the current row has been deleted (while the cursor was
132: // opened), the cursor result set (scroll insensitive) will
133: // return the cached row, while the target result set will
134: // return null (row has been deleted under owr feet).
135: if (rowLocation == null
136: || (cursorRow != null && currentRow == null)) {
137: activation
138: .addWarning(StandardException
139: .newWarning(SQLState.CURSOR_OPERATION_CONFLICT));
140: return null;
141: }
142:
143: /* beetle 3865: updateable cursor using index. If underlying is a covering
144: * index, target is a TableScanRS (instead of a IndexRow2BaseRowRS) for the
145: * index scan. But the problem is it returns a compact row in index key order.
146: * However the ProjectRestrictRS above us that sets up the old and new column
147: * values expects us to return a sparse row in heap order. We have to do the
148: * wiring here, since we don't have IndexRow2BaseRowRS to do this work. This
149: * problem was not exposed before, because we never used index scan for updateable
150: * cursors.
151: */
152: if (target instanceof TableScanResultSet) {
153: TableScanResultSet scan = (TableScanResultSet) target;
154: if (scan.indexCols != null && currentRow != null)
155: currentRow = getSparseRow(currentRow,
156: scan.indexCols);
157: }
158: /* If we are updating rows from cached RIDs, we should compare with forward-most
159: * scan key when deciding whether to add RID to hash table or not.
160: */
161: TableScanResultSet scan = (TableScanResultSet) activation
162: .getForUpdateIndexScan();
163: if (scan != null) {
164: if (target instanceof IndexRowToBaseRowResultSet)
165: scan.compareToLastKey = ((IndexRowToBaseRowResultSet) target).currentRowPrescanned;
166: else if (target instanceof TableScanResultSet)
167: scan.compareToLastKey = ((TableScanResultSet) target).currentRowPrescanned;
168: }
169:
170: // REMIND: verify the row is still there
171: // at present we get an ugly exception from the store,
172: // Hopefully someday we can just do this:
173: //
174: // if (!rowLocation.rowExists())
175: // throw StandardException.newException(SQLState.LANG_NO_CURRENT_ROW, cursorName);
176: } else {
177: currentRow = null;
178: rowLocation = null;
179: }
180: } else {
181: currentRow = null;
182: rowLocation = null;
183: }
184: setCurrentRow(currentRow);
185: return currentRow;
186: }
187:
188: /**
189: * Return a sparse heap row, based on a compact index row.
190: *
191: * @param row compact referenced index row
192: * @param indexCols base column positions of index keys, signed with asc/desc info
193: *
194: * @return a sparse heap row with referenced columns
195: */
196: private ExecRow getSparseRow(ExecRow row, int[] indexCols)
197: throws StandardException {
198: int colPos;
199: if (sparseRow == null) {
200: int numCols = 1;
201: for (int i = 0; i < indexCols.length; i++) {
202: colPos = (indexCols[i] > 0) ? indexCols[i]
203: : -indexCols[i];
204: if (colPos > numCols)
205: numCols = colPos;
206: }
207: sparseRow = new ValueRow(numCols);
208: }
209: for (int i = 1; i <= indexCols.length; i++) {
210: colPos = (indexCols[i - 1] > 0) ? indexCols[i - 1]
211: : -indexCols[i - 1];
212: sparseRow.setColumn(colPos, row.getColumn(i));
213: }
214:
215: return sparseRow;
216: }
217:
218: /**
219: * If the result set has been opened,
220: * close the open scan.
221: *
222: * @exception StandardException thrown on error
223: */
224: public void close() throws StandardException {
225: if (isOpen) {
226: // we don't want to keep around a pointer to the
227: // row ... so it can be thrown away.
228: // REVISIT: does this need to be in a finally
229: // block, to ensure that it is executed?
230: clearCurrentRow();
231: next = false;
232:
233: super .close();
234: } else if (SanityManager.DEBUG)
235: SanityManager.DEBUG("CloseRepeatInfo",
236: "Close of CurrentOfResultSet repeated");
237: }
238:
239: public void finish() throws StandardException {
240: finishAndRTS();
241: }
242:
243: /**
244: * Return the total amount of time spent in this ResultSet
245: *
246: * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet
247: * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.
248: *
249: * @return long The total amount of time spent (in milliseconds).
250: */
251: public long getTimeSpent(int type) {
252: /* RESOLVE - RunTimeStats not implemented yet */
253: return 0;
254: }
255:
256: /**
257: * This result set has its row location from
258: * the last fetch done. If it is closed,
259: * a null is returned.
260: *
261: * @see CursorResultSet
262: *
263: * @return the row location of the current row.
264: * @exception StandardException thrown on failure to get row location
265: */
266: public RowLocation getRowLocation() {
267: return rowLocation;
268: }
269:
270: /**
271: * @see CursorResultSet
272: *
273: * @return the last row returned by getNextRow.
274: */
275: public ExecRow getCurrentRow() {
276: return currentRow;
277: }
278:
279: //
280: // class implementation
281: //
282: /**
283: Because the positioned operation only gets one location
284: per execution, and the cursor could be completely different
285: for each execution (closed and reopened, perhaps), we
286: determine where caching the cursor could be applied.
287: <p>
288: When cached, we check if the cursor was closed'd,
289: and if so, throw it out and
290: see if there's one in the cache with our name.
291:
292: */
293: private void getCursor() throws StandardException {
294:
295: // need to look again if cursor was closed
296: if (cursor != null) {
297: if (cursor.isClosed()) {
298: cursor = null;
299: target = null;
300: }
301: }
302:
303: if (cursor == null) {
304:
305: LanguageConnectionContext lcc = getLanguageConnectionContext();
306:
307: CursorActivation cursorActivation = lcc
308: .lookupCursorActivation(cursorName);
309:
310: if (cursorActivation != null) {
311:
312: cursor = cursorActivation.getCursorResultSet();
313: target = cursorActivation.getTargetResultSet();
314: /* beetle 3865: updateable cursor using index. 2 way communication between
315: * update activation and cursor activation. Cursor passes index scan to
316: * update and update passes heap conglom controller to cursor.
317: */
318: activation.setForUpdateIndexScan(cursorActivation
319: .getForUpdateIndexScan());
320: if (cursorActivation.getHeapConglomerateController() != null)
321: cursorActivation.getHeapConglomerateController()
322: .close();
323: cursorActivation
324: .setHeapConglomerateController(activation
325: .getHeapConglomerateController());
326: }
327: }
328:
329: if (cursor == null || cursor.isClosed()) {
330: throw StandardException.newException(
331: SQLState.LANG_CURSOR_CLOSED, cursorName);
332: }
333: }
334:
335: /**
336: * @see NoPutResultSet#updateRow
337: */
338: public void updateRow(ExecRow row) throws StandardException {
339: ((NoPutResultSet) cursor).updateRow(row);
340: }
341:
342: /**
343: * @see NoPutResultSet#markRowAsDeleted
344: */
345: public void markRowAsDeleted() throws StandardException {
346: ((NoPutResultSet) cursor).markRowAsDeleted();
347: }
348:
349: }
|