secureswitools/swisistools/source/dbmanager/dbprocessor.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 18 Aug 2010 09:55:45 +0300
changeset 60 245df5276b97
parent 33 8110bf1194d1
permissions -rw-r--r--
Revision: 201031 Kit: 201033

/*
* Copyright (c) 2008-2010 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: 
* scrdatabase.cpp
* Implements the database handling which is mostly a replica of the SCR Data Layer API 
* which performs all interaction with the underlying database.
*
*/


/**
 @file
 @internalComponent
 @released
*/

#include "dbprocessor.h"
#include "exception.h"
#include "logs.h"
#include "util.h"
#include "symbiantypes.h"
#include "utf8_wrapper.h"
#include "../sisxlibrary/utility.h"
#include <string>
#include <cassert>

#ifdef __linux__
#include <dlfcn.h>


void* GetProcAddress(HINSTANCE aHandle, const char* aSymbol)
	{
	return dlsym(aHandle, aSymbol);
	}

HINSTANCE LoadLibraryA(const char* aLibraryName)
	{
	HINSTANCE handleUsingDefaultSearchPath = dlopen(aLibraryName, RTLD_LAZY);

	if( handleUsingDefaultSearchPath == NULL )
	{
		// Once the dlopen() fails by not finding the aLibraryName in the default
		// path specified by LD_LIBRARY_PATH, we will look in the epoc32/tools 
		// path as the second option.

		const char* epocRoot = getenv("EPOCROOT");		
		if(NULL == epocRoot)
			{
			throw CException("EPOCROOT environment variable not specified.", ExceptionCodes::EEnvNotSpecified);
			}
		std::string epocRootStr(epocRoot); 

		std::string absPathToLibrary = epocRootStr + std::string("epoc32/tools/") + std::string(aLibraryName);
		HINSTANCE handleUsingAbsSearchPath = dlopen(absPathToLibrary.c_str(), RTLD_LAZY);

		return handleUsingAbsSearchPath;
	}

	return handleUsingDefaultSearchPath;
	}

int FreeLibrary(HINSTANCE aHandle)
	{
	// FreeLibrary returns non-zero value on success whereas
	// dlcose returns zero on success.
	return (dlclose(aHandle) == 0)? true: false;
	}

std::string GetErrorMessage()
	{
	return dlerror();
	}

static utf16WString utf32WString2utf16WString(std::wstring& aParameter)
{
	int strLen = aParameter.length();
	const wchar_t * source = aParameter.c_str();
	unsigned short int* buffer = new unsigned short int[(strLen + 1) * 2];

	// Using a temp variable in place of buffer as ConvertUTF32toUTF16 modifies the source pointer passed.
	unsigned short int* temp = buffer;

	ConvertUTF32toUTF16(&source, source + strLen, &temp,  temp + strLen, lenientConversion);

	// Appending NUL to the converted buffer.
	*temp = 0;

	utf16WString utf16Ws;
	utf16Ws.resize(strLen);

	// The built-in basic_string template class copy operation
	// truncates when a NUL is encountered when a c_str() is
	// used to construct the required string.
	// So, if aParameter is any hashable string having the
	// syntax : swtypeName + L'\0' + someId then, we will end
	// up returning only part of the converted UTF-16 string.
	// Hence, we resort to the explicit copy operation with
	// two bytes at a time.
	while( strLen-- )
	{
		utf16Ws[ strLen ] = buffer[ strLen ];
	}

	delete[] buffer;

	return utf16Ws;
}

static std::wstring utf16WString2utf32WString(utf16WString& aParameter)
{	
	int strLen = aParameter.length();
	const unsigned short int* source = aParameter.c_str();
	wchar_t* buffer = new wchar_t[ strLen + 1 ];

	// Using a temp variable in place of buffer as ConvertUTF16toUCS4 modifies the source pointer passed.
	wchar_t* temp = buffer;

	ConvertUTF16toUCS4(&source, source + strLen, &temp, temp + strLen);

	// Appending NUL to the converted buffer.
	*temp = 0;

	std::wstring utf32Ws(buffer);

	delete[] buffer;

	return utf32Ws;
}

#else
std::string GetErrorMessage()
	{
	LPCVOID lpMsgBuf;
		
	DWORD err = GetLastError();
	FormatMessage	(	FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
						FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err,
						MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf,
						0, NULL 
					);
	std::wstring wErrMsg((wchar_t*)lpMsgBuf);
	return wstring2string(wErrMsg);
	}
#endif // __linux__

