diff -r 000000000000 -r bde4ae8d615e os/persistentdata/persistentstorage/sql/SRC/Server/SqlSrvSession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/os/persistentdata/persistentstorage/sql/SRC/Server/SqlSrvSession.cpp Fri Jun 15 03:10:57 2012 +0200 @@ -0,0 +1,1766 @@ +// Copyright (c) 2005-2010 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: +// NTT DOCOMO, INC - Fix for Bug 1915 "SQL server panics when using long column type strings" +// +// 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 "OstTraceDefinitions.h" +#ifdef OST_TRACE_COMPILER_IN_USE +#include "SqlSrvSessionTraces.h" +#endif +#include "SqlTraceDef.h" + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma BullseyeCoverage off + +#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) + { + TInt mode = aHeapFailureMode & (KDelayedDbHeapFailureMask - 1); + if(mode >= RAllocator::EBurstRandom && mode <= RAllocator::EBurstFailNext) + { + const TUint KBurst = 50; + User::__DbgSetBurstAllocFail(RHeap::EUser, static_cast (mode), aFailedAllocNumber, KBurst); + } + else + { + User::__DbgSetAllocFail(RHeap::EUser, static_cast (mode), aFailedAllocNumber); + } + } + +//Executes the delayed heap simulation failure, if the connection is in test mode +inline void CSqlSrvSession::DbSetDelayedAllocFail() + { + if(iDbResourceTestMode & KDelayedDbHeapFailureMask) + { + TInt mode = iDbResourceTestMode & (KDelayedDbHeapFailureMask - 1); + if(mode >= RAllocator::EBurstRandom && mode <= RAllocator::EBurstFailNext) + { + const TUint KBurst = 50; + User::__DbgSetBurstAllocFail(RHeap::EUser, static_cast (mode), iFailedAllocNumber, KBurst); + } + else + { + User::__DbgSetAllocFail(RHeap::EUser, static_cast (mode), 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 + +#pragma BullseyeCoverage on + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** +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 T& SqlSessObjFind(RDbObjContainer& aContainer, TInt aHandle, const RMessage2& aMessage) + { + T* obj = aContainer.Find(aHandle); + __SQLPANIC_CLIENT2(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; + SQL_TRACE_SESSION(OstTrace1(TRACE_INTERNALS, CONVERTSQLFULL2DISKFULLERRROR, "0;ConvertSqlFull2DiskFullErr;aError=KSqlErrFull;!HasFreeDiskSpace();aDrive=%d", (TInt)aDrive)); + } + 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() + { + SQL_TRACE_SESSION(OstTrace0(TRACE_INTERNALS, CSQLSRVSESSION_NEWL_ENTRY, "Entry;0;CSqlSrvSession::NewL")); + CSqlSrvSession* self = new (ELeave) CSqlSrvSession; + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + SQL_TRACE_SESSION(OstTrace1(TRACE_INTERNALS, CSQLSRVSESSION_NEWL_EXIT, "Exit;0x%X;CSqlSrvSession::NewL", (TUint)self)); + return self; + } + +/** +Frees the allocated by CSqlSrvSession instance resources and memory. +*/ +CSqlSrvSession::~CSqlSrvSession() + { + SQL_TRACE_SESSION(OstTraceExt2(TRACE_INTERNALS, CSQLSRVSESSION_CSQLSRVSESSION2_ENTRY, "Entry;0x%X;CSqlSrvSession::~CSqlSrvSession;iDatabase=0x%X", (TUint)this, (TUint)iDatabase)); + StopDbTestMode(); + DbFreeReservedSpace(); + iIpcStreams.Close(); + iStatements.Close(); + delete iDatabase; + SQL_TRACE_SESSION(OstTrace1(TRACE_INTERNALS, CSQLSRVSESSION_CSQLSRVSESSION2_EXIT, "Exit;0x%X;CSqlSrvSession::~CSqlSrvSession", (TUint)this)); + } + +/** +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); + } + __SQLTRACE_SESSIONEXPR(++iIpcCallCounter); + SQL_TRACE_SESSION(OstTraceExt4(TRACE_INTERNALS, CSQLSRVSESSION_SERVICEL_ENTRY, "Entry;0x%X;CSqlSrvSession::ServiceL;aMessage.Handle()=0x%X;funcCode=0x%X;iIpcCallCounter=%u", (TUint)this, (TUint)aMessage.Handle(), (TUint)funcCode, iIpcCallCounter)); + SQLPROFILER_IPC_START(iIpcCallCounter, 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: + retCode = 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); + } + SQL_TRACE_SESSION(OstTraceExt4(TRACE_INTERNALS, CSQLSRVSESSION_SERVICEL_EXIT, "Exit;0x%X;CSqlSrvSession::ServiceL;funcCode=0x%X;retCode=%d;iIpcCallCounter=%u", (TUint)this, (TUint)funcCode, retCode, iIpcCallCounter)); + } + +/** +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) + { + SQL_TRACE_SESSION(OstTraceExt4(TRACE_INTERNALS, CSQLSRVSESSION_SERVICEERROR_ENTRY, "Entry;0x%X;CSqlSrvSession::ServiceError;aMessage.Function()=0x%X;aError=%d;iIpcCallCounter=%u", (TUint)this, (TUint)aMessage.Function(), aError, iIpcCallCounter)); + 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"); + aMessage.Panic(KPanicCategory, ESqlPanicBadDescriptor); + } + SQLPROFILER_IPC_ERROR(iIpcCallCounter, static_cast (KSqlSrvFunctionMask & aMessage.Function()), + iDatabase ? (TUint)iDatabase->RawDbHandle() : 0, aError); + CSession2::ServiceError(aMessage, aError); + SQL_TRACE_SESSION(OstTraceExt3(TRACE_INTERNALS, CSQLSRVSESSION_SERVICEERROR_EXIT, "Exit;0x%X;CSqlSrvSession::ServiceError;aMessage.Function()=0x%X;iIpcCallCounter=%u", (TUint)this, (TUint)aMessage.Function(), iIpcCallCounter)); + } + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// Profiler operations /////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////////////////////////////// + +#pragma BullseyeCoverage off + +/** +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 res; + iDatabase->QueryConfigL(res); + aMessage.WriteL(2, res); + } + else + { + TSqlSrvResourceProfiler::QueryL(aMessage); + } + } + +#pragma BullseyeCoverage on + +/////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////// 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 + +@panic SqlDb 1 Client panic. iDatabase is not NULL (it has been created already) +@panic SqlDb 4 Client panic. Negative or too big config string length +@panic SqlDb 4 Client panic. Negative security policy length, or zero length if the request is to create a secure database +*/ +void CSqlSrvSession::DbCreateObjectL(const RMessage2& aMessage, TSqlSrvFunction aFunction) + { + __SQLPANIC_CLIENT(!iDatabase, aMessage, ESqlPanicObjExists); + const TInt KSecurityPolicyLen = (aMessage.Int2() & 0x7fff0000) >> 16; + //If the security policy length is negative then this is a programming error. + __SQLPANIC_CLIENT(KSecurityPolicyLen >= 0, aMessage, ESqlPanicBadArgument); + const TInt KConfigStringLen = aMessage.Int2() & 0xffff; + //If KConfigStringLen is invalid then this is a programming error. + //If the client sends a too big config string - this is handled in the client side session. + __SQLPANIC_CLIENT((TUint)KConfigStringLen <= KSqlSrvMaxConfigStrLen, aMessage, ESqlPanicBadArgument); + RBuf8 securityAndConfigBuf; + CleanupClosePushL(securityAndConfigBuf); + if((KSecurityPolicyLen + KConfigStringLen) > 0) + { + securityAndConfigBuf.CreateL(KSecurityPolicyLen + KConfigStringLen); + aMessage.ReadL(3, securityAndConfigBuf); + SQLPROFILER_REPORT_IPC(ESqlIpcRead, (KSecurityPolicyLen + KConfigStringLen)); + } + TSqlSrvFileData& fileData = Server().FileData(); + TPtrC8 configStr(KNullDesC8); + if(KConfigStringLen > 0) + { + configStr.Set(securityAndConfigBuf.Mid(KSecurityPolicyLen));//the first part of the buffer is for the security policies + } + 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: + { + __SQLPANIC_CLIENT(KSecurityPolicyLen > 0, aMessage, ESqlPanicBadArgument); + 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 = CreateSecurityPolicyL(securityAndConfigBuf.Left(KSecurityPolicyLen)); + iDatabase = CSqlSrvDatabase::CreateSecureL(fileData, policy); + } + break; + case ESqlSrvDbOpen: + iDatabase = CSqlSrvDatabase::OpenL(fileData); + break; + default: + __SQLLEAVE(KErrArgument); + break; + } + CleanupStack::PopAndDestroy(&securityAndConfigBuf); + } + +/** +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; + __SQLPANIC_CLIENT((TUint)KConfigStringLen <= KSqlSrvMaxConfigStrLen, aMessage, ESqlPanicBadArgument); + __SQLPANIC_CLIENT((TUint)KDbFileNameLen <= KMaxFileName, aMessage, ESqlPanicBadArgument); + 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 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;iCopy(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); + } + } + __SQLTRACE_SESSIONVAR(TPtrC fname(fileData.FileName())); + SQL_TRACE_SESSION(OstTraceExt2(TRACE_INTERNALS, CSQLSRVSESSION_DBDELETEFILEL, "0x%X;CSqlSrvSession::DbDeleteFileL;file='%S'", (TUint)this, __SQLPRNSTR(fname))); + __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 (&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 (aMessage.Int0()) & 0x00FFFFFF; + TSqlColumnType colType = static_cast ((static_cast (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((TUint)KDbNameLen > KMaxFileName) + { + __SQLLEAVE(KErrBadName); + } + TPtrC dbName(KNullDesC); + if(KDbNameLen > 0) + { + dbName.Set(ReadString16L(aMessage, 2, KDbNameLen)); + } + TPckgBuf 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((TUint)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). +@panic SqlDb 4 Client panic. Invalid IPC data, an indication of a problme in client side sql library. +*/ +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; + __SQLPANIC_CLIENT(KBufLen > 0, aMessage, ESqlPanicBadArgument); + 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); + } + } + __ASSERT_DEBUG(len > 0, __SQLPANIC2(ESqlPanicInternalError));//The "if" above should have hanled the case with "len == 0" + if((TUint)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 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. +@panic SqlDb 4 Client panic. IPC buffer length is 0. + +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); + + TInt ipcPrmLen = aMessage.Int0(); + __SQLPANIC_CLIENT(ipcPrmLen > 0, aMessage, ESqlPanicBadArgument); + + iIpcStreams.AllocL(); + + TDes8& ipcPrmDes = ReadString8ZL(aMessage, 1, ipcPrmLen); + RDesReadStream strm(ipcPrmDes); + + TBuf8 tblName; + ExtractNameL(strm, tblName); + + TBuf8 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 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 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 (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 data; + data().iPrm1 = static_cast (columnCount); + data().iPrm2 = static_cast (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); + __ASSERT_DEBUG(err == KSqlAtRow || err == KSqlAtEnd, __SQLPANIC(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 (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 (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); + } + +/** +Processes the request for retrieving the statement declared column type 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] input buffer max length in 16-bit characters +Arg 1: [out] ipc buffer, declared column type names +*/ +TInt CSqlSrvSession::StmtDeclColumnTypesL(const RMessage2& aMessage, TInt aStmtHandle) + { + CSqlSrvStatement& stmt = ::SqlSessObjFind(iStatements, aStmtHandle, aMessage); + const RSqlBufFlat& declColumnTypesBuf = stmt.GetDeclColumnTypesL(); + TInt size = declColumnTypesBuf.Size(); + if(size <= aMessage.Int0()) + { + aMessage.WriteL(1, declColumnTypesBuf.BufDes()); + return 0; + } + return size + KSqlClientBufOverflowCode; + } + + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////// 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(); + __ASSERT_DEBUG(size >= 0, __SQLPANIC(ESqlPanicInternalError)); + TPckgBuf ipcBuf; + // read the first buffer-full + TInt len = Min(size, KIpcBufSize); + aStreamBuf->ReadL(ipcBuf().iData, len); + TInt handle = 0; + if(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); + } + 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. + +@panic SqlDb 3 Client panic. The string length is not equal to aByteLen. If happens then it is an indication of a + problem inside client side sql library. +@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); + __SQLPANIC_CLIENT(buf.Length() == aByteLen, aMessage, ESqlPanicBadHandle); + 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. + +@panic SqlDb 3 Client panic. The string length is not equal to aCharLen. If happens then it is an indication of a + problem inside client side sql library. +@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))); + __SQLPANIC_CLIENT(buf.Length() == aCharLen, aMessage, ESqlPanicBadHandle); + 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. + +@panic SqlDb 3 Client panic. The string length is not equal to aCharLen. If happens then it is an indication of a + problem inside client side sql library. +@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))); + __SQLPANIC_CLIENT(buf.Length() == aCharLen, aMessage, ESqlPanicBadHandle); + return buf; + } + +/** +The method constructs a CSqlSecurityPolicy object from the passed as an argument descriptor. + +@param aSecurityPolicyData A descriptor with the security policy data. + +@return A pointer to the created CSqlSecurityPolicy instance. + +@leave KErrNoMemory, out of memory condition has occured. +*/ +CSqlSecurityPolicy* CSqlSrvSession::CreateSecurityPolicyL(const TDesC8& aSecurityPolicyData) + { + TSecurityPolicy defaultPolicy(TSecurityPolicy::EAlwaysFail); + CSqlSecurityPolicy* dbPolicy = CSqlSecurityPolicy::NewLC(defaultPolicy); + RSqlBufFlat& bufFlat = dbPolicy->BufFlat(); + __SQLLEAVE_IF_ERROR(bufFlat.ReAlloc(aSecurityPolicyData.Length())); + bufFlat.BufPtr().Copy(aSecurityPolicyData); + 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 (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 (msgCode & KSqlSrvHandleTypeMask); + aHandle = (msgCode & KSqlSrvHandleMask) >> KSqlSrvHandleShiftBits; + __SQLPANIC_CLIENT(aHandle > 0, aMessage, ESqlPanicBadArgument); + if(aFunction < ESqlSrvStreamBase) + { + __SQLPANIC_CLIENT(handleType == ESqlSrvStatementHandle, aMessage, ESqlPanicBadArgument); + } + else + { + __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 (&val), sizeof(val))); + SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val)); + } + break; + case ESqlInt64: + { + TInt64 val = aStmt.ColumnInt64(0); + aMessage.WriteL(3, TPtrC8(reinterpret_cast (&val), sizeof(val))); + SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val)); + } + break; + case ESqlReal: + { + TReal val = aStmt.ColumnReal(0); + aMessage.WriteL(3, TPtrC8(reinterpret_cast (&val), sizeof(val))); + SQLPROFILER_REPORT_IPC(ESqlIpcWrite, sizeof(val)); + } + break; + case ESqlText: + case ESqlBinary: + default: + { + TPtrC8 val; + if(aColType == ESqlText) + { + TPtrC textVal = aStmt.ColumnTextL(0); + val.Set(reinterpret_cast (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; + }