/*

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

   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;

import org.apache.derbyTesting.unitTests.harness.T_Generic;
import org.apache.derbyTesting.unitTests.harness.T_Fail;

import org.apache.derby.iapi.store.raw.*;

import org.apache.derby.shared.common.reference.SQLState;

import org.apache.derby.impl.store.raw.log.LogCounter;

import org.apache.derby.iapi.services.context.ContextService;
import org.apache.derby.iapi.services.context.ContextManager;
import org.apache.derby.iapi.services.locks.*;
import org.apache.derby.iapi.services.property.PropertyUtil;
import org.apache.derby.iapi.services.monitor.ModuleFactory;
import org.apache.derby.iapi.services.monitor.Monitor;
import org.apache.derby.shared.common.sanity.SanityManager;

import org.apache.derby.shared.common.error.StandardException;

import org.apache.derby.iapi.store.raw.xact.RawTransaction;
import org.apache.derby.iapi.store.raw.data.RawContainerHandle;
import org.apache.derby.iapi.store.raw.log.LogInstant;

import org.apache.derby.iapi.store.access.conglomerate.LogicalUndo;

import org.apache.derby.iapi.store.access.Qualifier;

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

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


import org.apache.derby.iapi.services.uuid.UUIDFactory;
import org.apache.derby.catalog.UUID;
import org.apache.derby.shared.common.reference.Property;
import org.apache.derby.shared.common.reference.Attribute;
import org.apache.derby.iapi.services.io.FormatableBitSet;

import java.io.*;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.security.AccessController;
import java.util.Properties;


/**
	A protocol unit test for recovery.

    To run, create a derby.properties file in a new directory with the
	contents

	derby.module.test.recovery=org.apache.derbyTesting.unitTests.store.T_Recovery

    Execute

	java -DSetupRecovery=true org.apache.derbyTesting.unitTests.harness.UnitTestMain
	java -DTestRecovery=true org.apache.derbyTesting.unitTests.harness.UnitTestMain
*/

public class T_Recovery extends T_Generic {

	private static final String testService = "RecoveryTest";

	static final String REC_001 = "McLaren";
	static final String REC_002 = "Ferrari";
	static final String REC_003 = "Benetton";
	static final String REC_004 = "Prost";
	static final String REC_005 = "Tyrell";
	static final String REC_006 = "Derby, Natscape, Goatscape, the popular names";
	static final String REC_UNDO = "Lotus";
	static final String REC_NULL = "NULL";

	static final String SP1 = "savepoint1";
	static final String SP2 = "savepoint2";

	static final FormatableBitSet BS_COL_0 = new FormatableBitSet(1);

	private RandomAccessFile filein = null;
	private RandomAccessFile fileout = null;

	private boolean setupRecovery;
	private boolean testRecovery;
	private static final String infoPath = "extinout/T_Recovery.info";


	private static final String SETUP_RECOVERY = "SetupRecovery";
	private static final String TEST_RECOVERY = "TestRecovery";
	private static final String RECOVERY_TESTPATH = "RecoveryTestPath";


	RawStoreFactory	factory;
	LockFactory  lf;
	ContextService contextService;
	UUIDFactory uuidfactory;
	T_Util t_util;

	public T_Recovery() {
		super();
		BS_COL_0.set(0);
	}

	/*
	** Methods required by T_Generic
	*/

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

	/**
	*/
	private void getConfig()
	{
	
		String param = PropertyUtil.getSystemProperty(SETUP_RECOVERY);
		setupRecovery = Boolean.valueOf(param).booleanValue();

		param = PropertyUtil.getSystemProperty(TEST_RECOVERY);
		testRecovery = Boolean.valueOf(param).booleanValue();
	}



	/**
		Tests in here come in pairs (Snnn Rnnn), one to set it up, one to test
		it after recovery.  Information that needs to be passed from the setup
		to the recovery should, ideally, be written out to a database.  For
		now, it is written out as a pair of (key,value) long in the file
		T_Recovery.info.

		To make sure you don't accidently tramples on someone else's key, 
		encode your test number (nnn) by shifting over 32 bits and then add
		your key.  Multiple invocations which needs paramaters saved should
		be encoded futher.

		001 &lt; nnn &lt; 200 -  no recovery undo
		200 &lt; nnn &lt; 400 -  recovery undo

		@exception T_Fail Unexpected behaviour from the API
	 */
	public void runTests() throws T_Fail {

		getConfig();

		if (!(setupRecovery ^ testRecovery))
			throw T_Fail.testFailMsg("One & only one of the SetupRecovery and TestRecovery properties must be set");

		try {

			uuidfactory = getMonitor().getUUIDFactory();
			if (uuidfactory == null) {
				throw T_Fail.testFailMsg("UUIDFactory.MODULE not found");
			}

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

			contextService = getContextService();

			if (testRecovery)
			{
				if (!startPersistentService(testService, startParams))
					throw T_Fail.testFailMsg("Monitor didn't know how to restart service: " + testService);
				factory = (RawStoreFactory) findService(getModuleToTestProtocolName(), testService);

			}
			else					// setup
			{
				// 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());

				// do not make T_Recovery test a source, or it won't run on a
				// syncless configuration
				//
				// remove the service directory to ensure a clean run
				startParams.put(Property.DELETE_ON_CREATE, Boolean.TRUE.toString());

				// keep all log files for diagnostics
				startParams.put(RawStoreFactory.KEEP_TRANSACTION_LOG, "true");

				factory = (RawStoreFactory) createPersistentService(getModuleToTestProtocolName(),
																  testService,
																  startParams);
			}
			
		} catch (StandardException mse) {
			throw T_Fail.exceptionFail(mse);
		}


		if (factory == null) {
			throw T_Fail.testFailMsg(getModuleToTestProtocolName() + " service not started.");
		}
			
		lf = factory.getLockFactory();
			
		if (lf == null) {		
			throw T_Fail.testFailMsg("LockFactory.MODULE not found");		
		}

		// get a utility helper
		t_util = new T_Util(factory, lf, contextService);


		try {

			if (setupRecovery)
			{
				// dmls
				S001();			// insert 1 row
				S002();			// insert a bunch of rows
				S003();			// update row
				S004();			// update field
				S005();			// purge and delete
				S006();			// page allocation
				S007();			// page deallocation
				S008();			// rollback of page deallocation
				S009();			// deallocation and reuse of page
				S010();			// allocation/deallocation with overflow pages
				S011();			// allocate a lot of pages so that > 1
								// allocation pages are needed
				S012();			// test page estimation

				// ddls
				S020();			// create multiple containers
				S022();
				

				// committed transactions
				S100();			// multiple intervening transactions
				S101();			// transaction with rollback to savepoints

				// aborted transaction
				//  - these transactions are rollback during runtime - 

				// incomplete transaction 
				//  - these transactions are rollback during recovery -

				S200();			// 1 incomplete transaction
				S201();			// multiple intervening incomplete transaction
				S202();			// incomplete transaction with rollback to
								// savepoint 
				S203();			// incomplete and committed and aborted
									// transaction with intervening rollback to savepoints
				S204();			// incomplete and committed and aborted
									// internal transactions

				// incomplete transaction with ddls
				S300();			// incomplete transactions with drop containers
				S301();			// incomplete transactions with create containers
				S302();         //purging rows with no data logged
				S303();         //purging long columns with no data logged
				S304();         //pruging long rows with no data logged.
			}

			if (testRecovery || setupRecovery)
			{
				// basic recovery
				R001();
				R002();
				R003();
				R004();
				R005();
				R006();
				R007();
				R008();
				R009();
				R010();
				R011();
				R012();	

				R020();
				R022();

				R100();
				R101();
				R302();

				// the following tests depends on recovery to roll back the
				// changes. Only test them after recovery and not during setup
				if (testRecovery)
				{

					R200();
					R201();
					R202();
					R203();
					R204();

					R300();
					R301();
					R303();
					R304();

					R999();
				}
			}

			if (setupRecovery)
			{
				S999();		// always run this last, this leaves a 1/2
							// written log record at the end of the log
							// and it leaves a page latched
			}

			if (fileout != null)
			{
				fileout.close();
				fileout = null;
			}


			if (filein != null)
			{
				filein.close();
				filein = null;
			}

		} catch (Throwable t) {

			SanityManager.showTrace(t);
			System.out.println("caught exception t " + t);

			t.printStackTrace();

			// this test should exit the JVM as abruptly as possible.
			System.exit(0);
		}
	}

	private long find(long inkey) throws T_Fail
	{
		try 
		{
			if (filein == null)
			{
				// make sure it does exist
				File infoFile = new File(infoPath);
				if (infoFile.exists())
				{
					try
					{
						filein = new RandomAccessFile(infoFile, "r");
					}
					catch (IOException ioe)
					{
						System.out.println("Cannot write to temporary file " +
										   infoPath + 
										   ".  Please make sure it is correct, if not, please set the property " +
										   "RecoveryTestPath=<where temp files should go>");
						throw T_Fail.exceptionFail(ioe);
					}
				}
				else
					return -1;
			}

			filein.seek(0);

			long key;
			while(true)
			{
				key = filein.readLong();
				if (key == inkey)
				{
					long value = filein.readLong();
					// System.out.println("found " + key + " " + value);
					return value;
				}
				filein.readLong();
			}
		} 
		catch (IOException ioe)
		{
			// System.out.println("key not found " + inkey);
			return -1;
		}

	}

	private long key(int test, int param)
	{
		long i = test;
		return ((i << 32) + param);
	}

	private void register(long key, long value)
		 throws T_Fail
	{
		// System.out.println("registering " + key + " " + value);
		try 
		{
			if (fileout == null)
			{
				// make sure it does not exist
				File infofile = new File(infoPath);
				if (infofile.exists())
					infofile.delete();
				
				//if external input output files dir does not exist ,create one
				File ifdir = new File("extinout");
				if(!ifdir.exists())
					ifdir.mkdirs();
				fileout = new RandomAccessFile(infoPath, "rw");
			}

			fileout.writeLong(key);
			fileout.writeLong(value);
		}
		catch (IOException ioe)
		{
			T_Fail.exceptionFail(ioe);
		}
	}


	/* 
	 * test 1 - insert 1 row
	 */
	protected void S001() throws T_Fail, StandardException 
	{
		Transaction t = t_util.t_startTransaction();

		long cid = t_util.t_addContainer(t, 0);

		t_util.t_commit(t);

		ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
		Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
		try
		{

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
	
			RecordHandle r1 = t_util.t_insertAtSlot(page, 0, row1);

			page.unlatch();

			c.close();

			register(key(1, 1), cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		REPORT("setup S001: " + cid);
	}

	/* recover test 1 */
	protected void R001() throws T_Fail, StandardException
	{
		long cid = find(key(1, 1));
		if (cid < 0)
		{
			REPORT("R001 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();

		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFieldCount(page, 0, 1);
		
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			page.unlatch();

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R001: containerId " + cid);

	}

	/*
	 * test 2 - insert a bunch of rows into one container
	 */
	protected void S002() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		long cid = t_util.t_addContainer(t, 0);

		t_util.t_commit(t);

		try
		{	
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			RecordHandle r1 = t_util.t_insertAtSlot(page, 0, row1);

			RecordHandle r2 = t_util.t_insertAtSlot(page, 1, row2);

			RecordHandle r3 = (r2 == null) ? r2 : t_util.t_insertAtSlot(page, 2, row3);

			RecordHandle r4 = (r3 == null) ? r3 : t_util.t_insertAtSlot(page, 3, row4);

			RecordHandle r5 = (r4 == null) ? r4 : t_util.t_insertAtSlot(page, 4, row5);

			REPORT("setup S002: containerId " + cid + " recordCount " + page.recordCount());
	
			register(key(2, 1), cid);
			register(key(2, 2), page.recordCount());
			page.unlatch();
			c.close();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 2 */
	protected void R002() throws T_Fail, StandardException
	{
		long cid = find(key(2,1));
		if (cid < 0)
		{
			REPORT("R002 not run");
			return;
		}

		int recordCount = (int)find(key(2,2));

		Transaction t = t_util.t_startTransaction();

		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);


			t_util.t_checkRecordCount(page, recordCount, recordCount);

			switch(recordCount)
			{
			case 5: t_util.t_checkFetchBySlot(page, 4, REC_005, false, false);
			case 4: t_util.t_checkFetchBySlot(page, 3, REC_004, false, false);
			case 3: t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);
			case 2: t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);
			case 1: t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			}
			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R002: containerId " + cid + " recordCount " + recordCount);

	}

	/*
	 *  test 3 -  update row
	 */
	protected void S003() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
	
			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);

			RecordHandle r1 = t_util.t_insertAtSlot(page, 0, row1);

			t_util.t_checkFetch(page, r1, row1);

		// REPORT("grows the #column in row");
			T_RawStoreRow upd1 = new T_RawStoreRow(3);
			upd1.setColumn(0, (String) null);
			upd1.setColumn(1, REC_003);
			upd1.setColumn(2, REC_004);

			r1 = page.updateAtSlot(0, upd1.getRow(), (FormatableBitSet) null);

			// REPORT("update that shrinks the #columns in row");
			T_RawStoreRow row2 = new T_RawStoreRow(3);
			row2.setColumn(0, REC_001);
			row2.setColumn(1, REC_002);
			row2.setColumn(2, REC_003);

			T_RawStoreRow upd2 = new T_RawStoreRow(REC_005);

			RecordHandle r2 = t_util.t_insertAtSlot(page, 1, row2);
			if (r2 != null) {
				r2 = page.updateAtSlot(1, upd2.getRow(), (FormatableBitSet) null);
			}

			t_util.t_checkFetch(page, r1, upd1);
			// first row should contain (null, REC_003, REC_004)
			DataValueDescriptor column = new SQLChar();

		// 				page, slot, field, column, forUpdate, data
			t_util.t_checkFetchColFromSlot(page, 0, 0, column, true, null);
			t_util.t_checkFetchColFromSlot(page, 0, 1, column, true, REC_003);
			t_util.t_checkFetchColFromSlot(page, 0, 2, column, true, REC_004);


			if (r2 != null)
			{
				t_util.t_checkFetch(page, r2, upd2);
				// second row should contain (REC_005)
				t_util.t_checkFetchColFromSlot(page, 1, 0, column, true, REC_005);
			}


			REPORT("setup S003: containerId " + cid + " recordCount " + page.recordCount());
			register(key(3, 1), cid);
			register(key(3, 2), page.recordCount());

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/*
	 * recover test 3 
	 */
	protected void R003() throws T_Fail, StandardException
	{
		long cid = find(key(3,1));
		if (cid < 0)
		{
			REPORT("R003 not run");
			return;
		}

		int recordCount = (int)find(key(3,2));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, recordCount, recordCount);


		// first row should contain (null, REC_003, REC_004)

			t_util.t_checkFieldCount(page, 0, 3);

			DataValueDescriptor column = new SQLChar();
			t_util.t_checkFetchColFromSlot(page, 0, 0, column, false, null);
			t_util.t_checkFetchColFromSlot(page, 0, 1, column, false, REC_003);
			t_util.t_checkFetchColFromSlot(page, 0, 2, column, false, REC_004);

			if (recordCount == 2)
			{
				// second row should contain (REC_005)
				t_util.t_checkFieldCount(page, 1, 1);
				t_util.t_checkFetchColFromSlot(page, 1, 0, column, false, REC_005);
			}

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
		PASS("R003: containerId " + cid + " recordCount " + recordCount);
	}

