diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/TEST/t_sqldbconfigfile.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sql/TEST/t_sqldbconfigfile.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,1652 @@ +// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include +#include +#include +#include +#include +#include "sqlite3.h" +#include "SqliteSymbian.h" +#include "SqlResourceTester.h" + +///////////////////////////////////////////////////////////////////////////////////////////////// +/// This test works only if the whole SQL component is built with SYSLIBS_TEST macro defined! /// +///////////////////////////////////////////////////////////////////////////////////////////////// + +RTest TheTest(_L("t_sqldbconfigfile test")); + +#ifdef SYSLIBS_TEST + +extern TBool IsStatementSupported(const TDesC& aStatementIn, const TDesC& aDbName, TDes& aStatementOut); + +RFs TheFs; +RSqlDatabase TheDb; +sqlite3 *TheDbHandle = NULL; + +_LIT(KTestDir, "c:\\test\\"); +_LIT(KSqlSrvName, "sqlsrv.exe"); + +enum TConfigParamType {EPrmCacheSize, EPrmPageSize, EPrmDbEncoding}; + +TInt KillProcess(const TDesC& aProcessName); + +_LIT(KCfgDb1, "c:[1111C1C1]a.db"); // shared, secure db +_LIT(KCfgDb2, "c:[1111C1C1]b.db"); // shared, secure db (no config file for it) +_LIT(KCfgDb3, "c:\\private\\1111C1C1\\c.db"); // private, secure db +_LIT(KCfgDb4, "c:\\test\\d.db"); // public db +_LIT(KCfgDb5, "c:[1111C1C1]e.db"); // shared, secure db (config file created before db is created) + +_LIT(KCfgDb1ConfigFilePath, "c:\\private\\10281e17\\cfg[1111C1C1]a.db.0%d"); // config file version %d for a.db +_LIT(KCfgDb3ConfigFileV01Path, "c:\\private\\10281e17\\cfgc.db.01"); // config file v01 for c.db (unsupported) +_LIT(KCfgDb4ConfigFileV01Path, "c:\\private\\10281e17\\cfgd.db.01"); // config file v01 for d.db (unsupported) +_LIT(KCfgDb5ConfigFileV01Path, "c:\\private\\10281e17\\cfg[1111C1C1]e.db.01"); // config file v01 for e.db +_LIT(KCfgDb1CorruptConfigFilePath, "c:\\private\\10281e17\\cfg[1111C1C1]a.db.invalidextension"); // invalid config file name syntax + +// config file v01 contents for db1 (also tests that upper and lower case statements are both supported) +_LIT8(KCfgDb1ConfigFile01Stmts, "CREATE INDEX idx ON table1(i1);CREATE INDEX idx2 ON table1(i2);INSERT INTO table1 (i1,i2,i3) values(5,8,9);DELETE FROM table1;create index multiidx ON TABLE1(i2,i3)"); +// config file v01 contents for db3 (will not be processed as db3 is not a shared, secure db) +_LIT8(KCfgDb3ConfigFile01Stmts, "CREATE INDEX idx ON table3(i1)"); +// config file v01 contents for db4 (will not be processed as db4 is not a shared, secure db) +_LIT8(KCfgDb4ConfigFile01Stmts, "CREATE INDEX idx ON table4(i1)"); +// config file v01 contents for db5 (will eventually be processed after db5 itself is created) +_LIT8(KCfgDb5ConfigFile01Stmts, "CREATE INDEX idx ON table5(i1);\r\n"); +// config file valid contents (used for v02 and others) +_LIT8(KCfgConfigFileValidStmt, "CREATE INDEX newIdx ON table1(i3) ;"); +// config file v03 empty contents +_LIT8(KCfgDb1ConfigFileV03EmptyStmt, ""); +// config file v04 unsupported contents +_LIT8(KCfgDb1ConfigFileV04UnsupportedStmt, "DELETE FROM table1"); +// config file v05 only whitespace contents +_LIT8(KCfgDb1ConfigFileV05OnlyWhitespaceStmt, " \r\n \r\n"); +// config file v06 invalid schema contents +_LIT8(KCfgDb1ConfigFileV06InvalidSchemaStmt, "CREATE INDEX thisIdx ON table999(i3)"); +// config file v07 unsupported comment style +_LIT8(KCfgDb1ConfigFileV07InvalidCommentedStmt, "CREATE INDEX ind1 ON table1(i2) // create an index"); +// config file v08 sequence of different statements +_LIT8(KCfgDb1ConfigFileV08SeqStmt, "; CREATE INDEX IdxFirst ON table1(i3)\r\n; DELETE FROM table1;INSERT INTO table1 (i1,i2,i3) values(6,7,8);;CREATE INDEX IdxSecond ON table1(i1);"); +// config file v09 whitespace before and after statement +_LIT8(KCfgDb1ConfigFileV09WhitespacePreAndPostStmt, " CREATE INDEX intIdx ON table1(i1) "); +// config file v10 valid contents +_LIT8(KCfgDb1ConfigFileV10ValidStmt, "CREATE INDEX i3Index ON table1(i3)\n"); +// config file v11 valid contents (also tests that any amount spaces and tabs are allowed between 'CREATE' and 'INDEX') +_LIT8(KCfgDb1ConfigFileV11ValidStmt, "CREATE INDEX i1Index ON table1(i1);\nCREATE INDEX i2Index ON table1(i2)"); +// config file v12 invalid stmt plus valid stmt +_LIT8(KCfgDb1ConfigFileV12InvalidPlusValidStmt, "CREATE UNIQUE INDEX uniqueIdx ON table1(i1);CREATE INDEX v12Idx ON table1(i2)"); +// config file v13 supported SQL comment style +_LIT8(KCfgDb1ConfigFileV13SQLCommentStmt, "CREATE INDEX v13Idx ON table1(i1) -- this is an SQL comment"); +// config file v14 supported 'C' comment style +_LIT8(KCfgDb1ConfigFileV14CCommentStmt, "CREATE INDEX v14Idx ON table1(i3) /* this is a C comment */;"); + +//KLongDbName1 is "long" enough to allow "-journal" to be added at the end. +_LIT(KLongDbName1, "c:[1111C1C1]a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a1234.db"); +_LIT(KLongCfgName1, "c:\\private\\10281e17\\cfg[1111C1C1]a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a123456789a1234.db.01"); + +_LIT(KSqlSrvPrivatePath, "\\private\\10281e17\\"); +_LIT(KGetSettingsSql, "SELECT * FROM symbian_settings WHERE Reserved==%d"); +_LIT(KResetCollationDllSql, "UPDATE symbian_settings SET CollationDllName='hjagafsff'"); +_LIT(KGetCollationDllSql, "SELECT CollationDllName FROM symbian_settings"); + +_LIT(KAttachDb1, "Db1"); +_LIT(KAttachDb2, "Db2"); +_LIT(KAttachDb5, "Db5"); + +_LIT(KDb1CheckNumRecords, "SELECT * FROM table1"); +_LIT(KDb2CheckNumRecords, "SELECT * FROM table2"); +_LIT(KDb3CheckNumRecords, "SELECT * FROM table3"); +_LIT(KDb4CheckNumRecords, "SELECT * FROM table4"); +_LIT(KDb5CheckNumRecords, "SELECT * FROM table5"); +_LIT(KDbCheckNumIndices, "SELECT name FROM sqlite_master WHERE type = 'index'"); + +/////////////////////////////////////////////////////////////////////////////////////// +// Destroy functions + +TInt KillProcess(const TDesC& aProcessName) + { + TFullName name; + //RDebug::Print(_L("Find and kill \"%S\" process.\n"), &aProcessName); + TBuf<64> pattern(aProcessName); + TInt length = pattern.Length(); + pattern += _L("*"); + TFindProcess procFinder(pattern); + + while (procFinder.Next(name) == KErrNone) + { + if (name.Length() > length) + {//If found name is a string containing aProcessName string. + TChar c(name[length]); + if (c.IsAlphaDigit() || + c == TChar('_') || + c == TChar('-')) + { + // If the found name is other valid application name + // starting with aProcessName string. + //RDebug::Print(_L(":: Process name: \"%S\".\n"), &name); + continue; + } + } + RProcess proc; + if (proc.Open(name) == KErrNone) + { + proc.Kill(0); + //RDebug::Print(_L("\"%S\" process killed.\n"), &name); + } + proc.Close(); + } + return KErrNone; + } + +void DeleteCfgFilesAndDbs() + { + (void)RSqlDatabase::Delete(KLongDbName1); + (void)RSqlDatabase::Delete(KCfgDb1); + (void)RSqlDatabase::Delete(KCfgDb2); + (void)RSqlDatabase::Delete(KCfgDb3); + (void)RSqlDatabase::Delete(KCfgDb4); + (void)RSqlDatabase::Delete(KCfgDb5); + (void)TheFs.Delete(KLongCfgName1); + (void)TheFs.Delete(KCfgDb3ConfigFileV01Path); + (void)TheFs.Delete(KCfgDb4ConfigFileV01Path); + (void)TheFs.Delete(KCfgDb5ConfigFileV01Path); + CFileMan* fileMan = 0; + TRAPD(err, fileMan = CFileMan::NewL(TheFs)); + if(KErrNone == err) + { + (void)fileMan->Delete(_L("c:\\private\\10281e17\\cfg[1111C1C1]a.db.*")); + delete fileMan; + } + } + +void DestroyTestEnv() + { + if(TheDbHandle) + { + sqlite3_close(TheDbHandle); + TheDbHandle = NULL; + } + TheDb.Close(); + (void)KillProcess(KSqlSrvName); + DeleteCfgFilesAndDbs(); + TheFs.Close(); + } + +/////////////////////////////////////////////////////////////////////////////////////// +// Test macros and functions + +void Check(TInt aValue, TInt aLine) + { + if(!aValue) + { + DestroyTestEnv(); + TheTest(EFalse, aLine); + } + } +void Check(TInt aValue, TInt aExpected, TInt aLine) + { + if(aValue != aExpected) + { + DestroyTestEnv(); + TheTest.Printf(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue); + TheTest(EFalse, aLine); + } + } +#define TEST(arg) ::Check((arg), __LINE__) +#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__) + +// OOM test functions + +static TInt TheHandleCount1; +static TInt TheHandleCount2; +static TInt TheAllocatedCellsCount; + +void MarkHandles() + { + RThread().HandleCount(TheHandleCount1, TheHandleCount2); + } + +void CheckHandles() + { + TInt endHandleCount1; + TInt endHandleCount2; + + RThread().HandleCount(endHandleCount1, endHandleCount2); + + TEST(TheHandleCount1 == endHandleCount1); + TEST(TheHandleCount2 == endHandleCount2); + } + +void MarkAllocatedCells() + { + TheAllocatedCellsCount = User::CountAllocCells(); + } + +void CheckAllocatedCells() + { + TInt allocatedCellsCount = User::CountAllocCells(); + TEST(allocatedCellsCount == TheAllocatedCellsCount); + } + +/////////////////////////////////////////////////////////////////////////////////////// +// Set up functions + +TInt DoCreateSecurityPolicy(RSqlSecurityPolicy& securityPolicy) + { + const TSecurityPolicy KDefaultPolicy(TSecurityPolicy::EAlwaysPass); + if((KErrNone != securityPolicy.Create(KDefaultPolicy)) + || + (KErrNone != securityPolicy.SetDbPolicy(RSqlSecurityPolicy::ESchemaPolicy, KDefaultPolicy)) + || + (KErrNone != securityPolicy.SetDbPolicy(RSqlSecurityPolicy::EWritePolicy, KDefaultPolicy)) + || + (KErrNone != securityPolicy.SetDbPolicy(RSqlSecurityPolicy::EReadPolicy, KDefaultPolicy))) + { + return KErrGeneral; + } + + return KErrNone; + } + +void CreateCfgFiles() + { + // Create v01 of the test config files + + RFile file; + TFileName fileName; + TInt v1 = 1; + fileName.Format(KCfgDb1ConfigFilePath(), v1); + TInt err = file.Create(TheFs, fileName, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 pDb1((const TUint8*)KCfgDb1ConfigFile01Stmts().Ptr(), KCfgDb1ConfigFile01Stmts().Length()); + err = file.Write(pDb1); + file.Close(); + TEST2(err, KErrNone); + + fileName.Copy(KCfgDb3ConfigFileV01Path); + err = file.Create(TheFs, fileName, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 pDb3((const TUint8*)KCfgDb3ConfigFile01Stmts().Ptr(), KCfgDb3ConfigFile01Stmts().Length()); + err = file.Write(pDb3); + file.Close(); + TEST2(err, KErrNone); + + fileName.Copy(KCfgDb4ConfigFileV01Path); + err = file.Create(TheFs, fileName, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 pDb4((const TUint8*)KCfgDb4ConfigFile01Stmts().Ptr(), KCfgDb4ConfigFile01Stmts().Length()); + err = file.Write(pDb4); + file.Close(); + TEST2(err, KErrNone); + + fileName.Copy(KCfgDb5ConfigFileV01Path); // create the config file for Db5 before the database has been created + err = file.Create(TheFs, fileName, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 pDb5((const TUint8*)KCfgDb5ConfigFile01Stmts().Ptr(), KCfgDb5ConfigFile01Stmts().Length()); + err = file.Write(pDb5); + file.Close(); + TEST2(err, KErrNone); + } + +void CreateCfgDbs() + { + // Create the test databases + + TBuf<100> sql; + + RSqlSecurityPolicy securityPolicy; + TInt err = DoCreateSecurityPolicy(securityPolicy); + TEST2(err, KErrNone); + + err = TheDb.Create(KCfgDb1, securityPolicy); + TEST2(err, KErrNone); + sql.Copy(_L("CREATE TABLE table1(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + err = TheDb.Exec(sql); + TEST(err >= 0); + sql.Copy(_L("INSERT INTO table1 (i1,i2,i3) values(1,2,3)")); + err = TheDb.Exec(sql); + TEST(err == 1); + TheDb.Close(); + + err = TheDb.Create(KCfgDb2, securityPolicy); + TEST2(err, KErrNone); + sql.Copy(_L("CREATE TABLE table2(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + err = TheDb.Exec(sql); + TEST(err >= 0); + sql.Copy(_L("INSERT INTO table2 (i1,i2,i3) values(4,5,6)")); + err = TheDb.Exec(sql); + TEST(err == 1); + TheDb.Close(); + + securityPolicy.Close(); + + err = TheDb.Create(KCfgDb3); + TEST2(err, KErrNone); + sql.Copy(_L("CREATE TABLE table3(i1 INTEGER, i2 INTEGER)")); + err = TheDb.Exec(sql); + TEST(err >= 0); + sql.Copy(_L("INSERT INTO table3 (i1,i2) values(7,8)")); + err = TheDb.Exec(sql); + TEST(err == 1); + TheDb.Close(); + + err = TheDb.Create(KCfgDb4); + TEST2(err, KErrNone); + sql.Copy(_L("CREATE TABLE table4(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + err = TheDb.Exec(sql); + TEST(err >= 0); + sql.Copy(_L("INSERT INTO table4 (i1,i2,i3) values(9,10,11)")); + err = TheDb.Exec(sql); + TEST(err == 1); + TheDb.Close(); + } + +void CreateCfgFilesAndDbs() + { + CreateCfgFiles(); + CreateCfgDbs(); + + // Must now kill the SQL Server so that the config files + // created above are found and processed when it restarts + (void)KillProcess(KSqlSrvName); + } + + void CreateDb5() + { + // Create the Db5 test database (a config file for it already exists) + + TBuf<100> sql; + + RSqlSecurityPolicy securityPolicy; + TInt err = DoCreateSecurityPolicy(securityPolicy); + TEST2(err, KErrNone); + + err = TheDb.Create(KCfgDb5, securityPolicy); + TEST2(err, KErrNone); + sql.Copy(_L("CREATE TABLE table5(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + err = TheDb.Exec(sql); + TEST(err >= 0); + sql.Copy(_L("INSERT INTO table5 (i1,i2,i3) values(1,2,3)")); + err = TheDb.Exec(sql); + TEST(err == 1); + + TheDb.Close(); + securityPolicy.Close(); + } + +void SetupTestEnv() + { + TInt err = TheFs.Connect(); + TEST2(err, KErrNone); + + err = TheFs.MkDir(KTestDir); + TEST(err == KErrNone || err == KErrAlreadyExists); + + err = TheFs.CreatePrivatePath(EDriveC); + TEST(err == KErrNone || err == KErrAlreadyExists); + + // Create the cfg dbs and config files + DeleteCfgFilesAndDbs(); // just incase any previous files are lingering + CreateCfgFilesAndDbs(); + } + +/////////////////////////////////////////////////////////////////////////////////////// +// +TInt CalcTimeMs(TUint32 aStartTicks, TUint32 aEndTicks) + { + static TInt freq = 0; + if(freq == 0) + { + TEST2(HAL::Get(HAL::EFastCounterFrequency, freq), KErrNone); + } + TInt64 diffTicks = (TInt64)aEndTicks - (TInt64)aStartTicks; + if(diffTicks < 0) + { + diffTicks = KMaxTUint32 + diffTicks + 1; + } + const TInt KMicroSecIn1Sec = 1000000; + TInt32 us = (diffTicks * KMicroSecIn1Sec) / freq; + return us / 1000; + } + +/////////////////////////////////////////////////////////////////////////////////////// +// Config file replacement functions + +void UpgradeDbConfigFile(const TInt aCurrentVersion) + { + (void)KillProcess(KSqlSrvName); + TFileName oldFile; + oldFile.Format(KCfgDb1ConfigFilePath(), aCurrentVersion); + TInt err = TheFs.Delete(oldFile); + TEST2(err, KErrNone); + RFile file; + TFileName newFile; + TInt newVersion = aCurrentVersion+1; + newFile.Format(KCfgDb1ConfigFilePath(), newVersion); + err = file.Create(TheFs, newFile, EFileRead | EFileWrite); + TEST2(err, KErrNone); + + switch(newVersion) + { + case 2: + { + TPtrC8 p((const TUint8*)KCfgConfigFileValidStmt().Ptr(), KCfgConfigFileValidStmt().Length()); + err = file.Write(p); + break; + } + case 3: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV03EmptyStmt().Ptr(), KCfgDb1ConfigFileV03EmptyStmt().Length()); + err = file.Write(p); + break; + } + case 4: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV04UnsupportedStmt().Ptr(), KCfgDb1ConfigFileV04UnsupportedStmt().Length()); + err = file.Write(p); + break; + } + case 5: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV05OnlyWhitespaceStmt().Ptr(), KCfgDb1ConfigFileV05OnlyWhitespaceStmt().Length()); + err = file.Write(p); + break; + } + case 6: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV06InvalidSchemaStmt().Ptr(), KCfgDb1ConfigFileV06InvalidSchemaStmt().Length()); + err = file.Write(p); + break; + } + case 7: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV07InvalidCommentedStmt().Ptr(), KCfgDb1ConfigFileV07InvalidCommentedStmt().Length()); + err = file.Write(p); + break; + } + case 8: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV08SeqStmt().Ptr(), KCfgDb1ConfigFileV08SeqStmt().Length()); + err = file.Write(p); + break; + } + case 9: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV09WhitespacePreAndPostStmt().Ptr(), KCfgDb1ConfigFileV09WhitespacePreAndPostStmt().Length()); + err = file.Write(p); + break; + } + case 12: + { + // also delete version 10 of file + oldFile.Format(KCfgDb1ConfigFilePath(), 10); + err = TheFs.Delete(oldFile); + TEST2(err, KErrNone); + + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV12InvalidPlusValidStmt().Ptr(), KCfgDb1ConfigFileV12InvalidPlusValidStmt().Length()); + err = file.Write(p); + break; + } + case 13: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV13SQLCommentStmt().Ptr(), KCfgDb1ConfigFileV13SQLCommentStmt().Length()); + err = file.Write(p); + break; + } + case 14: + { + TPtrC8 p((const TUint8*)KCfgDb1ConfigFileV14CCommentStmt().Ptr(), KCfgDb1ConfigFileV14CCommentStmt().Length()); + err = file.Write(p); + break; + } + default: + { + err = KErrArgument; + break; + } + } + file.Close(); + TEST2(err, KErrNone); + } + +void DowngradeDbConfigFile(const TInt aCurrentVersion) + { + (void)KillProcess(KSqlSrvName); + TFileName oldFile; + oldFile.Format(KCfgDb1ConfigFilePath(), aCurrentVersion); + TInt err = TheFs.Delete(oldFile); + TEST2(err, KErrNone); + RFile file; + TFileName newFile; + TInt previousVersion = aCurrentVersion-1; + TEST(previousVersion > 0); + newFile.Format(KCfgDb1ConfigFilePath(), previousVersion); + err = file.Create(TheFs, newFile, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 p((const TUint8*)KCfgConfigFileValidStmt().Ptr(), KCfgConfigFileValidStmt().Length()); + err = file.Write(p); + file.Close(); + TEST2(err, KErrNone); + } + +void CreateCorruptDbConfigFile(const TInt aCurrentVersion) + { + (void)KillProcess(KSqlSrvName); + TFileName oldFile; + oldFile.Format(KCfgDb1ConfigFilePath(), aCurrentVersion); + TInt err = TheFs.Delete(oldFile); + TEST2(err, KErrNone); + RFile file; + err = file.Create(TheFs, KCfgDb1CorruptConfigFilePath(), EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 p((const TUint8*)KCfgConfigFileValidStmt().Ptr(), KCfgConfigFileValidStmt().Length()); + err = file.Write(p); + file.Close(); + TEST2(err, KErrNone); + } + +void CreateTwoVersionsOfConfigFile() + { + (void)KillProcess(KSqlSrvName); + TInt err = TheFs.Delete(KCfgDb1CorruptConfigFilePath); + TEST2(err, KErrNone); + RFile file; + TFileName newFile; + TInt nextVersion = 10; + newFile.Format(KCfgDb1ConfigFilePath(), nextVersion); + err = file.Create(TheFs, newFile, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 p10((const TUint8*)KCfgDb1ConfigFileV10ValidStmt().Ptr(), KCfgDb1ConfigFileV10ValidStmt().Length()); + err = file.Write(p10); + file.Close(); + TEST2(err, KErrNone); + ++nextVersion; + newFile.Format(KCfgDb1ConfigFilePath(), nextVersion); + err = file.Create(TheFs, newFile, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 p11((const TUint8*)KCfgDb1ConfigFileV11ValidStmt().Ptr(), KCfgDb1ConfigFileV11ValidStmt().Length()); + err = file.Write(p11); + file.Close(); + TEST2(err, KErrNone); + } + +/////////////////////////////////////////////////////////////////////////////////////// +// DoDbCfgTests() functions + +TBool GuessSystemSettingsTable(const TDesC& aDbName, TInt aVersion) + { + // Note: We have to use SQLite directly to access the settings + // table as the SQL Server denies permission to access this table + // as it is in a shared, secure database + + TBool guessIsCorrect = EFalse; + + TParse parse; + parse.Set(aDbName, &KSqlSrvPrivatePath, 0); + + TBuf8 dbFileName; + dbFileName.Copy(parse.FullName()); + + TheDbHandle = NULL; + TInt rc = sqlite3_open((const char*)dbFileName.PtrZ(), &TheDbHandle); + TEST2(rc, SQLITE_OK); + + TBuf<100> queryBuf; + queryBuf.Format(KGetSettingsSql(), aVersion); + + sqlite3_stmt* stmtHandle = NULL; + const void* stmtTailZ = NULL; + rc = sqlite3_prepare16_v2(TheDbHandle, queryBuf.PtrZ(), -1, &stmtHandle, &stmtTailZ); + TEST2(rc, SQLITE_OK); + + rc = sqlite3_step(stmtHandle); + if(SQLITE_ROW == rc) + { + guessIsCorrect = ETrue; + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_DONE); + } + + sqlite3_finalize(stmtHandle); + sqlite3_close(TheDbHandle); + TheDbHandle = NULL; + + return guessIsCorrect; + } + +void CheckSystemSettingsTable(const TDesC& aDbName, TInt aVersion) + { + // Note: We have to use SQLite directly to access the settings + // table as the SQL Server denies permission to access this table + // as it is in a shared, secure database + + TParse parse; + parse.Set(aDbName, &KSqlSrvPrivatePath, 0); + + TBuf8 dbFileName; + dbFileName.Copy(parse.FullName()); + + sqlite3 *dbHandle = NULL; + TInt rc = sqlite3_open((const char*)dbFileName.PtrZ(), &dbHandle); + TEST2(rc, SQLITE_OK); + + TBuf<100> queryBuf; + queryBuf.Format(KGetSettingsSql(), aVersion); + + sqlite3_stmt* stmtHandle = NULL; + const void* stmtTailZ = NULL; + rc = sqlite3_prepare16_v2(dbHandle, queryBuf.PtrZ(), -1, &stmtHandle, &stmtTailZ); + TEST2(rc, SQLITE_OK); + + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_ROW); + + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_DONE); + + sqlite3_finalize(stmtHandle); + sqlite3_close(dbHandle); + } + +void CheckNumberRecordsL(const TDesC& aStmt) + { + // There should always be only 1 record in the table + // in each database as INSERT and DELETE statements are + // not supported in the config files + + // Prepare stmt + RSqlStatement stmt; + stmt.PrepareL(TheDb, aStmt); + + // Get each row + TUint numRows = 0; + TInt err = stmt.Next(); + while(KSqlAtRow == err) + { + numRows++; + err = stmt.Next(); + } + + if (KSqlAtEnd != err) + { + User::Leave(KErrCorrupt); + } + + if (numRows != 1) + { + User::Leave(KErrArgument); + } + + // Close stmt + stmt.Close(); + } + +TInt NumberIndicesExpectedInDb1(const TInt aExpectedStoredVersion) + { + TInt numIndices = -1; + + switch(aExpectedStoredVersion) + { + // Only files 01 - 04, 09 and 11 will be successfully processed and so stored in the settings table + case 1: + { + numIndices = 3; // 3 indices should be added to db1 based on config file 01 + break; + } + case 2: + { + numIndices = 4; // 1 more index should be added to db1 based on config file 02 + break; + } + case 3: + case 4: + case 5: + case 6: + case 7: + { + numIndices = 4; // no more indices should be added to db1 based on config file 03 - 07 + break; + } + case 8: + { + numIndices = 6; // 2 more indices should be added to db1 based on config file 08 + break; + } + case 9: + { + numIndices = 7; // 1 more index should be added to db1 based on config file 09 + break; + } + case 11: + { + numIndices = 9; // 2 more indices should be added to db1 based on config file 11 + break; + } + case 12: + { + numIndices = 10; // 1 more index should be added to db1 based on config file 12 + break; + } + case 13: + { + numIndices = 11; // 1 more index should be added to db1 based on config file 13 + break; + } + case 14: + { + numIndices = 12; // 1 more index should be added to db1 based on config file 14 + break; + } + } + + return numIndices; + } + +void CheckNumberIndicesL(const TInt aNumIndicesExpected) + { + // Prepare stmt + RSqlStatement stmt; + stmt.PrepareL(TheDb, KDbCheckNumIndices); + + // Get each entry of type 'index' in the 'sqlite_master' table + TUint numEntries = 0; + TInt err = stmt.Next(); + while(KSqlAtRow == err) + { + numEntries++; + err = stmt.Next(); + } + + if (KSqlAtEnd != err) + { + User::Leave(KErrCorrupt); + } + + if (numEntries != aNumIndicesExpected) + { + User::Leave(KErrArgument); + } + + // Close stmt + stmt.Close(); + } + +void DoCfgOpenTests(TInt aExpectedStoredVersion = 1) + { + // Open a shared, secure database - config ops should be applied on it + TheTest.Printf(_L("===CfgOpen: Open shared, secure database\r\n")); + TInt err = TheDb.Open(KCfgDb1); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); // there should still be only 1 record in the table + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(NumberIndicesExpectedInDb1(aExpectedStoredVersion))); // there should now be 3 indices in the table + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, aExpectedStoredVersion); // check that the ops in the specified config file have been applied + + // Open again the same shared, secure database - no config should occur (it has already been done) + TheTest.Printf(_L("===CfgOpen: Open shared, secure database again\r\n")); + err = TheDb.Open(KCfgDb1); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(NumberIndicesExpectedInDb1(aExpectedStoredVersion))); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, aExpectedStoredVersion); + + // Open a shared, secure database - no config should occur (there is no config file for this database) + TheTest.Printf(_L("===CfgOpen: Open shared, secure database (that has no config file)\r\n")); + err = TheDb.Open(KCfgDb2); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb2CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb2, 0); + + // Open a private, secure database - no config should occur + TheTest.Printf(_L("===CfgOpen: Open private, secure database\r\n")); + err = TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb3CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb3, 0); + + // Open a public database - no config should occur + TheTest.Printf(_L("===CfgOpen: Open public database\r\n")); + err = TheDb.Open(KCfgDb4); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb4CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb4, 0); + } + +void DoUpgradedCfgOpenTest() + { + // Upgrade the config file for KCfgDb1, i.e. replace v1 file with v2 file + UpgradeDbConfigFile(1); + DoCfgOpenTests(2); + } + +void DoBadCfgOpenTests() + { + // Test an empty config file + UpgradeDbConfigFile(2); // the current file version is 02, replace it with 03 + DoCfgOpenTests(3); // version 03 will be successfully processed and stored in the settings table + + // Test a config file with unsupported operations (these will be ignored) + UpgradeDbConfigFile(3); // the current file version is 03, replace it with 04 + DoCfgOpenTests(4); // version 04 will be successfully processed (the unsupported operations are ignored) and stored in the settings table + + // Test a config file with only whitespace in it + UpgradeDbConfigFile(4); // the current file version is 04, replace it with 05 + DoCfgOpenTests(5); // version 05 will be successfully processed (the whitespace is ignored) and stored in the settings table + + // Test a config file with operations on an invalid table + UpgradeDbConfigFile(5); // the current file version is 05, replace it with 06 + DoCfgOpenTests(6); // version 06 will be successfully processed (the failed-to-execute operation will be ignored) and stored in the settings table + + // Test a config file that contains an invalid comment style + UpgradeDbConfigFile(6); // the current file version is 06, replace it with 07 + DoCfgOpenTests(7); // version 07 will be successfully processed (the line with invalid comment syntax will be ignored) and stored in the settings table + + // Test a config file that contains a sequence of statements as one statement (this is currently unsupported) + UpgradeDbConfigFile(7); // the current file version is 07, replace it with 08 + DoCfgOpenTests(8); // version 08 will be successfully processed (the line with a sequence of statements is ignored) and stored in the settings table + + // Test a config file that contains whitespace before and after the SQL statement + UpgradeDbConfigFile(8); // the current file version is 08, replace it with 09 + DoCfgOpenTests(9); // version 09 will be successfully processed and stored in the settings table + + // Test a config file that has a lower extension number + DowngradeDbConfigFile(9); // the current file version is 09, replace it with 08 + DoCfgOpenTests(9); // version 08 will NOT be processed (as it is a lower version than that stored), and 09 will remain stored in the settings table + + // Test a config file that has an invalid extension + CreateCorruptDbConfigFile(8); // the current file version is 08, replace it with a file with an invalid extension + DoCfgOpenTests(9); // the invalid file will NOT be processed, and 09 will remain stored in the settings table + + // Test two versions of the config file (two versions should not be present at the same time) + CreateTwoVersionsOfConfigFile(); // the current file has an invalid extension, delete it and create version 10 and version 11 + DoCfgOpenTests(11); // only version 11 will be processed (as it is the highest version), and 11 will be stored in the settings table + + // Test a config file that contains an invalid statement and a valid statement + UpgradeDbConfigFile(11); // the current file versions are 10 and 11, replace them with 12 + DoCfgOpenTests(12); // version 12 will be successfully processed (the invalid statement will be ignored and the valid statement executed) and stored in the settings table + + // Test a config file that contains a SQL style comment + UpgradeDbConfigFile(12); // the current file version is 12, replace it with 13 + DoCfgOpenTests(13); // version 13 will be successfully processed (the SQL style comment will be ignored) and stored in the settings table + + // Test a config file that contains a 'C' style comment + UpgradeDbConfigFile(13); // the current file version is 13, replace it with 14 + DoCfgOpenTests(14); // version 14 will be successfully processed (the 'C' style comment will be ignored) and stored in the settings table + } + +void DoNewDbCfgOpenTest() + { + // Create Db5 - a config file already exists for this database + CreateDb5(); + + // Open the shared, secure database Db5 - config ops should be applied on it + TheTest.Printf(_L("===NewDbCfg: Open shared, secure database\r\n")); + TInt err = TheDb.Open(KCfgDb5); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb5CheckNumRecords)); // there should still be only 1 record in the table + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(1)); // there should now be 1 index in the table + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb5, 1); // check that the ops in the specified config file have been applied + + // Open again the same shared, secure database - no config should occur (it has already been done) + TheTest.Printf(_L("===NewDbCfg: Open shared, secure database again\r\n")); + err = TheDb.Open(KCfgDb5); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb5CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(1)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb5, 1); + } + +void DoCfgAttachTests(TInt aExpectedStoredVersion = 0) + { + // Open a private, secure database - no config should occur + TheTest.Printf(_L("===CfgAttach: Open private, secure database\r\n")); + TInt err = TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + // Attach a shared, secure database - the db1 config file should not be processed + TheTest.Printf(_L("===CfgAttach: Attach shared, secure database\r\n")); + err = TheDb.Attach(KCfgDb1, KAttachDb1); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, aExpectedStoredVersion); // check that the config file has not been processed for db1 + CheckSystemSettingsTable(KCfgDb3, 0); + + // Open a public database - no config should occur + TheTest.Printf(_L("===CfgAttach: Open public database\r\n")); + err = TheDb.Open(KCfgDb4); + TEST2(err, KErrNone); + // Attach a shared, secure database - no config should occur (there is no config file for this database) + TheTest.Printf(_L("===CfgAttach: Attach shared, secure database (that has no config file)\r\n")); + err = TheDb.Attach(KCfgDb2, KAttachDb2); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb2, 0); + CheckSystemSettingsTable(KCfgDb4, 0); + } + +void DoUpgradedCfgAttachTest() + { + // Upgrade the config file for KCfgDb1, i.e. replace v1 file with v2 file + UpgradeDbConfigFile(1); + DoCfgAttachTests(2); + } + +void DoBadCfgAttachTests() + { + // Test an empty config file + UpgradeDbConfigFile(2); // the current file version is 02, replace it with 03 + DoCfgAttachTests(3); // version 03 will not be processed so no update to settings table + + // Test a config file with unsupported operations + UpgradeDbConfigFile(3); // the current file version is 03, replace it with 04 + DoCfgAttachTests(4); // version 04 will not be processed so no update to settings table + + // Test a config file with only whitespace in it + UpgradeDbConfigFile(4); // the current file version is 04, replace it with 05 + DoCfgAttachTests(5); // version 05 will not be processed so no update to settings table + + // Test a config file with operations on an invalid table + UpgradeDbConfigFile(5); // the current file version is 05, replace it with 06 + DoCfgAttachTests(6); // version 06 will not be processed so no update to settings table + + // Test a config file that contains an invalid comment style + UpgradeDbConfigFile(6); // the current file version is 06, replace it with 07 + DoCfgAttachTests(7); // version 07 will not be processed so no update to settings table + + // Test a config file that contains a sequence of statements as one statement (this is currently unsupported) + UpgradeDbConfigFile(7); // the current file version is 07, replace it with 08 + DoCfgAttachTests(8); // version 08 will not be processed so no update to settings table + + // Test a config file that contains whitespace before and after the SQL statement + UpgradeDbConfigFile(8); // the current file version is 08, replace it with 09 + DoCfgAttachTests(9); // version 09 will not be processed so no update to settings table + + // Test a config file that has a lower extension number + DowngradeDbConfigFile(9); // the current file version is 09, replace it with 08 + DoCfgAttachTests(9); // version 08 will not be processed so no update to settings table + + // Test a config file that has an invalid extension + CreateCorruptDbConfigFile(8); // the current file version is 08, replace it with a file with an invalid extension + DoCfgAttachTests(9); // the invalid file will not be processed so no update to settings table + + // Test two versions of the config file (two versions should not be present at the same time) + CreateTwoVersionsOfConfigFile(); // the current file has an invalid extension, delete it and create version 10 and version 11 + DoCfgAttachTests(11); // neither version 10 or 11 will be processed so no update to settings table + + // Test a config file that contains an invalid statement and a valid statement + UpgradeDbConfigFile(11); // the current file versions are 10 and 11, replace them with 12 + DoCfgAttachTests(12); // version 12 will not be processed so no update to settings table + + // Test a config file that contains a SQL style comment + UpgradeDbConfigFile(12); // the current file version is 12, replace it with 13 + DoCfgAttachTests(13); // version 13 will not be processed so no update to settings table + + // Test a config file that contains a 'C' style comment + UpgradeDbConfigFile(13); // the current file version is 13, replace it with 14 + DoCfgAttachTests(14); // version 14 will not be processed so no update to settings table + } + +void DoNewDbCfgAttachTest() + { + // Create Db5 - a config file already exists for this + CreateDb5(); + + // Open a private, secure database - no config should occur + TheTest.Printf(_L("===NewDbCfgAttach: Open private, secure database\r\n")); + TInt err = TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb3CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + + // Attach a shared, secure database - the db5 config file should not be processed + TheTest.Printf(_L("===NewDbCfgAttach: Attach shared, secure database\r\n")); + err = TheDb.Attach(KCfgDb5, KAttachDb5); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb5CheckNumRecords)); // there should still be only 1 record in the table + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); // there should still be no indices in the table + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb5, 1); // check that the config file has been processed for db1 + CheckSystemSettingsTable(KCfgDb3, 0); + } + +void ResetStoredCollationDll() + { + // Note: We have to use SQLite directly to access the settings + // table as the SQL Server denies permission to access this table + // as it is in a shared, secure database + + TParse parse; + parse.Set(KCfgDb1, &KSqlSrvPrivatePath, 0); + + TBuf8 dbFileName; + dbFileName.Copy(parse.FullName()); + + sqlite3 *dbHandle = NULL; + TInt rc = sqlite3_open((const char*)dbFileName.PtrZ(), &dbHandle); + TEST2(rc, SQLITE_OK); + + TBuf<100> queryBuf; + queryBuf.Append(KResetCollationDllSql()); + + sqlite3_stmt* stmtHandle = NULL; + const void* stmtTailZ = NULL; + rc = sqlite3_prepare16_v2(dbHandle, queryBuf.PtrZ(), -1, &stmtHandle, &stmtTailZ); + TEST2(rc, SQLITE_OK); + + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_DONE); + + sqlite3_finalize(stmtHandle); + sqlite3_close(dbHandle); + } + +void CheckCollationDllUpdated() + { + // Note: We have to use SQLite directly to access the settings + // table as the SQL Server denies permission to access this table + // as it is in a shared, secure database + + TParse parse; + parse.Set(KCfgDb1, &KSqlSrvPrivatePath, 0); + + TBuf8 dbFileName; + dbFileName.Copy(parse.FullName()); + + sqlite3 *dbHandle = NULL; + TInt rc = sqlite3_open((const char*)dbFileName.PtrZ(), &dbHandle); + TEST2(rc, SQLITE_OK); + + TBuf<100> queryBuf; + queryBuf.Append(KGetCollationDllSql()); + + sqlite3_stmt* stmtHandle = NULL; + const void* stmtTailZ = NULL; + rc = sqlite3_prepare16_v2(dbHandle, queryBuf.PtrZ(), -1, &stmtHandle, &stmtTailZ); + TEST2(rc, SQLITE_OK); + + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_ROW); + + const TUint16* collationDllName = (const TUint16*)sqlite3_column_text16(stmtHandle, 0); + TInt collationDllNameLen = sqlite3_column_bytes(stmtHandle, 0) / sizeof(TUint16); + TPtrC ptr(collationDllName, collationDllNameLen); + + rc = sqlite3_step(stmtHandle); + TEST2(rc, SQLITE_DONE); + + sqlite3_finalize(stmtHandle); + sqlite3_close(dbHandle); + + _LIT(KTestCollationDllName, "hjagafsff");//The same as the used in KResetCollationDllSql statement + TEST(ptr != KTestCollationDllName); + } + +/** +@SYMTestCaseID SYSLIB-SQL-UT-4010 +@SYMTestCaseDesc New database configuration files feature - unit test. + The test opens several test databases, and the database configuration + file for one of the shared, secure databases is processed. This is + repeated for a variety of versions of the database configuration file, + both valid and invalid. The same tests are repeated for when attaching, + rather than opening, the test databases. The test also verifies that + reindexing occurs if necessary when a database is opened or attached, + regardless of whether or not database configuration is also required. + Finally, the test also verifies that a config file that exists before + the database itself is created will be processed when the database is + opened for the first time. +@SYMTestPriority High +@SYMTestActions New database configuration files feature - unit test. +@SYMTestExpectedResults The test must not fail +@SYMCR LMAN-79SJ7L +*/ +void DoDbCfgTests() + { + TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4010 DoDbCfgTests ")); + + // Do 'open' tests for new db config file feature + DoCfgOpenTests(); // open the test databases + DoUpgradedCfgOpenTest(); // upgrade the config file for db1 and reopen the test databases + DoBadCfgOpenTests(); // corrupt the config file for db1 (in a variety of ways) and reopen the test databases + DoNewDbCfgOpenTest(); // create a db for which a config file already exists and then open the db + + // Recreate the original dbs and config files + DeleteCfgFilesAndDbs(); + CreateCfgFilesAndDbs(); + + // Do 'attach' tests for new db config file feature + DoCfgAttachTests(1); // attach the test databases + DoUpgradedCfgAttachTest(); // upgrade the config file for db1 and reattach the test databases + DoBadCfgAttachTests(); // corrupt the config file for db1 (in a variety of ways) and reattach the test databases + DoNewDbCfgAttachTest(); // create a db for which a config file already exists and then attach the db + + // Recreate the original dbs and config files + DeleteCfgFilesAndDbs(); + CreateCfgFilesAndDbs(); + + // Do the test that causes both reindexing and db configuration to occur when db1 is opened + ResetStoredCollationDll(); + DoCfgOpenTests(); + CheckCollationDllUpdated(); + + // Recreate the original dbs and config files + DeleteCfgFilesAndDbs(); + CreateCfgFilesAndDbs(); + + // Do the test that causes reindexing to occur when db1 is attached (no db configuration occurs for attach) + ResetStoredCollationDll(); + DoCfgAttachTests(1); + CheckCollationDllUpdated(); // db1's stored collation dll name should now match db3's (to which Db1 is attached) stored collation name + } + +/** +@SYMTestCaseID SYSLIB-SQL-UT-4013 +@SYMTestCaseDesc New database configuration files feature - OOM test. + The test opens a shared secure database for which there + exists a database configuration file that is to be processed. + The open call is executed within an OOM loop, which causes + an OOM error to occur at different stages of the open call. + The test verifies that OOM errors are handled correctly and + that on the final loop - when no OOM errors occur - the database is + successfully opened and the database configuration file is processed. +@SYMTestPriority High +@SYMTestActions New database configuration files feature - OOM test. +@SYMTestExpectedResults The test must not fail +@SYMCR LMAN-79SJ7L +*/ +void DoDbCfgOOMTests() + { + TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4013 DoDbCfgOOMTests ")); + + // Recreate the original dbs and config files + DeleteCfgFilesAndDbs(); + CreateCfgFilesAndDbs(); + + // The server is stopped at the end of CreateCfgFilesAndDbs(). + // Before the test loop below begins we attach Db1 so that the + // server starts up again and stores Db1's security policy in + // CSqlServer::iSecurityMap. Otherwise there will be unmatching + // allocated cells in the test below. Do not call Detach() before + // Close(). We 'attach' Db1 here rather than 'open' it because + // open is the API that we want to test in the while loop + TInt err = TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + err = TheDb.Attach(KCfgDb1, KAttachDb1); + TEST2(err, KErrNone); + TheDb.Close(); + + err = KErrNoMemory; + TInt failingAllocationNo = 0; + while(err == KErrNoMemory) + { + MarkHandles(); + MarkAllocatedCells(); + + __UHEAP_MARK; + + TheTest.Printf(_L("%d\r"), failingAllocationNo + 1); + + // Set failure rate + TSqlResourceTester::SetDbHeapFailure(RHeap::EDeterministic, ++failingAllocationNo); + err = TheDb.Open(KCfgDb1); + if(err != KErrNoMemory) + { + TEST2(err, KErrNone); + TheDb.Close(); + if(GuessSystemSettingsTable(KCfgDb1, 0)) + { + err = KErrNoMemory; + } + } + + // Reset failure rate + TSqlResourceTester::SetDbHeapFailure(RHeap::ENone, 0); + + TheDb.Close(); + + __UHEAP_MARKEND; + + CheckAllocatedCells(); + CheckHandles(); + } + TEST2(err, KErrNone); + TheTest.Printf(_L("\r\n")); + CheckSystemSettingsTable(KCfgDb1, 1); // check that the settings table has been updated with v01 of the config file + } + +/** +@SYMTestCaseID SYSLIB-SQL-UT-4014 +@SYMTestCaseDesc New database configuration files feature - File I/O failures test. + The test opens a shared secure database for which there + exists a database configuration file that is to be processed. + The open call is executed within a file I/O failure loop, which + causes a selection of file I/O errors to occur at different stages + of the open call. + The test verifies that file I/O errors are handled correctly and + that on the final loop - when no file I/O errors occur - the database + is successfully opened and the database configuration file is processed. + NOTE: This test also acts as the test case for defect DEF116688. +@SYMTestPriority High +@SYMTestActions New database configuration files feature - File I/O failures test. +@SYMTestExpectedResults The test must not fail +@SYMCR LMAN-79SJ7L +*/ +void DoDbCfgFileIOFailuresTests() + { + TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4014 DoDbCfgFileIOFailuresTests ")); + + // Recreate the original dbs and config files + DeleteCfgFilesAndDbs(); + CreateCfgFilesAndDbs(); + + // The server is stopped at the end of CreateCfgFilesAndDbs(). + // Before the test loop below begins we start the server again + // by opening Db3) so that any server start up file i/o ops will + // succeed. We don't open Db1 here because opening Db1 is what + // we want to test in the while loop + TInt err = TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + TheDb.Close(); + + TBool isFinished = EFalse; + err = KErrNotFound; + TInt iter = 0; + TheTest.Printf(_L("Iteration\r\n")); + for(TInt cnt = 1; !isFinished; ++cnt) + { + for(TInt fsError = KErrNotFound; fsError >= KErrUnderflow; --fsError) // errors -1 to -10 will be generated + { + TheTest.Printf(_L("%d/%d \r"), ++iter, fsError); + (void)TheFs.SetErrorCondition(fsError, cnt); // set error code and how soon it is to occur + err = TheDb.Open(KCfgDb1); + (void)TheFs.SetErrorCondition(KErrNone); + TheDb.Close(); + if(err != KErrNone) + { + // An error has occured. We know that this means that the database + // configuration part of the open call wasn't reached (because the + // database configuration part returns KErrNone even if it fails). + // But check anyway that the database content is still the same as + // before, i.e. the settings table has not been updated + CheckSystemSettingsTable(KCfgDb1, 0); + } + else + { + // Either the database configuration file for Db1 has been successfully + // processed or it failed part-way through being processed (KErrNone is + // returned in this case too). + // If it was the former then the the settings table will have + // been updated to store v01 of the config file + if(GuessSystemSettingsTable(KCfgDb1, 1)) + { + isFinished = ETrue; + break; + } + } + } + } + TheTest.Printf(_L("\r\n")); + TEST2(err, KErrNone); + // Check that the database configuration was allowed to be successfully applied in one of the loops + CheckSystemSettingsTable(KCfgDb1, 1); // check that the settings table has been updated with v01 of the config file + } + +/** +@SYMTestCaseID SYSLIB-SQL-UT-4015 +@SYMTestCaseDesc New database configuration files feature - Performance test. + The test measures: + - the startup time of the SQL Server when 0 and 4 database + configuration files exist + - the time taken to open a shared, secure database when a + database configuration file does and does not exist for it + - the time taken to open a shared, secure database when a + database configuration file exists for it but it has already + been processed + - the time taken to attach a shared, secure database when a + database configuration file does and does not exist for it +@SYMTestPriority High +@SYMTestActions New database configuration files feature - Performance test. +@SYMTestExpectedResults The test must not fail +@SYMCR LMAN-79SJ7L +*/ +void DoDbCfgPerfTests() + { + TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4015 DoDbCfgPerfTests ")); + + // Recreate the original dbs + DeleteCfgFilesAndDbs(); + CreateCfgDbs(); + (void)KillProcess(KSqlSrvName); // stop the server + + // Measure the start up time of the server when + // there are no database configuration files to be cached. + // Open Db4 (public db): + // No reindexing required + // Database configuration is not considered + TUint32 start = User::FastCounter(); + TInt err = TheDb.Open(KCfgDb4); + TUint32 end = User::FastCounter(); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb4CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb4, 0); + TInt ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Server startup (via Open call) - no database config files to cache: %d ms\r\n"), ms); + + // Measure the time to open Db1 when no database + // configuration file exists for it. + // Open Db1 (shared, secure db): + // No reindexing required + // Database configuration is considered but no config file is found + start = User::FastCounter(); + err = TheDb.Open(KCfgDb1); + end = User::FastCounter(); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, 0); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Open shared, secure Db1 - no config file is found: %d ms\r\n"), ms); + + // Measure the time to attach Db1 (database configuration will not be considered). + // Attach Db1 (shared, secure db): + // No reindexing required + // Database configuration is not considered + TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + start = User::FastCounter(); + err = TheDb.Attach(KCfgDb1, KAttachDb1); + end = User::FastCounter(); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb3, 0); + CheckSystemSettingsTable(KCfgDb1, 0); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Attach shared, secure Db1 - database config is not considered: %d ms\r\n"), ms); + + // Create the 4 version 01 config files now + CreateCfgFiles(); + (void)KillProcess(KSqlSrvName); // stop the server so that the files are found when it is restarted + + // Measure the start up time of the server when + // there are 4 database configuration files to be cached. + // Open Db4 (public db): + // No reindexing required + // Database configuration is not considered + start = User::FastCounter(); + err = TheDb.Open(KCfgDb4); + end = User::FastCounter(); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb4CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(0)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb4, 0); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Server startup (via Open call) - 4 database config files to cache: %d ms\r\n"), ms); + + // Measure the time to open Db1 when a database + // configuration file exists for it. + // Open Db1 (shared, secure db): + // No reindexing required + // Database configuration is considered, a file is found and config is applied (3 indices are created) + start = User::FastCounter(); + err = TheDb.Open(KCfgDb1); + end = User::FastCounter(); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(3)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, 1); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Open shared, secure Db1 - config file is found and applied: %d ms\r\n"), ms); + + // Measure the time to open Db1 when a database + // configuration file exists for it but is has already been processed. + // Open Db1 (shared, secure db): + // No reindexing required + // Database configuration is considered, a file is found but it has already been processed + start = User::FastCounter(); + err = TheDb.Open(KCfgDb1); + end = User::FastCounter(); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(3)); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb1, 1); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Open shared, secure Db1 - config file is found but already processed: %d ms\r\n"), ms); + + // Measure the time to attach Db1 (database configuration will not be considered). + // Attach Db1 (shared, secure db): + // No reindexing required + // Database configuration is not considered + TheDb.Open(KCfgDb3); + TEST2(err, KErrNone); + start = User::FastCounter(); + err = TheDb.Attach(KCfgDb1, KAttachDb1); + end = User::FastCounter(); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KCfgDb3, 0); + CheckSystemSettingsTable(KCfgDb1, 1); + ms = CalcTimeMs(start, end); + TheTest.Printf(_L("Execution time: Attach shared, secure Db1 - database config is not considered: %d ms\r\n"), ms); + } + +void TestStatementsL() + { + _LIT(KDbName, "attachDb"); + _LIT(KCreateIndex, "CREATE INDEX idx ON tbl(ColA)"); + _LIT(KCreateIndexIfNotExists, "CREATE INDEX IF NOT EXISTS idx ON tbl(ColA)"); + _LIT(KCreateUniqueIndex, "CREATE UNIQUE INDEX idx ON tbl(ColA)"); + _LIT(KNonSupported, "CREATE idx ON tbl(ColA)"); + TBuf<200> buf; + + // supported statements + + TBool rc = IsStatementSupported(KCreateIndex, KDbName, buf); + TEST(rc); + + rc = IsStatementSupported(KCreateIndexIfNotExists, KDbName, buf); + TEST(rc); + + // unsupported statements + rc = IsStatementSupported(KCreateUniqueIndex, KDbName, buf); + TEST(!rc); + + rc = IsStatementSupported(KNonSupported, KDbName, buf); + TEST(!rc); + } + +/** +@SYMTestCaseID SYSLIB-SQL-UT-4030 +@SYMTestCaseDesc Test IsStatementSupportedLC function. + This function checks either SQL statement from DB configuration file is supported or not. + In the case of supported statement, function allocates buffer and copies statement into that buffer. + Call IsStatementSupportedLC on several supported and unsupported SQL statements. +@SYMTestPriority High +@SYMTestActions Test IsStatementSupportedLC function. +@SYMTestExpectedResults The test must not fail +@SYMDEF DEF118058 +*/ +void DoIsStatementSupportedTests() + { + TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4030 DoIsStatementSupportedTests ")); + TInt err = KErrNone; + TRAP(err, TestStatementsL()); + TEST2(err, KErrNone); + } + +void DoLongDbNameTest() + { + TheTest.Next(_L("'Long database name' tests")); + //Create the database + RSqlSecurityPolicy securityPolicy; + TInt err = DoCreateSecurityPolicy(securityPolicy); + TEST2(err, KErrNone); + err = TheDb.Create(KLongDbName1, securityPolicy); + TEST2(err, KErrNone); + err = TheDb.Exec(_L("CREATE TABLE table1(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + TEST(err >= 0); + err = TheDb.Exec(_L("INSERT INTO table1 (i1,i2,i3) values(1,2,3)")); + TEST(err == 1); + TheDb.Close(); + //Kill the server (to reload config file names at the server startup) + (void)KillProcess(KSqlSrvName); + /////////////////////////////////////////////////////////////////////// + TheTest.Printf(_L("Open a database with a long name\r\n")); + //Create cfg file + RFile file; + TFileName fileName; + err = file.Create(TheFs, KLongCfgName1, EFileRead | EFileWrite); + TEST2(err, KErrNone); + TPtrC8 pDb1((const TUint8*)KCfgConfigFileValidStmt().Ptr(), KCfgConfigFileValidStmt().Length()); + err = file.Write(pDb1); + file.Close(); + TEST2(err, KErrNone); + //Open the database + err = TheDb.Open(KLongDbName1); + TEST2(err, KErrNone); + TRAP(err, CheckNumberRecordsL(KDb1CheckNumRecords)); // there should still be only 1 record in the table + TEST2(err, KErrNone); + TRAP(err, CheckNumberIndicesL(1)); // there should now be 1 index in the table + TEST2(err, KErrNone); + TheDb.Close(); + const TInt KVersion = 1; + CheckSystemSettingsTable(KLongDbName1, KVersion); // check that the ops in the specified config file have been applied + /////////////////////////////////////////////////////////////////////// + TheTest.Printf(_L("Attach a database with a long logical name\r\n")); + //Attached database test + //Recreate the database + (void)RSqlDatabase::Delete(KLongDbName1); + err = TheDb.Create(KLongDbName1, securityPolicy); + TEST2(err, KErrNone); + err = TheDb.Exec(_L("CREATE TABLE table1(i1 INTEGER, i2 INTEGER, i3 INTEGER)")); + TEST(err >= 0); + err = TheDb.Exec(_L("INSERT INTO table1 (i1,i2,i3) values(1,2,3)")); + TEST(err == 1); + TheDb.Close(); + //Kill the server (to reload config file names at the server startup) + (void)KillProcess(KSqlSrvName); + //Open the main database + err = TheDb.Open(KCfgDb1); + securityPolicy.Close(); + TEST2(err, KErrNone); + //Attach a database with a very long logical name + TFileName attachDbName; + attachDbName.SetLength(KMaxFileName); + attachDbName.Fill(TChar('A')); + err = TheDb.Attach(KLongDbName1, attachDbName); + TEST2(err, KErrNone); + err = TheDb.Detach(attachDbName); + TEST2(err, KErrNone); + TheDb.Close(); + CheckSystemSettingsTable(KLongDbName1, KVersion); // check that the ops in the specified config file have been applied + //Cleanup + (void)RSqlDatabase::Delete(KLongDbName1); + } + +#endif //SYSLIBS_TEST + +TInt E32Main() + { + TheTest.Title(); + + CTrapCleanup* tc = CTrapCleanup::New(); + TheTest(tc != NULL); + + __UHEAP_MARK; + +#ifdef SYSLIBS_TEST + TheTest.Start(_L("t_sqldbconfigfile tests")); + + // Set up the test environment + SetupTestEnv(); + //Init sqlite library + sqlite3SymbianLibInit(); + + // Do tests for database config files + DoDbCfgTests(); + + // Do OOM tests for database config files + DoDbCfgOOMTests(); + + // Do file I/O failure tests for database config files + DoDbCfgFileIOFailuresTests(); + + // Do performance tests for database config files + DoDbCfgPerfTests(); + + // Test IsStatementSupportedLC function + DoIsStatementSupportedTests(); + + //Test with a very long database file (and config file) name + DoLongDbNameTest(); + + // Destroy the test environment + DestroyTestEnv(); + sqlite3SymbianLibFinalize(); + CloseSTDLIB(); + + TheTest.End(); +#else + TheTest.Start(_L("This test works only if the whole SQL component is built with SYSLIBS_TEST macro defined!")); + TheTest.End(); +#endif + + __UHEAP_MARKEND; + + TheTest.Close(); + + delete tc; + + User::Heap().Check(); + return KErrNone; + }