// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
//

#include "SqlSrvMain.h"			//CSqlServer
#include "SqlSrvSession.h"		//CSqlSrvSession
#include "SqlSrvStatement.h"	//CSqlSrvStatement, HSqlSrvStmtParamBuf
#include "SqlSecurityImpl.h"	//CSqlSecurityPolicy
#include "SqlSrvUtil.h"			//Global server functions
#include "SqlUtil.h"			// config length
#include "SqlSrvDriveSpace.h"	//CSqlDriveSpace, RSqlDriveSpaceCol
#include "SqlSrvBlob.h"
#include "SqlResourceProfiler.h"
#include "SqlCompact.h"
#include "UTraceSql.h"
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef _DEBUG

const TInt KDelayedDbHeapFailureMask = 0x1000;

//Puts the database connection in a test mode
//Returns true if the heap failure simulation has to be delayed untill the database is opened
//Initialises iDbResourceTestMode and iFailedAllocNumber (if delayed simulation) data members
inline TBool CSqlSrvSession::ActivateDbTestMode(TInt aHeapFailureMode, TInt aFailedAllocNumber)
	{
	iDbResourceTestMode = aHeapFailureMode;
	if(aHeapFailureMode & KDelayedDbHeapFailureMask)
		{
		iFailedAllocNumber = aFailedAllocNumber;
		return ETrue;
		}
	return EFalse;
	}

//If the database connection is in a test mode then the macro will reset the heap allocation failure type.
//and stop the test mode.
inline void CSqlSrvSession::StopDbTestMode()
	{
	if(iDbResourceTestMode)
		{
		iDbResourceTestMode = 0;
		User::__DbgSetAllocFail(RHeap::EUser, RHeap::ENone, 0);
		}
	}

//If the database connection is in a test mode then the function will mark the allocated by the 
//server resources.
inline void CSqlSrvSession::DbResourceMark()
	{
	if(iDbResourceTestMode)
		{
		ResourceCountMarkStart();
		}
	}

//If the database connection is in a test mode then the macro will check the allocated by the server resources,
//comparing their count with the resource count at the moment DbResourceMark() has been called.
//The client will be panicked if the resource count now is different.
inline void CSqlSrvSession::DbResourceEnd(const RMessage2& aMessage)
	{
	if(iDbResourceTestMode)
		{
		ResourceCountMarkEnd(aMessage);
		}
	}

//Executes the heap simulation failure.
inline void CSqlSrvSession::DbSetAllocFail(TInt aHeapFailureMode, TInt aFailedAllocNumber)
	{
	User::__DbgSetAllocFail(RHeap::EUser, 
							static_cast <RHeap::TAllocFail> (aHeapFailureMode & (KDelayedDbHeapFailureMask - 1)), 
							aFailedAllocNumber);
	}
	
//Executes the delayed heap simulation failure, if the connection is in test mode
inline void CSqlSrvSession::DbSetDelayedAllocFail()
	{
	if(iDbResourceTestMode & KDelayedDbHeapFailureMask)
		{
		User::__DbgSetAllocFail(RHeap::EUser, 
								static_cast <RHeap::TAllocFail> (iDbResourceTestMode & (KDelayedDbHeapFailureMask - 1)), 
								iFailedAllocNumber);
		}
	}
	
#else //_DEBUG

inline TBool CSqlSrvSession::ActivateDbTestMode(TInt, TInt)
	{
	return EFalse;
	}

inline void CSqlSrvSession::StopDbTestMode()
	{
	}

inline void CSqlSrvSession::DbResourceMark()
	{
	}

inline void CSqlSrvSession::DbResourceEnd(const RMessage2&)
	{
	}

inline void CSqlSrvSession::DbSetAllocFail(TInt, TInt)
	{
	}
	
inline void CSqlSrvSession::DbSetDelayedAllocFail()
	{
	}

#endif//_DEBUG

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
Searches aContainer for an object identified by aHandle.
If such object exists, a reference to it is returned.
If there is no object, the client gets a panic.

@panic SqlDb 4 Client panic. Invalid aHandle parameter value (zero, negative or out of range).

@internalComponent
*/
template <class T> T& SqlSessObjFind(RDbObjContainer<T>& aContainer, TInt aHandle, const RMessage2& aMessage)
	{
	T* obj = aContainer.Find(aHandle);
	__SQLPANIC_CLIENT(obj != NULL, aMessage, ESqlPanicBadArgument);
	return *obj;
	}

//This function return true, if there is free disk space on drive where the main database file is.
static TBool HasFreeDiskSpace(RFs& aFs, TDriveNumber aDrive)
	{
	TVolumeInfo volInfo;
	TInt err = aFs.Volume(volInfo, aDrive);
	if(err == KErrNone)
		{
		const TInt64 KDiskSpaceThreshold = 1024 * 4;
		return volInfo.iFree > KDiskSpaceThreshold;
		}
	return ETrue;
	}
	
