Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / sql / execute / ScrollInsensitiveResultSet.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/execute/ScrollInsensitiveResultSet.java
new file mode 100644 (file)
index 0000000..bdb6d46
--- /dev/null
@@ -0,0 +1,1205 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.ScrollInsensitiveResultSet\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.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\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
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.store.access.BackingStoreHashtable;\r
+\r
+import org.apache.derby.iapi.types.SQLBoolean;\r
+import org.apache.derby.iapi.types.SQLInteger;\r
+\r
+/**\r
+ *\r
+ * Provide insensitive scrolling functionality for the underlying\r
+ * result set.  We build a disk backed hash table of rows as the \r
+ * user scrolls forward, with the position as the key.\r
+ *\r
+ * For read-only result sets the hash table will containg the\r
+ * following columns:\r
+ *<pre>\r
+ *  +-------------------------------+\r
+ *  | KEY                           |\r
+ *  +-------------------------------+\r
+ *  | Row                           |\r
+ *  +-------------------------------+\r
+ *</pre>\r
+ * where key is the position of the row in the result set and row is the data.\r
+ *\r
+ * And for updatable result sets it will contain:\r
+ * <pre>\r
+ *  +-------------------------------+\r
+ *  | KEY                           | [0]\r
+ *  +-------------------------------+\r
+ *  | RowLocation                   | [POS_ROWLOCATION]\r
+ *  +-------------------------------+\r
+ *  | Deleted                       | [POS_ROWDELETED]\r
+ *  +-------------------------------+\r
+ *  | Updated                       | [POS_ROWUPDATED]\r
+ *  +-------------------------------+\r
+ *  | Row                           | [extraColumns ... n]\r
+ *  +-------------------------------+\r
+ *</pre>\r
+ * where key is the position of the row in the result set, rowLocation is\r
+ * the row location of that row in the Heap, Deleted indicates whether the\r
+ * row has been deleted, Updated indicates whether the row has been updated,\r
+ * and row is the data.\r
+ *\r
+ */\r
+\r
+public class ScrollInsensitiveResultSet extends NoPutResultSetImpl\r
+       implements CursorResultSet\r
+{\r
+       /*\r
+    ** Set in constructor and not altered during life of object.\r
+       */\r
+\r
+    public NoPutResultSet      source;\r
+\r
+\r
+\r
+       private int                                                     sourceRowWidth;\r
+\r
+       private   BackingStoreHashtable         ht;\r
+       private   ExecRow                                       resultRow;\r
+\r
+       // Scroll tracking\r
+       private int positionInSource;\r
+       private int currentPosition;\r
+       private int lastPosition;\r
+       private boolean seenFirst;\r
+       private boolean seenLast;\r
+       private boolean beforeFirst = true;\r
+       private boolean afterLast;\r
+\r
+       public int numFromHashTable;\r
+       public int numToHashTable;\r
+\r
+       private int maxRows;\r
+\r
+    private boolean keepAfterCommit;\r
+\r
+       /* The hash table will contain a different number of extra columns depending\r
+        * on whether the result set is updatable or not.\r
+        * extraColumns will contain the number of extra columns on the hash table,\r
+        * 1 for read-only result sets and LAST_EXTRA_COLUMN + 1 for updatable \r
+        * result sets.\r
+        */\r
+       private int extraColumns;\r
+       \r
+       /* positionInHashTable is used for getting a row from the hash table. Prior\r
+        * to getting the row, positionInHashTable will be set to the desired KEY.\r
+        */\r
+       private SQLInteger positionInHashTable;\r
+\r
+       /* Reference to the target result set. Target is used for updatable result\r
+        * sets in order to keep the target result set on the same row as the\r
+        * ScrollInsensitiveResultSet.  \r
+        */\r
+       private CursorResultSet target;\r
+\r
+       /* If the last row was fetched from the HashTable, updatable result sets\r
+        * need to be positioned in the last fetched row before resuming the \r
+        * fetch from core.\r
+        */\r
+       private boolean needsRepositioning;\r
+\r
+       /* Position of the different fields in the hash table row for updatable\r
+        * result sets \r
+        */\r
+       private static final int POS_ROWLOCATION = 1;\r
+       private static final int POS_ROWDELETED = 2;\r
+       private static final int POS_ROWUPDATED = 3;\r
+       private static final int LAST_EXTRA_COLUMN = 3;\r
+\r
+       /**\r
+        * Constructor for a ScrollInsensitiveResultSet\r
+        *\r
+        * @param source                                        The NoPutResultSet from which to get rows\r
+        *                                                                      to scroll through\r
+        * @param activation                            The activation for this execution\r
+        * @param resultSetNumber                       The resultSetNumber\r
+        * @param sourceRowWidth                        # of columns in the source row\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+\r
+       public ScrollInsensitiveResultSet(NoPutResultSet source,\r
+                                                         Activation activation, int resultSetNumber,\r
+                                                         int sourceRowWidth,\r
+                                                         double optimizerEstimatedRowCount,\r
+                                                         double optimizerEstimatedCost) throws StandardException\r
+       {\r
+               super(activation, resultSetNumber, \r
+                         optimizerEstimatedRowCount, optimizerEstimatedCost);\r
+               this.source = source;\r
+               this.sourceRowWidth = sourceRowWidth;\r
+        keepAfterCommit = activation.getResultSetHoldability();\r
+               maxRows = activation.getMaxRows();\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(maxRows != -1,\r
+                               "maxRows not expected to be -1");\r
+               }\r
+\r
+               constructorTime += getElapsedMillis(beginTime);\r
+\r
+               positionInHashTable = new SQLInteger();\r
+               needsRepositioning = false;\r
+               if (isForUpdate()) {\r
+                       target = ((CursorActivation)activation).getTargetResultSet();\r
+                       extraColumns = LAST_EXTRA_COLUMN + 1;\r
+               } else {\r
+                       target = null;\r
+                       extraColumns = 1;\r
+               }\r
+       }\r
+\r
+\r
+       //\r
+       // ResultSet interface (leftover from NoPutResultSet)\r
+       //\r
+\r
+       /**\r
+     * open a scan on the source. 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 \r
+     */\r
+       public void     openCore() throws StandardException\r
+       {\r
+               beginTime = getCurrentTimeMillis();\r
+               if (SanityManager.DEBUG)\r
+               SanityManager.ASSERT( ! isOpen, "ScrollInsensitiveResultSet already open");\r
+\r
+        source.openCore();\r
+           isOpen = true;\r
+               numOpens++;\r
+\r
+               /* Create the hash table.  We pass\r
+                * null in as the row source as we will\r
+                * build the hash table on demand as\r
+                * the user scrolls.\r
+                * The 1st column, the position in the\r
+                * scan, will be the key column.\r
+                */\r
+               final int[] keyCols = new int[] { 0 };\r
+               \r
+               /* We don't use the optimizer row count for this because it could be\r
+                * wildly pessimistic.  We only use Hash tables when the optimizer row count\r
+                * is within certain bounds.  We have no alternative for scrolling insensitive \r
+                * cursors so we'll just trust that it will fit.\r
+                * We need BackingStoreHashtable to actually go to disk when it doesn't fit.\r
+                * This is a known limitation.\r
+                */\r
+               ht = new BackingStoreHashtable(getTransactionController(),\r
+                                                                          null,\r
+                                                                          keyCols,\r
+                                                                          false,\r
+                                                                               -1, // don't trust optimizer row count\r
+                                                                          HashScanResultSet.DEFAULT_MAX_CAPACITY,\r
+                                                                          HashScanResultSet.DEFAULT_INITIAL_CAPACITY,\r
+                                                                          HashScanResultSet.DEFAULT_MAX_CAPACITY,\r
+                                                                          false,\r
+                                       keepAfterCommit);\r
+\r
+               // When re-using language result sets (DERBY-827) we need to\r
+               // reset some member variables to the value they would have\r
+               // had in a newly constructed object.\r
+               lastPosition = 0;\r
+               needsRepositioning = false;\r
+               numFromHashTable = 0;\r
+               numToHashTable = 0;\r
+               positionInSource = 0;\r
+               seenFirst = false;\r
+               seenLast = false;\r
+               maxRows = activation.getMaxRows();\r
+\r
+               openTime += getElapsedMillis(beginTime);\r
+               setBeforeFirstRow();\r
+       }\r
+\r
+       /**\r
+     * reopen 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 if cursor finished.\r
+     */\r
+       public void     reopenCore() throws StandardException \r
+       {\r
+               boolean constantEval = true;\r
+\r
+               beginTime = getCurrentTimeMillis();\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                   SanityManager.ASSERT(isOpen, "ScrollInsensitiveResultSet already open");\r
+                       SanityManager.THROWASSERT(\r
+                               "reopenCore() not expected to be called");\r
+               }\r
+               setBeforeFirstRow();\r
+       }\r
+\r
+       /**\r
+        * Returns the row at the absolute position from the query, \r
+        * and returns NULL when there is no such position.\r
+        * (Negative position means from the end of the result set.)\r
+        * Moving the cursor to an invalid position leaves the cursor\r
+        * positioned either before the first row (negative position)\r
+        * or after the last row (positive position).\r
+        * NOTE: An exception will be thrown on 0.\r
+        *\r
+        * @param row   The position.\r
+        * @return      The row at the absolute position, or NULL if no such position.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  getAbsoluteRow(int row) throws StandardException\r
+       {\r
+           if ( ! isOpen ) \r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "absolute");\r
+               }\r
+\r
+               attachStatementContext();\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (!isTopResultSet)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       this + "expected to be the top ResultSet");\r
+                       }\r
+               }\r
+\r
+                // Absolute 0 is defined to be before first!\r
+               if (row == 0)\r
+               {\r
+                    setBeforeFirstRow();\r
+                    return null;\r
+               }\r
+\r
+               if (seenLast && row > lastPosition) {\r
+                  return setAfterLastRow();\r
+               }               \r
+\r
+               if (row > 0)\r
+               {\r
+                       // position is from the start of the result set\r
+                       if (row <= positionInSource)\r
+                       {\r
+                               // We've already seen the row before\r
+                               return getRowFromHashTable(row);\r
+                       }\r
+                       \r
+                       /* We haven't seen the row yet, scan until we find\r
+                        * it or we get to the end.\r
+                        */\r
+                       int diff = row - positionInSource;\r
+                       ExecRow result = null;\r
+                       while (diff > 0)\r
+                       {\r
+                               if ((result = getNextRowFromSource()) != null)\r
+                               {\r
+                                       diff--;\r
+                               }\r
+                               else\r
+                               {\r
+                                       break;\r
+                               }\r
+                       }\r
+                       if (result != null) {\r
+                               result = getRowFromHashTable(row);\r
+                       }\r
+                       currentRow = result;\r
+                       return result;\r
+               }\r
+               else if (row < 0)\r
+               {\r
+                       // position is from the end of the result set\r
+\r
+                       // Get the last row, if we haven't already\r
+                       if (!seenLast)\r
+                       {\r
+                               getLastRow();\r
+                       }\r
+\r
+                       // Note, for negative values position is from beyond the end\r
+                       // of the result set, e.g. absolute(-1) points to the last row\r
+                       int beyondResult = lastPosition + 1;\r
+                       if (beyondResult + row > 0)\r
+                       {\r
+                               // valid row\r
+                               return getRowFromHashTable(beyondResult + row);\r
+                       }\r
+                       else\r
+                       {\r
+                               // position before the beginning of the result set\r
+                               return setBeforeFirstRow();\r
+                       }\r
+               }\r
\r
+               currentRow = null;\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Returns the row at the relative position from the current\r
+        * cursor position, and returns NULL when there is no such position.\r
+        * (Negative position means toward the beginning of the result set.)\r
+        * Moving the cursor to an invalid position leaves the cursor\r
+        * positioned either before the first row (negative position)\r
+        * or after the last row (positive position).\r
+        * NOTE: 0 is valid.\r
+        * NOTE: An exception is thrown if the cursor is not currently\r
+        * positioned on a row.\r
+        *\r
+        * @param row   The position.\r
+        * @return      The row at the relative position, or NULL if no such position.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  getRelativeRow(int row) throws StandardException\r
+       {\r
+           if ( ! isOpen ) \r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "relative");\r
+               }\r
+\r
+               attachStatementContext();\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (!isTopResultSet)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       this + "expected to be the top ResultSet");\r
+                       }\r
+               }\r
+\r
+               // Return the current row for 0\r
+               if (row == 0)\r
+               {\r
+                    if (beforeFirst || afterLast || currentPosition==0) {\r
+                        return null;\r
+                    } else {\r
+                       return getRowFromHashTable(currentPosition);\r
+                    }\r
+               }\r
+               else if (row > 0)\r
+               {\r
+                       return getAbsoluteRow(currentPosition + row);\r
+               }\r
+               else\r
+               {\r
+                       // row < 0\r
+                       if (currentPosition + row < 0)\r
+                       {\r
+                               return setBeforeFirstRow();\r
+                       }\r
+                       return getAbsoluteRow(currentPosition + row);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Sets the current position to before the first row and returns NULL\r
+        * because there is no current row.\r
+        *\r
+        * @return      NULL.\r
+        *\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  setBeforeFirstRow() \r
+       {\r
+               currentPosition = 0;\r
+               beforeFirst = true;\r
+               afterLast = false;\r
+               currentRow = null;\r
+               return null;\r
+       }\r
+\r
+       /**\r
+        * Returns the first row from the query, and returns NULL when there\r
+        * are no rows.\r
+        *\r
+        * @return      The first row, or NULL if no rows.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  getFirstRow() \r
+               throws StandardException\r
+       {\r
+           if ( ! isOpen ) \r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "first");\r
+               }\r
+\r
+               /* Get the row from the hash table if\r
+                * we have already seen it before.\r
+                */\r
+               if (seenFirst)\r
+               {\r
+                       return getRowFromHashTable(1);\r
+               }\r
+\r
+               attachStatementContext();\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (!isTopResultSet)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       this + "expected to be the top ResultSet");\r
+                       }\r
+               }\r
+\r
+               return getNextRowCore();\r
+       }\r
+\r
+       /**\r
+        *\r
+        * @exception StandardException thrown on failure \r
+        */\r
+       public ExecRow  getNextRowCore() throws StandardException\r
+       {\r
+               ExecRow result = null;\r
+\r
+               beginTime = getCurrentTimeMillis();\r
+               if (!isOpen)\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");\r
+\r
+               if (seenLast && currentPosition == lastPosition) {\r
+                  return setAfterLastRow();\r
+               }\r
+\r
+               /* Should we get the next row from the source or the hash table? */\r
+               if (currentPosition == positionInSource)\r
+               {\r
+                       /* Current position is same as position in source.\r
+                        * Get row from the source.\r
+                        */\r
+                       result = getNextRowFromSource();\r
+                       if (result !=null) {\r
+                               result = getRowFromHashTable(currentPosition);\r
+                       }\r
+               }\r
+               else if (currentPosition < positionInSource)\r
+               {\r
+                       /* Current position is before position in source.\r
+                        * Get row from the hash table.\r
+                        */\r
+                       result = getRowFromHashTable(currentPosition + 1);\r
+               }\r
+               else\r
+               {\r
+                       result = null;\r
+               }\r
+\r
+               if (result != null)\r
+               {\r
+                       rowsSeen++;\r
+                       afterLast = false;\r
+               }\r
+\r
+               currentRow = result;\r
+               setCurrentRow(currentRow);\r
+               beforeFirst = false;\r
+\r
+               nextTime += getElapsedMillis(beginTime);\r
+\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Returns the previous row from the query, and returns NULL when there\r
+        * are no more previous rows.\r
+        *\r
+        * @return      The previous row, or NULL if no more previous rows.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  getPreviousRow() \r
+               throws StandardException\r
+       {\r
+           if ( ! isOpen ) \r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");\r
+               }\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (!isTopResultSet)\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       this + "expected to be the top ResultSet");\r
+                       }\r
+               }\r
+\r
+               /* No row if we are positioned before the first row\r
+                * or the result set is empty.\r
+                */\r
+               if (beforeFirst || currentPosition == 0)\r
+               {\r
+                       currentRow = null;\r
+                       return null;\r
+               }\r
+\r
+               // Get the last row, if we are after it\r
+               if (afterLast)\r
+               {\r
+                       // Special case for empty tables\r
+                       if (lastPosition == 0)\r
+                       {\r
+                               afterLast = false;\r
+                               beforeFirst = false;\r
+                               currentRow = null;\r
+                               return null;\r
+                       }\r
+                       else\r
+                       {\r
+                               return getRowFromHashTable(lastPosition);\r
+                       }\r
+               }\r
+\r
+               // Move back 1\r
+               currentPosition--;\r
+               if (currentPosition == 0)\r
+               {\r
+                       setBeforeFirstRow();\r
+                       return null;\r
+               }\r
+               return getRowFromHashTable(currentPosition);\r
+       }\r
+\r
+       /**\r
+        * Returns the last row from the query, and returns NULL when there\r
+        * are no rows.\r
+        *\r
+        * @return      The last row, or NULL if no rows.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  getLastRow()\r
+               throws StandardException\r
+       {               \r
+           if ( ! isOpen ) \r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_RESULT_SET_NOT_OPEN, "next");\r
+               }\r
+               \r
+               if (!seenLast) \r
+               {\r
+                       attachStatementContext();\r
+\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (!isTopResultSet)\r
+                               {\r
+                                       SanityManager.THROWASSERT(\r
+                                                                                         this + "expected to be the top ResultSet");\r
+                               }\r
+                       }\r
+                       \r
+                       /* Scroll to the end, filling the hash table as\r
+                        * we scroll, and return the last row that we find.\r
+                        */\r
+                       ExecRow result = null;\r
+                       while ((result = getNextRowFromSource()) != null);\r
+               }\r
+               \r
+               if (SanityManager.DEBUG && !seenLast)\r
+               {\r
+                       SanityManager.THROWASSERT(this + "expected to have seen last");\r
+               }\r
+               \r
+               beforeFirst = false;\r
+               afterLast = false;\r
+\r
+               // Special case if table is empty\r
+               if (lastPosition == 0)\r
+               {\r
+                       currentRow = null;\r
+                       return null;\r
+               }\r
+               else\r
+               {\r
+                       return getRowFromHashTable(lastPosition);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Sets the current position to after the last row and returns NULL\r
+        * because there is no current row.\r
+        *\r
+        * @return      NULL.\r
+        *\r
+        * @exception StandardException         Thrown on failure\r
+        * @see org.apache.derby.iapi.sql.Row\r
+        */\r
+       public ExecRow  setAfterLastRow() \r
+               throws StandardException\r
+       {\r
+               if (! seenLast)\r
+               {\r
+                       getLastRow();\r
+               }\r
+               if (lastPosition == 0) {\r
+                  // empty rs special case\r
+                  currentPosition = 0;\r
+                  afterLast = false;\r
+               } else {\r
+                  currentPosition = lastPosition + 1;\r
+                  afterLast = true;\r
+               }\r
+\r
+               beforeFirst = false;\r
+               currentRow = null;\r
+               return null;\r
+       }\r
+\r
+    /**\r
+     * Determine if the cursor is before the first row in the result \r
+     * set.   \r
+     *\r
+     * @return true if before the first row, false otherwise. Returns\r
+     * false when the result set contains no rows.\r
+        * @exception StandardException Thrown on error.\r
+     */\r
+   public boolean checkRowPosition(int isType) throws StandardException\r
+       {\r
+               switch (isType) {\r
+               case ISBEFOREFIRST:\r
+\r
+                       if (! beforeFirst)\r
+                       {\r
+                               return false;\r
+                       }\r
+\r
+                       //  Spec says to return false if result set is empty\r
+                       if (seenFirst)\r
+                       {\r
+                               return true;\r
+                       }\r
+                       else\r
+                       {\r
+                               ExecRow firstRow = getFirstRow();\r
+                               if (firstRow == null)\r
+                               {\r
+                                       // ResultSet is empty\r
+                                       return false;\r
+                               }\r
+                               else\r
+                               {\r
+                                       // ResultSet is not empty - reset position\r
+                                       getPreviousRow();\r
+                                       return true;\r
+                               }\r
+                       }\r
+               case ISFIRST:\r
+                       return (currentPosition == 1);\r
+               case ISLAST:\r
+                       if (beforeFirst || afterLast || currentPosition==0 ||\r
+                               currentPosition<positionInSource)\r
+                       {\r
+                               return false;\r
+                       }                       \r
+                       \r
+                       /* If we have seen the last row, we can tell if we are \r
+                        * on it by comparing currentPosition with lastPosition.\r
+                        * Otherwise, we check if there is a next row.\r
+                        */\r
+                       if (seenLast)\r
+                       {\r
+                               return (currentPosition == lastPosition);\r
+                       }\r
+                       else\r
+                       {\r
+                               final int savePosition = currentPosition;\r
+                               final boolean retval = (getNextRowFromSource() == null);\r
+                               getRowFromHashTable(savePosition);\r
+                               return retval;\r
+                       }\r
+               case ISAFTERLAST:\r
+                       return afterLast;\r
+               default:\r
+                       return false;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Returns the row number of the current row.  Row\r
+        * numbers start from 1 and go to 'n'.  Corresponds\r
+        * to row numbering used to position current row\r
+        * in the result set (as per JDBC).\r
+        *\r
+        * @return      the row number, or 0 if not on a row\r
+        *\r
+        */\r
+       public int getRowNumber()\r
+       {\r
+               return currentRow == null ? 0 : currentPosition;\r
+       }\r
+\r
+       /* Get the next row from the source ResultSet tree and insert into the hash table */\r
+       private ExecRow getNextRowFromSource() throws StandardException\r
+       {\r
+               ExecRow         sourceRow = null;\r
+               ExecRow         result = null;\r
+\r
+               /* Don't give back more rows than requested */\r
+               if (maxRows > 0 && maxRows == positionInSource)\r
+               {\r
+                       seenLast = true;\r
+                       lastPosition = positionInSource;\r
+                       afterLast = true;\r
+                       return null;\r
+               }\r
+\r
+\r
+               if (needsRepositioning) {\r
+                       positionInLastFetchedRow();\r
+                       needsRepositioning = false;\r
+               }\r
+               sourceRow = source.getNextRowCore();\r
+\r
+               if (sourceRow != null)\r
+               {\r
+                       seenFirst = true;\r
+                       beforeFirst = false;\r
+\r
+                       long beginTCTime = getCurrentTimeMillis();\r
+                       /* If this is the first row from the source then we create a new row\r
+                        * for use when fetching from the hash table.\r
+                        */\r
+                       if (resultRow == null)\r
+                       {\r
+                               resultRow = activation.getExecutionFactory().getValueRow(sourceRowWidth);\r
+                       }\r
+\r
+                       positionInSource++;\r
+                       currentPosition = positionInSource;\r
+\r
+                       RowLocation rowLoc = null;\r
+                       if (source.isForUpdate()) {\r
+                               rowLoc = ((CursorResultSet)source).getRowLocation();\r
+                       }\r
+\r
+                       addRowToHashTable(sourceRow, currentPosition, rowLoc, false);\r
+\r
+               }\r
+               // Remember whether or not we're past the end of the table\r
+               else\r
+               {\r
+                       if (! seenLast)\r
+                       {\r
+                               lastPosition = positionInSource;\r
+                       }\r
+                       seenLast = true;\r
+                       // Special case for empty table (afterLast is never true)\r
+                       if (positionInSource == 0)\r
+                       {\r
+                               afterLast = false;\r
+                       }\r
+                       else\r
+                       {\r
+                               afterLast = true;\r
+                               currentPosition = positionInSource + 1;\r
+                       }\r
+               }\r
+\r
+               return sourceRow;\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
+               beginTime = getCurrentTimeMillis();\r
+           if ( isOpen )\r
+           {\r
+                       currentRow = null;\r
+               source.close();\r
+\r
+                       if (ht != null)\r
+                       {\r
+                               ht.close();\r
+                               ht = null;\r
+                       }\r
+\r
+                       super.close();\r
+           }\r
+               else\r
+                       if (SanityManager.DEBUG)\r
+                               SanityManager.DEBUG("CloseRepeatInfo","Close of ScrollInsensitiveResultSet repeated");\r
+               setBeforeFirstRow();\r
+\r
+               closeTime += getElapsedMillis(beginTime);\r
+       }\r
+\r
+       public void     finish() throws StandardException\r
+       {\r
+               source.finish();\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
+               long totTime = constructorTime + openTime + nextTime + closeTime;\r
+\r
+               if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)\r
+               {\r
+                       return  totTime - source.getTimeSpent(ENTIRE_RESULTSET_TREE);\r
+               }\r
+               else\r
+               {\r
+                       return totTime;\r
+               }\r
+       }\r
+\r
+       //\r
+       // CursorResultSet interface\r
+       //\r
+\r
+       /**\r
+        * Gets information from its source. We might want\r
+        * to have this take a CursorResultSet in its constructor some day,\r
+        * instead of doing a cast here?\r
+        *\r
+        * @see CursorResultSet\r
+        *\r
+        * @return the row location of the current cursor row.\r
+        *\r
+        * @exception StandardException thrown on failure \r
+        */\r
+       public RowLocation getRowLocation() throws StandardException \r
+       {\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(source instanceof CursorResultSet, "source not CursorResultSet");\r
+               return ( (CursorResultSet)source ).getRowLocation();\r
+       }\r
+\r
+       /**\r
+        * Gets information from last getNextRow call.\r
+        *\r
+        * @see CursorResultSet\r
+        *\r
+        * @return the last row returned.\r
+        */\r
+       /* RESOLVE - this should return activation.getCurrentRow(resultSetNumber),\r
+        * once there is such a method.  (currentRow is redundant)\r
+        */\r
+       public ExecRow getCurrentRow() throws StandardException\r
+       {\r
+               if (isForUpdate() && isDeleted()) {\r
+                       return null;\r
+               } else {\r
+                       return currentRow;\r
+               }\r
+       }\r
+\r
+       //\r
+       // class implementation\r
+       //\r
+\r
+       /**\r
+        * Add a row to the backing hash table, keyed on position.\r
+        * When a row gets updated when using scrollable insensitive updatable\r
+        * result sets, the old entry for the row will be deleted from the hash \r
+        * table and this method will be called to add the new values for the row\r
+        * to the hash table, with the parameter rowUpdated = true so as to mark \r
+        * the row as updated. The latter is done in order to implement \r
+        * detectability of own changes for result sets of this type.\r
+        *\r
+        * @param sourceRow     The row to add.\r
+        * @param position The key\r
+        * @param rowLoc The rowLocation of the row to add.\r
+        * @param rowUpdated Indicates whether the row has been updated.\r
+        *\r
+        */\r
+       private void addRowToHashTable(ExecRow sourceRow, int position,\r
+                       RowLocation rowLoc, boolean rowUpdated)\r
+               throws StandardException\r
+       {\r
+               DataValueDescriptor[] hashRowArray = new \r
+                               DataValueDescriptor[sourceRowWidth + extraColumns];\r
+               // 1st element is the key\r
+               hashRowArray[0] = new SQLInteger(position);\r
+               if (isForUpdate()) {\r
+                       hashRowArray[POS_ROWLOCATION] = rowLoc.getClone();\r
+                       hashRowArray[POS_ROWDELETED] = new SQLBoolean(false);\r
+                       hashRowArray[POS_ROWUPDATED] = new SQLBoolean(rowUpdated);\r
+               }\r
+\r
+               /* Copy rest of elements from sourceRow.\r
+                * NOTE: We need to clone the source row\r
+                * and we do our own cloning since the 1st column\r
+                * is not a wrapper.\r
+                */\r
+               DataValueDescriptor[] sourceRowArray = sourceRow.getRowArray();\r
+\r
+               System.arraycopy(sourceRowArray, 0, hashRowArray, extraColumns, \r
+                               sourceRowArray.length);\r
+\r
+               ht.putRow(true, hashRowArray);\r
+\r
+               numToHashTable++;\r
+       }\r
+\r
+       /**\r
+        * Get the row at the specified position\r
+        * from the hash table.\r
+        *\r
+        * @param position      The specified position.\r
+        *\r
+        * @return      The row at that position.\r
+        *\r
+        * @exception StandardException thrown on failure \r
+        */\r
+       private ExecRow getRowFromHashTable(int position)\r
+               throws StandardException\r
+       {\r
+\r
+               // Get the row from the hash table\r
+               positionInHashTable.setValue(position);\r
+               DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                               ht.get(positionInHashTable);\r
+\r
+\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(hashRowArray != null,\r
+                               "hashRowArray expected to be non-null");\r
+               }\r
+               // Copy out the Object[] without the position.\r
+               DataValueDescriptor[] resultRowArray = new \r
+                               DataValueDescriptor[hashRowArray.length - extraColumns];\r
+               System.arraycopy(hashRowArray, extraColumns, resultRowArray, 0, \r
+                               resultRowArray.length);\r
+\r
+               resultRow.setRowArray(resultRowArray);\r
+\r
+               // Reset the current position to the user position\r
+               currentPosition = position;\r
+\r
+               numFromHashTable++;\r
+\r
+               if (resultRow != null)\r
+               {\r
+                       beforeFirst = false;\r
+                       afterLast = false;\r
+               }\r
+\r
+               if (isForUpdate()) {\r
+                       RowLocation rowLoc = (RowLocation) hashRowArray[POS_ROWLOCATION];\r
+                       // Keep source and target with the same currentRow\r
+                       ((NoPutResultSet)target).setCurrentRow(resultRow);\r
+                       ((NoPutResultSet)target).positionScanAtRowLocation(rowLoc);\r
+                       needsRepositioning = true;\r
+               }\r
+               \r
+               setCurrentRow(resultRow);\r
+\r
+               return resultRow;\r
+       }\r
+       \r
+       /**\r
+        * Get the row data at the specified position \r
+        * from the hash table.\r
+        *\r
+        * @param position      The specified position.\r
+        *\r
+        * @return      The row data at that position.\r
+        *\r
+        * @exception StandardException thrown on failure \r
+        */\r
+       private DataValueDescriptor[] getRowArrayFromHashTable(int position)\r
+               throws StandardException\r
+       {\r
+               positionInHashTable.setValue(position);\r
+               final DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                       ht.get(positionInHashTable);\r
+               \r
+               // Copy out the Object[] without the position.\r
+               final DataValueDescriptor[] resultRowArray = new \r
+                       DataValueDescriptor[hashRowArray.length - extraColumns];\r
+               System.arraycopy(hashRowArray, extraColumns, resultRowArray, 0, \r
+                                                resultRowArray.length);\r
+               return resultRowArray;\r
+       }\r
+\r
+       /**\r
+        * Positions the cursor in the last fetched row. This is done before\r
+        * navigating to a row that has not previously been fetched, so that\r
+        * getNextRowCore() will re-start from where it stopped.\r
+        */\r
+       private void positionInLastFetchedRow() throws StandardException {\r
+               if (positionInSource > 0) {\r
+                       positionInHashTable.setValue(positionInSource);\r
+                       DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                                       ht.get(positionInHashTable);\r
+                       RowLocation rowLoc = (RowLocation) hashRowArray[POS_ROWLOCATION];\r
+                       ((NoPutResultSet)target).positionScanAtRowLocation(rowLoc);\r
+                       currentPosition = positionInSource;\r
+               }\r
+       }\r
+\r
+       /**\r
+        * @see NoPutResultSet#updateRow\r
+        *\r
+        * Sets the updated column of the hash table to true and updates the row\r
+        * in the hash table with the new values for the row.\r
+        */\r
+       public void updateRow(ExecRow row) throws StandardException {\r
+               ExecRow newRow = row;\r
+               boolean undoProjection = false;\r
+               \r
+               if (source instanceof ProjectRestrictResultSet) {\r
+                       newRow = ((ProjectRestrictResultSet)source).\r
+                               doBaseRowProjection(row);\r
+                       undoProjection = true;\r
+               }\r
+               positionInHashTable.setValue(currentPosition);\r
+               DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                               ht.get(positionInHashTable);\r
+               RowLocation rowLoc = (RowLocation) hashRowArray[POS_ROWLOCATION];\r
+               ht.remove(new SQLInteger(currentPosition));\r
+               addRowToHashTable(newRow, currentPosition, rowLoc, true);\r
+               \r
+               // Modify row to refer to data in the BackingStoreHashtable.\r
+               // This allows reading of data which goes over multiple pages\r
+               // when doing the actual update (LOBs). Putting columns of\r
+               // type SQLBinary to disk, has destructive effect on the columns,\r
+               // and they need to be re-read. That is the reason this is needed.\r
+               if (undoProjection) {\r
+                       \r
+                       final DataValueDescriptor[] newRowData = newRow.getRowArray();\r
+                       \r
+                       // Array of original position in row\r
+                       final int[] origPos =((ProjectRestrictResultSet)source).\r
+                               getBaseProjectMapping(); \r
+                       \r
+                       // We want the row to contain data backed in BackingStoreHashtable\r
+                       final DataValueDescriptor[] backedData = \r
+                               getRowArrayFromHashTable(currentPosition);\r
+                       \r
+                       for (int i=0; i<origPos.length; i++) {\r
+                               if (origPos[i]>=0) {\r
+                                       row.setColumn(origPos[i], backedData[i]);\r
+                               }\r
+                       }\r
+               } else {\r
+                       row.setRowArray(getRowArrayFromHashTable(currentPosition));\r
+               }\r
+       }\r
+\r
+       /**\r
+        * @see NoPutResultSet#markRowAsDeleted\r
+        *\r
+        * Sets the deleted column of the hash table to true in the current row.\r
+        */\r
+       public void markRowAsDeleted() throws StandardException  {\r
+               positionInHashTable.setValue(currentPosition);\r
+               DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                               ht.get(positionInHashTable);\r
+               RowLocation rowLoc = (RowLocation) hashRowArray[POS_ROWLOCATION];\r
+               ht.remove(new SQLInteger(currentPosition));\r
+               ((SQLBoolean)hashRowArray[POS_ROWDELETED]).setValue(true);\r
+               // Set all columns to NULL, the row is now a placeholder\r
+               for (int i=extraColumns; i<hashRowArray.length; i++) {\r
+                       hashRowArray[i].setToNull();\r
+               }\r
+\r
+               ht.putRow(true, hashRowArray);\r
+       }\r
+\r
+       /**\r
+        * Returns TRUE if the row was been deleted within the transaction,\r
+        * otherwise returns FALSE\r
+        *\r
+        * @return True if the row has been deleted, otherwise false\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public boolean isDeleted() throws StandardException  {\r
+               if (currentPosition <= positionInSource && currentPosition > 0) {\r
+                       positionInHashTable.setValue(currentPosition);\r
+                       DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                                       ht.get(positionInHashTable);\r
+                       return hashRowArray[POS_ROWDELETED].getBoolean();\r
+               }\r
+               return false;\r
+       }\r
+\r
+       /**\r
+        * Returns TRUE if the row was been updated within the transaction,\r
+        * otherwise returns FALSE\r
+        *\r
+        * @return True if the row has been deleted, otherwise false\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public boolean isUpdated() throws StandardException {\r
+               if (currentPosition <= positionInSource && currentPosition > 0) {\r
+                       positionInHashTable.setValue(currentPosition);\r
+                       DataValueDescriptor[] hashRowArray = (DataValueDescriptor[]) \r
+                                       ht.get(positionInHashTable);\r
+                       return hashRowArray[POS_ROWUPDATED].getBoolean();\r
+               }\r
+               return false;\r
+       }\r
+\r
+       public boolean isForUpdate() {\r
+               return source.isForUpdate();\r
+       }\r
+\r
+}\r