diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/SRC/Server/SqlSrvDbSysSettings.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sql/SRC/Server/SqlSrvDbSysSettings.cpp Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,1003 @@ +// Copyright (c) 2006-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: +// Store/Load database system settings (database settings, security policies) +// +// + +#include +#include +#include //CnvUtfConverter +#include //RSqlSecurityPolicy enum items +#include "SqlSrvDbSysSettings.h" +#include "SqlUtil.h" //Sql2OsErrCode() +#include "sqlite3.h" //SQLITE API +#include "SqliteSymbian.h" //sqlite3SymbianLastOsError() +#include "SqlSecurityImpl.h" //CSqlSecurityPolicy +#include "SqlSrvStatementUtil.h"//Global SQL execution functions +#include "SqlSrvCollation.h" //TSqlCollate +#include "SqlSrvStrings.h" //System table names +#include "SqlSrvUtil.h" //Global functions +#include "SqlSrvFileData.h" //TSqlSrvFileData + +extern TBool IsStatementSupported(const TDesC& aStatementIn, const TDesC& aDbName, TDes& aStatementOut); + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Local const data /////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +enum + { + ESqlSystemVersion1 = 1, //Base version + ESqlSystemVersion2 = 2, //New field into "Settings" table - "Reserved" (unused, set to 0) + ESqlSystemVersion3 = 3, //New field into "Settings" table - "CollationDllName" + //Existing field in "Settings" table - "Reserved" - now used to store processed version of db config file + ESqlSystemVersion4 = 4 //"CompactionMode" field added + }; + +//Current version of the system settings - reflects changes in the system tables. +const TInt KSqlSystemVersion = ESqlSystemVersion4; + +//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(KCreateSettingsSql,"CREATE TABLE \"%S\".symbian_settings(Id INTEGER,Reserved INTEGER,CollationDllName TEXT, CompactionMode INTEGER);\x0"); +_LIT(KDropSettingsSql, "DROP TABLE IF EXISTS \"%S\".symbian_settings;\x0"); +_LIT(KInsertSettingsSql, "INSERT INTO \"%S\".symbian_settings VALUES(%d, %d,'%S',%d);\x0"); +_LIT(KUpdateCollationSettingsSql, "UPDATE \"%S\".symbian_settings SET CollationDllName='%S';\x0"); +_LIT(KUpdateFileVersionSettingsSql, "UPDATE \"%S\".symbian_settings SET Reserved=%d;\x0"); +_LIT(KGetSettingsSql, "SELECT * FROM \"%S\".symbian_settings;\x0"); +_LIT(KGetIndexSql, "SELECT name FROM \"%S\".sqlite_master WHERE type = 'index' AND sql LIKE '%%%S%%'\x0"); +_LIT(KSettingsTableCheckSql, "SELECT name from \"%S\".sqlite_master WHERE type = 'table' AND name = 'symbian_settings';\x0"); +_LIT(KReindexSql, "REINDEX \"%S\";\x0"); + +_LIT8(KCreateSecuritySql,"CREATE TABLE symbian_security(Id INTEGER PRIMARY KEY AUTOINCREMENT,ObjectType INTEGER,ObjectName TEXT,PolicyType INTEGER,PolicyData BLOB);\x0"); +_LIT8(KInsertSecuritySql, "INSERT INTO symbian_security(ObjectType,ObjectName,PolicyType,PolicyData) VALUES(:V1,:V2,:V3,:V4);\x0"); +_LIT8(KGetSecuritySql, "SELECT * FROM symbian_security;\x0"); + +_LIT8(KBeginTransactionSql, "BEGIN;\x0"); +_LIT8(KCommitTransactionSql, "COMMIT;\x0"); +_LIT8(KRollbackTransactionSql, "ROLLBACK;\x0"); + + + +//KInsertSecuritySql statement - parameter indices +const TInt KObjTypePrmIdx = 1; +const TInt KObjNamePrmIdx = 2; +const TInt KObjPolicyTypePrmIdx = 3; +const TInt KObjPolicyDataPrmIdx = 4; + +//Default security policy - object type code and policy type code +const TInt KDefaultObjType = -2; +const TInt KDefaultPolicyType = -1; + +//Database security policy - object type code +const TInt KDbObjType = -1; + +//KGetSettingsSql sql statement - column indices +const TInt KSysVersionColIdx = 0; +const TInt KConfigFileVersionColIdx = 1; +const TInt KCollationDllNameColIdx = 2; +const TInt KCompactionModeColIdx = 3; + +//KGetSecuritySql sql statement - column indices +const TInt KObjTypeColIdx = 1; +const TInt KObjNameColIdx = 2; +const TInt KObjPolicyTypeColIdx = 3; +const TInt KObjPolicyDataColIdx = 4; + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// Local functions //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +//Finalzes the statement handle (aHandle argument). Used in stack cleanup operations. +//Panic SqlDb 4 In _DEBUG mode if aHandle argument is NULL. +static void FinalizeStatementHandle(void* aHandle) + { + __SQLASSERT(aHandle != NULL, ESqlPanicBadArgument); + sqlite3_stmt* stmtHandle = static_cast (aHandle); + (void)sqlite3_finalize(stmtHandle); + } + +//Transaction rollback. Used in stack cleanup operations. +//Panic SqlDb 4 In _DEBUG mode if aDbHandle argument is NULL. +static void RollbackTransaction(void* aDbHandle) + { + __SQLASSERT(aDbHandle != NULL, ESqlPanicBadArgument); + (void)::DbExecStmt8(reinterpret_cast (aDbHandle), KRollbackTransactionSql); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////// TSqlDbSysSettings //////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +Initializes TSqlDbSysSettings data members with default values. + +@param aDbHandle Database handle. TSqlDbSysSettings does not own aDbHandle argument. + +@panic SqlDb 4 In _DEBUG mode if aDbHandle argument is NULL. +*/ +TSqlDbSysSettings::TSqlDbSysSettings(sqlite3* aDbHandle) : + iDbHandle(aDbHandle) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicBadArgument); + } + +/** +Creates the database security policy table and stores the security policy in the table. + +@param aSecurityPolicy Database security policies container, which data needs to be persisted. + +@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. +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::StoreSecurityPolicyL(const CSqlSecurityPolicy& aSecurityPolicyCon) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KBeginTransactionSql())); + CleanupStack::PushL(TCleanupItem(&RollbackTransaction, iDbHandle)); + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KCreateSecuritySql())); + StoreSecurityPoliciesL(aSecurityPolicyCon); + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KCommitTransactionSql())); + CleanupStack::Pop();//TCleanupItem(&RollbackTransaction, iDbHandle) + } + +/** +Stores the database system settings in the settings table. The settings table is recreated. + +@param aDbName Logical database name: "main" for the main database or attached database name, +@param aCollationDllName Collation dll name. It uniquely identifies the current collation method in use. + If the default collation method changes later then the database will be reindexed + and the new collation dll name will replace the existing one in the settings table. +@param aDbConfigFileVersion Current config file version or KSqlNullDbConfigFileVersion +@param aCompactionMode Database compaction mode, one of TSqlCompactionMode enum item values (except ESqlCompactionNotSet) + +@see TSqlCompactionMode + +@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. +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +@panic SqlDb 4 In _DEBUG mode if aCompactionMode parameter value is invalid. +*/ +void TSqlDbSysSettings::StoreSettingsL(const TDesC& aDbName, const TDesC& aCollationDllName, TInt aDbConfigFileVersion, TSqlCompactionMode aCompactionMode) + { + __SQLASSERT(aCompactionMode == ESqlCompactionManual || aCompactionMode == ESqlCompactionBackground || aCompactionMode == ESqlCompactionAuto, ESqlPanicBadArgument); + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + HBufC* buf = HBufC::NewLC(Max((TInt)sizeof(KDropSettingsSql), + Max((TInt)sizeof(KCreateSettingsSql), (TInt)sizeof(KInsertSettingsSql))) + + aDbName.Length() + aCollationDllName.Length() + 10); + TPtr sql = buf->Des(); + //Begin transaction + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KBeginTransactionSql())); + CleanupStack::PushL(TCleanupItem(&RollbackTransaction, iDbHandle)); + //Recreate the "settings" table and store the default collation there (the DLL name) + sql.Format(KDropSettingsSql(), &aDbName); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, sql)); + sql.Format(KCreateSettingsSql, &aDbName); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, sql)); + sql.Format(KInsertSettingsSql(), &aDbName, KSqlSystemVersion, aDbConfigFileVersion, &aCollationDllName, aCompactionMode); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, sql)); + //Commit transaction + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KCommitTransactionSql())); + CleanupStack::Pop();//TCleanupItem(&RollbackTransaction, iDbHandle) + CleanupStack::PopAndDestroy(buf); + } + +/** +Read security policies from security policy table. + +The method does not guarantee that either the security policies will be read from the +security table and stored in aSecurityPolicy parameter or aSecurityPolicy argument +stays unchanged in a case of failure. + +@param aSecurityPolicyCon Security policies container which needs to be initializeed with + the database security policies. + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrGeneral, missing or invalid data in the system tables; + Note that the function may also leave with some other database specific + errors categorised as ESqlDbError, and other system-wide error codes. +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::LoadSecurityPolicyL(CSqlSecurityPolicy& aSecurityPolicyCon) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + //Even if the version of the system settings is bigger than the current one (KSqlSystemVersion constant), + //I think that all future modifications of the system tables shall not affect the already existing + //fields. So it is correct to think that all information available in version 1 should be available + //(and accessible) in all future versions of the system settings. + //Note: no attempt shall be made here to modify the system tables structure! There may be more than one + // connection to the database being processed! + // + //Prepare statement handle + sqlite3_stmt* stmtHandle = ::StmtPrepare8L(iDbHandle, KGetSecuritySql()); + CleanupStack::PushL(TCleanupItem(&FinalizeStatementHandle, stmtHandle)); + //Read the security policies + TBool defaultPolicySet = EFalse; + TInt dbPolicySetFlag = 0; + TInt err; + while((err = ::StmtNext(stmtHandle)) == KSqlAtRow) + { + TInt objType = KDefaultObjType - 1; + TPtrC objName(KNullDesC); + TInt policyType = RSqlSecurityPolicy::ESchemaPolicy - 1; + TSecurityPolicy policy = ReadCurrSecurityPolicyL(stmtHandle, objType, objName, policyType); + switch(objType) + { + case KDefaultObjType: + if(defaultPolicySet) + { + __SQLLEAVE(KErrGeneral);//two "default policy" records in the table + } + StoreDefaultSecurityPolicyL(aSecurityPolicyCon, policy, dbPolicySetFlag); + defaultPolicySet = ETrue; + break; + case KDbObjType: + StoreDbSecurityPolicyL(aSecurityPolicyCon, policyType, policy, dbPolicySetFlag); + break; + case RSqlSecurityPolicy::ETable: + StoreDbObjSecurityPolicyL(aSecurityPolicyCon, objType, objName, policyType, policy); + break; + default: + __SQLLEAVE(KErrGeneral); + break; + }//end of "switch(aObjType)" + } + CleanupStack::PopAndDestroy();//cleanupItem (statement handle) + __SQLLEAVE_IF_ERROR(err); + if(!defaultPolicySet) + { + __SQLLEAVE(KErrGeneral);//no default policy + } + } + +/** +Loads the database system settings from the settings table. +If the settings table does not exist then it is created with the default settings +(the stored collation dll name will be empty and the stored database configuration +file version will be 0). + +@param aDbName Logical database name: "main" for the main database or attached database name, +@param aCollationDllName Output parameter, will contain the stored collation dll name, +@param aDbConfigFileVersion Output parameter, will contain the stored database config file version. +@param aCompactionMode Output parameter. Database compaction mode (one of TSqlCompactionMode enum item values except ESqlCompactionNotSet). + Note that the input value of this parameter might be ESqlCompactionManual if the this is a legacy database, + created not by the SQL server. + +@see TSqlCompactionMode + +@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.. +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +@panic SqlDb 7 In _DEBUG mode if the stored compaction mode is invalid. +*/ +void TSqlDbSysSettings::LoadSettingsL(const TDesC& aDbName, TDes& aCollationDllName, TInt& aDbConfigFileVersion, TSqlCompactionMode& aCompactionMode) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + aCollationDllName.Zero(); + aDbConfigFileVersion = KSqlNullDbConfigFileVersion; + if(aCompactionMode == ESqlCompactionNotSet) + { + aCompactionMode = KSqlDefaultCompactionMode; + } + + //If the system settings table does not exist then create it now. + //For a database created by the SQL Server this will only occur + //when the database is being created in an application's private data cage - + //as part of this create call it is now being opened by the server. + //An externally created database is likely to not contain the settings table + //and adding it here makes the database 'SQL Server-compatible' + if(!SettingsTableExistsL(aDbName)) + { + StoreSettingsL(aDbName, aCollationDllName, KSqlNullDbConfigFileVersion, aCompactionMode); // store empty collation dll name, then reindexing will occur + } + else + { + //Get the settings from the existing table + TInt settingsVersion = 0; + GetSettingsL(aDbName, aCollationDllName, aDbConfigFileVersion, settingsVersion, aCompactionMode); + if(settingsVersion < KSqlSystemVersion) + { + //Recreate the settings table using the last version number format (this is what the old code did during reindexing) + StoreSettingsL(aDbName, aCollationDllName, aDbConfigFileVersion, aCompactionMode); // store empty collation dll name, then reindexing will occur + } + } + __SQLASSERT(aCompactionMode == ESqlCompactionManual || aCompactionMode == ESqlCompactionBackground || aCompactionMode == ESqlCompactionAuto, ESqlPanicInternalError); + } + +/** +Retrieves the database system settings from the settings table. + +@param aDbName Logical database name: "main" for the main database or attached database name, +@param aCollationDllName Output parameter, will contain the stored collation dll name, +@param aDbConfigFileVersion Output parameter, will contain the stored database config file version, +@param aSettingsVersion Output parameter, will contain the version of the settings table. +@param aCompactionMode Output parameter. Database compaction mode (one of TSqlCompactionMode enum item values except ESqlCompactionNotSet). + +@see TSqlCompactionMode + +@leave KErrGeneral, either unable to retrieve the data from the settings table or the + stored table version or config file version is invalid or the stored compaction mode is invalid. + KErrOverflow, aCollationDllName is not large enough to store the name of the + collation dll that is stored in the settings table. + Note that the function may also leave with other system-wide error codes or SQL + errors of ESqlDbError type +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::GetSettingsL(const TDesC& aDbName, TDes& aCollationDllName, TInt& aDbConfigFileVersion, + TInt& aSettingsVersion, TSqlCompactionMode& aCompactionMode) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + HBufC* buf = HBufC::NewLC(sizeof(KGetSettingsSql) + aDbName.Length()); + TPtr sql = buf->Des(); + + //Prepare statement handle + sql.Format(KGetSettingsSql(), &aDbName); + sqlite3_stmt* stmtHandle = ::StmtPrepare16L(iDbHandle, sql); + CleanupStack::PushL(TCleanupItem(&FinalizeStatementHandle, stmtHandle)); + //Move to the first record + TInt err = ::StmtNext(stmtHandle); + __SQLLEAVE_IF_ERROR(err); + //Check that it is a valid row + if(err != KSqlAtRow) + { + __SQLLEAVE(KErrGeneral); + } + //Get the system settings version + aSettingsVersion = sqlite3_column_int(stmtHandle, KSysVersionColIdx); + if(aSettingsVersion < ESqlSystemVersion1) + { + __SQLLEAVE(KErrGeneral); + } + if(aSettingsVersion > ESqlSystemVersion2) + { + //The "ConfigFileVersion" column exists and is used to store the most recent + //version of the database's config file (if there is one) that has + //been successfully processed + aDbConfigFileVersion = sqlite3_column_int(stmtHandle, KConfigFileVersionColIdx); + if(aDbConfigFileVersion < KSqlNullDbConfigFileVersion) + { + __SQLLEAVE(KErrGeneral); + } + + //The "CollationDllName" column exists and its value can be read + const void* ptr = sqlite3_column_text16(stmtHandle, KCollationDllNameColIdx); + if(ptr) + { + TPtrC16 src(reinterpret_cast (ptr)); + if(src.Length() > aCollationDllName.MaxLength()) + { + __SQLLEAVE(KErrOverflow); + } + aCollationDllName.Copy(src); + } + else + { + __SQLLEAVE(KErrGeneral); + } + } + if(aSettingsVersion > ESqlSystemVersion3) + { + aCompactionMode = static_cast (sqlite3_column_int(stmtHandle, KCompactionModeColIdx)); + if(aCompactionMode != ESqlCompactionManual && aCompactionMode != ESqlCompactionBackground && aCompactionMode != ESqlCompactionAuto) + { + __SQLLEAVE(KErrGeneral); + } + } + CleanupStack::PopAndDestroy();//TCleanupItem(&FinalizeStatementHandle, stmtHandle) + CleanupStack::PopAndDestroy(buf); + } + +/** +Deletes and recreates all database indexes which use user-defined collation methods, from scratch. +Also updates the database settings table with the name of the current collation dll. +This is all performed as a single atomic transaction. + +@param aDbName Logical database name: "main" for the main database or attached database name, +@param aCurrentCollationDllName The name of the current collation dll. + +@leave The function may leave with system-wide error codes or SQL errors of ESqlDbError type + +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::ReindexDatabaseL(const TDesC& aDbName, const TDesC& aCurrentCollationDllName) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + //Allocate memory for the SQL statements + HBufC* buf = HBufC::NewLC(Max((TInt)sizeof(KUpdateCollationSettingsSql), (TInt)sizeof(KReindexSql)) + + aDbName.Length() + aCurrentCollationDllName.Length()); + TPtr sql = buf->Des(); + + //Begin transaction + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KBeginTransactionSql())); + CleanupStack::PushL(TCleanupItem(&RollbackTransaction, iDbHandle)); + + const TSqlCollationUtil collationUtil(iDbHandle); + //Issue a "REINDEX" command for each collation that has at least one index using it + for(TInt i = collationUtil.CollationCount()-1; i >= 0 ;--i) + { + TPtrC collationName = collationUtil.CollationName(i); + if(IndexExistsL(aDbName, collationName)) + { + sql.Format(KReindexSql(), &collationName); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, sql)); + } + } + + //Update the settings table to store the current collation DLL name + sql.Format(KUpdateCollationSettingsSql(), &aDbName, &aCurrentCollationDllName); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, sql)); + + //Commit transaction + __SQLLEAVE_IF_ERROR(::DbExecStmt8(iDbHandle, KCommitTransactionSql())); + CleanupStack::Pop(); //TCleanupItem(&RollbackTransaction, iDbHandle) + CleanupStack::PopAndDestroy(buf); + } + +/** +Performs any necessary configuration file updates to the database if a +configuration file exists for the database that has not yet been processed. +If a configuration file is processed then the database settings table will +be updated with the version of the configuration file that was processed. + +@param aStoredDbConfigFileVersion The configuration file version that is + currently stored in the settings table +@param aFileData The database file data + +@param aDbName Logical database name: "main" for the main database or attached database name + +@leave One of the system-wide error codes or SQL errors of ESqlDbError type + +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object) +*/ +void TSqlDbSysSettings::ConfigureDatabaseL(TInt aStoredDbConfigFileVersion, const TSqlSrvFileData& aFileData, + const TDesC& aDbName) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + if(!aFileData.IsSecureFileNameFmt()) + { + //As a first implementation, config files will only be supported for + //shared, secure databases - not for private, secure databases or public databases + return; + } + + //Determine whether there is a configuration file + //for this database that has not yet been processed + const CDbConfigFiles* dbConfigFiles = aFileData.DbConfigFiles(); + if(dbConfigFiles) + { + //Get the database file name and extension to search for a corresponding config file + TPtrC dbName = aFileData.FileName(); + TParse parse; + __SQLLEAVE_IF_ERROR(parse.Set(dbName, NULL, NULL)); + TPtrC dbFileNameAndExt = parse.NameAndExt(); + HBufC* matchingConfigFile = dbConfigFiles->FindConfigFile(dbFileNameAndExt); + if(matchingConfigFile) + { + //There exists a config file for this database - now check whether its + //version is greater than the version stored in the settings table and + //only process the file if it is + TFileName configFileName; + configFileName.Copy(matchingConfigFile->Des()); + TInt offset = configFileName.LocateReverse('.') ; + if(KErrNotFound != offset) + { + TInt lengthOfExt = configFileName.Length() - offset - 1; + TPtrC versionNum = configFileName.Right(lengthOfExt); + TLex lex(versionNum); + TInt fileVersion = 0; + if(KErrNone == lex.Val(fileVersion)) + { + if(fileVersion > aStoredDbConfigFileVersion) + { + //The latest version of the configuration file has not yet been processed, so do it now + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ConfigureDatabaseL() - Processing config file %S"), configFileName); + ExecuteConfigurationUpdateL(aFileData, configFileName, fileVersion, aDbName); + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ConfigureDatabaseL() - SUCCESS! Config file %S was processed"), configFileName); + } + else + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ConfigureDatabaseL() - Not processing config file %S as this or a later version has already been processed"), configFileName); + } + } + else + { + //Invalid config file name extension - it cannot be converted to an integer + __SQLLEAVE(KErrCorrupt); + } + } + else + { + //Invalid config file name - it doesn't contain the character '.' + __SQLLEAVE(KErrCorrupt); + } + } + else + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ConfigureDatabaseL() - No config file found for database %S"), dbFileNameAndExt); + } + } + } + +/** +Executes the supported operations specified in the given database +configuration file and updates the settings table to store the +version of this configuration file. + +@param aFileData The database file data +@param aMatchingConfigFile The configuration file that is to be processed +@param aDbConfigFileVersion The configuration file version +@param aDbName Logical database name: "main" for the main database or attached database name + +@leave KErrNoMemory, if an out of memory condition occurs. + One of the other system-wide error codes if the configuration + file fails to be opened or read. + One of the SQL errors of ESqlDbError type if the update to the + database settings table fails + +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::ExecuteConfigurationUpdateL(const TSqlSrvFileData& aFileData, + const TDesC& aMatchingConfigFile, + TInt aDbConfigFileVersion, + const TDesC& aDbName) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + //Execute the specified database config file operations that are supported +#ifdef SYSLIBS_TEST + TDriveUnit drive = EDriveC; +#else + TDriveUnit drive = EDriveZ; +#endif + TFileName configFilePath; + TDriveName drvName = drive.Name(); + configFilePath.Append(drvName); + configFilePath.Append(aFileData.PrivatePath()); + configFilePath.Append(aMatchingConfigFile); + //If this method leaves then either the config file could not be + //opened or read or an out of memory condition occured. Either way + //another attempt will be made to process the config file when the + //database is next opened + DoExecuteDbConfigFileOpsL(aFileData.Fs(), configFilePath, aDbName); + + //Now update the settings table to store the current version of the database config file. + //If this fails then another attempt will be made to process the config file and update + //the settings table when the database is next opened + TBuf buf; + buf.Format(KUpdateFileVersionSettingsSql(), &aDbName, aDbConfigFileVersion); + __SQLLEAVE_IF_ERROR(::DbExecStmt16(iDbHandle, buf)); + } + +/** +Opens the specified database configuration file and executes those operations that +are specified in it which are currently supported (currently only 'CREATE INDEX' +operations are supported and will be executed). + +@param aFs The server's file server session +@param aConfigFilePath The configuration file path +@param aDbName Logical database name: "main" for the main database or attached database name + +@leave KErrNoMemory, if an out of memory condition occurs. + One of the other system-wide error codes if the configuration + file fails to be opened or read + +@panic SqlDb 2 In _DEBUG mode if iDbHandle is NULL (uninitialized TSqlDbSysSettings object). +*/ +void TSqlDbSysSettings::DoExecuteDbConfigFileOpsL(RFs& aFs, const TDesC& aConfigFilePath, const TDesC& aDbName) + { + __SQLASSERT(iDbHandle != NULL, ESqlPanicInvalidObj); + + //Open the config file and read it into a buffer + RFile64 file; + __SQLLEAVE_IF_ERROR(file.Open(aFs, aConfigFilePath, EFileRead)); + CleanupClosePushL(file); + TInt64 size = 0; + __SQLLEAVE_IF_ERROR(file.Size(size)); + if(size == 0) + { + //Config file is empty so just return + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::DoExecuteDbConfigFileOpsL() - Config file %S is empty"), aConfigFilePath); + CleanupStack::PopAndDestroy(); // file + return; + } + HBufC8* buffer = HBufC8::NewLC(size); + TPtr8 bufPtr = buffer->Des(); + __SQLLEAVE_IF_ERROR(file.Read(bufPtr)); + + //Convert buffer to Unicode for processing (the configuration file is expected to be ASCII or UTF-8) + HBufC16* buf16 = CnvUtfConverter::ConvertToUnicodeFromUtf8L(bufPtr); + CleanupStack::PushL(buf16); + TPtrC16 ptr16 = buf16->Des(); + + //Skip the BOM (byte ordering mark) at the start if there is one + if((ptr16.Locate(TChar(0xFEFF)) == 0) || (ptr16.Locate(TChar(0xFFFE)) == 0)) + { + ptr16.Set(ptr16.Mid(1)); + } + + //Parse the file contents and execute the specified + //config statements that are supported + ParseFileL(ptr16, aDbName); + + CleanupStack::PopAndDestroy(3); // buf16, buffer, file + } + +/** +Parses the configuration file buffer, reading each SQL statement +and processing it. + +@param aBuffer Buffer containing the configuration file contents +@param aDbName Logical database name: "main" for the main database or attached database name + +@leave KErrNoMemory, if an out of memory condition occurs +*/ +void TSqlDbSysSettings::ParseFileL(const TDesC& aBuffer, const TDesC& aDbName) + { + TLex fileParser(aBuffer); + //While not end of file + while (!fileParser.Eos()) + { + //Get the next of the SQL statements, which are seperated by semicolons + fileParser.SkipSpaceAndMark(); // skip any preceding whitespace before the next statement + while ((!fileParser.Eos()) && (fileParser.Peek() != ';')) + { + fileParser.Inc(); // continue to next character + } + TPtrC stmt = fileParser.MarkedToken(); // extract the marked token + fileParser.Inc(); // to skip the terminating ';' for next iteration + + //Process this statement + ProcessStatementL(stmt, aDbName); + } + } + +/** +Process an SQL statement contained in the configuration file +and executes it if it is a supported statement. +Currently only 'CREATE INDEX' statements are supported. +The statement can contain comments that are understood by SQLite +- these can be in the form of an SQL comment or a 'C' comment, +as defined at http://www.sqlite.org/lang_comment.html. + +@param aStmt An SQL statement string, stripped of any whitespace + at the beginning of it +@param aDbName Logical database name: "main" for the main database or attached database name + +@leave KErrNoMemory, if an out of memory condition occurs +*/ +void TSqlDbSysSettings::ProcessStatementL(const TDesC& aStmt, const TDesC& aDbName) + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Processing statement '%S'"), aStmt); + + //If the statement only contained whitespace then just return + if(aStmt.Length() == 0) + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Statement '%S' only contains whitespace - statement will be ignored"), aStmt); + return; + } + + //Check that the statement does not contain an unsupported comment style + if(KErrNotFound != aStmt.Find(_L("//"))) + { + //The statement contains '//' which is an unsupported comment style, but rather + //than leave here and cause the full file to fail, we just ignore this statement + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Statement '%S' contains invalid comment style - statement will be ignored"), aStmt); + return; + } + + HBufC* stmtBuf = HBufC::NewLC(aStmt.Length() + 1 + aDbName.Length() + 1); + TPtr stmtPtr = stmtBuf->Des(); + //If the 'aStmt' string is an SQL statement that is supported then execute it + if(::IsStatementSupported(aStmt, aDbName, stmtPtr)) + { + TInt err = ::DbExecStmt16(iDbHandle, stmtPtr); + if(KErrNone == err) + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Successfully executed statement '%S'"), aStmt); + } + else + { + __SQLLOG_ERR(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Failed to execute the statement, err=%d"), err); + if(err == KErrNoMemory) + { + __SQLLEAVE(err); + } + } + } + else + { + __SQLLOG_STRING(_L("SQLLOG: TSqlDbSysSettings::ProcessStatementL() - Non-supported statement, will be ignored - '%S'"), aStmt); + } + CleanupStack::PopAndDestroy(); // stmtBuf + } + + +/** +Stores the security policies into the security policies table. + +@param aSecurityPolicyCon Security policies container, which data needs to be persisted. + +@leave KErrNoMemory, an out of memory condition has occurred; + KErrArgument, system table name found in the list of the tables, which need special protection; + 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 aSecurityPolicy argument is NULL. +*/ +void TSqlDbSysSettings::StoreSecurityPoliciesL(const CSqlSecurityPolicy& aSecurityPolicyCon) + { + //Prepare the INSERT sql statement + sqlite3_stmt* stmtHandle = ::StmtPrepare8L(iDbHandle, KInsertSecuritySql()); + CleanupStack::PushL(TCleanupItem(&FinalizeStatementHandle, stmtHandle)); + //Store default security policy into the table + StoreSecurityPolicyL(stmtHandle, KDefaultObjType, KNullDesC, KDefaultPolicyType, aSecurityPolicyCon.DefaultPolicy()); + //Store database security policies into the table + StoreSecurityPolicyL(stmtHandle, KDbObjType, KNullDesC, RSqlSecurityPolicy::ESchemaPolicy, aSecurityPolicyCon.DbPolicy(RSqlSecurityPolicy::ESchemaPolicy)); + StoreSecurityPolicyL(stmtHandle, KDbObjType, KNullDesC, RSqlSecurityPolicy::EReadPolicy, aSecurityPolicyCon.DbPolicy(RSqlSecurityPolicy::EReadPolicy)); + StoreSecurityPolicyL(stmtHandle, KDbObjType, KNullDesC, RSqlSecurityPolicy::EWritePolicy, aSecurityPolicyCon.DbPolicy(RSqlSecurityPolicy::EWritePolicy)); + //Store database objects security policies into the table + TSqlSecurityPolicyIterator it(aSecurityPolicyCon); + RSqlSecurityPolicy::TObjectType objectType; + TPtrC objectName; + RSqlSecurityPolicy::TPolicyType policyType; + TSecurityPolicy policy; + while(it.Next(objectType, objectName, policyType, policy)) + { + if(objectType == RSqlSecurityPolicy::ETable && ::IsSystemTableName(objectName)) + { + //Clients are not allowed to put system table names in the RSqlSecurityPolicy container. + __SQLLEAVE(KErrArgument); + } + StoreSecurityPolicyL(stmtHandle, objectType, objectName, policyType, policy); + } + CleanupStack::PopAndDestroy();//cleanupItem (statement handle) + } + +/** +Stores a security policy object in the security policies table. + +@param aStmtHandle Statement handle. +@param aObjType Database object type: default security policies, database security policies, table security policies +@param aObjName Database object name. It is expected to be a non-empty string only for tables. +@param aPolicyType Security policy type: schema, write, read. +@param aPolicy Security policy object. + +@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. + +@panic SqlDb 4 In _DEBUG mode if aHandle argument is NULL. +*/ +void TSqlDbSysSettings::StoreSecurityPolicyL(sqlite3_stmt* aStmtHandle, TInt aObjType, const TDesC& aObjName, + TInt aPolicyType, const TSecurityPolicy& aPolicy) + { + __SQLASSERT(aStmtHandle != NULL, ESqlPanicBadArgument); + __SQLLEAVE_IF_ERROR(::StmtReset(aStmtHandle)); + __SQLLEAVE_IF_ERROR(BindSecurityPolicyPrm(aStmtHandle, aObjType, aObjName, aPolicyType, aPolicy)); + __SQLLEAVE_IF_ERROR(::StmtExec(aStmtHandle)); + } + +/** +Binds the parameter values for the insert SQL statement for security policies table. + +@param aStmtHandle Statement handle. +@param aObjType Database object type: default security policy, database security policies, table security policies +@param aObjName Database object name. It is expected to be a non-empty string only for tables. +@param aPolicyType Security policy type: schema, write, read. +@param aPolicy Security policy object. + +@see TSqlDbSysSettings::StoreSecurityPolicyL() + +@panic SqlDb 4 In _DEBUG mode if aHandle argument is NULL. +*/ +TInt TSqlDbSysSettings::BindSecurityPolicyPrm(sqlite3_stmt* aStmtHandle, TInt aObjType, const TDesC& aObjName, + TInt aPolicyType, const TSecurityPolicy& aPolicy) + { + (void)sqlite3SymbianLastOsError();//clear last OS error + TInt err = sqlite3_bind_int(aStmtHandle, KObjTypePrmIdx, aObjType); + if(err == SQLITE_OK) + { + err = sqlite3_bind_text16(aStmtHandle, KObjNamePrmIdx, aObjName.Ptr(), aObjName.Length() * sizeof(TText), SQLITE_STATIC); + if(err == SQLITE_OK) + { + err = sqlite3_bind_int(aStmtHandle, KObjPolicyTypePrmIdx, aPolicyType); + if(err == SQLITE_OK) + { + TPtrC8 policyData(aPolicy.Package()); + err = sqlite3_bind_blob(aStmtHandle, KObjPolicyDataPrmIdx, policyData.Ptr(), policyData.Length(), SQLITE_STATIC); + } + } + } + return ::Sql2OsErrCode(err, sqlite3SymbianLastOsError()); + } + +/** +Reads a record from security policies table. + +@param aStmtHandle Statement handle. It cannot be NULL. +@param aSecurityPolicy Security policies container. +@param aObjType Output parameter, will be initialized with the database object type: KDefaultObjType, + KDbObjType, RSqlSecurityPolicy::ETable. +@param aObjName Output parameter, database object name (for example, table name), which is protected by the + current security policy. +@param aPolicyType Output parameter, will be initialized with the database policy type: RSqlSecurityPolicy::EReadPolicy, + RSqlSecurityPolicy::EWritePolicy, RSqlSecurityPolicy::ESchemaPolicy. +@return The created security policy object. + +@leave KErrGeneral, invalid security policy data; + KErrNoMemory, Out of memory. +*/ +TSecurityPolicy TSqlDbSysSettings::ReadCurrSecurityPolicyL(sqlite3_stmt* aStmtHandle, TInt& aObjType, + TPtrC& aObjName, TInt& aPolicyType) + { + __SQLASSERT(aStmtHandle != NULL, ESqlPanicBadArgument); + aObjType = sqlite3_column_int(aStmtHandle, KObjTypeColIdx); + TInt len = (TUint)sqlite3_column_bytes16(aStmtHandle, KObjNameColIdx) / sizeof(TUint16); + const void* text = sqlite3_column_text16(aStmtHandle, KObjNameColIdx); + aObjName.Set(reinterpret_cast (text), len); + aPolicyType = sqlite3_column_int(aStmtHandle, KObjPolicyTypeColIdx); + len = sqlite3_column_bytes(aStmtHandle, KObjPolicyDataColIdx); + if(len != sizeof(TSecurityPolicy)) + { + //Check if the error is "out of memory" (which may happen when retrieving text column data + //and the column encoding is different, in which case the column text has to be converted + //and a new block of memory has to be allocated for the conversion). + TInt err2 = ::StmtReset(aStmtHandle); + __SQLLEAVE(err2 == KErrNoMemory ? KErrNoMemory : KErrGeneral); + } + const void* data = sqlite3_column_blob(aStmtHandle, KObjPolicyDataColIdx); + TSecurityPolicy policy; + policy.Set(TPtrC8(reinterpret_cast (data), len)); + return policy; + } + +/** +Stores the default security policy into aSecurityPolicyCon container. +Initialises all database security policies not set yet with the default security policy. + +@param aSecurityPolicyCon Security policies container. +@param aPolicy Default security policy object +@param aDbPolicySetFlag Bit flag. Keeps information which database security policies are set and which aren't. + +@leave See CSqlSecurityPolicy::SetDbPolicy() return values. +@see CSqlSecurityPolicy::SetDbPolicy() +*/ +void TSqlDbSysSettings::StoreDefaultSecurityPolicyL(CSqlSecurityPolicy& aSecurityPolicyCon, + const TSecurityPolicy& aPolicy, TInt aDbPolicySetFlag) + { + aSecurityPolicyCon.SetDefaultPolicy(aPolicy); + if(!(aDbPolicySetFlag & (1 << RSqlSecurityPolicy::ESchemaPolicy))) + { + __SQLLEAVE_IF_ERROR(aSecurityPolicyCon.SetDbPolicy(RSqlSecurityPolicy::ESchemaPolicy, aPolicy)); + } + if(!(aDbPolicySetFlag & (1 << RSqlSecurityPolicy::EReadPolicy))) + { + __SQLLEAVE_IF_ERROR(aSecurityPolicyCon.SetDbPolicy(RSqlSecurityPolicy::EReadPolicy, aPolicy)); + } + if(!(aDbPolicySetFlag & (1 << RSqlSecurityPolicy::EWritePolicy))) + { + __SQLLEAVE_IF_ERROR(aSecurityPolicyCon.SetDbPolicy(RSqlSecurityPolicy::EWritePolicy, aPolicy)); + } + } + +/** +Stores a database security policy into aSecurityPolicyCon container. + +@param aSecurityPolicyCon Security policies container. +@param aPolicyType Database policy type: RSqlSecurityPolicy::EReadPolicy, + RSqlSecurityPolicy::EWritePolicy, RSqlSecurityPolicy::ESchemaPolicy. +@param aPolicy Database security policy object +@param aDbPolicySetFlag Bit flag. Keeps information which database security policies are set and which aren't. + The parameter value may change if some of the database policies are set by + StoreDbSecurityPolicyL(). + +@leave KErrGeneral Invalid security policy type or the corresponding database security policy has been set already. +*/ +void TSqlDbSysSettings::StoreDbSecurityPolicyL(CSqlSecurityPolicy& aSecurityPolicyCon, TInt aPolicyType, + const TSecurityPolicy& aPolicy, TInt& aDbPolicySetFlag) + { + if(aPolicyType < (TInt)RSqlSecurityPolicy::ESchemaPolicy || aPolicyType > (TInt)RSqlSecurityPolicy::EWritePolicy) + { + __SQLLEAVE(KErrGeneral); + } + if(aDbPolicySetFlag & (1 << aPolicyType)) + { + __SQLLEAVE(KErrGeneral); + } + __SQLLEAVE_IF_ERROR(aSecurityPolicyCon.SetDbPolicy(static_cast (aPolicyType), aPolicy)); + aDbPolicySetFlag |= (1 << aPolicyType); + } + +/** +Stores a database object security policy into aSecurityPolicyCon container. + +@param aSecurityPolicyCon Security policies container. +@param aObjType Database object type: RSqlSecurityPolicy::ETable. +@param aObjName Database object name (for example, table name), which is protected by the current security policy. +@param aPolicyType Database policy type: RSqlSecurityPolicy::EReadPolicy, + RSqlSecurityPolicy::EWritePolicy, RSqlSecurityPolicy::ESchemaPolicy. +@param aPolicy Database security policy object +@param aDbPolicySetFlag Bit flag. Keeps information which database security policies are set and which aren't. + The parameter value may change if some of the database policies are set by + StoreDbSecurityPolicyL(). + +@leave KErrGeneral Invalid security policy type or invalid database object name length. +*/ +void TSqlDbSysSettings::StoreDbObjSecurityPolicyL(CSqlSecurityPolicy& aSecurityPolicyCon, + TInt aObjType, const TDesC& aObjName, + TInt aPolicyType, const TSecurityPolicy& aPolicy) + { + if(aPolicyType < (TInt)RSqlSecurityPolicy::EReadPolicy || aPolicyType > (TInt)RSqlSecurityPolicy::EWritePolicy) + { + __SQLLEAVE(KErrGeneral); + } + if(aObjName.Length() < 1) + { + __SQLLEAVE(KErrGeneral); + } + __SQLLEAVE_IF_ERROR(aSecurityPolicyCon.SetPolicy(static_cast (aObjType), + aObjName, static_cast (aPolicyType), aPolicy)); + } + +/** +Returns true if settigns table exists. +(Non-secure databases prior version 3 don't have settings table) + +@param aDbName Logical database name: "main" for the main database or attached database name. +*/ +TBool TSqlDbSysSettings::SettingsTableExistsL(const TDesC& aDbName) + { + HBufC* buf = HBufC::NewLC(sizeof(KSettingsTableCheckSql) + aDbName.Length()); + TPtr sql = buf->Des(); + sql.Format(KSettingsTableCheckSql(), &aDbName); + sqlite3_stmt* stmtHandle = ::StmtPrepare16L(iDbHandle, sql); + CleanupStack::PushL(TCleanupItem(&FinalizeStatementHandle, stmtHandle)); + TInt rc = ::StmtNext(stmtHandle); + CleanupStack::PopAndDestroy();//cleanupItem (statement handle) + CleanupStack::PopAndDestroy(buf); + __SQLLEAVE_IF_ERROR(rc); + return rc == KSqlAtRow; + } + +/** +Returns true if at least one index using aCollationName exists. + +@param aDbName Logical database name: "main" for the main database or attached database name, +@param aCollationName SQLITE collation name. +*/ +TBool TSqlDbSysSettings::IndexExistsL(const TDesC& aDbName, const TDesC& aCollationName) + { + //KGetIndexSql will contain '%%' search pattern, while Symbian OS search patterns use '*' symbol by default. + //This time the '%' is OK because the serach is performed in the SQLITE master table and before installing the + //user-defined Like() function. + HBufC* buf = HBufC::NewLC(sizeof(KGetIndexSql) + aDbName.Length() + aCollationName.Length()); + TPtr sql = buf->Des(); + sql.Format(KGetIndexSql(), &aDbName, &aCollationName); + sqlite3_stmt* stmtHandle = ::StmtPrepare16L(iDbHandle, sql); + CleanupStack::PushL(TCleanupItem(&FinalizeStatementHandle, stmtHandle)); + TInt rc = ::StmtNext(stmtHandle); + CleanupStack::PopAndDestroy(2);//buf, cleanupItem (statement handle) + __SQLLEAVE_IF_ERROR(rc); + return rc == KSqlAtRow; + }