--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/installationservices/swcomponentregistry/source/database/scrdatabase.cpp Tue Aug 31 15:21:33 2010 +0300
@@ -0,0 +1,521 @@
+/*
+* Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "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:
+* Implements the SCR Data Layer API which performs all interaction with the underlying database.
+*
+*/
+
+
+/**
+ @file
+ @internalComponent
+ @released
+*/
+
+#include "scrdatabase.h"
+#include "usiflog.h"
+// Userinclude headers are used instead of the standard ones as SCR uses a custom version of SQLite
+#include "sqlite3.h"
+#include "SqliteSecure.h"
+#include <stdio.h>
+
+using namespace Usif;
+
+namespace Usif
+ {
+ class CDatabaseImplementation : public CBase
+ /**
+ The database implementation class encapsulates the implementation of CDatabase from its interface.
+ */
+ {
+ friend class CDatabase;
+ public:
+ static CDatabaseImplementation* NewL(RFile& aDatabaseFile, RFile& aJournalFile);
+ ~CDatabaseImplementation();
+ /**
+ Maps the given SQL database error into a system wide symbian specific error code.
+ @leave If the given error code does not map to KErrNone, leaves with a system wide
+ symbian error code.
+ */
+ void CheckSqlErrCodeL(TInt aErr);
+
+ /**
+ @return The extended error codes that provide more detailed information about errors.
+ */
+ TInt ErrorCode();
+
+ private:
+ /**
+ @return The English-language text that describes the most recent error.
+ */
+ TPtrC8 ErrorMessage();
+
+ private:
+ CDatabaseImplementation();
+ void ConstructorL(RFile& aDatabaseFile, RFile& aJournalFile);
+
+ private:
+ sqlite3* iDbHandle; ///< The database handle, owned by CDatabaseImplementation.
+ };
+
+
+ class CStatementImplementation : public CBase
+ /**
+ This class encapsulates the implementation details of SQL statement object
+ which contains the result sets of queries.
+ */
+ {
+ public:
+ static CStatementImplementation* NewLC(sqlite3_stmt* aStmtHandle);
+ ~CStatementImplementation();
+ inline sqlite3_stmt* Handle() const;
+
+ private:
+ CStatementImplementation(sqlite3_stmt* aStmtHandle);
+
+ private:
+ sqlite3_stmt* iStmtHandle; //SQL statement handle
+ };
+
+ }// End of namespace Usif
+
+//
+// CDatabaseImplementation
+//
+
+CDatabaseImplementation::CDatabaseImplementation()
+// Constructor
+ {
+ // empty
+ }
+
+CDatabaseImplementation::~CDatabaseImplementation()
+// Destructor
+ {
+ // Close the db handle
+ TInt err = sqlite3_close(iDbHandle);
+ if(err != SQLITE_OK)
+ {
+ TPtrC8 ptrErrMsg(ErrorMessage());
+ DEBUG_PRINTF3(_L8("Failed to close the database handle. Error code:%d, Error message:%S"), ErrorCode(), &ptrErrMsg);
+ }
+ }
+
+CDatabaseImplementation* CDatabaseImplementation::NewL(RFile& aDatabaseFile, RFile& aJournalFile)
+ {
+ CDatabaseImplementation* self = new(ELeave) CDatabaseImplementation();
+ CleanupStack::PushL(self);
+ self->ConstructorL(aDatabaseFile, aJournalFile);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+void CDatabaseImplementation::ConstructorL(RFile& aDatabaseFile, RFile& aJournalFile)
+ {
+ TSqliteSecure sqliteSecure; // Utility class to open sqlite database via file handles. Implemented by Symbian.
+ TInt err = sqliteSecure.Open(aDatabaseFile, aJournalFile, &iDbHandle);
+
+ // Check the returned error code
+ CheckSqlErrCodeL(err);
+
+ // If comes here, means the database file has been opened successfully
+ // Now, enable the extended result codes feature of SQLite. In SQLite, this feature is
+ // disabled by default for historical compatibility.
+ err = sqlite3_extended_result_codes(iDbHandle, 0);
+ if(SQLITE_OK != err)
+ {
+ TPtrC8 ptrErrMsg(ErrorMessage());
+ DEBUG_PRINTF3(_L8("Failed to activate the extended error mechanism. Error code:%d, Error message:%S"), ErrorCode(), &ptrErrMsg);
+ }
+ }
+
+TPtrC8 CDatabaseImplementation::ErrorMessage()
+ {
+ const char *errMsg = sqlite3_errmsg(iDbHandle);
+ TPtrC8 errPtr((TUint8*)errMsg);
+ return errPtr;
+ }
+
+TInt CDatabaseImplementation::ErrorCode()
+ {
+ return sqlite3_errcode(iDbHandle);
+ }
+
+
+void CDatabaseImplementation::CheckSqlErrCodeL(TInt aErr)
+ {
+ TInt symbianErrCode(0);
+
+ switch(aErr)
+ {
+ case SQLITE_OK:
+ case SQLITE_DONE: // sqlite3_step() has finished executing
+ case SQLITE_ROW: // sqlite3_step() has another row ready
+ symbianErrCode = KErrNone;
+ break;
+ case SQLITE_NOTADB: // File opened that is not a database file
+ case SQLITE_NOTFOUND: // Table or record not found
+ case SQLITE_EMPTY: // Database is empty
+ case SQLITE_CANTOPEN: // Unable to open the database file
+ symbianErrCode = KErrNotFound;
+ break;
+ case SQLITE_CORRUPT: // The database disk image is malformed
+ case SQLITE_SCHEMA: // The database schema has changed
+ case SQLITE_FORMAT: // Auxiliary database format error
+ symbianErrCode = KErrCorrupt;
+ break;
+ case SQLITE_NOMEM: // A malloc() failed in sqlite engine
+ case SQLITE_IOERR: // Some kind of disk I/O error occurred
+ case SQLITE_FULL: // there is no space left on the disk, or the database is too big to hold any more information
+ symbianErrCode = KErrNoMemory;
+ break;
+ case SQLITE_BUSY: // The database file is locked
+ case SQLITE_LOCKED: // A table in the database is locked
+ symbianErrCode = KErrInUse;
+ break;
+ case SQLITE_TOOBIG: // Too much data for one row of a table
+ symbianErrCode = KErrOverflow;
+ break;
+ case SQLITE_INTERRUPT: // Operation terminated by sqlite_interrupt()
+ symbianErrCode = KErrCancel;
+ break;
+ case SQLITE_MISUSE: // The library has been used incorrectly
+ symbianErrCode = KErrNotSupported;
+ break;
+ case SQLITE_CONSTRAINT: // Abort due to constraint violation
+ symbianErrCode = KErrAlreadyExists;
+ break;
+ case SQLITE_ERROR: // SQL error or missing database
+ case SQLITE_MISMATCH : // Data type mismatch
+ case SQLITE_RANGE: // 2nd parameter to sqlite3_bind out of range
+ symbianErrCode = KErrArgument;
+ break;
+ case SQLITE_INTERNAL: // An internal logic error in SQLite
+ symbianErrCode = KErrUnknown;
+ break;
+ case SQLITE_PERM: // Access permission denied
+ symbianErrCode = KErrAccessDenied;
+ break;
+ default:
+ symbianErrCode = aErr < 0 ? aErr : KErrGeneral; // The porting layer may return Symbian error codes, which are negative
+ }// End of switch
+
+ if(KErrNone != symbianErrCode)
+ {
+ if (aErr > 0) // If the error is negative, it does not come from SQLite
+ {
+ TPtrC8 ptrErrMsg(ErrorMessage());
+ DEBUG_PRINTF3(_L8("The SQL engine error code:%d, The SQL engine error message:%S"), ErrorCode(), &ptrErrMsg);
+ }
+ // Some functions returns standard error codes.
+ // Here ErrorCode is called to get the extended error code, if exists.
+
+ User::Leave(symbianErrCode);
+ }
+ }
+
+
+//
+// CStatementImplementation
+//
+
+CStatementImplementation::CStatementImplementation(sqlite3_stmt* aStmtHandle)
+// Constructor
+ :iStmtHandle(aStmtHandle)
+ {
+ // Make sure that the statement handle is never NULL.
+ ASSERT(iStmtHandle != NULL);
+ }
+
+CStatementImplementation::~CStatementImplementation()
+// Destructor
+ {
+ TInt err = sqlite3_finalize(iStmtHandle);
+ if(SQLITE_OK != err)
+ {
+ DEBUG_PRINTF2(_L8("Failed to finalize the statement object. Error code:%d"), err);
+ }
+ }
+
+CStatementImplementation* CStatementImplementation::NewLC(sqlite3_stmt* aStmtHandle)
+ {
+ CStatementImplementation* self = new(ELeave) CStatementImplementation(aStmtHandle);
+ CleanupStack::PushL(self);
+ return self;
+ }
+
+inline sqlite3_stmt* CStatementImplementation::Handle() const
+ {
+ return iStmtHandle;
+ }
+
+//
+// CDatabase
+//
+
+CDatabase::CDatabase()
+// Constructor
+ {
+ // Empty
+ }
+
+EXPORT_C CDatabase::~CDatabase()
+// Destructor
+ {
+ delete iDbImpl;
+ }
+
+EXPORT_C CDatabase* CDatabase::NewL(RFile& aDatabaseFile, RFile& aJournalFile)
+ {
+ CDatabase *self = CDatabase::NewLC(aDatabaseFile, aJournalFile);
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+EXPORT_C CDatabase* CDatabase::NewLC(RFile& aDatabaseFile, RFile& aJournalFile)
+ {
+ CDatabase *self = new(ELeave) CDatabase();
+ CleanupStack::PushL(self);
+ self->ConstructL(aDatabaseFile, aJournalFile);
+ return self;
+ }
+
+void CDatabase::ConstructL(RFile& aDatabaseFile, RFile& aJournalFile)
+ {
+ iDbImpl = CDatabaseImplementation::NewL(aDatabaseFile, aJournalFile);
+ }
+
+EXPORT_C CStatement* CDatabase::PrepareStatementLC(const TDesC& aStatementStr)
+ {
+ // For statements which start with the SELECT key word, this function creates an sql statement
+ // object and returns it.
+ sqlite3_stmt* stmtHandle(0); // Temporary statement handle
+ const void* stmtTail(0); // Pointer to unused portion of Sql statement.
+ TInt err = sqlite3_prepare16_v2(iDbImpl->iDbHandle, aStatementStr.Ptr(), aStatementStr.Size(), &stmtHandle, &stmtTail);
+
+ // Check the returned error code
+ iDbImpl->CheckSqlErrCodeL(err);
+ // Since we expect single statement, stmtTail pointer should be NULL or point to zero.
+ if(stmtTail && static_cast <const TUint16*> (stmtTail)[0] != 0)
+ {
+ err = sqlite3_finalize(stmtHandle);
+ iDbImpl->CheckSqlErrCodeL(err);
+ DEBUG_PRINTF(_L8("There is a problem with the provided SQL statement. It may contain more "
+ "than one statement. Or It may not be terminated with semicolon. "
+ "Or It may contain a space or invalid char after semicolon."));
+ User::Leave(KErrArgument);
+ }
+ // stmtHandle can be NULL for statements like this: ";"
+ if(!stmtHandle)
+ {
+ DEBUG_PRINTF(_L8("The statement handle is NULL."));
+ User::Leave(KErrArgument);
+ }
+ // The statement object which carries handle to the result set of the sql statement
+ CStatementImplementation* stmtImpl = CStatementImplementation::NewLC(stmtHandle);
+ CStatement* stmtObj = CStatement::NewL(*this, stmtImpl);
+ CleanupStack::Pop(stmtImpl); // Ownership has been passed to CStatement
+ CleanupStack::PushL(stmtObj);
+
+ return stmtObj;
+ }
+
+EXPORT_C TInt CDatabase::LastInsertedIdL()
+ {
+ TInt retVal = (TInt)sqlite3_last_insert_rowid(iDbImpl->iDbHandle);
+ // it is now expected that row ids in scr will require 64-bit storage, so cast the return value to TInt
+ if(retVal <= 0)
+ {
+ User::Leave(KErrNotFound);
+ }
+ return retVal;
+ }
+
+//
+// CStatement
+//
+
+CStatement::CStatement(const CDatabase& aDb, CStatementImplementation* aStmtImpl)
+// Constructor
+ :iDb(aDb), iStmtImpl(aStmtImpl)
+ {
+ // Empty
+ }
+
+EXPORT_C CStatement::~CStatement()
+// Destructor
+ {
+ delete iStmtImpl;
+ }
+
+EXPORT_C CStatement* CStatement::NewL(const CDatabase& aDb, CStatementImplementation* aStmtImpl)
+ {
+ CStatement* self = new(ELeave) CStatement(aDb, aStmtImpl);
+ return self;
+ }
+
+EXPORT_C TBool CStatement::ProcessNextRowL()
+ {
+ TInt err = sqlite3_step(iStmtImpl->Handle());
+
+ switch(err)
+ {
+ case SQLITE_ROW: // A new row of data is ready for processing.
+ return ETrue;
+
+ case SQLITE_DONE: // The statement has finished executing successfully.
+ return EFalse;
+ default:
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }// End of switch
+ return EFalse;
+ }
+
+EXPORT_C void CStatement::ExecuteStatementL()
+ {
+ // If the statement doesn't return any result table, it should normally be executed
+ // with sqlite3_exec. However, sqlite does not have a 16-bit version of sqlite3_exec.
+ // Therefore, the execution is made with PrepareStatementLC and ProcessNextRowL functions.
+ // Now, execute and check if the function has completed successfully by calling ProcessNextRowL.
+ // If the function has failed, ProcessNextRowL will leave with one of the system wide error codes.
+ while(ProcessNextRowL())
+ {
+ // nop
+ }
+ }
+
+EXPORT_C void CStatement::BindIntL(TInt aParameterIndex, TInt aParameterValue)
+ {
+ TInt err = sqlite3_bind_int(iStmtImpl->Handle(), aParameterIndex, aParameterValue);
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }
+
+EXPORT_C void CStatement::BindInt64L(TInt aParameterIndex, TInt64 aParameterValue)
+ {
+ TInt err = sqlite3_bind_int64(iStmtImpl->Handle(), aParameterIndex, aParameterValue);
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }
+
+template <class A> void VerifyDescriptorLengthL(const A& aDesc, TUint aMaxDescriptorLength)
+ {
+ if (aDesc.Length() > aMaxDescriptorLength)
+ User::Leave(KErrArgument);
+ }
+
+EXPORT_C void CStatement::BindStrL(TInt aParameterIndex, const TDesC &aParameterStr)
+ {
+ const TInt KMaxInputDescriptorLength = 512;
+ VerifyDescriptorLengthL(aParameterStr, KMaxInputDescriptorLength);
+ TInt err = sqlite3_bind_text16(iStmtImpl->Handle(), aParameterIndex, aParameterStr.Ptr(), aParameterStr.Size(), SQLITE_TRANSIENT);
+ // The fifth argument has the value SQLITE_TRANSIENT, it means that SQLite makes its own private copy of the data immediately
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }
+
+EXPORT_C void CStatement::BindBinaryL(TInt aParameterIndex, const TDesC8 &aParameterStr)
+ {
+ const TInt KMaxInputDescriptorLength = 512;
+ BindBinaryL(aParameterIndex, aParameterStr, KMaxInputDescriptorLength);
+ }
+
+EXPORT_C void CStatement::BindBinaryL(TInt aParameterIndex, const TDesC8 &aParameterStr, TUint aCustomLength)
+ {
+ VerifyDescriptorLengthL(aParameterStr, aCustomLength);
+ TInt err = sqlite3_bind_blob(iStmtImpl->Handle(), aParameterIndex, reinterpret_cast<const char *>(aParameterStr.Ptr()), aParameterStr.Size(), SQLITE_TRANSIENT);
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }
+
+EXPORT_C TPtrC8 CStatement::BinaryColumnL(TInt aColIdx) const
+ {
+ TInt colType = SQLITE_BLOB;
+ ValidateRequestedColumnL(aColIdx, colType);
+
+ const TUint8* data = static_cast<const TUint8 *>(sqlite3_column_blob(iStmtImpl->Handle(), aColIdx));
+ if(!data)
+ iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode());
+
+ TInt len = sqlite3_column_bytes(iStmtImpl->Handle(), aColIdx);
+ if(!len)
+ iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode());
+
+ return TPtrC8(data, len);
+ }
+
+EXPORT_C TPtrC CStatement::StrColumnL(TInt aColIdx) const
+ {
+ TInt colType = SQLITE_TEXT;
+ ValidateRequestedColumnL(aColIdx, colType);
+
+ // Get the column data from the database
+ TUint16* str = (TUint16*)sqlite3_column_text16(iStmtImpl->Handle(), aColIdx);
+ if(!str)
+ {
+ iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode());
+ }
+ // Get the length of the column data
+ TInt len = sqlite3_column_bytes16(iStmtImpl->Handle(), aColIdx);
+ if(!len)
+ {
+ iDb.iDbImpl->CheckSqlErrCodeL(iDb.iDbImpl->ErrorCode());
+ }
+ // Return the value in a pointer descriptor
+ // len contains the number of bytes, so divide it by 2 to get the number of chars
+ ASSERT(len%2 == 0);
+ return TPtrC(str, len/2);
+ }
+
+EXPORT_C TInt64 CStatement::Int64ColumnL(TInt aColIdx) const
+ {
+ TInt colType = SQLITE_INTEGER;
+ ValidateRequestedColumnL(aColIdx, colType);
+ return sqlite3_column_int64(iStmtImpl->Handle(), aColIdx);
+ }
+
+EXPORT_C TInt CStatement::IntColumnL(TInt aColIdx) const
+ {
+ TInt colType = SQLITE_INTEGER;
+ ValidateRequestedColumnL(aColIdx, colType);
+ return sqlite3_column_int(iStmtImpl->Handle(), aColIdx);
+ }
+
+void CStatement::ValidateRequestedColumnL(TInt aColIdx, TInt& aColumnType) const
+// Validate the type and existence of the requested column.
+ {
+ // Get the number of columns in the result set
+ TInt colNum = sqlite3_column_count(iStmtImpl->Handle());
+ // Get the type of the column
+ TInt colType = sqlite3_column_type(iStmtImpl->Handle(), aColIdx);
+
+ if((aColIdx < 0 || aColIdx > colNum-1) || // if the column index is invalid
+ (aColumnType != colType && aColumnType != SQLITE_NULL)) // if its type doesn't match with the expected one
+ {// just column range validity is checked in case of SQLITE_NULL.
+ DEBUG_PRINTF5(_L("The provided column (Idx=%d, Type=%d) is not valid. Max Column Number=%d. Retrieved Column Type=%d"), aColIdx, aColumnType, colNum, colType);
+ User::Leave(KErrArgument);
+ }
+ aColumnType = colType;
+ }
+
+EXPORT_C void CStatement::ResetL()
+ {
+ TInt err = sqlite3_reset(iStmtImpl->Handle());
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ err = sqlite3_clear_bindings(iStmtImpl->Handle());
+ iDb.iDbImpl->CheckSqlErrCodeL(err);
+ }
+
+EXPORT_C TBool CStatement::IsFieldNullL(TInt aColIdx) const
+ {
+ TInt colType = SQLITE_NULL;
+ ValidateRequestedColumnL(aColIdx, colType);
+ return (SQLITE_NULL == colType) ? ETrue : EFalse;
+ }