/*

   Derby - Class org.apache.derbyTesting.unitTests.store.T_b2i

   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */

package org.apache.derbyTesting.unitTests.store;

// impl imports are the preferred way to create unit tests.
import org.apache.derbyTesting.unitTests.harness.T_MultiIterations;
import org.apache.derbyTesting.unitTests.harness.T_Fail;

import org.apache.derby.impl.store.access.btree.index.*;

import org.apache.derby.iapi.types.SQLLongint;

import org.apache.derby.iapi.reference.Property;
import org.apache.derby.iapi.reference.SQLState;

import org.apache.derby.iapi.services.io.FormatableBitSet;
import org.apache.derby.iapi.services.i18n.MessageService;

import org.apache.derby.iapi.services.monitor.Monitor;

import org.apache.derby.iapi.services.sanity.SanityManager;
import org.apache.derby.iapi.services.stream.HeaderPrintWriter;
import org.apache.derby.iapi.services.context.ContextService;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.io.FormatIdUtil;

import org.apache.derby.iapi.db.Database;
import org.apache.derby.iapi.error.StandardException;
import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
import org.apache.derby.iapi.store.access.conglomerate.TransactionManager;
import org.apache.derby.iapi.store.access.AccessFactory;
import org.apache.derby.iapi.store.access.ConglomerateController;
import org.apache.derby.iapi.store.access.Qualifier;
import org.apache.derby.iapi.types.RowLocation;
import org.apache.derby.iapi.store.access.ScanController;
import org.apache.derby.iapi.store.access.ScanInfo;
import org.apache.derby.iapi.store.access.TransactionController;
import org.apache.derby.iapi.store.access.RowUtil;
import org.apache.derby.iapi.store.access.ColumnOrdering;

import org.apache.derby.iapi.store.raw.ContainerHandle;
import org.apache.derby.iapi.store.raw.LockingPolicy;
import org.apache.derby.iapi.store.raw.RawStoreFactory;

import org.apache.derby.iapi.types.DataValueDescriptor;

import org.apache.derby.impl.store.access.btree.BTree;
import org.apache.derby.impl.store.access.conglomerate.TemplateRow;
import org.apache.derby.iapi.types.SQLChar;


import java.util.Properties;



public class T_b2i extends T_MultiIterations
{ 

    private static final String testService = "b2iTest";

    private Object store_module = null; 

    private ContextService contextService = null;

    /* Methods required by T_MultiIterations */


    /**
     * Routine one once per invocation of the test by the driver.
     * <p>
     * Do work that should only be done once, no matter how many times
     * runTests() may be executed.
     *
	 * @exception  T_Fail  Thrown on any error.
     **/
    protected void setupTest()
		throws T_Fail
    {
		// don't automatic boot this service if it gets left around
		if (startParams == null) {
			startParams = new Properties();
		}
		startParams.put(Property.NO_AUTO_BOOT, Boolean.TRUE.toString());
		// remove the service directory to ensure a clean run
		startParams.put(Property.DELETE_ON_CREATE, Boolean.TRUE.toString());

		// see if we are testing encryption
		startParams = T_Util.setEncryptionParam(startParams);


		try {
			store_module = Monitor.createPersistentService(
				getModuleToTestProtocolName(), testService, startParams);
			
			contextService = ContextService.getFactory();

		} catch (StandardException mse) {
			throw T_Fail.exceptionFail(mse);
		}
    }

	/*
	** Methods required by T_Generic
	*/

	public String getModuleToTestProtocolName() {
		return AccessFactory.MODULE;
	}

    /**
     * Driver routine for the btree secondary index tests.
     * <p>
     *
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
	protected void runTestSet() throws T_Fail
	{
		AccessFactory store = null;
		TransactionController tc = null;
		boolean pass = false;

        out.println("executing b2i test");

        store = (AccessFactory) store_module;

		if (store == null)
        {
			throw T_Fail.testFailMsg(
                    getModuleToTestProtocolName() + " service not started.");
		}

		ContextManager cm1 = contextService.newContextManager();
		contextService.setCurrentContextManager(cm1);

		REPORT("(unitTestMain) Testing " + testService);

		try {

			tc = store.getTransaction(cm1);

				pass = true;

            if (
                t_005(tc)   &&
                t_001(tc)   &&
                t_003(tc)   &&
                t_004(tc)   &&
                t_005(tc)   &&
                t_006(tc)   &&
                t_009(tc)   &&
                t_010(tc)   &&
                t_011(tc)   &&
                t_012(tc)   &&
                t_013(tc)   &&
                t_014(tc)   &&
				t_017(tc)   &&
				t_018(tc)   &&
				t_019(tc)   &&
				t_020(tc)   &&
				t_021(tc)
                )
                
			{
				pass = true;

                if (SanityManager.DEBUG)
                {
                    pass = false;

                    // The following tests depend on SanityManager functionality
                    // so can not be run in an insane server.

                    if (t_002(tc) &&
                        t_007(tc) &&
                        t_008(tc) &&
                        t_015(tc) &&
                        t_016(tc)
                        )
                        pass = true;
                }
			}

			tc.commit();
			tc.destroy();
		}
		catch (StandardException e)
		{
			String  msg = e.getMessage();
			if (msg == null)
				msg = e.getClass().getName();
			REPORT(msg);

            e.printStackTrace(out.getPrintWriter());
            cm1.cleanupOnError(e, isdbActive());

            pass = false;
		}
        catch (Throwable t)
        {
			String  msg = t.getMessage();
			if (msg == null)
				msg = t.getClass().getName();
			REPORT(msg);

            t.printStackTrace(out.getPrintWriter());
            cm1.cleanupOnError(t, isdbActive());

            pass = false;
        }
		finally {
			contextService.resetCurrentContextManager(cm1);
		}

		if (!pass)
			throw T_Fail.testFailMsg("");
	}

    /**
     * Utility routine to create base table for tests.
     * <p>
     * A little utility routine to create base tables for tests.  Just
     * here to make tests a little more readable.  It currently just 
     * creates a heap table with "num_cols" SQLLongint columns.
     *
     *
     * @param num_cols the number of columns in the base table.
     *
     * @exception  StandardException  Standard exception policy.
     **/

    void createCongloms(
    TransactionController   tc,
    int                     num_cols,
    boolean                 unique,
    boolean                 varying_first_col,
    int                     max_btreerows_per_page,
    T_CreateConglomRet        ret_val)
		throws StandardException
    {
        T_SecondaryIndexRow index_row  = new T_SecondaryIndexRow();
        DataValueDescriptor[] base_row = TemplateRow.newU8Row(num_cols);

        if (varying_first_col)
        {
            SQLChar    string_col = new SQLChar();

            base_row[0] = string_col;
        }

        long base_conglomid = 0;

        // create the base table
        base_conglomid = 
            tc.createConglomerate(
                "heap",   // create a heap conglomerate
                base_row, // base table template row
                null, //column sort order - not required for heap
                null, //default collation
                null,     // default properties
                TransactionController.IS_DEFAULT);// not temporary

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // initialize the secondary index row - pointing it at base row
        RowLocation base_rowloc = base_cc.newRowLocationTemplate();
        index_row.init(base_row, base_rowloc, num_cols + 1);
        
        // create the secondary index
        Properties properties = 
            createProperties(
                null,           // no current properties list 
                false,          // don't allow duplicates
                num_cols + 1,   // index on all base row cols + row location
                (unique ? num_cols : num_cols + 1),   // non-unique index
                true,           // maintain parent links
                base_conglomid, // base conglomid
                num_cols);      // row loc in last column

        if (max_btreerows_per_page > 1)
        {
            if (BTree.PROPERTY_MAX_ROWS_PER_PAGE_PARAMETER != null)
            {
                properties.put(
                    BTree.PROPERTY_MAX_ROWS_PER_PAGE_PARAMETER,
                    String.valueOf(max_btreerows_per_page));
            }
        }

        long index_conglomid = 
            tc.createConglomerate(
                "BTREE",    	    // create a btree secondary
                index_row.getRow(), // index row template
				null, //column sort order - default
                null, //default collation
                properties,         // properties
                TransactionController.IS_DEFAULT); // not temporary

        // return values to caller
        ret_val.base_conglomid     = base_conglomid;
        ret_val.index_conglomid    = index_conglomid;
        // RESOLVE (mikem - 04/29/98 - why is following line commented out?
        // ret_val.base_template_row  = TemplateRow.newU8Row(num_cols);
        ret_val.index_template_row = index_row.getRow();

        return;
    }

    protected static Properties createProperties(
    Properties  input_properties,
    boolean     input_allowduplicates,
    int         input_nkeyfields,
    int         input_nuniquecolumns,
    boolean     input_maintainparentlinks,
    long        input_baseconglomerateid,
    int         input_rowlocationcolumn)
        throws StandardException
    {
        Properties properties = 
            ((input_properties == null) ? new Properties() : input_properties);
        properties.put(
            "allowDuplicates",     String.valueOf(input_allowduplicates));
        properties.put(
            "nKeyFields",          String.valueOf(input_nkeyfields));
        properties.put(
            "nUniqueColumns",      String.valueOf(input_nuniquecolumns));
        properties.put(
            "maintainParentLinks", String.valueOf(input_maintainparentlinks));
        properties.put(
            "baseConglomerateId",  String.valueOf(input_baseconglomerateid));
        properties.put(
            "rowLocationColumn",   String.valueOf(input_rowlocationcolumn));
        return(properties);
    }

    /**
     * Test a single scan.
     * 
	 * @exception  StandardException  Standard exception policy.
     */
    protected boolean t_scan(
    TransactionController   tc,
    long                    conglomid,
    DataValueDescriptor[]	template,
    DataValueDescriptor[]	start_key, 
    int                     start_op,
    Qualifier               qualifier[][],
    DataValueDescriptor[]	stop_key,
    int                     stop_op,
    int                     expect_numrows,
    int                     expect_key)
        throws StandardException
    {

        // open a new scan

        ScanController scan = 
            tc.openScan(
                conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                start_key, start_op,
                qualifier,
                stop_key, stop_op);

        long key     = -42;
        long numrows = 0;

        while (scan.next())
        {
            scan.fetch(template);

            key = ((SQLLongint)template[2]).getLong();

            if (key != expect_key)
            {
                return(
                    FAIL("(t_scan) wrong key, expected (" + expect_key + ")" +
                     "but got (" + key + ")."));
            }
            else
            {
                expect_key++;
                numrows++;
            }
        }

        ((B2IForwardScan)scan).checkConsistency();
        
        scan.close();

        if (numrows != expect_numrows)
        {
            return(FAIL("(t_scan) wrong number of rows. Expected " +
                 expect_numrows + " rows, but got " + numrows + "rows."));
        }

        return(true);
    }

    /**
     * delete a single key, given key value.  assumes 3 column table, first
     * column = 1, second column is a unique key, and 3rd column is a 
     * RecordHandle into the base table.
     *
	 * @exception  StandardException  Standard exception policy.
     */
    protected boolean t_delete(
    TransactionController   tc,
    long                    conglomid,
    DataValueDescriptor[]   search_key,
    boolean useUpdateLocks)
        throws StandardException
    {
        SQLLongint column0 = new SQLLongint(-1);
        SQLLongint column1 = new SQLLongint(-1);

        int openmode = TransactionController.OPENMODE_FORUPDATE;
        if (useUpdateLocks) {
            openmode |= TransactionController.OPENMODE_USE_UPDATE_LOCKS;
        }

        // open a new scan

        ScanController scan = 
            tc.openScan(conglomid, false,
                        openmode,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
                        (FormatableBitSet) null,
                        search_key, ScanController.GE, 
                        null,
                        search_key, ScanController.GT); 

        long expect_key = 
            ((SQLLongint) search_key[1]).getLong();

        int numrows = 0;
		DataValueDescriptor[] partialRow = new DataValueDescriptor[2];
		partialRow[0] = column0;
		partialRow[1] = column1;

        while (scan.next())
        {
            numrows++;

			scan.fetch(partialRow);

            if (column0.getLong() != 1)
                return(FAIL("(t_delete) column[0] value is not 1"));

            if (column1.getLong() != expect_key)
                return(
                    FAIL("(t_delete) column[1]  value is not " + expect_key));

            if (!scan.delete())
            {
                return(FAIL("(t_delete): delete of row failed"));
            }
            if (scan.delete())
            {
                return(FAIL("(t_delete): re-delete of row succeeded"));
            }
        }

        scan.close();

        // This function expects unique keys, so scan should find single row.
        if (numrows != 1)
        {
            return(FAIL("(t_delete) wrong number of rows. Expected " +
                   "1 row, but got " + numrows + "rows."));
        }

        return(true);
    }