//If aError is KSqlErrFull and  there is no free disk space, then KSqlErrFull is converted to KErrDiskFull
//and returned.
static TInt ConvertSqlFull2DiskFullErr(TInt aError, RFs& aFs, TDriveNumber aDrive)
	{
	if(aError == KSqlErrFull && !HasFreeDiskSpace(aFs, aDrive))
		{
		aError = KErrDiskFull;
		}
	return aError;
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
Creates a new instance of CSqlSrvSession class.

This function shall never be called directly.
It is CSqlServer responsibility to create a new server side session object as a responce to the criation of a
client side session instance.

@return A pointer to the created CSqlSrvSession instance.

@leave KErrNoMemory, an out of memory condition has occurred;

@see CSqlServer
@see CSqlServer::NewSessionL()
*/
CSqlSrvSession* CSqlSrvSession::NewL()
	{
	CSqlSrvSession* self = new (ELeave)	CSqlSrvSession;
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/**
Frees the allocated by CSqlSrvSession instance resources and memory.
*/
CSqlSrvSession::~CSqlSrvSession()
	{
	StopDbTestMode();
	DbFreeReservedSpace();
	iIpcStreams.Close();
	iStatements.Close();
	delete iDatabase;
	}

/**
Receives and dispatches all client side requests.

CSession2::ServiceL() implementation.

@param aMessage Client message containing the request (function code and data)

@leave The function may leave with some database specific 
	   errors categorised as ESqlDbError or system-wide error codes.

@see CSession2::ServiceL()
*/
void CSqlSrvSession::ServiceL(const RMessage2& aMessage)
	{
	TSqlSrvFunction funcCode = ESqlSrvTestBase;
	TInt handle = 0;
	Extract(aMessage, funcCode, handle);
	TInt retCode = KErrNone;
	if(funcCode >= ESqlSrvDbBase)
		{
		SQLPROFILER_REPORT_IPC(ESqlIpcRq, 0);
		}
	SYMBIAN_TRACE_SQL_EVENTS_ONLY(TPtrC8 funcName = GetIPCFuncStr(funcCode));
	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EBorder), KSrvMsgStr, &funcName));
	
	SQLPROFILER_IPC_START(iIpcCounter, iDatabase ? (TUint)iDatabase->RawDbHandle() : 0);
	
	switch(funcCode)
		{
		//////////////////////  resource check operations  ///////////////////////////
		case ESqlSrvResourceMark:
			ResourceCountMarkStart();
			break;
		case ESqlSrvResourceCheck:
			ResourceCountMarkEnd(aMessage);
			break;
		case ESqlSrvResourceCount:
			retCode = CountResources();					//Returns the recourse count
			break;
		case ESqlSrvSetDbHeapFailure:
			if(ActivateDbTestMode(aMessage.Int0(), aMessage.Int1()))
				{
				break;	
				}
		case ESqlSrvSetHeapFailure:
			DbSetAllocFail(aMessage.Int0(), aMessage.Int1());
			break;
		//////////////////////   profiling operations  //////////////////////////////////
		case ESqlSrvProfilerStart:
			TSqlSrvResourceProfiler::StartL(aMessage);
			break;
		case ESqlSrvProfilerStop:
			TSqlSrvResourceProfiler::StopL(aMessage);
			break;
		case ESqlSrvProfilerReset:
			TSqlSrvResourceProfiler::ResetL(aMessage);
			break;
		case ESqlSrvProfilerQuery:
			ProfilerQueryL(aMessage);
			break;
		//////////////////////   database operations //////////////////////////////////
		case ESqlSrvDbCreate:
		case ESqlSrvDbCreateSecure:
		case ESqlSrvDbOpen:
			DbResourceMark();
			DbCreateObjectL(aMessage, funcCode);
			DbSetDelayedAllocFail();
			break;
		case ESqlSrvDbOpenFromHandle:
			DbResourceMark();
			DbCreateObjectFromHandleL(aMessage);
			DbSetDelayedAllocFail();
			break;
		case ESqlSrvDbAttach:
			DbAttachL(aMessage);
			break;
		case ESqlSrvDbAttachFromHandle:
			DbAttachFromHandleL(aMessage);
			break;
		case ESqlSrvDbDetach:
			DbDetachL(aMessage);
			break;
		case ESqlSrvDbClose:
			DbDestroyObject();
			DbResourceEnd(aMessage);
			StopDbTestMode();
			break;
		case ESqlSrvDbCopy:
			DbResourceMark();
			DbCopyFileL(aMessage);
			break;
		case ESqlSrvDbDelete:
			DbResourceMark();
			DbDeleteFileL(aMessage);
			break;
		case ESqlSrvLastErrorMsg:
			retCode = DbLastErrorMessageL(aMessage);//may return that the client buffer is not big enough for the message
			break;
		case ESqlSrvDbLastInsertedRowId:
			DbLastInsertedRowIdL(aMessage);
			break;
		case ESqlSrvDbExec8:
			retCode = DbExecSql8L(aMessage);		//returns the count of affected records
			break;
		case ESqlSrvDbExec16:
			retCode = DbExecSql16L(aMessage);	//returns the count of affected records
			break;
		case ESqlSrvDbSetIsolationLevel:
			DbSetIsolationLevelL(aMessage);
			break;
		case ESqlSrvDbGetSecurityPolicy:
			retCode = DbGetSecurityPolicyL(aMessage);//may return that the client buffer is not big enough for the security policy
			break;
		case ESqlSrvDbScalarFullSelect8:
			retCode = DbScalarFullSelectL(aMessage, EFalse);//may return that the client buffer is not big enough for the column value
			break;
		case ESqlSrvDbScalarFullSelect16:
			retCode = DbScalarFullSelectL(aMessage, ETrue);//may return that the client buffer is not big enough for the column value
			break;
		case ESqlSrvDbInTransaction:
			retCode = DbInTransaction(aMessage);	//Returns whether the database in in transaction or not - true/false
			break;
		case ESqlSrvDbSize:
			retCode = DbSizeL(aMessage);			//Returns the database size
			break;
		case ESqlSrvDbSize2:
			DbSize2L(aMessage);
			break;
		case ESqlSrvDbCompact:
			retCode = DbCompactL(aMessage);			//Returns the amount of the removed free database space
			break;
		//////////////////////   reserved drive space management ////////////////////////
		case ESqlSrvDbReserveDriveSpace:
			__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
			DbReserveDriveSpaceL();
			break;
		case ESqlSrvDbFreeReservedSpace:
			__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
			DbFreeReservedSpace();
			break;
		case ESqlSrvDbGetReserveAccess:
			__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
			DbGetReserveAccessL();
			break;
		case ESqlSrvDbReleaseReserveAccess:
			__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
			DbReleaseReserveAccess();
			break;
		//////////////////////   BLOB source ///////////////////////////////////////////
		case ESqlSrvDbBlobSource:
			retCode = DbBlobSourceL(aMessage);		//Returns the BLOB handle
			break;
		//////////////////////   statement operations //////////////////////////////////
		case ESqlSrvStmtPrepare8:
			retCode = StmtPrepareL(aMessage, EFalse);//returns the statement handle
			break;
		case ESqlSrvStmtPrepare16:
			retCode = StmtPrepareL(aMessage, ETrue);//returns the statement handle
			break;
		case ESqlSrvStmtClose:
			iStatements.Remove(handle);
			break;
		case ESqlSrvStmtReset:
			retCode = ::SqlSessObjFind(iStatements, handle, aMessage).Reset();//May return that the statement has expired
			break;
		case ESqlSrvStmtExec:
		case ESqlSrvStmtAsyncExec:
		case ESqlSrvStmtBindExec:
		case ESqlSrvStmtAsyncBindExec:
			retCode = StmtExecL(aMessage, handle, funcCode);//returns the count of affected records
			break;
		case ESqlSrvStmtNext:
		case ESqlSrvStmtBindNext:
			retCode = StmtNextL(aMessage, handle, funcCode);//returns a non-negative number if the client side buffer is too small
			break;
		case ESqlSrvStmtColumnNames:
		case ESqlSrvStmtParamNames:
			retCode = StmtNamesL(aMessage, handle, funcCode);//returns a non-negative number if the client side buffer is too small
			break;
		case ESqlSrvStmtColumnSource:
			retCode = StmtColumnSourceL(aMessage, handle);//returns an IPC stream handle
			break;
		case ESqlSrvStmtBinParamSink:
		case ESqlSrvStmtTxtParamSink16:
			retCode = StmtParamSinkL(aMessage, handle, funcCode);//returns an IPC stream handle
			break;
		case ESqlSrvStmtBufFlat:
			StmtGetBufFlatL(aMessage, handle);
			break;
		case ESqlSrvStmtColumnValue:
			StmtColumnValueL(aMessage, handle);
			break;
		case ESqlSrvStmtDeclColumnTypes:
			StmtDeclColumnTypesL(aMessage, handle);
			break;
		//////////////////////   stream operations //////////////////////////////////
		case ESqlSrvStreamRead:
			retCode = ::SqlSessObjFind(iIpcStreams, handle, aMessage).ReadL(aMessage);
			break;
		case ESqlSrvStreamWrite:
			::SqlSessObjFind(iIpcStreams, handle, aMessage).WriteL(aMessage);
			break;
		case ESqlSrvStreamSize:
			retCode = ::SqlSessObjFind(iIpcStreams, handle, aMessage).SizeL();
			break;
		case ESqlSrvStreamSynch:
			::SqlSessObjFind(iIpcStreams, handle, aMessage).SynchL();
			break;
		case ESqlSrvStreamClose:
			iIpcStreams.Remove(handle);
			break;
		//////////////////////                     //////////////////////////////////
		default:	
			retCode = KErrNotSupported;
			break;
		}
	Server().Compactor().RestartTimer();
	Server().MinimizeBuffers();		
	if(!aMessage.IsNull())
		{
		aMessage.Complete(retCode);
		}
    SQLPROFILER_IPC_END(iIpcCounter, funcCode, iDatabase ? (TUint)iDatabase->RawDbHandle() : 0, iIpcTraceData, retCode);
	}

/**
If aError is KErrBadDescriptor, then panic the client, else - default error handling.
KErrBadDescriptor error may be thrown from "message write" operations, if the client supplied a bad
descriptor to the server.
*/
void CSqlSrvSession::ServiceError(const RMessage2& aMessage, TInt aError)
 	{
	Server().MinimizeBuffers();		
	aError = ::ConvertSqlFull2DiskFullErr(aError, Server().FileData().Fs(), iDrive);
 	if(aError == KErrBadDescriptor)
 		{
		//The __SQLPANIC_CLIENT() macro cannot be used here because it calls a leaving function. A leaving call
		//from a leaving call will terminate the server.
		_LIT(KPanicCategory, "SqlDb");
		
		SYMBIAN_TRACE_SQL_ERR_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EError), KSqlSrvPanicClient, aError));
		aMessage.Panic(KPanicCategory, ESqlPanicBadDescriptor);
 		}
 	
 	SYMBIAN_TRACE_SQL_ERR_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EError), KSqlSrvError, aError));
 	SQLPROFILER_IPC_ERROR(iIpcCounter, static_cast <TSqlSrvFunction> (KSqlSrvFunctionMask & aMessage.Function()), 
 	       iDatabase ? (TUint)iDatabase->RawDbHandle() : 0, aError);
 	CSession2::ServiceError(aMessage, aError);
   	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////          Profiler  operations           ///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////