TDbLibrary* iLibraryHandler = NULL;

TDbLibrary::TDbLibrary(const std::string& aDllPath)
	{
	LoadSqlLibrary(aDllPath);
	LoadFunctions();
	}

/** 
* Failing to unload the library is not really a critical failure, when a dll is unloaded the 
* current process sends a notification to the dll to detach itself from the process, which 
* means the dll can process memory cleanup operations before it unloads. The only case where 
* freelibrary might throw an error is if the library was not loaded in the first place, an 
* error which would be caught beforehand. Hence if the library cannot be unloaded corresponding 
* error message is logged.
*/
TDbLibrary::~TDbLibrary()
	{
	int retCode = FreeLibrary(sqLiteHndl);
	if(retCode == 0)
		{
		//LOGERROR(GetErrorMessage());
		}
	}

void TDbLibrary::LoadSqlLibrary(const std::string& aDllPath)
	{
	sqLiteHndl = LoadLibraryA(aDllPath.c_str());
 
	// Check to see if the library was loaded successfully 

	if (sqLiteHndl != 0)
		{
		//LOGINFO("Library successfully loaded!");
		}
	else
		{
		std::string errMsg("Failed to load SQLite library - ");
		errMsg.append(aDllPath);
		//LOGERROR(errMsg);
		throw CException(errMsg,ExceptionCodes::ELibraryLoadError);
		}
	}

void TDbLibrary::LoadFunctions()
	{
	sqlite3_open = (FnPtr_sqlite3_open)GetProcAddress(sqLiteHndl,"sqlite3_open");
	VerifyLoadedFunction(sqlite3_open);
	
	sqlite3_prepare_v2	= (FnPtr_sqlite3_prepare_v2) GetProcAddress(sqLiteHndl,"sqlite3_prepare_v2");	
	VerifyLoadedFunction(sqlite3_prepare_v2);
	
	sqlite3_step		= (FnPtr_sqlite3_step) GetProcAddress(sqLiteHndl,"sqlite3_step");
	VerifyLoadedFunction(sqlite3_step);
	
	sqlite3_finalize	= (FnPtr_sqlite3_finalize) GetProcAddress(sqLiteHndl,"sqlite3_finalize");
	VerifyLoadedFunction(sqlite3_finalize);
	
	sqlite3_bind_text	= (FnPtr_sqlite3_bind_text) GetProcAddress(sqLiteHndl,"sqlite3_bind_text");
	VerifyLoadedFunction(sqlite3_bind_text);
	
	sqlite3_bind_text16 = (FnPtr_sqlite3_bind_text16) GetProcAddress(sqLiteHndl,"sqlite3_bind_text16");
	VerifyLoadedFunction(sqlite3_bind_text16);
	
	sqlite3_bind_int	= (FnPtr_sqlite3_bind_int) GetProcAddress(sqLiteHndl,"sqlite3_bind_int");
	VerifyLoadedFunction(sqlite3_bind_int);
	
	sqlite3_reset		= (FnPtr_sqlite3_reset) GetProcAddress(sqLiteHndl,"sqlite3_reset");
	VerifyLoadedFunction(sqlite3_reset);
	
	sqlite3_clear_bindings		= (FnPtr_sqlite3_clear_bindings) GetProcAddress(sqLiteHndl,"sqlite3_clear_bindings");
	VerifyLoadedFunction(sqlite3_clear_bindings);
	
	sqlite3_last_insert_rowid	= (FnPtr_sqlite3_last_insert_rowid) GetProcAddress(sqLiteHndl,"sqlite3_last_insert_rowid");
	VerifyLoadedFunction(sqlite3_last_insert_rowid);
	
	sqlite3_extended_result_codes = (FnPtr_sqlite3_extended_result_codes) GetProcAddress(sqLiteHndl, "sqlite3_extended_result_codes" );
	VerifyLoadedFunction(sqlite3_extended_result_codes);
	
	sqlite3_close			= (FnPtr_sqlite3_close)GetProcAddress(sqLiteHndl,"sqlite3_close");
	VerifyLoadedFunction(sqlite3_close);
	
	sqlite3_errmsg			= (FnPtr_sqlite3_errmsg)GetProcAddress(sqLiteHndl,"sqlite3_errmsg");
	VerifyLoadedFunction(sqlite3_errmsg);
	
	sqlite3_errcode			= (FnPtr_sqlite3_errcode)GetProcAddress(sqLiteHndl,"sqlite3_errcode");
	VerifyLoadedFunction(sqlite3_errcode);
	
	sqlite3_bind_int64		= (FnPtr_sqlite3_bind_int64)GetProcAddress(sqLiteHndl,"sqlite3_bind_int64");
	VerifyLoadedFunction(sqlite3_bind_int64);
	
	sqlite3_column_text16	= (FnPtr_sqlite3_column_text16)GetProcAddress(sqLiteHndl,"sqlite3_column_text16");
	VerifyLoadedFunction(sqlite3_column_text16);
	
	sqlite3_column_bytes16	= (FnPtr_sqlite3_column_bytes16)GetProcAddress(sqLiteHndl,"sqlite3_column_bytes16");
	VerifyLoadedFunction(sqlite3_column_bytes16);
	
	sqlite3_column_int64	= (FnPtr_sqlite3_column_int64)GetProcAddress(sqLiteHndl,"sqlite3_column_int64");
	VerifyLoadedFunction(sqlite3_column_int64);
	
	sqlite3_column_int		= (FnPtr_sqlite3_column_int)GetProcAddress(sqLiteHndl,"sqlite3_column_int");
	VerifyLoadedFunction(sqlite3_column_int);
	
	sqlite3_column_count	= (FnPtr_sqlite3_column_count)GetProcAddress(sqLiteHndl,"sqlite3_column_count");
	VerifyLoadedFunction(sqlite3_column_count);
	
	sqlite3_column_type		= (FnPtr_sqlite3_column_type)GetProcAddress(sqLiteHndl,"sqlite3_column_type");
	VerifyLoadedFunction(sqlite3_column_type);
	
	sqlite3_prepare16_v2	= (FnPtr_sqlite3_prepare16_v2)GetProcAddress(sqLiteHndl,"sqlite3_prepare16_v2");
	VerifyLoadedFunction(sqlite3_prepare16_v2);

	sqlite3_bind_blob = (FnPtr_sqlite3_bind_blob)GetProcAddress(sqLiteHndl,"sqlite3_bind_blob");
	VerifyLoadedFunction(sqlite3_bind_blob);

	}