    /**
     * Test BTreeController.insert()
     * <p>
     * Just verify that insert code works for a secondary index.  Just call
     * the interface and make sure the row got there.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_001(TransactionController tc)
        throws StandardException, T_Fail
    {
        REPORT("Starting t_001");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        if (!(index_cc instanceof B2IController))
        {
			throw T_Fail.testFailMsg("openConglomerate returned wrong type");
        }

        if (!index_cc.isKeyed())
        {
			throw T_Fail.testFailMsg("btree is not keyed.");
        }

        index_cc.checkConsistency();

		// Create a row and insert into base table, remembering it's location.
		DataValueDescriptor[] r1             = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow  index_row1      = new T_SecondaryIndexRow();
        RowLocation          base_rowloc1    = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);
		((SQLLongint) r1[0]).setValue(2);
		((SQLLongint) r1[1]).setValue(2);

		// Insert the row into the base table and remember its location.
		base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

		// Make sure we read back the value we wrote from base and index table.
		DataValueDescriptor[] r2            = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row2    = new T_SecondaryIndexRow();
        RowLocation           base_rowloc2  = base_cc.newRowLocationTemplate();

        index_row2.init(r2, base_rowloc2, 3);

        // base table check:
        if (!base_cc.fetch(base_rowloc1, r2, (FormatableBitSet) null))
        {
            return(FAIL("(t_001) insert into base table failed"));
        }

        if (((SQLLongint) r2[0]).getLong() != 2 ||
            ((SQLLongint) r2[1]).getLong() != 2)
        {
            return(FAIL("(t_001) insert into base table failed"));
        }

        // index check - there should be only one record:
        ScanController scan = 
            tc.openScan(create_ret.index_conglomid, false,
                        TransactionController.OPENMODE_FORUPDATE,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

        scan.next();
        scan.fetch(index_row2.getRow());

        // delete the only row in the table, and make sure 
        // isCurrentPositionDeleted() works.
        if (scan.isCurrentPositionDeleted())
            throw T_Fail.testFailMsg("current row should not be deleted\n");
        if (!scan.doesCurrentPositionQualify())
            throw T_Fail.testFailMsg("current row should still qualify\n");
        scan.delete();
        if (!scan.isCurrentPositionDeleted())
            throw T_Fail.testFailMsg("current row should be deleted\n");
        if (scan.doesCurrentPositionQualify())
            throw T_Fail.testFailMsg("deleted row should not qualify\n");

        // just call the debugging code to make sure it doesn't fail.
        REPORT("Calling scan.tostring(): " + scan);

        if (scan.next()                                                     ||
            ((SQLLongint)(index_row2.getRow()[0])).getLong() != 2 ||
            ((SQLLongint)(index_row2.getRow()[1])).getLong() != 2)
        {
            return(FAIL("(t_001) insert into index failed in base cols"));
        }

        // test the scaninfo interface.

        ScanInfo   scan_info = scan.getScanInfo();
        Properties prop      = scan_info.getAllScanInfo(null);

        if (Integer.parseInt(prop.getProperty(
		   MessageService.getTextMessage(SQLState.STORE_RTS_NUM_PAGES_VISITED)))
				!= 1)
        {
            throw T_Fail.testFailMsg(
                "(scanInfo) wrong numPagesVisited.  Expected 1, got " + 
                Integer.parseInt(prop.getProperty(
					MessageService.getTextMessage(
								SQLState.STORE_RTS_NUM_PAGES_VISITED))));
        }
        if (Integer.parseInt(prop.getProperty(
			MessageService.getTextMessage(SQLState.STORE_RTS_NUM_ROWS_VISITED)))
				!= 1)
        {
            throw T_Fail.testFailMsg(
                "(scanInfo) wrong numRowsVisited. Expected 1, got " + 
                Integer.parseInt(prop.getProperty(
					MessageService.getTextMessage(
								SQLState.STORE_RTS_NUM_ROWS_VISITED))));
        }
        if (Integer.parseInt(prop.getProperty(
		  MessageService.getTextMessage(SQLState.STORE_RTS_NUM_ROWS_QUALIFIED)))
				!= 1)
        {
            throw T_Fail.testFailMsg(
                "(scanInfo) wrong numRowsQualified. Expected 1, got " + 
                Integer.parseInt(prop.getProperty(
		   			MessageService.getTextMessage(
								SQLState.STORE_RTS_NUM_ROWS_QUALIFIED))));
        }


        int compare_result = base_rowloc1.compare(base_rowloc2);
        if (compare_result != 0)
        {
            return(FAIL("(t_001) insert into index failed in recordhandle.\n" +
                        "\texpected RecordHandle = " + base_rowloc1 + "\n" +
                        "\tgot      RecordHandle = " + base_rowloc2 + 
                        "\tcompare result = " + compare_result));
        }

        index_cc.checkConsistency();

		// Close the conglomerates.
		base_cc.close();
		index_cc.close();

        try 
        {
            base_cc.insert(r1);
            return(FAIL("(t_001) insert on closed conglomerate worked"));
        }
        catch (StandardException e)
        {
            // e.printStackTrace();
        }

        try 
        {
            if (index_cc.insert(r1) != 0)
                throw T_Fail.testFailMsg("insert failed");
            return(FAIL("(t_001) insert on closed conglomerate worked"));
        }
        catch (StandardException e)
        {
            // e.printStackTrace();
        }

        tc.commit();
        REPORT("Ending t_001");

        return true;
    }

    /**
     * Test backout during critical times of splits.
     * <p>
     * Use trace points to force errors in split at critical points:
     *     leaf_split_abort{1,2,3,4}
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_002(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        // SanityManager.DEBUG_SET("LockTrace");

        REPORT("Starting t_002");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        if (!(index_cc instanceof B2IController))
        {
			throw T_Fail.testFailMsg("openConglomerate returned wrong type");
        }

        index_cc.checkConsistency();

		// Create a row and insert into base table, remembering it's location.
		DataValueDescriptor[]   r1           = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow     index_row1   = new T_SecondaryIndexRow();
        RowLocation             base_rowloc1 = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // Commit the create of the tables so that the following aborts don't
        // undo that work.
        tc.commit();

        // Now try aborts of transactions during splits, using magic
        // trace flags.  This test inserts enough rows to cause a split
        // and then forces the split to fail at various key points.  The
        // split should be backed out and also the rows before the split.
        // The test makes sure that there are some inserts before the forced
        // split abort.
        String[] debug_strings = {
            "leaf_split_growRoot1",
            "leaf_split_growRoot2",
            "leaf_split_growRoot3",
            "leaf_split_growRoot4",
            "leaf_split_growRoot5",
            "leaf_split_abort1",
            "leaf_split_abort2",
            "leaf_split_abort3",
            "leaf_split_abort4",
            "branch_split_abort1",
            "branch_split_abort2",
            "branch_split_abort3",
            "branch_split_abort4",
            "BTreeController_doIns2"
            };

        for (int errs = 0; errs < debug_strings.length; errs++)
        {
            REPORT("Doing abort test: " + debug_strings[errs]);

            // set the debug flag, which will cause an error
            // RESOLVE (mmm) these tests should be run from the language to
            // make sure error handling really works.

            if (SanityManager.DEBUG)
                SanityManager.DEBUG_SET(debug_strings[errs]);
            
            try 
            {

                // Open the base table
                base_cc = 
                    tc.openConglomerate(
                        create_ret.base_conglomid,
                        false,
                        TransactionController.OPENMODE_FORUPDATE,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE);

                // Open the secondary index
                index_cc =	
                    tc.openConglomerate(
                        create_ret.index_conglomid,
                        false,
                        TransactionController.OPENMODE_FORUPDATE,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE);

                // insert one row that does not cause failure.
                ((SQLLongint)r1[0]).setValue(2);
                ((SQLLongint)r1[1]).setValue(10000 + errs);

                // Insert the row into the base table;remember its location.
                base_cc.insertAndFetchLocation(r1, base_rowloc1);

                // Insert the row into the secondary index.
                if (index_cc.insert(index_row1.getRow()) != 0)
                    throw T_Fail.testFailMsg("insert failed");


                // set the debug flag, which will cause an error
                // RESOLVE (mmm) these tests should be run from the 
                // language to make sure error handling really works.
                if (SanityManager.DEBUG)
                    SanityManager.DEBUG_SET(debug_strings[errs]);

                // now insert enough rows to cause failure
                for (int i = 100; i > 0; i -= 2)
                {
                    ((SQLLongint)r1[0]).setValue(2);
                    ((SQLLongint)r1[1]).setValue(i);

                    // Insert the row into the base table;remember its location.
                    base_cc.insertAndFetchLocation(r1, base_rowloc1);

                    // Insert the row into the secondary index.
                    if (index_cc.insert(index_row1.getRow()) != 0)
                    {
                        throw T_Fail.testFailMsg("insert failed");
                    }
                }

                throw T_Fail.testFailMsg(
                    "debug flag (" + debug_strings[errs] + 
                    ")did not cause exception.");
            }
            catch (StandardException e)
            {
                ContextService contextFactory = ContextService.getFactory();

                // Get the context manager.
                ContextManager cm = contextFactory.getCurrentContextManager();

                if (SanityManager.DEBUG)
                    SanityManager.ASSERT(cm != null);

                cm.cleanupOnError(e, isdbActive());
                
                // RESOLVE (mikem) - when split abort works come up with 
                // a good sanity check here.
                //
                // index check - there should be no records:
                scan = tc.openScan(create_ret.index_conglomid, false, 
                        TransactionController.OPENMODE_FORUPDATE,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);


                index_cc =	
                    tc.openConglomerate(
                        create_ret.index_conglomid,
                        false,
                        TransactionController.OPENMODE_FORUPDATE,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE);

                index_cc.checkConsistency();
                index_cc.close();

                if (scan.next())
                {
                    throw T_Fail.testFailMsg(
                            "t_002: there are still rows in table.");
                }


                scan.close();
            }

            // Unset the flag.
            if (SanityManager.DEBUG)
                SanityManager.DEBUG_CLEAR(debug_strings[errs]);
        }

        // Try a simple abort.  The following adds enough rows to cause a 
        // split.  The result of the split should be a tree with no rows, but
        // the splits will not be undone.  It is up to the implementation
        // whether the undo's cause shrinks in the tree.  In the initial
        // implementation it won't.
        {
            tc.commit();

            // Open the base table
            base_cc = 
                tc.openConglomerate(
                    create_ret.base_conglomid,
                    false,
                    TransactionController.OPENMODE_FORUPDATE,
                    TransactionController.MODE_RECORD,
                    TransactionController.ISOLATION_SERIALIZABLE);

            // Open the secondary index
            index_cc =	
                tc.openConglomerate(
                    create_ret.index_conglomid,
                    false,
                    TransactionController.OPENMODE_FORUPDATE,
                    TransactionController.MODE_RECORD,
                    TransactionController.ISOLATION_SERIALIZABLE);

            // BTree.PROPERTY_MAX_ROWS_PER_PAGE_PARAMETER has been set to 2 so
            // inserting 3 rows will cause a split at the leaf level.
            // Make sure that normal abort leaves the committed split.
            for (int i = 0; i < 3; i++)
            {
                ((SQLLongint)r1[0]).setValue(2);
                ((SQLLongint)r1[1]).setValue(i);

                // Insert the row into the base table;remember its location.
                base_cc.insertAndFetchLocation(r1, base_rowloc1);

                // Insert the row into the secondary index.
                if (index_cc.insert(index_row1.getRow()) != 0)
                    throw T_Fail.testFailMsg("insert failed");
            }

            tc.abort();

            // index check - there should be no records left.
            ScanController empty_scan = 
                tc.openScan(create_ret.index_conglomid, false, 
                            0,
                            TransactionController.MODE_RECORD,
                            TransactionController.ISOLATION_SERIALIZABLE,
							(FormatableBitSet) null,
                            null, ScanController.NA,
                            null,
                            null, ScanController.NA);

            if (empty_scan.next())
            {
                throw T_Fail.testFailMsg(
                    "t_002: there are still rows in table.");
            }
        }

        tc.commit();
        REPORT("Ending t_002");

        return true;
    }

    private boolean t_003_scan_test_cases(
    TransactionController   tc,
    long                    index_conglomid,
    T_SecondaryIndexRow     template
    )
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        // run through a predicates as described in the openScan() interface //
        DataValueDescriptor[] start_key  = TemplateRow.newU8Row(1);
        DataValueDescriptor[] stop_key   = TemplateRow.newU8Row(1);


        // test predicate x = 5
        //
        //     result set should be: {5,2,16}, {5,4,17}, {5,6,18}
        //
        REPORT("scan (x = 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   stop_key,  ScanController.GT,
                   3, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }
                   
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x > 5 |{5}  |GT |null |   |{6,1} .. {9,1}|{5,6} .. {9,1} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x > 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT, 
                   null,
                   null,  ScanController.NA,
                   3, 19, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x >= 5|{5}  |GE |null |   |{5,2} .. {9,1}|{4,6} .. {9,1} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x >= 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   null,  ScanController.NA,
                   6, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x <= 5|null |   |{5}  |GT |{1,1} .. {5,6}|first .. {5,6} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x <= 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GT,
                   8, 11, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }
        //
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        // 	|x < 5 |null |   |{5}  |GE |{1,1} .. {4,6}|first .. {4,6} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x < 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GE,
                   5, 11, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        //long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        //long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
	    //  |x >= 5 and x <= 7|{5},  |GE|{7}  |GT|{5,2} .. {7,1}|{4,6} .. {7,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x >= 5 and x <= 7)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(7);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   5, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
	    //  |x = 5 and y > 2  |{5,2} |GT|{5}  |GT|{5,4} .. {5,6}|{5,2} .. {9,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y > 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT,
                   null,
                   stop_key,  ScanController.GT,
                   2, 17, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 5 and y >= 2 | {5,2}|GE| {5} |GT|{5,2} .. {5,6}|{4,6} .. {9,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y >= 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   3, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 5 and y < 5  | {5}  |GE|{5,5}|GE|{5,2} .. {5,4}|{4,6} .. {5,4}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y < 5)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        ((SQLLongint)stop_key[1]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GE,
                   2, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 2            | {2}  |GE| {2} |GT|none          |{1,1} .. {1,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        //  +-----------------------------+
        // 	|max on btree - row locked    |
        // 	+-----------------------------+
        //
        REPORT("max on btree, row locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right max was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong max found.");
            }
        }

        //  +-----------------------------+
        // 	|max on btree - table locked    |
        // 	+-----------------------------+
        //
        REPORT("max on btree, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right max was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong max found.");
            }
        }

        //  +-----------------------------+
        // 	|max on btree - row locked    |
        // 	+-----------------------------+
        //
        REPORT("max on btree, row locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right max was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong max found.");
            }
        }

        //  +-----------------------------+
        // 	|max on btree - table locked    |
        // 	+-----------------------------+
        //
        REPORT("max on btree, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right max was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong max found.");
            }
        }

        return(ret_val);
    }

    /**
     * Test BTree.openScan(), BtreeScan.init(), BtreeScan.next(), 
     * BtreeScan.fetch().
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_003(TransactionController tc)
        throws StandardException, T_Fail
    {
        T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();

        // base row template - last column is just to make row long so that
        // multiple pages are spanned.
        DataValueDescriptor[] base_row             = TemplateRow.newU8Row(4);
        base_row[3] = new SQLChar();

        String   string_1500char = new String();
        for (int i = 0; i < 300; i++)
            string_1500char += "mikem";

        boolean     ret_val = true;
        long        value   = -1;
        long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};

        // set of deleted rows to make scans more interesting
        long d_col1[] ={ 0,  2,  3,  4,  4,  5,  5,  5,  6,  7,  8, 10, 11, 12};
        long d_col2[] ={ 1,  1,  2,  3,  5,  0,  3,  5,  0,  0,  1, 42, 42, 1};
        long d_col3[] ={91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104};

        REPORT("Starting t_003");

        // create the base table
        long base_conglomid = 
            tc.createConglomerate(
                "heap",                            // create a heap conglomerate
                base_row,                          // base table template row
				null, //column sort order - not required for heap
                null, //default collation
                null,                              // default properties
                TransactionController.IS_DEFAULT); // not temporary

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // initialize the secondary index row - pointing it at base row
        index_row.init(base_row, base_cc.newRowLocationTemplate(), 5);

        Properties properties = 
            createProperties(
                null,               // no current properties list 
                false,              // don't allow duplicates
                5,                  // 4 columns in index row
                5,                  // non-unique index
                true,               // maintain parent links
                base_conglomid,     // base conglom id
                4);                 // row loc in last column

        long index_conglomid = 
            tc.createConglomerate(
                "BTREE",    				// create a btree secondary
                index_row.getRow(),         // row template
				null, //column sort order - default
                null, //default collation
                properties,                 // properties
                TransactionController.IS_DEFAULT);   // not temporary

		// Open the conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
        T_SecondaryIndexRow template = new T_SecondaryIndexRow();
        RowLocation         row_loc  = base_cc.newRowLocationTemplate();
        template.init(base_row, row_loc, 5);

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

        ((B2IController)index_cc).debugConglomerate();

        ret_val = t_003_scan_test_cases(tc, index_conglomid, template);

        // insert and delete some interesting rows, deleted space management
        // may or may not clean these up.
        for (int i = d_col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(d_col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(d_col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(d_col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");

            // now delete the row.
            base_cc.delete(row_loc);

            ScanController delete_scan = 
                tc.openScan(index_conglomid, false,
                            TransactionController.OPENMODE_FORUPDATE,
                            TransactionController.MODE_RECORD,
                            TransactionController.ISOLATION_SERIALIZABLE,
                            (FormatableBitSet) null,
                            template.getRow(), ScanController.GE, 
                            null,
                            template.getRow(), ScanController.GT); 

            if (!delete_scan.next())
            {
                throw T_Fail.testFailMsg("delete could not find key");
            }
            else
            {
                delete_scan.delete();

                if (delete_scan.next())
                    throw T_Fail.testFailMsg("delete found more than one key");
            }

            delete_scan.close();
        }

        ret_val = t_003_scan_test_cases(tc, index_conglomid, template);


		// Close the conglomerate.
		index_cc.close();

        tc.commit();
        REPORT("Ending t_003");

        return(ret_val);
    }

    /**
     * Test qualifiers.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_004(TransactionController tc)
        throws StandardException, T_Fail
    {
        REPORT("Starting t_004");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

        Properties properties = 
            createProperties(
                null,       // no current properties list 
                false,      // don't allow duplicates
                4,          // 3 columns in index row
                4,          // non-unique index
                true,       // maintain parent links
                create_ret.base_conglomid,        // fake base conglom for now
                3);         // row loc in last column

        T_QualifierTest q_test = 
            new T_QualifierTest(
                "BTREE",    				    // create a btree secondary
                properties,                     // properties
                false,                          // not temporary
                out,
                T_QualifierTest.ORDER_FORWARD); // ordered 

        boolean test_result = q_test.t_testqual(tc);

        REPORT("Ending t_004");

        return(test_result);
    }

    /**
     * Test Branch splits - number of rows necessary to cause splits is raw
     * store implementation dependant (currently 5 rows per page in in-memory
     * implementation).
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_005(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        REPORT("Starting t_005");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);


		// Create a row.
		T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();
        RowLocation             rowloc    = base_cc.newRowLocationTemplate();
        DataValueDescriptor[]   base_row  = TemplateRow.newU8Row(2);
        index_row.init(base_row, rowloc, 3); 

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = 200; i >= 0; i -= 4)
        {
            ((SQLLongint)base_row[0]).setValue(1);
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }
        for (int i = 199; i >= 0; i -= 4)
        {
            ((SQLLongint)base_row[0]).setValue(1);
            ((SQLLongint)base_row[1]).setValue(i);

            base_cc.insertAndFetchLocation(base_row, rowloc);
            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

		// Close the conglomerate.
		index_cc.close();

        tc.commit();

        // Search for each of the keys and delete them one at a time.
        DataValueDescriptor[] delete_key = TemplateRow.newU8Row(2);
        for (int i = 200; i >= 0; i -= 4)
        {
            ((SQLLongint)delete_key[0]).setValue(1);
            ((SQLLongint)delete_key[1]).setValue(i);

            if (!t_delete(
                tc, create_ret.index_conglomid, delete_key, false))
            {
                ret_val = false;
            }
        }
        for (int i = 199; i >= 0; i -= 4)
        {
            ((SQLLongint)delete_key[0]).setValue(1);
            ((SQLLongint)delete_key[1]).setValue(i);

            if (!t_delete(
                tc, create_ret.index_conglomid, delete_key, false))
            {
                ret_val = false;
            }
        }

        tc.commit();

		// Open the base conglomerate.
		base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);


		// Open the conglomerate.
		index_cc = 
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // flush and empty cache to make sure rereading stuff works.
        RawStoreFactory rawstore = 
            (RawStoreFactory) Monitor.findServiceModule(
                this.store_module, RawStoreFactory.MODULE);

        rawstore.idle();

        // now make sure that additional splits don't cause delete bits to 
        // be enabled (one early bug did so).
        
        for (int i = 200; i >= 0; i -= 3)
        {
            ((SQLLongint)base_row[0]).setValue(1);
            ((SQLLongint)base_row[1]).setValue(i);

            base_cc.insertAndFetchLocation(base_row, rowloc);
            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }
        for (int i = 200; i >= 0; i -= 3)
        {
            ((SQLLongint)delete_key[0]).setValue(1);
            ((SQLLongint)delete_key[1]).setValue(i);

            if (!t_delete(
                tc, create_ret.index_conglomid, delete_key, false))
            {
                ret_val = false;
            }
        }

        // index check - there should be no records left.
        ScanController empty_scan = 
            tc.openScan(create_ret.index_conglomid, false, 
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);
        if (empty_scan.next())
			throw T_Fail.testFailMsg("t_005: there are still rows in table.");

        index_cc.checkConsistency();

        for (int i = 600; i >= 400; i -= 3)
        {
            ((SQLLongint)base_row[0]).setValue(1);
            ((SQLLongint)base_row[1]).setValue(i);

            base_cc.insertAndFetchLocation(base_row, rowloc);
            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

        tc.abort();

        // index check - there should be no records left.
        empty_scan = 
            tc.openScan(create_ret.index_conglomid, false,
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);
        if (empty_scan.next())
			throw T_Fail.testFailMsg("t_005: there are still rows in table.");


        REPORT("Ending t_005");

        return(ret_val);
    }
    /**
     * Test unimplemented interfaces.  
     *
     * The following ScanController interfaces are not supported by the
     * btree implementation, because row locations are not returned outside
     * the interface.  At some point we may package a key as a row location
     * but that does not really give any more functionality than using scan
     * to find your key:
     *     ScanController.fetchLocation()
     *     ScanController.newRowLocationTemplate()
     *     ScanController.replace()
     *     ConglomerateController.delete()
     *     ConglomerateController.fetch()
	 *     ConglomerateController.insertAndFetchLocation()
	 *     ConglomerateController.newRowLocationTemplate()
     *     ConglomerateController.replace()
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_006(TransactionController tc)
        throws StandardException, T_Fail
    {
        REPORT("Starting t_006");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a base row template.
        DataValueDescriptor[]   base_row    = TemplateRow.newU8Row(2);
        RowLocation             base_rowloc = base_cc.newRowLocationTemplate();

        T_SecondaryIndexRow index_row_from_base_row = new T_SecondaryIndexRow();
        index_row_from_base_row.init(base_row, base_rowloc, 3);
        ((SQLLongint)base_row[0]).setValue(1);

		// Create a row.
		T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();
        index_row.init(TemplateRow.newU8Row(2), 
                        base_cc.newRowLocationTemplate(), 3);

        // test: make sure scan position is right after inserts before scan
        //       no split case.  In this case the slot position of the current
        //       position should change, but the code will keep a record handle
        //       and not need to reposition by key.
        // before keys: 1000, 3000
        // last key gotten froms scan : 0
        // insert keys:1-900
        // next key from scan should be: 5

        // insert 1000
        ((SQLLongint)base_row[1]).setValue(1000);
        base_cc.insertAndFetchLocation(base_row, base_rowloc);
        if (index_cc.insert(index_row_from_base_row.getRow()) != 0)
        {
			throw T_Fail.testFailMsg("insert failed");
        }

        // try each of the unsupported interfaces:
        try
        {
            index_cc.delete(null);
            return(FAIL("t_006: ConglomerateController.delete() succeeded."));
        }
        catch (StandardException e)
        {
        }
        try
        {
            if (!index_cc.fetch(
                    null, RowUtil.EMPTY_ROW, (FormatableBitSet) null))
            {
                return(FAIL("t_006: ConglomerateController.fetch() bad ret."));
            }
            return(FAIL("t_006: ConglomerateController.fetch() succeeded."));
        }
        catch (StandardException e)
        {
        }
        try
        {
            index_cc.insertAndFetchLocation((DataValueDescriptor[]) null, null);
            return(FAIL(
                "t_006: ConglomerateController.insertAndFetchLocation() succeeded."));
        }
        catch (StandardException e)
        {
        }
        try
        {
            RowLocation rowloc = index_cc.newRowLocationTemplate();
            return(FAIL(
                "t_006: ConglomerateController.newRowLocationTemplate() succeeded."));
        }
        catch (StandardException e)
        {
        }
        try
        {
            index_cc.replace(null, null, null);
            return(FAIL("t_006: ConglomerateController.replace() succeeded."));
        }
        catch (StandardException e)
        {
        }

        index_cc.close();

        // open a new scan

        ScanController scan = 
            tc.openScan(create_ret.index_conglomid, false, 
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null, 
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

        int numrows = 0;
        while (scan.next())
        {
            numrows++;

            scan.fetch(index_row_from_base_row.getRow());

            try
            {
                scan.fetchLocation(null);
                return(FAIL("t_006: scan.fetchLocation() succeeded"));
            }
            catch (StandardException e)
            {
            }

            try
            {
                RowLocation rowloc = scan.newRowLocationTemplate();
                return(FAIL("t_006: scan.newRowLocationTemplate() succeeded"));
            }
            catch (StandardException e)
            {
            }

            try
            {
                scan.replace(index_row_from_base_row.getRow(), (FormatableBitSet) null);
                return(FAIL("t_006: scan.replace() succeeded"));
            }
            catch (StandardException e)
            {
            }

        }

        // make sure that scan.next() continues to return false
        if (scan.next())
            return(FAIL("t_006: scan.next() returned true after false."));

        scan.close();

        if (numrows != 1)
        {
            return(FAIL("(t_scan) wrong number of rows. Expected " +
                   "1 row, but got " + numrows + "rows."));
        }

        REPORT("Ending t_006");
        return(true);
    }

    /**
     * Test multiple scans in a single page/no split 
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_007(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val     = true;

        REPORT("Starting t_007");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
		T_SecondaryIndexRow   index_row = new T_SecondaryIndexRow();
        DataValueDescriptor[] base_row  = TemplateRow.newU8Row(2);
        RowLocation           row_loc   = base_cc.newRowLocationTemplate();
        index_row.init(base_row, row_loc, 3);

		// Create a row.
        ((SQLLongint)(index_row.getRow()[0])).setValue(1);

        // test: make sure scan position is right after inserts before scan
        //       no split case.  In this case the slot position of the current
        //       position should change, but the code will keep a record handle
        //       and not need to reposition by key.
        // before keys: 3, 5
        // last key gotten froms scan : 0
        // insert keys:1, 2
        // next key from scan should be: 5

        // insert 3
        ((SQLLongint)(index_row.getRow()[1])).setValue(3);
        base_cc.insertAndFetchLocation(base_row, row_loc);
        if (index_cc.insert(index_row.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        // insert 5
        ((SQLLongint)(index_row.getRow()[1])).setValue(5);
        base_cc.insertAndFetchLocation(base_row, row_loc);
        if (index_cc.insert(index_row.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        // open a new scan

        ScanController scan = 
            tc.openScan(create_ret.index_conglomid, false,
                        0,                        
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(scan.next());

        scan.fetch(index_row.getRow());
        long key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 3);

        // insert 1
        ((SQLLongint)(index_row.getRow()[1])).setValue(1);
        base_cc.insertAndFetchLocation(base_row, row_loc);
        if (index_cc.insert(index_row.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        // insert 2
        ((SQLLongint)(index_row.getRow()[1])).setValue(2);
        base_cc.insertAndFetchLocation(base_row, row_loc);
        if (index_cc.insert(index_row.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        // current position should not have changed
        scan.fetch(index_row.getRow());
        key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 3);

        // next position should be 5
        if (SanityManager.DEBUG)
            SanityManager.ASSERT(scan.next());
        scan.fetch(index_row.getRow());
        key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 5);

        index_cc.close();
        scan.close();

        REPORT("Ending t_007");

        return(ret_val);
    }
    /**
     * Test multiple scans in a single table/with splits
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_008(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val     = true;

        REPORT("Starting t_008");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a base row template.
        DataValueDescriptor[]   base_row    = TemplateRow.newU8Row(2);
        RowLocation             base_rowloc = base_cc.newRowLocationTemplate();

        T_SecondaryIndexRow index_row_from_base_row = new T_SecondaryIndexRow();
        index_row_from_base_row.init(base_row, base_rowloc, 3);
        ((SQLLongint)base_row[0]).setValue(1);

		// Create a row.
		T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();
        index_row.init(TemplateRow.newU8Row(2), 
                        base_cc.newRowLocationTemplate(), 3);

        // test: make sure scan position is right after inserts before scan
        //       no split case.  In this case the slot position of the current
        //       position should change, but the code will keep a record handle
        //       and not need to reposition by key.
        // before keys: 1000, 3000
        // last key gotten froms scan : 0
        // insert keys:1-900
        // next key from scan should be: 5

        // insert 1000
        ((SQLLongint)base_row[1]).setValue(1000);
        base_cc.insertAndFetchLocation(base_row, base_rowloc);
        if (index_cc.insert(index_row_from_base_row.getRow()) != 0)
        {
			throw T_Fail.testFailMsg("insert failed");
        }

        // open a new scan

        ScanController scan = 
            tc.openScan(create_ret.index_conglomid, false,
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(scan.next());

        scan.fetch(index_row.getRow());
        long key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 1000);

        // The following test works best if 5 rows fit on one page

        //for (int i = 1; i < 900; i++)
        for (int i = 0; i < 6; i++)
        {
            // insert i
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, base_rowloc);

            if (index_cc.insert(index_row_from_base_row.getRow()) != 0)
            {
                throw T_Fail.testFailMsg("insert failed");
            }

            // About every 2nd split page, recheck the position
            
            if (i % 10 == 0)
            {
                // current position should not have changed
                scan.fetch(index_row.getRow());
                key = ((SQLLongint)(index_row.getRow()[1])).getLong();
                if (SanityManager.DEBUG)
                    SanityManager.ASSERT(key == 1000);
            }

        }

        // insert 3000
        ((SQLLongint)base_row[1]).setValue(3000);
        base_cc.insertAndFetchLocation(base_row, base_rowloc);
        if (index_cc.insert(index_row_from_base_row.getRow()) != 0)
        {
			throw T_Fail.testFailMsg("insert failed");
        }

        // current position should not have changed
        scan.fetch(index_row.getRow());
        key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 1000);

        // next position should be 3000
        if (SanityManager.DEBUG)
            SanityManager.ASSERT(scan.next());
        scan.fetch(index_row.getRow());
        key = ((SQLLongint)(index_row.getRow()[1])).getLong();

        if (SanityManager.DEBUG)
            SanityManager.ASSERT(key == 3000);

        index_cc.checkConsistency();

        index_cc.close();
        scan.close();

        REPORT("Ending t_008");

        return(ret_val);
    }

    /**
     * Test unique/nonunique indexes - both positive and negative cases.
     * <p>
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_009(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        REPORT("Starting t_009");

        // NON-UNIQUE INDEX
        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row and insert into base table, remembering it's location.
		DataValueDescriptor[] r1             = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow  index_row1      = new T_SecondaryIndexRow();
        RowLocation          base_rowloc1    = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        ((SQLLongint)r1[0]).setValue(1);
        ((SQLLongint)r1[1]).setValue(1000);

        // Insert the row into the base table;remember its location.
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        if (index_cc.insert(index_row1.getRow()) != 
                ConglomerateController.ROWISDUPLICATE)
        {
            throw T_Fail.testFailMsg(
                "insert of duplicate returned wrong return value:");
        }

        // Delete the only entry and make sure it can be reinserted in same
        // xact.
        DataValueDescriptor[] delete_key = TemplateRow.newU8Row(2);
        ((SQLLongint)delete_key[0]).setValue(1);
        ((SQLLongint)delete_key[1]).setValue(1000);

        if (!t_delete(tc, create_ret.index_conglomid, 
                 delete_key, false))
        {
            throw T_Fail.testFailMsg(
                "t_008: could not delete key.");
        }

        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        tc.commit();

        // UNIQUE INDEX
        create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, true, false, 2, create_ret);

        // Open the base table
        base_cc = tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		index_cc =	tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row and insert into base table, remembering it's location.
		r1              = TemplateRow.newU8Row(2);
        index_row1      = new T_SecondaryIndexRow();
        base_rowloc1    = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        ((SQLLongint)r1[0]).setValue(1);
        ((SQLLongint)r1[1]).setValue(1000);

        // Insert the row into the base table;remember its location.
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
			throw T_Fail.testFailMsg("insert failed");

        // Insert the row into the base table;remember its location.
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 
                ConglomerateController.ROWISDUPLICATE)
        {
            throw T_Fail.testFailMsg(
                "insert of duplicate returned wrong return value:");
        }

        // Delete the only entry and make sure it can be reinserted in same
        // xact.
        delete_key = TemplateRow.newU8Row(2);
        ((SQLLongint)delete_key[0]).setValue(1);
        ((SQLLongint)delete_key[1]).setValue(1000);

        if (!t_delete(tc, create_ret.index_conglomid, 
                 delete_key, false))
        {
            throw T_Fail.testFailMsg(
                "t_008: could not delete key.");
        }


        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        REPORT("Ending t_009");

        return(true);
    }

    /**
     * Test restoreToNull
     *
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_010(TransactionController tc)
        throws StandardException, T_Fail
    {
        REPORT("Starting t_006");

        B2I testbtree = new B2I();

        // test restoreToNull()
        testbtree.restoreToNull();

        if (!(testbtree.isNull()))
            throw T_Fail.testFailMsg("bad restoreToNull/isNull");

        // test bad container open not working.
        try
        {

            // the following open should fail with a containerNotFound error
            
            // for testing purposes - assume TransactionController can be casted
            TransactionManager tm = (TransactionManager) tc;
            ConglomerateController cc = 
                testbtree.open(
                    tm, tm.getRawStoreXact(), false, 0, 0, 
                    (LockingPolicy) null, 
                    null, null);

            throw T_Fail.testFailMsg("bad open succeeded.");
        }
        catch(StandardException t)
        {
            // expected path comes here.
        }


        // create the base table
        DataValueDescriptor[]   base_row        = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow     index_row1      = new T_SecondaryIndexRow();

        long base_conglomid = 
            tc.createConglomerate(
                "heap",   // create a heap conglomerate
                base_row, // base table template row
                null, //column sort order - not required for heap
                null, //default collation
                null,     // default properties
                TransactionController.IS_DEFAULT);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        RowLocation         base_rowloc1    = base_cc.newRowLocationTemplate();

        index_row1.init(base_row, base_rowloc1, 3);

        // create the secondary index
        Properties properties = 
            createProperties(
                null,           // no current properties list 
                false,          // don't allow duplicates
                3,   // index on all base row cols + row location
                2,   // non-unique index
                true,           // maintain parent links
                -42, // fake base conglom for now
                2);      // row loc in last column

        TransactionManager tm = (TransactionManager) tc;

        // test bad property
        try
        {
            testbtree.create(
                tm, -1, ContainerHandle.DEFAULT_ASSIGN_ID, index_row1.getRow(),
                null, null, null, TransactionController.IS_TEMPORARY); 

            throw T_Fail.testFailMsg("bad create succeeded.");
        }
        catch (StandardException t)
        {
            // expected path comes here.
        }

        // try bad properties

        // create the secondary index
        properties = createProperties(
                            null,           // no current properties list 
                            false,          // don't allow duplicates
                            3,   // index on all base row cols + row location
                            1,   // 1 unique field leaving 1 non key field 
                                 // other than the rowlocation - should not be
                                 // allowed.
                            true,           // maintain parent links
                            -42, // fake base conglom for now
                            2);      // row loc in last column
        try
        {
            long index_conglomid = 
                tc.createConglomerate(
                    "BTREE",    				// create a btree secondary
                    index_row1.getRow(), // row template
					null, //column sort order - default
                    null, //default collation
                    properties,                 // properties
                    TransactionController.IS_DEFAULT); // not temporary

            throw T_Fail.testFailMsg("bad create succeeded.");

        }
        catch (Throwable t)
        {
            // expected path comes here
        }


        REPORT("Ending t_010");
        return(true);
    }

    /**
     * Test Special cases of split.
     * <p>
     * Testing: restartSplitFor() call in LeafControlRow().
     *
     * The first case is where we split
     * down the tree and reach the leaf, pick a split point, and then find
     * that there is not enough room to insert row into parent branch page.
     *
     * The second case is the same as the first except the calling code is
     * trying to split a branch page and the parent branch page doesn't have
     * room for the row.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_011(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        REPORT("Starting t_011");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, true, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);


		// Create a row.
		T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();
        RowLocation             rowloc    = base_cc.newRowLocationTemplate();
        DataValueDescriptor[]   base_row  = TemplateRow.newU8Row(2);
        base_row[0] = new SQLChar("aaaaaaaaaa");
        index_row.init(base_row, rowloc, 3); 

        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("a", 1000));
        ((SQLLongint)base_row[1]).setValue(1);
        base_cc.insertAndFetchLocation(base_row, rowloc);

        // CAUSE LEAF splitFor to loop:
        // pick numbers so that split will happen in middle of page.  Do this
        // by first inserting last row in table and then insert smaller rows,
        // then insert rows before it until the table is just ready to split
        // the root, and finally insert some shorter rows in such a way as
        // they cause a split but the split point is chosen with one of the
        // larger rows as the descriminator causing 1st splitfor pass to fail
        // and loop back and do a splitFor the larger row.
        
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("m", 1000));
        {
            ((SQLLongint)base_row[1]).setValue(0);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // insert enough rows to make a 2 level btree where if another row
        // with a 1000 byte string would cause a root split.
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("a", 1000));
        for (int i = 0; i < 5; i++)
        {
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // insert a shorter leaf row, such that it will fit in the root, but
        // make the split point pick a longer row descriminator which won't
        // fit in the root page.
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("z", 500));
        for (int i = 10; i > 8; i--)
        {
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

		// Close the conglomerate.
		index_cc.close();

        tc.dropConglomerate(create_ret.index_conglomid);
        tc.dropConglomerate(create_ret.base_conglomid);

        tc.abort();

        REPORT("Ending t_011");

        return(ret_val);
    }
    /**
     * Test Special cases of split.
     * <p>
     * Testing: restartSplitFor() call in BranchControlRow().
     *
     * The second case is the same as the first except the calling code is
     * trying to split a branch page and the parent branch page doesn't have
     * room for the row.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_012(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        REPORT("Starting t_011");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, true, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);


		// Create a row.
		T_SecondaryIndexRow     index_row = new T_SecondaryIndexRow();
        RowLocation             rowloc    = base_cc.newRowLocationTemplate();
        DataValueDescriptor[]   base_row  = TemplateRow.newU8Row(2);
        base_row[0] = new SQLChar("aaaaaaaaaa");
        index_row.init(base_row, rowloc, 3); 

        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("a", 1000));
        ((SQLLongint)base_row[1]).setValue(1);
        base_cc.insertAndFetchLocation(base_row, rowloc);


        // CAUSE BRANCH splitFor to loop:
        // pick numbers so that split will happen in middle of page.  Do this
        // by first inserting last row in table and then insert smaller rows,
        // then insert rows before it until the table is just ready to split
        // the root, and finally insert some shorter rows in such a way as
        // they cause a split but the split point is chosen with one of the
        // larger rows as the descriminator causing 1st splitfor pass to fail
        // and loop back and do a splitFor the larger row.
        

        // insert enough rows so the tree is 3 levels, just ready to go to 
        // 4 levels.
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("ma", 500));
        for (int i = 0; i < 3; i++)
        {
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("m", 1000));
        for (int i = 3; i < 23; i++)
        {
            ((SQLLongint)base_row[1]).setValue(i);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }
        
        ((SQLChar)base_row[0]).setValue(T_b2i.repeatString("a", 600));
        for (int i = 123; i > 111; i--)
        {
            ((SQLLongint)base_row[1]).setValue(i * 2);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }
        {
            ((SQLLongint)base_row[1]).setValue(227);
            base_cc.insertAndFetchLocation(base_row, rowloc);

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // ((B2IController)index_cc).printTree();
        tc.commit();

		// Close the conglomerate.
		index_cc.close();


        REPORT("Ending t_012");

        return(ret_val);
    }

    /**
     * Test backout during critical times of splits.
     * <p>
     * Force logical undo of an operation which generated an internal update
     * of a btree record:
     * case 1:
     *     o insert into unique btree key1, rowlocation_1
     *     o delete from btree        key1, rowlocation_1
     *         - this will mark the record logically deleted.
     *     o insert enough records to move the logically deleted row to another
     *       page to exercise logical undo of the delete.
     *     o insert into btree        key1, rowlocation_2
     *         - this internally will generate a logical update field on the
     *           record.
     *     o insert enough records to move the logically deleted row to another
     *       page to exercise logical undo of the delete.
     *     o abort.
     *
     * case 2:
     *     o same as case 1 but don't change the rowlocation_1 value.  This 
     *       simulates what the language will generate on an update of a key
     *       field.
     *
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_013(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        // SanityManager.DEBUG_SET("LockTrace");

        REPORT("Starting t_013");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, true, false, 5, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create an index row object for the "delete row"
		DataValueDescriptor[]         r1     = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow     index_row1   = new T_SecondaryIndexRow();
        RowLocation             base_rowloc1 = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // Create another index row object for the other inserts.
		DataValueDescriptor[]         r2              =  TemplateRow.newU8Row(2);
        T_SecondaryIndexRow index_row2      = new T_SecondaryIndexRow();
        RowLocation         base_rowloc2    = base_cc.newRowLocationTemplate();

        index_row2.init(r2, base_rowloc2, 3);

        // Commit the create of the tables so that the following aborts don't
        // undo that work.
        tc.commit();

        // CASE 1:
        tc.commit();

        // Open the base table
        base_cc = tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // Open the secondary index
        index_cc =	tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        ((SQLLongint)r1[0]).setValue(1);

        // insert row which will be deleted (key = 100, base_rowloc1):
        ((SQLLongint)r1[1]).setValue(100);
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        // insert enough rows so that the logical undo of the insert will
        // need to search the tree.  The tree has been set to split after
        // 5 rows are on a page, so 10 should be plenty.
        for (int i = 0; i < 10; i++)
        {
            ((SQLLongint)r2[1]).setValue(i);

            // Insert the row into the base table;remember its location.
            base_cc.insertAndFetchLocation(r2, base_rowloc2);

            // Insert the row into the secondary index.
            if (index_cc.insert(index_row2.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // delete row which was inserted (key = 100, base_rowloc1):
        if (!t_delete(tc, create_ret.index_conglomid, 
                index_row1.getRow(), false))
        {
            throw T_Fail.testFailMsg(
                "t_008: could not delete key.");
        }
        base_cc.delete(base_rowloc1);

        // insert enough rows so that the logical undo of the delete will
        // need to search the tree.  The tree has been set to split after
        // 5 rows are on a page, so 10 should be plenty.
        for (int i = 10; i < 20; i++)
        {
            ((SQLLongint)r2[1]).setValue(i);

            // Insert the row into the base table;remember its location.
            base_cc.insertAndFetchLocation(r2, base_rowloc2);

            // Insert the row into the secondary index.
            if (index_cc.insert(index_row2.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // insert row which will be deleted (key = 100, base_rowloc1):
        ((SQLLongint)r1[1]).setValue(100);
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        // insert enough rows so that the logical undo of the update field will
        // need to search the tree.  The tree has been set to split after
        // 5 rows are on a page, so 10 should be plenty.
        for (int i = 20; i < 30; i++)
        {
            ((SQLLongint)r2[1]).setValue(i);

            // Insert the row into the base table;remember its location.
            base_cc.insertAndFetchLocation(r2, base_rowloc2);

            // Insert the row into the secondary index.
            if (index_cc.insert(index_row2.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // RESOLVE (mikem) - check that the right row is at key 100.
        

        tc.abort();

        // index check - there should be no records left.
        ScanController empty_scan = 
            tc.openScan(create_ret.index_conglomid, false,
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
						(FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

        if (empty_scan.next())
            throw T_Fail.testFailMsg("t_002: there are still rows in table.");

        tc.commit();
        REPORT("Ending t_013");

        return true;
    }

    /**
     * Test getTableProperties() of BTreeController.
     * <p>
     *
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_014(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        // SanityManager.DEBUG_SET("LockTrace");

        REPORT("Starting t_014");


        // create the base table
        DataValueDescriptor[] base_row        = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row1      = new T_SecondaryIndexRow();

        long base_conglomid = 
            tc.createConglomerate(
                "heap",   // create a heap conglomerate
                base_row, // base table template row
                null, //column sort order - not required for heap
                null, //default collation
                null,     // default properties
                TransactionController.IS_DEFAULT);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        RowLocation         base_rowloc1    = base_cc.newRowLocationTemplate();

        index_row1.init(base_row, base_rowloc1, 3);

        // create the secondary index
        Properties properties = 
            createProperties(
                null,           // no current properties list 
                false,          // don't allow duplicates
                3,              // index on all base row cols + row location
                2,              // non-unique index
                true,           // maintain parent links
                base_conglomid, // fake base conglom for now
                2);             // row loc in last column

        properties.put(Property.PAGE_SIZE_PARAMETER,           "8192");
        properties.put(RawStoreFactory.PAGE_RESERVED_SPACE_PARAMETER, "99");
        properties.put(RawStoreFactory.MINIMUM_RECORD_SIZE_PARAMETER, "42");

        TransactionManager tm = (TransactionManager) tc;

		// Create a index.
		long conglomid = 
            tc.createConglomerate(
                "BTREE",       // create a heap conglomerate
                index_row1.getRow(), // 1 column template.
				null, //column sort order - default
                null, //default collation
                properties,         // default properties
                TransactionController.IS_DEFAULT);       // not temporary

		// Open the conglomerate.
		ConglomerateController cc =	
            tc.openConglomerate(
                conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // verify that input properties were used.
        Properties ret_prop = new Properties();
        ret_prop.put(Property.PAGE_SIZE_PARAMETER,           "");
        ret_prop.put(RawStoreFactory.PAGE_RESERVED_SPACE_PARAMETER, "");
        ret_prop.put(RawStoreFactory.MINIMUM_RECORD_SIZE_PARAMETER,   "");

        cc.getTableProperties(ret_prop);

        if (ret_prop.getProperty(Property.PAGE_SIZE_PARAMETER).
                compareTo("8192") != 0         ||
            ret_prop.getProperty(RawStoreFactory.PAGE_RESERVED_SPACE_PARAMETER).
                compareTo("0") != 0           ||
            ret_prop.getProperty(RawStoreFactory.MINIMUM_RECORD_SIZE_PARAMETER).
                compareTo("1") != 0)
        {
			throw T_Fail.testFailMsg(
                "(getTableProperties) Did not get expected table propertes." +
                "\nGot pageSize = " + 
                    ret_prop.getProperty(Property.PAGE_SIZE_PARAMETER) +
                "\nGot reserved = " +
                    ret_prop.getProperty(
                        RawStoreFactory.PAGE_RESERVED_SPACE_PARAMETER) +
                "\nGot minimum record size = " +
                    ret_prop.getProperty(
                        RawStoreFactory.MINIMUM_RECORD_SIZE_PARAMETER));
        }

        tc.commit();

        REPORT("Ending t_014");
        return(true);
    }

    /**
     * Test latch release during critical time during row level locking.
     * <p>
     * Use trace points to force errors in split at critical points:
     *     leaf_split_abort{1,2,3,4}
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_015(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        // SanityManager.DEBUG_SET("LockTrace");

        REPORT("Starting t_015");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        if (!(index_cc instanceof B2IController))
        {
			throw T_Fail.testFailMsg("openConglomerate returned wrong type");
        }

        index_cc.checkConsistency();

		// Create a row and insert into base table, remembering it's location.
		DataValueDescriptor[] r1           = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row1   = new T_SecondaryIndexRow();
        RowLocation           base_rowloc1 = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // Commit the create of the tables so that the following aborts don't
        // undo that work.
        tc.commit();

        // Now load up the table with multiple pages of data.

        // Open the base table
        base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // Open the secondary index
        index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // now insert enough rows to cause failure
        for (int i = 100; i > 0; i -= 2)
        {
            ((SQLLongint)r1[0]).setValue(2);
            ((SQLLongint)r1[1]).setValue(i);

            // Insert the row into the base table;remember its location.
            base_cc.insertAndFetchLocation(r1, base_rowloc1);

            // Insert the row into the secondary index.
            if (index_cc.insert(index_row1.getRow()) != 0)
            {
                throw T_Fail.testFailMsg("insert failed");
            }
        }

        // Now try simulated lock wait/latch release paths through the code.
        String[] latch_debug_strings = {
            "B2iRowLocking3_1_lockScanRow1",
            "B2iRowLocking3_2_lockScanRow1",
            "BTreeScan_positionAtStartPosition1",
            // "BTreeScan_reposition1",
            "BTreeScan_fetchNextGroup1",
        };

        for (int errs = 0; errs < latch_debug_strings.length; errs++)
        {
            REPORT("Doing latch release tests: " + latch_debug_strings[errs]);

            // set the debug flag, which will cause a simulated lock wait
            // latch release path through the code.
            if (SanityManager.DEBUG)
                SanityManager.DEBUG_SET(latch_debug_strings[errs]);

            // Just scan the rows and make sure you see them all, mostly just
            // a test to make sure no errors are thrown by the latch release
            // code paths.
            scan = tc.openScan(create_ret.index_conglomid, false, 
                    0,
                    TransactionController.MODE_RECORD,
                    TransactionController.ISOLATION_SERIALIZABLE,
                    (FormatableBitSet) null,
                    null, ScanController.NA,
                    null,
                    null, ScanController.NA);

            int row_count = 0;
            while (scan.next())
            {
                row_count++;
            }

            scan.close();

            if (row_count != 50)
                throw T_Fail.testFailMsg("wrong scan count = " + row_count);
        }

        tc.abort();
        REPORT("Ending t_015");

        return true;
    }

    /**
     * Test deadlocks during critical times of row level locking.
     * <p>
     * Use trace points to force errors in split at critical points:
     *     leaf_split_abort{1,2,3,4}
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_016(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        // SanityManager.DEBUG_SET("LockTrace");

        REPORT("Starting t_016");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        if (!(index_cc instanceof B2IController))
        {
			throw T_Fail.testFailMsg("openConglomerate returned wrong type");
        }

        index_cc.checkConsistency();

		// Create a row and insert into base table, remembering it's location.
		DataValueDescriptor[] r1            = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row1    = new T_SecondaryIndexRow();
        RowLocation           base_rowloc1  = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // Commit the create of the tables so that the following aborts don't
        // undo that work.
        tc.commit();

        // Open the base table
        base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // Open the secondary index
        index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // now insert enough rows to cause failure
        for (int i = 100; i > 0; i -= 2)
        {
            ((SQLLongint)r1[0]).setValue(2);
            ((SQLLongint)r1[1]).setValue(i);

            // Insert the row into the base table;remember its location.
            base_cc.insertAndFetchLocation(r1, base_rowloc1);

            // Insert the row into the secondary index.
            if (index_cc.insert(index_row1.getRow()) != 0)
            {
                throw T_Fail.testFailMsg("insert failed");
            }
        }

        tc.abort();

        // Now try simulated deadlocks
        // RESOLVE (Mikem) - test out aborts and errors during inserts.
        String[] deadlock_debug_strings = {
            "B2iRowLocking3_1_lockScanRow2",
            "B2iRowLocking3_2_lockScanRow2",
            // "BTreeController_doIns2",
            "BTreeScan_positionAtStartPosition2",
            // "BTreeScan_reposition2",
            "BTreeScan_fetchNextGroup2"
        };

        for (int errs = 0; errs < deadlock_debug_strings.length; errs++)
        {
            try
            {
                REPORT("Doing deadlock tests: " + deadlock_debug_strings[errs]);

                // set the debug flag, which will cause a simulated lock wait
                // latch release path through the code.
                if (SanityManager.DEBUG)
                    SanityManager.DEBUG_SET(deadlock_debug_strings[errs]);

                // Just scan the rows and make sure you see them all, mostly just
                // a test to make sure no errors are thrown by the latch release
                // code paths.
                scan = tc.openScan(create_ret.index_conglomid, false, 
                        0,
                        TransactionController.MODE_RECORD,
                        TransactionController.ISOLATION_SERIALIZABLE,
                        (FormatableBitSet) null,
                        null, ScanController.NA,
                        null,
                        null, ScanController.NA);

                int row_count = 0;
                while (scan.next())
                {
                    row_count++;
                }

                scan.close();

                throw T_Fail.testFailMsg("expected deadlock");
            }
            catch (StandardException e)
            {
				if (!e.getMessageId().equals(SQLState.DEADLOCK))
					throw e;

                ContextService contextFactory = 
                    ContextService.getFactory();

                // Get the context manager.
                ContextManager cm = contextFactory.getCurrentContextManager();

                if (SanityManager.DEBUG)
                    SanityManager.ASSERT(cm != null);

                cm.cleanupOnError(e, isdbActive());
            }
        }

        tc.commit();
        REPORT("Ending t_016");

        return true;
    }

    /**
     * Test simple btree insert performance 
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_perf(TransactionController tc)
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        REPORT("Starting t_005");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        createCongloms(tc, 2, false, false, 0, create_ret);

		// Open the base conglomerate.
		ConglomerateController base_cc =	
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the index conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
		T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();
        RowLocation             rowloc    = base_cc.newRowLocationTemplate();
        DataValueDescriptor[]   base_row  = TemplateRow.newU8Row(2);
        index_row.init(base_row, rowloc, 3); 

        ((SQLLongint)base_row[0]).setValue(1);
        ((SQLLongint)base_row[1]).setValue(1);
        base_cc.insertAndFetchLocation(base_row, rowloc);

        long startms = System.currentTimeMillis();

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = 0; i < 2000; i++)
        {
            ((SQLLongint)base_row[1]).setValue(i);
            // base_cc.insertAndFetchLocation(base_row, rowloc);
            // ((HeapRowLocation)rowloc).setFrom(0xffffffffffffffffl, 0xfffffff); 

            if (index_cc.insert(index_row.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        // ((B2IController)index_cc).printTree();
        tc.commit();
        long endms = System.currentTimeMillis();
        long elapsedms = endms - startms;

		System.out.println("  Elapsed (ms)      " + elapsedms);
		System.out.println("  inserts/second " + (1000 * 1000 / elapsedms));

		// Close the conglomerate.
		index_cc.close();


        REPORT("Ending t_011");

        return(ret_val);
    }

   private boolean t_desc_scan_test_cases(
    TransactionController   tc,
    long                    index_conglomid,
    T_SecondaryIndexRow     template
    )
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        // run through a predicates as described in the openScan() interface //
        DataValueDescriptor[] start_key  = TemplateRow.newU8Row(1);
        DataValueDescriptor[] stop_key   = TemplateRow.newU8Row(1);


        // test predicate x = 5
        //
        //     result set should be:{5,6,18}, {5,4,17}, {5,6,16}  //descending 
        //
        REPORT("scan (x = 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   stop_key,  ScanController.GT,
                   3, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }
                   
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x > 5 |null |na |5    |GE |{9,1} .. {6,1}|{5,6} .. {9,1} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x > 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GE,
                   3, 21, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x >= 5| null|na |5    |GT |{9,1} .. {5,2}|{4,6} .. {9,1} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x >= 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GT,
                   6, 21, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        //  |x <= 5|5    |GE |null |na |{5,6} .. {1,1}|first .. {5,6} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x <= 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   null,  ScanController.NA,
                   8, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }
        //
        //  +---------------------------------------------------------+
        //  |pred  |start|key|stop |key|rows returned |rows locked    |
        //  |      |value|op |value|op |              |(serialization)|
        //  +------+-----+---+-----+---+--------------+---------------+
        // 	|x < 5 |5    | GT|null |   |{4,6} .. {1,1}|first .. {4,6} |
        //  +-----------------------------------------+---------------+
        REPORT("scan (x < 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT, 
                   null,
                   null,  ScanController.NA,
                   5, 15, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        //long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        //long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};
        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
	    //  |x >= 5 and x <= 7|{7},  |GE|{5}  |GT|{5,2} .. {7,1}|{4,6} .. {7,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x >= 5 and x <= 7)");
        ((SQLLongint)start_key[0]).setValue(7);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   5, 20, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
	    //  |x = 5 and y > 2  |{5}   |GE|{5,2 |GE|{5,4} .. {5,6}|{5,2} .. {9,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y > 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        ((SQLLongint)stop_key[1]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GE,
                   2, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 5 and y >= 2 |{5}  |GE|{5,2}|GT|{5,2} .. {5,6}|{4,6} .. {9,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y >= 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        ((SQLLongint)stop_key[1]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   3, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 5 and y < 5  | {5,5}} |GE|{5}|GT|{5,2} .. {5,4}|{4,6} .. {5,4}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 5 and y < 5)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   2, 17, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +------------------------------------------------------------------+
        //  |pred             |start|key|stop |key|rows returned|rows locked   |
        //  |                 |value|op |value|op |             |(serialized)  |
        //  +-----------------+------+--+-----+--+--------------+--------------+
        // 	|x = 2            | {2}  |GE| {2} |GT|none          |{1,1} .. {1,1}|
        // 	+------------------------------------------------------------------+
        REPORT("scan (x = 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +-----------------------------+
        // 	|minium on btree - row locked    |
        // 	+-----------------------------+
        //
        REPORT("minimum on btree, row locked.");
		// the following function actually returns 
		// the minimum values because the index is in descending order
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no min.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong minimum found.");
            }
        }

        //  +-----------------------------+
        // 	|min on btree - table locked    |
        // 	+-----------------------------+
        //
        REPORT("min on btree, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no min.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong min found.");
            }
        }

        //  +-----------------------------+
        // 	|min on btree - row locked    |
        // 	+-----------------------------+
        //
        REPORT("min on btree, row locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong min found.");
            }
        }

        //  +-----------------------------+
        // 	|min on btree - table locked    |
        // 	+-----------------------------+
        //
        REPORT("min on btree, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no min.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong min found.");
            }
        }

        return(ret_val);
    }


    /**
     * Test BTree.openScan(), BtreeScan.init(), BtreeScan.next(), 
     * BtreeScan.fetch() with descending indexes.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_017(TransactionController tc)
        throws StandardException, T_Fail
    {
        T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();

        // base row template - last column is just to make row long so that
        // multiple pages are spanned.
        DataValueDescriptor[] base_row             = TemplateRow.newU8Row(4);
        base_row[3] = new SQLChar();

        String   string_1500char = new String();
        for (int i = 0; i < 300; i++)
            string_1500char += "mikem";

        boolean     ret_val = true;
        long        value   = -1;
        long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};

        // set of deleted rows to make scans more interesting
        long d_col1[] ={ 0,  2,  3,  4,  4,  5,  5,  5,  6,  7,  8, 10, 11, 12};
        long d_col2[] ={ 1,  1,  2,  3,  5,  0,  3,  5,  0,  0,  1, 42, 42, 1};
        long d_col3[] ={91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104};

		

        REPORT("Starting t_017");

        // create the base table
        long base_conglomid = 
            tc.createConglomerate(
                "heap",                            // create a heap conglomerate
                base_row,                          // base table template row
				null, //column sort order - not required for heap
                null, //default collation
                null,                              // default properties
                TransactionController.IS_DEFAULT); // not temporary

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // initialize the secondary index row - pointing it at base row
        index_row.init(base_row, base_cc.newRowLocationTemplate(), 5);

        Properties properties = 
            createProperties(
                null,               // no current properties list 
                false,              // don't allow duplicates
                5,                  // 4 columns in index row
                5,                  // non-unique index
                true,               // maintain parent links
                base_conglomid,     // base conglom id
                4);                 // row loc in last column

		// create the index with all the columns in descending order
        ColumnOrdering order[] = new ColumnOrdering[5];
		order[0] =  new T_ColumnOrderingImpl(0, false); // descending
		order[1] =  new T_ColumnOrderingImpl(1, false); // descending
		order[2] =  new T_ColumnOrderingImpl(2, false); // descending
		order[3] =  new T_ColumnOrderingImpl(3, false); // descending
		order[4] =  new T_ColumnOrderingImpl(4, true); // asccending

		long index_conglomid = 
            tc.createConglomerate(
                "BTREE",    				// create a btree secondary
                index_row.getRow(),         // row template
				order, //column sort order - default
                null, //default collation
                properties,                 // properties
                TransactionController.IS_DEFAULT);   // not temporary

		// Open the conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
        T_SecondaryIndexRow template = new T_SecondaryIndexRow();
        RowLocation         row_loc  = base_cc.newRowLocationTemplate();
        template.init(base_row, row_loc, 5);

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

        ((B2IController)index_cc).debugConglomerate();

        ret_val = t_desc_scan_test_cases(tc, index_conglomid, template);

        // insert and delete some interesting rows, deleted space management
        // may or may not clean these up.
        for (int i = d_col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(d_col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(d_col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(d_col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");

            // now delete the row.
            base_cc.delete(row_loc);

            ScanController delete_scan = 
                tc.openScan(index_conglomid, false,
                            TransactionController.OPENMODE_FORUPDATE,
                            TransactionController.MODE_RECORD,
                            TransactionController.ISOLATION_SERIALIZABLE,
                            (FormatableBitSet) null,
                            template.getRow(), ScanController.GE, 
                            null,
                            template.getRow(), ScanController.GT); 

            if (!delete_scan.next())
            {
                throw T_Fail.testFailMsg("delete could not find key");
            }
            else
            {
                delete_scan.delete();

                if (delete_scan.next())
                    throw T_Fail.testFailMsg("delete found more than one key");
            }

            delete_scan.close();
        }

        ret_val = t_desc_scan_test_cases(tc, index_conglomid, template);


		// Close the conglomerate.
		index_cc.close();

        tc.commit();
        REPORT("Ending t_017");

        return(ret_val);
    }


	/*  test cases for ASC DESC ASC DESC column sort order index 
	SORTED DATA
	col1, col2, col3
	AS DS AS  -- sort order
	1, 1, 11
	3, 2, 12
	4, 6, 15
	4, 4, 14
	4, 2, 13
	5, 6, 18
	5, 4, 17
	5, 2, 16
	6, 1, 19
	7, 1, 20
	9, 1, 21
	*/