/**
Retrieves the counter values for the specified profiling counter.

@leave  KErrNone, the operation completed successfully,
        KErrOverflow, the receiving buffer size is too small;
                  One of the other system-wide error codes may also be returned.

@see TSqlResourceProfiler

Usage of the IPC call arguments:
 - Arg 0: [in]  profiling counter type, one of the TSqlResourceProfiler::TSqlCounter enum item values.
 - Arg 1: [in]  the size of the buffer for the profiling counter values.
 - Arg 2: [out] the buffer for the profiling counter values.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
void CSqlSrvSession::ProfilerQueryL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	if(aMessage.Int0() == TSqlResourceProfiler::ESqlCounterConfig)
		{
		const TInt KConfigBufLen = 128;
		if(aMessage.Int1() < KConfigBufLen)
			{
			__SQLLEAVE(KErrOverflow);	
			}
		TBuf8<KConfigBufLen> res;
		iDatabase->QueryConfigL(res);
		aMessage.WriteL(2, res);
		}
	else
		{
		TSqlSrvResourceProfiler::QueryL(aMessage);
		}				
	}

///////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////          Database operations           ///////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////

/**
Processes the request for creating/opening a database.

The function initializes iDatabase and iDrive data members.

Usage of the IPC call arguments:
Arg 0: [in]  database file name length in 16-bit characters
Arg 1: [in]  database file name
Arg 2: [in]  PPPPCCCC, where PPPP is the security policy length, CCCC is the config string length.
Arg 3: [in]  security policies buffer | config string

@leave KErrArgument If config string length or security policy length is invalid (negative length or too big length)
@panic SqlDb 1 Client panic. iDatabase is not NULL (it has been created already)
*/
void CSqlSrvSession::DbCreateObjectL(const RMessage2& aMessage, TSqlSrvFunction aFunction)
	{
	__SQLPANIC_CLIENT(!iDatabase, aMessage, ESqlPanicObjExists);
	const TInt KSecurityPolicyLen = (aMessage.Int2() & 0x7fff0000) >> 16;
	const TInt KConfigStringLen = aMessage.Int2() & 0xffff;
	if(KSecurityPolicyLen < 0 || KConfigStringLen < 0 || KConfigStringLen > KSqlSrvMaxConfigStrLen)
		{
		__SQLLEAVE(KErrArgument);	
		}
	TBuf8<KSqlSrvMaxConfigStrLen> configStr;
	if(KConfigStringLen > 0)
		{
		aMessage.ReadL(3, configStr, KSecurityPolicyLen);
		SQLPROFILER_REPORT_IPC(ESqlIpcRead, KConfigStringLen);
		}
	TSqlSrvFileData& fileData = Server().FileData();
	fileData.SetL(aMessage, aMessage.Int0(), 1, &configStr);
	iDrive = fileData.Drive();
	switch(aFunction)
		{
		case ESqlSrvDbCreate:
			if(fileData.IsSecureFileNameFmt())
				{
				__SQLLEAVE(KErrArgument);	
				}
			iDatabase = CSqlSrvDatabase::CreateL(fileData);
			break;
		case ESqlSrvDbCreateSecure:
			{
			if(!fileData.IsSecureFileNameFmt())
				{
				__SQLLEAVE(KErrArgument);	
				}
			//The caller can create a secure database which secure UID matches his secure UID.
			if(fileData.SecureUid() != aMessage.SecureId())
				{
				__SQLLEAVE(KErrPermissionDenied);	
				}
			CSqlSecurityPolicy* policy = InternalizeSecurityPolicyL(aMessage);
			iDatabase = CSqlSrvDatabase::CreateSecureL(fileData, policy);
			}
			break;
		case ESqlSrvDbOpen:
			iDatabase = CSqlSrvDatabase::OpenL(fileData);
			break;
		default:
			__SQLLEAVE(KErrArgument);	
			break;
		}
	}

/**
Processes the request for opening a database from file handle.
The server expects that the database to be opened/created is in the applicatio's private data cage.

Usage of the IPC call arguments:
 - Arg 0: [in]  The 32 bits of the argument are used as follow:
 @code
 MSB                                                                                 LSB
 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
 Ro Cr  C  C  C  C  C  C  C  C  C  C  C  C  C  C  F  F  F  F  F  F F F F F F F F F F F 
 @endcode
 Where:
 @code
  - "Ro" - read-only flag, true if the file is read-only;
  - "Cr" - create/open flag, true if the file was created, false if the file was opened;
  - "C"  - config string length in 16-bit characters;
  - "F"  - database file name length in 16-bit characters;
 @endcode
 - Arg 1: [in]  database file name | config string
 - Arg 2: [in]  file session handle
 - Arg 3: [in]  database file handle

@panic SqlDb 1 Client panic. iDatabase is not NULL (it has been created already)
*/
void CSqlSrvSession::DbCreateObjectFromHandleL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(!iDatabase, aMessage, ESqlPanicObjExists);
	const TBool KReadOnly = (aMessage.Int0() & 0x80000000) != 0;
	const TBool KCreated = (aMessage.Int0() & 0x40000000) != 0;
	const TInt KDbFileNameLen = aMessage.Int0() & 0x0000FFFF;
	const TInt KConfigStringLen = (aMessage.Int0() & 0x3FFF0000) >> 16;
	if(KConfigStringLen < 0 || KConfigStringLen > KSqlSrvMaxConfigStrLen)
		{
		__SQLLEAVE(KErrArgument);	
		}
	if(KDbFileNameLen < 1 || KDbFileNameLen > KMaxFileName)
		{
		__SQLLEAVE(KErrBadName);
		}
	TDes16& buffer = Server().GetBuf16L(KDbFileNameLen + KConfigStringLen);
	aMessage.ReadL(1, buffer);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, ((KDbFileNameLen + KConfigStringLen) * sizeof(TText)));
	TFileName dbFileName;
	dbFileName.Copy(buffer.LeftTPtr(KDbFileNameLen));
	TBuf8<KSqlSrvMaxConfigStrLen> configStr;
	if(KConfigStringLen > 0)
		{
		configStr.Copy(buffer.MidTPtr(KDbFileNameLen, KConfigStringLen));
		}
	TSqlSrvFileData& fileData = Server().FileData();
	fileData.SetFromHandleL(aMessage, dbFileName, KCreated, KReadOnly, &configStr);
	iDrive = fileData.Drive();
	iDatabase = CSqlSrvDatabase::OpenL(fileData);
	}

/**
Processes the request for copying a database.

Only the database creator can copy the database if the database is a secure database.

Usage of the IPC call arguments:
Arg 0: [in]  source database file name length
Arg 1: [in]  source database file name
Arg 2: [in]  destination database file name length
Arg 3: [in]  destination database file name
*/
void CSqlSrvSession::DbCopyFileL(const RMessage2& aMessage)
	{
	const TInt KDbCnt = 2;											//"2" - because we have 2 dbases: src and dest
	const TInt KSrcDbIdx = 0;
	const TInt KDestDbIdx = 1;
	TInt fileNameLen[KDbCnt] = {aMessage.Int0(), aMessage.Int2()};
	TSqlSrvFileData& fileData = Server().FileData();
	TUint dbSecureFlag[KDbCnt];
	TUid  dbSID[KDbCnt] = {KNullUid, KNullUid};
	TFileName dbFileName[KDbCnt];
	//Initialize dbSecureFlag[], dbSID[] and dbFileName[] array elements
	for(TInt i=0;i<KDbCnt;++i)									
		{
		fileData.SetL(aMessage, fileNameLen[i], i * KDbCnt + 1);	//"i * KDbCnt + 1" is the RMessage2 parameter number: 1 for src db, 3 for dest db
		dbSecureFlag[i] = fileData.IsSecureFileNameFmt() ? 1 : 0;
		if(dbSecureFlag[i])
			{
			dbSID[i] = fileData.SecureUid();
			}
		dbFileName[i].Copy(fileData.FileName());
		}
	//It is not allowed to copy non-secure to a secure or secure to a non-secure database.
	if(dbSecureFlag[KSrcDbIdx] ^ dbSecureFlag[KDestDbIdx])
		{
		__SQLLEAVE(KErrPermissionDenied);	
		}
	//If this is a secure database "copy" operation, then...
	if(dbSecureFlag[KSrcDbIdx])
		{
		TUid callerSid = aMessage.SecureId();
		//A secure database can be copied only by its owner (database SID matches caller SID).
		if(callerSid != dbSID[KSrcDbIdx] || callerSid != dbSID[KDestDbIdx])
			{
			__SQLLEAVE(KErrPermissionDenied);	
			}
		}
	//Copy the database
	CFileMan* fileMan = CFileMan::NewL(fileData.Fs());
	CleanupStack::PushL(fileMan);
	__SQLLEAVE_IF_ERROR(fileMan->Copy(dbFileName[KSrcDbIdx], dbFileName[KDestDbIdx]));
	//"Copy" operation executed without errors. Now it is a time to turn off the read-only
	//flag of the target file (which may be on if the source file is on a read-only drive)
	__SQLLEAVE_IF_ERROR(fileData.Fs().SetAtt(dbFileName[KDestDbIdx], 0, KEntryAttReadOnly));
	CleanupStack::PopAndDestroy(fileMan);
	}

