Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / execute / TableScanResultSet.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/TableScanResultSet.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/execute/TableScanResultSet.java
new file mode 100644 (file)
index 0000000..898f4c1
--- /dev/null
@@ -0,0 +1,1465 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.execute.TableScanResultSet\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 java.util.Hashtable;\r
+import java.util.Properties;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.services.i18n.MessageService;\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecIndexRow;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;\r
+import org.apache.derby.iapi.store.access.ConglomerateController;\r
+import org.apache.derby.iapi.store.access.DynamicCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+/**\r
+ * Takes a table and a table filter and returns\r
+ * the table's rows satisfying the filter as a result set.\r
+ *\r
+ * There are several things we could do during object\r
+ * construction that are done in the open & next calls, to\r
+ * improve performance.\r
+ *\r
+ */\r
+class TableScanResultSet extends ScanResultSet\r
+       implements CursorResultSet, Cloneable\r
+{\r
+    protected ScanController scanController;\r
+       protected boolean               scanControllerOpened;\r
+       protected boolean               isKeyed;\r
+       protected boolean               firstScan = true;\r
+       protected ExecIndexRow  startPosition;\r
+       protected ExecIndexRow  stopPosition;\r
+\r
+    // set in constructor and not altered during\r
+    // life of object.\r
+       protected long conglomId;\r
+    protected DynamicCompiledOpenConglomInfo dcoci;\r
+    protected StaticCompiledOpenConglomInfo scoci;\r
+       protected GeneratedMethod resultRowAllocator;\r
+       protected GeneratedMethod startKeyGetter;\r
+       protected int startSearchOperator;\r
+       protected GeneratedMethod stopKeyGetter;\r
+       protected int stopSearchOperator;\r
+       public    Qualifier[][] qualifiers;\r
+       public String tableName;\r
+       public String userSuppliedOptimizerOverrides;\r
+       public String indexName;\r
+       protected boolean runTimeStatisticsOn;\r
+       protected FormatableBitSet accessedCols;\r
+    protected int[] indexCols;         //index keys base column position array\r
+       public int rowsPerRead;\r
+       public boolean forUpdate;\r
+       private boolean sameStartStopPosition;\r
+       private boolean nextDone;\r
+       private RowLocation rlTemplate;\r
+\r
+       // Run time statistics\r
+       private Properties scanProperties;\r
+       public String startPositionString;\r
+       public String stopPositionString;\r
+       public boolean isConstraint;\r
+       public boolean coarserLock;\r
+       public boolean oneRowScan;\r
+\r
+       protected long  rowsThisScan;\r
+\r
+       private long estimatedRowCount;\r
+\r
+       /* Following fields are used by beetle 3865, updateable cursor using index. "past2FutureTbl"\r
+        * is a hash table containing updated rows that are thrown into future direction of the\r
+        * index scan and as a result we'll hit it again but should skip it.  If this hash table\r
+        * is full, we scan forward and have a virtual memory style temp heap holding future row\r
+        * id's.\r
+        */\r
+       protected Hashtable past2FutureTbl;\r
+       protected TemporaryRowHolder futureForUpdateRows;  //tmp table for materialized rids\r
+       protected TemporaryRowHolderResultSet futureRowResultSet;       //result set for reading from above\r
+       protected boolean skipFutureRowHolder;          //skip reading rows from above\r
+       protected boolean sourceDrained;                        //all row ids materialized\r
+       protected boolean currentRowPrescanned; //got a row from above tmp table\r
+       protected boolean compareToLastKey;             //see comments in UpdateResultSet\r
+       protected ExecRow lastCursorKey;\r
+       private ExecRow sparseRow;                              //sparse row in heap column order\r
+       private FormatableBitSet sparseRowMap;                  //which columns to read\r
+\r
+       // For Scrollable insensitive updatable result sets, only qualify a row the \r
+       // first time it's been read, since an update can change a row so that it \r
+       // no longer qualifies\r
+       private boolean qualify;\r
+\r
+       // currentRowIsValid is set to the result of positioning at a rowLocation.\r
+       // It will be true if the positioning was successful and false if the row \r
+       // was deleted under our feet. Whenenver currentRowIsValid is false it means \r
+       // that the row has been deleted.\r
+       private boolean currentRowIsValid;\r
+       \r
+       // Indicates whether the scan has been positioned back to a previously read\r
+       // row, or it is accessing a row for the first time.\r
+       private boolean scanRepositioned;\r
+\r
+    //\r
+    // class interface\r
+    //\r
+    TableScanResultSet(long conglomId,\r
+               StaticCompiledOpenConglomInfo scoci, \r
+               Activation activation, \r
+               GeneratedMethod resultRowAllocator, \r
+               int resultSetNumber,\r
+               GeneratedMethod startKeyGetter, int startSearchOperator,\r
+               GeneratedMethod stopKeyGetter, int stopSearchOperator,\r
+               boolean sameStartStopPosition,\r
+               Qualifier[][] qualifiers,\r
+               String tableName,\r
+               String userSuppliedOptimizerOverrides,\r
+               String indexName,\r
+               boolean isConstraint,\r
+               boolean forUpdate,\r
+               int colRefItem,\r
+               int indexColItem,\r
+               int lockMode,\r
+               boolean tableLocked,\r
+               int isolationLevel,\r
+               int rowsPerRead,\r
+               boolean oneRowScan,\r
+               double optimizerEstimatedRowCount,\r
+               double optimizerEstimatedCost)\r
+                       throws StandardException\r
+    {\r
+               super(activation,\r
+                               resultSetNumber,\r
+                               resultRowAllocator,\r
+                               lockMode, tableLocked, isolationLevel,\r
+                               optimizerEstimatedRowCount,\r
+                               optimizerEstimatedCost);\r
+\r
+               this.conglomId = conglomId;\r
+\r
+               /* Static info created at compile time and can be shared across\r
+                * instances of the plan.\r
+                * Dynamic info created on 1st opening of this ResultSet as\r
+                * it cannot be shared.\r
+                */\r
+        this.scoci = scoci;\r
+\r
+               if (SanityManager.DEBUG) {\r
+                       SanityManager.ASSERT( activation!=null, "table scan must get activation context");\r
+                       SanityManager.ASSERT( resultRowAllocator!= null, "table scan must get row allocator");\r
+                       if (sameStartStopPosition)\r
+                       {\r
+                               SanityManager.ASSERT(stopKeyGetter == null,\r
+                                       "stopKeyGetter expected to be null when sameStartStopPosition is true");\r
+                       }\r
+               }\r
+\r
+        this.resultRowAllocator = resultRowAllocator;\r
+\r
+               this.startKeyGetter = startKeyGetter;\r
+               this.startSearchOperator = startSearchOperator;\r
+               this.stopKeyGetter = stopKeyGetter;\r
+               this.stopSearchOperator = stopSearchOperator;\r
+               this.sameStartStopPosition = sameStartStopPosition;\r
+               this.qualifiers = qualifiers;\r
+               this.tableName = tableName;\r
+               this.userSuppliedOptimizerOverrides = userSuppliedOptimizerOverrides;\r
+               this.indexName = indexName;\r
+               this.isConstraint = isConstraint;\r
+               this.forUpdate = forUpdate;\r
+               this.rowsPerRead = rowsPerRead;\r
+               this.oneRowScan = oneRowScan;\r
+\r
+               // retrieve the valid column list from\r
+               // the saved objects, if it exists\r
+               this.accessedCols = null;\r
+               if (colRefItem != -1)\r
+               {\r
+                       this.accessedCols = (FormatableBitSet)(activation.getPreparedStatement().\r
+                                               getSavedObject(colRefItem));\r
+               }\r
+               if (indexColItem != -1)\r
+               {\r
+                       this.indexCols = (int[])(activation.getPreparedStatement().\r
+                                               getSavedObject(indexColItem));\r
+               }\r
+               if (indexCols != null)\r
+                       activation.setForUpdateIndexScan(this);\r
+\r
+               runTimeStatisticsOn = (activation != null &&\r
+                                                          activation.getLanguageConnectionContext().getRunTimeStatisticsMode());\r
+\r
+               constructorTime += getElapsedMillis(beginTime);\r
+               \r
+               /* Always qualify the first time a row is being read */\r
+               qualify = true;\r
+               currentRowIsValid = false;\r
+               scanRepositioned = false;\r
+    }\r
+\r
+       //\r
+       // ResultSet interface (leftover from NoPutResultSet)\r
+       //\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
+       {\r
+               if (SanityManager.DEBUG)\r
+                   SanityManager.ASSERT( ! isOpen, "TableScanResultSet already open");\r
+\r
+        // Get the current transaction controller\r
+        TransactionController tc = activation.getTransactionController();\r
+\r
+               initIsolationLevel();\r
+\r
+               if (dcoci == null)\r
+                       dcoci = tc.getDynamicCompiledConglomInfo(conglomId);\r
+\r
+\r
+               if (startKeyGetter != null)\r
+               {\r
+                       startPosition = (ExecIndexRow) startKeyGetter.invoke(activation);\r
+                       if (sameStartStopPosition)\r
+                       {\r
+                               stopPosition = startPosition;\r
+                       }\r
+               }\r
+               if (stopKeyGetter != null)\r
+               {\r
+                       stopPosition = (ExecIndexRow) stopKeyGetter.invoke(activation);\r
+               }\r
+\r
+               /* NOTE: We always open the ScanController on the 1st open\r
+                * to do the keyed conglomerate check.\r
+                */\r
+\r
+               // Determine whether the conglomerate is keyed.  This determines\r
+               // how we find the RowLocation for the base heap.  For non-keyed\r
+               // conglomerates, we ask the scan.  For keyed conglomerates, it\r
+               // is the last column in the row.\r
+               //\r
+               // Do this here, rather than in the constructor, so we can avoid\r
+               // throwing exceptions from the constructor\r
+               if (firstScan)\r
+               {\r
+                       openScanController(tc);\r
+\r
+                       isKeyed = scanController.isKeyed();\r
+\r
+                       /*\r
+                       ** If scan tracing is turned on, print information about this\r
+                       ** TableScanResultSet when it is first opened.  We would like\r
+                       ** to do this when it is constructed, but it is not always\r
+                       ** possible to get the start and stop positioners at the time\r
+                       ** this object is constructed (because they may depend on outer\r
+                       ** rows).\r
+                       */\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (SanityManager.DEBUG_ON("ScanTrace"))\r
+                               {\r
+                                       //traceScanParameters();\r
+                               }\r
+                       }\r
+               }\r
+\r
+               // Check whether there are any comparisons with unordered nulls\r
+               // on either the start or stop position.  If there are, we can\r
+               // (and must) skip the scan, because no rows can qualify\r
+               if (skipScan(startPosition, stopPosition))\r
+               {\r
+                       scanControllerOpened = false;\r
+               }\r
+               /* NOTE: We always open the ScanController on the 1st open\r
+                * to do the keyed conglomerate check, so we only need to\r
+                * do it here if not the 1st scan.\r
+                */\r
+               else if (! firstScan)\r
+               {\r
+                       openScanController(tc);\r
+               }\r
+\r
+               /* If the scan is on an index and opened for update,\r
+                * then we cache the scan controller and conglomerate\r
+                * number in the activation so that the scan controller\r
+                * can be re-used by the update/delete if the index\r
+                * that we are scanning also needs to be updated.\r
+                */\r
+               if (forUpdate && isKeyed)\r
+               {\r
+                       activation.setIndexScanController(scanController);\r
+                       activation.setIndexConglomerateNumber(conglomId);\r
+               }\r
+\r
+               firstScan = false;\r
+           isOpen = true;\r
+               numOpens++;\r
+               nextDone = false;\r
+               openTime += getElapsedMillis(beginTime);\r
+       }\r
+\r
+       /*\r
+       ** Open the scan controller\r
+       **\r
+       ** @param transaction controller will open one if null\r
+       */\r
+       protected void openScanController(TransactionController tc)\r
+               throws StandardException\r
+       {\r
+               openScanController(tc, (DataValueDescriptor)null);\r
+       }\r
+\r
+       /*\r
+       ** Does the work of openScanController.\r
+       **\r
+       ** @param tc transaction controller; will open one if null.\r
+       ** @param probeValue If non-null then we will open the scan controller\r
+       **  and position it using the received probeValue as the start key.\r
+       **  Otherwise we'll use whatever value is in startPosition (if non-\r
+       **  null) as the start key.\r
+       */\r
+       protected void openScanController(TransactionController tc,\r
+               DataValueDescriptor probeValue) throws StandardException\r
+       {\r
+               DataValueDescriptor[] startPositionRow = \r
+            startPosition == null ? null : startPosition.getRowArray();\r
+               DataValueDescriptor[] stopPositionRow = \r
+            stopPosition == null ? null : stopPosition.getRowArray();\r
+\r
+               /* If we have a probe value then we do the "probe" by positioning\r
+                * the scan at the first row matching the value.  The way to do\r
+                * that is to use the value as a start key, which is what will\r
+                * happen if we plug it into first column of "startPositionRow".\r
+                * So in this case startPositionRow[0] functions as a "place-holder"\r
+                * for the probe value.  The same goes for stopPositionRow[0].\r
+                *\r
+                * Note that it *is* possible for a start/stop key to contain more\r
+                * than one column (ex. if we're scanning a multi-column index). In\r
+                * that case we plug probeValue into the first column of the start\r
+                * and/or stop key and leave the rest of the key as it is.  As an \r
+                * example, assume we have the following predicates:\r
+                *\r
+                *    ... where d in (1, 20000) and b > 200 and b <= 500\r
+                *\r
+                * And assume further that we have an index defined on (d, b).\r
+                * In this case it's possible that we have TWO start predicates\r
+                * and TWO stop predicates: the IN list will give us "d = probeVal",\r
+                * which is a start predicate and a stop predicate; then "b > 200"\r
+                * may give us a second start predicate, while "b <= 500" may give\r
+                * us a second stop predicate.  So in this situation we want our\r
+                * start key to be:\r
+                *\r
+                *    (probeValue, 200)\r
+                *\r
+                * and our stop key to be:\r
+                *\r
+                *    (probeValue, 500).\r
+                *\r
+                * This will effectively limit the scan so that it only returns\r
+                * rows whose "D" column equals probeValue and whose "B" column\r
+                * falls in the range of 200 thru 500.\r
+                *\r
+                * Note: Derby currently only allows a single start/stop predicate\r
+                * per column. See PredicateList.orderUsefulPredicates().\r
+                */\r
+               if (probeValue != null)\r
+               {\r
+                       startPositionRow[0] = probeValue;\r
+\r
+                       /* If the start key and stop key are the same, we've already set\r
+                        * stopPosition equal to startPosition as part of openCore().\r
+                        * So by putting the probe value into startPositionRow[0], we\r
+                        * also put it into stopPositionRow[0].\r
+                        */\r
+                       if (!sameStartStopPosition)\r
+                               stopPositionRow[0] = probeValue;\r
+               }\r
+\r
+               // Clear the Qualifiers's Orderable cache \r
+               if (qualifiers != null)\r
+               {\r
+                       clearOrderableCache(qualifiers);\r
+               }\r
+\r
+               // Get the current transaction controller\r
+               if (tc == null)\r
+                       tc = activation.getTransactionController();\r
+\r
+        int openMode = 0;\r
+        if (forUpdate)\r
+        {\r
+            openMode = TransactionController.OPENMODE_FORUPDATE;\r
+\r
+            if (activation.isCursorActivation())\r
+                openMode |= TransactionController.OPENMODE_USE_UPDATE_LOCKS;\r
+        }\r
+\r
+               scanController = tc.openCompiledScan(\r
+                               activation.getResultSetHoldability(),\r
+                               openMode,\r
+                               lockMode,\r
+                               isolationLevel,\r
+                               accessedCols,\r
+                               startPositionRow,\r
+                                       // not used when giving null start position\r
+                               startSearchOperator,\r
+                               qualifiers,\r
+                               stopPositionRow,\r
+                                       // not used when giving null stop position\r
+                               stopSearchOperator,\r
+                               scoci,\r
+                               dcoci);\r
+\r
+               /* Remember that we opened the scan */\r
+               scanControllerOpened = true;\r
+\r
+               rowsThisScan = 0;\r
+\r
+               /*\r
+               ** Inform the activation of the estimated number of rows.  Only\r
+               ** do it here, not in reopen, so that we don't do this costly\r
+               ** check too often.\r
+               */\r
+               estimatedRowCount = scanController.getEstimatedRowCount();\r
+               activation.informOfRowCount(\r
+                                                                       this,\r
+                                                                       scanController.getEstimatedRowCount()\r
+                                                                       );\r
+       }\r
+\r
+       /*\r
+       ** reopen the scan controller\r
+       */\r
+       protected void reopenScanController() throws StandardException\r
+       {\r
+               reopenScanController((DataValueDescriptor)null);\r
+       }\r
+\r
+       /*\r
+       ** Does the work of reopenScanController.\r
+       **\r
+       ** @param probeValue If non-null then we will open the scan controller\r
+       **  and position it using the received probeValue as the start key.\r
+       **  Otherwise we'll use whatever value is in startPosition (if non-\r
+       **  null) as the start key.\r
+       */\r
+       protected void reopenScanController(DataValueDescriptor probeValue)\r
+               throws StandardException\r
+       {\r
+               DataValueDescriptor[] startPositionRow = \r
+            startPosition == null ? null : startPosition.getRowArray();\r
+               DataValueDescriptor[] stopPositionRow = \r
+            stopPosition == null ? null : stopPosition.getRowArray();\r
+\r
+               /* If we have a probe value then we do the "probe" by using the\r
+                * value as a start and stop key.  See openScanController() for\r
+                * details.  Note that in this case we do *not* want to reset\r
+                * the rowsThisScan variable because we are going to be doing\r
+                * multiple "probes" for a single scan.  Logic to detect when\r
+                * when we've actually started a new scan (as opposed to just\r
+                * repositioning an existing scan based on a probe value) is\r
+                * in MultiProbeTableScanResultSet.reopenScanController(),\r
+                * and that method will then take care of resetting the variable\r
+                * (if needed) for probing scans.\r
+                */\r
+               if (probeValue != null)\r
+               {\r
+                       startPositionRow[0] = probeValue;\r
+                       if (!sameStartStopPosition)\r
+                               stopPositionRow[0] = probeValue;\r
+               }\r
+               else\r
+                       rowsThisScan = 0;\r
+\r
+               // Clear the Qualifiers's Orderable cache \r
+               if (qualifiers != null)\r
+               {\r
+                       clearOrderableCache(qualifiers);\r
+               }\r
+\r
+               scanController.reopenScan(\r
+                                               startPositionRow,\r
+                                               startSearchOperator,\r
+                                               qualifiers,\r
+                                               stopPositionRow,\r
+                                               stopSearchOperator);\r
+\r
+               /* Remember that we opened the scan */\r
+               scanControllerOpened = true;\r
+       }\r
+\r
+       /**\r
+     * Reopen a table scan.  Here we take advantage\r
+        * of the reopenScan() interface on scanController\r
+        * for optimimal performance on joins where we are\r
+        * an inner table.\r
+        *\r
+        * @exception StandardException thrown on failure to open\r
+     */\r
+       public void     reopenCore() throws StandardException\r
+       {\r
+               beginTime = getCurrentTimeMillis();\r
+               if (SanityManager.DEBUG)\r
+                   SanityManager.ASSERT(isOpen, "TableScanResultSet not open, cannot reopen");\r
+\r
+               if (startKeyGetter != null)\r
+               {\r
+                       startPosition = (ExecIndexRow) startKeyGetter.invoke(activation);\r
+                       if (sameStartStopPosition)\r
+                       {\r
+                               stopPosition = startPosition;\r
+                       }\r
+               }\r
+               if (stopKeyGetter != null)\r
+               {\r
+                       stopPosition = (ExecIndexRow) stopKeyGetter.invoke(activation);\r
+               }\r
+\r
+               // Check whether there are any comparisons with unordered nulls\r
+               // on either the start or stop position.  If there are, we can\r
+               // (and must) skip the scan, because no rows can qualify\r
+               if (skipScan(startPosition, stopPosition))\r
+               {\r
+                       scanControllerOpened = false;\r
+               }\r
+               else\r
+               {\r
+                       if (scanController == null)\r
+                               openScanController((TransactionController)null);\r
+                       else\r
+                               reopenScanController();\r
+               \r
+               }\r
+\r
+               numOpens++;\r
+               nextDone = false;\r
+               openTime += getElapsedMillis(beginTime);\r
+       }\r
+\r
+       /**\r
+     * Check and make sure sparse heap row and accessed bit map are created.\r
+        * beetle 3865, update cursor using index.\r
+        *\r
+        * @exception StandardException thrown on failure\r
+        */\r
+       private void getSparseRowAndMap() throws StandardException\r
+       {\r
+               int numCols = 1, colPos;\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
+               sparseRowMap = new FormatableBitSet(numCols);\r
+               for (int i = 0; i < indexCols.length; i++)\r
+               {\r
+                       if (accessedCols.get(i))\r
+                       {\r
+                               colPos = (indexCols[i] > 0) ? indexCols[i] : -indexCols[i];\r
+                               sparseRow.setColumn(colPos, candidate.getColumn(i + 1));\r
+                               sparseRowMap.set(colPos - 1);\r
+                       }\r
+               }\r
+       }\r
+               \r
+\r
+       /**\r
+     * Return the next row (if any) from the scan (if open).\r
+        *\r
+        * @exception StandardException thrown on failure to get next row\r
+        */\r
+       public ExecRow getNextRowCore() throws StandardException\r
+       {\r
+        checkCancellationFlag();\r
+            \r
+               if (currentRow == null || scanRepositioned)\r
+               {\r
+                       currentRow =\r
+                               getCompactRow(candidate, accessedCols, (FormatableBitSet) null, isKeyed);\r
+               }\r
+\r
+               beginTime = getCurrentTimeMillis();\r
+\r
+               ExecRow result = null;\r
+\r
+               /* beetle 3865, updateable cursor using index. We first saved updated rows with new value\r
+                * falling into future direction of index scan in hash table, if it's full, we scanned\r
+                * forward and saved future row ids in a virtual mem heap.\r
+                */\r
+               if (futureForUpdateRows != null)\r
+               {\r
+                       currentRowPrescanned = false;\r
+                       if (! skipFutureRowHolder)\r
+                       {\r
+                               if (futureRowResultSet == null)\r
+                               {\r
+                                       futureRowResultSet = (TemporaryRowHolderResultSet) futureForUpdateRows.getResultSet();\r
+                                       futureRowResultSet.openCore();\r
+                               }\r
+\r
+                               ExecRow ridRow = futureRowResultSet.getNextRowCore();\r
+\r
+                               if (ridRow != null)\r
+                               {\r
+                                       /* to boost performance, we used virtual mem heap, and we can insert after\r
+                                        * we start retrieving results.  The assumption is to\r
+                                        * delete current row right after we retrieve it.\r
+                                        */\r
+                                       futureRowResultSet.deleteCurrentRow();\r
+                                       RowLocation rl = (RowLocation) ridRow.getColumn(1);\r
+                                       ConglomerateController baseCC = activation.getHeapConglomerateController();\r
+                                       if (sparseRow == null)\r
+                                               getSparseRowAndMap();\r
+                       baseCC.fetch(\r
+                                     rl, sparseRow.getRowArray(), sparseRowMap);\r
+                                       RowLocation rl2 = (RowLocation) rl.getClone();\r
+                                       currentRow.setColumn(currentRow.nColumns(), rl2);\r
+                                       candidate.setColumn(candidate.nColumns(), rl2);         // have to be consistent!\r
+\r
+                                       result = currentRow;\r
+                                       currentRowPrescanned = true;\r
+                               }\r
+                               else if (sourceDrained)\r
+                               {\r
+                                       currentRowPrescanned = true;\r
+                                       currentRow = null;\r
+                               }\r
+\r
+                               if (currentRowPrescanned)\r
+                               {\r
+                                       setCurrentRow(result);\r
+\r
+                                       nextTime += getElapsedMillis(beginTime);\r
+                                       return result;\r
+                               }\r
+                       }\r
+               }\r
+\r
+           if ( isOpen  && !nextDone)\r
+           {\r
+                       /* Only need to do 1 next per scan\r
+                        * for 1 row scans.\r
+                        */\r
+                       nextDone = oneRowScan;\r
+\r
+                       if (scanControllerOpened)\r
+                       {\r
+                               boolean moreRows;\r
+\r
+                               while (moreRows =\r
+                                                       scanController.fetchNext(candidate.getRowArray()))\r
+                               {\r
+                                       rowsSeen++;\r
+                                       rowsThisScan++;\r
+\r
+                                       /*\r
+                                       ** Skip rows where there are start or stop positioners\r
+                                       ** that do not implement ordered null semantics and\r
+                                       ** there are columns in those positions that contain\r
+                                       ** null.\r
+                                       ** No need to check if start and stop positions are the\r
+                                       ** same, since all predicates in both will be ='s,\r
+                                       ** and hence evaluated in the store.\r
+                                       */\r
+                                       if ((! sameStartStopPosition) && skipRow(candidate))\r
+                                       {\r
+                                               rowsFiltered++;\r
+                                               continue;\r
+                                       }\r
+\r
+                                       /* beetle 3865, updateable cursor use index. If we have a hash table that\r
+                                        * holds updated records, and we hit it again, skip it, and remove it from\r
+                                        * hash since we can't hit it again, and we have a space in hash, so can\r
+                                        * stop scanning forward.\r
+                                        */\r
+                                       if (past2FutureTbl != null)\r
+                                       {\r
+                                               RowLocation rowLoc = (RowLocation) currentRow.getColumn(currentRow.nColumns());\r
+                                               if (past2FutureTbl.get(rowLoc) != null)\r
+                                               {\r
+                                                       past2FutureTbl.remove(rowLoc);\r
+                                                       continue;\r
+                                               }\r
+                                       }\r
+\r
+                                       result = currentRow;\r
+\r
+                                       break;\r
+                               }\r
+\r
+                               /*\r
+                               ** If we just finished a full scan of the heap, update\r
+                               ** the number of rows in the scan controller.\r
+                               **\r
+                               ** NOTE: It would be more efficient to only update the\r
+                               ** scan controller if the optimizer's estimated number of\r
+                               ** rows were wrong by more than some threshold (like 10%).\r
+                               ** This would require a little more work than I have the\r
+                               ** time for now, however, as the row estimate that is given\r
+                               ** to this result set is the total number of rows for all\r
+                               ** scans, not the number of rows per scan.\r
+                               */\r
+                               if (! moreRows)\r
+                               {\r
+                                       setRowCountIfPossible(rowsThisScan);\r
+                                       currentRow = null;\r
+                               }\r
+                       }\r
+           }\r
+\r
+               setCurrentRow(result);\r
+               currentRowIsValid = true;\r
+               scanRepositioned = false;\r
+               qualify = true;\r
+\r
+               nextTime += getElapsedMillis(beginTime);\r
+           return result;\r
+       }\r
+\r
+       /**\r
+        * If the result set has been opened,\r
+        * close the open scan.\r
+        * @exception StandardException on error\r
+        */\r
+       public void     close() throws StandardException\r
+       {\r
+               beginTime = getCurrentTimeMillis();\r
+               if ( isOpen )\r
+           {\r
+                       /*\r
+                       ** If scan tracing is turned on, print information about this\r
+                       ** TableScanResultSet when it is closed.\r
+                       */\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               if (SanityManager.DEBUG_ON("ScanTrace"))\r
+                               {\r
+                                       //traceClose();\r
+                               }\r
+                       }\r
+\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
+;\r
+                       if (scanController != null)\r
+                       {\r
+                               // This is where we get the positioner info for inner tables\r
+                               if (runTimeStatisticsOn)\r
+                               {\r
+                                       // This is where we get the scan properties for a subquery\r
+                                       scanProperties = getScanProperties();\r
+                                       startPositionString = printStartPosition();\r
+                                       stopPositionString = printStopPosition();\r
+                               }\r
+                       scanController.close();\r
+                               scanController = null; // should not access after close\r
+                               activation.clearIndexScanInfo();\r
+                       }\r
+                       scanControllerOpened = false;\r
+                       startPosition = null;\r
+                       stopPosition = null;\r
+\r
+                       super.close();\r
+\r
+                       if (indexCols != null)\r
+                       {\r
+                               ConglomerateController borrowedBaseCC = activation.getHeapConglomerateController();\r
+                               if (borrowedBaseCC != null)\r
+                               {\r
+                                       borrowedBaseCC.close();\r
+                                       activation.clearHeapConglomerateController();\r
+                               }\r
+                       }\r
+                       if (futureRowResultSet != null)\r
+                               futureRowResultSet.close();\r
+           }\r
+               else\r
+                       if (SanityManager.DEBUG)\r
+                               SanityManager.DEBUG("CloseRepeatInfo","Close of TableScanResultSet repeated");\r
+\r
+               closeTime += getElapsedMillis(beginTime);\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
+               /* RESOLVE - subtract out store time later, when available */\r
+               if (type == NoPutResultSet.CURRENT_RESULTSET_ONLY)\r
+               {\r
+                       return  totTime;\r
+               }\r
+               else\r
+               {\r
+                       return totTime;\r
+               }\r
+       }\r
+\r
+\r
+       //\r
+       // CursorResultSet interface\r
+       //\r
+\r
+       /**\r
+        * This result set has its row location from\r
+        * the last fetch done. If the cursor is closed, \r
+        * or the row has been deleted a null is returned.\r
+        *\r
+        * @see CursorResultSet\r
+        *\r
+        * @return the row location of the current cursor row.\r
+        * @exception StandardException thrown on failure to get row location\r
+        */\r
+       public RowLocation getRowLocation() throws StandardException\r
+       {\r
+               RowLocation rl;\r
+\r
+               if (! isOpen) return null;\r
+\r
+               if ( ! scanControllerOpened)\r
+                       return null;\r
+\r
+               /*\r
+               ** If the conglomerate is keyed, the row location of the base row\r
+               ** is in the last column of the current row.  If it's not keyed,\r
+               ** we get the row location from the scan of the heap.\r
+               */\r
+               if (isKeyed)\r
+               {\r
+                       if (SanityManager.DEBUG)\r
+                       {\r
+                               SanityManager.ASSERT(currentRow != null,\r
+                                 "There must be a current row when fetching the row location");\r
+                       }\r
+\r
+                       rl = (RowLocation) currentRow.getColumn(\r
+                                                                                                       currentRow.nColumns());\r
+               }\r
+               else\r
+               {\r
+                       if (currentRowIsValid) {\r
+                               // we reuse the same rowlocation object across several calls.\r
+                               if (rlTemplate == null)\r
+                                       rlTemplate = scanController.newRowLocationTemplate();\r
+                               rl = rlTemplate;\r
+                               try {\r
+                                       scanController.fetchLocation(rl);\r
+                               } catch (StandardException se) {\r
+                                       if (se.getMessageId().\r
+                                               equals(SQLState.HEAP_SCAN_NOT_POSITIONED)) {\r
+                                               //Have a easier to understand error message than what \r
+                                               //we get from store \r
+                                               throw StandardException.\r
+                                                       newException(SQLState.NO_CURRENT_ROW);\r
+                                       }\r
+                    throw se;\r
+                               }\r
+                       } else {\r
+                               rl = null;\r
+                       }\r
+               }\r
+\r
+               return rl;\r
+       }\r
+\r
+       /**\r
+        * This result set has its row from the last fetch done. \r
+        * If the cursor is closed, the row has been deleted, or\r
+        * no longer qualifies (for forward only result sets) a \r
+        * null is returned.\r
+        *\r
+        * @see CursorResultSet\r
+        *\r
+        * @return the last row returned;\r
+        * @exception StandardException thrown on failure.\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
+           ExecRow result = null;\r
+\r
+               if (SanityManager.DEBUG)\r
+                       SanityManager.ASSERT(isOpen, "TSRS expected to be open");\r
+\r
+               if (currentRowPrescanned)\r
+                       return currentRow;\r
+\r
+               /* Nothing to do if we're not currently on a row or\r
+                * if the current row get deleted out from under us\r
+                * or if there is no current scan (can happen if the\r
+                * scan is being skipped) or if the current position\r
+                * no longer qualifies.\r
+                */\r
+               try\r
+               {\r
+                       if ((currentRow == null)                        ||\r
+                       (!currentRowIsValid)                            ||\r
+                       (!scanControllerOpened)                         ||\r
+                       (qualify && scanController.isCurrentPositionDeleted())     ||\r
+                       (qualify && (!scanController.doesCurrentPositionQualify())))\r
+                       {\r
+                               return null;\r
+                       }\r
+               }\r
+               catch (StandardException se)\r
+               {\r
+                       if (se.getMessageId().equals(SQLState.AM_SCAN_NOT_POSITIONED))\r
+                       {\r
+                               //bug 4515 - Have a easier to understand error message than what we get from store \r
+                               se=StandardException.newException(SQLState.NO_CURRENT_ROW);\r
+                               throw se;\r
+                       }\r
+               }\r
+\r
+               result = (ExecRow) resultRowAllocator.invoke(activation);\r
+               currentRow = \r
+            getCompactRow(result, accessedCols, (FormatableBitSet) null, isKeyed);\r
+\r
+        try\r
+        {\r
+            scanController.fetchWithoutQualify(result.getRowArray());\r
+        }\r
+        catch (StandardException se)\r
+        {\r
+            if (se.getMessageId().equals(SQLState.AM_RECORD_NOT_FOUND))\r
+            {\r
+                // Somehow the row got deleted between the above \r
+                // doesCurrentPositionQualify() call and here (one way is if\r
+                // this scan is read uncommitted isolation level).\r
+                return null;\r
+            }\r
+            else\r
+            {\r
+                throw se;\r
+            }\r
+        }\r
+\r
+               setCurrentRow(result);\r
+           return currentRow;\r
+       }\r
+\r
+       /**\r
+        * @see NoPutResultSet#positionScanAtRowLocation\r
+        * \r
+        * Also sets qualify to false so that later calls to getCurrentRow\r
+        * will not attempt to re-qualify the current row. \r
+        */\r
+       public void positionScanAtRowLocation(RowLocation rl) \r
+               throws StandardException \r
+       {\r
+               // Check if the scanController is a B-tree scan controller. Do not\r
+               // attempt to re-position a b-tree controller.\r
+               if (!isKeyed) {\r
+                       currentRowIsValid = scanController.positionAtRowLocation(rl);\r
+               }\r
+               qualify = false;\r
+               scanRepositioned = true;\r
+       }\r
+\r
+       /**\r
+        * Print the parameters that constructed this result set to the\r
+        * trace stream.\r
+        */\r
+/*\r
+       private final void traceScanParameters()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       HeaderPrintWriter traceStream = SanityManager.GET_DEBUG_STREAM();\r
+\r
+                       traceStream.println("");\r
+                       traceStream.println("TableScanResultSet number " +\r
+                                                               resultSetNumber +\r
+                                                               " parameters:");\r
+\r
+                       traceStream.println("");\r
+                       traceStream.println("\tTable name: " + tableName);\r
+                       if (indexName != null)\r
+                       {\r
+                               traceStream.println("\tIndex name: " + indexName);\r
+                       }\r
+                       traceStream.println("");\r
+                       traceStream.println("\tStart position is: ");\r
+                       tracePrintPosition(traceStream,\r
+                                                               startSearchOperator,\r
+                                                               startKeyGetter);\r
+                       traceStream.println("");\r
+                       traceStream.println("\tStop position is: " );\r
+                       tracePrintPosition(traceStream,\r
+                                                               stopSearchOperator,\r
+                                                               stopKeyGetter);\r
+                       traceStream.println("");\r
+                       traceStream.println("\tQualifiers are: ");\r
+                       tracePrintQualifiers(traceStream, qualifiers, 2);\r
+                       traceStream.println("");\r
+               }\r
+       }\r
+*/\r
+\r
+       /**\r
+        * Print I/O statistics about a scan when it closes.\r
+        */\r
+/*\r
+       private final void traceClose()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       InfoStreams                     infoStreams;\r
+                       HeaderPrintWriter       traceStream;\r
+\r
+                       traceStream = SanityManager.GET_DEBUG_STREAM();\r
+\r
+                       traceStream.println("TableScanResultSet number " +\r
+                                                               resultSetNumber +\r
+                                                               " closed.");\r
+                       if (isKeyed)\r
+                       {\r
+                               traceStream.println("\t" +\r
+                                                                       rowCount() +\r
+                                                                       " row(s) qualified from " +\r
+                                                                       "keyed" +\r
+                                                                       " table " +\r
+                                                                       tableName +\r
+                                                                       " using index " +\r
+                                                                       indexName);\r
+                       }\r
+                       else\r
+                       {\r
+                               traceStream.println("\t" +\r
+                                                                       rowCount() +\r
+                                                                       " row(s) qualified from " +\r
+                                                                       "non-keyed" +\r
+                                                                       " table " +\r
+                                                                       tableName);\r
+                       }\r
+                       traceStream.println("");\r
+               }\r
+       }\r
+*/\r
+\r
+       /**\r
+        * Print a start or stop positioner to the trace stream.\r
+        */\r
+/*\r
+       private final void tracePrintPosition(HeaderPrintWriter traceStream,\r
+                                                                                 int searchOperator,\r
+                                                                                 GeneratedMethod positionGetter)\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (positionGetter == null)\r
+                       {\r
+                               traceStream.println("\t\tNone");\r
+                               return;\r
+                       }\r
+\r
+                       ExecIndexRow    positioner = null;\r
+\r
+                       try\r
+                       {\r
+                               positioner = (ExecIndexRow) positionGetter.invoke(activation);\r
+                       }\r
+                       catch (StandardException e)\r
+                       {\r
+                               traceStream.println("\t\tUnexpected exception " +\r
+                                                                       e +\r
+                                                                       " getting positioner.");\r
+                               e.printStackTrace(traceStream.getPrintWriter());\r
+                               return;\r
+                       }\r
+\r
+                       if (positioner == null)\r
+                       {\r
+                               traceStream.println("\t\tNone");\r
+                               return;\r
+                       }\r
+\r
+                       String searchOp = null;\r
+\r
+                       switch (searchOperator)\r
+                       {\r
+                         case ScanController.GE:\r
+                               searchOp = "GE";\r
+                               break;\r
+\r
+                         case ScanController.GT:\r
+                               searchOp = "GT";\r
+                               break;\r
+\r
+                         default:\r
+                               searchOp = "unknown value (" + searchOperator + ")";\r
+                               break;\r
+                       }\r
+\r
+                       traceStream.println("\t\t" +\r
+                                                               searchOp +\r
+                                                               " on first " +\r
+                                                               positioner.nColumns() +\r
+                                                               " column(s).");\r
+\r
+                       traceStream.print(\r
+                                       "\t\tOrdered null semantics on the following columns: ");\r
+                       for (int position = 0; position < positioner.nColumns(); position++)\r
+                       {\r
+                               if (positioner.areNullsOrdered(position))\r
+                               {\r
+                                       traceStream.print(position + " ");\r
+                               }\r
+                       }\r
+                       traceStream.println("");\r
+               }\r
+       }\r
+*/\r
+\r
+\r
+       /**\r
+        * Print an array of Qualifiers to the trace stream.\r
+        */\r
+/*\r
+       private final void tracePrintQualifiers(HeaderPrintWriter traceStream,\r
+                                                                                       Qualifier[][] qualifiers,\r
+                                                                                       int depth)\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       char[] indentchars = new char[depth];\r
+\r
+                       /*\r
+                       ** Form an array of tab characters for indentation.\r
+                       *\r
+                       while (depth > 0)\r
+                       {\r
+                               indentchars[depth - 1] = '\t';\r
+                               depth--;\r
+                       }\r
+                       String indent = new String(indentchars);\r
+\r
+                       if (qualifiers == null)\r
+                       {\r
+                               traceStream.println(indent +\r
+                                                                       MessageService.getTextMessage(\r
+                                                                               SQLState.LANG_NONE)\r
+                                                                       );\r
+                               return;\r
+                       }\r
+\r
+            // RESOLVE (mikem) We don't support 2-d qualifiers yet.\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                SanityManager.ASSERT(qualifiers.length == 1);\r
+            }\r
+\r
+                       for (int i = 0; i < qualifiers[0].length; i++)\r
+                       {\r
+                               Qualifier qual = qualifiers[0][i];\r
+\r
+                               traceStream.println("");\r
+                               traceStream.println(indent + "Column Id: " + qual.getColumnId());\r
+                               \r
+                               int operator = qual.getOperator();\r
+                               String opString = null;\r
+                               switch (operator)\r
+                               {\r
+                                 case Orderable.ORDER_OP_EQUALS:\r
+                                       opString = "=";\r
+                                       break;\r
+\r
+                                 case Orderable.ORDER_OP_LESSOREQUALS:\r
+                                       opString = "<=";\r
+                                       break;\r
+\r
+                                 case Orderable.ORDER_OP_LESSTHAN:\r
+                                       opString = "<";\r
+                                       break;\r
+\r
+                                 default:\r
+                                       opString = "unknown value (" + operator + ")";\r
+                                       break;\r
+                               }\r
+                               traceStream.println(indent + "Operator: " + opString);\r
+                               traceStream.println(indent + "Ordered nulls: " +\r
+                                                                                       qual.getOrderedNulls());\r
+                               traceStream.println(indent + "Unknown return value: " +\r
+                                                                                       qual.getUnknownRV());\r
+                               traceStream.println(indent + "Negate comparison result: " +\r
+                                                                                       qual.negateCompareResult());\r
+                               traceStream.println("");\r
+                       }\r
+               }\r
+       }\r
+*/\r
+\r
+       public String printStartPosition()\r
+       {\r
+               return printPosition(startSearchOperator, startKeyGetter, startPosition);\r
+       }\r
+\r
+       public String printStopPosition()\r
+       {\r
+               if (sameStartStopPosition)\r
+               {\r
+                       return printPosition(stopSearchOperator, startKeyGetter, startPosition);\r
+               }\r
+               else\r
+               {\r
+                       return printPosition(stopSearchOperator, stopKeyGetter, stopPosition);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Return a start or stop positioner as a String.\r
+        *\r
+        * If we already generated the information, then use\r
+        * that.  Otherwise, invoke the activation to get it.\r
+        */\r
+       private String printPosition(int searchOperator,\r
+                                                                GeneratedMethod positionGetter,\r
+                                                                ExecIndexRow positioner)\r
+       {\r
+                String idt = "";\r
+               String output = "";\r
+               if (positionGetter == null)\r
+               {\r
+                       return "\t" +\r
+                                       MessageService.getTextMessage(SQLState.LANG_NONE) +\r
+                                       "\n";\r
+               }\r
+               \r
+               if (positioner == null)\r
+               {\r
+                       try\r
+                       {\r
+                               positioner = (ExecIndexRow)positionGetter.invoke(activation);\r
+                       }\r
+                       catch (StandardException e)\r
+                       {\r
+                               // the positionGetter will fail with a NullPointerException\r
+                               // if the outer table is empty\r
+                               // (this isn't a problem since we won't call it on the inner\r
+                               // table if there are no rows on the outer table)\r
+                               if (e.getSQLState() == SQLState.LANG_UNEXPECTED_USER_EXCEPTION )\r
+                                       return "\t" + MessageService.getTextMessage(\r
+                                               SQLState.LANG_POSITION_NOT_AVAIL);\r
+                               return "\t" + MessageService.getTextMessage(\r
+                                               SQLState.LANG_UNEXPECTED_EXC_GETTING_POSITIONER,\r
+                                               e.toString());\r
+                       }\r
+               }\r
+               if (positioner == null)\r
+               {\r
+                       return "\t" +\r
+                                       MessageService.getTextMessage(SQLState.LANG_NONE) +\r
+                                       "\n";\r
+               }\r
+               String searchOp = null;\r
+\r
+               switch (searchOperator)\r
+               {\r
+                       case ScanController.GE:\r
+                               searchOp = ">=";\r
+                               break;\r
+\r
+                       case ScanController.GT:\r
+                               searchOp = ">";\r
+                               break;\r
+\r
+                       default:\r
+                               if (SanityManager.DEBUG)\r
+                               {\r
+                                       SanityManager.THROWASSERT("Unknown search operator " +\r
+                                                                                               searchOperator);\r
+                               }\r
+\r
+                               // NOTE: This does not have to be internationalized because\r
+                               // this code should never be reached.\r
+                               searchOp = "unknown value (" + searchOperator + ")";\r
+                               break;\r
+               }\r
+\r
+               output = output + "\t" +\r
+                                               MessageService.getTextMessage(\r
+                                                       SQLState.LANG_POSITIONER,\r
+                                                       searchOp,\r
+                                                       String.valueOf(positioner.nColumns())) +\r
+                                               "\n";\r
+\r
+               output = output + "\t" +\r
+                                       MessageService.getTextMessage(\r
+                                               SQLState.LANG_ORDERED_NULL_SEMANTICS) +\r
+                                       "\n";\r
+               for (int position = 0; position < positioner.nColumns(); position++)\r
+               {\r
+                       if (positioner.areNullsOrdered(position))\r
+                       {\r
+                               output = output + position + " ";\r
+                       }\r
+               }\r
+               \r
+               return output + "\n";\r
+       }\r
+\r
+       public Properties getScanProperties()\r
+       {\r
+               if (scanProperties == null)\r
+               {\r
+                       scanProperties = new Properties();\r
+               }\r
+               try\r
+               {\r
+                       if (scanController != null)\r
+                       {\r
+                               scanController.getScanInfo().getAllScanInfo(scanProperties);\r
+                               /* Did we get a coarser lock due to\r
+                                * a covering lock, lock escalation\r
+                                * or configuration?\r
+                                */\r
+                               coarserLock = scanController.isTableLocked() &&\r
+                                       (lockMode == TransactionController.MODE_RECORD);\r
+                       }\r
+               }\r
+               catch(StandardException se)\r
+               {\r
+                       // ignore\r
+               }\r
+\r
+               return scanProperties;\r
+       }\r
+\r
+       /**\r
+        * @see NoPutResultSet#requiresRelocking\r
+        */\r
+       public boolean requiresRelocking()\r
+       {\r
+               return(\r
+            isolationLevel == \r
+                TransactionController.ISOLATION_READ_COMMITTED_NOHOLDLOCK);\r
+       }\r
+\r
+       /**\r
+        * Update the number of rows in the scan controller.\r
+        *\r
+        * NOTE: It would be more efficient to only update the\r
+        * scan controller if the optimizer's estimated number of\r
+        * rows were wrong by more than some threshold (like 10%).\r
+        * This would require a little more work than I have the\r
+        * time for now, however, as the row estimate that is given\r
+        * to this result set is the total number of rows for all\r
+        * scans, not the number of rows per scan.\r
+        *\r
+        *\r
+        * @param rowsThisScan  The number of rows to update the scanController to\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       protected final void setRowCountIfPossible(long rowsThisScan)\r
+                                       throws StandardException\r
+       {\r
+               /*\r
+               ** Is it a heap scan with no qualifiers (full table scan?)\r
+               ** and is it not for update (we don't want to count rows we're\r
+               ** about to delete.\r
+               */\r
+               if ( ( ! scanController.isKeyed() ) &&\r
+                       (qualifiers == null || qualifiers.length == 0) &&\r
+                       ( ! forUpdate ) )\r
+               {\r
+\r
+                       // Only update rows if different by more than 10%\r
+                       long diff = rowsThisScan - estimatedRowCount;\r
+\r
+                       long tenPerCent = estimatedRowCount  / 10;\r
+\r
+                       if (diff < 0)\r
+                               diff = -diff;\r
+\r
+                       if (diff > tenPerCent)\r
+                               scanController.setEstimatedRowCount(rowsThisScan);\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Can we get instantaneous locks when getting share row\r
+        * locks at READ COMMITTED.\r
+        */\r
+       protected boolean canGetInstantaneousLocks()\r
+       {\r
+               return false;\r
+       }\r
+\r
+\r
+       /**\r
+        * Is this ResultSet or it's source result set for update\r
+        * \r
+        * @return Whether or not the result set is for update.\r
+        */\r
+       public boolean isForUpdate()\r
+       {\r
+               return forUpdate;\r
+       }\r
+\r
+       /**\r
+        * Shallow clone this result set.  Used in trigger reference.\r
+        * beetle 4373.\r
+        */\r
+       public Object clone()\r
+       {\r
+               Object clo = null;\r
+               try {\r
+                       clo = super.clone();\r
+               }\r
+               catch (CloneNotSupportedException e) {}\r
+               return clo;\r
+       }\r
+}\r