// 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 <f32file.h>
#include <f32file64.h>
#include <utf.h> //CnvUtfConverter
#include <sqldb.h> //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 <sqlite3_stmt*> (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 <sqlite3*> (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 <const TUint16*> (ptr));
if(src.Length() > aCollationDllName.MaxLength())
{
__SQLLEAVE(KErrOverflow);
}
aCollationDllName.Copy(src);
}
else
{
__SQLLEAVE(KErrGeneral);
}
}
if(aSettingsVersion > ESqlSystemVersion3)
{
aCompactionMode = static_cast <TSqlCompactionMode> (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<sizeof(KUpdateFileVersionSettingsSql) + KMaxFileName + 10> 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 <const TUint16*> (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 <const TUint8*> (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 <RSqlSecurityPolicy::TPolicyType> (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<RSqlSecurityPolicy::TObjectType> (aObjType),
aObjName, static_cast <RSqlSecurityPolicy::TPolicyType> (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 '%<CollationName>%' 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;
}