/**
Processes the request for deleting a database.

Only the database creator can delete the database if the database is a secure database.

Usage of the IPC call arguments:
Arg 0: [in]  database file name length
Arg 1: [in]  database file name
*/
void CSqlSrvSession::DbDeleteFileL(const RMessage2& aMessage)
	{	
	TSqlSrvFileData& fileData = Server().FileData();
	fileData.SetL(aMessage, aMessage.Int0(), 1);
	if(fileData.IsSecureFileNameFmt())
		{
		//A secure database can be deleted only by its owner (database SID matches caller SID).
		if(fileData.SecureUid() != aMessage.SecureId())
			{
			__SQLLEAVE(KErrPermissionDenied);	
			}
		}
	#ifdef _NOTIFY
	TPtrC fname = fileData.FileName();
	RDebug::Print(_L("--SrvSess, delete, fname=\"%S\"\r\n"), &fname);
	#endif		
	__SQLLEAVE_IF_ERROR(fileData.Fs().Delete(fileData.FileName()));
	}

/**
Processes the request for retrieving the last error message.

If the client side buffer size is not big enough, the function returns the needed 
buffer size + KSqlClientBufOverflowCode.
In this case the client must increase the buffer and try again to get the message.

Usage of the IPC call arguments:
Arg 0: [in]		Message buffer length in 16-bit characters
Arg 1: [in/out]	Message buffer

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
@panic SqlDb 4 Client panic. Negative client message buffer length (Arg 0).
*/
TInt CSqlSrvSession::DbLastErrorMessageL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TPtrC msg = iDatabase->LastErrorMessage();
	TInt msgLen = msg.Length();
	TInt bufSize = aMessage.Int0();
	__SQLPANIC_CLIENT(bufSize >= 0, aMessage, ESqlPanicBadArgument);
	if(msgLen <= bufSize)
		{
		aMessage.WriteL(1, msg);
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, (msgLen * sizeof(TText)));
		return 0;
		}
	return msgLen + KSqlClientBufOverflowCode;
	}
	
/**
Processes the request for retrieving the last inserted ROWID of this database connection.

Usage of the IPC call arguments:
Arg 0: [in/out]	Receiving buffer

@panic SqlDb 2 Client panic. The database object is not yet created yet (iDatabase is NULL).
*/
void CSqlSrvSession::DbLastInsertedRowIdL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TInt64 rowid = iDatabase->LastInsertedRowId();
	aMessage.WriteL(0, TPtrC8(reinterpret_cast <const TUint8*> (&rowid), sizeof(rowid)));
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(rowid));
	}

/**
Processes the request for retrieving the database security policies.

The method leaves with KErrNotSupported if the database is not a secure database.

If the client side buffer size is not big enough, the function returns the needed 
buffer size + KSqlClientBufOverflowCode.
In this case the client must increase the buffer and try again to get the database security policy.

Usage of the IPC call arguments:
Arg 0: [in]		security policy buffer length in bytes
Arg 1: [in/out]	buffer for the database security policies

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
TInt CSqlSrvSession::DbGetSecurityPolicyL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	const CSqlSecurityPolicy* securityPolicy = iDatabase->SecurityPolicy();
	if(!securityPolicy)
		{
		__SQLLEAVE(KErrNotSupported);
		}
	const RSqlBufFlat& bufFlat = securityPolicy->BufFlat();
	TInt size = bufFlat.Size();
	if(size <= aMessage.Int0())
		{
		aMessage.WriteL(1, bufFlat.BufDes());
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, size);
		return 0;
		}
	return size + KSqlClientBufOverflowCode;
	}

/**
If an error occurs during the execution the function leaves with the error code.
Possible non-leaving return values:
 - KErrNone              - the function has completed successfully;
 - Positive return value - the length of the column, which means - the destination buffer is too small.
                           This return value is possible only with text or binary columns.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).

Usage of the IPC call arguments: 
Arg 0: [in]		(8/16-bit character length of SQL statement) | (expected column value type << 24).
Arg 1: [in]		SQL statement.
Arg 2: [in]		Byte max length of the receiving buffer
Arg 3: [in/out]	The receiving buffer
*/
TInt CSqlSrvSession::DbScalarFullSelectL(const RMessage2& aMessage, TBool aIsText16)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TUint sqlLen = static_cast <TUint> (aMessage.Int0()) & 0x00FFFFFF;
	TSqlColumnType  colType = static_cast <TSqlColumnType> ((static_cast <TUint> (aMessage.Int0()) & 0xFF000000) >> 24);
	TInt columnCount = -1;
	TInt paramCount = -1;
	CSqlSrvStatement*  stmt = aIsText16 ? 
								CSqlSrvStatement::NewLC(iDatabase->RawDbHandle(), ReadString16ZL(aMessage, 1, sqlLen), columnCount, paramCount) :
								CSqlSrvStatement::NewLC(iDatabase->RawDbHandle(), ReadString8ZL(aMessage, 1, sqlLen), columnCount, paramCount);
	if(columnCount != 1 || paramCount != 0)
		{
		__SQLLEAVE(KErrArgument);	
		}
	TInt err = stmt->Next();
	if(err == KSqlAtRow)
		{
		err = GetColumnValueL(aMessage, *stmt, colType);
		}
	else
		{
		__SQLLEAVE(err == KSqlAtEnd ? KErrNotFound : err);
		}
	CleanupStack::PopAndDestroy(stmt);
	return err;
	}

/**
@return True, if the database is in transaction, false otherwise.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
TBool CSqlSrvSession::DbInTransaction(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	return iDatabase->InTransaction();
	}

/**
Main database size in bytes.

@return Main database size in bytes.

@leave KErrNoMemory, an out of memory condition has occurred,
                     Note that the function may also leave with some other system wide errors or 
                     database specific errors categorised as ESqlDbError,
	   KErrTooBig,   The database is very big and the size cannot fit in a 32-bit signed integer.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
TInt CSqlSrvSession::DbSizeL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TInt64 size = iDatabase->SizeL();
	if(size > KMaxTInt)
		{
		__SQLLEAVE(KErrTooBig);
		}
	return size;
	}

/**
Retrieves the database size and free space.

Usage of the IPC call arguments:
Arg 0: [in/out]	Points to a RSqlDatabase::TSize object, where the database size and free space values
			    will be copied.
Arg 1: [in]		The database name length in 16-bit characters
Arg 2: [in]		The attached database name or KNullDesC for the main database

@leave KErrNoMemory, an out of memory condition has occurred,
                     Note that the function may also leave with some other system wide errors or 
                     database specific errors categorised as ESqlDbError.
       KErrBadName, Invalid database name

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
void CSqlSrvSession::DbSize2L(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	const TInt KDbNameLen = aMessage.Int1();
	if(KDbNameLen < 0 || KDbNameLen > KMaxFileName)
		{
		__SQLLEAVE(KErrBadName);
		}
	TPtrC dbName(KNullDesC);
	if(KDbNameLen > 0)
		{
		dbName.Set(ReadString16L(aMessage, 2, KDbNameLen));
		}
	TPckgBuf<RSqlDatabase::TSize> data;
	data().iSize = iDatabase->SizeL(dbName);
	data().iFree = iDatabase->FreeSpaceL(dbName);
	aMessage.WriteL(0, data);
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(RSqlDatabase::TSize));
	}

/**
Runs database compaction.

Usage of the IPC call arguments:
Arg 0: [in]	    How much space in bytes should be compacted, all free pages should be removed if the
				parameter value is RSqlDatabase::EMaxCompaction.
				Note that the requested space to be compacted will be rounded up to the nearest page count,
				e.g. request for removing 1 byte will remove one free page from the database file. 
Arg 1: [in]		The database name length in characters
Arg 2: [in]		The attached database name or KNullDesC for the main database

@return The size of the removed free space

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
TInt CSqlSrvSession::DbCompactL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	const TInt KSize = aMessage.Int0();
	if(KSize < 0)
		{
		if(KSize != RSqlDatabase::EMaxCompaction)
			{
			__SQLLEAVE(KErrArgument);
			}
		}
	if(KSize == 0)
		{
		return 0;	
		}
	const TInt KDbNameLen = aMessage.Int1();
	if(KDbNameLen < 0 || KDbNameLen > KMaxFileName)
		{
		__SQLLEAVE(KErrBadName);
		}
	TPtrC dbName(KNullDesC);
	if(KDbNameLen > 0)
		{
		dbName.Set(ReadString16L(aMessage, 2, KDbNameLen));
		}
	return iDatabase->CompactL(KSize, dbName);
	}
	
/**
Usage of the IPC call arguments:
Arg 0: [in]		requested size of the space to be reserved - not used

The function leaves with KErrAlreadyExists if a drive space has been reserved already by this session.
*/
void CSqlSrvSession::DbReserveDriveSpaceL()
	{
	if(iDriveSpaceReserved)
		{
		__SQLLEAVE(KErrAlreadyExists);
		}
	RSqlDriveSpaceCol& driveSpaceCol = Server().DriveSpaceCol();
    if(!driveSpaceCol.Find(iDrive))
    	{
    	(void)driveSpaceCol.AddL(iDrive);
    	}
	
	iDriveSpaceReserved = ETrue;
	//Only iDriveSpaceReserved is set, nothing more needs to be done, because RSqlDriveSpaceCol::AddL() will
	//reserve a drive space on the specified drive.
	//Although it looks like that the implementation can ommit "iDriveSpaceReserved" flag, this flag plays important
	//role, because it is used to ensure that every "reserve drive space" request is matched by a "free drive space"
	//call.
	}
	