	private boolean t_ascdesc_scan_test_cases(
    TransactionController   tc,
    long                    index_conglomid,
    T_SecondaryIndexRow     template
    )
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        // run through a predicates as described in the openScan() interface //
        DataValueDescriptor[] start_key  = TemplateRow.newU8Row(1);
        DataValueDescriptor[] stop_key   = TemplateRow.newU8Row(1);


        // test predicate x = 5
        //
        //     result set should be:{5,6,18}, {5,4,17}, {5,6,16}   
        //
        REPORT("scan (x = 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   stop_key,  ScanController.GT,
                   3, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }
 

        REPORT("scan (x > 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT, 
                   null,
                   null,  ScanController.NA,
                   3, 19, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        REPORT("scan (x >= 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   null,  ScanController.NA,
                   6, 16, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x <= 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GT,
                   8, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }
  
        REPORT("scan (x < 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GE,
                   5, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x >= 5 and x <= 7)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(7);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   5, 16, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x = 5 and y > 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        ((SQLLongint)stop_key[1]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GE,
                   2, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }


        REPORT("scan (x = 5 and y >= 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        ((SQLLongint)stop_key[1]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   3, 18, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }


        REPORT("scan (x = 5 and y < 5)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   2, 17, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        REPORT("scan (x = 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        //  +-----------------------------+---------------
        // 	|last leaf last row  on btree - row locked    |
        // 	+-----------------------------+---------------
        //
        REPORT("minimum on btree, row locked.");

        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found last row in the last leaf.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf.");
            }
        }

        //  +-----------------------------+--------------
        // 	|last row in the last leaf - table locked    |
        // 	+-----------------------------+--------------
        //
        REPORT("last row in the last leaf, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no min.");
        }
        else
        {
            // make sure right last row in the last leaf  found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        //  +-----------------------------+-----------
        // 	|last row in the last leaf- row locked    |
        // 	+-----------------------------+-----------
        //
        REPORT("last row in the last leaf, row locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right last row in the last leaf found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        //  +-----------------------------+-------------
        // 	|last row in the last leaf- table locked    |
        // 	+-----------------------------+-------------
        //
        REPORT("last row in the last leaf, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no last row in the last leaf");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 21)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        return(ret_val);
    }


 /**
     * Test BTree.openScan(), BtreeScan.init(), BtreeScan.next(), 
     * BtreeScan.fetch() with alternating ascending and descending coulmn 
	 * sort order indexes.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_018(TransactionController tc)
        throws StandardException, T_Fail
    {
        T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();

        // base row template - last column is just to make row long so that
        // multiple pages are spanned.
        DataValueDescriptor[] base_row             = TemplateRow.newU8Row(4);
        base_row[3] = new SQLChar();

        String   string_1500char = new String();
        for (int i = 0; i < 300; i++)
            string_1500char += "mikem";

        boolean     ret_val = true;
        long        value   = -1;
        long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};

        // set of deleted rows to make scans more interesting
        long d_col1[] ={ 0,  2,  3,  4,  4,  5,  5,  5,  6,  7,  8, 10, 11, 12};
        long d_col2[] ={ 1,  1,  2,  3,  5,  0,  3,  5,  0,  0,  1, 42, 42, 1};
        long d_col3[] ={91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104};

		

        REPORT("Starting t_018");

        // create the base table
        long base_conglomid = 
            tc.createConglomerate(
                "heap",                            // create a heap conglomerate
                base_row,                          // base table template row
				null, //column sort order - not required for heap
                null, //default collation
                null,                              // default properties
                TransactionController.IS_DEFAULT); // not temporary

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // initialize the secondary index row - pointing it at base row
        index_row.init(base_row, base_cc.newRowLocationTemplate(), 5);

        Properties properties = 
            createProperties(
                null,               // no current properties list 
                false,              // don't allow duplicates
                5,                  // 4 columns in index row
                5,                  // non-unique index
                true,               // maintain parent links
                base_conglomid,     // base conglom id
                4);                 // row loc in last column

		// create the index with all the columns in descending order
        ColumnOrdering order[] = new ColumnOrdering[5];
		order[0] =  new T_ColumnOrderingImpl(0, true); // Ascending
		order[1] =  new T_ColumnOrderingImpl(1, false); // descending
		order[2] =  new T_ColumnOrderingImpl(2, true); // Ascending
		order[3] =  new T_ColumnOrderingImpl(3, false); // descending
		order[4] =  new T_ColumnOrderingImpl(4, true); // asccending

		long index_conglomid = 
            tc.createConglomerate(
                "BTREE",    				// create a btree secondary
                index_row.getRow(),         // row template
				order, //column sort order - default
                null, //default collation
                properties,                 // properties
                TransactionController.IS_DEFAULT);   // not temporary

		// Open the conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
        T_SecondaryIndexRow template = new T_SecondaryIndexRow();
        RowLocation         row_loc  = base_cc.newRowLocationTemplate();
        template.init(base_row, row_loc, 5);

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

        ((B2IController)index_cc).debugConglomerate();

        ret_val = t_ascdesc_scan_test_cases(tc, index_conglomid, template);

        // insert and delete some interesting rows, deleted space management
        // may or may not clean these up.
        for (int i = d_col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(d_col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(d_col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(d_col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");

            // now delete the row.
            base_cc.delete(row_loc);

            ScanController delete_scan = 
                tc.openScan(index_conglomid, false,
                            TransactionController.OPENMODE_FORUPDATE,
                            TransactionController.MODE_RECORD,
                            TransactionController.ISOLATION_SERIALIZABLE,
                            (FormatableBitSet) null,
                            template.getRow(), ScanController.GE, 
                            null,
                            template.getRow(), ScanController.GT); 

            if (!delete_scan.next())
            {
                throw T_Fail.testFailMsg("delete could not find key");
            }
            else
            {
                delete_scan.delete();

                if (delete_scan.next())
                    throw T_Fail.testFailMsg("delete found more than one key");
            }

            delete_scan.close();
        }

        ret_val = t_ascdesc_scan_test_cases(tc, index_conglomid, template);


		// Close the conglomerate.
		index_cc.close();

        tc.commit();
        REPORT("Ending t_018");

        return(ret_val);
    }



	/*  test cases for ASC DESC DESC ASC column sort order index 
	SORTED DATA 
	col1, col2, col3
	DS AS DS  -- sort order
	9, 1, 21
    7, 1, 20
	6, 1, 19
	5, 2, 16
	5, 4, 17
	5, 6, 18
	4, 2, 13
	4, 4, 14
	4, 6, 15
	3, 1, 12
	1, 1, 11
	*/
	private boolean t_ascdesc1_scan_test_cases(
    TransactionController   tc,
    long                    index_conglomid,
    T_SecondaryIndexRow     template
    )
        throws StandardException, T_Fail
    {
        boolean     ret_val = true;

        // run through a predicates as described in the openScan() interface //
        DataValueDescriptor[] start_key  = TemplateRow.newU8Row(1);
        DataValueDescriptor[] stop_key   = TemplateRow.newU8Row(1);


        REPORT("scan (x = 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   stop_key,  ScanController.GT,
                   3, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }
 
        REPORT("scan (x > 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GE,
                   3, 21, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        REPORT("scan (x >= 5)");
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA, 
                   null,
                   stop_key,  ScanController.GT,
                   6, 16, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

  
        REPORT("scan (x <= 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE, 
                   null,
                   null,  ScanController.NA,
                   8, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }
  
        REPORT("scan (x < 5)");
        ((SQLLongint)start_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT, 
                   null,
                   null,  ScanController.NA,
                   5, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x >= 5 and x <= 7)");
        ((SQLLongint)start_key[0]).setValue(7);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   5, 16, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x = 5 and y > 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT,
                   null,
                   stop_key,  ScanController.GT,
                   2, 17, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        REPORT("scan (x = 5 and y >= 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(5);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   3, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        REPORT("scan (x = 5 and y < 5)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(5);
        ((SQLLongint)stop_key[0]).setValue(5);
		((SQLLongint)stop_key[1]).setValue(5);

        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   2, 16, T_QualifierTest.ORDER_FORWARD))
        {
            ret_val = false;
        }

        REPORT("scan (x = 2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }

        // values '2' does not exist as such in the data set 
        REPORT("scan (x > 2)");
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA,
                   null,
                   stop_key,  ScanController.GE,
                   10, 12, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }



        // values '2' does not exist as such in the data set 
        REPORT("scan (x >= 2)");
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   null, ScanController.NA,
                   null,
                   stop_key,  ScanController.GT,
                   10, 12, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }


        // values '2' does not exist as such in the data set 
        REPORT("scan (x < 2)");
        start_key  = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT,
                   null,
                   null,  ScanController.NA,
                   1, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }


