sl@0: // Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0: // All rights reserved.
sl@0: // This component and the accompanying materials are made available
sl@0: // under the terms of "Eclipse Public License v1.0"
sl@0: // which accompanies this distribution, and is available
sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0: //
sl@0: // Initial Contributors:
sl@0: // Nokia Corporation - initial contribution.
sl@0: //
sl@0: // Contributors:
sl@0: //
sl@0: // Description:
sl@0: //
sl@0: 
sl@0: #include <e32test.h>
sl@0: #include <bautils.h>
sl@0: #include <sqldb.h>
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: RTest TheTest(_L("t_sqlmulti test"));
sl@0: 
sl@0: _LIT(KTestDir, "c:\\test\\");
sl@0: _LIT(KTestDbName1, "c:\\test\\t_sqlmulti.db");
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: void DeleteTestFiles()
sl@0: 	{
sl@0: 	RSqlDatabase::Delete(KTestDbName1);
sl@0: 	}
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: //Test macros and functions
sl@0: void Check1(TInt aValue, TInt aLine, TBool aPrintThreadName = EFalse)
sl@0: 	{
sl@0: 	if(!aValue)
sl@0: 		{
sl@0: 		DeleteTestFiles();
sl@0: 		if(aPrintThreadName)
sl@0: 			{
sl@0: 			RThread th;
sl@0: 			TName name = th.Name();
sl@0: 			RDebug::Print(_L("*** Thread %S, Line %d\r\n"), &name, aLine);
sl@0: 			}
sl@0: 		else
sl@0: 			{
sl@0: 			RDebug::Print(_L("*** Line %d\r\n"), aLine);
sl@0: 			}
sl@0: 		TheTest(EFalse, aLine);
sl@0: 		}
sl@0: 	}
sl@0: void Check2(TInt aValue, TInt aExpected, TInt aLine, TBool aPrintThreadName = EFalse)
sl@0: 	{
sl@0: 	if(aValue != aExpected)
sl@0: 		{
sl@0: 		DeleteTestFiles();
sl@0: 		if(aPrintThreadName)
sl@0: 			{
sl@0: 			RThread th;
sl@0: 			TName name = th.Name();
sl@0: 			RDebug::Print(_L("*** Thread %S, Line %d Expected error: %d, got: %d\r\n"), &name, aLine, aExpected, aValue);
sl@0: 			}
sl@0: 		else
sl@0: 			{
sl@0: 			RDebug::Print(_L("*** Line %d, Expected error: %d, got: %d\r\n"), aLine, aExpected, aValue);
sl@0: 			}
sl@0: 		TheTest(EFalse, aLine);
sl@0: 		}
sl@0: 	}
sl@0: #define TEST(arg) ::Check1((arg), __LINE__)
sl@0: #define TEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__)
sl@0: #define TTEST(arg) ::Check1((arg), __LINE__, ETrue)
sl@0: #define TTEST2(aValue, aExpected) ::Check2(aValue, aExpected, __LINE__, ETrue)
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: void CreateTestDir()
sl@0:     {
sl@0:     RFs fs;
sl@0: 	TInt err = fs.Connect();
sl@0: 	TEST2(err, KErrNone);
sl@0: 
sl@0: 	err = fs.MkDir(KTestDir);
sl@0: 	TEST(err == KErrNone || err == KErrAlreadyExists);
sl@0: 	
sl@0: 	fs.Close();
sl@0: 	}
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: /**
sl@0: @SYMTestCaseID			SYSLIB-SQL-CT-1612
sl@0: @SYMTestCaseDesc		Two connections to the same database in the same thread. Create a test database
sl@0: 						and insert some records from both connections. Verify that all records were inserted
sl@0: 						successfully.
sl@0: @SYMTestPriority		High
sl@0: @SYMTestActions			Testing SQL engine behaviour when having mutiple connections to the same database
sl@0: 						in the same thread.
sl@0: @SYMTestExpectedResults Test must not fail
sl@0: @SYMREQ					REQ5792
sl@0:                         REQ5793
sl@0: */	
sl@0: void TestMultiConnSameThread()
sl@0: 	{
sl@0: 	//Connection 1
sl@0: 	RSqlDatabase db1;
sl@0: 	TInt err = db1.Create(KTestDbName1);
sl@0: 	TEST2(err, KErrNone);
sl@0: 
sl@0: 	//Create test database
sl@0: 	RDebug::Print(_L("###Create test database\r\n"));
sl@0: 	_LIT8(KCreateSql, "CREATE TABLE A(Id INTEGER PRIMARY KEY AUTOINCREMENT, Data INTEGER)");
sl@0: 	err = db1.Exec(KCreateSql);
sl@0: 	TEST(err >= 0);
sl@0: 	
sl@0: 	//Connection 2
sl@0: 	RSqlDatabase db2;
sl@0: 	err = db2.Open(KTestDbName1);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	
sl@0: 	//Insert some records using both connections
sl@0: 	RDebug::Print(_L("###Insert some records\r\n"));
sl@0: 	const TInt KRecNum = 100;
sl@0: 	_LIT8(KInsertSql, "INSERT INTO A(Data) VALUES(");
sl@0: 	for(TInt i=0;i<KRecNum;++i)
sl@0: 		{
sl@0: 		TBuf8<100> sql(KInsertSql);
sl@0: 		sql.AppendNum((TInt64)i + 1);
sl@0: 		sql.Append(_L(");"));
sl@0: 		err = (i%2) ? db1.Exec(sql) : db2.Exec(sql);
sl@0: 		if(err < 0)
sl@0: 			{
sl@0: 			TPtrC msg = (i%2) ? db1.LastErrorMessage() : db2.LastErrorMessage();
sl@0: 			RDebug::Print(_L("##Db Error msg: \"%S\"\n\r"), &msg);
sl@0: 			}
sl@0: 		TEST2(err, 1);
sl@0: 		}
sl@0: 		
sl@0: 	//Check the database content
sl@0: 	RDebug::Print(_L("###Check the database content\r\n"));
sl@0: 	_LIT8(KSelectSql, "SELECT * FROM A");
sl@0: 	RSqlStatement stmt;
sl@0: 	err = stmt.Prepare(db1, KSelectSql);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	
sl@0: 	for(TInt j=0;j<KRecNum;++j)
sl@0: 		{
sl@0: 		err = stmt.Next();
sl@0: 		TEST2(err, KSqlAtRow);
sl@0: 		TEST(stmt.AtRow());
sl@0: 		TInt id = stmt.ColumnInt(0);
sl@0: 		TInt data = stmt.ColumnInt(1);
sl@0: 		TEST(id == data);
sl@0: 		}
sl@0: 	
sl@0: 	stmt.Close();
sl@0: 
sl@0: 	//Cleanup	
sl@0: 	db2.Close();
sl@0: 	db1.Close();
sl@0: 	RDebug::Print(_L("###Delete the test database\r\n"));
sl@0: 	(void)RSqlDatabase::Delete(KTestDbName1);
sl@0: 	}
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: struct TThreadData
sl@0: 	{
sl@0: 	TInt							iTransType;
sl@0: 	RSqlDatabase::TIsolationLevel	iIsolationLevel;
sl@0: 	TInt							iLowRecNo;
sl@0: 	TInt							iHighRecNo;	
sl@0: 	};
sl@0: 
sl@0: TInt ThreadFunc(void* aData)
sl@0: 	{
sl@0: 	__UHEAP_MARK;
sl@0: 	
sl@0: 	CTrapCleanup* tc = CTrapCleanup::New();
sl@0: 	TTEST(tc != NULL);
sl@0: 
sl@0: 	TThreadData* data = static_cast<TThreadData*> (aData);
sl@0: 	TTEST(data != NULL);
sl@0: 
sl@0: 	RSqlDatabase db;
sl@0: 	TInt err = db.Open(KTestDbName1);
sl@0: 	TTEST2(err, KErrNone);
sl@0: 	
sl@0: 	err = db.SetIsolationLevel(data->iIsolationLevel);
sl@0: 	TTEST2(err, KErrNone);
sl@0: 		
sl@0: 	if(data->iTransType == 1)
sl@0: 		{
sl@0: 		_LIT8(KBeginTrans, "BEGIN");
sl@0: 		err = db.Exec(KBeginTrans);
sl@0: 		TTEST(err >= 0);
sl@0: 		}
sl@0: 
sl@0: 	_LIT8(KInsertSql, "INSERT INTO A(Id) VALUES(");	
sl@0: 	for(TInt id=data->iLowRecNo;id<=data->iHighRecNo;++id)
sl@0: 		{
sl@0: 		TBuf8<128> sql(KInsertSql);
sl@0: 		sql.AppendNum((TInt64)id);
sl@0: 		sql.Append(_L(")"));
sl@0: 		err = KSqlErrBusy;
sl@0: 		const TInt KAttempts = 20;
sl@0: 		for(TInt i=0;i<KAttempts&&err==KSqlErrBusy;++i)
sl@0: 			{
sl@0: 			err = db.Exec(sql);
sl@0: 			if(err == KSqlErrBusy)
sl@0: 				{
sl@0: 				RThread th;
sl@0: 				TName name = th.Name();
sl@0: 				RDebug::Print(_L("!!!Database locked, Thread: %S, Attempt %d, column value %d\r\n"), &name, i + 1, id);
sl@0: 				User::After(1000000);
sl@0: 				}
sl@0: 			}
sl@0: 		TTEST2(err, 1);
sl@0: 		}
sl@0: 
sl@0: 	if(data->iTransType == 1)
sl@0: 		{
sl@0: 		_LIT8(KCommitTrans, "COMMIT");
sl@0: 		err = db.Exec(KCommitTrans);
sl@0: 		TTEST(err >= 0);
sl@0: 		}
sl@0: 
sl@0: 	db.Close();		
sl@0: 	delete tc;
sl@0: 	
sl@0: 	__UHEAP_MARKEND;
sl@0: 	
sl@0: 	return KErrNone;		
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @SYMTestCaseID			SYSLIB-SQL-CT-1613
sl@0: @SYMTestCaseDesc		Multiple connections to the same database from different threads. 
sl@0: 						Each thread inserts set of record to the same table. Verify that all expected records
sl@0: 						and their column values meet the expectations.
sl@0: @SYMTestPriority		High
sl@0: @SYMTestActions			Testing SQL engine behaviour when having mutiple connections to the same database
sl@0: 						from different threads.
sl@0: @SYMTestExpectedResults Test must not fail
sl@0: @SYMREQ					REQ5792
sl@0:                         REQ5793
sl@0: */	
sl@0: void TestMultiConnDiffThread()
sl@0: 	{
sl@0: 	//Create a test database
sl@0: 	RDebug::Print(_L("+++:MainThread: Create test database\r\n"));
sl@0: 	RSqlDatabase db;
sl@0: 	TInt err = db.Create(KTestDbName1);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	
sl@0: 	//Create a test table
sl@0: 	RDebug::Print(_L("+++:MainThread: Create a table in the test database\r\n"));
sl@0: 	_LIT8(KCreateSql, "CREATE TABLE A(Id INTEGER PRIMARY KEY)");
sl@0: 	err = db.Exec(KCreateSql);
sl@0: 	TEST(err >= 0);
sl@0: 	
sl@0: 	const TInt KThreadCnt = 4;
sl@0: 	const TInt KRange = 100;
sl@0: 	
sl@0: 	const TInt KIsolationLevelCnt = 2;
sl@0: 	TPtrC KIsolationLevelName[KIsolationLevelCnt] = {_L("Read Uncommitted"), _L("Serializable")};
sl@0: 	const RSqlDatabase::TIsolationLevel KIsolationLevels[KIsolationLevelCnt] = {
sl@0: 											RSqlDatabase::EReadUncommitted, RSqlDatabase::ESerializable};
sl@0: 											
sl@0: 	const TInt KTransTypeCnt = 2;											
sl@0: 
sl@0: 	//Do the tests:
sl@0: 	// - doing each per thread database operation in a single transaction;
sl@0: 	// - doing all per thread database operations in a single transaction;
sl@0: 	for(TInt transType=0;transType<KTransTypeCnt;++transType)	
sl@0: 		{
sl@0: 		//For both supported isolation levels: read uncommitted and serializable
sl@0: 		for(TInt isolLevel=0;isolLevel<KIsolationLevelCnt;++isolLevel)
sl@0: 			{
sl@0: 			TInt low = 1;
sl@0: 			TInt high = KRange;
sl@0: 			
sl@0: 			RDebug::Print(_L("+++:MainThread: Test: thread count %d, records %d, trans type %d, isolation level: %S\r\n"), 
sl@0: 									KThreadCnt, KRange, transType, &KIsolationLevelName[isolLevel]);
sl@0: 									
sl@0: 			RThread thread[KThreadCnt];
sl@0: 			TRequestStatus status[KThreadCnt];
sl@0: 			TThreadData	data[KThreadCnt];
sl@0: 
sl@0: 			//Create the test threads and run them. Each thread establishes a connection with the test database
sl@0: 			//and attempts to write set of records in the test table.
sl@0: 			TInt j;
sl@0: 			for(j=0;j<KThreadCnt;++j,low=high+1,high+=KRange)
sl@0: 				{
sl@0: 				data[j].iTransType = transType;
sl@0: 				data[j].iIsolationLevel = KIsolationLevels[isolLevel];
sl@0: 				data[j].iLowRecNo = low;
sl@0: 				data[j].iHighRecNo = high;
sl@0: 				
sl@0: 				_LIT(KThreadName,"Thr-");
sl@0: 				TBuf<32> threadName(KThreadName);
sl@0: 				threadName.AppendNum((TInt64)j + 1);
sl@0: 				
sl@0: 				TEST2(thread[j].Create(threadName, &ThreadFunc, 0x2000, 0x1000, 0x10000, (void*)&data[j], EOwnerThread), KErrNone);
sl@0: 				thread[j].Logon(status[j]);
sl@0: 				TEST2(status[j].Int(), KRequestPending);
sl@0: 				thread[j].Resume();
sl@0: 				}
sl@0: 			
sl@0: 			User::After(2000000);
sl@0: 			//Wait until threads finish the database operations and close them.				
sl@0: 			for(j=0;j<KThreadCnt;++j)
sl@0: 				{
sl@0: 				User::WaitForRequest(status[j]);
sl@0: 				TEST(thread[j].ExitType() != EExitPanic);
sl@0: 				thread[j].Close();
sl@0: 				}
sl@0: 
sl@0: 			//Check that all records which are esupposed to be in the database, are there.
sl@0: 			RDebug::Print(_L("+++:MainThread: Check that all records have been written\r\n"));
sl@0: 			_LIT8(KSelectSql1, "SELECT COUNT(*) FROM A;");
sl@0: 			RSqlStatement stmt;
sl@0: 			err = stmt.Prepare(db, KSelectSql1);
sl@0: 			TEST2(err, KErrNone);
sl@0: 			err = stmt.Next();
sl@0: 			TEST2(err, KSqlAtRow);
sl@0: 			TInt cnt = stmt.ColumnInt(0);
sl@0: 			TEST2(cnt, KThreadCnt * KRange);
sl@0: 			stmt.Close();
sl@0: 			
sl@0: 			//Check that all records have expected column values.
sl@0: 			RDebug::Print(_L("+++:MainThread: Check that all records have expected column values\r\n"));
sl@0: 			_LIT8(KSelectSql2, "SELECT * FROM A;");
sl@0: 			err = stmt.Prepare(db, KSelectSql2);
sl@0: 			TEST2(err, KErrNone);
sl@0: 			for(TInt k=0;k<(KThreadCnt*KRange);++k)
sl@0: 				{
sl@0: 				err = stmt.Next();
sl@0: 				TEST2(err, KSqlAtRow);
sl@0: 				TInt val = stmt.ColumnInt(0);
sl@0: 				TEST(val > 0 && val <= (KThreadCnt * KRange));
sl@0: 				}
sl@0: 			stmt.Close();
sl@0: 
sl@0: 			//Prepare for the next test run - delete all records.
sl@0: 			RDebug::Print(_L("+++:MainThread: Delete all records\r\n"));
sl@0: 			_LIT8(KDeleteSql, "DELETE FROM A");
sl@0: 			err = db.Exec(KDeleteSql);
sl@0: 			TEST(err >= 0);
sl@0: 			}//end of "for(TInt isolLevel=0;isolLevel<KIsolationLevelCnt;++isolLevel)"
sl@0: 		}//end of "for(TInt transType=0;transType<KTransTypeCnt;++transType)"
sl@0: 		
sl@0: 	db.Close();
sl@0: 	RDebug::Print(_L("+++:MainThread: Delete the test database\r\n"));
sl@0: 	(void)RSqlDatabase::Delete(KTestDbName1);
sl@0: 	}
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: static RCriticalSection UpdateThreadCrS;
sl@0: static RCriticalSection MainThreadCrS;
sl@0: const TInt KInitialValue = 10;
sl@0: const TInt KUpdatedValue = 20;
sl@0: 
sl@0: TInt UpdateThreadFunc(void*)
sl@0: 	{
sl@0: 	__UHEAP_MARK;
sl@0: 	
sl@0: 	CTrapCleanup* tc = CTrapCleanup::New();
sl@0: 	TTEST(tc != NULL);
sl@0: 
sl@0: 	RSqlDatabase db;
sl@0: 	TInt err = db.Open(KTestDbName1);
sl@0: 	TTEST2(err, KErrNone);
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Set the isolation level to \"Read uncommitted\"\r\n"));
sl@0: 	err = db.SetIsolationLevel(RSqlDatabase::EReadUncommitted);
sl@0: 	TTEST2(err, KErrNone);
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Begin a write transaction\r\n"));
sl@0: 	_LIT8(KBeginTransSql, "BEGIN IMMEDIATE TRANSACTION");
sl@0: 	err = db.Exec(KBeginTransSql);
sl@0: 	TTEST(err >= 0);
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Update the record\r\n"));
sl@0: 	_LIT8(KUpdateSql, "UPDATE A SET Id = ");
sl@0: 	TBuf8<64> sql(KUpdateSql);
sl@0: 	sql.AppendNum((TInt64)KUpdatedValue);
sl@0: 	err = db.Exec(sql);
sl@0: 	TTEST(err >= 0);
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Notify the main thread about the update\r\n"));
sl@0: 	MainThreadCrS.Signal();
sl@0: 	
sl@0: 	RDebug::Print(_L("---:UpdThread: Wait for permisson to continue...\r\n"));
sl@0: 	UpdateThreadCrS.Wait();
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Rollback the update\r\n"));
sl@0: 	_LIT8(KRollBackTransSql, "ROLLBACK TRANSACTION");
sl@0: 	err = db.Exec(KRollBackTransSql);
sl@0: 	TTEST(err >= 0);
sl@0: 
sl@0: 	RDebug::Print(_L("---:UpdThread: Notify the main thread about the rollback\r\n"));
sl@0: 	MainThreadCrS.Signal();
sl@0: 	
sl@0: 	db.Close();
sl@0: 	delete tc;
sl@0: 	
sl@0: 	__UHEAP_MARKEND;
sl@0: 	
sl@0: 	return KErrNone;		
sl@0: 	}
sl@0: 
sl@0: /**
sl@0: @SYMTestCaseID			SYSLIB-SQL-CT-1614
sl@0: @SYMTestCaseDesc		Verifying that when having 2 database connections in different threads, both set
sl@0: 						the isolation level to "Read Uncommitted", the reading thread can make "dirty read"
sl@0: 						operations (can read the updated but not committed yet record values made by the
sl@0: 						writing thread).
sl@0: @SYMTestPriority		High
sl@0: @SYMTestActions			Testing "Read Uncommitted" database isolation level.
sl@0: @SYMTestExpectedResults Test must not fail
sl@0: @SYMREQ					REQ5792
sl@0:                         REQ5793
sl@0: */	
sl@0: void TestIsolationLevel()
sl@0: 	{
sl@0: 	RDebug::Print(_L("+++:MainThread: Create critical sections\r\n"));
sl@0: 	TEST2(UpdateThreadCrS.CreateLocal(), KErrNone);
sl@0: 	UpdateThreadCrS.Wait();
sl@0: 	TEST2(MainThreadCrS.CreateLocal(), KErrNone);
sl@0: 	MainThreadCrS.Wait();
sl@0: 	
sl@0: 	RDebug::Print(_L("+++:MainThread: Create test database\r\n"));
sl@0: 	RSqlDatabase db;
sl@0: 	TInt err = db.Create(KTestDbName1);
sl@0: 	TEST2(err, KErrNone);
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Set the isolation level to \"Read uncommitted\"\r\n"));
sl@0: 	err = db.SetIsolationLevel(RSqlDatabase::EReadUncommitted);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	
sl@0: 	RDebug::Print(_L("+++:MainThread: Create a table in the test database\r\n"));
sl@0: 	_LIT8(KCreateSql, "CREATE TABLE A(Id INTEGER)");
sl@0: 	err = db.Exec(KCreateSql);
sl@0: 	TEST(err >= 0);
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Insert one record in the table\r\n"));
sl@0: 	_LIT8(KInsertSql, "INSERT INTO A(Id) VALUES(");
sl@0: 	TBuf8<64> sql(KInsertSql);
sl@0: 	sql.AppendNum((TInt64)KInitialValue);
sl@0: 	sql.Append(_L(")"));
sl@0: 	err = db.Exec(sql);
sl@0: 	TEST2(err, 1);
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Create the \"update\" thread\r\n"));
sl@0: 	_LIT(KThreadName, "UpdTh");
sl@0: 	RThread thread;
sl@0: 	TEST2(thread.Create(KThreadName, &UpdateThreadFunc, 0x2000, 0x1000, 0x10000, NULL, EOwnerThread), KErrNone);
sl@0: 	TRequestStatus status;
sl@0: 	thread.Logon(status);
sl@0: 	TEST2(status.Int(), KRequestPending);
sl@0: 	thread.Resume();
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Wait for record update completion...\r\n"));
sl@0: 	MainThreadCrS.Wait();
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Read the record and check the data...\r\n"));
sl@0: 	_LIT8(KSelectSql, "SELECT * FROM A");
sl@0: 	RSqlStatement stmt;
sl@0: 	err = stmt.Prepare(db, KSelectSql);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	err = stmt.Next();
sl@0: 	TEST2(err, KSqlAtRow);
sl@0: 	TInt val = stmt.ColumnInt(0);
sl@0: 	TEST(val == KUpdatedValue);
sl@0: 	stmt.Close();
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Notify the update thread that it can rollback\r\n"));
sl@0: 	UpdateThreadCrS.Signal();
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Wait for  rollback  completion...\r\n"));
sl@0: 	MainThreadCrS.Wait();
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Read the record and check the data...\r\n"));
sl@0: 	err = stmt.Prepare(db, KSelectSql);
sl@0: 	TEST2(err, KErrNone);
sl@0: 	err = stmt.Next();
sl@0: 	TEST2(err, KSqlAtRow);
sl@0: 	val = stmt.ColumnInt(0);
sl@0: 	TEST2(val, KInitialValue);
sl@0: 	stmt.Close();
sl@0: 
sl@0: 	User::WaitForRequest(status);
sl@0: 	thread.Close();
sl@0: 
sl@0: 	db.Close();
sl@0: 	RDebug::Print(_L("+++:MainThread: Delete the test database\r\n"));
sl@0: 	(void)RSqlDatabase::Delete(KTestDbName1);
sl@0: 
sl@0: 	RDebug::Print(_L("+++:MainThread: Close critical sections\r\n"));
sl@0: 	MainThreadCrS.Close();
sl@0: 	UpdateThreadCrS.Close();
sl@0: 	}
sl@0: 
sl@0: ///////////////////////////////////////////////////////////////////////////////////////
sl@0: 
sl@0: void DoTestsL()
sl@0: 	{
sl@0: 	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1612 Multiple connections, the same thread "));
sl@0: 	TestMultiConnSameThread();
sl@0: 
sl@0: 	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1613 Multiple connections, different threads "));
sl@0: 	TestMultiConnDiffThread();
sl@0: 
sl@0: 	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-CT-1614 Isolation level "));
sl@0: 	TestIsolationLevel();
sl@0: 	}
sl@0: 
sl@0: TInt E32Main()
sl@0: 	{
sl@0: 	TheTest.Title();
sl@0: 	
sl@0: 	CTrapCleanup* tc = CTrapCleanup::New();
sl@0: 	
sl@0: 	__UHEAP_MARK;
sl@0: 	
sl@0: 	CreateTestDir();
sl@0: 	DeleteTestFiles();
sl@0: 	TRAPD(err, DoTestsL());
sl@0: 	DeleteTestFiles();
sl@0: 	TEST2(err, KErrNone);
sl@0: 
sl@0: 	__UHEAP_MARKEND;
sl@0: 	
sl@0: 	TheTest.End();
sl@0: 	TheTest.Close();
sl@0: 	
sl@0: 	delete tc;
sl@0: 	
sl@0: 	User::Heap().Check();
sl@0: 	return KErrNone;
sl@0: 	}