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 sl@0: #include sl@0: #include 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 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 (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;iiTransType == 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 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 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= 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: }