/**
If the client has been given an access to the reserved drive space, that access will be released.
*/
void CSqlSrvSession::DbFreeReservedSpace()
	{
	DbReleaseReserveAccess();
	iDriveSpaceReserved = EFalse;
	}
	
/**
The function leaves with KErrInUse if an access to the reserved drive space has been given to the client.
The function leaves with KErrNotFound if no drive space has been reserved for the drive, where the database file is.
*/
void CSqlSrvSession::DbGetReserveAccessL()
	{
	if(iDriveSpaceInUse)
		{
		__SQLLEAVE(KErrInUse);	
		}
	if(!iDriveSpaceReserved)
		{
		__SQLLEAVE(KErrNotFound);
		}
	CSqlDriveSpace* driveSpace = Server().DriveSpaceCol().Find(iDrive);
   	if(!driveSpace)
   		{
		__SQLLEAVE(KErrNotFound);
   		}
   	driveSpace->GetAccessL();
   	iDriveSpaceInUse = ETrue;
	}
	
/**
Releases the drive space reserve if it has been in use by this session (resp. client).
*/
void CSqlSrvSession::DbReleaseReserveAccess()
	{
	if(iDriveSpaceInUse)
		{
		CSqlDriveSpace* driveSpace = Server().DriveSpaceCol().Find(iDrive);
		if(driveSpace)
			{
   			driveSpace->ReleaseAccess();
			}
   		iDriveSpaceInUse = EFalse;
		}
	}

/**
Processes the request for attaching a secure or non-secure database.

Usage of the IPC call arguments: 
Arg 0: [in]	Database file name length (counted in 16-bit characters).
Arg 1: [in]	Database file name.
Arg 2: [in]	Logical database name length (counted in 16-bit characters).
Arg 3: [in]	Logical database name.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
void CSqlSrvSession::DbAttachL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TSqlSrvFileData& fileData = Server().FileData();
	fileData.SetL(aMessage, aMessage.Int0(), 1);
	TInt logicalDbNameLen = aMessage.Int2();
	if(logicalDbNameLen < 1 || logicalDbNameLen > KMaxFileName)
		{
		__SQLLEAVE(KErrBadName);
		}
	iDatabase->AttachDbL(fileData, ReadString16L(aMessage, 3, logicalDbNameLen));
	}

/**
Processes the request for attaching a database using file session and file handles sent by the client.

Usage of the IPC call arguments:
 - Arg 0: [in]  The 32 bits of the argument are used as follow:
 @code
 MSB                                                                                 LSB
 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
 Ro  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F  F F F F F F F F F F F
 @endcode
 Where:
 @code
  - "Ro" - read-only flag, true if the file is read-only;
  - "F"  - database file name length in 16-bit characters;
 @endcode
Arg 1: [in]  db names buffer
Arg 2: [in]  file session handle
Arg 3: [in]  database file handle

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
void CSqlSrvSession::DbAttachFromHandleL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	//Read-only flag, buffer length, buffer allocation
	TBool readOnly = (aMessage.Int0() & 0x80000000) != 0;
	const TInt KBufLen = aMessage.Int0() & 0x7FFFFFFF;
	if(KBufLen <= 0)
		{
		__SQLLEAVE(KErrArgument);
		}
	HBufC8* buf = HBufC8::NewLC(KBufLen);
	TPtr8 bufPtr = buf->Des();
	aMessage.ReadL(1, bufPtr);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, KBufLen);
	if(KBufLen != bufPtr.Length())
		{
		__SQLLEAVE(KErrArgument);
		}
	RDesReadStream in(bufPtr);
	TDes& dbFileName = Server().FileNameBuf();
	TDes16& dbName = Server().GetBuf16L(KMaxFileName);
	in >> dbFileName;
	in >> dbName;
	CleanupStack::PopAndDestroy(buf);
	TSqlSrvFileData& fileData = Server().FileData();
	fileData.SetFromHandleL(aMessage, dbFileName, EFalse, readOnly);
	iDatabase->AttachDbL(fileData, dbName);
	}
	
/**
Processes the request for detaching a secure or non-secure database.

Usage of the IPC call arguments: 
Arg 0: [in]	Logical database name length (counted in 16-bit characters).
Arg 1: [in]	Logical database name.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
*/
void CSqlSrvSession::DbDetachL(const RMessage2& aMessage)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	TInt logicalDbNameLen = aMessage.Int0();
	if(logicalDbNameLen < 1 || logicalDbNameLen > KMaxFileName)
		{
		__SQLLEAVE(KErrBadName);
		}
	iDatabase->DetachDbL(ReadString16L(aMessage, 1, logicalDbNameLen));
	}

/**
Reads a 16-bit string from the specified stream and returns it in zero-terminated
8-bit format in aNameOut.
If the string is of zero length then the substitute string provided will be used instead.

@param aStrm 				The read stream
@param aNameOut 			The output parameter that will contain the string read
@param aEmptyNameSubstitute The substitute string to use if the string to be read from
							the stream is zero length

@leave KErrNoMemory, 		 An out of memory condition has occurred;
	   KErrArgument, 		 The UTF-16 to UTF-8 string conversion failed;	
	   KErrBadName,	 		 The string has an invalid length;
*/
void CSqlSrvSession::ExtractNameL(RDesReadStream& aStrm, TDes8& aNameOut, const TDesC& aEmptyNameSubstitute)
	{
	 TBool replace = EFalse;
	 TInt32 len;
	 aStrm >> len;

	 if(len == 0)
	 	{
	   	if(aEmptyNameSubstitute.Length() > 0)
			{
		 	len = aEmptyNameSubstitute.Length();
	 	 	replace = ETrue;
			}
	 	else
			{
			__SQLLEAVE(KErrBadName);
			}
	  	}

	 if(len < 1 || len > KMaxFileName)
	  {
	  __SQLLEAVE(KErrBadName);
	  }

	 HBufC* buf = HBufC::NewLC(len + 1);
	 TPtr ptr = buf->Des();
	 if(replace)
	 	{
	  	ptr.Copy(aEmptyNameSubstitute);
	  	}
	 else
	 	{
	  	aStrm >> ptr;
	 	}
	 ptr.Append(0);
	 
	 if(!::UTF16ZToUTF8Z(ptr, aNameOut))
	  {
	  __SQLLEAVE(KErrArgument);
	  }
	  
	 CleanupStack::PopAndDestroy(buf);
	 }

