--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.CurrentOfResultSet\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.execute;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorActivation;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.PreparedStatement;\r
+\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.depend.DependencyManager;\r
+\r
+/**\r
+ * Takes a cursor name and returns the current row\r
+ * of the cursor; for use in generating the source\r
+ * row and row location for positioned update/delete operations.\r
+ * <p>\r
+ * This result set returns only one row.\r
+ *\r
+ */\r
+class CurrentOfResultSet extends NoPutResultSetImpl\r
+ implements CursorResultSet {\r
+\r
+ private boolean next;\r
+ private RowLocation rowLocation;\r
+\r
+ private CursorResultSet cursor;\r
+ private CursorResultSet target;\r
+ private ExecRow sparseRow;\r
+\r
+ // set in constructor and not altered during\r
+ // life of object.\r
+ private final String cursorName;\r
+\r
+ //\r
+ // class interface\r
+ //\r
+ CurrentOfResultSet(String cursorName, Activation activation, \r
+ int resultSetNumber)\r
+ {\r
+ super(activation, resultSetNumber, 0.0d, 0.0d);\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT( cursorName!=null, "current of scan must get cursor name");\r
+ this.cursorName = cursorName;\r
+ }\r
+\r
+ //\r
+ // ResultSet interface (leftover from NoPutResultSet)\r
+ //\r
+ /**\r
+ * open a scan on the table. scan parameters are evaluated\r
+ * at each open, so there is probably some way of altering\r
+ * their values...\r
+ *\r
+ * @exception StandardException thrown on failure to open\r
+ */\r
+ public void openCore() throws StandardException {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT( ! isOpen, "CurrentOfResultSet already open");\r
+\r
+ // get the cursor\r
+ getCursor();\r
+\r
+ next = false;\r
+ isOpen = true;\r
+ }\r
+\r
+ /**\r
+ * If open and not returned yet, returns the row.\r
+ *\r
+ * @exception StandardException thrown on failure.\r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException {\r
+\r
+ if ( isOpen ) {\r
+ if ( ! next ) {\r
+ next = true;\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(! cursor.isClosed(), "cursor closed");\r
+\r
+ ExecRow cursorRow = cursor.getCurrentRow();\r
+\r
+ // requalify the current row\r
+ if (cursorRow == null) {\r
+ throw StandardException.newException(SQLState.NO_CURRENT_ROW);\r
+ }\r
+ // we know it will be requested, may as well get it now.\r
+ rowLocation = cursor.getRowLocation();\r
+\r
+ // get the row from the base table, which is the real result\r
+ // row for the CurrentOfResultSet\r
+ currentRow = target.getCurrentRow();\r
+\r
+ // if the source result set is a ScrollInsensitiveResultSet, and\r
+ // the current row has been deleted (while the cursor was \r
+ // opened), the cursor result set (scroll insensitive) will \r
+ // return the cached row, while the target result set will \r
+ // return null (row has been deleted under owr feet).\r
+ if (rowLocation == null || \r
+ (cursorRow != null && currentRow == null)) {\r
+ activation.addWarning(StandardException.\r
+ newWarning(SQLState.CURSOR_OPERATION_CONFLICT));\r
+ return null;\r
+ }\r
+\r
+ /* beetle 3865: updateable cursor using index. If underlying is a covering\r
+ * index, target is a TableScanRS (instead of a IndexRow2BaseRowRS) for the\r
+ * index scan. But the problem is it returns a compact row in index key order.\r
+ * However the ProjectRestrictRS above us that sets up the old and new column\r
+ * values expects us to return a sparse row in heap order. We have to do the\r
+ * wiring here, since we don't have IndexRow2BaseRowRS to do this work. This\r
+ * problem was not exposed before, because we never used index scan for updateable\r
+ * cursors.\r
+ */\r
+ if (target instanceof TableScanResultSet)\r
+ {\r
+ TableScanResultSet scan = (TableScanResultSet) target;\r
+ if (scan.indexCols != null && currentRow != null)\r
+ currentRow = getSparseRow(currentRow, scan.indexCols);\r
+ }\r
+ /* If we are updating rows from cached RIDs, we should compare with forward-most\r
+ * scan key when deciding whether to add RID to hash table or not.\r
+ */\r
+ TableScanResultSet scan = (TableScanResultSet) activation.getForUpdateIndexScan();\r
+ if (scan != null)\r
+ {\r
+ if (target instanceof IndexRowToBaseRowResultSet)\r
+ scan.compareToLastKey = ((IndexRowToBaseRowResultSet) target).currentRowPrescanned;\r
+ else if (target instanceof TableScanResultSet)\r
+ scan.compareToLastKey = ((TableScanResultSet) target).currentRowPrescanned;\r
+ }\r
+\r
+ // REMIND: verify the row is still there\r
+ // at present we get an ugly exception from the store,\r
+ // Hopefully someday we can just do this:\r
+ //\r
+ // if (!rowLocation.rowExists())\r
+ // throw StandardException.newException(SQLState.LANG_NO_CURRENT_ROW, cursorName);\r
+ }\r
+ else {\r
+ currentRow = null;\r
+ rowLocation = null;\r
+ }\r
+ }\r
+ else {\r
+ currentRow = null;\r
+ rowLocation = null;\r
+ }\r
+ setCurrentRow(currentRow);\r
+ return currentRow;\r
+ }\r
+\r
+ /**\r
+ * Return a sparse heap row, based on a compact index row.\r
+ *\r
+ * @param row compact referenced index row\r
+ * @param indexCols base column positions of index keys, signed with asc/desc info\r
+ *\r
+ * @return a sparse heap row with referenced columns\r
+ */\r
+ private ExecRow getSparseRow(ExecRow row, int[] indexCols) throws StandardException\r
+ {\r
+ int colPos;\r
+ if (sparseRow == null)\r
+ {\r
+ int numCols = 1;\r
+ for (int i = 0; i < indexCols.length; i++)\r
+ {\r
+ colPos = (indexCols[i] > 0) ? indexCols[i] : -indexCols[i];\r
+ if (colPos > numCols)\r
+ numCols = colPos;\r
+ }\r
+ sparseRow = new ValueRow(numCols);\r
+ }\r
+ for (int i = 1; i <= indexCols.length; i++)\r
+ {\r
+ colPos = (indexCols[i-1] > 0) ? indexCols[i-1] : -indexCols[i-1];\r
+ sparseRow.setColumn(colPos, row.getColumn(i));\r
+ }\r
+\r
+ return sparseRow;\r
+ }\r
+\r
+ /**\r
+ * If the result set has been opened,\r
+ * close the open scan.\r
+ *\r
+ * @exception StandardException thrown on error\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ if ( isOpen ) {\r
+ // we don't want to keep around a pointer to the\r
+ // row ... so it can be thrown away.\r
+ // REVISIT: does this need to be in a finally\r
+ // block, to ensure that it is executed?\r
+ clearCurrentRow();\r
+ next = false;\r
+\r
+ super.close();\r
+ }\r
+ else\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.DEBUG("CloseRepeatInfo","Close of CurrentOfResultSet repeated");\r
+ }\r
+ public void finish() throws StandardException\r
+ {\r
+ finishAndRTS();\r
+ }\r
+\r
+ /**\r
+ * Return the total amount of time spent in this ResultSet\r
+ *\r
+ * @param type CURRENT_RESULTSET_ONLY - time spent only in this ResultSet\r
+ * ENTIRE_RESULTSET_TREE - time spent in this ResultSet and below.\r
+ *\r
+ * @return long The total amount of time spent (in milliseconds).\r
+ */\r
+ public long getTimeSpent(int type)\r
+ {\r
+ /* RESOLVE - RunTimeStats not implemented yet */\r
+ return 0;\r
+ }\r
+\r
+ /**\r
+ * This result set has its row location from\r
+ * the last fetch done. If it is closed,\r
+ * a null is returned.\r
+ *\r
+ * @see CursorResultSet\r
+ *\r
+ * @return the row location of the current row.\r
+ * @exception StandardException thrown on failure to get row location\r
+ */\r
+ public RowLocation getRowLocation() {\r
+ return rowLocation;\r
+ }\r
+\r
+ /**\r
+ * @see CursorResultSet\r
+ *\r
+ * @return the last row returned by getNextRow.\r
+ */\r
+ public ExecRow getCurrentRow() {\r
+ return currentRow;\r
+ }\r
+\r
+ //\r
+ // class implementation\r
+ //\r
+ /**\r
+ Because the positioned operation only gets one location\r
+ per execution, and the cursor could be completely different\r
+ for each execution (closed and reopened, perhaps), we \r
+ determine where caching the cursor could be applied.\r
+ <p>\r
+ When cached, we check if the cursor was closed'd, \r
+ and if so, throw it out and \r
+ see if there's one in the cache with our name. \r
+\r
+ */\r
+ private void getCursor() throws StandardException {\r
+\r
+ // need to look again if cursor was closed\r
+ if (cursor != null) {\r
+ if (cursor.isClosed())\r
+ {\r
+ cursor = null;\r
+ target = null;\r
+ }\r
+ }\r
+\r
+ if (cursor == null) {\r
+\r
+ LanguageConnectionContext lcc = getLanguageConnectionContext();\r
+\r
+ CursorActivation cursorActivation = lcc.lookupCursorActivation(cursorName);\r
+\r
+ if (cursorActivation != null)\r
+ {\r
+ \r
+ cursor = cursorActivation.getCursorResultSet();\r
+ target = cursorActivation.getTargetResultSet();\r
+ /* beetle 3865: updateable cursor using index. 2 way communication between\r
+ * update activation and cursor activation. Cursor passes index scan to\r
+ * update and update passes heap conglom controller to cursor.\r
+ */\r
+ activation.setForUpdateIndexScan(cursorActivation.getForUpdateIndexScan());\r
+ if (cursorActivation.getHeapConglomerateController() != null)\r
+ cursorActivation.getHeapConglomerateController().close();\r
+ cursorActivation.setHeapConglomerateController(activation.getHeapConglomerateController()); \r
+ }\r
+ }\r
+\r
+ if (cursor == null || cursor.isClosed()) {\r
+ throw StandardException.newException(SQLState.LANG_CURSOR_NOT_FOUND, cursorName); \r
+ }\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#updateRow\r
+ */\r
+ public void updateRow (ExecRow row) throws StandardException {\r
+ ((NoPutResultSet)cursor).updateRow(row);\r
+ }\r
+ \r
+ /**\r
+ * @see NoPutResultSet#markRowAsDeleted\r
+ */\r
+ public void markRowAsDeleted() throws StandardException {\r
+ ((NoPutResultSet)cursor).markRowAsDeleted();\r
+ }\r
+ \r
+}\r