        // values '2' does not exist as such in the data set 
        REPORT("scan (x <= 2)");
        start_key  = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   null,  ScanController.NA,
                   1, 11, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }


        REPORT("scan (x >= 2 and x <= 7)");
        ((SQLLongint)start_key[0]).setValue(7);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   9, 12, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x = 2 and y > 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GT,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x = 2 and y >= 2)");
        start_key  = TemplateRow.newU8Row(2);
        stop_key   = TemplateRow.newU8Row(1);
        ((SQLLongint)start_key[0]).setValue(2);
        ((SQLLongint)start_key[1]).setValue(2);
        ((SQLLongint)stop_key[0]).setValue(2);
        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   0, 0, T_QualifierTest.ORDER_NONE))
        {
            ret_val = false;
        }

        REPORT("scan (x = 4 and y <=2)");
        start_key  = TemplateRow.newU8Row(1);
        stop_key   = TemplateRow.newU8Row(2);
        ((SQLLongint)start_key[0]).setValue(4);
        ((SQLLongint)stop_key[0]).setValue(4);
		((SQLLongint)stop_key[1]).setValue(2);

        if (!T_QualifierTest.t_scan(tc, index_conglomid, template.getRow(),
                   template.getRow(),
                   start_key, ScanController.GE,
                   null,
                   stop_key,  ScanController.GT,
                   1, 13, T_QualifierTest.ORDER_DESC))
        {
            ret_val = false;
        }




        //  +-----------------------------+---------------
        // 	|last leaf last row  on btree - row locked    |
        // 	+-----------------------------+---------------
        //
        REPORT("last row in the last leaf, row locked.");

        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found last row in the last leaf.");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf.");
            }
        }

        //  +-----------------------------+--------------
        // 	|last row in the last leaf - table locked    |
        // 	+-----------------------------+--------------
        //
        REPORT("last row in the last leaf, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_SERIALIZABLE,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no min.");
        }
        else
        {
            // make sure right last row in the last leaf  found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        //  +-----------------------------+-----------
        // 	|last row in the last leaf- row locked    |
        // 	+-----------------------------+-----------
        //
        REPORT("last row in the last leaf, row locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no max.");
        }
        else
        {
            // make sure right last row in the last leaf found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        //  +-----------------------------+-------------
        // 	|last row in the last leaf- table locked    |
        // 	+-----------------------------+-------------
        //
        REPORT("last row in the last leaf, table locked.");
        if (!tc.fetchMaxOnBtree(
                index_conglomid,
                0,
                TransactionController.MODE_TABLE,
                TransactionController.ISOLATION_READ_COMMITTED,
                (FormatableBitSet) null,
                template.getRow()))
        {
            throw T_Fail.testFailMsg("found no last row in the last leaf");
        }
        else
        {
            // make sure right min was found.
            long key = ((SQLLongint) template.getRow()[2]).getLong();
            
            if (key != 11)
            {
                throw T_Fail.testFailMsg("wrong last row in the last leaf found.");
            }
        }

        return(ret_val);
    }


 /**
     * Test BTree.openScan(), BtreeScan.init(), BtreeScan.next(), 
     * BtreeScan.fetch() with alternating ascending and descending coulmn 
	 * sort order indexes.
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     */
    protected boolean t_019(TransactionController tc)
        throws StandardException, T_Fail
    {
        T_SecondaryIndexRow index_row = new T_SecondaryIndexRow();

        // base row template - last column is just to make row long so that
        // multiple pages are spanned.
        DataValueDescriptor[] base_row             = TemplateRow.newU8Row(4);
        base_row[3] = new SQLChar();

        String   string_1500char = new String();
        for (int i = 0; i < 300; i++)
            string_1500char += "mikem";

        boolean     ret_val = true;
        long        value   = -1;
        long        col1[]  = { 1,  3,  4,  4,  4,  5,  5,  5,  6,  7,  9};
        long        col2[]  = { 1,  1,  2,  4,  6,  2,  4,  6,  1,  1,  1};
        long        col3[]  = {11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};

        // set of deleted rows to make scans more interesting
        long d_col1[] ={ 0,  2,  3,  4,  4,  5,  5,  5,  6,  7,  8, 10, 11, 12};
        long d_col2[] ={ 1,  1,  2,  3,  5,  0,  3,  5,  0,  0,  1, 42, 42, 1};
        long d_col3[] ={91, 92, 93, 94, 95, 96, 97, 98, 99,100,101,102,103,104};

		

        REPORT("Starting t_019");

        // create the base table
        long base_conglomid = 
            tc.createConglomerate(
                "heap",                            // create a heap conglomerate
                base_row,                          // base table template row
				null, //column sort order - not required for heap
                null, //default collation
                null,                              // default properties
                TransactionController.IS_DEFAULT); // not temporary

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // initialize the secondary index row - pointing it at base row
        index_row.init(base_row, base_cc.newRowLocationTemplate(), 5);

        Properties properties = 
            createProperties(
                null,               // no current properties list 
                false,              // don't allow duplicates
                5,                  // 4 columns in index row
                5,                  // non-unique index
                true,               // maintain parent links
                base_conglomid,     // base conglom id
                4);                 // row loc in last column

		// create the index with all the columns in descending order
        ColumnOrdering order[] = new ColumnOrdering[5];
		order[0] =  new T_ColumnOrderingImpl(0, false); // Descending
		order[1] =  new T_ColumnOrderingImpl(1, true); // Ascending
		order[2] =  new T_ColumnOrderingImpl(2, true); // Ascending
		order[3] =  new T_ColumnOrderingImpl(3, false); // descending
		order[4] =  new T_ColumnOrderingImpl(4, true); // asccending

		long index_conglomid = 
            tc.createConglomerate(
                "BTREE",    				// create a btree secondary
                index_row.getRow(),         // row template
				order, //column sort order - default
                null, //default collation
                properties,                 // properties
                TransactionController.IS_DEFAULT);   // not temporary

		// Open the conglomerate.
		ConglomerateController index_cc =	
            tc.openConglomerate(
                index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Create a row.
        T_SecondaryIndexRow template = new T_SecondaryIndexRow();
        RowLocation         row_loc  = base_cc.newRowLocationTemplate();
        template.init(base_row, row_loc, 5);

        // insert them in reverse order just to make sure btree is sorting them
        for (int i = col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");
        }

        index_cc.checkConsistency();

        ((B2IController)index_cc).debugConglomerate();

        ret_val = t_ascdesc1_scan_test_cases(tc, index_conglomid, template);

        // insert and delete some interesting rows, deleted space management
        // may or may not clean these up.
        for (int i = d_col1.length - 1; i >= 0; i--)
        {
            ((SQLLongint)(template.getRow()[0])).setValue(d_col1[i]);
            ((SQLLongint)(template.getRow()[1])).setValue(d_col2[i]);
            ((SQLLongint)(template.getRow()[2])).setValue(d_col3[i]);
            base_row[3] = new SQLChar(string_1500char);

            base_cc.insertAndFetchLocation(base_row, row_loc);

            // Insert the row.
            // System.out.println("Adding record (" + -(i - (col1.length -1)) +
            //                ")" + template);
            if (index_cc.insert(template.getRow()) != 0)
                throw T_Fail.testFailMsg("insert failed");

            // now delete the row.
            base_cc.delete(row_loc);

            ScanController delete_scan = 
                tc.openScan(index_conglomid, false,
                            TransactionController.OPENMODE_FORUPDATE,
                            TransactionController.MODE_RECORD,
                            TransactionController.ISOLATION_SERIALIZABLE,
                            (FormatableBitSet) null,
                            template.getRow(), ScanController.GE, 
                            null,
                            template.getRow(), ScanController.GT); 

            if (!delete_scan.next())
            {
                throw T_Fail.testFailMsg("delete could not find key");
            }
            else
            {
                delete_scan.delete();

                if (delete_scan.next())
                    throw T_Fail.testFailMsg("delete found more than one key");
            }

            delete_scan.close();
        }

        ret_val = t_ascdesc1_scan_test_cases(tc, index_conglomid, template);


		// Close the conglomerate.
		index_cc.close();

        tc.commit();
        REPORT("Ending t_019");

        return(ret_val);
    }

    /**
     * Test read uncommitted cases on scan.
     * <p>
     *
	 * @exception  StandardException  Standard exception policy.
	 * @exception  T_Fail  Throws T_Fail on any test failure.
     **/
    protected boolean t_020(TransactionController tc)
        throws StandardException, T_Fail
    {
        ScanController scan         = null;

        REPORT("Starting t_020");

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid, 
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// Open the secondary index
		ConglomerateController index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

		// objects used to insert rows into base and index tables.
		DataValueDescriptor[] r1            = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row1    = new T_SecondaryIndexRow();
        RowLocation           base_rowloc1  = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // insert one row into the table/index

        // Open the base table
        base_cc = 
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // Open the secondary index
        index_cc =	
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // insert one row that does not cause failure.
        ((SQLLongint)r1[0]).setValue(2);
        ((SQLLongint)r1[1]).setValue(10000);

        // Insert the row into the base table;remember its location.
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        // Commit the create of the tables so that the following aborts don't
        // undo that work.
        tc.commit();


        // TEST 1 - position a read uncommitted scan on a row which is
        // purged by a split trying to get space on the page, and then see
        // what happens when the scan trys to continue.  This can only happen
        // currently if the same transaction deletes the row and while having
        // the scan open also does inserts onto the same page.  Otherwise the
        // btree scan will maintain a "page scan locks" which will prevent
        // the row from being purged out from underneath it.


        tc.commit();
        REPORT("Ending t_020");

        return true;
    }

    /**
     * Test latch release at critical time during delete on an index scan that
     * uses update locks.
     */
    protected boolean t_021(TransactionController tc)
        throws StandardException, T_Fail
    {
        REPORT("Starting t_021");

        boolean ret_val = true;

        T_CreateConglomRet create_ret = new T_CreateConglomRet();

        // Create the btree so that it only allows 2 rows per page.
        createCongloms(tc, 2, false, false, 2, create_ret);

        // Open the base table
        ConglomerateController base_cc =
            tc.openConglomerate(
                create_ret.base_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // Open the secondary index
        ConglomerateController index_cc =
            tc.openConglomerate(
                create_ret.index_conglomid,
                false,
                TransactionController.OPENMODE_FORUPDATE,
                TransactionController.MODE_RECORD,
                TransactionController.ISOLATION_SERIALIZABLE);

        // objects used to insert rows into base and index tables.
        DataValueDescriptor[] r1            = TemplateRow.newU8Row(2);
        T_SecondaryIndexRow   index_row1    = new T_SecondaryIndexRow();
        RowLocation           base_rowloc1  = base_cc.newRowLocationTemplate();

        index_row1.init(r1, base_rowloc1, 3);

        // insert one row into the table/index
        ((SQLLongint)r1[0]).setValue(1);
        ((SQLLongint)r1[1]).setValue(1);

        // Insert the row into the base table;remember its location.
        base_cc.insertAndFetchLocation(r1, base_rowloc1);

        // Insert the row into the secondary index.
        if (index_cc.insert(index_row1.getRow()) != 0)
            throw T_Fail.testFailMsg("insert failed");

        // Commit the create of the tables.
        tc.commit();

        // Enable the debug code that releases the latch at critical time.
        if (SanityManager.DEBUG) {
            SanityManager.DEBUG_SET("BTreeScan_delete_useUpdateLocks1");
        }

        // Delete the row using the index and update locks. Before DERBY-4083,
        // the call to delete() would fail with record not found if the latch
        // was released.
        DataValueDescriptor[] delete_key = TemplateRow.newU8Row(2);
        ((SQLLongint)delete_key[0]).setValue(1);
        ((SQLLongint)delete_key[1]).setValue(1);
        if (!t_delete(tc, create_ret.index_conglomid, delete_key, true)) {
            ret_val = false;
        }

        // Disable the debug code that releases the latch at critical time.
        if (SanityManager.DEBUG) {
            SanityManager.DEBUG_CLEAR("BTreeScan_delete_useUpdateLocks1");
        }

        tc.commit();
        REPORT("Ending t_021");

        return ret_val;
    }

	public static String repeatString(String data, int repeat) {

		String s = data;
		for (int i = 1; i < repeat; i++)
			s += data;

		return s;
	}
    
    /** Check wheather the database is active or not
     * @return {@code true} if the database is active, {@code false} otherwise
     */
    public boolean isdbActive() {
        LanguageConnectionContext lcc = (LanguageConnectionContext) ContextService
                .getContextOrNull(LanguageConnectionContext.CONTEXT_ID);
        Database db = (Database) (lcc != null ? lcc.getDatabase() : null);
        return (db != null ? db.isActive() : false);
    }

}

class T_CreateConglomRet 
{
    public long                     base_conglomid;
    public long                     index_conglomid;
    // public DataValueDescriptor[]	base_template_row;
    public DataValueDescriptor[]	index_template_row;
}