/**
Processes the request for creating an IPC stream for accessing the content of a blob column.

@param aMessage The client request wrapped in an IPC message

@return The blob stream handle

@leave KErrNoMemory, 		 An out of memory condition has occurred;
	   KErrArgument, 		 The IPC data buffer length is invalid, or the ROWID is invalid,
	   				 		 or UTF-16 to UTF-8 string conversion failed;	
	   KErrBadDescriptor 	 The transferred data is bigger than the specified length;
	   KErrBadName,	 		 The table name, column name or database name has an invalid length;
       KErrPermissionDenied, The client does not have the required security capabilites for this operation; 						 
       						 Note that the function may also leave with some other system wide errors or 
                     		 database specific errors categorised as ESqlDbError.

@panic SqlDb 2 Client panic. The database object is not yet created (iDatabase is NULL)
@panic SqlDb 3 Client panic. Failed to create a blob stream handle

Usage of the IPC call arguments:
Arg 0: [in]	    The length of the IPC data buffer
Arg 1: [in]	    IPC data buffer containing blob parameters: table name, column name, rowid, mode, database name.
Arg 2: [out]	IPC buffer containing the blob stream handle
*/
TInt CSqlSrvSession::DbBlobSourceL(const RMessage2& aMessage)
	{	
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	
	iIpcStreams.AllocL();
	
	TInt ipcPrmLen = aMessage.Int0();
	if(ipcPrmLen < 1)
		{
		__SQLLEAVE(KErrArgument);
		}
	TDes8& ipcPrmDes = ReadString8ZL(aMessage, 1, ipcPrmLen);
	RDesReadStream strm(ipcPrmDes);
	
	TBuf8<KMaxFileName + 1> tblName;
	ExtractNameL(strm, tblName);
	
	TBuf8<KMaxFileName + 1> colName;
	ExtractNameL(strm, colName);
	
	TInt64 rowId;
	strm >> rowId;
	if(rowId == -1)
		{
		rowId = iDatabase->LastInsertedRowId();
		}
	if(rowId <= 0)
		{
		__SQLLEAVE(KErrArgument);
		}

	TInt32 tmp;	
	strm >> tmp;
	TBool isReadOnly = tmp != 0;
	
	TBuf8<KMaxFileName + 1> dbName;
	ExtractNameL(strm, dbName, KMainDb16);
						
	strm.Close();
			 		
	// If the database is secure then check that the client has the required capabilities for the operation
	TInt dbOpType = isReadOnly ? SQLITE_READ : SQLITE_UPDATE;
	if(CSqlSrvDatabase::AuthorizeCallback(iDatabase, dbOpType, (char*)tblName.Ptr(), (char*)colName.Ptr(), (char*)dbName.Ptr(), '\0') != SQLITE_OK)	
		{
		__SQLLEAVE(KErrPermissionDenied);	
		}

	// Create the stream buffer
	HBlobBuf* blobBuf = HBlobBuf::NewL(iDatabase->RawDbHandle(), dbName, tblName, colName, rowId, isReadOnly ? HBlobBuf::EReadOnly : HBlobBuf::EReadWrite);
	blobBuf->PushL();
		
	// Return the blob size to the client
	TPckgBuf<TIpcStreamBuf> ipcBuf;
	TInt size = blobBuf->SizeL();
	ipcBuf().iExt = size;
	
	// If this is a read stream then return the first client buffer-full of data
	TInt len = 0;
	if(isReadOnly && (size > 0))
		{
		len = Min(size, KIpcBufSize);
		blobBuf->ReadL(ipcBuf().iData, len);	
		}
	
	// Create the stream object
	HIpcStream* ipcStream = new (ELeave) HIpcStream(blobBuf, len);
	TInt strmHandle = iIpcStreams.Add(ipcStream);
	__SQLPANIC_CLIENT(strmHandle > 0, aMessage, ESqlPanicBadHandle);
	CleanupStack::Pop(blobBuf);

	// Send the size and data to the client
	aMessage.WriteL(2, ipcBuf);
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, size);

	return strmHandle;
	}
	
///////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////          Statement operations           //////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////

/**
Processes the request for preparing a 8/16-bit SQL statement.

@panic SqlDb 2 Client panic. iDatabase is NULL (the database object is not created yet).
@panic SqlDb 3 Client panic. Internal error - invalid statement handle.

Usage of the IPC call arguments:
Arg 0: [out]	Column count and parameter count
Arg 1: [in]		8/16-bit character length of SQL statement
Arg 2: [in]		SQL statement
*/
TInt CSqlSrvSession::StmtPrepareL(const RMessage2& aMessage, TBool aIsText16)
	{
	__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
	iStatements.AllocL();
	TInt columnCount = -1;
	TInt paramCount = -1;
	TUint len = static_cast <TUint> (aMessage.Int1());
	CSqlSrvStatement*  stmt = aIsText16 ? 
						CSqlSrvStatement::NewLC(iDatabase->RawDbHandle(), ReadString16ZL(aMessage, 2, len), columnCount, paramCount) :
						CSqlSrvStatement::NewLC(iDatabase->RawDbHandle(), ReadString8ZL(aMessage, 2, len), columnCount, paramCount);
	TPckgBuf<TSqlIpcData> data;
	data().iPrm1 = static_cast <TUint32> (columnCount);
	data().iPrm2 = static_cast <TUint32> (paramCount);
	aMessage.WriteL(0, data);
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(TSqlIpcData));
	TInt stmtHandle = iStatements.Add(stmt);
	__SQLPANIC_CLIENT(stmtHandle > 0, aMessage, ESqlPanicBadHandle);
	CleanupStack::Pop(stmt);
	return stmtHandle;
	}

/**
Processes the request for executing the SQL statement.

@param aFunction ESqlSrvStmtExec, ESqlSrvStmtAsyncExec, ESqlSrvStmtBindExec, ESqlSrvStmtBindExecRowId, ESqlSrvStmtAsyncBindExec

Usage of the IPC call arguments:
Arg 0: [in]    parameter buffer length in bytes (if aFunction == ESqlSrvStmtBindExec || aFunction == ESqlSrvStmtAsyncBindExec)
Arg 1: [in]    parameter buffer 		(if aFunction == ESqlSrvStmtBindExec || aFunction == ESqlSrvStmtAsyncBindExec)
*/
TInt CSqlSrvSession::StmtExecL(const RMessage2& aMessage, TInt aStmtHandle, TSqlSrvFunction aFunction)
	{	
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	if(aFunction == ESqlSrvStmtBindExec || aFunction == ESqlSrvStmtAsyncBindExec)
		{
		DoStmtBindL(aMessage, stmt);
		}
	__SQLLEAVE_IF_ERROR(stmt.Exec());
	return iDatabase->LastChangesCount();
	}

/**
Processes the request for moving the SQL statement on the next record.

If the call does not fail, the only valid acceptable return codes should be KSqlAtRow and KSqlAtEnd.

@panic SqlDb 7 In _DEBUG mode. The call completed with no error but the return code is not KSqlAtRow or KSqlAtEnd.

Usage of the IPC call arguments:
Arg 0: [in]    parameter buffer length in bytes (if aFunction == ESqlSrvStmtBindNext)
Arg 1: [in]    parameter buffer 		(if aFunction == ESqlSrvStmtBindNext)
Arg 2: [in]    client side column buffer length in bytes
Arg 3: [out]   column buffer
*/
TInt CSqlSrvSession::StmtNextL(const RMessage2& aMessage, TInt aStmtHandle, TSqlSrvFunction aFunction)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	if(aFunction == ESqlSrvStmtBindNext)
		{
		DoStmtBindL(aMessage, stmt);
		}
	TInt err = stmt.Next();
	if(err == KSqlAtRow)
		{
		const RSqlBufFlat& bufFlat = stmt.ColumnValuesL();
		TInt size = bufFlat.Size();
		if(size > aMessage.Int2())
			{
			return size + KSqlClientBufOverflowCode;
			}
		aMessage.WriteL(3, bufFlat.BufDes());
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, size);
		}
	__SQLLEAVE_IF_ERROR(err);
	__SQLASSERT(err == KSqlAtRow || err == KSqlAtEnd, ESqlPanicInternalError);
	return err;
	}

