persistentstorage/sql/SRC/Client/SqlBlob.cpp
changeset 0 08ec8eefde2f
child 23 26645d81f48d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/SRC/Client/SqlBlob.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,467 @@
+// Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "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 <s32mem.h>
+#include "SqlDb.h"
+#include "SqlPanic.h"
+#include "SqlDatabaseImpl.h"
+#include "IPCBuf.h"
+
+// The maximum time (100 milliseconds) that a block of data is allowed to take to be read/written by TSqlBlob.
+// If the time taken is longer than this value then the next block size used will be less
+const TInt KTimeThresholdInMicroSecs = 100000; 
+// The largest block size used by TSqlBlob is 32Kb
+const TInt KLargestBlockSize = 32 * 1024; 
+// The client-side buffer will be used instead if the calculated block size is 2Kb or less 
+const TInt KUseClientBufferThreshold = 2 * 1024; 
+
+// Prepare an IPC buffer containing the parameter values
+static HBufC8* PrepareIpcParamBufLC(const TDesC& aTableName, const TDesC& aColumnName, TInt64 aRowId, 
+								  TBool aReadOnly, const TDesC& aDbName)
+	{
+	HBufC8* ipcPrmBuf = HBufC8::NewLC(aTableName.Size() + sizeof(TDesC) + 
+									  aColumnName.Size() + sizeof(TDesC) +
+									  sizeof(aRowId) + sizeof(aReadOnly) + 
+									  aDbName.Size() + sizeof(TDesC) + sizeof(TInt32) * 3);
+	TPtr8 ipcPrmDes = ipcPrmBuf->Des();
+	RDesWriteStream strm(ipcPrmDes);
+	
+	strm << static_cast <TInt32> (aTableName.Length()) 
+	     << aTableName 
+	     << static_cast <TInt32> (aColumnName.Length()) 
+	     << aColumnName 
+	     << aRowId 
+	     << static_cast<TInt32>(aReadOnly) 
+	     << static_cast <TInt32> (aDbName.Length()) 
+	     << aDbName;
+	strm.CommitL();
+	strm.Close();
+	return ipcPrmBuf;
+	}
+
+// Create an IPC stream, sending the IPC buffer containing the parameter values
+static MStreamBuf* CreateIpcStreamL(RSqlDbSession& aDbSession, const TDesC8& aIpcPrmBuf)
+	{
+	TIpcArgs ipcArgs;
+	ipcArgs.Set(0, aIpcPrmBuf.Length());
+	ipcArgs.Set(1, &aIpcPrmBuf);
+	HIpcBuf* ipcBuf = HIpcBuf::NewL(aDbSession, ::MakeMsgCode(ESqlSrvDbBlobSource, ESqlSrvNoHandle, 0), ipcArgs);
+	return ipcBuf;
+	}	
+	
+// Used by TSqlBlob to calculate the appropriate block size to use for the next transfer of blob data
+static TInt CalculateBlockSize(TInt aRemainingDataSize, TInt aPreviousBlockSize, TBool aTimeThresholdBreached)
+	{
+	TInt blockSize = aPreviousBlockSize;
+	if(aTimeThresholdBreached)
+		{
+		// The time threshold was breached using the previous block size
+		// so we will reduce the block size by a factor of 2
+		blockSize /= 2;
+		if(blockSize <= KUseClientBufferThreshold)
+			{
+			// Just use the client-side IPC buffer size if things get to this stage
+			blockSize = KIpcBufSize; 
+			}
+		}	
+	return aRemainingDataSize <= blockSize ? aRemainingDataSize : blockSize;
+	}
+		
+// Retrieve the data in blocks
+static void DoReadInBlocksL(RSqlBlobReadStream& aStrm, TDes8& aDestBuffer, TInt aBlobSize)
+	{
+	TInt remainingDataSize = aBlobSize;	
+	TBool timeThresholdBreached = EFalse;
+	TInt blockSize = KLargestBlockSize;
+	TPtr8 ptr((TUint8*)aDestBuffer.Ptr(), aDestBuffer.MaxSize());
+	
+	while(remainingDataSize > 0)
+		{	
+		// Calculate the block size to use for this iteration, based on 
+		// how much data is remaining and how fast the previous read was
+		blockSize = CalculateBlockSize(remainingDataSize, blockSize, timeThresholdBreached);
+		
+		// Read the block of data, timing how long it takes
+		TTime t1, t2;
+		t1.HomeTime();
+		aStrm.ReadL(ptr, blockSize);
+		t2.HomeTime();
+		TTimeIntervalMicroSeconds readTime = t2.MicroSecondsFrom(t1);
+		timeThresholdBreached = (readTime.Int64() > KTimeThresholdInMicroSecs);
+	
+		// Update how much data is still to be read, and the buffer pointer
+		remainingDataSize -= blockSize;
+		__SQLASSERT(remainingDataSize >= 0, ESqlPanicInternalError);
+		ptr.Set((TUint8*)(ptr.Ptr() + blockSize), 0, remainingDataSize);
+		}	
+	aDestBuffer.SetLength(aBlobSize);
+	}
+	
+// Send the data in blocks
+static void DoWriteInBlocksL(RSqlBlobWriteStream& aStrm, const TDesC8& aData, TInt aDataSize)
+	{
+	TInt remainingDataSize = aDataSize;	
+	TBool timeThresholdBreached = EFalse;
+	TInt blockSize = KLargestBlockSize;
+	
+	while(remainingDataSize > 0)
+		{	
+		// Calculate the block size to use for this iteration, based on 
+		// how much data is remaining and how fast the previous write was
+		blockSize = CalculateBlockSize(remainingDataSize, blockSize, timeThresholdBreached);
+
+		// Write the block of data, timing how long it takes
+		TTime t1, t2;
+		t1.HomeTime();
+		aStrm.WriteL(aData.Right(remainingDataSize), blockSize);
+		t2.HomeTime();
+		TTimeIntervalMicroSeconds writeTime = t2.MicroSecondsFrom(t1);
+		timeThresholdBreached = (writeTime.Int64() > KTimeThresholdInMicroSecs);
+	
+		// Update how much data is still to be written
+		remainingDataSize -= blockSize;
+		__SQLASSERT(remainingDataSize >= 0, ESqlPanicInternalError);
+		}	
+	aStrm.CommitL();	
+	}
+	
+// The data is returned in a UTF-8 buffer that is allocated on the heap
+static HBufC8* ReadLC(RSqlDatabase& aDb, 	
+				   	  const TDesC& aTableName, 
+				   	  const TDesC& aColumnName, 
+				   	  TInt64 aRowId,
+				   	  const TDesC& aDbName)	
+	{
+	RSqlBlobReadStream strm;
+	CleanupClosePushL(strm);
+	strm.OpenL(aDb, aTableName, aColumnName, aRowId, aDbName);		
+	TInt blobSize = strm.SizeL(); // size of the blob, in bytes
+	HBufC8* buf = HBufC8::NewLC(blobSize);
+	TPtr8 ptr = buf->Des();	
+	DoReadInBlocksL(strm, ptr, blobSize);
+	CleanupStack::Pop(); // buf
+	CleanupStack::PopAndDestroy(); // strm
+	CleanupStack::PushL(buf);
+	return buf;	
+	}
+
+// The data is returned in the UTF-8 buffer passed in
+static void ReadL(RSqlDatabase& aDb, 	
+				  const TDesC& aTableName, 
+				  const TDesC& aColumnName, 	
+				  TDes8& aBuffer,
+				  TInt64 aRowId,
+				  const TDesC& aDbName)
+	{	
+	RSqlBlobReadStream strm;
+	CleanupClosePushL(strm);
+	strm.OpenL(aDb, aTableName, aColumnName, aRowId, aDbName);		
+	TInt blobSize = strm.SizeL(); // size of the blob, in bytes
+	if(blobSize > aBuffer.MaxSize())
+		{
+		__SQLLEAVE(KErrOverflow);
+		}	
+	DoReadInBlocksL(strm, aBuffer, blobSize);
+	CleanupStack::PopAndDestroy(); // strm
+	}
+	
+// The data in the client-specified UTF-8 buffer is written
+static void WriteL(RSqlDatabase& aDb, 	
+				   const TDesC& aTableName, 
+				   const TDesC& aColumnName,
+				   const TDesC8& aData,	
+				   TInt64 aRowId,
+				   const TDesC& aDbName)
+	{
+	RSqlBlobWriteStream strm;
+	CleanupClosePushL(strm);
+	strm.OpenL(aDb, aTableName, aColumnName, aRowId, aDbName);
+	TInt dataSize = aData.Size(); // size of the data, in bytes
+	TInt blobSize = strm.SizeL(); // size of the blob, in bytes
+	if(dataSize > blobSize)
+		{
+		__SQLLEAVE(KErrEof);
+		}	
+	if(dataSize > 0)
+		{
+		DoWriteInBlocksL(strm, aData, dataSize);
+		}	
+	CleanupStack::PopAndDestroy(); // strm
+	}
+
+////////////////////////////////////////////////////////////////////////////////	
+			
+/**
+Gives access to a blob as a read-only stream of bytes.
+
+@param aDb        	  A connection to the database that contains the blob
+@param aTableName     The name of the table that contains the blob
+@param aColumnName    The name of the column that contains the blob
+@param aRowId         The ROWID of the record that contains the blob,
+					  or KSqlLastInsertedRowId if the last inserted ROWID 
+					  of the specified database connection is to be used
+@param aDbName  	  The name of the attached database if the blob is 
+					  contained in an attached database
+
+@leave KSqlErrGeneral, 		 Invalid database connection or table name or column name 
+							 or column type or ROWID or database name;							 
+	   KErrNoMemory, 		 An out of memory condition occurred;	   
+	   KErrArgument, 		 The ROWID is zero or negative or a UTF-16 to UTF-8 string conversion failed;
+  	   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 The database object is not yet created
+@panic SqlDb 3 Server failed to create a handle to the blob
+@panic SqlDb 4 In _DEBUG mode. Bad parameter value
+@panic SqlDb 7 In _DEBUG mode. NULL blob handle
+
+@capability None, if the aDb parameter represents a handle which operates on a non-secure database;
+            RSqlSecurityPolicy::EReadPolicy or 
+            RSqlSecurityPolicy::ESchemaPolicy database policy type, if the blob belongs to a secure database; 
+*/		
+EXPORT_C void RSqlBlobReadStream::OpenL(RSqlDatabase& aDb, const TDesC& aTableName, const TDesC& aColumnName, 
+										TInt64 aRowId, const TDesC& aDbName)
+	{
+	SQLUTRACE_PROFILER(this);
+	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KRSqlBlobParam16, &aDb, &aTableName, 
+			&aColumnName, aRowId, &aDbName));
+	
+	HBufC8* ipcPrmBuf = ::PrepareIpcParamBufLC(aTableName, aColumnName, aRowId, ETrue, aDbName);
+	MStreamBuf* strm = ::CreateIpcStreamL(aDb.Impl().Session(), ipcPrmBuf->Des());
+	Attach(strm);
+	CleanupStack::PopAndDestroy(ipcPrmBuf);	
+	}
+	
+/**
+Returns the size of the blob object, in bytes.
+
+@return The size of the blob object, in bytes
+
+@leave One of the system-wide error codes
+
+@panic SqlDb 2 The stream buffer is NULL
+	
+@capability None
+*/
+EXPORT_C TInt RSqlBlobReadStream::SizeL()
+	{
+	SQLUTRACE_PROFILER(this);
+	
+	MStreamBuf* src = Source();
+	__SQLASSERT_ALWAYS(src != NULL, ESqlPanicInvalidObj);
+	return src->SizeL();
+	}
+
+/**
+Gives access to a blob as a writeable stream of bytes.
+
+@param aDb        	  A connection to the database that contains the blob
+@param aTableName     The name of the table that contains the blob
+@param aColumnName    The name of the column that contains the blob
+@param aRowId         The ROWID of the record that contains the blob,
+					  or KSqlLastInsertedRowId if the last inserted ROWID 
+					  of the specified database connection is to be used
+@param aDbName  	  The name of the attached database if the blob is 
+					  contained in an attached database
+
+@leave KSqlErrGeneral, 		 Invalid database connection or table name or column name or column 
+							 type or ROWID or database name, or the specified column is indexed;
+	   KErrNoMemory, 		 An out of memory condition occurred;	   
+	   KErrArgument, 		 The ROWID is zero or negative or a UTF-16 to UTF-8 string conversion failed;
+  	   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 The database object is not yet created
+@panic SqlDb 3 Server failed to create a handle to the blob
+@panic SqlDb 4 In _DEBUG mode. Bad parameter value
+@panic SqlDb 7 In _DEBUG mode. NULL blob handle
+
+@capability None, if the aDb parameter represents a handle which operates on a non-secure database;
+            RSqlSecurityPolicy::EWritePolicy or 
+            RSqlSecurityPolicy::ESchemaPolicy database policy type, if the blob belongs to a secure database; 
+*/							
+EXPORT_C void RSqlBlobWriteStream::OpenL(RSqlDatabase& aDb, const TDesC& aTableName, const TDesC& aColumnName, 
+										 TInt64 aRowId, const TDesC& aDbName)
+	{
+	SQLUTRACE_PROFILER(this);
+	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KRSqlBlobParam16, &aDb, &aTableName, 
+			&aColumnName, aRowId, &aDbName));
+	
+	HBufC8* ipcPrmBuf = ::PrepareIpcParamBufLC(aTableName, aColumnName, aRowId, EFalse, aDbName);
+	MStreamBuf* strm = ::CreateIpcStreamL(aDb.Impl().Session(), ipcPrmBuf->Des());
+	Attach(strm);
+	CleanupStack::PopAndDestroy(ipcPrmBuf);		
+	}
+
+/**
+Returns the size of the blob object, in bytes.
+
+@return The size of the blob object, in bytes
+
+@leave One of the system-wide error codes
+
+@panic SqlDb 2 The stream buffer is NULL
+	
+@capability None
+*/
+EXPORT_C TInt RSqlBlobWriteStream::SizeL()
+	{
+	SQLUTRACE_PROFILER(this);
+
+	MStreamBuf* sink = Sink();
+	__SQLASSERT_ALWAYS(sink != NULL, ESqlPanicInvalidObj);
+	return sink->SizeL();
+	}
+
+/**
+Retrieves the entire content of a blob and returns it to the client 
+in a heap allocated buffer which has been placed on the cleanup stack. 
+It is the responsibility of the client to destroy the returned buffer.
+
+@param aDb        	  A connection to the database that contains the blob
+@param aTableName     The name of the table that contains the blob
+@param aColumnName    The name of the column that contains the blob
+@param aRowId         The ROWID of the record that contains the blob,
+					  or KSqlLastInsertedRowId if the last inserted ROWID 
+					  of the specified database connection is to be used
+@param aDbName  	  The name of the attached database if the blob is 
+					  contained in an attached database
+
+@leave KSqlErrGeneral, 		 Invalid database connection or table name or column name 
+							 or column type or ROWID or database name;							 
+	   KErrNoMemory, 		 An out of memory condition occurred;	   
+	   KErrArgument, 		 The ROWID is zero or negative or a UTF-16 to UTF-8 string conversion failed;
+  	   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 The database object is not yet created
+@panic SqlDb 3 Server failed to create a handle to the blob
+@panic SqlDb 4 In _DEBUG mode. Bad parameter value
+@panic SqlDb 7 In _DEBUG mode. NULL blob handle or miscalculation of remaining data to be read
+
+@capability None, if the aDb parameter represents a handle which operates on a non-secure database;
+            RSqlSecurityPolicy::EReadPolicy or 
+            RSqlSecurityPolicy::ESchemaPolicy database policy type, if the blob belongs to a secure database; 
+*/
+EXPORT_C HBufC8* TSqlBlob::GetLC(RSqlDatabase& aDb, 	
+					     	     const TDesC& aTableName, 
+					     		 const TDesC& aColumnName, 	
+					     		 TInt64 aRowId,
+					     		 const TDesC& aDbName)
+	{
+	SQLUTRACE_PROFILER(0);
+	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KRSqlBlobParam16, &aDb, &aTableName, 
+			&aColumnName, aRowId, &aDbName));
+	
+	return ReadLC(aDb, aTableName, aColumnName, aRowId, aDbName);
+	}
+	
+/**
+Retrieves the entire content of a blob into a client specified buffer.
+
+@param aDb        	  A connection to the database that contains the blob
+@param aTableName     The name of the table that contains the blob
+@param aColumnName    The name of the column that contains the blob
+@param aRowId         The ROWID of the record that contains the blob,
+					  or KSqlLastInsertedRowId if the last inserted ROWID 
+					  of the specified database connection is to be used
+@param aDbName  	  The name of the attached database if the blob is 
+					  contained in an attached database
+
+@return KSqlErrGeneral, 	  Invalid database connection or table name or column name 
+							  or column type or ROWID or database name;							 
+	    KErrNoMemory, 		  An out of memory condition occurred;	   
+	    KErrArgument, 		  The ROWID is zero or negative or a UTF-16 to UTF-8 string conversion failed;
+ 	    KErrOverflow, 		  The specified buffer is not big enough to hold the entire blob;
+  	    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 The database object is not yet created
+@panic SqlDb 3 Server failed to create a handle to the blob
+@panic SqlDb 4 In _DEBUG mode. Bad parameter value
+@panic SqlDb 7 In _DEBUG mode. NULL blob handle or miscalculation of remaining data to be read
+
+@capability None, if the aDb parameter represents a handle which operates on a non-secure database;
+            RSqlSecurityPolicy::EReadPolicy or 
+            RSqlSecurityPolicy::ESchemaPolicy database policy type, if the blob belongs to a secure database; 
+*/
+EXPORT_C TInt TSqlBlob::Get(RSqlDatabase& aDb, 	
+					 		const TDesC& aTableName, 
+					 	    const TDesC& aColumnName, 	
+					 	    TDes8& aBuffer,
+					 		TInt64 aRowId,
+					 	    const TDesC& aDbName)
+	{
+	SQLUTRACE_PROFILER(0);
+	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KRSqlBlobParam16, &aDb, &aTableName, 
+			&aColumnName, aRowId, &aDbName));
+	
+	TRAPD(err, ReadL(aDb, aTableName, aColumnName, aBuffer, aRowId, aDbName));
+	return err;	
+	}
+
+/**
+Writes the data in a client specified buffer to a blob.
+
+@param aDb        	  A connection to the database that contains the blob
+@param aTableName     The name of the table that contains the blob
+@param aColumnName    The name of the column that contains the blob
+@param aRowId         The ROWID of the record that contains the blob,
+					  or KSqlLastInsertedRowId if the last inserted ROWID 
+					  of the specified database connection is to be used
+@param aDbName  	  The name of the attached database if the blob is 
+					  contained in an attached database
+
+@leave KSqlErrGeneral, 		 Invalid database connection or table name or column name or column 
+							 type or ROWID or database name, or the specified column is indexed;
+	   KErrNoMemory, 		 An out of memory condition occurred;	   
+	   KErrArgument, 		 The ROWID is zero or negative or a UTF-16 to UTF-8 string conversion failed;
+	   KErrEof,	     		 The data in the specified buffer is larger in size than the blob object;
+  	   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 The database object is not yet created
+@panic SqlDb 3 Server failed to create a handle to the blob
+@panic SqlDb 4 In _DEBUG mode. Bad parameter value
+@panic SqlDb 7 In _DEBUG mode. NULL blob handle or miscalculation of remaining data to be written
+
+@capability None, if the aDb parameter represents a handle which operates on a non-secure database;
+            RSqlSecurityPolicy::EWritePolicy or 
+            RSqlSecurityPolicy::ESchemaPolicy database policy type, if the blob belongs to a secure database; 
+*/
+EXPORT_C void TSqlBlob::SetL(RSqlDatabase& aDb, 	
+					  		 const TDesC& aTableName, 
+					  		 const TDesC& aColumnName,
+					  		 const TDesC8& aData,	
+					  		 TInt64 aRowId,
+					  		 const TDesC& aDbName)
+	{
+	SQLUTRACE_PROFILER(0);
+	SYMBIAN_TRACE_SQL_EVENTS_ONLY(UTF::Printf(UTF::TTraceContext(UTF::EInternals), KRSqlBlobParam16, &aDb, &aTableName, 
+			&aColumnName, aRowId, &aDbName));
+	
+	WriteL(aDb, aTableName, aColumnName, aData,	aRowId, aDbName);
+	}
+