	/*
	 * test 4 - update field
	 */
	protected void S004() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row = new T_RawStoreRow(5);
			row.setColumn(0, (String) null);
			row.setColumn(1, REC_004);
			row.setColumn(2, (String) null);
			row.setColumn(3, REC_005);
			row.setColumn(4, REC_005);
		

			RecordHandle rh = t_util.t_insert(page, row);

			DataValueDescriptor col0 = new SQLChar((String)null);
			DataValueDescriptor col1 = new SQLChar(REC_001);
			DataValueDescriptor col2 = new SQLChar(REC_002);
			DataValueDescriptor col3 = new SQLChar((String)null);

			if (page.updateFieldAtSlot(page.FIRST_SLOT_NUMBER, 0, col0, null) == null ||
				page.updateFieldAtSlot(page.FIRST_SLOT_NUMBER, 1, col1, null) == null ||
				page.updateFieldAtSlot(page.FIRST_SLOT_NUMBER, 2, col2, null) == null ||
				page.updateFieldAtSlot(page.FIRST_SLOT_NUMBER, 3, col3, null) == null)
			{
				throw T_Fail.testFailMsg("Failed to update field");
			}

			page.unlatch();
			REPORT("setup S004: containerId " + cid);

			register(key(4,1), cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 4 */
	protected void R004() throws T_Fail,StandardException
	{
		long cid = find(key(4,1));
		if (cid < 0)
		{
			REPORT("R004 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();

		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			// first row should contain (null, REC_001, REC_002, null, REC_005)
			DataValueDescriptor column = new SQLChar();
			t_util.t_checkFetchColFromSlot(page, 0, 0, column, false, null);
			t_util.t_checkFetchColFromSlot(page, 0, 1, column, false, REC_001);
			t_util.t_checkFetchColFromSlot(page, 0, 2, column, false, REC_002);
			t_util.t_checkFetchColFromSlot(page, 0, 3, column, false, null);
			t_util.t_checkFetchColFromSlot(page, 0, 4, column, false, REC_005);

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R004: containerId " + cid );

	}

	/*
	 * test 5 - purge and delete
	 */
	protected void S005() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			long numPurged = 0;

			// row 0
			RecordHandle r1 = t_util.t_insertAtSlot(page, 0, row1);

			// purge slot 0
			page.purgeAtSlot(0, 1, true);
			numPurged++;

			// slot 0
			RecordHandle r2 = t_util.t_insertAtSlot(page, 0, row2);
			if (r2 != null) {
				page.deleteAtSlot(0, true, null);
			}

			// slot 1
			RecordHandle r3 = (r2 == null) ? r2 : t_util.t_insertAtSlot(page, 1, row3);
			if (r3 != null)
			{
				page.deleteAtSlot(1, true, null);
			}

			// slot 2
			RecordHandle r4 = (r3 == null) ? r3 : t_util.t_insertAtSlot(page, 2, row4);

			// slot 3
			RecordHandle r5 = (r4 == null) ? r4 : t_util.t_insertAtSlot(page, 3, row5);
			if (r5 != null)
			{
				// purge slot 1 and 2
				page.purgeAtSlot(1, 2, true);
				numPurged += 2;
			}

			REPORT("setup S005: containerId " + cid + " recordCount " +
				   page.recordCount() + " numPurges " + numPurged);

			register(key(5,1), cid);
			register(key(5,2), page.recordCount());
			register(key(5,3), numPurged);

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 5 */
	protected void R005() throws T_Fail, StandardException
	{
		long cid = find(key(5,1));
		if (cid < 0)
		{
			REPORT("R005 not run");
			return;
		}
		int recordCount = (int)find(key(5,2));
		int numPurged = (int)find(key(5,3));

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, recordCount, 1);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			if (numPurged == 1)
			{
				// REC_002 (deleted), REC_003 (deleted), REC_004
				switch(recordCount)
				{
				case 3:
					t_util.t_checkFetchBySlot(page, 2, REC_004, false, false);

				case 2:
					t_util.t_checkFetchBySlot(page, 1, REC_003, true, false);

				case 1:
					t_util.t_checkFetchBySlot(page, 0, REC_002, true, false);
				}
			}
			else
			{
				// REC_002 (deleted), REC_005
				switch(recordCount)
				{
				case 2:
					t_util.t_checkFetchBySlot(page, 1, REC_005, false, false);

				case 1:
					t_util.t_checkFetchBySlot(page, 0, REC_002, true, false);
					if (!page.isDeletedAtSlot(0))
						throw T_Fail.testFailMsg("record should be deleted");
				}
			}

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R005: containerId " + cid + " recordCount " +
			   recordCount + " numPurges " + numPurged);
	}

	/*
	 * test 6 - page allocation
	 */
	protected void S006() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_insertAtSlot(page, 0, row1);
			long page1_Id = page.getPageNumber();
			page.unlatch();

			page = t_util.t_addPage(c);
			t_util.t_insertAtSlot(page, 0, row2);
			long page2_Id = page.getPageNumber();
			page.unlatch();

			page = t_util.t_addPage(c);
			t_util.t_insertAtSlot(page, 0, row3);
			long page3_Id = page.getPageNumber();
			page.unlatch();

			if (page1_Id == page2_Id ||
				page1_Id == page3_Id ||
				page2_Id == page3_Id)
				throw T_Fail.testFailMsg("not getting new pages");

			REPORT("setup S006: containerId " + cid);

			register(key(6,1), cid);
			register(key(6,2), page1_Id);
			register(key(6,3), page2_Id);
			register(key(6,4), page3_Id);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 6 */
	protected void R006() throws T_Fail, StandardException
	{
		long cid = find(key(6,1));
		if (cid < 0)
		{
			REPORT("R006 not run");
			return;
		}

		long page1_Id = find(key(6,2));
		long page2_Id = find(key(6,3));
		long page3_Id = find(key(6,4));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			if (page1_Id != c.FIRST_PAGE_NUMBER)
				throw T_Fail.testFailMsg("first page != container first page");

			Page page = t_util.t_getPage(c, page1_Id);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			page.unlatch();

			page = t_util.t_getPage(c, page2_Id);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_002, false, false);
			page.unlatch();

			page = t_util.t_getPage(c, page3_Id);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_003, false, false);
			page.unlatch();

