Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / store / access / btree / BTreeScan.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/store/access/btree/BTreeScan.java
new file mode 100644 (file)
index 0000000..0f02fbd
--- /dev/null
@@ -0,0 +1,2551 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.store.access.btree.BTreeScan\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.store.access.btree;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.store.access.conglomerate.Conglomerate;\r
+import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;\r
+import org.apache.derby.iapi.store.access.conglomerate.ScanManager;\r
+import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;\r
+\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.RowUtil;\r
+import org.apache.derby.iapi.store.access.ScanController;\r
+import org.apache.derby.iapi.store.access.ScanInfo;\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+\r
+import org.apache.derby.iapi.store.raw.ContainerHandle;\r
+import org.apache.derby.iapi.store.raw.FetchDescriptor;\r
+import org.apache.derby.iapi.store.raw.LockingPolicy;\r
+import org.apache.derby.iapi.store.raw.Page;\r
+import org.apache.derby.iapi.store.raw.RecordHandle;\r
+import org.apache.derby.iapi.store.raw.Transaction;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.impl.store.access.conglomerate.TemplateRow;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.store.access.BackingStoreHashtable;\r
+\r
+/**\r
+\r
+  A b-tree scan controller corresponds to an instance of an open b-tree scan.\r
+  <P>\r
+  <B>Concurrency Notes<\B>\r
+  <P>\r
+  The concurrency rules are derived from OpenBTree.\r
+  <P>\r
+  @see OpenBTree\r
+\r
+**/\r
+\r
+public abstract class BTreeScan extends OpenBTree implements ScanManager\r
+{\r
+\r
+       /*\r
+       ** Fields of BTreeScan\r
+       */\r
+\r
+    /**\r
+     * init_startKeyValue, init_qualifier, and init_stopKeyValue all are used \r
+     * to store * references to the values passed in when ScanController.init()\r
+     * is called.  It is assumed that these are not altered by the client\r
+     * while the scan is active.\r
+     */\r
+    protected Transaction           init_rawtran             = null;\r
+    protected boolean               init_forUpdate;\r
+    protected FormatableBitSet      init_scanColumnList;\r
+    protected DataValueDescriptor[] init_template;\r
+    protected DataValueDescriptor[] init_startKeyValue;\r
+    protected int                   init_startSearchOperator = 0;\r
+    protected Qualifier             init_qualifier[][]       = null;\r
+    protected DataValueDescriptor[] init_stopKeyValue;\r
+    protected int                   init_stopSearchOperator  = 0;\r
+    protected boolean               init_hold;\r
+\r
+\r
+    /**\r
+     * The fetch descriptor which describes the row to be returned by the scan.\r
+     **/\r
+    protected FetchDescriptor       init_fetchDesc;\r
+\r
+\r
+    /**\r
+     * A constant FetchDescriptor which describes the position of the \r
+     * RowLocation field within the btree, currently always the last column).  \r
+     * Used by lock/unlock to fetch the RowLocation.  \r
+     * Only needs to be allocated once per scan.\r
+     **/\r
+    protected FetchDescriptor       init_lock_fetch_desc;\r
+\r
+\r
+    BTreeRowPosition                scan_position;\r
+\r
+\r
+     /**\r
+      * Whether the scan should requests UPDATE locks which then will be \r
+      * converted to X locks when the actual operation is performed.\r
+     **/\r
+     protected boolean init_useUpdateLocks = false;\r
+\r
+    /*\r
+     * There are 5 states a scan can be in.\r
+     *     SCAN_INIT - A scan has started but no positioning has been done.\r
+     *                 The scan will be positioned when the first next() call\r
+     *                 has been made.  None of the positioning state variables\r
+     *                 are valid in this state.\r
+     *     SCAN_INPROGRESS -\r
+     *                 A scan is in this state after the first next() call.\r
+     *                 On exit from any BTreeScan method, while in this state,\r
+     *                 the scan "points" at a row which qualifies for the \r
+     *                 scan.  While not maintaining latches on a page the \r
+     *                 current position of the scan is either kept by record\r
+     *                 handle or key.  To tell which use the following:\r
+     *                 if (record key == null)\r
+     *                    record handle has current position\r
+     *                 else\r
+     *                    record key has current position\r
+     *\r
+     *     SCAN_DONE - Once the end of the table or the stop condition is met\r
+     *                 then the scan is placed in this state.  Only valid \r
+     *                 ScanController method at this point is close().\r
+     *\r
+     *     SCAN_HOLD_INIT -\r
+     *                 The scan has been opened and held open across a commit,\r
+     *                 at the last commit the state was SCAN_INIT.\r
+     *                 The scan has never progressed from the SCAN_INIT state\r
+     *                 during a transaction.  When a next is done the state\r
+     *                 will either progress to SCAN_INPROGRESS or SCAN_DONE.\r
+     *\r
+     *     SCAN_HOLD_INPROGRESS -\r
+     *                 The scan has been opened and held open across a commit,\r
+     *                 at the last commit the state was in SCAN_INPROGRESS.\r
+     *                 The transaction which opened the scan has committed,\r
+     *                 but the scan was opened with the "hold" option true.\r
+     *                 At commit the locks were released and the "current"\r
+     *                 position is remembered.  In this state only two calls\r
+     *                 are valid, either next() or close().  When next() is\r
+     *                 called the scan is reopened, the underlying container\r
+     *                 is opened thus associating all new locks with the current\r
+     *                 transaction, and the scan continues at the "next" row.\r
+     */\r
+    protected static final int    SCAN_INIT             = 1;\r
+    protected static final int    SCAN_INPROGRESS       = 2;\r
+    protected static final int    SCAN_DONE             = 3;\r
+    protected static final int    SCAN_HOLD_INIT        = 4;\r
+    protected static final int    SCAN_HOLD_INPROGRESS  = 5;\r
+\r
+    /**\r
+     * Delay positioning the  table at the start position until the first\r
+     * next() call.  The initial position is done in positionAtStartPosition().\r
+     */\r
+    protected int         scan_state      = SCAN_INIT;\r
+\r
+    /**\r
+     * Performance counters ...\r
+     */\r
+    protected int stat_numpages_visited         = 0;\r
+    protected int stat_numrows_visited          = 0;\r
+    protected int stat_numrows_qualified        = 0;\r
+    protected int stat_numdeleted_rows_visited  = 0;\r
+\r
+    /**\r
+        * What kind of row locks to get during the scan.\r
+     **/\r
+    protected int lock_operation;\r
+\r
+\r
+    /**\r
+     * A 1 element array to turn fetchNext and fetch calls into \r
+     * fetchNextGroup calls.\r
+     **/\r
+    protected DataValueDescriptor[][] fetchNext_one_slot_array = \r
+                                            new DataValueDescriptor[1][];\r
+\r
+    /* Constructors for This class: */\r
+\r
+       public BTreeScan()\r
+       {\r
+       }\r
+\r
+       /*\r
+       ** Private/Protected methods of This class, sorted alphabetically\r
+       */\r
+\r
+    /**\r
+     * Fetch the next N rows from the table.\r
+     * <p>\r
+     * Utility routine used by both fetchSet() and fetchNextGroup().\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    abstract protected int fetchRows(\r
+    BTreeRowPosition        pos,\r
+    DataValueDescriptor[][] row_array,\r
+    RowLocation[]           rowloc_array,\r
+    BackingStoreHashtable   hash_table,\r
+    long                    max_rowcnt,\r
+    int[]                   key_column_numbers)\r
+        throws StandardException;\r
+\r
+\r
+    /**\r
+     * Shared initialization code between init() and reopenScan().\r
+     * <p>\r
+     * Basically save away input parameters describing qualifications for\r
+     * the scan, and do some error checking.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       private void initScanParams(\r
+    DataValueDescriptor[]   startKeyValue,\r
+    int                     startSearchOperator,\r
+    Qualifier               qualifier[][],\r
+    DataValueDescriptor[]   stopKeyValue,\r
+    int                     stopSearchOperator)\r
+        throws StandardException\r
+       {\r
+        // startKeyValue init.\r
+           this.init_startKeyValue         = startKeyValue;\r
+               if (RowUtil.isRowEmpty(this.init_startKeyValue))\r
+                       this.init_startKeyValue = null;\r
+\r
+        // startSearchOperator init.\r
+           this.init_startSearchOperator   = startSearchOperator;\r
+\r
+        // qualifier init.\r
+        if ((qualifier != null) && (qualifier .length == 0))\r
+            qualifier = null;\r
+        this.init_qualifier             = qualifier;\r
+\r
+        // stopKeyValue init.\r
+           this.init_stopKeyValue          = stopKeyValue;\r
+        if (RowUtil.isRowEmpty(this.init_stopKeyValue))\r
+            this.init_stopKeyValue = null;\r
+\r
+        // stopSearchOperator init.\r
+           this.init_stopSearchOperator    = stopSearchOperator;\r
+\r
+        // reset the "current" position to starting condition.\r
+        // RESOLVE (mmm) - "compile" this.\r
+        scan_position = new BTreeRowPosition();\r
+\r
+        scan_position.init();\r
+\r
+        scan_position.current_lock_template = \r
+            new DataValueDescriptor[this.init_template.length];\r
+\r
+        scan_position.current_lock_template[this.init_template.length - 1] = \r
+            scan_position.current_lock_row_loc = \r
+                (RowLocation) ((RowLocation) \r
+                     init_template[init_template.length - 1]).cloneObject(); \r
+\r
+        // Verify that all columns in start key value, stop key value, and\r
+        // qualifiers are present in the list of columns described by the\r
+        // scanColumnList.\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            if (init_scanColumnList != null)\r
+            {\r
+                // verify that all columns specified in qualifiers, start\r
+                // and stop positions are specified in the scanColumnList.  \r
+                \r
+                FormatableBitSet required_cols;\r
+\r
+                if (qualifier != null)\r
+                    required_cols = RowUtil.getQualifierBitSet(qualifier);\r
+                else\r
+                    required_cols = new FormatableBitSet(0);\r
+\r
+                // add in start columns\r
+                if (this.init_startKeyValue != null)\r
+                {\r
+                                       required_cols.grow(this.init_startKeyValue.length);\r
+                    for (int i = 0; i < this.init_startKeyValue.length; i++)\r
+                        required_cols.set(i);\r
+                }\r
+\r
+                if (this.init_stopKeyValue != null)\r
+                {\r
+                                       required_cols.grow(this.init_stopKeyValue.length);\r
+                    for (int i = 0; i < this.init_stopKeyValue.length; i++)\r
+                        required_cols.set(i);\r
+                }\r
+\r
+                FormatableBitSet required_cols_and_scan_list = \r
+                    (FormatableBitSet) required_cols.clone();\r
+\r
+                required_cols_and_scan_list.and(init_scanColumnList);\r
+\r
+                               // FormatableBitSet equals requires the two FormatableBitSets to be of same\r
+                               // length.\r
+                               required_cols.grow(init_scanColumnList.size());\r
+\r
+                if (!required_cols_and_scan_list.equals(required_cols))\r
+                {\r
+                    SanityManager.THROWASSERT(\r
+                        "Some column specified in a Btree " +\r
+                        " qualifier/start/stop list is " +\r
+                        "not represented in the scanColumnList." +\r
+                        "\n:required_cols_and_scan_list = " + \r
+                            required_cols_and_scan_list + \r
+                        "\n;required_cols = " + required_cols +\r
+                        "\n;init_scanColumnList = " + init_scanColumnList);\r
+                }\r
+            }\r
+               } \r
+       }\r
+\r
+    /**\r
+     * Position scan at "start" position for a forward scan.\r
+     * <p> \r
+     * Positions the scan to the slot just before the first record to be \r
+     * returned from the scan.  Returns the start page latched, and \r
+     * sets "current_slot" to the slot number.\r
+     * <p>\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    protected void positionAtStartForForwardScan(\r
+    BTreeRowPosition    pos)\r
+               throws StandardException\r
+       {\r
+               boolean         exact;\r
+\r
+        // This routine should only be called from first next() call //\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(\r
+                (scan_state == SCAN_INIT) || (scan_state == SCAN_HOLD_INIT));\r
+            SanityManager.ASSERT(pos.current_rh          == null);\r
+            SanityManager.ASSERT(pos.current_positionKey == null);\r
+            SanityManager.ASSERT(pos.current_scan_pageno == 0);\r
+        }\r
+\r
+        // Loop until you can lock the row previous to the first row to be\r
+        // returned by the scan, while holding the page latched, without\r
+        // waiting.  If you have to wait, drop the latch, wait for the lock -\r
+        // which makes it likely if you wait for the lock you will loop just\r
+        // once, find the same lock satisfies the search and since you already\r
+        // have the lock it will be granted.\r
+        while (true)\r
+        {\r
+            // Find the starting page and row slot, must start at root and\r
+            // search either for leftmost leaf, or search for specific key.\r
+            ControlRow root = ControlRow.get(this, BTree.ROOTPAGEID); \r
+\r
+            // include search of tree in page visited stats.\r
+            stat_numpages_visited += root.getLevel() + 1;\r
+\r
+            boolean need_previous_lock = true;\r
+\r
+            if (init_startKeyValue == null)\r
+            {\r
+                // No start given, so position at 0 slot of leftmost leaf page\r
+                pos.current_leaf = (LeafControlRow) root.searchLeft(this);\r
+\r
+                pos.current_slot = ControlRow.CR_SLOT;\r
+                exact     = false;\r
+            }\r
+            else\r
+            {\r
+                // Search for the starting row.\r
+\r
+                if (SanityManager.DEBUG)\r
+                    SanityManager.ASSERT(\r
+                        (init_startSearchOperator == ScanController.GE) ||\r
+                        (init_startSearchOperator == ScanController.GT));\r
+\r
+                SearchParameters sp = new SearchParameters(\r
+                    init_startKeyValue, \r
+                    ((init_startSearchOperator == ScanController.GE) ? \r
+                        SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH : \r
+                        SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH),\r
+                    init_template, this, false);\r
+\r
+                pos.current_leaf = (LeafControlRow) root.search(sp);\r
+\r
+                pos.current_slot = sp.resultSlot;\r
+                exact     = sp.resultExact;\r
+\r
+                // The way that scans are used, the caller calls next()\r
+                // to position on the first row.  If the result of the\r
+                // search that found the starting page and slot was not\r
+                // exact, then the page/slot will refer to the row before\r
+                // the first qualifying row.  The first call to next()\r
+                // will therefore move to the first (potentially) qualifying\r
+                // row.  However, if the search was exact, then we don't\r
+                // want to move the position on the first call to next.\r
+                // In that case, by decrementing the slot, the first call\r
+                // to next will put us back    on the starting row.\r
+\r
+                if (exact && init_startSearchOperator == ScanController.GE)\r
+                {\r
+                    pos.current_slot--;\r
+\r
+                    // A scan on a unique index, with a start position of\r
+                    // GE, need not get a previous key lock to protect the\r
+                    // range.  Since it is unique no other key can go before\r
+                    // the first row returned from the scan.\r
+                    //\r
+                    // RESOLVE - currently btree's only support allowDuplicates\r
+                    // of "false", so no need to do the extra check, current\r
+                    // btree implementation depends on RowLocation field\r
+                    // making every key unique (duplicate indexes are supported\r
+                    // by the nUniqueColumns and nKeyFields). \r
+                    if (getConglomerate().nUniqueColumns < getConglomerate().nKeyFields)\r
+                    {\r
+                        // this implies unique index, thus no prev key.\r
+                        need_previous_lock = false;\r
+                    }\r
+                }\r
+            }\r
+\r
+            boolean latch_released = false;\r
+            if (need_previous_lock)\r
+            {\r
+                latch_released = \r
+                    !this.getLockingPolicy().lockScanRow(\r
+                        this, this.getConglomerate(), pos,\r
+                        true, \r
+                        init_lock_fetch_desc,\r
+                        pos.current_lock_template,\r
+                        pos.current_lock_row_loc,\r
+                        true, init_forUpdate, \r
+                        lock_operation);\r
+            }\r
+            else\r
+            {\r
+                // Don't need to lock the "previous key" but still need to get\r
+                // the scan lock to protect the position in the btree.\r
+\r
+                latch_released =\r
+                    !this.getLockingPolicy().lockScan(\r
+                        pos.current_leaf,   // the page we are positioned on.\r
+                        (ControlRow) null,  // no other page to unlatch\r
+                        false,              // lock for read.\r
+                        lock_operation);    // not used.\r
+            }\r
+\r
+            // special test to see if latch release code works\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                latch_released = \r
+                    test_errors(\r
+                        this,\r
+                        "BTreeScan_positionAtStartPosition", true,\r
+                        this.getLockingPolicy(), \r
+                        pos.current_leaf, latch_released);\r
+            }\r
+\r
+            if (latch_released)\r
+            {\r
+                // lost latch on pos.current_leaf, search the tree again.\r
+                pos.current_leaf = null;\r
+                continue;\r
+            }\r
+            else\r
+            {\r
+                // success! got all the locks, while holding the latch.\r
+                break;\r
+            }\r
+        }\r
+\r
+        this.scan_state         = SCAN_INPROGRESS;\r
+        pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();\r
+               pos.current_slot        = pos.current_slot;\r
+\r
+        if (SanityManager.DEBUG)\r
+            SanityManager.ASSERT(pos.current_leaf != null);\r
+       }\r
+\r
+    /**\r
+     * Position scan at "start" position for a backward scan.\r
+     * <p>\r
+     * Positions the scan to the slot just after the first record to be \r
+     * returned from the backward scan.  Returns the start page latched, and \r
+     * sets "current_slot" to the slot number just right of the first slot\r
+     * to return.\r
+     * <p>\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    protected void positionAtStartForBackwardScan(\r
+    BTreeRowPosition    pos)\r
+        throws StandardException\r
+       {\r
+               boolean         exact;\r
+\r
+        // This routine should only be called from first next() call //\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(\r
+                (this.scan_state == SCAN_INIT) || \r
+                (this.scan_state == SCAN_HOLD_INIT));\r
+\r
+            SanityManager.ASSERT(pos.current_rh          == null);\r
+            SanityManager.ASSERT(pos.current_positionKey         == null);\r
+            SanityManager.ASSERT(pos.current_scan_pageno == 0);\r
+        }\r
+\r
+        // Loop until you can lock the row previous to the first row to be\r
+        // returned by the scan, while holding the page latched, without\r
+        // waiting.  If you have to wait, drop the latch, wait for the lock -\r
+        // which makes it likely if you wait for the lock you will loop just\r
+        // once, find the same lock satisfies the search and since you already\r
+        // have the lock it will be granted.\r
+        while (true)\r
+        {\r
+            // Find the starting page and row slot, must start at root and\r
+            // search either for leftmost leaf, or search for specific key.\r
+            ControlRow root = ControlRow.get(this, BTree.ROOTPAGEID); \r
+\r
+            // include search of tree in page visited stats.\r
+            stat_numpages_visited += root.getLevel() + 1;\r
+\r
+            if (init_startKeyValue == null)\r
+            {\r
+                // No start given, position at last slot + 1 of rightmost leaf \r
+                pos.current_leaf = (LeafControlRow) root.searchRight(this);\r
+\r
+                pos.current_slot = pos.current_leaf.page.recordCount();\r
+                exact     = false;\r
+            }\r
+            else\r
+            {\r
+                /*\r
+                if (SanityManager.DEBUG)\r
+                    SanityManager.THROWASSERT(\r
+                        "Code not ready yet for positioned backward scans.");\r
+                        */\r
+\r
+\r
+                if (SanityManager.DEBUG)\r
+                    SanityManager.ASSERT(\r
+                        (init_startSearchOperator == ScanController.GE) ||\r
+                        (init_startSearchOperator == ScanController.GT));\r
+\r
+                // Search for the starting row.\r
+\r
+                SearchParameters sp = new SearchParameters(\r
+                    init_startKeyValue, \r
+                    ((init_startSearchOperator == ScanController.GE) ? \r
+                        SearchParameters.POSITION_RIGHT_OF_PARTIAL_KEY_MATCH : \r
+                        SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH),\r
+                    init_template, this, false);\r
+\r
+                pos.current_leaf = (LeafControlRow) root.search(sp);\r
+\r
+                pos.current_slot = sp.resultSlot;\r
+                exact     = sp.resultExact;\r
+\r
+                // The way that backward scans are used, the caller calls next()\r
+                // to position on the first row.  If the result of the\r
+                // search that found the starting page and slot was not\r
+                // exact, then the page/slot will refer to the row before\r
+                // the first qualifying row.  The first call to next()\r
+                // will therefore move to the first (potentially) qualifying\r
+                // row.  However, if the search was exact, then we don't\r
+                // want to move the position on the first call to next.\r
+                // In that case, by decrementing the slot, the first call\r
+                // to next will put us back    on the starting row.\r
+\r
+\r
+                if (exact)\r
+                {\r
+                    // the search has found exactly the start position key \r
+                    if (init_startSearchOperator == ScanController.GE)\r
+                    {\r
+                        // insure backward scan returns this row by moving\r
+                        // slot to one after this row.\r
+                        pos.current_slot++;\r
+                    }\r
+                    else\r
+                    {\r
+                        // no work necessary leave startslot positioned on the\r
+                        // row, we will skip this record\r
+                        if (SanityManager.DEBUG)\r
+                            SanityManager.ASSERT(\r
+                                init_startSearchOperator == ScanController.GT);\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    // the search positioned one before the start position key,\r
+                    // move it to one "after"\r
+                    pos.current_slot++;\r
+                }\r
+            }\r
+\r
+            boolean latch_released = \r
+                !this.getLockingPolicy().lockScanRow(\r
+                    this, this.getConglomerate(), pos,\r
+                    true, \r
+                    init_lock_fetch_desc,\r
+                    pos.current_lock_template,\r
+                    pos.current_lock_row_loc,\r
+                    true, init_forUpdate, lock_operation);\r
+\r
+            // special test to see if latch release code works\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                latch_released = \r
+                    test_errors(\r
+                        this,\r
+                        "BTreeScan_positionAtStartPosition", true,\r
+                        this.getLockingPolicy(), pos.current_leaf, latch_released);\r
+            }\r
+\r
+            if (latch_released)\r
+            {\r
+                // lost latch on pos.current_leaf, search the tree again.\r
+                pos.current_leaf = null;\r
+                continue;\r
+            }\r
+            else\r
+            {\r
+                // success! got all the locks, while holding the latch.\r
+                break;\r
+            }\r
+        }\r
+\r
+        this.scan_state          = SCAN_INPROGRESS;\r
+        pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();\r
+\r
+        if (SanityManager.DEBUG)\r
+            SanityManager.ASSERT(pos.current_leaf != null);\r
+\r
+        // System.out.println("backward scan end start position: " +\r
+        //       " current_slot = " + this.current_slot );\r
+       }\r
+\r
+    /**\r
+     * Position scan to 0 slot on next page.\r
+     * <p>\r
+     * Position to next page, keeping latch on previous page until we have \r
+     * latch on next page.  This routine releases the latch on current_page\r
+     * once it has successfully gotten both the latch on the next page and\r
+     * the scan lock on the next page.\r
+     *\r
+     * @param pos           current row position of the scan.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    protected void positionAtNextPage(\r
+    BTreeRowPosition    pos)\r
+        throws StandardException\r
+    {\r
+        // RESOLVE (mikem) - not sure but someday in the future this\r
+        // assert may not be true, but for now we always have the scan\r
+        // lock when we call this routine.\r
+        if (SanityManager.DEBUG)\r
+            SanityManager.ASSERT(pos.current_scan_pageno != 0);\r
+\r
+        while (true)\r
+        {\r
+            if ((pos.next_leaf = \r
+                 (LeafControlRow) pos.current_leaf.getRightSibling(this)) == null)\r
+            {\r
+                break;\r
+            }\r
+\r
+            boolean latch_released = \r
+                !this.getLockingPolicy().lockScan(\r
+                    pos.next_leaf,\r
+                    (LeafControlRow) null, // no other latch currently\r
+                    false /* not for update */,\r
+                    ConglomerateController.LOCK_READ); // get read scan lock.\r
+\r
+            // TESTING CODE:\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                latch_released = \r
+                    test_errors(\r
+                        this,\r
+                        "BTreeScan_positionAtNextPage", true,\r
+                        this.getLockingPolicy(), pos.next_leaf, latch_released);\r
+            }\r
+\r
+            if (!latch_released)\r
+            {\r
+                break;\r
+            }\r
+        }\r
+\r
+        // Now that we either have both latch and scan lock on next leaf, or \r
+        // there is no next leaf we can release scan and latch on current page.\r
+        if (SanityManager.DEBUG)\r
+        {\r
+                       if (pos.current_scan_pageno != pos.current_leaf.page.getPageNumber())\r
+                               SanityManager.THROWASSERT(\r
+                "pos.current_scan_pageno = " + pos.current_scan_pageno +\r
+                "pos.current_leaf = " + pos.current_leaf);\r
+        }\r
+\r
+        // unlock the previous row if doing read.\r
+        if (pos.current_rh != null)\r
+        {\r
+            this.getLockingPolicy().unlockScanRecordAfterRead(\r
+                pos, init_forUpdate);\r
+        }\r
+\r
+        this.getLockingPolicy().unlockScan(\r
+            pos.current_leaf.page.getPageNumber());\r
+        pos.current_leaf.release();\r
+        pos.current_leaf        = pos.next_leaf;\r
+\r
+        pos.current_scan_pageno = \r
+            (pos.next_leaf == null) ? 0 : pos.next_leaf.page.getPageNumber();\r
+\r
+        // set up for scan to continue at beginning of next page.\r
+        pos.current_slot        = Page.FIRST_SLOT_NUMBER;\r
+        pos.current_rh          = null;\r
+    }\r
+\r
+       /**\r
+       Position scan at "start" position.\r
+       <p>\r
+    Positions the scan to the slot just before the first record to be returned\r
+    from the scan.  Returns the start page latched, and sets "current_slot" to\r
+    the slot number.\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+       **/\r
+    abstract void positionAtStartPosition(\r
+    BTreeRowPosition    pos)\r
+        throws StandardException;\r
+\r
+\r
+    /**\r
+     * Do any necessary work to complete the scan.\r
+     *\r
+     * @param pos           current row position of the scan.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    protected void positionAtDoneScanFromClose(\r
+    BTreeRowPosition    pos)\r
+        throws StandardException\r
+    {\r
+        // call unlockScanRecordAfterRead() before closing, currently\r
+        // this is only important for releasing RR locks on non-qualified\r
+        // rows.   \r
+        //\r
+        // Otherwise the correct behavior happens as part of the close, ie.:\r
+        //\r
+        //     for READ_UNCOMMITTED there is no lock to release, \r
+        //     for READ_COMMITTED   all read locks will be released, \r
+        //     for REPEATABLE_READ or SERIALIZABLE no locks are released.\r
+\r
+        if ((pos.current_rh != null) && !pos.current_rh_qualified)\r
+        {\r
+            if (pos.current_leaf == null || pos.current_leaf.page == null)\r
+            {\r
+                // If we are being called from a "normal" close then there\r
+                // will be no latch on current_leaf, get it and do the the\r
+                // unlock.  We may be called sometimes, after an error where\r
+                // we may have the latch, in this case the transaction is about\r
+                // to be backed out anyway so don't worry about doing this \r
+                // unlock (thus why we only do the following code if we\r
+                // "don't" have lock, ie. pos.current_leaf== null).\r
+\r
+                if (!reposition(pos, false))\r
+                {\r
+                    if (SanityManager.DEBUG)\r
+                    {\r
+                        SanityManager.THROWASSERT(\r
+                            "can not fail while holding update row lock.");\r
+                    }\r
+                }\r
+\r
+                this.getLockingPolicy().unlockScanRecordAfterRead(\r
+                    pos, init_forUpdate);\r
+\r
+                pos.current_rh   = null;\r
+                pos.current_leaf.release();\r
+                pos.current_leaf = null;\r
+            }\r
+        }\r
+\r
+\r
+        // Need to do this unlock in any case, until lock manager provides\r
+        // a way to release locks associated with a compatibility space.  This\r
+        // scan lock is special, as it is a lock on the btree container rather\r
+        // than the heap container.  The open container on the btree actually\r
+        // has a null locking policy so the close of that container does not\r
+        // release this lock, need to explicitly unlock it here or when the\r
+        // scan is closed as part of the abort the lock will not be released.\r
+        if (pos.current_scan_pageno != 0)\r
+        {\r
+            this.getLockingPolicy().unlockScan(pos.current_scan_pageno);\r
+            pos.current_scan_pageno = 0;\r
+        }\r
+\r
+        pos.current_slot = Page.INVALID_SLOT_NUMBER;\r
+        pos.current_rh   = null;\r
+        pos.current_positionKey  = null;\r
+        this.scan_state   = SCAN_DONE;\r
+\r
+        return;\r
+    }\r
+\r
+    /**\r
+     * Do work necessary to close a scan.\r
+     * <p>\r
+     * This routine can only be called "inline" from other btree routines,\r
+     * as it counts on the state of the pos to be correct.\r
+     * <p>\r
+     * Closing a scan from close() must handle long jumps from exceptions\r
+     * where the state of pos may not be correct.  The easiest case is\r
+     * a lock timeout which has caused us not to have a latch on a page,\r
+     * but pos still thinks there is a latch.  This is the easiest but\r
+     * other exceptions can also caused the same state at close() time.\r
+     **/\r
+    protected void positionAtDoneScan(\r
+    BTreeRowPosition    pos)\r
+        throws StandardException\r
+    {\r
+\r
+        // Need to do this unlock in any case, until lock manager provides\r
+        // a way to release locks associated with a compatibility space.  This\r
+        // scan lock is special, as it is a lock on the btree container rather\r
+        // than the heap container.  The open container on the btree actually\r
+        // has a null locking policy so the close of that container does not\r
+        // release this lock, need to explicitly unlock it here or when the\r
+        // scan is closed as part of the abort the lock will not be released.\r
+        if (pos.current_scan_pageno != 0)\r
+        {\r
+            this.getLockingPolicy().unlockScan(pos.current_scan_pageno);\r
+            pos.current_scan_pageno = 0;\r
+        }\r
+\r
+        pos.current_slot        = Page.INVALID_SLOT_NUMBER;\r
+        pos.current_rh          = null;\r
+        pos.current_positionKey = null;\r
+        this.scan_state         = SCAN_DONE;\r
+\r
+        return;\r
+    }\r
+\r
+\r
+    /**\r
+     * process_qualifier - Determine if a row meets all qualifier conditions.\r
+     * <p>\r
+     * Check all qualifiers in the qualifier array against row.  Return true\r
+     * if all compares specified by the qualifier array return true, else\r
+     * return false.\r
+     * <p>\r
+     * It is up to caller to make sure qualifier list is non-null.\r
+     *\r
+     * @param row      The row with the same partial column list as the\r
+     *                 row returned by the current scan.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     */\r
+    protected boolean process_qualifier(\r
+    DataValueDescriptor[]     row) \r
+        throws StandardException\r
+    {\r
+        boolean     row_qualifies = true;\r
+        Qualifier   q;\r
+\r
+        // Process the 2-d qualifier which is structured as follows:\r
+        //\r
+        // A two dimensional array is to be used to pass around a AND's and OR's\r
+        // in conjunctive normal form (CNF).  The top slot of the 2 dimensional\r
+        // array is optimized for the more frequent where no OR's are present.  \r
+        // The first array slot is always a list of AND's to be treated as \r
+        // described above for single dimensional AND qualifier arrays.  The \r
+        // subsequent slots are to be treated as AND'd arrays or OR's.  Thus \r
+        // the 2 dimensional array qual[][] argument is to be treated as the \r
+        // following, note if qual.length = 1 then only the first array is \r
+        // valid and // it is and an array of and clauses:\r
+        //\r
+        // (qual[0][0] and qual[0][0] ... and qual[0][qual[0].length - 1])\r
+        // and\r
+        // (qual[1][0] or  qual[1][1] ... or  qual[1][qual[1].length - 1])\r
+        // and\r
+        // (qual[2][0] or  qual[2][1] ... or  qual[2][qual[2].length - 1])\r
+        // ...\r
+        // and\r
+        // (qual[qual.length - 1][0] or  qual[1][1] ... or  qual[1][2])\r
+\r
+        // First do the qual[0] which is an array of qualifer terms.\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            // routine should not be called if there is no qualifier\r
+            SanityManager.ASSERT(this.init_qualifier != null);\r
+            SanityManager.ASSERT(this.init_qualifier.length > 0);\r
+        }\r
+\r
+        for (int i = 0; i < this.init_qualifier[0].length; i++)\r
+        {\r
+            // process each AND clause \r
+\r
+            row_qualifies = false;\r
+\r
+            // process each OR clause.\r
+\r
+            q = this.init_qualifier[0][i];\r
+\r
+            // Get the column from the possibly partial row, of the \r
+            // q.getColumnId()'th column in the full row.\r
+            DataValueDescriptor columnValue = row[q.getColumnId()];\r
+\r
+            row_qualifies =\r
+                columnValue.compare(\r
+                    q.getOperator(),\r
+                    q.getOrderable(),\r
+                    q.getOrderedNulls(),\r
+                    q.getUnknownRV());\r
+\r
+            if (q.negateCompareResult())\r
+                row_qualifies = !row_qualifies;\r
+\r
+            // Once an AND fails the whole Qualification fails - do a return!\r
+            if (!row_qualifies)\r
+                return(false);\r
+        }\r
+\r
+        // all the qual[0] and terms passed, now process the OR clauses\r
+\r
+        for (int and_idx = 1; and_idx < this.init_qualifier.length; and_idx++)\r
+        {\r
+            // process each AND clause \r
+\r
+            row_qualifies = false;\r
+\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                // Each OR clause must be non-empty.\r
+                SanityManager.ASSERT(this.init_qualifier[and_idx].length > 0);\r
+            }\r
+\r
+            for (int or_idx = 0; \r
+                 or_idx < this.init_qualifier[and_idx].length; or_idx++)\r
+            {\r
+                // process each OR clause.\r
+\r
+                q = this.init_qualifier[and_idx][or_idx];\r
+\r
+                // Get the column from the possibly partial row, of the \r
+                // q.getColumnId()'th column in the full row.\r
+                DataValueDescriptor columnValue = row[q.getColumnId()];\r
+\r
+                row_qualifies =\r
+                    columnValue.compare(\r
+                        q.getOperator(),\r
+                        q.getOrderable(),\r
+                        q.getOrderedNulls(),\r
+                        q.getUnknownRV());\r
+\r
+                if (q.negateCompareResult())\r
+                    row_qualifies = !row_qualifies;\r
+\r
+                // once one OR qualifies the entire clause is TRUE\r
+                if (row_qualifies)\r
+                    break;\r
+            }\r
+\r
+            if (!row_qualifies)\r
+                break;\r
+        }\r
+\r
+        return(row_qualifies);\r
+    }\r
+\r
+\r
+    /**\r
+     * Reposition the scan leaving and reentering the access layer.\r
+     * <p>\r
+     * When a scan leaves access it saves the RecordHandle of the record\r
+     * on the page.  There are 2 cases to consider when trying to reposition\r
+     * the scan when re-entering access:\r
+     *     o ROW has not moved off the page.\r
+     *       If the row has not moved then the RecordHandle we have saved\r
+     *       away is valid, and we just call RawStore to reposition on that\r
+     *       RecordHandle (RawStore takes care of the row moving within\r
+     *       the page).\r
+     *     o ROW has moved off the page.\r
+     *       This can only happen in the case of a btree split.  In that\r
+     *       case the splitter will have caused all scans positioned on \r
+     *       this page within the same transaction to save a copy of the\r
+     *       row that the scan was positioned on.  Then to reposition the\r
+     *       scan it is necessary to research the tree from the top using\r
+     *       the copy of the row.\r
+     *\r
+     * If the scan has saved it's position by key (and thus has given up the\r
+     * scan lock on the page), there are a few cases where it is possible that\r
+     * the key no longer exists in the table.  In the case of a scan held \r
+     * open across commit it is easy to imagine that the row the scan was \r
+     * positioned on could be deleted and subsequently purged from the table \r
+     * all before the scan resumes.  Also in the case of read uncommitted \r
+     * the scan holds no lock on the current row, so it could be purged -\r
+     * in the following scenario for instance:  read uncommitted transaction 1\r
+     * opens scan and positions on row (1,2), transaction 2 deletes (1,2) and\r
+     * commits, transaction 1 inserts (1,3) which goes to same page as (1,2)\r
+     * and is going to cause a split, transaction 1 saves scan position as\r
+     * key, gives up scan lock and then purges row (1, 2), when transaction \r
+     * 1 resumes scan (1, 2) no longer exists.  missing_row_for_key_ok \r
+     * parameter is added as a sanity check to make sure it ok that \r
+     * repositioning does not go to same row that we were repositioned on.\r
+     *\r
+     *\r
+     *\r
+     * @param   pos                     position to set the scan to.\r
+     *\r
+     * @param   missing_row_for_key_ok  if true and exact key is not found then\r
+     *                                  scan is just set to key just left of\r
+     *                                  the key (thus a next will move to the\r
+     *                                  key just after "pos")\r
+     *\r
+     * @return  returns true if scan has been repositioned successfully, else\r
+     *          returns false if the position key could not be found and\r
+     *          missing_row_for_key_ok was false indicating that scan could\r
+     *          only be positioned on the exact key match.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+       protected boolean reposition(\r
+    BTreeRowPosition pos,\r
+    boolean          missing_row_for_key_ok)\r
+        throws StandardException\r
+       {\r
+        // RESOLVE (mikem) - performance - we need to do a buffer manager\r
+        // get for every row returned from the scan.  It may be better to\r
+        // allow a reference to the page with no latch (ie. a fixed bit).\r
+\r
+        if (this.scan_state != SCAN_INPROGRESS)\r
+        {\r
+            throw StandardException.newException(\r
+                SQLState.BTREE_SCAN_NOT_POSITIONED, \r
+                new Integer(this.scan_state));\r
+        }\r
+\r
+        // Either current_rh or positionKey is valid - the other is null.\r
+        if (SanityManager.DEBUG)\r
+        {\r
+                       if ((pos.current_rh == null) != (pos.current_positionKey != null))\r
+               SanityManager.THROWASSERT(\r
+                       "pos.current_rh  = (" + pos.current_rh + "), " +\r
+                       "pos.current_positionKey = (" + \r
+                    pos.current_positionKey + ").");\r
+        }\r
+\r
+        if (!((pos.current_rh == null) == (pos.current_positionKey != null)))\r
+        {\r
+            throw StandardException.newException(\r
+                    SQLState.BTREE_SCAN_INTERNAL_ERROR, \r
+                    new Boolean(pos.current_rh == null), \r
+                    new Boolean(pos.current_positionKey == null));\r
+        }\r
+\r
+        if (pos.current_positionKey == null)\r
+        {\r
+            // Reposition to remembered spot on page.\r
+            if (SanityManager.DEBUG)\r
+                SanityManager.ASSERT(pos.current_scan_pageno != 0);\r
+\r
+            pos.current_leaf = (LeafControlRow)\r
+                ControlRow.get(this, pos.current_rh.getPageNumber());\r
+            pos.current_slot =\r
+                pos.current_leaf.page.getSlotNumber(pos.current_rh);\r
+        }\r
+        else\r
+        {\r
+            // RESOLVE (mikem) - not sure but someday in the future this\r
+            // assert may not be true, but for now we always release the \r
+            // scan lock when we save the row away as the current position.\r
+            if (SanityManager.DEBUG)\r
+                SanityManager.ASSERT(pos.current_scan_pageno == 0);\r
+\r
+            SearchParameters sp =\r
+                new SearchParameters(\r
+                    pos.current_positionKey, \r
+                    // this is a full key search, so this arg is not used.\r
+                    SearchParameters.POSITION_LEFT_OF_PARTIAL_KEY_MATCH,\r
+                    init_template, this, false);\r
+\r
+            // latch/lock loop, continue until you can get scan lock on page\r
+            // while holding page latched without waiting.\r
+\r
+\r
+            boolean latch_released;\r
+            do\r
+            {\r
+                pos.current_leaf = (LeafControlRow)\r
+                    ControlRow.get(this, BTree.ROOTPAGEID).search(sp);\r
+\r
+                if (sp.resultExact || missing_row_for_key_ok)\r
+                {\r
+                    // RESOLVE (mikem) - we could have a scan which always \r
+                    // maintained it's position by key value, or we could \r
+                    // optimize and delay this lock until we were about to \r
+                    // give up the latch.  But it is VERY likely we will get \r
+                    // the lock since we have the latch on the page.\r
+                    //\r
+                    // In order to be successfully positioned we must get the \r
+                    // scan lock again.\r
+                    latch_released = \r
+                        !this.getLockingPolicy().lockScan(\r
+                            pos.current_leaf, \r
+                            (LeafControlRow) null, // no other latch currently\r
+                            false /* not for update */,\r
+                            ConglomerateController.LOCK_READ); // read lock on scan position\r
+\r
+                    // TESTING CODE:\r
+                    if (SanityManager.DEBUG)\r
+                    {\r
+                        latch_released = \r
+                            test_errors(\r
+                                this,\r
+                                "BTreeScan_reposition", true, \r
+                                this.getLockingPolicy(),\r
+                                pos.current_leaf, latch_released);\r
+                    }\r
+                }\r
+                else\r
+                {\r
+                    // Did not find key to exactly position on.\r
+\r
+                    pos.current_leaf.release();\r
+                    pos.current_leaf = null;\r
+                    return(false);\r
+                }\r
+\r
+            } while (latch_released);\r
+\r
+            pos.current_scan_pageno = pos.current_leaf.page.getPageNumber();\r
+            pos.current_slot        = sp.resultSlot;\r
+            pos.current_positionKey = null;\r
+        }\r
+\r
+        return(true);\r
+       }\r
+\r
+       /*\r
+       ** Public Methods of BTreeScan\r
+       */\r
+\r
+\r
+       /**\r
+       Initialize the scan for use.\r
+       <p>\r
+       Any changes to this method may have to be reflected in close as well.\r
+    <p>\r
+    The btree init opens the container (super.init), and stores away the\r
+    state of the qualifiers.  The actual searching for the first position\r
+    is delayed until the first next() call.\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+       **/\r
+       public void init(\r
+    TransactionManager              xact_manager,\r
+    Transaction                     rawtran,\r
+    boolean                         hold,\r
+    int                             open_mode,\r
+    int                             lock_level,\r
+    BTreeLockingPolicy              btree_locking_policy,\r
+    FormatableBitSet                scanColumnList,\r
+    DataValueDescriptor[]                  startKeyValue,\r
+    int                             startSearchOperator,\r
+    Qualifier                       qualifier[][],\r
+    DataValueDescriptor[]                  stopKeyValue,\r
+    int                             stopSearchOperator,\r
+    BTree                           conglomerate,\r
+    LogicalUndo                     undo,\r
+    StaticCompiledOpenConglomInfo   static_info,\r
+    DynamicCompiledOpenConglomInfo  dynamic_info)\r
+        throws StandardException\r
+       {\r
+               super.init(\r
+            xact_manager, xact_manager, (ContainerHandle) null, rawtran,\r
+            hold,\r
+            open_mode, lock_level, btree_locking_policy, \r
+            conglomerate, undo, dynamic_info);\r
+\r
+\r
+        this.init_rawtran               = rawtran;\r
+        this.init_forUpdate             = \r
+            ((open_mode & ContainerHandle.MODE_FORUPDATE) == \r
+                 ContainerHandle.MODE_FORUPDATE);\r
+\r
+               // Keep track of whether this scan should use update locks.\r
+               this.init_useUpdateLocks = \r
+            ((open_mode &\r
+                ContainerHandle.MODE_USE_UPDATE_LOCKS) != 0);\r
+\r
+               this.init_hold                  = hold;\r
+\r
+               this.init_template              = \r
+            runtime_mem.get_template(getRawTran());\r
+\r
+        this.init_scanColumnList        = scanColumnList;\r
+\r
+        this.init_lock_fetch_desc        = \r
+            RowUtil.getFetchDescriptorConstant(init_template.length - 1);\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(\r
+                init_lock_fetch_desc.getMaxFetchColumnId() == \r
+                    (init_template.length - 1));\r
+            SanityManager.ASSERT(\r
+                (init_lock_fetch_desc.getValidColumnsArray())[init_template.length - 1] == 1); \r
+        }\r
+\r
+        // note that we don't process qualifiers in btree fetch's\r
+        this.init_fetchDesc             = \r
+            new FetchDescriptor(\r
+                init_template.length, init_scanColumnList,(Qualifier[][]) null);\r
+\r
+        initScanParams( \r
+            startKeyValue, startSearchOperator, \r
+            qualifier, stopKeyValue, stopSearchOperator);\r
+\r
+        \r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(\r
+                TemplateRow.checkColumnTypes(\r
+                    getRawTran().getDataValueFactory(),\r
+                    this.getConglomerate().format_ids, \r
+                    this.getConglomerate().collation_ids,\r
+                    init_template));\r
+        }\r
+\r
+        // System.out.println("initializing scan:" + this);\r
+\r
+        // initialize default locking operation for the scan.\r
+        this.lock_operation = \r
+            (init_forUpdate ? \r
+                ConglomerateController.LOCK_UPD : \r
+                ConglomerateController.LOCK_READ);\r
+\r
+        if (init_useUpdateLocks)\r
+            this.lock_operation |= ConglomerateController.LOCK_UPDATE_LOCKS;\r
+\r
+        // System.out.println("Btree scan: " + this);\r
+       }\r
+\r
+\r
+\r
+       /*\r
+       ** Methods of ScanController\r
+       */\r
+\r
+    /**\r
+    Close the scan.\r
+    **/\r
+    public void close()\r
+        throws StandardException\r
+       {\r
+        // Scan is closed, make sure no access to any state variables\r
+        positionAtDoneScanFromClose(scan_position);\r
+\r
+               super.close();\r
+\r
+        // null out so that these object's can get GC'd earlier.\r
+        this.init_rawtran       = null;\r
+        this.init_template      = null;\r
+        this.init_startKeyValue = null;\r
+        this.init_qualifier     = null;\r
+        this.init_stopKeyValue  = null;\r
+\r
+        this.getXactMgr().closeMe(this);\r
+       }\r
+\r
+    /**\r
+    Delete the row at the current position of the scan.\r
+       @see ScanController#delete\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+    **/\r
+    public boolean delete()\r
+               throws StandardException\r
+       {\r
+        boolean     ret_val      = false;\r
+\r
+        if (scan_state != SCAN_INPROGRESS)\r
+            throw StandardException.newException(\r
+                SQLState.AM_SCAN_NOT_POSITIONED);\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(this.container != null,\r
+                "BTreeScan.delete() called on a closed scan.");\r
+            SanityManager.ASSERT(init_forUpdate);\r
+        }\r
+\r
+        try\r
+        {\r
+            // Get current page of scan, with latch.\r
+            if (!reposition(scan_position, false))\r
+            {\r
+                throw StandardException.newException(\r
+                        SQLState.AM_RECORD_NOT_FOUND,\r
+                        new Long(err_containerid),\r
+                        new Long(scan_position.current_rh.getId()));\r
+            }\r
+\r
+\r
+            if (init_useUpdateLocks)\r
+            {\r
+                // RESOLVE (mikem) - I don't think lockScanRow() is the right\r
+                // thing to call.\r
+\r
+                // if we are doing update locking, then we got an U lock on\r
+                // this row when the scan positioned on it, but now that we\r
+                // are doing a delete on the current position we need to upgrade\r
+                // the lock to X.\r
+                boolean latch_released =\r
+                    !this.getLockingPolicy().lockScanRow(\r
+                        this, this.getConglomerate(), scan_position,\r
+                        false, \r
+                        init_lock_fetch_desc,\r
+                        scan_position.current_lock_template,\r
+                        scan_position.current_lock_row_loc,\r
+                        false, init_forUpdate, lock_operation);\r
+\r
+                if (latch_released)\r
+                {\r
+                    // lost latch on page in order to wait for row lock.\r
+                    // Because we have scan lock on page, we need only\r
+                    // call reposition() which will use the saved record\r
+                    // handle to reposition to the same spot on the page.\r
+                    // We don't have to search the\r
+                    // tree again, as we have the a scan lock on the page\r
+                    // which means the current_rh is valid to reposition on.\r
+                    if (reposition(scan_position, false))\r
+                    {\r
+                        throw StandardException.newException(\r
+                                SQLState.AM_RECORD_NOT_FOUND,\r
+                                new Long(err_containerid),\r
+                                new Long(scan_position.current_rh.getId()));\r
+                    }\r
+                }\r
+            }\r
+\r
+            if (SanityManager.DEBUG) \r
+            {\r
+                // DERBY-2197: Assume no row locking here. If locking policy\r
+                // requires row locking, we would need to obtain a row lock at\r
+                // this point.\r
+                SanityManager.ASSERT(\r
+                    (container.getLockingPolicy().getMode() !=\r
+                         LockingPolicy.MODE_RECORD),\r
+                    "Locking policy requires row locking.");\r
+            }\r
+\r
+            if (scan_position.current_leaf.page.isDeletedAtSlot(\r
+                    scan_position.current_slot)) \r
+            {\r
+                ret_val = false;\r
+            } \r
+            else \r
+            {\r
+                scan_position.current_leaf.page.deleteAtSlot(\r
+                    scan_position.current_slot, true, this.btree_undo);\r
+                ret_val = true;\r
+            }\r
+\r
+            // See if we just deleted the last row on the page, in a btree a\r
+            // page with all rows still has 1 left - the control row.\r
+               // Do not reclaim the root page of the btree if there are no \r
+            // children since we were doing too many post commit actions in a \r
+            // benchmark which does an insert/commit/delete/commit operations \r
+            // in a single user system.  Now with this change the work will \r
+            // move to the user thread which does the insert and finds no space\r
+            // on the root page.  In that case it will try a split, which \r
+            // automatically first checks if there is committed deleted space\r
+            // that can be reclaimed.\r
+\r
+            if (scan_position.current_leaf.page.nonDeletedRecordCount() == 1 &&\r
+                !(scan_position.current_leaf.getIsRoot() && \r
+                 scan_position.current_leaf.getLevel() == 0)) \r
+            {\r
+                this.getXactMgr().addPostCommitWork(new BTreePostCommit(\r
+                    this.getXactMgr().getAccessManager(),\r
+                    this.getConglomerate(),\r
+                    scan_position.current_leaf.page.getPageNumber()));\r
+            }\r
+        }\r
+        finally\r
+        {\r
+            if (scan_position.current_leaf != null)\r
+            {\r
+                // release latch on page\r
+                scan_position.current_leaf.release();\r
+                scan_position.current_leaf = null;\r
+            }\r
+        }\r
+\r
+        return(ret_val);\r
+       }\r
+\r
+    /**\r
+     * A call to allow client to indicate that current row does not qualify.\r
+     * <p>\r
+     * Indicates to the ScanController that the current row does not\r
+     * qualify for the scan.  If the isolation level of the scan allows, \r
+     * this may result in the scan releasing the lock on this row.\r
+     * <p>\r
+     * Note that some scan implimentations may not support releasing locks on \r
+     * non-qualifying rows, or may delay releasing the lock until sometime\r
+     * later in the scan (ie. it may be necessary to keep the lock until \r
+     * either the scan is repositioned on the next row or page).\r
+     * <p>\r
+     * This call should only be made while the scan is positioned on a current\r
+     * valid row.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    public void didNotQualify()\r
+        throws StandardException\r
+    {\r
+    }\r
+\r
+\r
+    /**\r
+     * Returns true if the current position of the scan still qualifies\r
+     * under the set of qualifiers passed to the openScan().  When called\r
+     * this routine will reapply all qualifiers against the row currently\r
+     * positioned and return true if the row still qualifies.  If the row\r
+     * has been deleted or no longer passes the qualifiers then this routine\r
+     * will return false.\r
+     * <p>\r
+     * This case can come about if the current scan\r
+     * or another scan on the same table in the same transaction\r
+     * deleted the row or changed columns referenced by the qualifier after\r
+     * the next() call which positioned the scan at this row.\r
+     * <p>\r
+     * Note that for comglomerates which don't support update, like btree's,\r
+     * there is no need to recheck the qualifiers.\r
+     * <p>\r
+     * The results of a fetch() performed on a scan positioned on\r
+     * a deleted row are undefined.\r
+     * <p>\r
+        * @exception StandardException Standard exception policy.\r
+    **/\r
+    public boolean doesCurrentPositionQualify()\r
+               throws StandardException\r
+    {\r
+        if (scan_state != SCAN_INPROGRESS)\r
+            throw StandardException.newException(\r
+                SQLState.AM_SCAN_NOT_POSITIONED);\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(this.container != null,\r
+            "BTreeScan.doesCurrentPositionQualify() called on a closed scan.");\r
+        }\r
+\r
+        try\r
+        {\r
+            // Get current page of scan, with latch\r
+            if (!reposition(scan_position, false))\r
+            {\r
+                // TODO - write unit test to get here, language always calls\r
+                // isCurrentPositionDeleted() right before calling this, so\r
+                // hard to write .sql test to exercise this.\r
+\r
+                // if reposition fails it means the position of the scan\r
+                // has been purged from the table - for example if this is\r
+                // a uncommitted read scan and somehow the row was purged \r
+                // since the last positioning.\r
+\r
+                return(false);\r
+            }\r
+\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                SanityManager.ASSERT(\r
+                    scan_position.current_leaf.page.fetchNumFieldsAtSlot(\r
+                        scan_position.current_slot) > 1);\r
+            }\r
+\r
+            // Since btree row don't get updated, the only way a current\r
+            // position may not qualify is if it got deleted.\r
+            return(\r
+                !scan_position.current_leaf.page.isDeletedAtSlot(\r
+                    scan_position.current_slot));\r
+        }\r
+        finally\r
+        {\r
+\r
+            if (scan_position.current_leaf != null)\r
+            {\r
+                // release latch on page.\r
+                scan_position.current_leaf.release();\r
+                scan_position.current_leaf = null;\r
+            }\r
+        }\r
+    }\r
+\r
+\r
+    /**\r
+     * Fetch the row at the current position of the Scan.\r
+     * \r
+     * @param row The row into which the value of the current \r
+     * position in the scan is to be stored.\r
+     * @param qualify indicates whether the qualifiers should be applied.\r
+     * \r
+     * @exception  StandardException  Standard exception policy.\r
+     */\r
+       private void fetch(DataValueDescriptor[] row, boolean qualify)\r
+               throws StandardException\r
+       {\r
+        if (scan_state != SCAN_INPROGRESS)\r
+            throw StandardException.newException(\r
+                SQLState.AM_SCAN_NOT_POSITIONED);\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(this.container != null,\r
+                "BTreeScan.fetch() called on a closed scan.");\r
+            \r
+            TemplateRow.checkPartialColumnTypes(\r
+                this.getConglomerate().format_ids, \r
+                init_scanColumnList, (int []) null, row);\r
+        }\r
+\r
+        try\r
+        {\r
+            // Get current page of scan, with latch\r
+            if (!reposition(scan_position, false))\r
+            {\r
+                // TODO - write unit test to get here, language always calls\r
+                // isCurrentPositionDeleted() right before calling this, so\r
+                // hard to write .sql test to exercise this.\r
+\r
+                throw StandardException.newException(\r
+                        SQLState.AM_RECORD_NOT_FOUND,\r
+                        new Long(err_containerid),\r
+                        new Long(scan_position.current_rh.getId()));\r
+            }\r
+\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                SanityManager.ASSERT(\r
+                    scan_position.current_leaf.page.fetchNumFieldsAtSlot(\r
+                        scan_position.current_slot) > 1);\r
+            }\r
+\r
+            scan_position.current_rh = \r
+                scan_position.current_leaf.page.fetchFromSlot(\r
+                (RecordHandle) null, \r
+                scan_position.current_slot, row, \r
+                qualify ? init_fetchDesc : null,\r
+                true);\r
+\r
+            // The possibility is that the row at the current position\r
+            // has been marked as deleted (it cannot have been purged\r
+            // since the scan maintains a lock on the row, and purges\r
+            // are always done from system transactions).  I'm not sure\r
+            // what the desired behavior is in this case.  For now,\r
+            // just return null.\r
+\r
+            // RESOLVE (mikem) - what should be done here?\r
+            if (scan_position.current_leaf.page.isDeletedAtSlot(\r
+                    scan_position.current_slot))\r
+            {\r
+                if (SanityManager.DEBUG)\r
+                    SanityManager.ASSERT(false, "positioned on deleted row");\r
+            }\r
+        }\r
+        finally\r
+        {\r
+            if (scan_position.current_leaf != null)\r
+            {\r
+                // release latch on page.\r
+                scan_position.current_leaf.release();\r
+                scan_position.current_leaf = null;\r
+            }\r
+        }\r
+\r
+               return;\r
+       }\r
+\r
+    /**\r
+     * @see org.apache.derby.iapi.store.access.ScanController#isHeldAfterCommit\r
+     */\r
+    public boolean isHeldAfterCommit() throws StandardException\r
+    {\r
+        return (scan_state == SCAN_HOLD_INIT ||\r
+                scan_state == SCAN_HOLD_INPROGRESS);\r
+    }\r
+\r
+    /**\r
+    Fetch the row at the current position of the Scan.\r
+       @see ScanController#fetch\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+    **/\r
+       public void fetch(DataValueDescriptor[] row)\r
+        throws StandardException\r
+    {\r
+        fetch(row, true);\r
+    }\r
+\r
+    /**\r
+     * Fetch the row at the current position of the Scan without applying the \r
+     * qualifiers.\r
+     * @see ScanController#fetchWithoutQualify\r
+     * \r
+     * @exception  StandardException  Standard exception policy.\r
+     */\r
+       public void fetchWithoutQualify(DataValueDescriptor[] row)\r
+               throws StandardException\r
+    {\r
+        fetch(row, false);\r
+    }\r
+    \r
+    /**\r
+     * Return ScanInfo object which describes performance of scan.\r
+     * <p>\r
+     * Return ScanInfo object which contains information about the current\r
+     * scan.\r
+     * <p>\r
+     *\r
+     * @see ScanInfo\r
+     *\r
+        * @return The ScanInfo object which contains info about current scan.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    public ScanInfo getScanInfo()\r
+               throws StandardException\r
+    {\r
+        return(new BTreeScanInfo(this));\r
+    }\r
+\r
+    /**\r
+    Returns true if the current position of the scan is at a\r
+    deleted row.  This case can come about if the current scan\r
+    or another scan on the same table in the same transaction\r
+    deleted the row after the next() call which positioned the\r
+    scan at this row.\r
+\r
+    The results of a fetch() performed on a scan positioned on\r
+    a deleted row are undefined.\r
+\r
+       @exception StandardException Standard exception policy.\r
+    **/\r
+    public boolean isCurrentPositionDeleted()\r
+               throws StandardException\r
+    {\r
+        boolean     ret_val;\r
+\r
+        if (scan_state != SCAN_INPROGRESS)\r
+            throw StandardException.newException(\r
+                SQLState.AM_SCAN_NOT_POSITIONED);\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(this.container != null,\r
+                "BTreeScan.isCurrentPositionDeleted() called on closed scan.");\r
+        }\r
+        try\r
+        {\r
+            // Get current page of scan, with latch\r
+\r
+            if (reposition(scan_position, false))\r
+            {\r
+\r
+                if (SanityManager.DEBUG)\r
+                {\r
+                    SanityManager.ASSERT(\r
+                        scan_position.current_leaf.page.fetchNumFieldsAtSlot(\r
+                            scan_position.current_slot) > 1);\r
+                }\r
+\r
+                ret_val = \r
+                    scan_position.current_leaf.page.isDeletedAtSlot(\r
+                        scan_position.current_slot);\r
+            }\r
+            else\r
+            {\r
+                ret_val = false;\r
+            }\r
+        }\r
+        finally\r
+        {\r
+            if (scan_position.current_leaf != null)\r
+            {\r
+                // release latch on page.\r
+                scan_position.current_leaf.release();\r
+                scan_position.current_leaf = null;\r
+            }\r
+        }\r
+\r
+               return(ret_val);\r
+    }\r
+\r
+    /**\r
+     * Return whether this is a keyed conglomerate.\r
+     * <p>\r
+     *\r
+        * @return whether this is a keyed conglomerate.\r
+     **/\r
+       public boolean isKeyed()\r
+    {\r
+        return(true);\r
+    }\r
+\r
+    /**\r
+     * @see ScanController#positionAtRowLocation\r
+     *\r
+     * Not implemented for this class\r
+     */\r
+    public boolean positionAtRowLocation (RowLocation rLoc) \r
+        throws StandardException \r
+    {\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);        \r
+    }\r
+\r
+    /**\r
+    Move to the next position in the scan.\r
+       @see ScanController#next\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+    **/\r
+    public boolean next()\r
+               throws StandardException\r
+       {\r
+        // Turn this call into a group fetch of a 1 element group.\r
+        fetchNext_one_slot_array[0] = runtime_mem.get_scratch_row(getRawTran());\r
+        boolean ret_val = \r
+            fetchRows(\r
+                scan_position,\r
+                fetchNext_one_slot_array, \r
+                (RowLocation[]) null,\r
+                (BackingStoreHashtable) null,\r
+                1,\r
+                (int[]) null) == 1;\r
+\r
+\r
+        return(ret_val);\r
+    }\r
+\r
+    /**\r
+    Fetch the row at the next position of the Scan.\r
+\r
+    If there is a valid next position in the scan then\r
+       the value in the template storable row is replaced\r
+       with the value of the row at the current scan\r
+       position.  The columns of the template row must\r
+       be of the same type as the actual columns in the\r
+       underlying conglomerate.\r
+\r
+    The resulting contents of templateRow after a fetchNext() \r
+    which returns false is undefined.\r
+\r
+    The result of calling fetchNext(row) is exactly logically\r
+    equivalent to making a next() call followed by a fetch(row)\r
+    call.  This interface allows implementations to optimize \r
+    the 2 calls if possible.\r
+\r
+    @param row The template row into which the value\r
+       of the next position in the scan is to be stored.\r
+\r
+    @return True if there is a next position in the scan,\r
+       false if there isn't.\r
+\r
+       @exception StandardException Standard exception policy.\r
+    **/\r
+       public boolean fetchNext(DataValueDescriptor[] row)\r
+               throws StandardException\r
+       {\r
+        boolean ret_val;\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            TemplateRow.checkPartialColumnTypes(\r
+                this.getConglomerate().format_ids, \r
+                init_scanColumnList, (int[]) null, row);\r
+        }\r
+\r
+        // Turn this call into a group fetch of a 1 element group.\r
+        fetchNext_one_slot_array[0] = row;\r
+        ret_val = \r
+            fetchRows(\r
+                scan_position,\r
+                fetchNext_one_slot_array, \r
+                (RowLocation[]) null,\r
+                (BackingStoreHashtable) null,\r
+                1,\r
+                (int[]) null) == 1;\r
+\r
+        return(ret_val);\r
+    }\r
+\r
+    /**\r
+     * Fetch the next N rows from the table.\r
+     * <p>\r
+     * The client allocates an array of N rows and passes it into the\r
+     * fetchNextSet() call.  This routine does the equivalent of N \r
+     * fetchNext() calls, filling in each of the rows in the array.\r
+     * Locking is performed exactly as if the N fetchNext() calls had\r
+     * been made.\r
+     * <p>\r
+     * It is up to Access how many rows to return.  fetchNextSet() will\r
+     * return how many rows were filled in.  If fetchNextSet() returns 0\r
+     * then the scan is complete, (ie. the scan is in the same state as if\r
+     * fetchNext() had returned false).  If the scan is not complete then\r
+     * fetchNext() will return (1 <= row_count <= N).\r
+     * <p>\r
+     * The current position of the scan is undefined if fetchNextSet()\r
+     * is used (ie. mixing fetch()/fetchNext() and fetchNextSet() calls\r
+     * in a single scan does not work).  This is because a fetchNextSet()\r
+     * request for 5 rows from a heap where the first 2 rows qualify, but\r
+     * no other rows qualify will result in the scan being positioned at\r
+     * the end of the table, while if 5 rows did qualify the scan will be\r
+     * positioned on the 5th row.\r
+     * <p>\r
+     * Qualifiers, start and stop positioning of the openscan are applied\r
+     * just as in a normal scan. \r
+     * <p>\r
+     * The columns of the row will be the standard columns returned as\r
+     * part of a scan, as described by the validColumns - see openScan for\r
+     * description.\r
+     * <p>\r
+     * Expected usage:\r
+     *\r
+     * // allocate an array of 5 empty row templates\r
+     * DataValueDescriptor[][] row_array = allocate_row_array(5);\r
+     * int row_cnt = 0;\r
+     *\r
+     * scan = openScan();\r
+     *\r
+     * while ((row_cnt = scan.fetchNextSet(row_array) != 0)\r
+     * {\r
+     *     // I got "row_cnt" rows from the scan.  These rows will be\r
+     *     // found in row_array[0] through row_array[row_cnt - 1]\r
+     * }\r
+     *\r
+     * <p>\r
+     *\r
+     * RESOLVE - This interface is being provided so that we can prototype\r
+     *           the performance results it can achieve.  If it looks like\r
+     *           this interface is useful, it is very likely we will look\r
+     *           into a better way to tie together the now 4 different\r
+     *           fetch interfaces: fetch, fetchNext(), fetchNextGroup(),\r
+     *           and fetchSet().\r
+     *\r
+        * @return The number of qualifying rows found and copied into the \r
+     *         provided array of rows.  If 0 then the scan is complete, \r
+     *         otherwise the return value will be: \r
+     *         1 <= row_count <= row_array.length\r
+     *\r
+     * @param row_array         The array of rows to copy rows into.  \r
+     *                          row_array[].length must >= 1.  This routine\r
+     *                          assumes that all entries in the array \r
+     *                          contain complete template rows.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    public int fetchNextGroup(\r
+    DataValueDescriptor[][] row_array,\r
+    RowLocation[]           rowloc_array)\r
+        throws StandardException\r
+       {\r
+        return(\r
+            fetchRows(\r
+                scan_position,\r
+                row_array, \r
+                rowloc_array,\r
+                (BackingStoreHashtable) null,\r
+                row_array.length,\r
+                (int[]) null));\r
+    }\r
+\r
+    public int fetchNextGroup(\r
+    DataValueDescriptor[][] row_array,\r
+    RowLocation[]           old_rowloc_array,\r
+    RowLocation[]           new_rowloc_array)\r
+        throws StandardException\r
+       {\r
+        // This interface is currently only used to move rows around in\r
+        // a heap table, unused in btree's -- so not implemented.\r
+\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+    }\r
+\r
+    /**\r
+     * Insert all rows that qualify for the current scan into the input\r
+     * Hash table.  \r
+     * <p>\r
+     * This routine scans executes the entire scan as described in the \r
+     * openScan call.  For every qualifying unique row value an entry is\r
+     * placed into the HashTable. For unique row values the entry in the\r
+     * BackingStoreHashtable has a key value of the object stored in \r
+     * row[key_column_number], and the value of the data is row.  For row \r
+     * values with duplicates, the key value is also row[key_column_number], \r
+     * but the value of the data is a Vector of\r
+     * rows.  The caller will have to call "instanceof" on the data value\r
+     * object if duplicates are expected, to determine if the data value\r
+     * of the Hashtable entry is a row or is a Vector of rows.\r
+     * <p>\r
+     * Note, that for this routine to work efficiently the caller must \r
+     * ensure that the object in row[key_column_number] implements \r
+     * the hashCode and equals method as appropriate for it's datatype.\r
+     * <p>\r
+     * It is expected that this call will be the first and only call made in\r
+     * an openscan.  Qualifiers and stop position of the openscan are applied\r
+     * just as in a normal scan.  This call is logically equivalent to the \r
+     * caller performing the following:\r
+     *\r
+     * import java.util.Hashtable;\r
+     *\r
+     * hash_table = new Hashtable();\r
+     *\r
+     * while (next())\r
+     * {\r
+     *     row = create_new_row();\r
+     *     fetch(row);\r
+     *     if ((duplicate_value = \r
+     *         hash_table.put(row[key_column_number], row)) != null)\r
+     *     {\r
+     *         Vector row_vec;\r
+     *\r
+     *         // inserted a duplicate\r
+     *         if ((duplicate_value instanceof vector))\r
+     *         {\r
+     *             row_vec = (Vector) duplicate_value;\r
+     *         }\r
+     *         else\r
+     *         {\r
+     *             // allocate vector to hold duplicates\r
+     *             row_vec = new Vector(2);\r
+     *\r
+     *             // insert original row into vector\r
+     *             row_vec.addElement(duplicate_value);\r
+     *\r
+     *             // put the vector as the data rather than the row\r
+     *             hash_table.put(row[key_column_number], row_vec);\r
+     *         }\r
+     *         \r
+     *         // insert new row into vector\r
+     *         row_vec.addElement(row);\r
+     *     }\r
+     * }\r
+     * <p>\r
+     * The columns of the row will be the standard columns returned as\r
+     * part of a scan, as described by the validColumns - see openScan for\r
+     * description.\r
+     * RESOLVE - is this ok?  or should I hard code somehow the row to\r
+     *           be the first column and the row location?\r
+     * <p>\r
+     * Currently it is only possible to hash on the first column in the\r
+     * conglomerate, in the future we may change the interface to allow\r
+     * hashing either on a different column or maybe on a combination of\r
+     * columns.\r
+     * <p>\r
+     * No overflow to external storage is provided, so calling this routine\r
+     * on a 1 gigabyte conglomerate will incur at least 1 gigabyte of memory\r
+     * (probably failing with a java out of memory condition).  If this\r
+     * routine gets an out of memory condition, or if "max_rowcnt" is \r
+     * exceeded then then the routine will give up, empty the Hashtable, \r
+     * and return "false."\r
+     * <p>\r
+     * On exit from this routine, whether the fetchSet() succeeded or not\r
+     * the scan is complete, it is positioned just the same as if the scan\r
+     * had been drained by calling "next()" until it returns false (ie. \r
+     * fetchNext() and next() calls will return false).  \r
+     * reopenScan() can be called to restart the scan.\r
+     * <p>\r
+     *\r
+     * RESOLVE - until we get row counts what should we do for sizing the\r
+     *           the size, capasity, and load factor of the hash table.\r
+     *           For now it is up to the caller to create the Hashtable,\r
+     *           Access does not reset any parameters.\r
+     * <p>\r
+     * RESOLVE - I am not sure if access should be in charge of allocating\r
+     *           the new row objects.  I know that I can do this in the\r
+     *           case of btree's, but I don't think I can do this in heaps.\r
+     *           Maybe this is solved by work to be done on the sort \r
+     *           interface.\r
+     *\r
+     *\r
+     * @param max_rowcnt        The maximum number of rows to insert into the \r
+     *                          Hash table.  Pass in -1 if there is no maximum.\r
+     * @param key_column_numbers The column numbers of the columns in the\r
+     *                          scan result row to be the key to the Hashtable.\r
+     *                          "0" is the first column in the scan result\r
+     *                          row (which may be different than the first\r
+     *                          column in the row in the table of the scan).\r
+     * @param hash_table        The java HashTable to load into.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    public void fetchSet(\r
+    long                    max_rowcnt,\r
+    int[]                   key_column_numbers,\r
+    BackingStoreHashtable   hash_table)\r
+        throws StandardException\r
+    {\r
+        // System.out.println("fetchSet");\r
+        \r
+        fetchRows(\r
+            scan_position,\r
+            (DataValueDescriptor[][]) null,\r
+            (RowLocation[]) null,\r
+            (BackingStoreHashtable) hash_table,\r
+            max_rowcnt,\r
+            key_column_numbers);\r
+\r
+        return;\r
+    }\r
+\r
+\r
+    /**\r
+    Reposition the current scan.  This call is semantically the same as if\r
+    the current scan had been closed and a openScan() had been called instead.\r
+    The scan is reopened with against the same conglomerate, and the scan\r
+    is reopened with the same "hold" and "forUpdate" parameters passed in\r
+    the original openScan.  The previous template row continues to be used.\r
+\r
+       @param startKeyValue  An indexable row which holds a\r
+       (partial) key value which, in combination with the\r
+       startSearchOperator, defines the starting position of\r
+       the scan.  If null, the starting position of the scan\r
+       is the first row of the conglomerate.\r
+\r
+       @param startSearchOperator an operator which defines\r
+       how the startKeyValue is to be searched for.  If\r
+    startSearchOperation is ScanController.GE, the scan starts on\r
+       the first row which is greater than or equal to the\r
+       startKeyValue.  If startSearchOperation is ScanController.GT,\r
+       the scan starts on the first row whose key is greater than\r
+       startKeyValue.  The startSearchOperation parameter is\r
+       ignored if the startKeyValue parameter is null.\r
+\r
+       @param qualifier An array of qualifiers which, applied\r
+       to each key, restrict the rows returned by the scan.  Rows\r
+       for which any one of the qualifiers returns false are not\r
+       returned by the scan. If null, all rows are returned.\r
+\r
+       @param stopKeyValue  An indexable row which holds a\r
+       (partial) key value which, in combination with the\r
+       stopSearchOperator, defines the ending position of\r
+       the scan.  If null, the ending position of the scan\r
+       is the last row of the conglomerate.\r
+\r
+       @param stopSearchOperator an operator which defines\r
+       how the stopKeyValue is used to determine the scan stopping\r
+       position. If stopSearchOperation is ScanController.GE, the scan\r
+       stops just before the first row which is greater than or\r
+       equal to the stopKeyValue.  If stopSearchOperation is\r
+       ScanController.GT, the scan stops just before the first row whose\r
+       key is greater than     startKeyValue.  The stopSearchOperation\r
+       parameter is ignored if the stopKeyValue parameter is null.\r
+\r
+       @exception StandardException Standard exception policy.\r
+    **/\r
+       public final void reopenScan(\r
+    DataValueDescriptor[]   startKeyValue,\r
+    int                     startSearchOperator,\r
+    Qualifier               qualifier[][],\r
+    DataValueDescriptor[]   stopKeyValue,\r
+    int                     stopSearchOperator)\r
+        throws StandardException\r
+    {\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            if (!init_hold)\r
+                SanityManager.ASSERT(this.container != null,\r
+                    "BTreeScan.reopenScan() called on non-held closed scan.");\r
+\r
+            // should only be called by clients outside of store, so should\r
+            // not be possible for a latch to held.\r
+            SanityManager.ASSERT(scan_position.current_leaf == null);\r
+        }\r
+\r
+        // call unlockScanRecordAfterRead() before setting the scan back\r
+        // to init state, so that we release the last lock if necessary (ie.\r
+        // for read committed).\r
+        //\r
+        \r
+        if (scan_position.current_rh != null)\r
+        {\r
+            // reposition to get record handle if we don't have it.\r
+\r
+            if (!reposition(scan_position, false))\r
+            {\r
+                if (SanityManager.DEBUG)\r
+                {\r
+                    SanityManager.THROWASSERT(\r
+                        "can not fail while holding update row lock.");\r
+                }\r
+            }\r
+\r
+            this.getLockingPolicy().unlockScanRecordAfterRead(\r
+                scan_position, init_forUpdate);\r
+\r
+            scan_position.current_rh   = null;\r
+            scan_position.current_leaf.release();\r
+            scan_position.current_leaf = null;\r
+        }\r
+\r
+\r
+        // Need to do this unlock in any case, until lock manager provides\r
+        // a way to release locks associated with a compatibility space.  This\r
+        // scan lock is special, as it is a lock on the btree container rather\r
+        // than the heap container.  The open container on the btree actually\r
+        // has a null locking policy so the close of that container does not\r
+        // release this lock, need to explicitly unlock it here or when the\r
+        // scan is closed as part of the abort the lock will not be released.\r
+        if (scan_position.current_scan_pageno != 0)\r
+        {\r
+            this.getLockingPolicy().unlockScan(\r
+                scan_position.current_scan_pageno);\r
+            scan_position.current_scan_pageno = 0;\r
+        }\r
+\r
+        scan_position.current_slot = Page.INVALID_SLOT_NUMBER;\r
+        scan_position.current_rh   = null;\r
+        scan_position.current_positionKey  = null;\r
+\r
+        initScanParams(\r
+            startKeyValue, startSearchOperator, \r
+            qualifier, stopKeyValue, stopSearchOperator);\r
+\r
+        if (!init_hold)\r
+            this.scan_state = SCAN_INIT;\r
+        else\r
+            this.scan_state = \r
+                (this.container != null ? SCAN_INIT : SCAN_HOLD_INIT);\r
+    }\r
+\r
+    /**\r
+    Reposition the current scan.  This call is semantically the same as if\r
+    the current scan had been closed and a openScan() had been called instead.\r
+    The scan is reopened against the same conglomerate, and the scan\r
+    is reopened with the same "scan column list", "hold" and "forUpdate"\r
+    parameters passed in the original openScan.  \r
+    <p>\r
+    The statistics gathered by the scan are not reset to 0 by a reopenScan(),\r
+    rather they continue to accumulate.\r
+    <p>\r
+    Note that this operation is currently only supported on Heap conglomerates.\r
+    Also note that order of rows within are heap are not guaranteed, so for\r
+    instance positioning at a RowLocation in the "middle" of a heap, then\r
+    inserting more data, then continuing the scan is not guaranteed to see\r
+    the new rows - they may be put in the "beginning" of the heap.\r
+\r
+       @param startRowLocation  An existing RowLocation within the conglomerate,\r
+    at which to position the start of the scan.  The scan will begin at this\r
+    location and continue forward until the end of the conglomerate.  \r
+    Positioning at a non-existent RowLocation (ie. an invalid one or one that\r
+    had been deleted), will result in an exception being thrown when the \r
+    first next operation is attempted.\r
+\r
+       @param qualifier An array of qualifiers which, applied\r
+       to each key, restrict the rows returned by the scan.  Rows\r
+       for which any one of the qualifiers returns false are not\r
+       returned by the scan. If null, all rows are returned.\r
+\r
+       @exception StandardException Standard exception policy.\r
+    **/\r
+       public void reopenScanByRowLocation(\r
+    RowLocation startRowLocation,\r
+    Qualifier   qualifier[][])\r
+        throws StandardException\r
+    {\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+    }\r
+\r
+       /*\r
+       ** Methods of ScanController, which are not supported by btree.\r
+       */\r
+\r
+       /**\r
+       Fetch the location of the current position in the scan.\r
+       @see ScanController#fetchLocation\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+       **/\r
+       public void fetchLocation(RowLocation templateLocation)\r
+               throws StandardException\r
+       {\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+       }\r
+\r
+       /**\r
+       Return a row location object of the correct type to be\r
+       used in calls to fetchLocation.\r
+       @see org.apache.derby.iapi.store.access.GenericScanController#newRowLocationTemplate\r
+\r
+       @exception  StandardException  Standard exception policy.\r
+       **/\r
+       public RowLocation newRowLocationTemplate()\r
+               throws StandardException\r
+       {\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+       }\r
+\r
+    /**\r
+    Replace the entire row at the current position of the scan.\r
+\r
+    Unimplemented interface by btree, will throw an exception.\r
+\r
+       @see ScanController#replace\r
+       @exception  StandardException  Standard exception policy.\r
+    **/\r
+    public boolean replace(DataValueDescriptor[] row, FormatableBitSet validColumns)\r
+               throws StandardException\r
+       {\r
+        throw StandardException.newException(\r
+                SQLState.BTREE_UNIMPLEMENTED_FEATURE);\r
+       }\r
+\r
+       /*\r
+       ** Methods of ScanManager\r
+       */\r
+\r
+\r
+    /**\r
+    Close the scan, a commit or abort is about to happen.\r
+    **/\r
+    public boolean closeForEndTransaction(boolean closeHeldScan)\r
+        throws StandardException\r
+       {\r
+        if (!init_hold || closeHeldScan)\r
+        {\r
+            // Scan is closed, make sure no access to any state variables\r
+            positionAtDoneScan(scan_position);\r
+\r
+            super.close();\r
+\r
+            // null out so that these object's can get GC'd earlier.\r
+            this.init_rawtran       = null;\r
+            this.init_template      = null;\r
+            this.init_startKeyValue = null;\r
+            this.init_qualifier     = null;\r
+            this.init_stopKeyValue  = null;\r
+\r
+            this.getXactMgr().closeMe(this);\r
+\r
+            return(true);\r
+        }\r
+        else\r
+        {\r
+\r
+            if (this.scan_state == SCAN_INPROGRESS)\r
+            {\r
+                if (SanityManager.DEBUG)\r
+                {\r
+                    SanityManager.ASSERT(scan_position != null);\r
+                }\r
+\r
+                if (scan_position.current_positionKey == null)\r
+                {\r
+                    // save position of scan by key rather than location so \r
+                    // that we can recover if the page with the position \r
+                    // disappears while we don't have a scan lock.\r
+\r
+                    savePosition();\r
+                }\r
+                this.scan_state = SCAN_HOLD_INPROGRESS;\r
+            }\r
+            else if (this.scan_state == SCAN_INIT)\r
+            {\r
+                this.scan_state = SCAN_HOLD_INIT;\r
+            }\r
+\r
+            super.close();\r
+\r
+            return(false);\r
+        }\r
+       }\r
+\r
+    /**\r
+     * Do work necessary to maintain the current position in the scan.\r
+     * <p>\r
+     * Save the current position of the scan as a key.\r
+     * Do whatever is necessary to maintain the current position of the scan.\r
+     * For some conglomerates this may be a no-op.\r
+     *\r
+     * <p>\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    private void savePosition()\r
+               throws StandardException\r
+    {\r
+        if (this.scan_state == SCAN_INPROGRESS)\r
+        {\r
+            // Either current_rh or positionKey is valid - the other is null.\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                SanityManager.ASSERT(\r
+                    (scan_position.current_rh == null) == \r
+                    (scan_position.current_positionKey != null));\r
+            }\r
+\r
+            try\r
+            {\r
+                if (scan_position.current_rh != null)\r
+                {\r
+                    // if scan position is not saved by key, then make it so.\r
+\r
+                    // must reposition to get the page latched.\r
+\r
+                    if (reposition(scan_position, false))\r
+                    {\r
+                        scan_position.current_positionKey = \r
+                            runtime_mem.get_row_for_export(getRawTran());\r
+\r
+\r
+                        Page page = scan_position.current_leaf.getPage();\r
+\r
+\r
+                        RecordHandle rh =\r
+                            page.fetchFromSlot(\r
+                                (RecordHandle) null,\r
+                                page.getSlotNumber(scan_position.current_rh), \r
+                                scan_position.current_positionKey, \r
+                                (FetchDescriptor) null,\r
+                                true);\r
+\r
+                        if (SanityManager.DEBUG)\r
+                        {\r
+                            SanityManager.ASSERT(rh != null);\r
+                        }\r
+\r
+                        scan_position.current_rh = null;\r
+                        scan_position.current_slot = Page.INVALID_SLOT_NUMBER;\r
+\r
+                        // release scan lock now that the row is saved away.\r
+\r
+                        if (scan_position.current_scan_pageno != 0)\r
+                        {\r
+                            this.getLockingPolicy().unlockScan(\r
+                                scan_position.current_scan_pageno);\r
+                            scan_position.current_scan_pageno = 0;\r
+                        }\r
+\r
+                    }\r
+                    else\r
+                    {\r
+                        // this should never happen as we hold the scan lock\r
+                        // on the page while maintaining the position by \r
+                        // recordhandle - reposition should always work in this\r
+                        // case.\r
+\r
+                        if (SanityManager.DEBUG)\r
+                            SanityManager.THROWASSERT(\r
+                                "Must always be able to reposition.");\r
+                    }\r
+                }\r
+\r
+            }\r
+            finally\r
+            {\r
+\r
+                if (scan_position.current_leaf != null)\r
+                {\r
+                    // release latch on page\r
+                    scan_position.current_leaf.release();\r
+                    scan_position.current_leaf = null;\r
+                }\r
+            }\r
+        }\r
+\r
+    }\r
+\r
+    /**\r
+     * Do work necessary to maintain the current position in the scan.\r
+     * <p>\r
+     * The latched page in the conglomerate "congomid" is changing, do\r
+     * whatever is necessary to maintain the current position of the scan.\r
+     * For some conglomerates this may be a no-op.\r
+     * <p>\r
+     *\r
+     * @param conglom  Conglomerate object of the conglomerate being changed.\r
+     * @param page      Page in the conglomerate being changed.\r
+     *\r
+        * @exception  StandardException  Standard exception policy.\r
+     **/\r
+    public void savePosition(Conglomerate conglom, Page page)\r
+        throws StandardException\r
+       {\r
+        // page should be latched by split.  This scan is assuming that latch\r
+        // and reading off it's key from the page under the split's latch.\r
+        // A lock should have already been gotten on this row.\r
+\r
+\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            SanityManager.ASSERT(page.isLatched());\r
+        }\r
+\r
+        /*\r
+        System.out.println(\r
+            "Saving position in btree at top: " +\r
+            " this.conglomerate = " +  this.conglomerate        +\r
+            " this.scan_state   = " +  this.scan_state);\r
+        SanityManager.DEBUG_PRINT("savePosition()", \r
+            "Saving position in btree at top: " +\r
+            " this.conglomerate = " +  this.conglomerate        +\r
+            " this.scan_state   = " +  this.scan_state);\r
+        */\r
+\r
+\r
+        if ((this.getConglomerate() == conglom) &&\r
+            (this.scan_state == SCAN_INPROGRESS))\r
+        {\r
+            // Either current_rh or positionKey is valid - the other is null.\r
+            if (SanityManager.DEBUG)\r
+            {\r
+                SanityManager.ASSERT(\r
+                    (scan_position.current_rh == null) == \r
+                    (scan_position.current_positionKey != null));\r
+            }\r
+\r
+            /*\r
+            SanityManager.DEBUG_PRINT("savePosition()", \r
+                "Saving position in btree: " +\r
+                ";current_scan_pageno = " + this.current_scan_pageno +\r
+                "this.current_rh = " + this.current_rh +\r
+                ";page.getPageNumber() = " + page.getPageNumber() +\r
+                ((this.current_rh != null) ?\r
+                    (";this.current_rh.getPageNumber() = " +\r
+                     this.current_rh.getPageNumber()) : ""));\r
+            */\r
+\r
+            if (scan_position.current_rh != null &&\r
+                page.getPageNumber() == \r
+                    scan_position.current_rh.getPageNumber())\r
+            {\r
+                scan_position.current_positionKey = \r
+                    runtime_mem.get_row_for_export(getRawTran());\r
+\r
+                RecordHandle rh =\r
+                    page.fetchFromSlot(\r
+                        (RecordHandle) null,\r
+                        page.getSlotNumber(scan_position.current_rh), \r
+                        scan_position.current_positionKey, \r
+                        (FetchDescriptor) null,\r
+                        true);\r
+\r
+                if (SanityManager.DEBUG)\r
+                {\r
+                    SanityManager.ASSERT(rh != null);\r
+                }\r
+\r
+                scan_position.current_rh = null;\r
+                scan_position.current_slot = Page.INVALID_SLOT_NUMBER;\r
+\r
+                // release the scan lock now that we have saved away the row.\r
+\r
+                if (scan_position.current_scan_pageno != 0)\r
+                {\r
+                    this.getLockingPolicy().unlockScan(\r
+                        scan_position.current_scan_pageno);\r
+                    scan_position.current_scan_pageno = 0;\r
+                }\r
+            }\r
+        }\r
+       }\r
+\r
+    public RecordHandle getCurrentRecordHandleForDebugging()\r
+    {\r
+        return(scan_position.current_rh);\r
+    }\r
+\r
+    /*\r
+    ** Standard toString() method.  Prints out current position in scan.\r
+    */\r
+    public String toString()\r
+    {\r
+        if (SanityManager.DEBUG)\r
+        {\r
+            String string =\r
+                "\n\tbtree = " + this.getConglomerate() +\r
+                "\n\tscan direction       = " +\r
+                    (this instanceof BTreeForwardScan  ? "forward"  :\r
+                    (this instanceof BTreeMaxScan ? "backward" : \r
+                                                         "illegal")) +\r
+                "\n\t(scan_state:" +\r
+                (this.scan_state == SCAN_INIT       ? "SCAN_INIT"       :\r
+                 this.scan_state == SCAN_INPROGRESS ? "SCAN_INPROGRESS" :\r
+                 this.scan_state == SCAN_DONE       ? "SCAN_DONE"       :\r
+                 this.scan_state == SCAN_HOLD_INIT  ? "SCAN_HOLD_INIT"  :\r
+                 this.scan_state == SCAN_HOLD_INPROGRESS ? "SCAN_HOLD_INPROGRESS" :\r
+                                                      "BAD_SCAN_STATE") +\r
+                "\n\trh:"  + scan_position.current_rh  +\r
+                "\n\tkey:" + scan_position.current_positionKey + ")"                        +\r
+                "\n\tinit_rawtran = "              + init_rawtran +\r
+                "\n\tinit_hold = "                 + init_hold +\r
+                "\n\tinit_forUpdate = "            + init_forUpdate +\r
+                "\n\tinit_useUpdateLocks = "       + init_useUpdateLocks +\r
+                "\n\tinit_scanColumnList = "       + init_scanColumnList +\r
+                "\n\tinit_scanColumnList.size() = "+ (\r
+                    (init_scanColumnList != null ? \r
+                        init_scanColumnList.size() : 0)) +\r
+                "\n\tinit_template = "             + \r
+                    RowUtil.toString(init_template) +\r
+                "\n\tinit_startKeyValue = "        + \r
+                    RowUtil.toString(init_startKeyValue) +\r
+                "\n\tinit_startSearchOperator = "  + \r
+                    (init_startSearchOperator == ScanController.GE ? "GE" : \r
+                    (init_startSearchOperator == ScanController.GT ? "GT" : \r
+                     Integer.toString(init_startSearchOperator))) +\r
+                "\n\tinit_qualifier[]         = "  + init_qualifier +\r
+                "\n\tinit_stopKeyValue = "         + \r
+                    RowUtil.toString(init_stopKeyValue) +\r
+                "\n\tinit_stopSearchOperator = "   + \r
+                    (init_stopSearchOperator == ScanController.GE ? "GE" : \r
+                    (init_stopSearchOperator == ScanController.GT ? "GT" : \r
+                     Integer.toString(init_stopSearchOperator)))  +\r
+                "\n\tstat_numpages_visited         = " + \r
+                    stat_numpages_visited +\r
+                "\n\tstat_numrows_visited          = " + \r
+                    stat_numrows_visited  +\r
+                "\n\tstat_numrows_qualified        = " + \r
+                    stat_numrows_qualified +\r
+                "\n\tstat_numdeleted_rows_visited  = " + \r
+                    stat_numdeleted_rows_visited ;\r
+\r
+            return(string);\r
+        }\r
+        else\r
+        {\r
+            return(null);\r
+        }\r
+    }\r
+}\r