/**
Processes the request for retrieving the statement column or parameter names.

If the client side buffer size is not big enough, the function returns the size + KSqlClientBufOverflowCode.
In this case the client must increase the buffer and try again to get the buffer only.

Usage of the IPC call arguments:
Arg 0: [in]		size of the client side buffer for the names (in bytes)
Arg 1: [out]	ipc buffer, column or parameter names
*/
TInt CSqlSrvSession::StmtNamesL(const RMessage2& aMessage, TInt aStmtHandle, TSqlSrvFunction aFunction)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	const RSqlBufFlat& namesBuf = aFunction == ESqlSrvStmtParamNames ? stmt.ParamNamesL() : stmt.ColumnNamesL();
	TInt size = namesBuf.Size();
	if(size <= aMessage.Int0())
		{
		aMessage.WriteL(1, namesBuf.BufDes());
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, size);
		return 0;
		}
	return size + KSqlClientBufOverflowCode;
	}

/**
Processes the request for accessing a large column value as a stream of bytes/characters.

Usage of the IPC call arguments:
Arg 0: [in]		column index (0 based)
Arg 2: [out]	ipc buffer, column source
*/
TInt CSqlSrvSession::StmtColumnSourceL(const RMessage2& aMessage, TInt aStmtHandle)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	TInt columnIndex = aMessage.Int0();
	TPtrC8 columnSource;
	TInt err = stmt.ColumnSource(columnIndex, columnSource);
	__SQLLEAVE_IF_ERROR(err);
	HIpcReadBuf* ipcBuf = HIpcReadBuf::NewL(columnSource);
	return NewOutputStreamL(aMessage, ipcBuf);
	}

/**
Processes the request for setting a large parameter value from a stream of bytes or 8/16-bit characters.

@panic SqlDb 3 Client panic. Internal error - invalid stream handle.

Usage of the IPC call arguments:
Arg 0: [in]		parameter index (0 based)
Arg 2: [out]	ipc buffer, parameter source
*/
TInt CSqlSrvSession::StmtParamSinkL(const RMessage2& aMessage, TInt aStmtHandle, TSqlSrvFunction aFunction)
	{
	iIpcStreams.AllocL();
	TInt parameterIndex = aMessage.Int0();
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	HSqlSrvStmtParamBuf::TDataType dataType = aFunction == ESqlSrvStmtBinParamSink ? HSqlSrvStmtParamBuf::EBinary : HSqlSrvStmtParamBuf::EText16;
	HSqlSrvStmtParamBuf* paramBuf = stmt.GetParamBufL(parameterIndex, dataType, HSqlSrvStmtParamBuf::EBufIpcStream);
	HIpcStream* ipcStream = new (ELeave) HIpcStream(paramBuf, 0);
	TInt strmHandle = iIpcStreams.Add(ipcStream);
	__SQLPANIC_CLIENT(strmHandle > 0, aMessage, ESqlPanicBadHandle);
	return strmHandle;
	}

/**
Usage of the IPC call arguments:
Arg 0: [in]		the client side buffer length in bytes
Arg 1: [out]	refers to a place where the buffer data will be copied to.
*/
void CSqlSrvSession::StmtGetBufFlatL(const RMessage2& aMessage, TInt aStmtHandle)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	const RSqlBufFlat& bufFlat = stmt.BufFlatL(static_cast <TSqlBufFlatType> (aMessage.Int0()));
	aMessage.WriteL(1, bufFlat.BufDes());
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, bufFlat.Size());
	}

/**
Usage of the IPC call arguments:
Arg 0: [in]		column index
Arg 1: [in]		column buffer length in bytes
Arg 2: [in/out]	column buffer
*/
void CSqlSrvSession::StmtColumnValueL(const RMessage2& aMessage, TInt aStmtHandle)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	TPtrC8 columnSource;
	TInt columnIndex = aMessage.Int0();
	TInt err = stmt.ColumnSource(columnIndex, columnSource);
	__SQLLEAVE_IF_ERROR(err);
	TInt len = aMessage.Int1();
	if(columnSource.Length() > len)
		{
		TPtr8 ptr(const_cast <TUint8*> (columnSource.Ptr()), columnSource.Length(), columnSource.Length());
		ptr.SetLength(len);
		aMessage.WriteL(2, ptr);
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, len);
		__SQLLEAVE(KErrOverflow);	
		}
	else
		{
		aMessage.WriteL(2, columnSource);
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, len);
		}
	}

/**
Usage of the IPC call arguments:
Arg 0: [in]    parameter buffer length in bytes
Arg 1: [in]    parameter buffer
*/
void CSqlSrvSession::DoStmtBindL(const RMessage2& aMessage, CSqlSrvStatement& aStmt)
	{
	TInt prmLen = aMessage.Int0();
	RSqlBufFlat& prmBuf = Server().GetFlatBufL(prmLen);
	aMessage.ReadL(1, prmBuf.BufPtr());
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, prmLen);
	aStmt.BindL(prmBuf);
	}

/**
Usage of the IPC call arguments:
Arg 0: [in]    	input buffer max length in 16-bit characters
Arg 1: [in/out]	buffer
*/
void CSqlSrvSession::StmtDeclColumnTypesL(const RMessage2& aMessage, TInt aStmtHandle)
	{
	CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage);
	HBufC* colTypesBuf = stmt.GetDeclColumnTypesL();
	CleanupStack::PushL(colTypesBuf);
	if(colTypesBuf->Des().Length() > aMessage.Int0())
		{
		__SQLLEAVE(KErrOverflow);
		}
	aMessage.WriteL(1, colTypesBuf->Des());
	SQLPROFILER_REPORT_IPC(ESqlIpcWrite, (colTypesBuf->Des().Length() * sizeof(TText)));
	CleanupStack::PopAndDestroy(colTypesBuf);
	}


//////////////////////////////////////////////////////////////////////////////////////////////////////////	
////////////////////////              Helper methods                     /////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////	

/**
Creates a new output IPC stream object using the aStreamBuf parameter as a stream buffer (stream data source).

This method immediately pushes aStreamBuf onto the cleanup stack before creating a new output IPC 
stream and so callers of this method should ensure that aStreamBuf is not already on the cleanup stack.

Returns the handle of the created stream.

@panic SqlDb 3 Client panic. Internal error - invalid stream handle.

Usage of the IPC call arguments:
Arg 2: [in/out]   IPC buffer
*/
TInt CSqlSrvSession::NewOutputStreamL(const RMessage2& aMessage, MStreamBuf* aStreamBuf)
	{
	aStreamBuf->PushL();
	iIpcStreams.AllocL();
	TInt size = aStreamBuf->SizeL();
	TPckgBuf<TIpcStreamBuf> ipcBuf;
	if(size > 0)						// read the first buffer-full
		{
		TInt len = Min(size, KIpcBufSize);
		aStreamBuf->ReadL(ipcBuf().iData, len);
		}
	TInt handle = 0;
	if(size < 0 || size > KIpcBufSize)
		{								// create the stream object
		HIpcStream* ipcStream = new (ELeave) HIpcStream(aStreamBuf, KIpcBufSize);
		handle = iIpcStreams.Add(ipcStream);
		__SQLPANIC_CLIENT(handle > 0, aMessage, ESqlPanicBadHandle);
		CleanupStack::Pop(aStreamBuf);
		}
	else								// no more data to send
		{
		CleanupStack::PopAndDestroy(aStreamBuf);
		}
	if(size >= 0)
		{
		ipcBuf().iExt = size;
		aMessage.WriteL(2, ipcBuf);
		SQLPROFILER_REPORT_IPC(ESqlIpcWrite, size);
		}
	return handle;
	}

/**
Reads a 8-bit string with "aByteLen" bytes length, which is in "aArgNum" argument of aMessage.
The string will be zero terminated after the "read" operation.
Returns TDes8 reference pointing to the zero-terminated string.

@leave KErrBadDescriptor The transferred data length is bigger than the aByteLen value 

@panic SqlDb 4 Client panic. Negative aByteLen value.
*/
TDes8& CSqlSrvSession::ReadString8ZL(const RMessage2& aMessage, TInt aArgNum, TInt aByteLen)
	{
	__SQLPANIC_CLIENT(aByteLen >= 0, aMessage, ESqlPanicBadArgument);
	TDes8& buf = Server().GetBuf8L(aByteLen + 1);
	aMessage.ReadL(aArgNum, buf);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, aByteLen);
	if(buf.Length() > aByteLen)
		{
		__SQLLEAVE(KErrBadDescriptor);
		}
	buf.Append(TChar(0));
	return buf;
	}