			page = t_util.t_getLastPage(c);
			t_util.t_checkPageNumber(page, page3_Id);
			page.unlatch();

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R006: containerId " + cid );
	}

	/*
	 * test 7 - page deallocation
	 */
	protected void S007() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);

			Page page1 = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			long p1 = page1.getPageNumber();

			Page page2 = t_util.t_addPage(c);
			long p2 = page2.getPageNumber();

			Page page3 = t_util.t_addPage(c);
			long p3 = page3.getPageNumber();

			t_util.t_removePage(c, page2);
			t_util.t_removePage(c, page3);
			t_util.t_removePage(c, page1);

			if (page1.isLatched())
				throw T_Fail.testFailMsg("page is still latched after remove");

			if (page2.isLatched())
				throw T_Fail.testFailMsg("page is still latched after remove");

			if (page3.isLatched())
				throw T_Fail.testFailMsg("page is still latched after remove");

			register(key(7,0), cid);
			register(key(7,1), p1);
			register(key(7,2), p2);
			register(key(7,3), p3);

			REPORT("setup S007: containerId " + cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 7 */
	protected void R007() throws T_Fail, StandardException
	{
		long cid = find(key(7,0));
		if (cid < 0)
		{
			REPORT("R007 not run");
			return;
		}

		long p1 = find(key(7,1));
		long p2 = find(key(7,2));
		long p3 = find(key(7,3));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);

			Page p = c.getPage(p1);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p1);

			p = c.getPage(p2);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p2);

			p = c.getPage(p3);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p3);

			p = c.getPage(p3+1);
			if (p != null)
				throw T_Fail.testFailMsg("got a non-existant page " + p3+100);

			p = c.getFirstPage();
			if (p != null)
				throw T_Fail.testFailMsg("got a non-existant first page ");

			p = t_util.t_getLastPage(c);
			if (p != null)
				throw T_Fail.testFailMsg("got a non-existant last page ");

			PASS("R007: containerId " + cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/*
	 * test 8 - page deallocation with rollback
	 */
	protected void S008() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);

			Page page1 = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			long p1 = page1.getPageNumber();

			Page page2 = t_util.t_addPage(c);
			long p2 = page2.getPageNumber();

			Page page3 = t_util.t_addPage(c);
			long p3 = page3.getPageNumber();

			Page page4 = t_util.t_addPage(c);
			long p4 = page4.getPageNumber();

			Page page5 = t_util.t_addPage(c);
			long p5 = page5.getPageNumber();

			t_util.t_removePage(c, page1);
			t_util.t_removePage(c, page3);
			t_util.t_removePage(c, page5);
			t_util.t_commit(t);

			c = t_util.t_openContainer(t, 0, cid, true);
			page3 = t_util.t_getPage(c, p2);
			page1 = t_util.t_getPage(c, p4);

			// page 2 and page 4 are not removed
			t_util.t_removePage(c, page2); 
			t_util.t_removePage(c, page4);

			register(key(8,0), cid);
			register(key(8,1), p1);
			register(key(8,2), p2);
			register(key(8,3), p3);
			register(key(8,4), p4);
			register(key(8,5), p5);

			REPORT("setup S008: containerId " + cid);
		}
		finally
		{
			t_util.t_abort(t);
			t.close();
		}

	}

	/* recover test 8 */
	protected void R008() throws T_Fail, StandardException
	{
		long cid = find(key(8,0));
		if (cid < 0)
		{
			REPORT("R008 not run");
			return;
		}

		long p1 = find(key(8,1));
		long p2 = find(key(8,2));
		long p3 = find(key(8,3));
		long p4 = find(key(8,4));
		long p5 = find(key(8,5));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);

		/* page 1, 3, 5 has been removed, page 2, 4 has not */

			Page p = c.getPage(p1);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p1);

			p = t_util.t_getPage(c,p2);
			p.unlatch();

			p = c.getPage(p3);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p3);

			p = t_util.t_getPage(c,p4);
			p.unlatch();

			p = c.getPage(p5);
			if (p != null)
				throw T_Fail.testFailMsg("got a deallcated page " + p5);

			p = c.getPage(p5+1);
			if (p != null)
				throw T_Fail.testFailMsg("got a non-existant page " + p5+1);

		// make sure get first page skips over p1
			p = c.getFirstPage();
			if (p == null || p.getPageNumber() != p2)
				throw T_Fail.testFailMsg("get first page failed");
			p.unlatch();

			// make sure get next page skips over p3
			p = c.getNextPage(p2);	
			if (p == null || p.getPageNumber() != p4)
				throw T_Fail.testFailMsg("get next page failed");
			p.unlatch();

			// make sure get next page skips over p5
			p = c.getNextPage(p4);
			if (p != null)
			{
				p.unlatch();
				throw T_Fail.testFailMsg("get next page failed to terminate");
			}

			p = t_util.t_getLastPage(c);	// make sure it skips over p5
			if (p == null || p.getPageNumber() != p4)
				throw T_Fail.testFailMsg("getLastPage failed");
			p.unlatch();

			PASS("R008: containerId " + cid);

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

	}

	/*
	 * test 9 - deallocation and reuse pag
	 */
	protected void S009() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);


			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			int numpages = 10;

			Page[] origpage = new Page[numpages];
			int[] origrid = new int[numpages];
			long[] origpnum = new long[numpages];
			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);

			for (int i = 0; i < numpages; i++)
			{
				if (i == 0)
					origpage[i] = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
				else
					origpage[i] = t_util.t_addPage(c);

				origrid[i] = t_util.t_insert(origpage[i], row1).getId();
				origpnum[i] = origpage[i].getPageNumber();

				t_util.t_removePage(c, origpage[i]);
			}
			t_util.t_commit(t);

			// check that pages are not reused before transaction is committed
			for (int i = 0; i < numpages-1; i++)
			{
				for (int j = i+1; j < numpages; j++)
				{
					if (origpnum[i] == origpnum[j])
						throw T_Fail.testFailMsg("page reused before transaction is committed");
				}
			}

			register(key(9,0), cid);
			register(key(9,1), numpages);

			for (int i = 0; i < numpages; i++)
			{
				register(key(9,i+10), origpnum[i]);
				register(key(9,i+numpages+10), origrid[i]);
			}

			// now see if we can reuse them
			c = t_util.t_openContainer(t, 0, cid, true);

			Page[] newpage = new Page[numpages];
			int[] newrid = new int[numpages];
			long[] newpnum = new long[numpages];
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);

			for (int i = 0; i < numpages; i++)
			{
				newpage[i] = t_util.t_addPage(c);
				newpnum[i] = newpage[i].getPageNumber();
				newrid[i] = t_util.t_insert(newpage[i], row2).getId();
			}

			// if any page is reused, make sure the rid is not reused
			int reuse = 0;
			for (int i = 0; i < numpages; i++)
			{
				for (int j = 0; j < numpages; j++)
				{
					if (origpnum[i] == newpnum[j])
					{
						reuse++;
						if (origrid[i] == newrid[j])
							throw T_Fail.testFailMsg("resued page rid is not preserved");
					
						break;		// inner loop
					}
				}
			}

			for (int i = 0; i < numpages; i++)
			{
				register(key(9,i+100), newpnum[i]);
				register(key(9,i+numpages+100), newrid[i]);
			}

			REPORT("setup S009: containerId " + cid + " of " +
				   numpages + " original pages," +
				   reuse + " pages were reused.");
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 9 */
	protected void R009() throws T_Fail, StandardException
	{
		long cid = find(key(9,0));
		if (cid < 0)
		{
			REPORT("R009 not run");
			return;
		}
		int numpages = (int)find(key(9,1));

		int[] newrid = new int[numpages];
		long[] newpnum = new long[numpages];
		Page[] newpage = new Page[numpages];

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);

			for (int i = 0; i < numpages; i++)
			{
				newrid[i] = (int)find(key(9, i+numpages+100));
				newpnum[i] = find(key(9,i+100));

				newpage[i] = t_util.t_getPage(c, newpnum[i]);
				t_util.t_checkRecordCount(newpage[i], 1, 1);
				RecordHandle rh = t_util.t_checkFetchFirst(newpage[i], REC_002);
				if (rh.getId() != newrid[i])
					throw T_Fail.testFailMsg("recordId not match");
			}
			REPORT("R009: containerId " + cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/*
	 * test 10 - allocation/deallocation with overflow page
	 */
	protected void S010() throws T_Fail,StandardException
	{
		// maufacture a container with the first and last page being overflow
		// pages 
		Transaction t = t_util.t_startTransaction();
		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			int numpages = 10;
			Page[] page = new Page[numpages];
			long[] pnum = new long[numpages];
			RecordHandle[] recordHandles = new RecordHandle[numpages];

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			for (int i = 0; i < numpages; i++)
			{
				if (i == 0)
					page[i] = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
				else
					page[i] = t_util.t_addPage(c);

				pnum[i] = page[i].getPageNumber();

				// remove first two and last page as we go, pages are not reused
				// until after commit. These pages have no rows in them
				if (i < 2 || i == numpages-1)
				{
					t_util.t_checkEmptyPage(page[i]);
					t_util.t_removePage(c, page[i]); 
				}
				else
					recordHandles[i] = t_util.t_insert(page[i], row1);

			}

			t_util.t_commit(t);
			c = t_util.t_openContainer(t, 0, cid, true);

			Page p = c.getFirstPage();
			if (p.getPageNumber() != pnum[2])
				throw T_Fail.testFailMsg("first page expected to be page " +
										 pnum[2] + ", got " + p.getPageNumber() + 
										 " instead");
			p.unlatch();
			p = t_util.t_getLastPage(c);
			if (p.getPageNumber() != pnum[numpages-2])
				throw T_Fail.testFailMsg("last page expected to be page " +
										 pnum[numpages-2] + ", got " + p.getPageNumber() + 
										 " instead");
			p.unlatch();

		// now make rows on the rest of the page overflow
			RecordHandle rh;
			T_RawStoreRow big = new T_RawStoreRow(String.valueOf(new char[1500]));
			REPORT("start reusing pages hopefully");
			for (int i = 2; i < numpages-1; i++)
			{
				T_RawStoreRow row2 = new T_RawStoreRow(REC_002);

				p = t_util.t_getPage(c, pnum[i]);
				while(p.spaceForInsert(row2.getRow(), (FormatableBitSet) null, 100))
					t_util.t_insert(p, row2);

				// now page is filled
                rh = p.fetchFromSlot(
                        (RecordHandle) null, 
                        0, 
                        row2.getRow(), 
                        (FetchDescriptor) null, 
                        true);
				int slot = p.getSlotNumber(rh);
				p.updateAtSlot(slot, big.getRow(), null);
				p.unlatch();
			}

			register(key(10, 1), cid);
			register(key(10, 2), numpages);
			for (int i = 0; i < numpages; i++)
				register(key(10, 10+i), pnum[i]);
			REPORT("setup S010");
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	protected void R010() throws T_Fail, StandardException
	{
		long cid = find(key(10, 1));
		if (cid < 0)
		{
			REPORT("R010 not run");
			return;
		}
		int numpages = (int)find(key(10,2));
		long[] pnum = new long[numpages];
		for (int i = 0; i < numpages; i++)
			pnum[i] = find(key(10, 10+i));

		// now check the pages, 0, 1 and last page (...) are all overflowpages
		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page p;

			p = c.getPage(pnum[0]);
			if (p.recordCount() == 0) // it has no overflow rows in it
			{
				p.unlatch();
				throw T_Fail.testFailMsg("first page failed to get any overflow records");
			}
			p.unlatch();
			p = c.getPage(pnum[1]);
			if (p.recordCount() == 0) // it has no overflow rows in it
			{
				p.unlatch();
				throw T_Fail.testFailMsg("second page failed to get any overflow records");
			}
			p.unlatch();
			p = c.getPage(pnum[numpages-1]);
			if (p.recordCount() == 0) // it has no overflow rows in it
			{
				p.unlatch();
				throw T_Fail.testFailMsg("last page failed to get any overflow records");
			}
			p.unlatch();

		// all other pages have one huge row at the beginning
			p = c.getFirstPage();
			if (p.getPageNumber() != pnum[2])
				throw T_Fail.testFailMsg("first page expected to be page " +
										 pnum[2] + ", got " + p.getPageNumber() + 
										 " instead");
			long pageNum = p.getPageNumber();
			t_util.t_checkStringLengthFetch(p, 0, 1500);

			p.unlatch();
			int i = 3;
			while((p = c.getNextPage(pageNum)) != null)
			{
				pageNum = p.getPageNumber();
				if (pageNum != pnum[i])
					throw T_Fail.testFailMsg("expect page " + pnum[i] + 
											 " get page " + pageNum);
				t_util.t_checkStringLengthFetch(p, 0, 1500);			
				p.unlatch();
				i++;
			}
			if (i != numpages-1)
				throw T_Fail.testFailMsg("expect last head page to be " +
										 (numpages-2) +  " got " + i + " page instead"); 
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R010");
	}

	/*
	 * test 11 - allocate a lot of pages so that we need > 1 allocation pages
	 */
	protected void S011() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		int iterations = 10000;

		try
		{
			long cid = t_util.t_addContainer(t, 0, 4096);
			t_util.t_commit(t);
			
			T_RawStoreRow row = new T_RawStoreRow(REC_001);
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);

			// allocate iterations pages, this ought to bring the number of pages
			// over what 1 allocation page can handle
			Page p = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_insert(p, row);
			p.unlatch();

			long pnum = ContainerHandle.FIRST_PAGE_NUMBER;
			long lastPageNum = ContainerHandle.INVALID_PAGE_NUMBER;
			for (int i = 1; i <= iterations; i++)
			{
				p = t_util.t_addPage(c);
				if (p.getPageNumber() != pnum+1)
					REPORT("S011: skipping " + (pnum+1) + " going to " + p.getPageNumber());
				pnum = p.getPageNumber();

				t_util.t_insert(p, row);

				if (i == iterations)
				{
					lastPageNum = p.getPageNumber();
					REPORT("S011: Last page number is " + lastPageNum);
				}

				p.unlatch();
			}
			t_util.t_commit(t);

			// now scan the pages
			c = t_util.t_openContainer(t, 0, cid, true);
			p = c.getFirstPage();
			if (p == null || p.getPageNumber() !=
				ContainerHandle.FIRST_PAGE_NUMBER)
				throw T_Fail.testFailMsg("first page not where it is expected");
			p.unlatch();

			p = t_util.t_getLastPage(c);
			if (p == null || p.getPageNumber() != lastPageNum)
				throw T_Fail.testFailMsg("last page not where it is expected");
			p.unlatch();

			register(key(11, 1), cid);
			register(key(11, 2), lastPageNum);
			register(key(11, 3), iterations);

			REPORT("setup S011, container id = " + cid);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	protected void R011() throws T_Fail, StandardException
	{
		long cid = find(key(11,1));
		if (cid < 0)
		{
			REPORT("R011 not run");
			return;
		}
		else
			REPORT("R011 container id = " + cid);

		long expectedLastPageNum = find(key(11,2));
		int iterations = (int)find(key(11,3));
			 
		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page p = c.getFirstPage();
			if (p == null || p.getPageNumber() !=
				ContainerHandle.FIRST_PAGE_NUMBER)
				throw T_Fail.testFailMsg("first page not where it is expected");
			p.unlatch();


			long pageNum = ContainerHandle.FIRST_PAGE_NUMBER;
			long pnum = pageNum;
			int pcount = 1;
			while((p = c.getNextPage(pageNum)) != null)
			{
				t_util.t_checkFetchFirst(p, REC_001);
				pageNum = p.getPageNumber();
				if (pageNum != pnum+1)
					REPORT("R011: skipping " + (pnum+1) + " going to " + pageNum);
				pnum = pageNum;

				pcount++;
				p.unlatch();
			}
			if (pcount != (iterations+1))
            {
				throw T_Fail.testFailMsg(
                    "expect to see " + (iterations+1) + " pages, got: " + pcount + 
					" last page number is " + pageNum);
            }

			p = t_util.t_getLastPage(c);
			if (p.getPageNumber() != expectedLastPageNum)
			{
				throw T_Fail.testFailMsg(
					"expect last page num to be " + expectedLastPageNum +
					" , instead got " + p.getPageNumber());
			}

			REPORT("Last page pagenumber is " + p.getPageNumber() + 
				   ", it is the last page of " + (iterations+1) + " user pages");
			p.unlatch();
			

			PASS("R011");
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

	}

	/*
	 * test 12 - test estimated page count
	 */
	protected void S012() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		long cid = t_util.t_addContainer(t, 0, 4096);
		t_util.t_commit(t);

		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);

			if (c.getEstimatedPageCount(0) != 1)
				throw T_Fail.testFailMsg("Expect 2 user page, got " + c.getEstimatedPageCount(0));

			// allocate 30 pages
			Page p = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			T_RawStoreRow row = new T_RawStoreRow(REC_001);

			t_util.t_insert(p, row);
			p.unlatch();
			for (int i = 2; i <= 30; i++)
			{
				p = t_util.t_addPage(c);
				t_util.t_insert(p, row);
				p.unlatch();
			}

			register(key(12, 1), cid);
			REPORT("Setup S012");
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	protected void R012() throws T_Fail, StandardException
	{
		long cid = find(key(12,1));
		if (cid < 0)
		{
			REPORT("R012 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			if (c.getEstimatedPageCount(0) != 30)
				throw T_Fail.testFailMsg("expect 30 pages, got " + c.getEstimatedPageCount(0));

			PASS("R012");
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

	}

	/*
	 * test 20 - create multiple containers
	 */
	protected void S020() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
		T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
		T_RawStoreRow row3 = new T_RawStoreRow(REC_003);

		try
		{

			long cid1 = t_util.t_addContainer(t, 0);
			ContainerHandle c1 = t_util.t_openContainer(t, 0, cid1, true);
			Page page = t_util.t_getPage(c1, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_insertAtSlot(page, 0, row1);
			page.unlatch();
		
			long cid2 = t_util.t_addContainer(t, 0);
			ContainerHandle c2 = t_util.t_openContainer(t, 0, cid2, true);

			long cid3 = t_util.t_addContainer(t, 0);
			ContainerHandle c3 = t_util.t_openContainer(t, 0, cid3, true);

			page = t_util.t_getPage(c2, ContainerHandle.FIRST_PAGE_NUMBER);
			// blank first page
			page.unlatch();

			page = t_util.t_addPage(c2);
			t_util.t_insertAtSlot(page, 0, row2);
			long pageId = page.getPageNumber();
			page.unlatch();

			page = t_util.t_getPage(c3, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_insertAtSlot(page, 0, row3);
			page.unlatch();

			REPORT("setup S020: container1 " + cid1 + 
				   " container2 " + cid2 + " container3 " + cid3 +
				   " page " + pageId);

			register(key(20, 1), cid1);
			register(key(20, 2), cid2);
			register(key(20, 3), cid3);
			register(key(20, 4), pageId);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 20 */
	protected void R020() throws T_Fail, StandardException
	{
		long cid1 = find(key(20, 1));
		if (cid1 < 0)
		{
			REPORT("R020 not run");
			return;
		}

		long cid2 = find(key(20,2));
		long cid3 = find(key(20,3));
		long pageId = find(key(20,4));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid1, false);
			Page page = t_util.t_getPage(c, c.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			page.unlatch();

			c = t_util.t_openContainer(t, 0, cid2, false);
			page = t_util.t_getPage(c,  c.FIRST_PAGE_NUMBER);
			t_util.t_checkEmptyPage(page);
			page.unlatch();			

			page = t_util.t_getPage(c, pageId);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_002, false, false);
			page.unlatch();

			c = t_util.t_openContainer(t, 0, cid3, false);
			page = t_util.t_getPage(c, c.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 1, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_003, false, false);
			page.unlatch();

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R020 container1 " + cid1 + 
			   " container2 " + cid2 + " container3 " + cid3 +
			   " page " + pageId);
	}

	/*
	 * test 022 - drop containers
	 */
	protected void S022() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			t_util.t_openContainer(t, 0, cid, true);
			t_util.t_dropContainer(t, 0, cid);

			t_util.t_abort(t);			// this should rollback the drop
			t_util.t_openContainer(t, 0, cid, true);

			REPORT("rollback of drop container tested");

			t.dropContainer(new ContainerKey(0, cid));

			t.commit();

			REPORT("setup S022: containerId " + cid);

			register(key(22, 1), cid);
				 
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* 
	 * recover test 022 - drop container
	 */
	protected void R022() throws T_Fail, StandardException
	{
		long cid = find(key(22, 1));
		if (cid < 0)
		{
			REPORT("R022 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerKey id = new ContainerKey(0, cid);
			ContainerHandle c1 = t.openContainer(id, ContainerHandle.MODE_READONLY);	// this should fail
			if (c1 != null)
				throw T_Fail.testFailMsg("dropped container should fail to open");				
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
		PASS("R022 : containerId " + cid);
	}

	/*
	 * test 100 - multiple intervening committed transactions
	 */
	protected void S100() throws T_Fail, StandardException
	{
		T_TWC t1 = t_util.t_startTransactionWithContext();
		T_TWC t2 = t_util.t_startTransactionWithContext();
		try
		{

			long cid10 = t_util.t_addContainer(t1,0);
			long cid11 = t_util.t_addContainer(t1,0);
			t_util.t_commit(t1);

			long cid20 = t_util.t_addContainer(t2, 0);
			long cid21 = t_util.t_addContainer(t2, 0);
			t_util.t_commit(t2);

			ContainerHandle c10 = t_util.t_openContainer(t1, 0, cid10, true);
			ContainerHandle c11 = t_util.t_openContainer(t1, 0, cid11, true);
			ContainerHandle c20 = t_util.t_openContainer(t2, 0, cid20, true);
			ContainerHandle c21 = t_util.t_openContainer(t2, 0, cid21, true);

			t1.switchTransactionContext();
			Page p10 = t_util.t_getPage(c10, ContainerHandle.FIRST_PAGE_NUMBER);
			Page p11 = t_util.t_getPage(c11, ContainerHandle.FIRST_PAGE_NUMBER);
			t1.resetContext();

			t2.switchTransactionContext();
			Page p20 = t_util.t_getPage(c20, ContainerHandle.FIRST_PAGE_NUMBER);
			Page p21 = t_util.t_getPage(c21, ContainerHandle.FIRST_PAGE_NUMBER);

			// for each page, insert, update, updatefield, (some) delete

			T_RawStoreRow row1 = new T_RawStoreRow(3);
			row1.setColumn(0, REC_001);
			row1.setColumn(1, REC_002);
			row1.setColumn(2, (String) null);

			T_RawStoreRow row2 = new T_RawStoreRow(2);
			row2.setColumn(0, REC_003);
			row2.setColumn(1, REC_004);


			T_RawStoreRow rowP = new T_RawStoreRow(1);
			rowP.setColumn(0, REC_005);
			t2.resetContext();


			t1.switchTransactionContext();
			RecordHandle r10 = t_util.t_insertAtSlot(p10, 0, row1);
			RecordHandle r11 = t_util.t_insertAtSlot(p11, 0, row1);
			t1.resetContext();

			t2.switchTransactionContext();
			RecordHandle r20 = t_util.t_insertAtSlot(p20, 0, row1);
			RecordHandle r21 = t_util.t_insertAtSlot(p21, 0, row1);
			t2.resetContext();

			t1.switchTransactionContext();
			int slot10 = p10.getSlotNumber(r10);
			p10.updateAtSlot(slot10, row2.getRow(), null);
			int slot11 = p11.getSlotNumber(r11);
			p11.updateAtSlot(slot11, row2.getRow(), null);
			t1.resetContext();

			t2.switchTransactionContext();
			int slot20 = p20.getSlotNumber(r20);
			p20.updateAtSlot(slot20, row2.getRow(), null);
			int slot21 = p21.getSlotNumber(r21);
			p21.updateAtSlot(slot21, row2.getRow(), null);
			t2.resetContext();
		
			t1.switchTransactionContext();
			slot10 = p10.getSlotNumber(r10);
			p10.updateAtSlot(slot10, rowP.getRow(), BS_COL_0);
			slot11 = p11.getSlotNumber(r11);
			p11.updateAtSlot(slot11, rowP.getRow(), BS_COL_0);
			p10.unlatch();
			p11.unlatch();
			t1.resetContext();



			t2.switchTransactionContext();
			slot20 = p20.getSlotNumber(r20);
			p20.updateAtSlot(slot20, rowP.getRow(), BS_COL_0);
			slot21 = p21.getSlotNumber(r21);
			p21.updateAtSlot(slot21, rowP.getRow(), BS_COL_0);

			p21.deleteAtSlot(slot21, true, null);
			p20.unlatch();
			p21.unlatch();
			t2.resetContext();

			REPORT("setup S100: container1 " + cid10 + " container2 " + cid11 + 
				   " container3 " + cid20 + " container4 " + cid21);

			register(key(100, 1), cid10);
			register(key(100, 2), cid11);
			register(key(100, 3), cid20);
			register(key(100, 4), cid21);
		}
		finally
		{
			t_util.t_commit(t1);
			t_util.t_close(t1);

			t_util.t_commit(t2);
			t_util.t_close(t2);
		}
	}

	/* recover S100 */
	protected void R100 () throws T_Fail, StandardException
	{
		long[] cid = new long[4];
		cid[0] = find(key(100, 1));
		if (cid[0] < 0)
		{
			REPORT("R100 not run");
			return;
		}
			
		cid[1] = find(key(100, 2));
		cid[2] = find(key(100, 3));
		cid[3] = find(key(100, 4));

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c;
			Page page;

			for (int i = 0; i < 4; i++)
			{
				c = t_util.t_openContainer(t, 0, cid[i], false);
				page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
				if (i == 3)
					t_util.t_checkRecordCount(page, 1, 0);
				else
					t_util.t_checkRecordCount(page, 1, 1);

				t_util.t_checkFieldCount(page, 0, 2);

				// each row has REC_005, REC_004
				DataValueDescriptor column = new SQLChar();
				t_util.t_checkFetchColFromSlot(page, 0, 0, column, false, REC_005);
				t_util.t_checkFetchColFromSlot(page, 0, 1, column, false, REC_004);
				page.unlatch();
			}

			PASS("R100 passed: container1 " + cid[0] + " container2 " + cid[1] + 
				 " container3 " + cid[2] + " container4 " + cid[3]);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/*
	 * test 101 - transaction with rollback to savepoint
	 */
	protected void S101() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();
		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);
		
			RecordHandle r0 = t_util.t_insertAtSlot(page, 0, row1);
			if (t_util.t_insertAtSlot(page, 1, row2) == null)
				return;			// test case not interesting

			t_util.t_checkRecordCount(page, 2, 2);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);
			/////////////////////////////////////////////////////
			// At SP1, has 2 records of REC_001 and REC_002    //
			/////////////////////////////////////////////////////
			t.setSavePoint(SP1, null);

			if (t_util.t_insertAtSlot(page, 2, row3) == null)
				return;			// test case not interesting

			page.purgeAtSlot(1, 1, true);

			if (t_util.t_insertAtSlot(page, 1, row4) == null)
				return;

			t_util.t_checkRecordCount(page, 3, 3);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);
			////////////////////////////////////////////////////////////////
			// At SP2, has 3 records of REC_001 and REC_004 and REC_003   //
			////////////////////////////////////////////////////////////////
			t.setSavePoint(SP2, null);

			int slot0 = page.getSlotNumber(r0);
			page.updateAtSlot(slot0, row5.getRow(), null);
			page.deleteAtSlot(1, true, (LogicalUndo)null);

			t_util.t_checkRecordCount(page, 3, 2);
			t_util.t_checkFetchBySlot(page, 0, REC_005, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);

			page.unlatch();
			t.rollbackToSavePoint(SP2, null);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, 3, 3);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);

			// after a rollback to sp, do some more changes

			slot0 = page.getSlotNumber(r0);
			page.updateAtSlot(slot0, row5.getRow(), null);
			page.deleteAtSlot(0, true, (LogicalUndo)null);
			page.deleteAtSlot(1, true, (LogicalUndo)null);
			page.deleteAtSlot(2, true, (LogicalUndo)null);

			t_util.t_checkRecordCount(page, 3, 0);
			t_util.t_checkFetchBySlot(page, 0, REC_005, true, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, true, false);

			page.unlatch();
			t.rollbackToSavePoint(SP1, null);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			// note that an insert, when rolled back, becomes a deleted row but
			// will not disappear.  A purge row will come back at the same slot
			// and with the same record id.
			t_util.t_checkRecordCount(page, 4, 2);

			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 3, REC_003, true, false);

			// add one more record to this
			if (page.spaceForInsert())
				t_util.t_insertAtSlot(page, 3, row5);

			REPORT("setup S101: containerId " + cid + " recordCount " + page.recordCount());

			register(key(101, 1), cid);
			register(key(101, 2), page.recordCount());

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 101 */
	protected void R101() throws T_Fail, StandardException
	{
		long cid = find(key(101, 1));
		if (cid < 0)
		{
			REPORT("R101 not run");
			return;
		}
		int recordCount = (int)find(key(101, 2));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, recordCount, recordCount-2);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_004, true, false);
			if (recordCount == 5)
			{
				t_util.t_checkFetchBySlot(page, 3, REC_005, false, false);
				t_util.t_checkFetchBySlot(page, 4, REC_003, true, false);
			}
			else
				t_util.t_checkFetchBySlot(page, 3, REC_003, true, false);

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R101: containerId " + cid + " recordCount " + recordCount);
	}


	/*
	 * the following tests has recovery undo work, cannot test Rnnn during
	 * setup because it hasn't been rolled back during setup yet.  Test the
	 * state in Snnn.
	 */

	/*
	 * test 200 - incomplete transaction
	 */
	protected void S200() throws T_Fail, StandardException
	{
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		Page page = null;

		try
		{
			long cid = t_util.t_addContainer(t, 0);

			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);

			ctx.switchTransactionContext();

			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);

			int rowcount = 0;
			while(page.spaceForInsert())
			{
				if (t_util.t_insertAtSlot(page, 0, row1) != null)
					rowcount++;
			}

			t_util.t_checkRecordCount(page, rowcount, rowcount);
			for (int i = 0; i < rowcount; i++)
				t_util.t_checkFetchBySlot(page, i, REC_001, false, false);

			REPORT("setup S200: containerId " + cid + " recordCount " + rowcount);
			register(key(200, 1), cid);
			register(key(200, 2), rowcount);
		}
		finally
		{
			if (page != null && page.isLatched())
				page.unlatch();
			ctx.resetContext();
		}
		// do not abort it at run time, abort it at recovery time
		// t_util.t_abort(t);
		// t.close();
	}

	/* recover test 200 */
	protected void R200() throws T_Fail, StandardException
	{
		long cid = find(key(200, 1));
		if (cid < 0)
		{
			REPORT("R200 not run");
			return;
		}

		int recordCount = (int)find(key(200, 2));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			// rollback of an insert is a deleted record
			t_util.t_checkRecordCount(page, recordCount, 0);
			for (int i = 0; i < recordCount; i++)
				t_util.t_checkFetchBySlot(page, i, REC_001, true, false);
			page.unlatch();

			PASS("R200: containerId " + cid + " recordCount " + recordCount);

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/*
	 * test 201 - multiple intervening incomplete transaction
	 */
	protected void S201() throws T_Fail, StandardException
	{
		/* this is the same as S100 but left it at an incomplete state */
		T_TWC t1 = t_util.t_startTransactionWithContext();
		T_TWC t2 = t_util.t_startTransactionWithContext();
		Page p10, p11, p20, p21;
		p10 = p11 = p20 = p21 = null;

		try
		{
			long cid10 = t_util.t_addContainer(t1,0);
			long cid11 = t_util.t_addContainer(t1,0);

			long cid20 = t_util.t_addContainer(t2, 0);
			long cid21 = t_util.t_addContainer(t2, 0);

			t_util.t_commit(t1);
			t_util.t_commit(t2);

			ContainerHandle c10 = t_util.t_openContainer(t1, 0, cid10, true);
			ContainerHandle c11 = t_util.t_openContainer(t1, 0, cid11, true);
			ContainerHandle c20 = t_util.t_openContainer(t2, 0, cid20, true);
			ContainerHandle c21 = t_util.t_openContainer(t2, 0, cid21, true);

			t1.switchTransactionContext();
			p10 = t_util.t_getPage(c10, ContainerHandle.FIRST_PAGE_NUMBER);
			p11 = t_util.t_getPage(c11, ContainerHandle.FIRST_PAGE_NUMBER);
			t1.resetContext();

			t2.switchTransactionContext();
			p20 = t_util.t_getPage(c20, ContainerHandle.FIRST_PAGE_NUMBER);
			p21 = t_util.t_getPage(c21, ContainerHandle.FIRST_PAGE_NUMBER);

			// for each page, insert, update, updatefield, (some) delete

			T_RawStoreRow row1 = new T_RawStoreRow(3);
			row1.setColumn(0, REC_001);
			row1.setColumn(1, REC_002);
			row1.setColumn(2, (String) null);

			T_RawStoreRow row2 = new T_RawStoreRow(2);
			row2.setColumn(0, REC_003);
			row2.setColumn(1, REC_004);


			T_RawStoreRow rowP = new T_RawStoreRow(1);
			rowP.setColumn(0, REC_005);
			t2.resetContext();

			t1.switchTransactionContext();
			RecordHandle r10 = t_util.t_insertAtSlot(p10, 0, row1);
			RecordHandle r11 = t_util.t_insertAtSlot(p11, 0, row1);
			t1.resetContext();

			t2.switchTransactionContext();
			RecordHandle r20 = t_util.t_insertAtSlot(p20, 0, row1);
			RecordHandle r21 = t_util.t_insertAtSlot(p21, 0, row1);
			t2.resetContext();

			t1.switchTransactionContext();
			int slot10 = p10.getSlotNumber(r10);
			p10.updateAtSlot(slot10, row2.getRow(), null);
			int slot11 = p11.getSlotNumber(r11);
			p11.updateAtSlot(slot11, row2.getRow(), null);
			t1.resetContext();

			t2.switchTransactionContext();
			int slot20 = p20.getSlotNumber(r20);
			p20.updateAtSlot(slot20, row2.getRow(), null);
			int slot21 = p21.getSlotNumber(r21);
			p21.updateAtSlot(slot21, row2.getRow(), null);
			t2.resetContext();


			t1.switchTransactionContext();
			slot10 = p10.getSlotNumber(r10);
			p10.updateAtSlot(slot10, rowP.getRow(), BS_COL_0);
			slot11 = p11.getSlotNumber(r11);
			p11.updateAtSlot(slot11, rowP.getRow(), BS_COL_0);
			t1.resetContext();

			t2.switchTransactionContext();
			slot20 = p20.getSlotNumber(r20);
			p20.updateAtSlot(slot20, rowP.getRow(), BS_COL_0);
			slot21 = p21.getSlotNumber(r21);
			p21.updateAtSlot(slot21, rowP.getRow(), BS_COL_0);

			p21.deleteAtSlot(slot21, true, null);
			t2.resetContext();

			REPORT("setup S201: container1 " + cid10 + " container2 " + cid11 + 
				   " container3 " + cid20 + " container4 " + cid21);

			register(key(201, 1), cid10);
			register(key(201, 2), cid11);
			register(key(201, 3), cid20);
			register(key(201, 4), cid21);
		}
		finally
		{
			if (p10 != null && p10.isLatched())
				p10.unlatch();


			if (p11 != null && p11.isLatched())
				p11.unlatch();


			if (p20 != null && p20.isLatched())
				p20.unlatch();

			if (p21 != null && p21.isLatched())
				p21.unlatch();
		}

		// let recovery do the abort
	}

	/* recover test 201 */
	protected void R201() throws T_Fail, StandardException
	{
		long[] cid = new long[4];
		cid[0] = find(key(201, 1));
		if (cid[0] < 0)
		{
			REPORT("R201 not run");
			return;
		}
			
		cid[1] = find(key(201, 2));
		cid[2] = find(key(201, 3));
		cid[3] = find(key(201, 4));

		Transaction t = t_util.t_startTransaction();
		try
		{

			ContainerHandle c;
			Page page;

			for (int i = 0; i < 4; i++)
			{
				c = t_util.t_openContainer(t, 0, cid[i], false);
				page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
				t_util.t_checkRecordCount(page, 1, 0);

				// record has the following fields: REC_001, REC_002, null
				DataValueDescriptor column = new SQLChar();
				t_util.t_checkFetchColFromSlot(page, 0, 0, column, false, REC_001);
				t_util.t_checkFetchColFromSlot(page, 0, 1, column, false, REC_002);
				t_util.t_checkFetchColFromSlot(page, 0, 2, column, false, null);
			}
		}
		finally
		{
			t_util.t_commit(t);	
			t.close();
		}
		PASS("R201 passed:  container1 " + cid[0] + " container2 " + cid[1] + 
			   " container3 " + cid[2] + " container4 " + cid[3]);
	}

	/*
	 * test 202 - incomplete transaction with rollback to savepoints
	 */
	protected void S202() throws T_Fail, StandardException
	{
		/* this is S101 which is left in an incomplete state */
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		Page page = null;
		ctx.switchTransactionContext();

		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			RecordHandle r0 = t_util.t_insertAtSlot(page, 0, row1);
			if (t_util.t_insertAtSlot(page, 1, row2) == null)
			{
				page.unlatch();
				t_util.t_abort(t);
				t.close();
				return;			// test case not interesting
			}


			/////////////////////////////////////////////////////
			// At SP1, has 2 records of REC_001 and REC_002    //
			/////////////////////////////////////////////////////
			t.setSavePoint(SP1, null);

			int slot0 = page.getSlotNumber(r0);
			page.updateAtSlot(slot0, row5.getRow(), null);
			t_util.t_checkFetchBySlot(page, 0, REC_005, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);

			page.unlatch();
			t.rollbackToSavePoint(SP1, null);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);

			if (t_util.t_insertAtSlot(page, 2, row3) == null)
			{
				page.unlatch();
				t_util.t_abort(t);
				t.close();
				return;			// test case not interesting
			}

			page.purgeAtSlot(1, 1, true);

			if (t_util.t_insertAtSlot(page, 1, row4) == null)
			{
				page.unlatch();
				t_util.t_abort(t);
				t.close();
				return;			// test case not interesting
			}

			t_util.t_checkRecordCount(page, 3, 3);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);
			////////////////////////////////////////////////////////////////
			// At SP2, has 3 records of REC_001 and REC_004 and REC_003   //
			////////////////////////////////////////////////////////////////
			t.setSavePoint(SP2, null);

			slot0 = page.getSlotNumber(r0);
			page.updateAtSlot(slot0, row5.getRow(), null);
			page.deleteAtSlot(1, true, (LogicalUndo)null);

			t_util.t_checkRecordCount(page, 3, 2);
			t_util.t_checkFetchBySlot(page, 0, REC_005, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);

			page.unlatch();
			t.rollbackToSavePoint(SP2, null);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, 3, 3);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, false, false);

		// after a rollback to sp, do some more changes

			slot0 = page.getSlotNumber(r0);
			page.updateAtSlot(slot0, row5.getRow(), null);
			page.deleteAtSlot(0, true, (LogicalUndo)null);
			page.deleteAtSlot(1, true, (LogicalUndo)null);
			page.deleteAtSlot(2, true, (LogicalUndo)null);

			t_util.t_checkRecordCount(page, 3, 0);
			t_util.t_checkFetchBySlot(page, 0, REC_005, true, false);
			t_util.t_checkFetchBySlot(page, 1, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 2, REC_003, true, false);

			page.unlatch();
			t.rollbackToSavePoint(SP1, null);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			// note that an insert, when rolled back, becomes a deleted row but
			// will not disappear.  A purge row will come back at the same slot
			// and with the same record id.
			t_util.t_checkRecordCount(page, 4, 2);

			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, false, false);
			t_util.t_checkFetchBySlot(page, 2, REC_004, true, false);
			t_util.t_checkFetchBySlot(page, 3, REC_003, true, false);

			// add one more record to this
			if (page.spaceForInsert())
				t_util.t_insertAtSlot(page, 3, row5);

			REPORT("setup S202: containerId " + cid + " recordCount " + page.recordCount());

			register(key(202, 1), cid);
			register(key(202, 2), page.recordCount());

		}
		finally
		{
			if (page != null && page.isLatched())
				page.unlatch();
			ctx.resetContext();
		}

		// let recovery undo rollback this transaction

	}


	/* recover test 202 */
	protected void R202() throws T_Fail, StandardException
	{
		long cid = find(key(202, 1));
		if (cid < 0)
		{
			REPORT("R202 not run");
			return;
		}
		int recordCount = (int)find(key(202, 2));

		Transaction t = t_util.t_startTransaction();
		Page page = null;

		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			// rollback will leave the page with only deleted rows
			t_util.t_checkRecordCount(page, recordCount, 0);
			t_util.t_checkFetchBySlot(page, 0, REC_001, true, true);
			t_util.t_checkFetchBySlot(page, 1, REC_002, true, true);
			t_util.t_checkFetchBySlot(page, 2, REC_004, true, true);
			if (recordCount == 5)
			{
				t_util.t_checkFetchBySlot(page, 3, REC_005, true, true);
				t_util.t_checkFetchBySlot(page, 4, REC_003, true, true);
			}
			else
				t_util.t_checkFetchBySlot(page, 3, REC_003, true, true);

		}
		finally
		{
			if (page != null && page.isLatched())
				page.unlatch();

			t_util.t_commit(t);
			t.close();
		}
		PASS("R202: containerId " + cid + " recordCount " + recordCount);
	}

	/*
	 * test 203 -  incomplete and committed and aborted
	 *				transaction with intervening rollback to savepoints
	 */
	protected void S203() throws T_Fail, StandardException
	{
		int numtrans = 5;
		int numpages = 2;
		int i,j;

		T_TWC[] t = new T_TWC[numtrans];
		for (i = 0; i < numtrans; i++)
			t[i] =  t_util.t_startTransactionWithContext();

		Page[][] page = null;

		try
		{
			long[] cid = new long[numtrans];
			ContainerHandle[] c = new ContainerHandle[numtrans];

			for (i = 0; i < numtrans; i++)
			{
				cid[i] = t_util.t_addContainer(t[i], 0);
				t_util.t_commit(t[i]);
				c[i] = t_util.t_openContainer(t[i], 0, cid[i], true);
			}

			page = new Page[numtrans][numpages];
			long pagenum[][] = new long[numtrans][numpages];

			for (i = 0; i < numtrans; i++)
			{
				for (j = 0; j < numpages; j++)
				{
					t[i].switchTransactionContext();
					page[i][j] = t_util.t_addPage(c[i]);
					pagenum[i][j] = page[i][j].getPageNumber();
					t[i].resetContext();
				}
			}

			// set up numtrans (at least 5) transactions, each with one
			// container and numpages pages.  Do the following test:
			//
			// 1) insert 1 row onto each page
			// set savepoint SP1 on first transaction (t0)
			//
			// 2) update every rows
			// set savepoint SP1 on all other transactions
			//
			// 3) update every rows
			// set savepoint SP2 on all transactions
			// 
			// 4) update every rows
			//
			// 5) rollback t0 to SP1
			//
			// check that only page[0][x] have been rolled back
			// past SP2
			//
			// 6) update every row
			// 7) rollback SP2 on all transaction except the first
			// 
			// 8) update every rows
			// 9) rollback t0 to SP1
			//
			// 10) leave transactions in the following state
			// t0 - incomplete
			// t1 - abort
			// t2 - commit
			// t3 - incomplete
			// t4 - commit
			// any other transactions - incomplete


			//////////////////////// step 1 ////////////////////////
			RecordHandle[][] rh = new RecordHandle[numtrans][numpages];
			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			for (i = 0; i < numtrans; i++)
				for (j = 0; j < numpages; j++)
				{
					t[i].switchTransactionContext();
					rh[i][j] = t_util.t_insert(page[i][j], row1); 
					t[i].resetContext();
				}

			t[0].switchTransactionContext();
			t[0].setSavePoint(SP1, null);	// sp1
			t[0].resetContext();


			//////////////////////// step 2 ////////////////////////
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			for (i = 0; i < numtrans; i++)
				for (j = 0; j < numpages; j++)
				{
					t[i].switchTransactionContext();
					int slot = page[i][j].getSlotNumber(rh[i][j]);
					page[i][j].updateAtSlot(slot, row2.getRow(), null);
					t[i].resetContext();
				}

			for (i = 1; i < numtrans; i++) // sp1
			{
				t[i].switchTransactionContext();
				t[i].setSavePoint(SP1, null);
				t[i].resetContext();
			}

			//////////////////////// step 3 ////////////////////////
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			for (i = 0; i < numtrans; i++)
				for (j = 0; j < numpages; j++) {
					int slot = page[i][j].getSlotNumber(rh[i][j]);
					page[i][j].updateAtSlot(slot, row3.getRow(), null);
				}

			for (i = 0; i < numtrans; i++)
				t[i].setSavePoint(SP2, null);	// sp2

		//////////////////////// step 4 ////////////////////////
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			for (i = 0; i < numtrans; i++)
			{
				t[i].switchTransactionContext();

				for (j = 0; j < numpages; j++) {
					int slot = page[i][j].getSlotNumber(rh[i][j]);
					page[i][j].updateAtSlot(slot, row4.getRow(), null);
				}
				t[i].resetContext();
			}


			//////////////////////// step 5 ////////////////////////
			// unlatch relavante pages
			t[0].switchTransactionContext();

			for (j = 0; j < numpages; j++)
				page[0][j].unlatch();

			t[0].rollbackToSavePoint(SP1, null); // step 5

			// relatch relavante pages
			for (j = 0; j < numpages; j++)
				page[0][j] = t_util.t_getPage(c[0], pagenum[0][j]);
					
			t[0].resetContext();


		//////////////////////// check ////////////////////////
			for (i = 1; i < numtrans; i++)
			{
				t[i].switchTransactionContext();
				for (j = 0; j < numpages; j++)
					t_util.t_checkFetch(page[i][j], rh[i][j], REC_004);
				t[i].resetContext();
			}

			t[0].switchTransactionContext();
			for (j = 0; j < numpages; j++)
				t_util.t_checkFetch(page[0][j], rh[0][j], REC_001);

			t[0].resetContext();
		//////////////////////// step 6 ////////////////////////
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);
			for (i = 0; i < numtrans; i++)
			{
				t[i].switchTransactionContext();
				for (j = 0; j < numpages; j++) {
					int slot = page[i][j].getSlotNumber(rh[i][j]);
					page[i][j].updateAtSlot(slot, row5.getRow(), null);
				}
				t[i].resetContext();
			}

			//////////////////////// step 7 ////////////////////////
			for (i = 1; i < numtrans; i++)
			{
				t[i].switchTransactionContext();

				for (j = 0; j < numpages; j++)
					page[i][j].unlatch();

				t[i].rollbackToSavePoint(SP2, null);

				for (j = 0; j < numpages; j++)
					page[i][j] = t_util.t_getPage(c[i],pagenum[i][j]);
				t[i].resetContext();
			}

			//////////////////////// check ////////////////////////
			for (i = 1; i < numtrans; i++)
			{
				t[i].switchTransactionContext();
				for (j = 0; j < numpages; j++)
					t_util.t_checkFetch(page[i][j], rh[i][j], REC_003);
				t[i].resetContext();
			}

			t[0].switchTransactionContext();
			for (j = 0; j < numpages; j++)
				t_util.t_checkFetch(page[0][j], rh[0][j], REC_005);
			t[0].resetContext();


		//////////////////////// step 8 ////////////////////////
			T_RawStoreRow row6 = new T_RawStoreRow(REC_006);
			for (i = 0; i < numtrans; i++)
			{
				t[i].switchTransactionContext();
				for (j = 0; j < numpages; j++) {
					int slot = page[i][j].getSlotNumber(rh[i][j]);
					page[i][j].updateAtSlot(slot, row6.getRow(), null);
				}
				t[i].resetContext();
			}

			//////////////////////// step 9 ////////////////////////
			// unlatch relavante pages
			t[0].switchTransactionContext();
			for (j = 0; j < numpages; j++)
				page[0][j].unlatch();

			t[0].rollbackToSavePoint(SP1, null); 

			// relatch relavante pages
			for (j = 0; j < numpages; j++)
				page[0][j] = t_util.t_getPage(c[0], pagenum[0][j]);

			t[0].resetContext();
		//////////////////////// check ////////////////////////
			for (i = 1; i < numtrans; i++)
			{
				t[i].switchTransactionContext();

				for (j = 0; j < numpages; j++)
				{
					t_util.t_checkFetch(page[i][j], rh[i][j], REC_006);
					t_util.t_checkRecordCount(page[i][j], 1, 1);
				}
				t[i].resetContext();
			}

			t[0].switchTransactionContext();
			for (j = 0; j < numpages; j++)
			{
				t_util.t_checkFetch(page[0][j], rh[0][j], REC_001);
				t_util.t_checkRecordCount(page[0][j], 1, 1);
			}
			t[0].resetContext();

			//////////////////////// step 10 ////////////////////////
			// unlatch all pages
			for (i = 0; i < numtrans; i++)
			{
				t[i].switchTransactionContext();
				for (j = 0; j < numpages; j++)
					page[i][j].unlatch();
				t[i].resetContext();
			}

			// t[0] incomplete
			t_util.t_abort(t[1]);
			t_util.t_commit(t[2]);
			// t[3] incomplete
			t_util.t_commit(t[4]);

		// reopen containers 1, 2, and 4, where were closed when the
		// transaction terminated.
			c[1] = t_util.t_openContainer(t[1], 0, cid[1], false);
			c[2] = t_util.t_openContainer(t[2], 0, cid[2], false);
			c[4] = t_util.t_openContainer(t[4], 0, cid[4], false);

			//////////////////////// check ////////////////////////
			for (j = 0; j < numpages; j++)	
			{
				t[0].switchTransactionContext();
				t_util.t_checkFetch(c[0], rh[0][j], REC_001);
				t[0].resetContext();

				// t[1] has been aborted
				// rh[1][j] (REC_001) is deleted
				t[1].switchTransactionContext();
				page[1][j] = t_util.t_getPage(c[1], pagenum[1][j]);
				t_util.t_checkRecordCount(page[1][j], 1, 0);
				t_util.t_checkFetchBySlot(page[1][j], Page.FIRST_SLOT_NUMBER,
										  REC_001, true, false);
				page[1][j].unlatch();
				t[1].resetContext();

				t[2].switchTransactionContext();
				t_util.t_checkFetch(c[2], rh[2][j], REC_006);
				t[2].resetContext();

				t[3].switchTransactionContext();
				t_util.t_checkFetch(c[3], rh[3][j], REC_006);
				t[3].resetContext();

				t[4].switchTransactionContext();
				t_util.t_checkFetch(c[4], rh[4][j], REC_006);
				t[4].resetContext();
			}


			REPORT("setup S203: numtrans " + numtrans + " numpages " + numpages);

			for (i = 0; i < numtrans; i++)
			{
				String str = "container " + i + ":" + cid[i] + " pages: ";
				register(key(203, i+10), cid[i]);

				for (j = 0; j < numpages; j++)
				{
					str += pagenum[i][j] + " ";
					register(key(203, (i+1)*1000+j), pagenum[i][j]);
				}
				REPORT("\t" + str);
			}

			register(key(203,1), numtrans); 
			register(key(203,2), numpages);

		}
		finally
		{
			for (i = 0; i < numtrans; i++)
			{
				for (j =0; j < numpages; j++)
				{
					if (page != null && page[i][j] != null
						&& page[i][j].isLatched())
						page[i][j].unlatch();
				}
			}
		}

		// let recovery rollback incomplete transactions t0 and t3

	}


	/* recover test 203 */
	protected void R203() throws T_Fail, StandardException
	{
		int numtrans = (int)find(key(203, 1));
		int numpages = (int)find(key(203, 2));
		int i,j;
		if (numtrans < 5 || numpages < 1)
		{
			REPORT("R203 not run");
			return;
		}
		else
		{
			REPORT("R203 started, numtrans " + numtrans + " numpages " +
				  numpages );
		}

		Transaction t = t_util.t_startTransaction();

		try
		{

			long[] cid = new long[numtrans];
			ContainerHandle[] c = new ContainerHandle[numtrans];

			long[][] pagenum = new long[numtrans][numpages];
			Page[][] page = new Page[numtrans][numpages];


			for (i = 0; i < numtrans; i++)
			{

				cid[i] = find(key(203, i+10));
				c[i] = t_util.t_openContainer(t, 0, cid[i], true);
			
				for (j = 0; j < numpages; j++)
				{
					pagenum[i][j] = find(key(203, (i+1)*1000+j));

					if (SanityManager.DEBUG)
					{
						if (i == 0 && j == (numpages-1))
						{
							SanityManager.DEBUG_SET("TEST_BAD_CHECKSUM");
							Page p = null;
							try {
								p = c[i].getPage(pagenum[i][j]);
							} catch (StandardException se) {

								if (se.getMessageId().equals(SQLState.FILE_IO_GARBLED))
									REPORT("bad checksum tested");
								else
									throw se; // not expected
							}
							SanityManager.DEBUG_CLEAR("TEST_BAD_CHECKSUM");
							if (p != null)
								throw T_Fail.testFailMsg("failed to generate expected error with bad checksum");
						}
					}

					page[i][j] = t_util.t_getPage(c[i], pagenum[i][j]);
				}
			}


			// transactions were left in the following state
			// t0 - incomplete (rolled back)
			// t1 - abort
			// t2 - commit
			// t3 - incomplete (rolled back)
			// t4 - commit
			// any other transactions - incomplete
			//
			// all the rolled back transaction should have a deleted REC_001
			// all the committed transactions should have a REC_006
			//
			for (j = 0; j < numpages; j++)
			{
				t_util.t_checkRecordCount(page[0][j], 1, 0);
				t_util.t_checkFetchBySlot(page[0][j], Page.FIRST_SLOT_NUMBER,
										  REC_001, true, true);

				t_util.t_checkRecordCount(page[1][j], 1, 0);
				t_util.t_checkFetchBySlot(page[1][j], Page.FIRST_SLOT_NUMBER,
										  REC_001, true, true);

				t_util.t_checkRecordCount(page[2][j], 1, 1);
				t_util.t_checkFetchBySlot(page[2][j], Page.FIRST_SLOT_NUMBER,
										  REC_006, false, true);

				t_util.t_checkRecordCount(page[3][j], 1, 0);
				t_util.t_checkFetchBySlot(page[3][j], Page.FIRST_SLOT_NUMBER,
										  REC_001, true, true);

				t_util.t_checkRecordCount(page[4][j], 1, 1);
				t_util.t_checkFetchBySlot(page[4][j], Page.FIRST_SLOT_NUMBER,
										  REC_006, false, true);
			}

			for (i = 0; i < numtrans; i++)
			{
				String str = "container " + i + ":" + cid[i] + " pages: ";
				for (j = 0; j < numpages; j++)
					str += pagenum[i][j] + " ";
				REPORT("\t" + str);
			}
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
		PASS("R203: numtrans " + numtrans + " numpages " + numpages);
	}

	/*
	 * test 204 - incomplete and committed and aborted internal transactions
	 */
	protected void S204() throws T_Fail, StandardException
	{
		// start 2 user transaction, then 2 internal transaction, then
		// another user transaction.
		// do some work on all 5 transactions
		// roll back one of the internal transaction
		// commit the other one
		// do some more work on the 2 internal transactions and let
		// recovery roll them all back
		T_TWC ut1 = t_util.t_startTransactionWithContext();
		T_TWC ut2 = t_util.t_startTransactionWithContext();
		Page p10, p11, p20, p21, p30;
		p10 = p11 = p20 = p21 = p30 = null;

		try
		{

			long cid10 = t_util.t_addContainer(ut1, 0);
			long cid11 = t_util.t_addContainer(ut1, 0);
			t_util.t_commit(ut1);

			long cid20 = t_util.t_addContainer(ut2, 0);
			long cid21 = t_util.t_addContainer(ut2, 0);
			t_util.t_commit(ut2);

			T_RawStoreRow row = new T_RawStoreRow(REC_001);

		// all three user transactions have committed.
		// 
		// container	used by		row				row
		// cid10		ut1			r10
		// cid11		it1			r11		commit	r12
		// cid20		ut2			r20	
		// cid21		it2			r21		abort	r22
		// cid30		ut3			r30		
		// (ut3 is started after everything is done)
		// after recovery, r11 is the only record 

			ut1.switchTransactionContext();

			ContainerHandle c10 = t_util.t_openContainer(ut1, 0, cid10, true);
			p10 = t_util.t_addPage(c10);
			RecordHandle r10 = t_util.t_insert(p10, row);

			Transaction it1 = t_util.t_startInternalTransaction();
			ContainerHandle c11 = t_util.t_openContainer(it1, 0, cid11, true);
			p11 = t_util.t_addPage(c11);
			RecordHandle r11 = t_util.t_insert(p11, row);
			ut1.resetContext();

			ut2.switchTransactionContext();
			ContainerHandle c20 = t_util.t_openContainer(ut2, 0, cid20, true);
			p20 = t_util.t_addPage(c20);
			RecordHandle r20 = t_util.t_insert(p20, row);

			Transaction it2 = t_util.t_startInternalTransaction();
			ContainerHandle c21 = t_util.t_openContainer(it2, 0, cid21, true);
			p21 = t_util.t_addPage(c21);
			RecordHandle r21 = t_util.t_insert(p21, row);
			ut2.resetContext();

		// r10, r1, r20, r21, r30 inserted by the corresponding transactions

		// commit it1 - it uses the same context manager as ut1
			ut1.switchTransactionContext();
			it1.commit();

		// container is left opened and page p11 is left latched
			t_util.t_checkFetch(p11, r11, REC_001);

			// use it1 to add another row
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			RecordHandle r12 = t_util.t_insert(p11, row2);
			t_util.t_checkFetch(p11, r12, REC_002);
			ut1.resetContext();

			// abort it2 - it uses the same context maanger as ut2
			ut2.switchTransactionContext();
			it2.abort();
			
		// need to reopen container
			c21 = t_util.t_openContainer(it2, 0, cid21, true);
			p21 = t_util.t_getLastPage(c21);
			RecordHandle r22 = t_util.t_insert(p21, row2);
			ut2.resetContext();

			// start ut3 at the end to test
			// internal transactions are rolled back first

			T_TWC ut3 = t_util.t_startTransactionWithContext();
			long cid30 = t_util.t_addContainer(ut3, 0);
			t_util.t_commit(ut3);

			ContainerHandle c30 = t_util.t_openContainer(ut3, 0, cid30, true);

			ut3.switchTransactionContext();
			p30 = t_util.t_addPage(c30);
			RecordHandle r30 = t_util.t_insert(p30, row);
			ut3.resetContext();

			register(key(204, 10), cid10);
			register(key(204, 11), cid11);
			register(key(204, 20), cid20);
			register(key(204, 21), cid21);
			register(key(204, 30), cid30);

			REPORT("setup S204: cid10 " + cid10 +
				   ", cid11 " + cid11 +
				   ", cid20 " + cid20 +
				   ", cid21 " + cid21 +
				   ", cid30 " + cid30);

		}
		finally
		{
			if (p10 != null && p10.isLatched())
				p10.unlatch();

			if (p11 != null && p11.isLatched())
				p11.unlatch();

			if (p20 != null && p20.isLatched())
				p20.unlatch();

			if (p21 != null && p21.isLatched())
				p21.unlatch();

			if (p30 != null && p30.isLatched())
				p30.unlatch();
		}
			// let recovery rollback incomplete transactions 

	}

	/*
	 * test recovery of 204
	 */
	protected void R204() throws T_Fail, StandardException
	{
		long cid10 = find(key(204, 10));
		if (cid10 < 0)
		{
			REPORT("R204 not run");
			return;
		}

		long cid11 = find(key(204, 11));
		long cid20 = find(key(204, 20));
		long cid21 = find(key(204, 21));
		long cid30 = find(key(204, 30));

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c;
			Page p;

			c = t_util.t_openContainer(t, 0, cid10, false);
			p = t_util.t_getLastPage(c);
			t_util.t_checkRecordCount(p, 1, 0);
			p.unlatch();

			c = t_util.t_openContainer(t, 0, cid11, false);
			p = t_util.t_getLastPage(c);
			t_util.t_checkRecordCount(p, 2, 1); // r11 is the only record that is not rolled back
			p.unlatch();

			c = t_util.t_openContainer(t, 0, cid20, false);
			p = t_util.t_getLastPage(c);
			t_util.t_checkRecordCount(p, 1, 0);
			p.unlatch();

			c = t_util.t_openContainer(t, 0, cid21, false);
			p = t_util.t_getLastPage(c);
			t_util.t_checkRecordCount(p, 2, 0);
			p.unlatch();

			c = t_util.t_openContainer(t, 0, cid30, false);
			p = t_util.t_getLastPage(c);
			t_util.t_checkRecordCount(p, 1, 0);
			p.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R204 passed: cid10 " + cid10 +
			   ", cid11 " + cid11 +
			   ", cid20 " + cid20 +
			   ", cid21 " + cid21 +
			   ", cid30 " + cid30);
	}

	/*
	 * test 300 - incomplete transaction with drop containers 
	 */
	protected void S300() throws T_Fail, StandardException
	{
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;

		try
		{
			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			t_util.t_openContainer(t, 0, cid, true);
			t_util.t_dropContainer(t, 0, cid);

			REPORT("setup S300: containerId " + cid);

			register(key(300, 1), cid);
		}
		catch (StandardException se)
		{
			t_util.t_abort(t);	
			t.close();
			throw se;
		}
		catch (T_Fail tf)
		{
			t_util.t_abort(t);	
			t.close();
			throw tf;
		}

		// let recovery rollback incomplete transaction
	}

	/*
	 * test recovery of 300
	 */
	protected void R300() throws T_Fail, StandardException
	{
		long cid = find(key(300, 1));
		if (cid < 0)
		{
			REPORT("R300 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();
		// recovery should have rolled back the dropped container
		t_util.t_openContainer(t, 0, cid, true);

		t_util.t_commit(t);
		t.close();

		PASS("R300 : containerId " + cid);

	}

	/*
	 * incomplete transactions with create container
	 */
	protected void S301() throws T_Fail, StandardException
	{
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		Page page = null;
		ctx.switchTransactionContext();

		try
		{

			long cid1 = t_util.t_addContainer(t, 0);
			ContainerHandle c1 = t_util.t_openContainer(t, 0, cid1, true);
			page = t_util.t_addPage(c1);
			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			RecordHandle r1 = t_util.t_insert(page, row1);

			t.abort();

			ContainerKey id1 = new ContainerKey(0, cid1);
			c1 = t.openContainer(id1, ContainerHandle.MODE_READONLY);
			if (c1 != null)
				throw T_Fail.testFailMsg("expect container to be dropped");

			LockingPolicy nolock = 
				t.newLockingPolicy(LockingPolicy.MODE_NONE, 0, false);

			RawContainerHandle stub = 
				((RawTransaction)t).openDroppedContainer(
					new ContainerKey(0, cid1), nolock);
			
			/*Not true always after fix for p4#25641(fix for bug:4580)
			  Checkpoint calls cleans up the stubs that not necessary
			  for recovery.
			  if (stub == null)
			  throw T_Fail.testFailMsg("drop container should still be there");
			*/
			if(stub!=null)
				if (stub.getContainerStatus() != RawContainerHandle.COMMITTED_DROP)
					throw T_Fail.testFailMsg("expect container to be committed dropped");

			long cid2 = t_util.t_addContainer(t, 0);
			ContainerHandle c2 = t_util.t_openContainer(t, 0, cid2, true);
			page = t_util.t_addPage(c2);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			RecordHandle r2 = t_util.t_insert(page, row2);

			REPORT("setup S301: cid1 " + cid1 + " cid2 " + cid2);
			register(key(301, 1), cid1);
			register(key(301, 2), cid2);

		}
		finally	
		{
			if (page != null && page.isLatched())
				page.unlatch();
			ctx.resetContext();
		}
		// let recovery rollback incomplete transaction
	}

	/*
	 * test recovery of 301
	 */
	protected void R301() throws T_Fail, StandardException
	{
		long cid1 = find(key(301, 1));
		if (cid1 < 0)
		{
			REPORT("R301 not run");
			return;
		}

		long cid2 = find(key(301, 2));

		Transaction t = t_util.t_startTransaction();
		try
		{
			LockingPolicy nolock = 
				t.newLockingPolicy(LockingPolicy.MODE_NONE, 0, false);

			ContainerKey id1 = new ContainerKey(0, cid1);
			ContainerHandle c = t.openContainer(id1, ContainerHandle.MODE_READONLY);
			if (c != null)
				throw T_Fail.testFailMsg("expect container to be dropped");

			RawContainerHandle stub = ((RawTransaction)t).openDroppedContainer(
				id1, nolock);
			/*Not true always after fix for p4#25641(fix for bug:4580)
			  Checkpoint calls cleans up the stubs that not necessary
			  for recovery.
			  if (stub == null)
			  throw T_Fail.testFailMsg("drop container should still be there");
			*/
			if(stub!=null)
				if (stub.getContainerStatus() != RawContainerHandle.COMMITTED_DROP)
					throw T_Fail.testFailMsg("expect container to be committed dropped");

			ContainerKey id2 = new ContainerKey(0, cid2);
			c = t.openContainer(id2, ContainerHandle.MODE_READONLY);
			if (c != null)
				throw T_Fail.testFailMsg("expect container to be dropped");

			stub = ((RawTransaction)t).openDroppedContainer(
				id2, nolock);
			/*Not true always after fix for p4#25641(fix for bug:4580)
			  Checkpoint calls cleans up the stubs that not necessary
			  for recovery.
			  if (stub == null)
			  throw T_Fail.testFailMsg("drop container should still be there");
			*/
			if(stub!=null)
				if (stub.getContainerStatus() != RawContainerHandle.COMMITTED_DROP)
					throw T_Fail.testFailMsg("expect container to be committed dropped");

		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R301 : cid1 " + cid1 + " cid2 " + cid2);

	}


	
	/*
	 * test 302 - purge and delete with no data logging for purges
	 */
	protected void S302() throws T_Fail, StandardException
	{
		Transaction t = t_util.t_startTransaction();

		try
		{

			long cid = t_util.t_addContainer(t, 0);
			t_util.t_commit(t);

			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			long numPurged = 0;

			// slot 0
			RecordHandle r1 = t_util.t_insertAtSlot(page, 0, row1);
			// slot 1
			RecordHandle r2 = t_util.t_insertAtSlot(page, 1, row2);
			// slot 2
			RecordHandle r3 = (r2 == null) ? r2 : t_util.t_insertAtSlot(page, 2, row3);
			// slot 3
			RecordHandle r4 = (r3 == null) ? r3 : t_util.t_insertAtSlot(page, 3, row4);
			// slot 4
			RecordHandle r5 = (r4 == null) ? r4 : t_util.t_insertAtSlot(page, 4, row5);
			if (r5 != null)
			{
				int slot = page.getSlotNumber(r5);
				page.deleteAtSlot(slot, true, null);
			}

			t_util.t_commit(t);
			
			c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			//do some purging now
			// purge slot 0
			page.purgeAtSlot(0, 1, false);
			// purge slot 1 and 2 3
			page.purgeAtSlot(0, 3, false);
			t_util.t_abort(t);
			c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			//repurge the rows that got rolled back.
			page.purgeAtSlot(0, 1, false);
			numPurged++;
			if (r5 != null)
			{
				// purge slot 1 and 2
				page.purgeAtSlot(0, 2, false);
				numPurged += 2;
			}

			REPORT("setup S302: containerId " + cid + " recordCount " +
				   page.recordCount() + " numPurges " + numPurged);

			register(key(302,1), cid);
			register(key(302,2), page.recordCount());
			register(key(302,3), numPurged);

			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
	}

	/* recover test 302 */
	protected void R302() throws T_Fail, StandardException
	{
		long cid = find(key(302,1));
		if (cid < 0)
		{
			REPORT("R302 not run");
			return;
		}
		int recordCount = (int)find(key(302,2));
		int numPurged = (int)find(key(302,3));

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, false);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, recordCount, 1);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);
			T_RawStoreRow row4 = new T_RawStoreRow(REC_004);
			T_RawStoreRow row5 = new T_RawStoreRow(REC_005);

			t_util.t_checkFetchBySlot(page, 1, REC_005, true, false);
			if (!page.isDeletedAtSlot(1))
				throw T_Fail.testFailMsg("record should be deleted");
			//REC_004 should have become a null value because of rollback of purge
			t_util.t_checkFetchBySlot(page, 0, "NULL", false, false);
			page.unlatch();
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R302: containerId " + cid + " recordCount " +
			   recordCount + " numPurges " + numPurged);
	}


		
	/**
		Test space reclaimation - purging of a row with serveral long columns
	    rollback and repurge them again.

		@exception T_Fail Unexpected behaviour from the API
		@exception StandardException Unexpected exception from the implementation
	 */
	protected void S303() throws StandardException, T_Fail
	{
		REPORT("START S303");
		long segment = 0;
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		ctx.switchTransactionContext();

		long cid = t_util.t_addContainer(t, segment, 4096);
		ContainerHandle c = t_util.t_openContainer(t, segment, cid, true);
		Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
		try
		{
			t_util.t_checkEmptyPage(page);

			int insertFlag = Page.INSERT_INITIAL | Page.INSERT_OVERFLOW;
			T_RawStoreRow r1 = new T_RawStoreRow(1);
			// insert a long column
			r1.setColumn(0, 5000, REC_001);
			RecordHandle rh1 = t_util.t_insertAtSlot(page, 0, r1, (byte) insertFlag);	
			t_util.t_checkFetch(page, rh1, r1);

			// insert a 6 column row, every other column is long
			T_RawStoreRow r2 = new T_RawStoreRow(6);
			r2.setColumn(0, 1600, REC_001); // this takes 3200 bytes
			r2.setColumn(1, 4000, REC_002); // this takes 8000 bytes
			r2.setColumn(2, 1600, REC_001);
			r2.setColumn(3, 4000, REC_002);
			r2.setColumn(4, 1600, REC_001);
			r2.setColumn(5, 4000, REC_002);
			RecordHandle rh2 = t_util.t_insertAtSlot(page, 0, r2, (byte)
													 insertFlag);	
			
			t_util.t_checkFetch(page, rh2, r2);

			// insert a long column - this should fail
			RecordHandle rh3 = t_util.t_insertAtSlot(page, 0, r1, (byte) insertFlag);	
			if (rh3 != null)
            {
				throw T_Fail.testFailMsg("expect the 3rd row to not fit on page");
            }
			page.unlatch();
			page = null;

			Page nextPage = t_util.t_addPage(c);
			long nextPageNumber = nextPage.getPageNumber();
			// deallocate it
			t_util.t_removePage(c, nextPage);

			REPORT("S303 - Nextpage is " + nextPageNumber);

			t_util.t_commit(t);

			// now purge them
			c = t_util.t_openContainer(t, segment, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			t_util.t_checkRecordCount(page, 2, 2);
			page.purgeAtSlot(0, 2, false);
			t_util.t_checkEmptyPage(page);
			page.unlatch();
			page = null;

			t_util.t_abort(t);

			// give some time for post commit to finish
			t_util.t_wait(10);		// wait 10 milliseconds.


			// Purge them again and roll them back via recovery.  These should not
			// be reclaimed.
			c = t_util.t_openContainer(t, segment, cid, true);
 			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 2, 2);
			int rh1slotnumber = page.getSlotNumber(rh1);
			int rh2slotnumber = page.getSlotNumber(rh2);
			page.purgeAtSlot(0, 2, false);
			t_util.t_checkEmptyPage(page);

			page.unlatch();
			page = null;

			REPORT("S303 - Purged Slots" + rh1slotnumber + "," + rh2slotnumber);

			register(key(303,1), cid);
			register(key(303,2), rh1slotnumber);
			register(key(303,3), rh2slotnumber);
			
			// let recovery undo rollback this transaction
		}
		finally
		{
			if (page != null)
				page.unlatch();
			ctx.resetContext();
		}

		PASS("S303");
	}

	
	/* recover test 303:
	 * repurge the same rows whose purging we rolled back in s303.
	 */
	protected void R303() throws T_Fail, StandardException
	{
		long cid = find(key(303,1));
		if (cid < 0)
		{
			REPORT("R303 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 2, 2);
			int r1slot = (int)find(key(303,2));
			int r2slot = (int)find(key(303,3));
			T_RawStoreRow r1 = new T_RawStoreRow(1);
			// insert a long column
			r1.setColumn(0, 5000, REC_001);
			// During purges when data is not logged when slots are purged
			// they become null on rollback and some cases like long columns
			// we remove the wholepage on rollback we get the data back.
			T_RawStoreRow r2_wnl = new T_RawStoreRow(6);
			r2_wnl.setColumn(0, 4, REC_NULL); 
			r2_wnl.setColumn(1, 4000, REC_002); 
			r2_wnl.setColumn(2, 1600, REC_001);
			r2_wnl.setColumn(3, 4000, REC_002);
			r2_wnl.setColumn(4, 1600, REC_001);
			r2_wnl.setColumn(5, 4000, REC_002);
			RecordHandle rh1 = page.getRecordHandleAtSlot(r1slot) ;

			t_util.t_checkFetch(page, rh1, r1);
			RecordHandle rh2 = page.getRecordHandleAtSlot(r2slot) ;
			t_util.t_checkFetch(page, rh2, r2_wnl);

			//purge after the recovery.
			page.purgeAtSlot(0, 2, false);
			
			page.unlatch();
			page = null;
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R303");
	}



		
	/**
		Test space reclaimation - purging of a long rows with a rollback and
        purging again after recovery in R304
		@exception T_Fail Unexpected behaviour from the API
		@exception StandardException Unexpected exception from the implementation
	 */
	protected void S304() throws StandardException, T_Fail
	{
		long segment = 0;
		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		ctx.switchTransactionContext();

		long cid = t_util.t_addContainer(t, segment, 4096);
		ContainerHandle c = t_util.t_openContainer(t, segment, cid, true);
		Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
		try
		{
			t_util.t_checkEmptyPage(page);

			int insertFlag = Page.INSERT_INITIAL | Page.INSERT_OVERFLOW;

			// insert a row with 100 columns from 200 to 400 bytes each and make it
			// sprawl across many pages.
			T_RawStoreRow r1 = new T_RawStoreRow(100);
			for (int i = 0; i < 100; i++)
				r1.setColumn(i, 100+i, REC_001);

			RecordHandle rh1 = t_util.t_insertAtSlot(page, 0, r1, (byte)insertFlag);
			t_util.t_checkFetch(page, rh1, r1);
			page.unlatch();
			page = null;
			t_util.t_commit(t);

			c = t_util.t_openContainer(t, segment, cid, true);
			// Now purge that long row.
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 1, 1);
			page.purgeAtSlot(0, 1, false);
			t_util.t_checkEmptyPage(page);
			page.unlatch();
			page = null;
			// let recovery undo rollback this transaction
			register(key(304,1), cid);

		}
		finally	
		{
			if (page != null)
				page.unlatch();
			ctx.resetContext();
		}

		PASS("S304");
	}

	/* recover test 304:
	 * repurge the same rows whose purging we rolled back in s304 at recovery.
	 */
	protected void R304() throws T_Fail, StandardException
	{
		long cid = find(key(304,1));
		if (cid < 0)
		{
			REPORT("R304 not run");
			return;
		}

		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);
			t_util.t_checkRecordCount(page, 1, 1);

			// During purges when data is not logged when slots are purged
			// they become null on rollback and some cases like long columns
			// we remove the wholepage on rollback we get the data back.
			T_RawStoreRow r1_wnl = new T_RawStoreRow(100);
			for (int i = 0; i < 18; i++)
				r1_wnl.setColumn(i, 4, REC_NULL);
			for (int i = 18; i < 100; i++)
				r1_wnl.setColumn(i, 100+i, REC_001);
			RecordHandle rh1 = page.getRecordHandleAtSlot(0) ;
			t_util.t_checkFetch(page, rh1, r1_wnl);		

			//purge after the recovery.
			page.purgeAtSlot(0, 1, false);
			page.unlatch();
			page = null;
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}

		PASS("R304");
	}


	/*
	 * S999 - last test of the recovery unit test, this leave the end of the
	 * log in a fuzzy state, do NOT write any more log record after this or 
	 * you will corrupt the database
	 *
	 * DO NOT run this test if recovery test is ever run iteratively
	 */
	protected void S999() throws T_Fail, StandardException
	{
		// only runnable in debug server since trace flags are set by SanityManager
		if (!SanityManager.DEBUG) 
			return;

		T_TWC ctx = t_util.t_startTransactionWithContext();
		Transaction t = ctx.tran;
		ctx.switchTransactionContext();

		// LogToFile.TEST_LOG_SWITCH_LOG
		SanityManager.DEBUG_SET("TEST_LOG_SWITCH_LOG");

		// this will switch the log without writing out a checkpoint log record
		factory.checkpoint();
		Page page = null;

		try
		{
			long cid = t_util.t_addContainer(t, 0);
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			T_RawStoreRow row1 = new T_RawStoreRow(REC_001);
			RecordHandle rh1 = t_util.t_insertAtSlot(page, 0, row1);
			t_util.t_commit(t);

			T_RawStoreRow row2 = new T_RawStoreRow(REC_002);
			c = t_util.t_openContainer(t, 0, cid, true);
			page = t_util.t_getPage(c, ContainerHandle.FIRST_PAGE_NUMBER);

			RecordHandle rh2 = t_util.t_insertAtSlot(page, 1, row2);	
			if (rh2 == null)
			{
				REPORT("S999 not run, page cannot accomodate 2 rows");
				return;
			}


			t_util.t_checkRecordCount(page, 2, 2);
			t_util.t_checkFetch(page, rh1, REC_001);
			t_util.t_checkFetch(page, rh2, REC_002);

			T_RawStoreRow row3 = new T_RawStoreRow(REC_003);

			// LogToFile.TEST_LOG_INCOMPLETE_LOG_WRITE
			SanityManager.DEBUG_SET("TEST_LOG_INCOMPLETE_LOG_WRITE");

			// let's hope that this page is not written to disk... can't until
			// the page is unlatch, and we never unlatch it
			RecordHandle rh3 = t_util.t_insert(page, row3); // this is written out incompletely
			if (rh3 == null) {
				REPORT("S999 not run, page cannot accomodate 3 rows");
				return;
			}


			t_util.t_checkRecordCount(page, 3, 3);
			t_util.t_checkFetch(page, rh3, REC_003);

			REPORT("setup S999: cid1 " + cid + " page " + page.getPageNumber());
			register(key(999, 1), cid);
			register(key(999, 2), page.getPageNumber());
		}
		finally
		{
			SanityManager.DEBUG_CLEAR("TEST_LOG_SWITCH_LOG");
			SanityManager.DEBUG_CLEAR("TEST_LOG_INCOMPLETE_LOG_WRITE");

			ctx.resetContext();
			// let recovery roll it back
		}
	}

	protected void R999() throws StandardException, T_Fail
	{
		long cid = find(key(999,1));
		if (cid < 0)
		{
			REPORT("R999 not run");
			return;
		}

		long pageid = find(key(999,2));
		Transaction t = t_util.t_startTransaction();
		try
		{
			ContainerHandle c = t_util.t_openContainer(t, 0, cid, true);
			Page page = t_util.t_getPage(c, pageid);

			// there should be 2 rows on the page, 1 undelete and 1 deleted.
			// The third row is only partially written out the log and should
			// never appear
			t_util.t_checkRecordCount(page, 2, 1);
			t_util.t_checkFetchBySlot(page, 0, REC_001, false, false);
			t_util.t_checkFetchBySlot(page, 1, REC_002, true, false);
		}
		finally
		{
			t_util.t_commit(t);
			t.close();
		}
		PASS("R999: cid " + cid + " page " + pageid);

	}

    
    /**
     * Privileged lookup of the ContextService. Must be private so that user code
     * can't call this entry point.
     */
    private  static  ContextService    getContextService()
    {
        return AccessController.doPrivileged
            (
             new PrivilegedAction<ContextService>()
             {
                 public ContextService run()
                 {
                     return ContextService.getFactory();
                 }
             }
             );
    }

    
    /**
     * Privileged Monitor lookup. Must be private so that user code
     * can't call this entry point.
     */
    private  static  ModuleFactory  getMonitor()
    {
        return AccessController.doPrivileged
            (
             new PrivilegedAction<ModuleFactory>()
             {
                 public ModuleFactory run()
                 {
                     return Monitor.getMonitor();
                 }
             }
             );
    }

    /**
     * Privileged service lookup. Must be private so that user code
     * can't call this entry point.
     */
    private static  Object findService( final String factoryInterface, final String serviceName )
    {
        return AccessController.doPrivileged
            (
             new PrivilegedAction<Object>()
             {
                 public Object run()
                 {
                     return Monitor.findService( factoryInterface, serviceName );
                 }
             }
             );
    }
    
    /**
     * Privileged startup. Must be private so that user code
     * can't call this entry point.
     */
    private  static  boolean startPersistentService( final String serviceName, final Properties properties ) 
        throws StandardException
    {
        try {
            return AccessController.doPrivileged
                (
                 new PrivilegedExceptionAction<Boolean>()
                 {
                     public Boolean run()
                         throws StandardException
                     {
                         return Monitor.startPersistentService( serviceName, properties );
                     }
                 }
                 ).booleanValue();
        } catch (PrivilegedActionException pae)
        {
            throw StandardException.plainWrapException( pae );
        }
    }

    /**
     * Privileged startup. Must be private so that user code
     * can't call this entry point.
     */
    private  static  Object createPersistentService( final String factoryInterface, final String serviceName, final Properties properties ) 
        throws StandardException
    {
        try {
            return AccessController.doPrivileged
                (
                 new PrivilegedExceptionAction<Object>()
                 {
                     public Object run()
                         throws StandardException
                     {
                         return Monitor.createPersistentService( factoryInterface, serviceName, properties );
                     }
                 }
                 );
        } catch (PrivilegedActionException pae)
        {
            throw StandardException.plainWrapException( pae );
        }
    }

}