void TDbLibrary::VerifyLoadedFunction(void* aFnPtr)
	{
	if(aFnPtr != NULL)
		return;

	std::string errMsg = GetErrorMessage();
	//LOGERROR(errMsg);
	throw CException(errMsg,ExceptionCodes::ELibraryLoadError);
	}


//
// CDbProcessor
// 

// Constructor
CDbProcessor::CDbProcessor(const std::string& aDllPath, const std::string& aDbName )
	:iLibraryHandler(aDllPath)
	{
	
	// Then open the SCR database
	TInt err = iLibraryHandler.sqlite3_open(aDbName.c_str(), &iDbHandle);
	
	// Check the returned error code
	CheckSqlErrCode(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 = iLibraryHandler.sqlite3_extended_result_codes(iDbHandle, 0);
	// Check the returned error code
	CheckSqlErrCode(err,"Failed to activate the extended error mechanism.");
	}

// Destructor
CDbProcessor::~CDbProcessor()
	{
	//LOGINFO("Closing the SCR database connection.");

	// Close the db handle
	TInt err = iLibraryHandler.sqlite3_close(iDbHandle);
	
	if(err != SQLITE_OK)
		{
		std::string errMsg("Failed to close the database handle.");
		//LOGERROR(errMsg);
		}
	}


CStatement* CDbProcessor::PrepareStatement(const std::string& aStatementStr)
	{
	// For statements which start with the SELECT key word, this function creates an sql statement  
	// object and returns it.
	sqlite3_stmt* stmtHandle = NULL; // Temporary statement handle
	const char* stmtTail = NULL;	 // Pointer to unused portion of Sql statement.                         
	TInt err = iLibraryHandler.sqlite3_prepare_v2(iDbHandle, aStatementStr.c_str(), aStatementStr.size(), &stmtHandle, &stmtTail);
	
	// Check the returned error code
	CheckSqlErrCode(err);
	// Since we expect single statement, stmtTail pointer should be NULL or point to zero.
	if(stmtTail && stmtTail[0] != 0)
		{
		err = iLibraryHandler.sqlite3_finalize(stmtHandle);
		std::string errMessage = "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.";
		//LOGERROR(errMessage);
		CheckSqlErrCode(err,errMessage);
		}
	// stmtHandle can be NULL for statements like this: ";"
	if(!stmtHandle)
		{
		throw CException(ExceptionCodes::ESqlArgumentError);
		}

	std::string str = "Query:";
	//LOGINFO(str+aStatementStr);
	// The statement object which carries handle to the result set of the sql statement
	CStatement* stmtObj = new CStatement(stmtHandle,iLibraryHandler,*iDbHandle); 
	
	return stmtObj;	
	}


TInt64 CDbProcessor::LastInsertedId()
	{
	TInt retVal = (TInt)iLibraryHandler.sqlite3_last_insert_rowid(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)
		{
		throw CException(ExceptionCodes::ESqlNotFoundError);
		}
	return retVal;
	}

void CDbProcessor::CheckSqlErrCode(TInt aErrorCode, std::string& aErrorMessage ) 
	{
	if(aErrorCode != SQLITE_OK)
		{
		TInt errorCode = iLibraryHandler.sqlite3_errcode(iDbHandle);
		//LOGERROR(aErrorMessage);
		throw CException(aErrorMessage,aErrorCode);
		}
	}

void CDbProcessor::CheckSqlErrCode(TInt aErrorCode) 
	{

	if(aErrorCode != SQLITE_OK)
		{
		TInt errorCode = iLibraryHandler.sqlite3_errcode(iDbHandle);
		const char* errMsg = iLibraryHandler.sqlite3_errmsg(iDbHandle);
		CheckSqlErrCode(errorCode,errMsg);
		}
	}

void CDbProcessor::CheckSqlErrCode(TInt aErrorCode, const char* aErrorMessage ) 
	{
	std::string errMsg(aErrorMessage);
	CheckSqlErrCode(aErrorCode,errMsg);
	}

//
// CStatement
//

// Constructor
CStatement::CStatement(sqlite3_stmt* aStmtHandle, const TDbLibrary& aLibraryHandler , sqlite3& aDbHandle)
	:iStmtHandle(aStmtHandle),
	iLibraryHandler(aLibraryHandler),
	iDbHandle(aDbHandle)
	{
	// Make sure that the statement handle is never NULL.
	assert(iStmtHandle != NULL);
	}

// Destructor
CStatement::~CStatement()
	{
	TInt err = iLibraryHandler.sqlite3_finalize(iStmtHandle);
	if(SQLITE_OK != err)
		{
		std::string errMsg = "Failed to finalize the statement object.";
		//LOGERROR(errMsg);
		throw CException(errMsg,err);
		}
	}

void CStatement::ExecuteStatement()
	{
	// 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.
	//LOGINFO("Executing the prepared SCR SQL statement.");
	
	// 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(ProcessNextRow())
		{}
	}


bool CStatement::ProcessNextRow()
	{
	TInt err = iLibraryHandler.sqlite3_step(iStmtHandle);
	
	switch(err)
		{
	case SQLITE_ROW: // A new row of data is ready for processing.
		return true;
	
	case SQLITE_DONE: // The statement has finished executing successfully.
		return false;
	default:		 
		CheckSqlErrCode(err);
		}// End of switch
	return false;
	}

void CStatement::BindInt(TInt aParameterIndex, TInt aParameterValue)
	{
	TInt err = iLibraryHandler.sqlite3_bind_int(iStmtHandle, aParameterIndex, aParameterValue);
	CheckSqlErrCode(err);
	}

void CStatement::BindInt64(TInt aParameterIndex, TInt64 aParameterValue)
	{
	TInt err = iLibraryHandler.sqlite3_bind_int64(iStmtHandle, aParameterIndex, aParameterValue);
	CheckSqlErrCode(err);
	}


#ifdef __TOOLS2_LINUX__
void CStatement::BindStr(TInt aParameterIndex, const std::wstring &aParameterStr, int aConvertSlash=allowSlashConversion)
#else
void CStatement::BindStr(TInt aParameterIndex, const std::wstring &aParameterStr)
#endif
	{
	/*
	 * Under LINUX : Because of the UTF-32 format of wstring, we can't directly use the sqlite3_bind_text16() which
	 * requires UTF-16 format. So, we convert the UTF-32 data into UTF-16 before using it.
	 *
	 * Under WINDOWS : No conversion required since wstring will be in UTF-16 format itself.
	 */

    #ifdef __LINUX__
	// Make sure that the wstring passed to this function is not having any trailing
	// explicit NUL( Preferably, pass c_str() part of wstring ).
    //
	// Only case in which you shouldn't pass c_str() is that the wstring has NUL as
	// part of its actual string content(like swtypename + L'\0' + someID etc).

	// In order to maintain the consistency of DB contents across the WINDOWS and LINUX platforms, before interacting
	// with the DB we convert the local paths into WINDOWS specific paths.
    //
	// If aParameterStr is not a PATH but contains a forward slash, we should restrain 
	// from the slash conversion. One such instance is MimeType.
	//
	std::wstring temp = aParameterStr;
	if( aConvertSlash == allowSlashConversion )
	{
	    ConvertToWindowsSpecificPaths(temp);
	}

    utf16WString utf16s = utf32WString2utf16WString(temp);

	TInt err = iLibraryHandler.sqlite3_bind_text16(iStmtHandle, aParameterIndex, utf16s.c_str(), aParameterStr.size()*2, SQLITE_TRANSIENT);

    #else
	TInt err = iLibraryHandler.sqlite3_bind_text16(iStmtHandle, aParameterIndex, aParameterStr.c_str(), aParameterStr.size()*2, SQLITE_TRANSIENT);
	#endif
	// The fifth argument has the value SQLITE_TRANSIENT, it means that SQLite makes its own private copy of the data immediately
	CheckSqlErrCode(err);
	}

void CStatement::BindBinary(TInt aParameterIndex, const std::string &aParameterStr)
	{
	TInt err = iLibraryHandler.sqlite3_bind_blob(iStmtHandle, aParameterIndex, aParameterStr.c_str(), 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
	CheckSqlErrCode(err);
	}

void CStatement::BindBinary(TInt aParameterIndex, const std::wstring &aParameterStr)
	{
	#ifdef __LINUX__
	// To maintain consistency of the binary equivalent of the wstring
	// being binded, we convert the wstring with UTF-32 encoding under LINUX
	// to UTF-16 encoding which is same as that of wstring under WINDOWS.
	
	std::wstring temp = aParameterStr;
	utf16WString utf16s = utf32WString2utf16WString(temp);

	TInt err = iLibraryHandler.sqlite3_bind_blob(iStmtHandle, aParameterIndex, utf16s.c_str(), aParameterStr.size()*2, SQLITE_TRANSIENT);
	#else
	TInt err = iLibraryHandler.sqlite3_bind_blob(iStmtHandle, aParameterIndex, aParameterStr.c_str(), aParameterStr.size()*2, SQLITE_TRANSIENT);
	#endif

	// The fifth argument has the value SQLITE_TRANSIENT, it means that SQLite makes its own private copy of the data immediately
	CheckSqlErrCode(err);
	}

void CStatement::Reset()
	{
	TInt err = iLibraryHandler.sqlite3_reset(iStmtHandle);
	CheckSqlErrCode(err);
	err = iLibraryHandler.sqlite3_clear_bindings(iStmtHandle);
	CheckSqlErrCode(err);
	}

void CStatement::CheckSqlErrCode(TInt aErrorCode) 
	{
	
	if(aErrorCode != SQLITE_OK)
		{
		TInt errorCode = iLibraryHandler.sqlite3_errcode(&iDbHandle);
		const char* errMsg = iLibraryHandler.sqlite3_errmsg(&iDbHandle);
		std::string errStr(errMsg);
		//LOGERROR(errStr);
		throw CException(errStr,errorCode);
		}
	}

int CStatement::IntColumn(int aColumnId ) const
	{
	return iLibraryHandler.sqlite3_column_int(iStmtHandle, aColumnId);
	}

std::wstring CStatement::StrColumn(int aColumnId ) const
	{
	/*
	 * Under LINUX : While writing onto DB, we bind the wstring after converting it into UTF-16 from
	 * UTF-32 format. So, now while reading we need to convert the UTF-16 data back to UTF-32
	 * format so that we can return the required UTF-32 wstring.
	 *
	 * Under WINDOWS : No conversion required since wstring will be in UTF-16 format itself.
	 */
	#ifdef __LINUX__

	utf16WString utf16S = iLibraryHandler.sqlite3_column_text16(iStmtHandle, aColumnId);
	std::wstring utf32S = utf16WString2utf32WString(utf16S);

	// The DB will have WINDOWS specific paths to maintain the consistency of DB contents across WINDOWS and LINUX platforms.
	// So, after reading under LINUX we will convert them into local paths.

    ConvertToLinuxSpecificPaths(utf32S);

	#else
	std::wstring utf32S(static_cast<const wchar_t*>(iLibraryHandler.sqlite3_column_text16(iStmtHandle, aColumnId)));
	#endif

	return utf32S;
	}

TInt64 CStatement::Int64Column(int aColumnId ) const
	{
	return iLibraryHandler.sqlite3_column_int64(iStmtHandle, aColumnId);
	}