/**
Reads a 16-bit string with "aCharLen" character length, which is in "aArgNum" argument of aMessage.
The string will be zero terminated after the "read" operation.
Returns TDes16 reference pointing to the zero-terminated string.

@leave KErrBadDescriptor The transferred data length is bigger than the aCharLen value

@panic SqlDb 4 Client panic. Negative aCharLen value.
*/
TDes16& CSqlSrvSession::ReadString16ZL(const RMessage2& aMessage, TInt aArgNum, TInt aCharLen)
	{
	__SQLPANIC_CLIENT(aCharLen >= 0, aMessage, ESqlPanicBadArgument);
	TDes16& buf = Server().GetBuf16L(aCharLen + 1);
	aMessage.ReadL(aArgNum, buf);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, (aCharLen * sizeof(TText)));
	if(buf.Length() > aCharLen)
		{
		__SQLLEAVE(KErrBadDescriptor);
		}
	buf.Append(TChar(0));
	return buf;
	}

/**
Reads a 16-bit string with "aCharLen" character length, which is in "aArgNum" argument of aMessage.
Returns TDes16 reference pointing to the string.

@leave KErrBadDescriptor The transferred data length is bigger than the aCharLen value

@panic SqlDb 4 Client panic. Negative aCharLen value.
*/
TDes16& CSqlSrvSession::ReadString16L(const RMessage2& aMessage, TInt aArgNum, TInt aCharLen)
	{
	__SQLPANIC_CLIENT(aCharLen >= 0, aMessage, ESqlPanicBadArgument);
	TDes16& buf = Server().GetBuf16L(aCharLen);
	aMessage.ReadL(aArgNum, buf);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, (aCharLen * sizeof(TText)));
	if(buf.Length() > aCharLen)
		{
		__SQLLEAVE(KErrBadDescriptor);
		}
	return buf;
	}

/**
The method reads the message argument 1 data and constructs a CSqlSecurityPolicy object from the data.

@param aMessage Client request encapsulated in RMessage2 object.

@return A pointer to the created CSqlSecurityPolicy instance.

@leave KErrArgument, if aMessage argument 0 length is 0 or negative (no security data);
       KErrNoMemory, out of memory condition has occured.

Usage of the IPC call arguments:
Arg 2: [in]  security policies buffer length in bytes if aFunction is ESqlSrvDbCreateSecure
Arg 3: [in]  security policies buffer if aFunction is ESqlSrvDbCreateSecure
*/
CSqlSecurityPolicy* CSqlSrvSession::InternalizeSecurityPolicyL(const RMessage2& aMessage)
	{
	// Leave if there is no security policy data
	// The format of arg[2] is an unsigned int
	// with the policy length shifted and concated to the config length
	// the policy data is the first part of arg[3]
	const TUint KConfigStrLenBitWidth = 16;
	TInt securityPolicyLen = aMessage.Int2() >> KConfigStrLenBitWidth;
	if(securityPolicyLen < 1)
		{
		__SQLLEAVE(KErrArgument);
		}
	TSecurityPolicy defaultPolicy(TSecurityPolicy::EAlwaysFail);
	CSqlSecurityPolicy* dbPolicy = CSqlSecurityPolicy::NewLC(defaultPolicy);
	RSqlBufFlat& bufFlat = dbPolicy->BufFlat();
	if(securityPolicyLen > bufFlat.MaxSize())
		{
		__SQLLEAVE_IF_ERROR(bufFlat.ReAlloc(securityPolicyLen));
		}
	TPtr8& ptr = bufFlat.BufPtr();
	aMessage.ReadL(3, ptr);
	SQLPROFILER_REPORT_IPC(ESqlIpcRead, securityPolicyLen);
	// trim off config data if any
	TInt extraBytes = ptr.Length() - securityPolicyLen;
	if(extraBytes > 0)
		{
		ptr.Delete(securityPolicyLen, extraBytes);
		}
	CleanupStack::Pop(dbPolicy);
	return dbPolicy;
	}

/**
Reports how many objects are allocated by the client.
If the database connection is not in a test mode, the allocated memory cells count will be ignored.
*/
TInt CSqlSrvSession::CountResources()
	{
	return iStatements.Count() + iIpcStreams.Count() + (iDbResourceTestMode ? User::CountAllocCells() : 0);
	}

/**
Extracts from aMessage:
- function code;
- stream or statement handle;
The function will panic the client if aMessage contains bad function code or bad handle encoded in it.
*/
void CSqlSrvSession::Extract(const RMessage2& aMessage, TSqlSrvFunction& aFunction, TInt& aHandle)
	{
	TInt msgCode = aMessage.Function();
	aFunction = static_cast <TSqlSrvFunction> (KSqlSrvFunctionMask & msgCode);
	//All operations with code > ESqlSrvDbDelete require valid iDatabase object.
	if(aFunction > ESqlSrvDbDelete)
		{
		__SQLPANIC_CLIENT(iDatabase != NULL, aMessage, ESqlPanicInvalidObj);
		}
	if(aFunction >= ESqlSrvStmtClose)
		{
		//Extracting handle and handle type from the message code
		TSqlSrvHandleType handleType = static_cast <TSqlSrvHandleType> (msgCode & KSqlSrvHandleTypeMask);
		aHandle = (msgCode & KSqlSrvHandleMask) >> KSqlSrvHandleShiftBits;
		__SQLPANIC_CLIENT(aHandle > 0, aMessage, ESqlPanicBadArgument);
		if(aFunction >= ESqlSrvStmtClose && aFunction < ESqlSrvStreamBase)
			{
			__SQLPANIC_CLIENT(handleType == ESqlSrvStatementHandle, aMessage, ESqlPanicBadArgument);
			}
		else if(aFunction > ESqlSrvStreamBase)
			{
			__SQLPANIC_CLIENT(handleType == ESqlSrvStreamHandle, aMessage, ESqlPanicBadArgument);
			}
		}
	}

/**
The function reads aStmt column 0 value and copies it into the client buffer, accessed via aMessage argument.

If an error occurs during the execution the function leaves with the error code.
Possible non-leaving return values:
 - KErrNone              - the function has completed successfully;
 - Positive return value - the length of the column, which means - the destination buffer is too small.
                           This return value is possible only with text or binary columns.

Usage of the IPC call arguments: 
Arg 0: [in]		(8/16-bit character length of SQL statement) | (expected column value type << 24).
Arg 1: [in]		SQL statement.
Arg 2: [in]		Byte max length of the receiving buffer
Arg 3: [in/out]	The receiving buffer
*/
TInt CSqlSrvSession::GetColumnValueL(const RMessage2& aMessage, CSqlSrvStatement& aStmt, TSqlColumnType aColType)
	{
	TInt rc = KErrNone;
	switch(aColType)
		{
		case ESqlInt:
			{
			TInt val = aStmt.ColumnInt(0);
			aMessage.WriteL(3, TPtrC8(reinterpret_cast <const TUint8*> (&val), sizeof(val)));
			SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val));
			}
			break;
		case ESqlInt64:
			{
			TInt64 val = aStmt.ColumnInt64(0);
			aMessage.WriteL(3, TPtrC8(reinterpret_cast <const TUint8*> (&val), sizeof(val)));
			SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val));
			}
			break;
		case ESqlReal:
			{
			TReal val = aStmt.ColumnReal(0);
			aMessage.WriteL(3, TPtrC8(reinterpret_cast <const TUint8*> (&val), sizeof(val)));
			SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val));
			}
			break;
		case ESqlText:
		case ESqlBinary:
		default:
			{
			TPtrC8 val;
			if(aColType == ESqlText)
				{
				TPtrC textVal = aStmt.ColumnText(0);
				val.Set(reinterpret_cast <const TUint8*> (textVal.Ptr()), textVal.Length() * sizeof(TUint16));
				}
			else
				{
				val.Set(aStmt.ColumnBinary(0));
				}
			TInt len = val.Length();
			if(len > aMessage.Int2())
				{
				rc = aColType == ESqlText ? (TUint)len / sizeof(TUint16) : len;
				len = aMessage.Int2();
				}
			aMessage.WriteL(3, val.Left(len));
			SQLPROFILER_REPORT_IPC(ESqlIpcWrite, len);
			}
			break;
		}
	return rc;
	}
