diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/SRC/Server/SqlSrvDatabase.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sql/SRC/Server/SqlSrvDatabase.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,1626 @@ +// Copyright (c) 2005-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 "SqlSrvFileData.h" //TSqlSrvFileData +#include "SqlSrvMain.h" //CSqlServer +#include "SqlSrvAuthorizer.h" //MSqlPolicyInspector +#include "SqlSrvDatabase.h" +#include "SqlSrvStatement.h" +#include "SqlUtil.h" //Panic codes, Sql2OsErrCode() +#include "SqlSrvUtil.h" //Global server functions +#include "SqlCompact.h" +#include "SqlSrvResourceProfiler.h" + +// +// The following macro disables the creation/loading of the settings table. +// It is for internal testing purposes only! +// +// __SQL_DISABLE_SYMBIAN_SETTINGS_TABLE__ +// +// This means that the database index will always be rebuilt when loaded by +// by the server regardless of the current system collation/locale in use. +// The benefit of enabling this macro is that a client can send PRAGMA +// commands to the SQL server before any tables have been explicity created in +// a NON-SECURE database (secure databases still automatically get a security +// policy table). +// +// The macro is applied inside: +// CSqlSrvDatabase::StoreSettingsL +// CSqlSrvDatabase::ProcessSettingsL +// +// We should inform the user at compile-time if this macro has been enabled: +#if defined(__SQL_DISABLE_SYMBIAN_SETTINGS_TABLE__) +#pragma message(">>> WARNING: Use of SYMBIAN_SETTINGS table has been disabled <<<") +#endif + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Local const data /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +const char* KErrMsg1 = "Missing ESCAPE expression"; +const char* KErrMsg2 = "ESCAPE expression must be a single character"; + +//////////////////////////////////////////////////////// +//Attach/detach SQL statements (zero-terminated strings) +_LIT(KAttachDb, "ATTACH DATABASE :FileName AS :DbName\x0"); +_LIT(KDetachDb, "DETACH DATABASE :DbName\x0"); +//////////////////////////////////////////////////////// +// Pragma SQL statements. The database names in all statements are quoted to avoid the "sql injection" threat. +// (At the moment there is no way to pass an invalid database name for these statements, because the database has to be attached +// first and a parameter binding is used there. So, the quoting is just for safety and against changes in the future) +_LIT(KCacheSizePragma, "PRAGMA \"%S\".cache_size=%d\x0"); +_LIT(KPageSizePragma, "PRAGMA \"%S\".page_size=%d\x0"); +_LIT(KAutoVacuumPragma, "PRAGMA \"%S\".auto_vacuum=%d\x0"); +//_LIT(KPersist, "persist"); +//_LIT(KPersistentJournalPragma, "PRAGMA \"%S\".journal_mode=%S\x0"); +//////////////////////////////////////////////////////// +//"LIKE" - user defined function name +_LIT8(KStrLikeFuncName, "LIKE\x0"); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Local functions /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//Local function, used for comparing TSqlAttachDbPair objects. +//(TSqlAttachDbPair structure represents type of the objects, members +// of the map used for keeping the information regarding attached databases) +// +//Note that iKey members of aLeft and aRight function parameters are expected to be +//UTF8 encoded, zero-terminated strings. +// +//The function will panic with panic code 7 in _DEBUG mode if iKey member of aLeft or +//aRight argument is NULL. +static TInt Compare(const TSqlAttachDbPair& aLeft, const TSqlAttachDbPair& aRight) + { + __SQLASSERT(aLeft.iKey != NULL && aRight.iKey != NULL, ESqlPanicInternalError); + return ::CompareNoCase8(TPtrC8(aLeft.iKey), TPtrC8(aRight.iKey)); + } + +//Local function, used for comparing TSqlCompactDbPair objects. +//(TSqlCompactDbPair structure represents type of the objects, members +// of the map used for keeping the information regarding compacted databases) +// +//Note that iKey members of aLeft and aRight function parameters are expected to be +//UTF16 encoded strings. +// +//The function will panic with panic code 7 in _DEBUG mode if iKey member of aLeft or +//aRight argument is NULL. +static TInt Compare2(const TSqlCompactDbPair& aLeft, const TSqlCompactDbPair& aRight) + { + __SQLASSERT(aLeft.iKey != NULL && aRight.iKey != NULL, ESqlPanicInternalError); + return ::CompareNoCase(*aLeft.iKey, *aRight.iKey); + } + +//Creates/opens database file (database file name in aFileData parameter) and initializes aDbHandle parameter. +//The database will be created either with UTF-16 or UTF-8 encoding, depending on the +//TSqlSrvFileData::IsUTF16() property. +static void CreateDbHandleL(const TSqlSrvFileData& aFileData, sqlite3*& aDbHandle) + { + if(aFileData.ConfigParams().iDbEncoding == TSqlSrvConfigParams::EEncUtf8) + { + TBuf8 fileNameZ; + if(!::UTF16ZToUTF8Z(aFileData.FileNameZ(), fileNameZ)) + { + __SQLLEAVE(KErrGeneral); + } + __SQLLEAVE_IF_ERROR(::CreateDbHandle8(fileNameZ, aDbHandle)); + } + else + { + __SQLLEAVE_IF_ERROR(::CreateDbHandle16(aFileData.FileNameZ(), aDbHandle)); + } + } + +//LoadAttachedDbSecurityPolicyLC() creates a new CSqlSecurityPolicy object and initializes it with the +//security policies read from the attached database file, which name is in aFileData parameter. +//The created database security policy object is placed in the cleanup stack. +//The caller is responsible for the destruction of the created and returned security policy object. +//This function is used to read the security policies of attached databases. +static CSqlSecurityPolicy* LoadAttachedDbSecurityPolicyLC(const TSqlSrvFileData& aFileData) + { + //Create new database security policy object and initialize it with a default security policy + TSecurityPolicy defaultPolicy(TSecurityPolicy::EAlwaysFail); + CSqlSecurityPolicy* dbPolicy = CSqlSecurityPolicy::NewLC(defaultPolicy); + //This is an attached database and has to be opened + sqlite3* dbHandle = NULL; + CreateDbHandleL(aFileData, dbHandle); + CleanupStack::PushL(TCleanupItem(&CloseDbCleanup, dbHandle)); + //Read the security policies. + TSqlDbSysSettings dbSysSettings(dbHandle); + dbSysSettings.LoadSecurityPolicyL(*dbPolicy); + CleanupStack::PopAndDestroy();//TCleanupItem(&CloseDbCleanup, dbHandle) + return dbPolicy; + } + +//LoadDbSecurityPolicyLC() creates a new CSqlSecurityPolicy object and initializes it with the +//security policies read from the database file. +//The created database security policy object is placed in the cleanup stack. +//The caller is responsible for destroying the returned database security policy object. +//The function is used to read the security policy of the main database. +static CSqlSecurityPolicy* LoadDbSecurityPolicyLC(sqlite3* aDbHandle) + { + __SQLASSERT(aDbHandle != NULL, ESqlPanicBadArgument); + //Create new database security policy object and initialize it with a default security policy + TSecurityPolicy defaultPolicy(TSecurityPolicy::EAlwaysFail); + CSqlSecurityPolicy* dbPolicy = CSqlSecurityPolicy::NewLC(defaultPolicy); + //Read the security policies. + TSqlDbSysSettings dbSysSettings(aDbHandle); + dbSysSettings.LoadSecurityPolicyL(*dbPolicy); + return dbPolicy; + } + +//CreateStrCopyLC() makes a copy of aSrc string and places it in the cleanup stack. +//aSrc is expected to be UTF8 encoded, zero terminated string. +//The function panics in _DEBUG mode if aSrc is NULL. +static TUint8* CreateStrCopyLC(const TUint8* aSrc) + { + __SQLASSERT(aSrc != NULL, ESqlPanicBadArgument); + TInt len = User::StringLength(aSrc) + 1; + TUint8* copy = new (ELeave) TUint8[len]; + Mem::Copy(copy, aSrc, len); + CleanupStack::PushL(copy); + return copy; + } + +//EnableAuthorizer() function is used to reenable the authorizer callback +//during the stack cleanup. +static void EnableAuthorizer(void* aAuthorizerDisabled) + { + __SQLASSERT(aAuthorizerDisabled != NULL, ESqlPanicBadArgument); + TBool* authorizerDisabled = static_cast (aAuthorizerDisabled); + *authorizerDisabled = EFalse; + } + +//Used by DbFileCleanup() +NONSHARABLE_STRUCT(TDbFileCleanup) + { + TDbFileCleanup(const TSqlSrvFileData& aSqlSrvFileData, sqlite3*& aDbHandle) : + iSqlSrvFileData(aSqlSrvFileData), + iDbHandle(aDbHandle) + { + //aDbHandle can be NULL (it is a reference to a pointer and the pointer can be initialized later) + } + void Cleanup() + { + ::CloseDbHandle(iDbHandle);//This operation also deletes the journal file + iDbHandle = NULL; + (void)iSqlSrvFileData.Fs().Delete(iSqlSrvFileData.FileName()); + } +private: + const TSqlSrvFileData& iSqlSrvFileData; + sqlite3*& iDbHandle; + }; + +//DbFileCleanup() is used to close and delete the database file during the stack cleanup, if +//CSqlSrvDatabase's ConstructL() method(s) leave (when creating a new database file). +static void DbFileCleanup(void* aDbFileCleanup) + { + __SQLASSERT(aDbFileCleanup != NULL, ESqlPanicBadArgument); + TDbFileCleanup* dbFileCleanup = static_cast (aDbFileCleanup); + dbFileCleanup->Cleanup(); + } + +//Executes "PRAGMA" SQL statement + INTEGER value. +//Pragma parameters: +// aValue - integer pragma value to be set; +// aPragma - pragma statement, the format is: ""PRAGMA .=%d\x0"" +// aDbName - "main" or the attached database name +//This function is used for setting "cache_size", "page_size", "auto_vacuum" pragmas. +//During the call the authorizer will be disabled. +static TInt ExecPragma(sqlite3* aDbHandle, TBool& aAuthorizerDisabled, const TDesC& aPragma, TInt aValue, const TDesC& aDbName = KMainDb16) + { + __SQLASSERT(aDbHandle != NULL, ESqlPanicBadArgument); + TBuf pragmaSql;//(KMaxFileName + 64) characters buffer length is enough for the longest possible PRAGMA statement + pragmaSql.Format(aPragma, &aDbName, aValue); + TBool authorizerDisabledState = aAuthorizerDisabled; + aAuthorizerDisabled = ETrue; + TInt err = DbExecStmt16(aDbHandle, pragmaSql); + aAuthorizerDisabled = authorizerDisabledState; + return err; + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// CSqlSrvDatabase class ///////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + + + +///////////////////////////// Object creation methods ///////////////////////////////////////////// + +/** +Creates new CSqlSrvDatabase instance which creates and manages new secure SQL database. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + The format of the name must be: + \:\<[SID]database file name excluding the path\>. + "[SID]" refers to SID of the application which creates the database. +@param aSecurityPolicy The database security policies container. + CSqlSrvDatabase instance stores the pointer in the security policy map, + if it does not exist there already. The security policies map is owned by the CSqlServer class. + +@return A pointer to the created CSqlSrvDatabase instance. + +@see CSqlServer +@see TSqlSrvFileData +@see MSqlPolicyInspector +@see RSqlSecurityMap +@see CSqlSecurityPolicy + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrArgument, if a system table name found in the security policies (aSecurityPolicy); + KErrAlreadyExists, the file already exists; + KErrNotReady, the drive does not exist or is not ready; + KErrInUse, the file is already open; + KErrPermissionDenied, the client application does not satisfy the relevant database security policies. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@panic SqlDb 4 In _DEBUG mode if aFileData does not refer to a secure database file name. +@panic SqlDb 4 In _DEBUG mode if aSecurityPolicy is NULL. +*/ +CSqlSrvDatabase* CSqlSrvDatabase::CreateSecureL(const TSqlSrvFileData& aFileData, CSqlSecurityPolicy* aSecurityPolicy) + { + __SQLASSERT(aFileData.IsSecureFileNameFmt(), ESqlPanicBadArgument); + __SQLASSERT(aSecurityPolicy != NULL, ESqlPanicBadArgument); + if(!::SqlServer().SecurityInspector().Check(aSecurityPolicy->DbPolicy(RSqlSecurityPolicy::ESchemaPolicy))) + { + //The caller has no "SCHEMA" policy. Then the client is not given a permission to create the database. + //Delete aSecurityPolicy since no database object is going to be created and the security policy object + //won't be put in the security policies map. + delete aSecurityPolicy; + __SQLLEAVE(KErrPermissionDenied); + } + //What does happen with aSecurityPolicy instance? + // If the database is created successfully, then a lookup will be made in the security policies map. + // (the security policies map contains reference counted security policies) + // If the same security policy already exists in the map, then aSecurityPolicy will be deleted. + // The reference counter of the found policy will be incremented. + // If aSecurityPolicy is not in the map, then it will be put in the map and removed + // from the map when CSqlSrvDatabase object is destroyed. + // (the "remove" operation decrements the security policy counter + // and destroys the policy if it reaches 0) + // + //The security policy map pair is: + //{secure_db_name, reference_counted db_security_policy} + //The security policy is reference counted, because the same database can be shared between one or more + //connections (clients), in which case only a single, reference counted instance of the database security + //policy is kept in the map. + CleanupStack::PushL(aSecurityPolicy); + CSqlSrvDatabase* self = new (ELeave) CSqlSrvDatabase(); + CleanupStack::Pop(aSecurityPolicy); + CleanupStack::PushL(self); + self->ConstructCreateSecureL(aFileData, aSecurityPolicy); + CleanupStack::Pop(self); + return self; + } + +/** +Creates new CSqlSrvDatabase instance which creates and manages new SQL database. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + +@return A pointer to the created CSqlSrvDatabase instance. + +@see CSqlServer +@see TSqlSrvFileData +@see MSqlPolicyInspector +@see RSqlSecurityMap +@see CSqlSecurityPolicy + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrArgument, the file name is a name of a secure database; + KErrAlreadyExists, the file already exists; + KErrNotReady, the drive does not exist or is not ready; + KErrInUse, the file is already open; + KErrArgument, the file name refers to a secure database, but aSecurityPolicy is NULL; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@panic SqlDb 4 In _DEBUG mode if aFileData refers to a secure database file name. +*/ +CSqlSrvDatabase* CSqlSrvDatabase::CreateL(const TSqlSrvFileData& aFileData) + { + __SQLASSERT(!aFileData.IsSecureFileNameFmt(), ESqlPanicBadArgument); + CSqlSrvDatabase* self = new (ELeave) CSqlSrvDatabase(); + CleanupStack::PushL(self); + self->ConstructCreateL(aFileData); + CleanupStack::Pop(self); + return self; + } + +/** +Creates new CSqlSrvDatabase instance which opens and manages an existing SQL database. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which created the database. + If this is application's private database, then the format of aFileData is as it is described + in TSqlSrvFileData::SetFromHandleL() comments. + +@return A pointer to the created CSqlSrvDatabase instance. + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrNotReady, the drive does not exist or is not ready; + KErrNotFound, the database file does not exist; + KErrInUse, the file is already open; + KErrPermissionDenied, the client application does not satisfy the relevant database security policies. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see CSqlServer +@see TSqlSrvFileData +@see MSqlPolicyInspector +@see RSqlSecurityMap +@see CSqlSecurityPolicy +@see TSqlSrvFileData::SetFromHandleL() +*/ +CSqlSrvDatabase* CSqlSrvDatabase::OpenL(const TSqlSrvFileData& aFileData) + { + CSqlSrvDatabase* self = new (ELeave) CSqlSrvDatabase(); + CleanupStack::PushL(self); + aFileData.IsSecureFileNameFmt() ? self->ConstructOpenSecureL(aFileData) : self->ConstructOpenL(aFileData); + CleanupStack::Pop(self); + return self; + } + +/** +Cleans up the allocated for the database connection memory and other resources. +*/ +CSqlSrvDatabase::~CSqlSrvDatabase() + { + SQLPROFILER_DB_CLOSE((TUint)iDbHandle); + TSqlCompactDbMapIterator compactDbIt(iCompactDbMap); + TSqlCompactDbPair compactDbPair; + while(compactDbIt.Next(compactDbPair)) + { + ::SqlServer().Compactor().ReleaseEntry(*compactDbPair.iData); + } + iCompactDbMap.Close(); + //If iSecureDbName is not NULL, the current CSqlSrvDatabase object operates on a secure database. + //The database security policy has to be removed from the security policy map. + //(The "remove" operation actually decrements the security policy reference counter and destroys the policy + //when the counter reaches 0. The counter value may be greater than 1 if the database is shared between + //more than one connection) + if(iSecureDbName) + { + ::SqlServer().SecurityMap().Remove(iSecureDbName); + //The security policy map owns iSecureDbName and iSecurityPolicy and is responsible for + //iSecureDbName and iSecurityPolicy destruction. + } + iAttachDbMap.Close(); + ::CloseDbHandle(iDbHandle); + } + +/** +Initializes CSqlSrvDatabase data memebers with their default values. + + +@see MSqlPolicyInspector +@see RSqlSecurityMap +@see CSqlServer +*/ +CSqlSrvDatabase::CSqlSrvDatabase() : + iAttachDbMap(TSqlAttachDbLinearOrder(&Compare), TSqlAttachDbDestructor()), + iCompactDbMap(TSqlCompactDbLinearOrder(&Compare2), TSqlCompactDbDestructor()) + { + } + +/** +Creates a new SQL database file and executes config pragmas. +This function is part of CSqlSrvDatabase instance initialization. +If the function leaves and the error is not KErrAlreadyExists, the database file will be deleted. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. + +@see TSqlSrvFileData + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrAlreadyExists, the file already exists. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. +*/ +void CSqlSrvDatabase::CreateNewDbFileL(const TSqlSrvFileData& aFileData) + { + if(::FileExists(aFileData.Fs(), aFileData.FileName())) + { + __SQLLEAVE(KErrAlreadyExists); + } + TDbFileCleanup dbFileCleanup(aFileData, iDbHandle); + CleanupStack::PushL(TCleanupItem(&DbFileCleanup, &dbFileCleanup)); + ::CreateDbHandleL(aFileData, iDbHandle); + SetConfigL(aFileData.ConfigParams(), ETrue); + CleanupStack::Pop();//DbFileCleanup + } + +/** +Opens an existing SQL database file and executes config pragmas. + +This function is part of CSqlSrvDatabase instance initialization. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. + If this is application's private database, then the format of aFileData is as it is described + in TSqlSrvFileData::SetFromHandleL() comments. + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrNotFound, SQL database file not found. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see TSqlSrvFileData +@see TSqlSrvFileData::SetFromHandleL() +*/ +void CSqlSrvDatabase::OpenExistingDbFileL(const TSqlSrvFileData& aFileData) + { + if(!aFileData.ContainHandles()) + {//This check is valid only if the database is outside application's private data cage + if(!::FileExists(aFileData.Fs(), aFileData.FileName())) + { + __SQLLEAVE(KErrNotFound); + } + } + ::CreateDbHandleL(aFileData, iDbHandle); + //this is an existing database, only the cache size can be changed, the page size is persistent database property. + //But for private databases, opened on the client side - the page size can be set (for the "create database" operations). + SetConfigL(aFileData.ConfigParams(), aFileData.ContainHandles()); + } + +/** +Installs an authorizer callback function. + +The callback function is invoked by the SQL parser at SQL statement compile time for each attempt +to access a column of a table in the database and is used to assert the calling application's rights to +perform specific SQL operation. + +This function is part of CSqlSrvDatabase instance initialization. + +@leave The function may leave with some database specific errors categorised as ESqlDbError. +*/ +void CSqlSrvDatabase::InstallAuthorizerL() + { + //Install the authorizer just once. "Install authorizer" may be expensive and dangerous operation because + //it will expire the already prepared statements. + if(!iAuthorizerInstalled) + { + (void)sqlite3SymbianLastOsError();//clear last OS error + TInt err = sqlite3_set_authorizer(iDbHandle, &CSqlSrvDatabase::AuthorizeCallback, this); + __SQLLEAVE_IF_ERROR(::Sql2OsErrCode(err, sqlite3SymbianLastOsError())); + } + iAuthorizerInstalled = ETrue; + } + +#ifdef SYMBIAN_USE_SQLITE_VERSION_3_6_4 +extern "C" int sqlite3RegisterInternalUtf8Like(sqlite3 *db); +#endif +/** +Installs user-defined functions. At the moment there is only one: LIKE. + +Replacing the LIKE operator default implementation with user defined LIKE functions. +The default implementation need a replacement because it does not work correctly with UTF16 encoded strings. +*/ +void CSqlSrvDatabase::InstallUDFsL() + { + //Registering user defined LIKE function with 2 parameters for UTF16 encoding + TInt err = sqlite3_create_function(iDbHandle, reinterpret_cast (KStrLikeFuncName().Ptr()), + 2/*arg*/, SQLITE_UTF16, this /*user data*/, + &CSqlSrvDatabase::LikeSqlFunc, NULL/*xStep*/, NULL/*xFinal*/); + __SQLLEAVE_IF_ERROR(::Sql2OsErrCode(err, sqlite3SymbianLastOsError())); + //Registering user defined LIKE function with 3 parameters for UTF16 encoding + err = sqlite3_create_function(iDbHandle, reinterpret_cast (KStrLikeFuncName().Ptr()), + 3/*arg*/, SQLITE_UTF16, this/*user data*/, + &CSqlSrvDatabase::LikeSqlFunc, NULL/*xStep*/, NULL/*xFinal*/); + __SQLLEAVE_IF_ERROR(::Sql2OsErrCode(err, sqlite3SymbianLastOsError())); + +#ifdef SYMBIAN_USE_SQLITE_VERSION_3_6_4 + //Registering user defined LIKE function with 2 and 3 parameters for UTF8 encoding + //Once registered, these will be treated as built-in functions + err = sqlite3RegisterInternalUtf8Like(iDbHandle); + __SQLLEAVE_IF_ERROR(::Sql2OsErrCode(err, sqlite3SymbianLastOsError())); +#endif + } + +/** +Constructs a key for searching in the security policies map. + +The key is UTF8 encoded, zero-terminated string. + +Every time when CSqlSrvDatabase instance creates, opens or attaches a secure database, it updates +the contents of the security policies map (RSqlSecurityMap class), which is a single instance owned +by the CSqlServer class. + +@param aDbFileName Full secure database file name, from which the security policies map key + will be constructed. + +@return A const pointer to the constructed security map key. No memory is allocated for the key. + +@leave KErrGeneral the UTF16 to UTF8 conversion of aDbFileName parameter failed. + +@see RSqlSecurityMap +@see CSqlServer +*/ +const TUint8* CSqlSrvDatabase::SecurityMapKeyL(const TDesC& aDbFileName) + { + //Form the map item key - the secure database name + TParsePtrC parse(aDbFileName);//this call may panic if aDbFileName cannot be parsed. But aDbFileName was preprocessed by TSqlSrvFileData::Set + TFileName secureDbName; + secureDbName.Copy(parse.Drive()); + secureDbName.Append(parse.NameAndExt()); + secureDbName.Append(TChar(0)); + TPtr8 ptr(iFileNameBuf, sizeof(iFileNameBuf)); + if(!::UTF16ZToUTF8Z(secureDbName, ptr)) + { + __SQLLEAVE(KErrGeneral); + } + return iFileNameBuf; + } + +/** +Attaches a secure or non-secure database to the current database connection. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. +@param aDbName Database name. It must be unique (per database connection). This is the name which can + be used for accessing tables in the attached database. The syntax is "database-name.table-name". + +@leave KErrNotFound, the database file which has to be attached does not exist. + KErrPermissionDenied, the client application does not satisfy the relevant security policies of + the attached database. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see TSqlSrvFileData +*/ +void CSqlSrvDatabase::AttachDbL(const TSqlSrvFileData& aFileData, const TDesC& aDbName) + { + if(!aFileData.ContainHandles()) + {//This check is valid only if the database is outside application's private data cage + if(!::FileExists(aFileData.Fs(), aFileData.FileName())) + { + __SQLLEAVE(KErrNotFound); + } + } + if(!aFileData.IsSecureFileNameFmt()) + {//A non-secure database to be attached - execute the "ATTACH DB" SQL statement and initialize the attached database. + InitAttachedDbL(aFileData, aDbName); + } + else + {//A secure database has to be attached. This is a complex operation and if it fails, proper cleanup + //has to be performed. "state" variable keeps the state, after which the "attach db" operation failed. + //There are three things needed to be done when atatching a secure database: + // 1. Executing the "ATTACH DB" SQL statement and initializing the attached database + // 2. Updating the security policy map + // 3. Updating the {db name, logical db name} map + //The additional map (3) is needed because when the authorizer callback is called by SQLITE, the callback + //is given the logical database name, if that's an operation on an attached database. Since the security + //policy map uses the database name as a key, the map (3) is used to find what is the physical database + //name, which the logical database name points to. + // + //There is an additional step (0), which may happen when a secure database is attached to a + //non-secure database. But this step does not require a cleanup. + CSqlSrvDatabase::TAttachState state = CSqlSrvDatabase::EAStNone; + const TUint8* securityMapKey = NULL; + TRAPD(err, DoAttachSecureDbL(state, aFileData, aDbName, securityMapKey)); + if(err != KErrNone) + { + //Cleanup + if(state > CSqlSrvDatabase::EAStNone) + { + (void)FinalizeAttachedDb(aDbName); + if(state > CSqlSrvDatabase::EAStDbAttached) + { + ::SqlServer().SecurityMap().Remove(securityMapKey); + } + } + __SQLLEAVE(err); + } + } + } + +/** +Attaches a secure database to the existing connection. + +The function also updates the following maps: +- Security policies map (CSqlServer::iSecurityMap), where a reference counted copy of database security policies + is kept for each created/opened/attached database during the life time of the SQL server; +- Attached secure databases map (CSqlSrvDatabase::iAttachDbMap), where an information is kept what SQL database + file name corresponds to a specific attached database name. This information is used by the authorizer callback + function in order to find what database security policies have to be asserted when a SQL operation is issued + for a particular attached database (the attached database name is identified always by its name, not the file name); + +@param aState Output parameter - the attach progress state, used by the calling function to perform correctly + the cleanup, if the attach operation fails. + It may have the following values set: + - CSqlSrvDatabase::EAStNone - no resources need to be freed; + - CSqlSrvDatabase::EAStDbAttached - the function was able to execute the "ATTACH DATABASE" + SQL statement before an error occured. The calling function has to execute "DETACH DATABASE" + SQL statement to detach the database; + - CSqlSrvDatabase::EAStSecurityMapUpdated - the function was able to update the security policies + map (CSqlServer::iSecurityMap) before an error occured. The calling function has to remove + the attached database security policies from the map and detach the database. +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + The secure database name format must be: + \:\<[SID]database file name excluding the path\>. + "[SID]" refers to SID of the application which creates the database. +@param aDbName Database name. It must be unique (per database connection). This is the name which can + be used for accessing tables in the attached database. The syntax is "database-name.table-name". +@param aMapKey Output parameter, UTF8 encoded, zero-terminated string, which can be used for searching + of the attached database security policies in the security policies map (CSqlServer::iSecurityMap). + No memory is allocated for the key. + +@see RSqlSecurityMap +@see RSqlAttachDbMap +@see CSqlServer +@see TSqlSrvFileData +@see CSqlSrvDatabase + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrPermissionDenied, the client application does not satisfy the relevant security policies of + the attached database. + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. +*/ +void CSqlSrvDatabase::DoAttachSecureDbL(CSqlSrvDatabase::TAttachState& aState, + const TSqlSrvFileData& aFileData, + const TDesC& aDbName, const TUint8*& aMapKey) + { + //Step 1: Attach the database and initialize the attached database + InitAttachedDbL(aFileData, aDbName); + aState = CSqlSrvDatabase::EAStDbAttached; + //Step 2: Load the database security policy and update the security map + const CSqlSecurityPolicy* securityPolicy = NULL; + UpdateSecurityMapL(ETrue, aFileData, aMapKey, securityPolicy); + aState = CSqlSrvDatabase::EAStSecurityMapUpdated; + //Check that the caller has at least one of {Schema, Read, Write} policies. + BasicSecurityPolicyCheckL(*securityPolicy); + //Step 3: Update the attached databases map + InsertInAttachDbMapL(aFileData.FileName(), aDbName); + } + +/** +Detaches a database from the current database connection. + +The function also will search the security policies map (CSqlServer::iSecurityMap) and the attached databases +map (CSqlSrvDatabase::iAttachDbMap) for entries which correspond to the database , +which is about to be detached, and will remove the entries. + +@param aDbName Attached database name. It must be unique (per database connection). + +@leave The function may leave with some database specific errors categorised as ESqlDbError, + and other system-wide error codes. + +@see CSqlSrvDatabase::DoAttachSecureDbL() +@see RSqlSecurityMap +@see RSqlAttachDbMap +@see CSqlServer +@see CSqlSrvDatabase +*/ +void CSqlSrvDatabase::DetachDbL(const TDesC& aDbName) + { + TInt err = FinalizeAttachedDb(aDbName); + if(err == KErrNone) + { + TRAP(err, RemoveFromMapsL(aDbName));//ignore the error + } + else + { + __SQLLEAVE(err); + } + } + +/** +Calculates and returns the database size. + +@param aDbName Attached database name or KNullDesC for the main database + +@leave The function may leave with some database specific errors categorised as ESqlDbError, + and other system-wide error codes. + +@return Database size in bytes. +*/ +TInt64 CSqlSrvDatabase::SizeL(const TDesC& aDbName) + { + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + TInt pageCount = 0; + __SQLLEAVE_IF_ERROR(::DbPageCount(iDbHandle, aDbName, pageCount)); + __SQLASSERT(pageCount >= 0, ESqlPanicInternalError); + CleanupStack::PopAndDestroy(); + return (TInt64)pageCount * PageSizeL(aDbName); + } + +/** +Retrieves the database free space. + +@param aDbName Attached database name or KNullDesC for the main database + +@return Database free space in bytes. + +@leave The function may leave with some database specific errors categorised as ESqlDbError, + and other system-wide error codes. +*/ +TInt64 CSqlSrvDatabase::FreeSpaceL(const TDesC& aDbName) + { + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + TInt freePageCount = 0; + __SQLLEAVE_IF_ERROR(::DbFreePageCount(iDbHandle, aDbName, freePageCount)); + CleanupStack::PopAndDestroy(); + __SQLASSERT(freePageCount >= 0, ESqlPanicInternalError); + return (TInt64)freePageCount * PageSizeL(aDbName); + } + +/** +Collects information regarding the current cache size, page, size, encoding, etc. and puts the values +in the aDest output string. + +@param aDest Output parameter, where the result values will be stored. The string format is: ";...;". + +@leave The function may leave with some database specific errors categorised as ESqlDbError, + and other system-wide error codes. +*/ +void CSqlSrvDatabase::QueryConfigL(TDes8& aDest) + { + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + + TInt cacheSize = 0; + __SQLLEAVE_IF_ERROR(::DbCacheSize(iDbHandle, KNullDesC, cacheSize)); + + TInt pageSize = 0; + __SQLLEAVE_IF_ERROR(::DbPageSize(iDbHandle, KNullDesC, pageSize)); + + TBuf8<16> encoding; + __SQLLEAVE_IF_ERROR(::DbEncoding(iDbHandle, KNullDesC, encoding)); + + TInt defaultSoftHeapLimit = TSqlSrvConfigParams::KDefaultSoftHeapLimitKb; + + TInt vacuum = 0; + __SQLLEAVE_IF_ERROR(::DbVacuumMode(iDbHandle, KNullDesC, vacuum)); + + aDest.AppendNum(cacheSize); + _LIT8(KSemiColon, ";"); + aDest.Append(KSemiColon); + aDest.AppendNum(pageSize); + aDest.Append(KSemiColon); + aDest.Append(encoding); + aDest.Append(KSemiColon); + aDest.AppendNum(defaultSoftHeapLimit); + aDest.Append(KSemiColon); + aDest.AppendNum(vacuum); + aDest.Append(KSemiColon); + + CleanupStack::PopAndDestroy(); + } + +/** +Compacts the database free space. + +@param aSize The requested database space to be compacted, in bytes. + If aSize value is RSqlDatabase::EMaxCompaction, then all free pages will be removed. + Note that the requested space to be compacted will be rounded up to the nearest page count, + e.g. request for removing 1 byte will remove one free page from the database file. + +@param aDbName Attached database name or KNullDesC for the main database + +@return The size of the removed free space + +@leave The function may leave with some database specific errors categorised as ESqlDbError, + and other system-wide error codes. +*/ +TInt CSqlSrvDatabase::CompactL(TInt aSize, const TDesC& aDbName) + { + __SQLASSERT(aSize > 0 || aSize == RSqlDatabase::EMaxCompaction, ESqlPanicBadArgument); + TInt pageSize = PageSizeL(aDbName);//PageSizeL() will disable/enable the authorizer + TInt pageCount = KMaxTInt; + if(aSize > 0) + {//64-bit calculations to avoid the overflow in case if (aSize + pageSize) >= KMaxTInt. + pageCount = (TInt64)((TInt64)aSize + pageSize - 1) / pageSize; + } + if(pageCount > 0) + { + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + __SQLLEAVE_IF_ERROR(::DbCompact(iDbHandle, aDbName, pageCount, pageCount)); + CleanupStack::PopAndDestroy(); + } + return pageCount * pageSize; + } + +/** +This structure is used in case if the InitAttachedDbL() execution fails and the +executed operations ("attach database", "init compact") have to be reverted calling +FinalizeAttachedDb(). + +@see CSqlSrvDatabase::InitAttachedDbL +@see CSqlSrvDatabase::FinalizeAttachedDb +@see CSqlSrvDatabase::AttachCleanup + +@internalComponent +*/ +NONSHARABLE_STRUCT(TAttachCleanup) + { + inline TAttachCleanup(CSqlSrvDatabase& aSelf, const TDesC& aDbName) : + iSelf(aSelf), + iDbName(aDbName) + { + } + CSqlSrvDatabase& iSelf; + const TDesC& iDbName; + }; + +/** +Cleanup function. Calls FinalizeAttachedDb() if InitAttachedDbL() has failed. + +@param aCleanup A pointer to a TAttachCleanup object that contains the needed for the cleanup information. + +@see TAttachCleanup + +@internalComponent +*/ +void CSqlSrvDatabase::AttachCleanup(void* aCleanup) + { + TAttachCleanup* cleanup = reinterpret_cast (aCleanup); + __SQLASSERT(cleanup != NULL, ESqlPanicBadArgument); + if(cleanup) + { + (void)cleanup->iSelf.FinalizeAttachedDb(cleanup->iDbName); + } + } + +/** +Forms and executes "ATTACH DATABASE" SQL statement. +If the database is not read-only: + - Makes the attached database journal file persistent. + - Initializes the attached database compaction mode. + - The attached database will be reindexed if the default collation has been changed. + +@param aFileData Attached database file data +@param aDbName Attached database name. It must be unique (per database connection). + +@leave KErrNoMemory, an out of memory condition has occurred; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see InitCompactionL +@see ProcessSettingsL +*/ +void CSqlSrvDatabase::InitAttachedDbL(const TSqlSrvFileData& aFileData, const TDesC& aDbName) + { + TPtrC dbFileName = aFileData.FileName(); + TBool readOnlyDbFile = aFileData.IsReadOnly(); + + InstallAuthorizerL(); + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + sqlite3_stmt* stmtHandle = StmtPrepare16L(iDbHandle, KAttachDb); + TInt err = ::Sql2OsErrCode(sqlite3_bind_text16(stmtHandle, 1, dbFileName.Ptr(), dbFileName.Length() * sizeof(TUint16), SQLITE_STATIC), sqlite3SymbianLastOsError()); + if(err == KErrNone) + { + err = ::Sql2OsErrCode(sqlite3_bind_text16(stmtHandle, 2, aDbName.Ptr(), aDbName.Length() * sizeof(TUint16), SQLITE_STATIC), sqlite3SymbianLastOsError()); + if(err == KErrNone) + { + err = StmtExec(stmtHandle); + } + } + TInt err2 = ::FinalizeStmtHandle(stmtHandle); + CleanupStack::PopAndDestroy();//TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled) + if(err == KErrNone && err2 != KErrNone) + {//::FinalizeStmtHandle() has failed + err = err2; + } + __SQLLEAVE_IF_ERROR(err); + + TAttachCleanup attachCleanup(*this, aDbName); + CleanupStack::PushL(TCleanupItem(&CSqlSrvDatabase::AttachCleanup, &attachCleanup)); + + SetConfigL(aFileData.ConfigParams(), EFalse, aDbName); + + if(!readOnlyDbFile) + { + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + ProcessSettingsL(aFileData, aDbName); + CleanupStack::PopAndDestroy();//TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled) + } + + CleanupStack::Pop();//TCleanupItem(&CSqlSrvDatabase::AttachCleanup, &attachCleanup) + } + +/** +Forms and executes "DETACH DATABASE" SQL statement. +If the database was scheduled for background compacting, the related database entry will be removed from +the vaccum db map. + +@param aDbName Attached database name. It must be unique (per database connection). + +@return KErrNone, Operation completed successfully; + KErrNoMemory, an out of memory condition has occurred. + Note that the function may also return some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. +*/ +TInt CSqlSrvDatabase::FinalizeAttachedDb(const TDesC& aDbName) + { + ReleaseCompactEntry(aDbName); + iAuthorizerDisabled = ETrue; + sqlite3_stmt* stmtHandle = NULL; + TRAPD(err, stmtHandle = StmtPrepare16L(iDbHandle, KDetachDb)); + if(err == KErrNone) + { + err = ::Sql2OsErrCode(sqlite3_bind_text16(stmtHandle, 1, aDbName.Ptr(), aDbName.Length() * sizeof(TUint16), SQLITE_STATIC), sqlite3SymbianLastOsError()); + if(err == KErrNone) + { + err = StmtExec(stmtHandle); + } + } + TInt err2 = ::FinalizeStmtHandle(stmtHandle); + if(err == KErrNone && err2 != KErrNone) + {//::FinalizeStmtHandle() has failed + err = err2; + } + iAuthorizerDisabled = EFalse; + return err; + } + +/** +Updates the security policies map (CSqlServer::iSecurityMap). + +Inserts a new item in the security map, or if such item already exists there - its reference counter will +be incremented. +The method guarantees that either the security map will be updated, or the method leaves and the security +policies map stays unchanged. + +@param aAttachedDb True if the currently processed database is an attached database, false if it is the main db. +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + The secure database name format must be: + \:\<[SID]database file name excluding the path\>. + "[SID]" refers to SID of the application which creates the database. +@param aMapKey Output parameter. UTF8 encoded, zero-terminated string. On function exit cannot be NULL. + It will be set to point to the security policies map key. No memory is allocated for the key. +@param aSecurityPolicy Output parameter. On function exit cannot be NULL. It will be set to point to the security policies. + +@leave KErrNoMemory, an out of memory condition has occurred; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see RSqlSecurityMap +@see CSqlServer +@see TSqlSrvFileData +@see CSqlSecurityPolicy +*/ +void CSqlSrvDatabase::UpdateSecurityMapL(TBool aAttachedDb, const TSqlSrvFileData& aFileData, + const TUint8*& aMapKey, const CSqlSecurityPolicy*& aSecurityPolicy) + { + //Check if a copy of the database security policies is already in the security map. + aMapKey = SecurityMapKeyL(aFileData.FileName()); + TSqlSecurityPair* pair = ::SqlServer().SecurityMap().Entry(aMapKey); + if(pair) + { + //Yes, it is in the map. Increment the reference counter. + //(It will be decremented when detaching the database). + pair->iRefCounter.Increment(); + aMapKey = pair->iKey; + aSecurityPolicy = pair->iData; + } + else + { + //No, it is not in the map. Read the security policies from the security policies tables and + //insert a new item in the map. + __SQLASSERT(aMapKey != NULL, ESqlPanicInternalError); + aMapKey = ::CreateStrCopyLC(aMapKey); + CSqlSecurityPolicy* securityPolicy = aAttachedDb ? ::LoadAttachedDbSecurityPolicyLC(aFileData) : + ::LoadDbSecurityPolicyLC(iDbHandle); + __SQLLEAVE_IF_ERROR(::SqlServer().SecurityMap().Insert(aMapKey, securityPolicy)); + CleanupStack::Pop(2);//iSecurityMap owns aMapKey and securityPolicy objects + aSecurityPolicy = securityPolicy; + } + __SQLASSERT(aMapKey != NULL, ESqlPanicInternalError); + __SQLASSERT(aSecurityPolicy != NULL, ESqlPanicInternalError); + } + +/** +Removes attached secure database entries from the maps. + +The function will search the security policies map (CSqlServer::iSecurityMap) and the attached databases +map (CSqlSrvDatabase::iAttachDbMap) for entries which correspond to the database with aDbName name, +and will remove the entries. + +The sequence of the performed by the function operations is: +1. Looks for a map item which key is aDbName in iAttachDbMap map. +2. If such pair exists, the data part of the pair is used as a key in iSecurityMap map. +3. Remove the iSecurityMap map item pointed by the data part of the found pair. +4. Remove the iAttachDbMap map item pointed by aDbName. + +@param aDbName Attached database name. It must be unique (per database connection). + +@leave KErrGeneral It is not possible to convert aDbName parameter to UTF8 encoded string. + +@see CSqlSrvDatabase::DoAttachDbL() +@see CSqlSrvDatabase::DoAttachSecureDbL() +@see RSqlSecurityMap +@see RSqlAttachDbMap +@see CSqlServer +@see CSqlSrvDatabase +*/ +void CSqlSrvDatabase::RemoveFromMapsL(const TDesC& aDbName) + { + TPtr8 ptr(iFileNameBuf, sizeof(iFileNameBuf)); + if(!::UTF16ToUTF8Z(aDbName, ptr)) + { + __SQLLEAVE(KErrGeneral); + } + TSqlAttachDbPair* attachDbPair = iAttachDbMap.Entry(iFileNameBuf); + if(attachDbPair) + {//aDbName refers to attached secure database + ::SqlServer().SecurityMap().Remove(attachDbPair->iData); + iAttachDbMap.Remove(iFileNameBuf); + } + } + +/** +Inserts a new entry in the attached databases map (CSqlSrvDatabase::iAttachDbMap). + +The method guarantees that either a new [logical db name, secure db name] pair will be inserted in +iAttachDbMap or the method fails and the content of iAttachDbMap remains unchanged. + +@param aDbFileName Database file name, including the path. +@param aDbName Database name. + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrGeneral, it is not possible to convert the function parameters to UTF8 encoded strings. + +@see RSqlAttachDbMap +@see CSqlSrvDatabase +*/ +void CSqlSrvDatabase::InsertInAttachDbMapL(const TDesC& aDbFileName, const TDesC& aDbName) + { + //Convert aDbName to UTF8, zero-terminated name + TPtr8 ptr(iFileNameBuf, sizeof(iFileNameBuf)); + if(!::UTF16ToUTF8Z(aDbName, ptr)) + { + __SQLLEAVE(KErrGeneral); + } + const TUint8* mapKey = ::CreateStrCopyLC(iFileNameBuf); + const TUint8* mapData = SecurityMapKeyL(aDbFileName); + mapData = ::CreateStrCopyLC(mapData); + __SQLLEAVE_IF_ERROR(iAttachDbMap.Insert(mapKey, mapData)); + CleanupStack::Pop(2);//iAttachDbMap owns mapKey amd mapData. + } + +/** +Processes the database settings that are currently stored in the settings table. +Makes the journal file persistent. +Initializes the database compaction mode. +Based on the current settings the database may be configured to become 'up-to-date'. +This configuration may include reindexing the database if the collation dll has +changed and/or executing database configuration file operations. + +@param aFileData The file data object, +@param aDbName Logical database name: "main" for the main database or attached database name, + +@leave The function may leave with system-wide error codes or SQL errors of ESqlDbError type + +@panic SqlDb 7 In _DEBUG mode if aFileData does not refer to a r/w database file. +*/ +void CSqlSrvDatabase::ProcessSettingsL(const TSqlSrvFileData& aFileData, const TDesC& aDbName) + { + __SQLASSERT(!aFileData.IsReadOnly(), ESqlPanicInternalError); +#if !defined(__SQL_DISABLE_SYMBIAN_SETTINGS_TABLE__) + //Make the journal file persistent - done by SQLite automatically because the locking mode is EXCLUSIVE + //__SQLLEAVE_IF_ERROR(::ExecPragma(iDbHandle, iAuthorizerDisabled, KPersistentJournalPragma, KPersist, aDbName)); + //Load the current settings + TFileName storedCollationDllName; + TInt storedDbConfigFileVer = -1; + TInt currVacuumMode = -1; + __SQLLEAVE_IF_ERROR(::DbVacuumMode(iDbHandle, aDbName, currVacuumMode)); + //currVacuumMode == ESqliteVacuumOff ==> This is a database created not by the SQL server + TSqlCompactionMode compactionMode = currVacuumMode == ESqliteVacuumOff ? ESqlCompactionManual : ESqlCompactionNotSet; + TSqlDbSysSettings dbSettings(iDbHandle); + dbSettings.LoadSettingsL(aDbName, storedCollationDllName, storedDbConfigFileVer, compactionMode); + __SQLASSERT(currVacuumMode == ESqliteVacuumOff ? compactionMode == ESqlCompactionManual : 1, ESqlPanicInternalError); + if(aFileData.ContainHandles() && aFileData.IsCreated()) + { + compactionMode = aFileData.ConfigParams().iCompactionMode; + if(compactionMode == ESqlCompactionNotSet) + { + compactionMode = KSqlDefaultCompactionMode; + } + //This is a just created private database. Store the compaction mode (the compaction mode may have been set using a config string). + StoreSettingsL(storedCollationDllName, storedDbConfigFileVer, compactionMode); + } + //Init the database compaction mode + InitCompactionL(compactionMode, aFileData.ConfigParams().iFreePageThresholdKb, aFileData.FileName(), + (TSqliteVacuumMode)currVacuumMode, aDbName); + //Based on the current settings, apply any necessary configuration updates to the database + ApplyConfigUpdatesL(storedCollationDllName, storedDbConfigFileVer, aFileData, aDbName); +#endif // !(__SQL_DISABLE_SYMBIAN_SETTINGS_TABLE__) + } + +/** +Applies any necessary configuration updates to the database, based on the current settings +in the settings table and how the database is being used (i.e. 'Opened' or 'Attached'). +The applied configuration may include: +- Reindexing the main database and all attached databases, if the collation dll has been changed. +After the reindexation the new collation dll name will be stored in the settings table of the database. +- Executing all supported operations on the database that are specified in a database configuration +file, if such a file exists and has not already been processed. +The settings table will updated with the current version of the database configuration file if the file +is processed. + +@param aStoredCollationDllName The name of the collation dll that is stored in the settings table, +@param aStoredDbConfigFileVersion The database configuration file version that is stored in the settings table, +@param aFileData The file data object, +@param aDbName Logical database name: "main" for the main database or attached database name, + +@leave The function may leave with system-wide error codes or SQL errors of ESqlDbError type +*/ +void CSqlSrvDatabase::ApplyConfigUpdatesL(const TDesC& aStoredCollationDllName, const TInt& aStoredDbConfigFileVersion, + const TSqlSrvFileData& aFileData, const TDesC& aDbName) + { + TSqlDbSysSettings dbSettings(iDbHandle); + //Check whether reindexing is necessary + if(::SqlServer().CollationDllName().CompareF(aStoredCollationDllName) != 0) + { + dbSettings.ReindexDatabaseL(aDbName, ::SqlServer().CollationDllName()); + } + + //Perform any necessary configuration file updates to the database. + //We do not want failures here to cause the database to fail + //to be opened and so any leave error is TRAPed and ignored + //(the error is logged in _DEBUG mode) + TRAPD(err, dbSettings.ConfigureDatabaseL(aStoredDbConfigFileVersion, aFileData, aDbName)); + if(KErrNone != err) + { + __SQLLOG_ERR(_L("SQLLOG: CSqlSrvDatabase::ApplyConfigUpdatesL() - ConfigureDatabaseL() failed with error code %d"), err); + } + } + +/** +Sets the "cache size" and "page size" parameter values, if their values are valid. +This is done formatting and executing PRAGMA statements. +@param aConfigParams This object contains the cofiguration parameters +@param aSetPageSize If true, the page size will be set, otherwise not. + The aSetPageSize is set to true if: + 1) The operation is "create database" + 2) The operation is "open private database" +@param aLogicalDbName Parameter with default value of KNullDesC. If aLogicalDbName length is not 0, then the + "cache_size" pragma will be executed on the attached database with aLogicalDbName name. +*/ +void CSqlSrvDatabase::SetConfigL(const TSqlSrvConfigParams& aConfigParams, TBool aSetPageSize, const TDesC& aLogicalDbName) + { + __SQLASSERT(aConfigParams.iPageSize == TSqlSrvConfigParams::KConfigPrmValueNotSet || aConfigParams.iPageSize >= 0, ESqlPanicBadArgument); + __SQLASSERT(aConfigParams.iCacheSize == TSqlSrvConfigParams::KConfigPrmValueNotSet || aConfigParams.iCacheSize >= 0, ESqlPanicBadArgument); + if(aSetPageSize && aConfigParams.iPageSize != TSqlSrvConfigParams::KConfigPrmValueNotSet) + { + __SQLLEAVE_IF_ERROR(::ExecPragma(iDbHandle, iAuthorizerDisabled, KPageSizePragma, aConfigParams.iPageSize)); + } + + const TDesC& logicalDbName = aLogicalDbName.Length() > 0 ? aLogicalDbName : KMainDb16; + + //Setting the cache size. + //Step 1: Check if aConfigParams.iCacheSize value is set. If it is set, then use it. + if(aConfigParams.iCacheSize != TSqlSrvConfigParams::KConfigPrmValueNotSet) + { + __SQLLEAVE_IF_ERROR(::ExecPragma(iDbHandle, iAuthorizerDisabled, KCacheSizePragma, aConfigParams.iCacheSize, logicalDbName)); + } + else + { + //Step 2: aConfigParams.iCacheSize value is not set. Then check if aConfigParams.iSoftHeapLimitKb value is set. + if(aConfigParams.iSoftHeapLimitKb != TSqlSrvConfigParams::KConfigPrmValueNotSet) + { + __SQLASSERT(aConfigParams.iSoftHeapLimitKb >= TSqlSrvConfigParams::KMinSoftHeapLimitKb && + aConfigParams.iSoftHeapLimitKb <= TSqlSrvConfigParams::KMaxSoftHeapLimitKb, ESqlPanicInternalError); + //Step 3: aConfigParams.iSoftHeapLimitKb value is set. Then use it to calculate the cache size. But we need the page size first. + // aLogicalDbName is used instead of logicalDbName because PageSizeL() if called with non-zero length name, + // "thinks" it is the main database name. KMainDb16 will be interpreted as an attached database name. + TInt pageSize = PageSizeL(aLogicalDbName); + //Step 4: Calculate the cache size. + TInt cacheSize = ((TInt64)aConfigParams.iSoftHeapLimitKb * 1024) / pageSize;//"TInt64" cast is used because of a possible overflow + //Step 5: Set the cache size. + __SQLLEAVE_IF_ERROR(::ExecPragma(iDbHandle, iAuthorizerDisabled, KCacheSizePragma, cacheSize, logicalDbName)); + } + } + } + +/** +Initializes the database compaction mode. +If the aCompactionMode parameter is ESqlCompactionBackground, the database with aDbFileName file name will be added +to the compactor for background compacting. + +@param aCompactionMode The database compaction mode. See TSqlCompactionMode enum for the supported database compaction modes. +@param aFreePageThresholdKb Free page threshold. The background compaction won't start if the free pages size in Kb is less than + the free page threshold. +@param aDbFileName Database file name including full path +@param aCurrentVacuumMode The current SQLite vacuum mode, one of TSqliteVacuumMode enum item values. + If the current database vacuum mode is ESqliteVacuumOff, which means + the database has been created not by the SQL server, + then the "off" vacuum mode is kept unchanged. +@param aDbName "main" or the attached database name + +@leave KErrNoMemory, an out of memory condition has occurred; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see TSqlCompactionMode +@see CSqlCompactor +@see TSqliteVacuumMode + +@panic SqlDb 4 In _DEBUG mode if aCompactionMode parameter value is invalid. +*/ +void CSqlSrvDatabase::InitCompactionL(TSqlCompactionMode aCompactionMode, TInt aFreePageThresholdKb, + const TDesC& aDbFileName, TSqliteVacuumMode aCurrentVacuumMode, const TDesC& aDbName) + { + __SQLASSERT(aCompactionMode == ESqlCompactionManual || aCompactionMode == ESqlCompactionBackground || aCompactionMode == ESqlCompactionAuto, ESqlPanicBadArgument); + __SQLASSERT(aCurrentVacuumMode == ESqliteVacuumOff || aCurrentVacuumMode == ESqliteVacuumAuto || + aCurrentVacuumMode == ESqliteVacuumIncremental, ESqlPanicBadArgument); + __SQLASSERT(aFreePageThresholdKb >= 0, ESqlPanicBadArgument); + TSqliteVacuumMode newSqliteVacuumMode = aCompactionMode == ESqlCompactionAuto ? ESqliteVacuumAuto : ESqliteVacuumIncremental; + if(aCurrentVacuumMode == ESqliteVacuumOff) + { + newSqliteVacuumMode = ESqliteVacuumOff; + } + if(aCurrentVacuumMode != newSqliteVacuumMode) + { + __SQLLEAVE_IF_ERROR(::ExecPragma(iDbHandle, iAuthorizerDisabled, KAutoVacuumPragma, newSqliteVacuumMode, aDbName)); + } + if(aCompactionMode == ESqlCompactionBackground) + { + NewCompactEntryL(aFreePageThresholdKb, aDbFileName, aDbName); + } + } + +/** +Adds the aDbFileName database to the compactor object for background compacting. + +@param aFreePageThresholdKb Free page threshold in Kb. The background compaction won't start if the total size of the free pages + is less than the free page threshold. +@param aDbFileName Database file name including full path +@param aDbName "main" or the attached database name + +@leave KErrNoMemory, an out of memory condition has occurred; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@see CSqlCompactor +@see RSqlCompactDbMap +*/ +void CSqlSrvDatabase::NewCompactEntryL(TInt aFreePageThresholdKb, const TDesC& aDbFileName, const TDesC& aDbName) + { + TSqlCompactSettings settings; + settings.iFreePageThresholdKb = aFreePageThresholdKb; + ::SqlServer().Compactor().AddEntryL(aDbFileName, settings); + TInt err = KErrNoMemory; + HBufC* key = aDbName.Alloc(); + HBufC* data = aDbFileName.Alloc(); + if(key && data) + { + err = iCompactDbMap.Insert(key, data);//returns the index of the new entry + } + if(err < 0) //If either "key" or "data" or both is NULL, then "err" is KErrNoMemory and the next "if" will be executed. + { + delete data; + delete key; + ::SqlServer().Compactor().ReleaseEntry(aDbFileName); + } + __SQLLEAVE_IF_ERROR(err); + } + +/** +Removes database (identified by its logical name) from the compactor. + +@param aDbName "main" or the attached database name + +@see CSqlCompactor +@see RSqlCompactDbMap +*/ +void CSqlSrvDatabase::ReleaseCompactEntry(const TDesC& aDbName) + { + TSqlCompactDbMapIterator compactDbIt(iCompactDbMap); + TSqlCompactDbPair compactDbPair; + while(compactDbIt.Next(compactDbPair)) + { + if(::CompareNoCase(*compactDbPair.iKey, aDbName) == 0) + { + ::SqlServer().Compactor().ReleaseEntry(*compactDbPair.iData); + iCompactDbMap.Remove(compactDbPair.iKey); + break; + } + } + } + +/** +Cleanup function. +Used during the construction phase of the CSqlSrvDatabase instance when a new database is created. +If the database creation succeeds, the "init compaction" operation succeeds but some other init operation fail, +the just created database file has to be deleted. But before that the database has to be removed from the compactor, +because the compactor creates independent database connection that has to be closed before the database deletion. + +The database will be removed from the compactor as a result of the call. + +@param aCleanup A pointer to the CSqlSrvDatabase object +*/ +void CSqlSrvDatabase::CompactCleanup(void* aCleanup) + { + CSqlSrvDatabase* self = reinterpret_cast (aCleanup); + __SQLASSERT(self != NULL, ESqlPanicBadArgument); + if(self) + { + self->ReleaseCompactEntry(KMainDb16); + } + } + +/** +Retrieves the database page size. +If the request is for the main database page size and if the size is not retrieved yet, then the page size value will be +cached in iPageSize data member. + +@param aDbName Attached database name or KNullDesC for the main database + +@leave KErrNoMemory, an out of memory condition has occurred; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. + +@return The database page size +*/ +TInt CSqlSrvDatabase::PageSizeL(const TDesC& aDbName) + { + if(iPageSize > 0 && aDbName == KNullDesC) + { + return iPageSize; + } + iAuthorizerDisabled = ETrue; + CleanupStack::PushL(TCleanupItem(&EnableAuthorizer, &iAuthorizerDisabled)); + TInt pageSize = 0; + __SQLLEAVE_IF_ERROR(::DbPageSize(iDbHandle, aDbName, pageSize)); + CleanupStack::PopAndDestroy(); + __SQLASSERT(pageSize > 0, ESqlPanicInternalError); + if(aDbName == KNullDesC) + { + iPageSize = pageSize; + } + return pageSize; + } + +////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// ConstructL() methods /////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Second phase construction method. Creates a new secure database file. +If the function fails, the database file will be closed and deleted. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. +@param aSecurityPolicy Database security policy + +@panic SqlDb 4 In _DEBUG mode if aSecurityPolicy is NULL. +*/ +void CSqlSrvDatabase::ConstructCreateSecureL(const TSqlSrvFileData& aFileData, CSqlSecurityPolicy* aSecurityPolicy) + { + __SQLASSERT(aSecurityPolicy != NULL, ESqlPanicBadArgument); + //Insert a new item in the security policies map. + CleanupStack::PushL(aSecurityPolicy); + const TUint8* mapKey = SecurityMapKeyL(aFileData.FileName()); + mapKey = ::CreateStrCopyLC(mapKey); + __SQLLEAVE_IF_ERROR(::SqlServer().SecurityMap().Insert(mapKey, aSecurityPolicy)); + CleanupStack::Pop(2);//iSecurityMap owns mapKey and aSecurityPolicy. + iSecureDbName = mapKey; + iSecurityPolicy = aSecurityPolicy; + // + DoCommonConstructCreateL(aFileData, ETrue); + } + +/** +Second phase construction method. Creates a new non-secure database file. +If the function fails, the database file will be closed and deleted. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. +*/ +void CSqlSrvDatabase::ConstructCreateL(const TSqlSrvFileData& aFileData) + { + DoCommonConstructCreateL(aFileData, EFalse); + } + +//Called by the two "Contruct&Create" methods: ConstructCreateL() and ConstructCreateSecureL(). +//The aSecureDb parameter tells which method is the caller. +//The function performs common construction and initialization: +// - creates the database file +// - makes the journal file persistent +// - initializes the database compaction mode +// - stores the initial settings in the settings table, including the current collation dll name +// - stores the security settings and installs the authorizer, if aSecureDb is true +// - installs the user-defined functions +// - installs collations +//If the method fails and the error is not KErrAlreadyExists, the database file will be closed and deleted. +void CSqlSrvDatabase::DoCommonConstructCreateL(const TSqlSrvFileData& aFileData, TBool aSecureDb) + { + __SQLASSERT(!iDbHandle, ESqlPanicInternalError); + __SQLASSERT(aSecureDb ? iSecurityPolicy != NULL : ETrue, ESqlPanicInternalError); + CreateNewDbFileL(aFileData); + TDbFileCleanup dbFileCleanup(aFileData, iDbHandle); + CleanupStack::PushL(TCleanupItem(&DbFileCleanup, &dbFileCleanup)); + //Make the journal file persistent - done by SQLite automatically because the locking mode is EXCLUSIVE + //::ExecPragma(iDbHandle, iAuthorizerDisabled, KPersistentJournalPragma, KPersist); + //Init database compaction mode + TSqlCompactionMode compactionMode = aFileData.ConfigParams().iCompactionMode; + if(compactionMode == ESqlCompactionNotSet) + { + compactionMode = KSqlDefaultCompactionMode; + } + TInt currVacuumMode = -1; + __SQLLEAVE_IF_ERROR(::DbVacuumMode(iDbHandle, KNullDesC, currVacuumMode)); + //currVacuumMode == ESqliteVacuumOff ==> This is a database created not by the SQL server + InitCompactionL(compactionMode, aFileData.ConfigParams().iFreePageThresholdKb, aFileData.FileName(), (TSqliteVacuumMode)currVacuumMode); + CleanupStack::PushL(TCleanupItem(&CSqlSrvDatabase::CompactCleanup, this)); + //Store the initial settings in the settings table (including the current collation dll name) + StoreSettingsL(::SqlServer().CollationDllName(), KSqlNullDbConfigFileVersion, compactionMode); + if(aSecureDb) + { + //Store the security policies in the security policies tables. + TSqlDbSysSettings dbSysSettings(iDbHandle); + dbSysSettings.StoreSecurityPolicyL(*iSecurityPolicy); + } + InstallAuthorizerL(); + InstallUDFsL(); + InstallCollationsL(); + CleanupStack::Pop(2);//CompactCleanup, DbFileCleanup + SQLPROFILER_DB_CREATE((TUint)iDbHandle, aFileData.FileName()); + } + +/** +Second phase construction method. Opens an existing secure database file. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. +*/ +void CSqlSrvDatabase::ConstructOpenSecureL(const TSqlSrvFileData& aFileData) + { + DoCommonConstructOpenL(aFileData, ETrue); + } + +/** +Second phase construction method. Opens an existing non-secure database file. + +@param aFileData A reference to a TSqlSrvFileData instance, which keeps the database name, a + file session reference and some other database file related properties. + If this is a secure database, then the format of the name must be: + \:\<[SID]database file name excluding the path\>. + If this is a non-secure database, then the file name has to be the full database file name. + "[SID]" refers to SID of the application which creates the database. + If this is application's private database, then the format of aFileData is as it is described + in TSqlSrvFileData::SetFromHandleL() comments. +@see TSqlSrvFileData::SetFromHandleL() +*/ +void CSqlSrvDatabase::ConstructOpenL(const TSqlSrvFileData& aFileData) + { + DoCommonConstructOpenL(aFileData, EFalse); + } + +//Opens a database and does all necessary initializations +//Called by the two "Contruct&Open" methods: ConstructOpenL() and ConstructOpenSecureL(). +//The aSecureDb parameter tells which method is the caller. +//The function performs common construction and initialization: +// - opens the database file +// - installs the user-defined functions +// - installs collations +// - installs the authoriser callback +void CSqlSrvDatabase::DoCommonConstructOpenL(const TSqlSrvFileData& aFileData, TBool aSecureDb) + { + OpenExistingDbFileL(aFileData);//iDbHandle is valid after a successful call + //The user-defined collations must be installed before the possible database reindexing!!! + InstallCollationsL(); + if(!aFileData.IsReadOnly()) + {//No need to disable the authorizer since it is not installed yet. + //Make sure that the user-defined collation have been installed before the reindexing operation. + ProcessSettingsL(aFileData, KMainDb16); + } + if(aSecureDb) + { + const TUint8* mapKey = NULL; + //Load database security policy, update the security policy map + UpdateSecurityMapL(EFalse, aFileData, mapKey, iSecurityPolicy); + mapKey = NULL;//it is not used + //Check that the caller has at least one of {Schema, Read, Write} policies. + BasicSecurityPolicyCheckL(*iSecurityPolicy); + } + //Install user-defined functions. + InstallUDFsL(); + + //Install the authorizer. + InstallAuthorizerL(); + + SQLPROFILER_DB_OPEN((TUint)iDbHandle, aFileData.FileName()); + } + +/* +Implementation of the like() SQL function. This function implements +the user defined LIKE operator. The first argument to the function is the +pattern and the second argument is the string. So, the SQL statements: +A LIKE B +is implemented as like(B, A). + +@param aContext Function call context; +@param aArgc Number of LIKE arguments: 2 for the standard LIKE operator, 3 for the LIKE operator with an ESCAPE clause; +@param aArgv LIKE arguments; + +@internalComponent +*/ +void CSqlSrvDatabase::LikeSqlFunc(sqlite3_context* aContext, int aArgc, sqlite3_value** aArgv) + { + TUint escapeChar = 0; + if(aArgc == 3) + { + //The escape character string must consist of a single UTF16 character. + //Otherwise, return an error. + const TUint16* esc = static_cast (sqlite3_value_text16(aArgv[2])); + if(!esc) + { + sqlite3_result_error(aContext, KErrMsg1, -1); + return; + } + if(User::StringLength(esc) != 1) + { + sqlite3_result_error(aContext, KErrMsg2, -1); + return; + } + escapeChar = *esc; + } + const TUint16* pattern = static_cast (sqlite3_value_text16(aArgv[0])); + const TUint16* candidate = static_cast (sqlite3_value_text16(aArgv[1])); + if(pattern && candidate) + { + TInt wildChar = '_'; + TInt wildSeqChar = '%'; + TPtrC16 patternStr(pattern, (TUint)sqlite3_value_bytes16(aArgv[0]) / sizeof(TUint16)); + TPtrC16 candidateStr(candidate, (TUint)sqlite3_value_bytes16(aArgv[1]) / sizeof(TUint16)); + TInt res = candidateStr.MatchC(patternStr, wildChar, wildSeqChar, escapeChar, 0/*collation level*/); + sqlite3_result_int(aContext, res >= 0); + //RDebug::Print(_L("--res=%d, pattern=%S, candidate=%S\r\n"), res, &patternStr, &candidateStr); + } + } +