persistentstorage/sql/TEST/t_sqlblob.cpp
changeset 0 08ec8eefde2f
child 16 6b6fd149daa2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/persistentstorage/sql/TEST/t_sqlblob.cpp	Fri Jan 22 11:06:30 2010 +0200
@@ -0,0 +1,2045 @@
+// 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 <e32test.h>
+#include <bautils.h>
+#include <sqldb.h>
+#include <s32file.h>
+
+RTest TheTest(_L("t_sqlblob test"));
+
+RSqlDatabase TheDb1;
+RSqlDatabase TheDb2;
+RSqlDatabase ThePrivateDb;
+
+_LIT(KTestDir, "c:\\test\\");
+_LIT(KTestDbName1, "c:\\test\\t_blob1.db");
+_LIT(KTestDbName2, "c:\\test\\t_blob2.db");
+_LIT(KAttachedDbName, "attached_db");
+_LIT(KPrivateSecureDb, "c:\\private\\1111C1CC\\ps.db"); 
+
+const TInt KLargeDataBufLen = 2048;
+
+// A buffer containing 2Kb of data.
+// When it is used to write to a blob the data will exceed the size of the client buffer
+// (which is 8 bytes in debug, 1.5Kb on target) and will be immediately transferred to the server
+TBuf8<KLargeDataBufLen> TheLargeData;
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Test database delete functions
+
+void DeleteTestDbs()
+	{
+	TheDb1.Close();
+	TheDb2.Close();
+	ThePrivateDb.Close();
+	(void)RSqlDatabase::Delete(KTestDbName1);
+	(void)RSqlDatabase::Delete(KTestDbName2);
+	(void)RSqlDatabase::Delete(KPrivateSecureDb);
+	}
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Test macros and functions
+
+void Check(TInt aValue, TInt aLine)
+	{
+	if(!aValue)
+		{
+		DeleteTestDbs();
+		TheTest(EFalse, aLine);
+		}
+	}
+void Check(TInt aValue, TInt aExpected, TInt aLine)
+	{
+	if(aValue != aExpected)
+		{
+		DeleteTestDbs();
+		RDebug::Print(_L("*** Expected error: %d, got: %d\r\n"), aExpected, aValue);
+		TheTest(EFalse, aLine);
+		}
+	}
+#define TEST(arg) ::Check((arg), __LINE__)
+#define TEST2(aValue, aExpected) ::Check(aValue, aExpected, __LINE__)
+
+///////////////////////////////////////////////////////////////////////////////////////
+// Test database create functions
+
+void CreateTestDir()
+    {
+    RFs fs;
+	TInt err = fs.Connect();
+	TEST2(err, KErrNone);
+
+	err = fs.MkDir(KTestDir);
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+
+	err = fs.CreatePrivatePath(EDriveC);	
+	TEST(err == KErrNone || err == KErrAlreadyExists);
+	
+	fs.Close();
+	}
+
+void CreateTestDbs()
+	{
+	// Create t_blob1.db
+	TInt err = TheDb1.Create(KTestDbName1);
+	TEST2(err, KErrNone);
+	
+ 	err = TheDb1.Exec(_L("CREATE TABLE table1(I INTEGER, T TEXT, B BLOB)"));	
+	TEST(err >= 0);	
+	
+	// Create t_blob2.db
+	err = TheDb2.Create(KTestDbName2);
+	TEST2(err, KErrNone);
+
+	err = TheDb2.Exec(_L("CREATE TABLE table2(int INTEGER, text TEXT, blob BLOB)"));	
+	TEST(err >= 0);
+		
+	// Insert a blob value of 'FGFGFGFGFG' (10 characters in size)		
+	err = TheDb2.Exec(_L("INSERT INTO table2 VALUES(1, 'Text Data', x'46474647464746474647')"));
+	TEST2(err, 1);	
+	
+	// Create private secure db
+	err = ThePrivateDb.Create(KPrivateSecureDb);
+	TEST2(err, KErrNone);
+	
+	err = ThePrivateDb.Exec(_L("CREATE TABLE table3(age INTEGER, name TEXT, picture BLOB)"));	
+	TEST(err >= 0);
+	
+	// Insert a blob value of 'ABABABABABABABA' (15 characters in size)		
+	err = ThePrivateDb.Exec(_L("INSERT INTO table3 VALUES(31, 'John Smith', x'414241424142414241424142414241')"));
+	TEST2(err, 1);		
+	}
+	
+void CreateIndices()
+	{
+	TInt err = TheDb1.Exec(_L("CREATE INDEX textIdx1 on table1(T)"));	
+	TEST(err >= 0);
+	err = TheDb1.Exec(_L("CREATE INDEX blobIdx1 on table1(B)"));	
+	TEST(err >= 0);
+	
+	err = TheDb2.Exec(_L("CREATE INDEX textIdx2 on table2(text)"));	
+	TEST(err >= 0);
+	err = TheDb2.Exec(_L("CREATE INDEX blobIdx2 on table2(blob)"));	
+	TEST(err >= 0);
+	}
+	
+void RemoveIndices()
+	{
+	TInt err = TheDb1.Exec(_L("DROP INDEX textIdx1"));	
+	TEST(err >= 0);
+	err = TheDb1.Exec(_L("DROP INDEX blobIdx1"));	
+	TEST(err >= 0);
+
+	err = TheDb2.Exec(_L("DROP INDEX textIdx2"));	
+	TEST(err >= 0);
+	err = TheDb2.Exec(_L("DROP INDEX blobIdx2"));	
+	TEST(err >= 0);
+	}
+		
+void AttachTestDb2()
+	{
+	TInt err = TheDb1.Attach(KTestDbName2, KAttachedDbName);
+	TEST2(err, KErrNone);
+	}
+	
+void FillLargeDataBuf(TChar aChar = 'Z')
+	{
+	TheLargeData.Fill(aChar, KLargeDataBufLen);	
+	}
+	
+///////////////////////////////////////////////////////////////////////////////////////
+// Unit test functions
+
+void CheckBlobPropertiesL(TInt aBlobSize)
+	{
+	// Check properties of the last inserted blob 
+		
+	RSqlStatement stmt;
+	CleanupClosePushL(stmt);
+	TInt err = stmt.Prepare(TheDb1, _L("SELECT B FROM table1 WHERE ROWID == :Val"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindInt(paramIndex, TheDb1.LastInsertedRowId());
+	TEST2(err, KErrNone);
+	err = stmt.Next();
+	TEST2(err, KSqlAtRow);
+	
+	// Check the 'declared column type' is ESqlBinary
+	TSqlColumnType declColType;
+	err = stmt.DeclaredColumnType(0, declColType);
+	TEST2(declColType, ESqlBinary);
+	// Check the 'runtime column type' is ESqlBinary
+	TSqlColumnType colType = stmt.ColumnType(0);
+	TEST2(colType, ESqlBinary);
+	// Check the 'column size' is the size of the blob
+	TInt blobSize = stmt.ColumnSize(0);
+	TEST2(blobSize, aBlobSize);
+	// Check the 'column value' is not 'NULL' (even for a zeroblob)
+	TBool isNull = stmt.IsNull(0);
+	TEST2(isNull, EFalse);
+	// Check the 'column value' can be retrieved as a binary value
+	TPtrC8 binaryPtr = stmt.ColumnBinaryL(0);
+	TEST2(binaryPtr.Length(), aBlobSize);
+	// Check the 'column value' cannot be retrieved as a text value (it is of type ESqlBinary)
+	TPtrC textptr = stmt.ColumnTextL(0);
+	TEST2(textptr.Length(), 0);	
+	CleanupStack::PopAndDestroy(&stmt);
+	}
+
+void InsertBindZeroBlob(TInt aBlobSize)
+	{
+	// Insert a record that has had a zeroblob bound to it
+	RSqlStatement stmt;
+	TInt err = stmt.Prepare(TheDb1, _L("INSERT INTO table1 values(1, 'some text', :Val)"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindZeroBlob(paramIndex, aBlobSize);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	stmt.Close();
+	TEST2(err, 1);
+	}
+	
+void InsertSQLiteZeroBlob(TInt aBlobSize)
+	{		
+	// Insert a record that contains the 'zeroblob()' function
+	RSqlStatement stmt;
+	TInt err = stmt.Prepare(TheDb1, _L("INSERT INTO table1 values(2, 'more text', zeroblob(:Val))"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val")); 
+	TEST(paramIndex >= 0);	
+	err = stmt.BindInt(paramIndex, aBlobSize);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	stmt.Close();
+	TEST2(err, 1);
+	}
+
+void InsertBlobValueL(TInt aBlobSize)
+	{	
+	// Insert a record that contains an actual blob value
+	HBufC8* binaryData = HBufC8::NewLC(aBlobSize);
+	TPtr8 binaryDataPtr(binaryData->Des());
+	for(TInt i = 0; i < aBlobSize/2; ++i)
+		{
+		binaryDataPtr.Append(_L8("DE"));
+		}
+		
+	RSqlStatement stmt;
+	CleanupClosePushL(stmt);
+	TInt err = stmt.Prepare(TheDb1, _L("INSERT INTO table1 values(3, 'even more text', :Val)"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val")); 
+	TEST(paramIndex >= 0);	
+	err = stmt.BindBinary(paramIndex, binaryDataPtr);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	TEST2(err, 1);
+	CleanupStack::PopAndDestroy(2); // stmt, binaryData		
+	}	
+
+void StreamBlob1L(TInt aBlobSize) 
+	{	
+	// Blob 1 is a zeroblob of size aBlobSize
+	
+	// Read (zero) data from the blob
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	_LIT8(KFiveZeros, "\x0\x0\x0\x0\x0");
+	_LIT8(KTwentyZeros, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0");
+	TBuf8<50> data;
+	rdStrm.ReadL(data, 5);
+	TEST(data.Compare(KFiveZeros) == 0); // check 5 bytes of zero have been read
+	rdStrm.ReadL(data, 20);
+	TEST(data.Compare(KTwentyZeros) == 0);	// check 20 bytes of zero have been read
+	CleanupStack::PopAndDestroy(&rdStrm);
+	
+	// Write some actual data to the blob
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	wrStrm.WriteL(_L8("AABBCCDDEE")); // write 10 bytes
+	wrStrm.WriteL(_L8("FFGGHHIIJJ")); // write another 10 bytes
+	wrStrm.WriteL(_L8("KKLLMMNNOOPPQQRRSSTTUUVVWWXX")); // write another 28 bytes
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// Read back some of the blob data
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	rdStrm.ReadL(data, 4);
+	TEST(data.Compare(_L8("AABB")) == 0); // check the first 4 bytes
+	rdStrm.ReadL(data, 35);
+	TEST(data.Compare(_L8("CCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSST")) == 0); // check the next 35 bytes
+	rdStrm.ReadL(data, 19);
+	_LIT8(KTrailingZeros, "TUUVVWWXX\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0"); 
+	TEST(data.Compare(KTrailingZeros) == 0); // check the next 19 bytes (which includes some of the original zero bytes)
+	CleanupStack::PopAndDestroy(&rdStrm);
+	}
+
+void StreamText1L() 
+	{	
+	// Read data from the text column -
+	// the database encoding is UTF-16 so the text is stored as UTF-16
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("T"));
+	TBuf<50> dataUTF16;
+	rdStrm.ReadL(dataUTF16, 9);
+	TEST(dataUTF16.Compare(_L("some text")) == 0); 
+	CleanupStack::PopAndDestroy(&rdStrm);
+	
+	// Write some data to the text column (as UTF-16)
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("T"));
+	wrStrm.WriteL(_L("new text!")); // can only write up to the original size of the data - 9 chars
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// Read back some of the text (as UTF-16)
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("T"));
+	rdStrm.ReadL(dataUTF16, 9);
+	TEST(dataUTF16.Compare(_L("new text!")) == 0);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	
+	// Write some data to the text column (as UTF-8)
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("T"));
+	wrStrm.WriteL(_L8("try again"));
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// Read back some of the text (as UTF-8)
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("T"));
+	TBuf8<50> dataUTF8;
+	rdStrm.ReadL(dataUTF8, 9);
+	TEST(dataUTF8.Compare(_L8("try again")) == 0);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	}
+	
+void StreamBlob2L(TInt aBlobSize)
+	{
+	// Blob 2 is a zeroblob of size aBlobSize
+	
+	// Read (zero) data from the blob
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	_LIT8(KFifteenZeros, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0");
+	_LIT8(KSixtyOneZeros, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0");
+	TBuf8<200> data;
+	rdStrm.ReadL(data, 15);
+	TEST(data.Compare(KFifteenZeros) == 0); // check 15 bytes of zero have been read
+	rdStrm.ReadL(data, 61);
+	TEST(data.Compare(KSixtyOneZeros) == 0); // check 61 bytes of zero have been read
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm); 
+
+	// Write some actual data to the blob 
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	wrStrm.WriteL(_L8("SOMENEWDATASOMENEWDATAS")); // write 23 bytes
+	wrStrm.WriteL(_L8("OMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATA")); // write another 43 bytes
+	wrStrm.WriteL(_L8("SOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENE")); // write another 72 bytes
+	wrStrm.WriteL(_L8("WDATASOMENEWDATA")); // write another 16 bytes
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm); 
+	
+	// Read back some of the blob data
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	rdStrm.ReadL(data, 1);
+	TEST(data.Compare(_L8("S")) == 0);  // check the first byte
+	rdStrm.ReadL(data, 136);
+	TEST(data.Compare(_L8("OMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMENEWDATASOMEN")) == 0); // check the next 136 bytes
+	rdStrm.ReadL(data, 30);
+	_LIT8(KTrailingZeros, "EWDATASOMENEWDATA\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0"); 
+	TEST(data.Compare(KTrailingZeros) == 0); // check the next 30 bytes (which includes some of the original zero bytes)
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm);	
+	}
+	
+void StreamBlob3L(TInt aBlobSize)
+	{
+	// Blob 3 is a 'real' blob of value "DEDEDEDEDEDEDEDEDEDE"
+	
+	// Read some of the zero data
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	TBuf8<20> data;
+	rdStrm.ReadL(data, 3);
+	TEST(data.Compare(_L8("DED")) == 0); // check the first 3 bytes
+	rdStrm.ReadL(data, 12);
+	TEST(data.Compare(_L8("EDEDEDEDEDED")) == 0); // check the next 12 bytes
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm); 	
+	
+	// Write some new data to the blob 
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	wrStrm.WriteL(_L8("ABCDEF")); // write 6 bytes
+	wrStrm.WriteL(_L8("GHIJKLMNOPQ")); // write another 11 bytes
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);	
+	
+	// Read back some of the blob data
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize);
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("AB")) == 0); // check the first 2 bytes
+	rdStrm.ReadL(data, 7);
+	TEST(data.Compare(_L8("CDEFGHI")) == 0); // check the next 7 bytes
+	rdStrm.ReadL(data, 11);
+	TEST(data.Compare(_L8("JKLMNOPQEDE")) == 0); // check the next 11 bytes
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, aBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm);	
+
+	// Seek to position - the blob value is now "ABCDEFGHIJKLMNOPQEDE"
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TStreamPos pos(9);
+	rdStrm.Source()->SeekL(MStreamBuf::ERead, pos);
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("JK")) == 0);
+	rdStrm.ReadL(data, 5);
+	TEST(data.Compare(_L8("LMNOP")) == 0);
+	TStreamPos pos2(3);
+	rdStrm.Source()->SeekL(MStreamBuf::ERead, pos2);
+	rdStrm.ReadL(data, 4);
+	TEST(data.Compare(_L8("DEFG")) == 0);
+	TStreamPos pos3(21);
+	TRAPD(err, rdStrm.Source()->SeekL(MStreamBuf::ERead, pos3));
+	TEST2(err, KErrEof);
+	TStreamPos pos4(18);
+	rdStrm.Source()->SeekL(MStreamBuf::ERead, pos4);
+	TRAP(err, rdStrm.ReadL(data, 3));
+	TEST2(err, KErrEof);
+	CleanupStack::PopAndDestroy(&rdStrm);	
+
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TStreamPos pos5(7);
+	wrStrm.Sink()->SeekL(MStreamBuf::EWrite, pos5);
+	wrStrm.WriteL(_L8("ZZZZZ"));
+	wrStrm.WriteL(_L8("YYY"));
+	TStreamPos pos6(17);
+	wrStrm.Sink()->SeekL(MStreamBuf::EWrite, pos6);
+	wrStrm.WriteL(_L8("XXX"));
+	wrStrm.CommitL();
+	wrStrm.Close();
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	rdStrm.ReadL(data, 20);
+	TEST(data.Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);
+	rdStrm.Close();
+	TStreamPos pos7(21);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, wrStrm.Sink()->SeekL(MStreamBuf::EWrite, pos7));
+	TEST2(err, KErrEof);
+	TStreamPos pos8(18);
+	wrStrm.Sink()->SeekL(MStreamBuf::EWrite, pos8);
+	wrStrm.WriteL(_L8("TTT"));
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);
+	wrStrm.Close();
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	rdStrm.ReadL(data, 20);
+	TEST(data.Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);
+	CleanupStack::PopAndDestroy(2);	
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4099
+@SYMTestCaseDesc		Incremental blob tests on a database using streams.
+						Insert a zeroblob using RSqlStatement::BindZeroBlob(), read and write to
+					 	the blob using streams, also read and write to a text column using streams.
+						Tests the RSqlBlobReadStream and RSqlBlobWriteStream methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of incremental blob stream operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/	
+void StreamBindZeroBlobTestL()
+	{
+	const TInt KBlobSize = 300;
+	InsertBindZeroBlob(KBlobSize);
+	CheckBlobPropertiesL(KBlobSize);
+	StreamBlob1L(KBlobSize);
+	StreamText1L();
+	CheckBlobPropertiesL(KBlobSize);
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4100
+@SYMTestCaseDesc		Incremental blob tests on a database, using streams.
+						Insert a zeroblob using the SQLite function zeroblob(), 
+						read and write to the blob using streams.
+						Tests the RSqlBlobReadStream and RSqlBlobWriteStream methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of incremental blob stream operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/	
+void StreamSqliteZeroBlobTestL()
+	{
+	const TInt KBlobSize = 1500;
+	InsertSQLiteZeroBlob(KBlobSize);
+	CheckBlobPropertiesL(KBlobSize);
+	StreamBlob2L(KBlobSize);
+	CheckBlobPropertiesL(KBlobSize);
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4101
+@SYMTestCaseDesc		Incremental blob tests on a database, using streams.
+						Insert a real blob, read and write to the blob using streams.
+						Tests the RSqlBlobReadStream and RSqlBlobWriteStream methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of incremental blob stream operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void StreamRealBlobTestL()
+	{
+	const TInt KBlobSize = 20;
+	InsertBlobValueL(KBlobSize);
+	CheckBlobPropertiesL(KBlobSize);
+	StreamBlob3L(KBlobSize);
+	CheckBlobPropertiesL(KBlobSize);
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4102
+@SYMTestCaseDesc		Whole value blob retrieval tests on a database.
+						Retrieve the whole value of a blob object in one go.
+						Tests the TSqlBlob 'get' methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of whole value blob retrieval operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void GetWholeBlob3L()
+	{
+	// Blob 3 is a 'real' blob of value "ABCDEFGZZZZZYYYPQXXX"
+	const TInt KBlobSize = 20;
+	
+	// Get the whole content of Blob 3 in one go, using TSqlBlob::GetLC()
+	HBufC8* wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"));
+	TInt bufSize = wholeBuf->Size();
+	TEST2(bufSize, KBlobSize);
+	TEST(wholeBuf->Des().Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);	
+	CleanupStack::PopAndDestroy(wholeBuf); 
+
+	// Get the whole content of Blob 3 in one go, using TSqlBlob::Get()
+	HBufC8* buf = HBufC8::NewLC(KBlobSize);	
+	TPtr8 bufPtr(buf->Des());	  
+	TInt err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr);
+	TEST2(err, KErrNone); 
+	TEST(bufPtr.Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);	
+	CleanupStack::PopAndDestroy(buf); 
+	
+	// Get the whole content of Blob 3 in one go, using TSqlBlob::Get(),
+	// ensuring that a buffer larger than the blob can be used
+	HBufC8* largerBuf = HBufC8::NewLC(KBlobSize * 2);	
+	TPtr8 largerBufPtr(largerBuf->Des());	  
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), largerBufPtr);
+	TEST2(err, KErrNone); 
+	TEST(largerBufPtr.Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);	
+	CleanupStack::PopAndDestroy(largerBuf); 
+	
+	// Get the whole content of the blob in 2 chunks of 10 bytes
+	HBufC8* streamBuf = HBufC8::NewLC(10);
+	TPtr8 streamBufPtr(streamBuf->Des());
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL();
+	TEST2(size, KBlobSize);
+	HBufC8* combinedData = HBufC8::NewLC(KBlobSize);
+	TPtr8 combinedDataPtr(combinedData->Des());		
+	for(TInt i = 2; i > 0 ; --i)
+		{
+		rdStrm.ReadL(streamBufPtr, 10);
+		combinedDataPtr.Append(streamBufPtr);
+		}
+	TEST(combinedDataPtr.Compare(_L8("ABCDEFGZZZZZYYYPQXXX")) == 0);
+	CleanupStack::PopAndDestroy(3); // combinedDataRead, rdStrm, streamBuf
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4104
+@SYMTestCaseDesc		Whole value blob write tests on a database.
+						Write the whole value of a blob object in one go.
+						Tests the TSqlBlob 'set' methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of whole value blob write operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void SetWholeBlob3L()
+	{
+	// Blob 3 is a 'real' blob of value "KKKKKKKKKKKKKKKKKEDE"
+	TInt KBlobSize = 20;
+	
+	// Set the whole content of Blob 3 in one go
+	HBufC8* dataBuf = HBufC8::NewLC(KBlobSize);
+	TPtr8 dataPtr(dataBuf->Des());
+	dataPtr.Append(_L8("CDCDCDCDCDCDCDCDCDCD"));
+	TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), dataPtr);
+	CleanupStack::PopAndDestroy(dataBuf); 
+	
+	// Check that the new blob data was written
+	HBufC8* retrievedDataBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"));
+	TInt blobLength = retrievedDataBuf->Size();
+	TEST2(blobLength, KBlobSize);
+	TEST(retrievedDataBuf->Des().Compare(_L8("CDCDCDCDCDCDCDCDCDCD")) == 0);		
+	CleanupStack::PopAndDestroy(retrievedDataBuf);
+	
+	// Set the whole content of the blob in 2 chunks of 10 bytes
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = wrStrm.SizeL();
+	TEST2(size, KBlobSize);		
+	for(TInt i = 2; i > 0; --i)
+		{
+		wrStrm.WriteL(_L8("ZYZYZYZYZY")); 
+		}
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// Check that the new blob data was written
+	retrievedDataBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"));
+	blobLength = retrievedDataBuf->Size();
+	TEST2(blobLength, KBlobSize);
+	TEST(retrievedDataBuf->Des().Compare(_L8("ZYZYZYZYZYZYZYZYZYZY")) == 0);		
+	CleanupStack::PopAndDestroy(retrievedDataBuf);
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4106
+@SYMTestCaseDesc		Blob read and write tests on an attached database.
+						Performs streaming and whole value read and write operations
+						on a blob in an attached database to ensure that the 
+						RSqlBlobReadStream, RSqlBlobWriteStream and TSqlBlob methods 
+						can be used on an attached database.
+						Tests the RSqlBlobReadStream, RSqlBlobWriteStream and TSqlBlob methods.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of blob read and write operations on an attached database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void AttachDbTestL()
+	{
+	// Attach test db 2 to test db 1
+	AttachTestDb2();
+	
+	// Open a read stream on a blob in the attached database - 
+	// the blob in the single record has a value of "FGFGFGFGFG"
+	const TInt KAttachedBlobSize = 10;
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName);
+	TInt size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize);
+	TBuf8<20> data;
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("FG")) == 0);	// check the first 2 bytes
+	rdStrm.ReadL(data, 8);
+	TEST(data.Compare(_L8("FGFGFGFG")) == 0); // check the next 8 bytes
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm);	
+	
+	// Write some new data to the blob 
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName);
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize);
+	wrStrm.WriteL(_L8("LLLL")); // write 4 bytes
+	wrStrm.WriteL(_L8("MMMMM")); // write another 5 bytes
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize); 
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);	
+	
+	// Read back some of the blob data
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName);
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize);
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("LL")) == 0); // check the first 2 bytes
+	rdStrm.ReadL(data, 5);
+	TEST(data.Compare(_L8("LLMMM")) == 0); // check the next 5 bytes
+	rdStrm.ReadL(data, 3);
+	TEST(data.Compare(_L8("MMG")) == 0); // check the next 3 bytes
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KAttachedBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm);	
+	
+	// Get the entire blob in the attached database
+	HBufC8* wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName);
+	TInt blobLength = wholeBuf->Length();
+	TEST2(blobLength, KAttachedBlobSize);
+	TEST(wholeBuf->Des().Compare(_L8("LLLLMMMMMG")) == 0);	
+	CleanupStack::PopAndDestroy(wholeBuf); 
+		
+	TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), data, TheDb2.LastInsertedRowId(), KAttachedDbName);	 		 
+	TEST(data.Compare(_L8("LLLLMMMMMG")) == 0);	
+
+	// Set the entire blob in the attached database
+	data.Zero();
+	data.Append(_L8("STSTSTSTST"));
+	TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), data, TheDb2.LastInsertedRowId(), KAttachedDbName);
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName);
+	TEST(wholeBuf->Des().Compare(_L8("STSTSTSTST")) == 0);	
+	CleanupStack::PopAndDestroy(wholeBuf); 	
+	}	
+
+void BadParamReadStreamL()	
+	{
+	HBufC* tooLongName = HBufC::NewLC(KMaxFileName + 1);
+	TPtr tooLongNameDes = tooLongName->Des();
+	tooLongNameDes.Fill('A', KMaxFileName + 1);	
+
+	// RSqlBlobReadStream::OpenL()	
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	TRAPD(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("T"))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb2, _L("table1"), _L("B"))); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, rdStrm.OpenL(TheDb1, _L(""), _L("B"))); // empty table name
+	TEST2(err, KErrBadName);
+	TRAP(err, rdStrm.OpenL(TheDb1, tooLongNameDes, _L("B"))); // too long table name
+	TEST2(err, KErrBadName);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("invalidTableName"), _L("B"))); // invalid table name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L(""))); // empty column name
+	TEST2(err, KErrBadName);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), tooLongNameDes)); // too long column name
+	TEST2(err, KErrBadName);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("invalidColumnName"))); // invalid column name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("I"))); // invalid column type
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), -12)); // illegal ROWID
+	TEST2(err, KErrArgument);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), 99)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), KSqlLastInsertedRowId, _L("main"))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), KSqlLastInsertedRowId, _L(""))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("T"), KSqlLastInsertedRowId, _L("main"))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("T"), KSqlLastInsertedRowId, _L(""))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb2, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // wrong db connection
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), tooLongNameDes)); // too long attached db name
+	TEST2(err, KErrBadName);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), _L("invalidAttachedDbName"))); // invalid attached db name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("invalidTableName"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid table name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("invalidColumnName"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("int"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column type
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), 64, KAttachedDbName)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	CleanupStack::PopAndDestroy(tooLongName);		
+	
+	// RSqlBlobReadStream::Source()::SeekL()
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL();
+	TEST(size > 0);
+	TStreamPos pos(-1);
+	TRAP(err, rdStrm.Source()->SeekL(MStreamBuf::ERead, pos));
+	TEST2(err, KErrEof);
+	TStreamPos pos2(size - 1);
+	TRAP(err, rdStrm.Source()->SeekL(MStreamBuf::ERead, pos2));
+	TEST2(err, KErrNone);
+	TStreamPos pos3(size + 1);
+	TRAP(err, rdStrm.Source()->SeekL(MStreamBuf::ERead, pos3));
+	TEST2(err, KErrEof);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	
+	// RSqlBlobReadStream::ReadL()
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = rdStrm.SizeL();
+	TEST2(size, 20);
+	TBuf8<20> dataBuf;
+	TBuf8<50> tooBigDataBuf;	
+	TRAP(err, rdStrm.ReadL(tooBigDataBuf));
+	TEST2(err, KErrEof);
+	rdStrm.Close();
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(tooBigDataBuf, size + 1));
+	TEST2(err, KErrEof);
+	rdStrm.Close();
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(tooBigDataBuf, TChar('J'))); // doesn't find 'J' so tries to fill the buffer to its max size
+	TEST2(err, KErrEof);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	}
+	
+void BadParamWriteStreamL()
+	{
+	HBufC* tooLongName = HBufC::NewLC(KMaxFileName + 1);
+	TPtr tooLongNameDes = tooLongName->Des();
+	tooLongNameDes.Fill('A', KMaxFileName + 1);	
+	
+	// RSqlBlobWriteStream::OpenL()
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	TRAPD(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("T"))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb2, _L("table1"), _L("B"))); // wrong db connection
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L(""), _L("B"))); // empty table name
+	TEST2(err, KErrBadName);
+	TRAP(err, wrStrm.OpenL(TheDb1, tooLongNameDes, _L("B"))); // too long table name
+	TEST2(err, KErrBadName);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("invalidTableName"), _L("B"))); // invalid table name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L(""))); // empty column name
+	TEST2(err, KErrBadName);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), tooLongNameDes)); // too long column name
+	TEST2(err, KErrBadName);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("invalidColumnName"))); // invalid column name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("I"))); // invalid column type
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"), 0)); // illegal ROWID
+	TEST2(err, KErrArgument);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"), 99)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"), KSqlLastInsertedRowId, _L("main"))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"), KSqlLastInsertedRowId, _L(""))); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("T"), KSqlLastInsertedRowId, _L("main"))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("T"), KSqlLastInsertedRowId, _L(""))); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on a BLOB column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on a TEXT column)
+	TEST2(err, KErrNone);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb2, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // wrong db connection
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), tooLongNameDes)); // too long attached db name
+	TEST2(err, KErrBadName);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), _L("invalidAttachedDbName"))); // invalid attached db name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("invalidTableName"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid table name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("invalidColumnName"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column name
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("int"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column type
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), 64, KAttachedDbName)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral);
+	CleanupStack::PopAndDestroy(&wrStrm);
+	CleanupStack::PopAndDestroy(tooLongName);
+
+	// RSqlBlobWriteStream::Sink()::SeekL()
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = wrStrm.SizeL();
+	TEST(size > 0);
+	TStreamPos pos(-1);
+	TRAP(err, wrStrm.Sink()->SeekL(MStreamBuf::ERead, pos));
+	TEST2(err, KErrEof);
+	TStreamPos pos2(size - 1);
+	TRAP(err, wrStrm.Sink()->SeekL(MStreamBuf::ERead, pos2));
+	TEST2(err, KErrNone);
+	TStreamPos pos3(size + 1);
+	TRAP(err, wrStrm.Sink()->SeekL(MStreamBuf::ERead, pos3));
+	TEST2(err, KErrEof);
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// RSqlBlobWriteStream::WriteL()
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = wrStrm.SizeL();
+	TEST2(size, 20);
+	TBuf8<50> tooBigDataBuf;
+	tooBigDataBuf.Fill('B', 50);	
+	TRAP(err, wrStrm.WriteL(tooBigDataBuf));
+#ifdef _DEBUG
+	TEST2(err, KErrEof);
+#else
+	TEST2(err, KErrNone);
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);
+#endif
+	wrStrm.Close();
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, wrStrm.WriteL(tooBigDataBuf, size + 1));
+#ifdef _DEBUG
+	TEST2(err, KErrEof);
+#else
+	TEST2(err, KErrNone);
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);
+#endif
+	CleanupStack::PopAndDestroy(&wrStrm);
+	}
+	
+void BadParamGetL()
+	{
+	HBufC* tooLongName = HBufC::NewLC(KMaxFileName + 1);
+	TPtr tooLongNameDes = tooLongName->Des();
+	tooLongNameDes.Fill('A', KMaxFileName + 1);	
+
+	// TSqlBlob::GetLC()	
+	HBufC8* wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B")); // a successful get (on a BLOB column)
+	TInt blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("T")); // a successful get (on a TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	TRAPD(err, wholeBuf = TSqlBlob::GetLC(TheDb2, _L("table1"), _L("B"))); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L(""), _L("B"))); // empty table name
+	TEST2(err, KErrBadName); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, tooLongNameDes, _L("B"))); // too long table name
+	TEST2(err, KErrBadName); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("invalidTableName"), _L("B"))); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L(""))); // empty column name
+	TEST2(err, KErrBadName); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), tooLongNameDes)); // too long column name
+	TEST2(err, KErrBadName); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("invalidColumnName"))); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("I"))); // invalid column type
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"), -2)); // illegal ROWID
+	TEST2(err, KErrArgument); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"), 3731)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId(), _L("main")); // a successful get (on a BLOB column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId(), _L("")); // a successful get (on a BLOB column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("T"), TheDb1.LastInsertedRowId(), _L("main")); // a successful get (on a TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("T"), TheDb1.LastInsertedRowId(), _L("")); // a successful get (on a TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on a BLOB column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on a TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf);
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb2, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("invalidTableName"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("invalidColumnName"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("int"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column type
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), 345, KAttachedDbName)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), _L("invalidAttachedDbName"))); // invalid attached db name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), tooLongNameDes)); // too long attached db name
+	TEST2(err, KErrBadName); 
+
+	// TSqlBlob::Get()
+	HBufC8* buf = HBufC8::NewLC(50);
+	TPtr8 bufPtr(buf->Des());	  	
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr); // a successful get (on a BLOB column)
+	TEST2(err, KErrNone);
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("T"), bufPtr); // a successful get (on a TEXT column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb2, _L("table1"), _L("B"), bufPtr); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L(""), _L("B"), bufPtr); // empty table name
+	TEST2(err, KErrBadName); 
+	err = TSqlBlob::Get(TheDb1, tooLongNameDes, _L("B"), bufPtr); // too long table name
+	TEST2(err, KErrBadName);
+	err = TSqlBlob::Get(TheDb1, _L("invalidTableName"), _L("B"), bufPtr); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L(""), bufPtr); // empty column name
+	TEST2(err, KErrBadName); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), tooLongNameDes, bufPtr); // too long column name
+	TEST2(err, KErrBadName); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("invalidColumnName"), bufPtr); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("I"), bufPtr); // invalid column type
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr, 0); // illegal ROWID
+	TEST2(err, KErrArgument); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr, 3731); // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr, TheDb1.LastInsertedRowId(), _L("main")); // a successful get (on a BLOB column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), bufPtr, TheDb1.LastInsertedRowId(), _L("")); // a successful get (on a BLOB column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("T"), bufPtr, TheDb1.LastInsertedRowId(), _L("main")); // a successful get (on a TEXT column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("T"), bufPtr, TheDb1.LastInsertedRowId(), _L("")); // a successful get (on a TEXT column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on a BLOB column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("text"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on a TEXT column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb2, _L("table2"), _L("blob"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("invalidTableName"), _L("blob"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("invalidColumnName"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("int"), bufPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // invalid column type
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), bufPtr, 345, KAttachedDbName); // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), bufPtr, TheDb2.LastInsertedRowId(), _L("invalidAttachedDbName")); // invalid attached db name
+	TEST2(err, KSqlErrGeneral); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), bufPtr, TheDb2.LastInsertedRowId(), tooLongNameDes); // too long attached db name
+	TEST2(err, KErrBadName);
+	CleanupStack::PopAndDestroy(buf);	
+	
+	CleanupStack::PopAndDestroy(tooLongName);
+	}
+
+void BadParamSetL()
+	{
+	HBufC* tooLongName = HBufC::NewLC(KMaxFileName + 1);
+	TPtr tooLongNameDes = tooLongName->Des();
+	tooLongNameDes.Fill('A', KMaxFileName + 1);	
+	
+	// TSqlBlob::SetL()
+	TRAPD(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("twenty characters !!"))); // a successful set (on a BLOB column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("T"), _L8("twenty characters !!"))); // a successful set (on a TEXT column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb2, _L("table1"), _L("B"), _L8("twenty characters..."))); // wrong db connection
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L(""), _L("B"), _L8("twenty characters..."))); // empty table name
+	TEST2(err, KErrBadName); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, tooLongNameDes, _L("B"), _L8("twenty characters..."))); // too long table name
+	TEST2(err, KErrBadName);   
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("invalidTableName"), _L("B"), _L8("twenty characters..."))); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L(""), _L8("twenty characters..."))); // empty column name
+	TEST2(err, KErrBadName); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), tooLongNameDes, _L8("twenty characters..."))); // too long column name
+	TEST2(err, KErrBadName); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("invalidColumnName"), _L8("twenty characters..."))); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("I"), _L8("twenty characters..."))); // invalid column type
+	TEST2(err, KSqlErrGeneral);
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("twenty characters..."), -3));  // illegal ROWID
+	TEST2(err, KErrArgument);
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("twenty characters..."), 1113));  // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("!!!twenty characters"), TheDb1.LastInsertedRowId(), _L("main"))); // a successful set (on a BLOB column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("!!!twenty characters"), TheDb1.LastInsertedRowId(), _L(""))); // a successful set (on a BLOB column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("T"), _L8("!!!twenty characters"), TheDb1.LastInsertedRowId(), _L("main"))); // a successful set (on a TEXT column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("T"), _L8("!!!twenty characters"), TheDb1.LastInsertedRowId(), _L(""))); // a successful set (on a TEXT column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), _L8("10 chars!!"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful set (on a BLOB column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("text"), _L8("10 chars!!"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful set (on a TEXT column)
+	TEST2(err, KErrNone); 
+	TRAP(err, TSqlBlob::SetL(TheDb2, _L("table2"), _L("blob"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), KAttachedDbName)); // wrong db connection
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("invalidTableName"), _L("blob"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid table name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("invalidColumnName"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column name
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("int"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), KAttachedDbName)); // invalid column type
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), _L8("10 chars.."), 13, KAttachedDbName)); // invalid ROWID
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), _L("invalidAttachedDbName"))); // invalid attached db name
+	TEST2(err, KSqlErrGeneral); 	
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), _L8("10 chars.."), TheDb2.LastInsertedRowId(), tooLongNameDes)); // too long attached db name
+	TEST2(err, KErrBadName); 
+	
+	CleanupStack::PopAndDestroy(tooLongName);
+	}
+	
+void BadParamBindZeroBlobL()
+	{
+	// RSqlStatement::BindZeroBlob()
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	RSqlStatement stmt;
+	CleanupClosePushL(stmt);
+	TInt err = stmt.Prepare(TheDb2, _L("INSERT INTO table2 values(1, 'dummy text', :Val)"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindZeroBlob(paramIndex, -1); // a negative blob size
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	TEST2(err, 1);
+	stmt.Reset();
+	rdStrm.OpenL(TheDb2, _L("table2"), _L("blob"));
+	TInt size = rdStrm.SizeL(); // check the blob's size is 0 (0 is used if a negative number was specified)
+	TEST2(size, 0);
+	rdStrm.Close();
+	err = stmt.BindZeroBlob(paramIndex, 0); // a blob size of zero
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	TEST2(err, 1);
+	stmt.Reset();
+	rdStrm.OpenL(TheDb2, _L("table2"), _L("blob"));
+	size = rdStrm.SizeL(); // check the blob's size is 0
+	TEST2(size, 0);
+	// For subsequent test purposes make the last inserted record have a zeroblob > 0 size
+	err = stmt.BindZeroBlob(paramIndex, 1);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();
+	TEST2(err, 1);
+	CleanupStack::PopAndDestroy(&stmt);		
+	CleanupStack::PopAndDestroy(&rdStrm);	
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4107
+@SYMTestCaseDesc		Bad parameter tests for the methods of RSqlBlobReadStream, 
+						RSqlBlobWriteStream, TSqlBlob and RSqlStatement::BindZeroBlob().
+						Tests that the correct error code is returned when a bad parameter
+						is used in a call to one of the methods.
+						Tests the RSqlBlobReadStream, RSqlBlobWriteStream and TSqlBlob methods
+						and RSqlStatement::BindZeroBlob().
+@SYMTestPriority		High
+@SYMTestActions			Execution of bad parameter tests.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void BadParamTestL()
+	{
+	BadParamReadStreamL();	
+	BadParamWriteStreamL();	
+	BadParamGetL();
+	BadParamSetL();	
+	BadParamBindZeroBlobL();
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4108
+@SYMTestCaseDesc		Indexed column tests for the methods of RSqlBlobReadStream
+						and RSqlBlobWriteStream, to ensure that a blob or text column
+						that is indexed cannot be written to but can be read from 
+						(an SQLite restriction).
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of read and write operations on an indexed column in a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/	
+void IndexedColumnTestL()
+	{
+	// Create an index on the BLOB column and on the TEXT column in the main and attached databases
+	CreateIndices();
+
+	// Attempt to open a write stream on an indexed BLOB and an indexed TEXT column - this should not be permitted.
+	// (This is an SQLite restriction, but having an index on a large BLOB or a TEXT column is highly unlikely!)
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	TRAPD(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("B"))); // indexed BLOB column
+	TEST2(err, KSqlErrGeneral);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table1"), _L("T"))); // indexed TEXT column
+	TEST2(err, KSqlErrGeneral);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // indexed BLOB column
+	TEST2(err, KSqlErrGeneral);
+	wrStrm.Close();
+	TRAP(err, wrStrm.OpenL(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // indexed TEXT column
+	TEST2(err, KSqlErrGeneral);
+	CleanupStack::PopAndDestroy(&wrStrm);
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("twenty characters !!"))); // indexed BLOB column
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("T"), _L8("twenty characters !!"))); // indexed TEXT column
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("blob"), _L8("10 chars!!"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // indexed BLOB column
+	TEST2(err, KSqlErrGeneral); 
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table2"), _L("text"), _L8("10 chars!!"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // indexed TEXT column
+	TEST2(err, KSqlErrGeneral);
+	
+	// Attempt to open a read stream on an indexed BLOB and an indexed text COLUMN - this should be permitted
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("B"))); // a successful open (on an indexed BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table1"), _L("T"))); // a successful open (on an indexed TEXT column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on an indexed BLOB column)
+	TEST2(err, KErrNone);
+	rdStrm.Close();
+	TRAP(err, rdStrm.OpenL(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName)); // a successful open (on an indexed TEXT column)
+	TEST2(err, KErrNone);
+	CleanupStack::PopAndDestroy(&rdStrm);
+	HBufC8* wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("B")); // a successful get (on an indexed BLOB column)
+	TInt blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("T")); // a successful get (on an indexed TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("blob"), TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on an indexed BLOB column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf); 	
+	wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table2"), _L("text"), TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on an indexed TEXT column)
+	blobLength = wholeBuf->Length();
+	TEST(blobLength > 0);
+	CleanupStack::PopAndDestroy(wholeBuf);
+	HBufC8* buf = HBufC8::NewLC(50);
+	TPtr8 buffPtr(buf->Des());	  	
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), buffPtr); // a successful get (on an indexed BLOB column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("T"), buffPtr); // a successful get (on an indexed TEXT column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("blob"), buffPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on an indexed BLOB column)
+	TEST2(err, KErrNone); 
+	err = TSqlBlob::Get(TheDb1, _L("table2"), _L("text"), buffPtr, TheDb2.LastInsertedRowId(), KAttachedDbName); // a successful get (on an indexed TEXT column)
+	TEST2(err, KErrNone);
+	CleanupStack::PopAndDestroy(buf);
+	
+	RemoveIndices();
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4109
+@SYMTestCaseDesc		'End of file' tests for the methods of RSqlBlobReadStream
+						RSqlBlobWriteStream and TSqlBlob, to ensure that a client cannot
+						read beyond the end of a blob object or write beyond the end of a 
+						blob object and that an appropriate error code is returned.
+@SYMTestPriority		High
+@SYMTestActions			Execution of read and write operations beyond the end of a blob object.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/	
+void EofTestL()
+	{
+	// Use Blob 3 in table1, whose value is "!!!twenty characters"
+	
+	// Read Eof tests
+
+	const TInt KBlobSize = 20;	
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TInt size = rdStrm.SizeL(); 
+	TEST2(size, KBlobSize);
+	TBuf8<50> data;
+	TRAPD(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("!!!twenty characters")) == 0);
+	TRAP(err, rdStrm.ReadL(data, 1));
+	TEST2(err, KErrEof);
+	rdStrm.Close();
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, 21));
+	TEST2(err, KErrEof); 
+	rdStrm.Close();
+	
+	HBufC8* exactSizeBuf = HBufC8::NewLC(KBlobSize);	
+	TPtr8 exactSizeBufPtr(exactSizeBuf->Des());	  	  
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), exactSizeBufPtr);
+	TEST2(err, KErrNone); 
+	TEST(exactSizeBufPtr.Compare(_L8("!!!twenty characters")) == 0);
+	CleanupStack::PopAndDestroy(exactSizeBuf);	
+	
+	HBufC8* tooSmallBuf = HBufC8::NewLC(KBlobSize/2);	
+	TPtr8 tooSmallBufPtr(tooSmallBuf->Des());	  	  			  		  	  
+	err = TSqlBlob::Get(TheDb1, _L("table1"), _L("B"), tooSmallBufPtr);
+	TEST2(err, KErrOverflow); 	
+	CleanupStack::PopAndDestroy(tooSmallBuf);	
+	
+	// Write Eof tests
+	
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	size = wrStrm.SizeL();
+	TEST2(size, KBlobSize);
+	// For debug builds: This 20 bytes will be written to the blob straight away
+	// because 20 bytes is more than the 8 byte buffer
+	// For release builds: This 20 bytes will NOT be written to the blob straight away
+	// because there is room for it to be stored in the 1.5K byte buffer
+	TRAP(err, wrStrm.WriteL(_L8("a twenty char string"))); 
+	TEST2(err, KErrNone);
+	// For debug/release builds: This 1 byte will NOT be written to the blob straight away
+	// because there is room for it to be stored in the 8 byte/1.5K buffer
+	TRAP(err, wrStrm.WriteL(_L8("a")));
+	TEST2(err, KErrNone);
+	// For debug builds: The CommitL() call will cause an attempt to write the 1 byte 
+	// in the buffer to the blob - however, the stream's write position indicates 
+	// that the write would be beyond the end of the blob and so an error occurs
+	// For release builds: The CommitL() call will cause an attempt to write the 21 bytes 
+	// in the buffer to the blob - however, 21 bytes is larger than the size of the blob
+	// and so an error occurs
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);	
+	wrStrm.Close();
+	// For debug builds: Check that the blob value is now "a twenty char string"
+	// For release builds: Check that the blob value is still "!!!twenty characters"
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+#ifdef _DEBUG	
+	TEST(data.Compare(_L8("a twenty char string")) == 0);
+#else	
+	TEST(data.Compare(_L8("!!!twenty characters")) == 0);
+#endif
+	rdStrm.Close();
+	
+	TheDb1.Exec(_L("BEGIN"));
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	// For debug/release builds: These 3 bytes will be stored in the 8 byte/1.5K buffer
+	TRAP(err, wrStrm.WriteL(_L8("eee"))); 
+	TEST2(err, KErrNone);
+	// For debug builds: These 25 bytes will not fit in the 8 byte buffer and so an attempt 
+	// will be made to write these 25 bytes and the 3 buffered bytes to the blob - 
+	// however, the size of the blob is only 20 and so an error occurs
+	// For release builds: These 25 bytes will be stored in the 1.5K buffer
+	TRAP(err, wrStrm.WriteL(_L8("fffffffffffffffffffffffff"))); 
+#ifdef _DEBUG
+	TEST2(err, KErrEof);
+	wrStrm.Close();
+	TheDb1.Exec(_L("ROLLBACK"));
+
+	// Check that the blob value is still "a twenty char string"
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("a twenty char string")) == 0);
+	rdStrm.Close();
+#else
+	TEST2(err, KErrNone);
+	// For release builds: The CommitL() call will cause an attempt to write the 28 bytes 
+	// in the buffer to the blob - however, 28 bytes is larger than the size of the blob
+	// and so an error occurs
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);
+	wrStrm.Close();
+	TheDb1.Exec(_L("ROLLBACK"));
+	
+	// Check that the blob value is still "!!!twenty characters"
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("!!!twenty characters")) == 0);
+	rdStrm.Close();
+#endif
+
+	TheDb1.Exec(_L("BEGIN"));
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, wrStrm.WriteL(_L8("a string that is longer than 20 characters"))); 	
+#ifdef _DEBUG	
+	TEST2(err, KErrEof);
+	wrStrm.Close();
+	TheDb1.Exec(_L("ROLLBACK"));
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, wrStrm.WriteL(_L8("ggg"))); 
+	TEST2(err, KErrNone);
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrNone);
+	CleanupStack::PopAndDestroy(&wrStrm);	
+	// Check that the blob value is now "gggwenty char string"
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("gggwenty char string")) == 0);
+	rdStrm.Close();
+#else
+	TEST2(err, KErrNone);
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrEof);
+	wrStrm.Close();
+	TheDb1.Exec(_L("ROLLBACK"));
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, wrStrm.WriteL(_L8("hhh"))); 
+	TEST2(err, KErrNone);
+	TRAP(err, wrStrm.CommitL());
+	TEST2(err, KErrNone);
+	CleanupStack::PopAndDestroy(&wrStrm);		
+	// Check that the blob value is now "hhhtwenty characters"
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("hhhtwenty characters")) == 0);
+	rdStrm.Close();
+#endif
+
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("a twenty char string")));
+	TEST2(err, KErrNone); 
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("a twenty char string")) == 0);
+	rdStrm.Close();
+
+	TheDb1.Exec(_L("BEGIN"));
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("a string that is longer than 20 characters")));
+	TEST2(err, KErrEof);
+	TheDb1.Exec(_L("ROLLBACK"));
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); 
+	TEST(data.Compare(_L8("a twenty char string")) == 0);
+	rdStrm.Close();
+	
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("B"), _L8("less than 20")));
+	TEST2(err, KErrNone); 
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	TRAP(err, rdStrm.ReadL(data, KBlobSize));
+	TEST2(err, KErrNone); // changing only part of the blob data is permitted
+	TEST(data.Compare(_L8("less than 20r string")) == 0);
+	rdStrm.Close();
+
+	CleanupStack::PopAndDestroy(&rdStrm);	
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4110
+@SYMTestCaseDesc		General blob read and write tests on a private secure database,
+						to ensure that there are no security issues with the methods of 
+						RSqlBlobReadStream, RSqlBlobWriteStream and TSqlBlob.
+@SYMTestPriority		High
+@SYMTestActions			Execution of read and write operations on a private secure database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+						REQ5794
+*/	
+void PrivateSecureDbTestL()
+	{
+	// The blob has the value 'ABABABABABABABA'
+	
+	// Read data from the blob
+	const TInt KBlobSize = 15;
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(ThePrivateDb, _L("table3"), _L("picture"));
+	TInt size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KBlobSize); 
+	TBuf8<50> data;
+	rdStrm.ReadL(data, 5);
+	TEST(data.Compare(_L8("ABABA")) == 0); // check 5 bytes have been read
+	rdStrm.ReadL(data, 8);
+	TEST(data.Compare(_L8("BABABABA")) == 0);	// check the next 8 bytes have been read
+	rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KBlobSize); 
+	CleanupStack::PopAndDestroy(&rdStrm);
+
+	HBufC8* wholeBuf = TSqlBlob::GetLC(ThePrivateDb, _L("table3"), _L("picture"));
+	TInt blobLength = wholeBuf->Length();
+	TEST2(blobLength, KBlobSize);
+	TEST(wholeBuf->Des().Compare(_L8("ABABABABABABABA")) == 0);	
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	
+	HBufC8* buf = HBufC8::NewLC(KBlobSize);	
+	TPtr8 bufPtr(buf->Des());	  
+	TInt err = TSqlBlob::Get(ThePrivateDb, _L("table3"), _L("picture"), bufPtr);
+	TEST2(err, KErrNone); 
+	TEST(bufPtr.Compare(_L8("ABABABABABABABA")) == 0);	
+	CleanupStack::PopAndDestroy(buf); 
+	
+	// Write data to the blob 
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(ThePrivateDb, _L("table3"), _L("picture"));
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, KBlobSize); 
+	wrStrm.WriteL(_L8("AABBCC")); // write 6 bytes
+	wrStrm.WriteL(_L8("DD")); // write another 2 bytes
+	wrStrm.WriteL(_L8("EEFFG")); // write another 5 bytes
+	size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, KBlobSize); 
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	// Check that the new blob data was written
+	HBufC8* retrievedDataBuf = TSqlBlob::GetLC(ThePrivateDb, _L("table3"), _L("picture"));
+	blobLength = retrievedDataBuf->Size();
+	TEST2(blobLength, KBlobSize);
+	TEST(retrievedDataBuf->Des().Compare(_L8("AABBCCDDEEFFGBA")) == 0);		
+	CleanupStack::PopAndDestroy(retrievedDataBuf);
+	
+	HBufC8* dataBuf = HBufC8::NewLC(KBlobSize);
+	TPtr8 dataPtr(dataBuf->Des());
+	dataPtr.Append(_L8("CDCDCDCDCDCDCDC"));
+	TSqlBlob::SetL(ThePrivateDb, _L("table3"), _L("picture"), dataPtr);
+	CleanupStack::PopAndDestroy(dataBuf); 
+	
+	// Check that the new blob data was written
+	retrievedDataBuf = TSqlBlob::GetLC(ThePrivateDb, _L("table3"), _L("picture"));
+	blobLength = retrievedDataBuf->Size();
+	TEST2(blobLength, KBlobSize);
+	TEST(retrievedDataBuf->Des().Compare(_L8("CDCDCDCDCDCDCDC")) == 0);		
+	CleanupStack::PopAndDestroy(retrievedDataBuf);
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4111
+@SYMTestCaseDesc		Concurrent blob read and write tests using the 
+						methods of RSqlBlobReadStream and RSqlBlobWriteStream.
+						Tests that read and write operations on different blobs
+						can happen concurrently and that read operations on the
+						same blob from different streams can happen concurrently.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of concurrent blob read and write operations on a database.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/	
+void ConcurrentReadAndWriteTestL()
+	{	
+	// Insert a zeroblob of size 2Kb
+	InsertSQLiteZeroBlob(KLargeDataBufLen);
+	// Insert a zeroblob of size 4Kb 
+	InsertBindZeroBlob(4 * 1024);
+	
+	// Handles on different blobs
+	
+	// Write and read from different blobs
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId() - 1);
+
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	
+	wrStrm.WriteL(_L8("GHIJKL")); // blob2 is not updated in cache (as client buffer not full)
+	TBuf8<100> data;
+	rdStrm.ReadL(data, 6);
+	_LIT8(KSixZeros, "\x0\x0\x0\x0\x0\x0");
+	TEST(data.Compare(KSixZeros) == 0);		
+	wrStrm.CommitL(); // blob2 update is not committed (as the rdStrm handle is open)
+	wrStrm.Close();
+	rdStrm.ReadL(data, 6);
+	TEST(data.Compare(KSixZeros) == 0);	
+	rdStrm.Close();	// the blob2 update is committed
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId() - 1);
+	rdStrm.ReadL(data, 2); // read 2 bytes
+	_LIT8(KTwoZeros, "\x0\x0");
+	TEST(data.Compare(KTwoZeros) == 0);	
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	wrStrm.WriteL(TheLargeData); // blob2 is updated in the cache	
+	rdStrm.ReadL(data, 10); // read the next 10 bytes
+	_LIT8(KTenZeros, "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0");
+	TEST(data.Compare(KTenZeros) == 0);	
+	rdStrm.Close();
+	wrStrm.CommitL(); // the blob2 update is committed
+	CleanupStack::PopAndDestroy(2);
+	
+	// Write to different blobs via different streams
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId() - 1);
+
+	RSqlBlobWriteStream wrStrm2;
+	CleanupClosePushL(wrStrm2);
+	wrStrm2.OpenL(TheDb1, _L("table1"), _L("B"));
+	
+	wrStrm.WriteL(_L8("ABABABABABABAB"));
+	wrStrm2.WriteL(_L8("CDCDCD"));
+	wrStrm.WriteL(_L8("EFEF"));
+	wrStrm.Close();
+	wrStrm2.Close(); // the blob1 update is committed and the blob2 update is committed 
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId() - 1);
+	rdStrm.ReadL(data, 18);
+	TEST(data.Compare(_L8("ABABABABABABABEFEF")) == 0);	
+	rdStrm.Close();
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	rdStrm.ReadL(data, 6);
+	TEST(data.Compare(_L8("CDCDCD")) == 0);	
+	rdStrm.Close();
+	CleanupStack::PopAndDestroy(3); 
+	
+	// Read from different blobs via different streams
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"), TheDb1.LastInsertedRowId() - 1);
+
+	RSqlBlobReadStream rdStrm2;
+	CleanupClosePushL(rdStrm2);
+	rdStrm2.OpenL(TheDb1, _L("table1"), _L("B"));
+
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("AB")) == 0);		
+	rdStrm2.ReadL(data, 3);
+	TEST(data.Compare(_L8("CDC")) == 0);	
+	rdStrm.ReadL(data, 15);
+	TEST(data.Compare(_L8("ABABABABABABEFE")) == 0);		
+	rdStrm2.ReadL(data, 2);
+	TEST(data.Compare(_L8("DC")) == 0);	
+	CleanupStack::PopAndDestroy(2); 
+	
+	// Handles on the same blob
+	// NOTE: using different stream objects on the same blob is only
+	// safe when all of the stream objects are read streams - writing to 
+	// the same blob from different streams or writing and reading from 
+	// the same blob at the same time has undefined behaviour
+	
+	// Read from the same blob (blob2) via different streams
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+	wrStrm.WriteL(_L8("MNOPQR"));
+	CleanupStack::PopAndDestroy(); // the blob2 update is committed 
+
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb1, _L("table1"), _L("B"));
+
+	CleanupClosePushL(rdStrm2);
+	rdStrm2.OpenL(TheDb1, _L("table1"), _L("B"));
+
+	rdStrm.ReadL(data, 2);
+	TEST(data.Compare(_L8("MN")) == 0);		
+	rdStrm2.ReadL(data, 3);
+	TEST(data.Compare(_L8("MNO")) == 0);	
+	rdStrm.ReadL(data, 10);
+	TEST(data.Compare(_L8("OPQRZZZZZZ")) == 0);		
+	rdStrm2.ReadL(data, 15);
+	TEST(data.Compare(_L8("PQRZZZZZZZZZZZZ")) == 0);	
+	CleanupStack::PopAndDestroy(2); 
+	}
+	
+void UTF16TextL(TInt aTextSize)
+	{
+	// The text value is "test", size 8 bytes in UTF-16, aTextSize = 8
+	
+	// Try to get the whole content of the text as UTF-8, using TSqlBlob::GetLC()
+	HBufC8* wholeBuf = TSqlBlob::GetLC(TheDb1, _L("table1"), _L("T"));
+	TInt bufSize = wholeBuf->Size(); // get the number of bytes in the buffer
+	TEST2(bufSize, aTextSize);
+	CleanupStack::PopAndDestroy(wholeBuf); 
+	
+	// Try to get the whole content of the text as UTF-8, using TSqlBlob::Get()
+	HBufC8* buf = HBufC8::NewLC(aTextSize);	
+	TPtr8 bufPtr(buf->Des());	  
+	TInt err = TSqlBlob::Get(TheDb1, _L("table1"), _L("T"), bufPtr);
+	TEST2(err, KErrNone); 
+	bufSize = buf->Size(); // get the number of bytes in the buffer
+	TEST2(bufSize, aTextSize);
+	CleanupStack::PopAndDestroy(buf); 
+
+	// Try to set the whole content of the text as UTF-8, using TSqlBlob::SetL()
+	HBufC8* dataBuf = HBufC8::NewLC(aTextSize);
+	TPtr8 dataPtr(dataBuf->Des());
+	dataPtr.Append(_L8("OPOPOPOP"));
+	TRAP(err, TSqlBlob::SetL(TheDb1, _L("table1"), _L("T"), dataPtr));
+	TEST2(err, KErrNone); // can set 8 UTF-8 characters as this is 8 bytes
+	CleanupStack::PopAndDestroy(dataBuf); 	
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4112
+@SYMTestCaseDesc		UTF-16 text read and write tests using the UTF-8 methods of 
+						TSqlBlob.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of UTF-16 text read and write operations using 
+						UTF-8 descriptors.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10411
+                        REQ10418
+*/
+void UTF16FormatTestL()
+	{	
+	// Insert a record with a UTF-16 text value (text is encoded in UTF-16 by default) 
+	TInt err = TheDb1.Exec(_L("INSERT INTO table1 VALUES(1, 'test', x'46474647464746474647')"));
+	TEST2(err, 1);
+	const TInt KTextSize = 8; // 8 bytes (4 UTF-16 characters)
+	
+	// Now read and write the UTF-16 text value using UTF-8 methods
+	UTF16TextL(KTextSize);	
+	}
+	
+/**
+@SYMTestCaseID			SYSLIB-SQL-UT-4114
+@SYMTestCaseDesc		Storing a big blob test, to ensure that by using the methods 
+						of RSqlBlobReadStream and RSqlBlobWriteStream larger blobs can 
+						be stored in practice than in previous versions of Symbian SQL.
+						Creates a 18Mb zeroblob and then writes data into it and reads
+						the data back.
+						Also tests the TSqlBlob APIs to store and retrieve a large blob.
+						Note that the test will use 18Mb blob only in WINSCW builds.
+						Otherwise the used blob size is 3Mb.
+@SYMTestPriority		Medium
+@SYMTestActions			Execution of creating a 18Mb zeroblob, writing data to it and 
+						reading it back.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10410
+*/
+void BigBlobTestL()
+	{
+	// In this test we create a zeroblob big enough to hold a 18MB blob.
+	// 18MB is larger than the server could previously read or write,
+	// due to the server heap limit of 6MB (WINSCW builds).
+	// This test will prove that a 18MB blob can be written and read
+	// using the new APIs but not with the old APIs
+		
+#if defined __WINS__ ||	defined __WINSCW__
+	const TInt KBigBlobSize = 18 * 1024 * 1024;
+#else
+	const TInt KBigBlobSize = 3 * 1024 * 1024;
+#endif
+		
+	// Create a zeroblob big enough to hold a 36MB blob
+	TInt err = TheDb2.Exec(_L("BEGIN"));
+	TEST(err >= 0);
+	
+	RSqlStatement stmt;
+	err = stmt.Prepare(TheDb2, _L("INSERT INTO table2 values(435, 'big blob test', :Val)"));
+	TEST2(err, KErrNone);
+	TInt paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindZeroBlob(paramIndex, KBigBlobSize);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();	
+	stmt.Close();
+	if(err == KErrDiskFull)	
+		{
+		(void)TheDb2.Exec(_L("ROLLBACK"));
+		TheTest.Printf(_L("==== The disk is full. The test cannot be completed!\r\n"));
+		RFs fs;
+		err = fs.Connect();
+		if(err == KErrNone)
+			{
+			TVolumeInfo vinfo;
+			err = fs.Volume(vinfo, EDriveC);
+			if(err == KErrNone)
+				{
+				TheTest.Printf(_L("==== Free disk space=%d\r\n"), vinfo.iFree);
+				}
+			fs.Close();
+			}
+		return;	
+		}
+	TEST2(err, 1);
+	
+	/////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Fill a buffer with KBigBlobSize/KBlobPartCnt bytes of data 
+	// (the test application's heap may be too small to allocate a KBigBlobSize bytes buffer)
+    const TInt KBlobWrPartCnt = 16;// 1/16 part of the blob will be written at once using streaming API
+	const TInt KBufferSize1 = KBigBlobSize / KBlobWrPartCnt;
+	HBufC8* blobWrBuf = HBufC8::NewLC(KBufferSize1);
+	TPtr8 blobWrChunk(blobWrBuf->Des());
+	blobWrChunk.Fill('Z', KBufferSize1);	
+	
+	// Write KBigBlobSize bytes to the blob in the inserted record
+	RSqlBlobWriteStream wrStrm;
+	CleanupClosePushL(wrStrm);
+	wrStrm.OpenL(TheDb2, _L("table2"), _L("blob"));
+	TInt size = wrStrm.SizeL(); // check the blob's size
+	TEST2(size, KBigBlobSize);
+	for(TInt i1=0;i1<KBlobWrPartCnt;++i1)
+	    {
+        TRAP(err, wrStrm.WriteL(blobWrChunk)); // write KBufferSize1 bytes of data
+        TEST2(err, KErrNone);
+	    }
+	wrStrm.CommitL();
+	CleanupStack::PopAndDestroy(&wrStrm);
+	
+	err = TheDb2.Exec(_L("COMMIT"));
+	TEST2(err, 1);
+	
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Read the big blob value back from the record in KBigBlobSize/6 chunks
+    const TInt KBlobRdPartCnt = 24;// 1/24 part of the blob will be read at once using streaming API
+	const TInt KBufferSize2 = KBigBlobSize / KBlobRdPartCnt;
+	HBufC8* blobRdBuf = HBufC8::NewLC(KBufferSize2);
+	TPtr8 blobRdBufPtr(blobRdBuf->Des());
+	RSqlBlobReadStream rdStrm;
+	CleanupClosePushL(rdStrm);
+	rdStrm.OpenL(TheDb2, _L("table2"), _L("blob"));
+	size = rdStrm.SizeL(); // check the blob's size
+	TEST2(size, KBigBlobSize);
+	for(TInt i2=0;i2<KBlobRdPartCnt;++i2)
+	    {
+        rdStrm.ReadL(blobRdBufPtr, KBufferSize2);
+        TEST(blobRdBufPtr.Compare(blobWrChunk.Left(KBufferSize2)) == 0); // check the first KBigBlobSize/KBlobRdPartCnt bytes
+	    }
+	TRAP(err, rdStrm.ReadL(blobRdBufPtr, 1));
+	TEST2(err, KErrEof); // check that there is no more data to be read
+	CleanupStack::PopAndDestroy(2, blobRdBuf); // rdStrm, blobRdBuf
+		
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Try to read the whole KBigBlobSize blob value using the old API
+	err = stmt.Prepare(TheDb2, _L("SELECT blob FROM table2 WHERE ROWID == :Val"));
+	TEST2(err, KErrNone);
+	paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindInt(paramIndex, TheDb2.LastInsertedRowId());
+	TEST2(err, KErrNone);
+	// Check that the blob retrieval fails (because there is
+	// not enough server-side memory to load it into the VDBE)
+	err = stmt.Next();
+#if defined __WINS__ ||	defined __WINSCW__
+	TEST2(err, KErrNoMemory);
+#else	
+	TEST2(err, KSqlAtRow);
+#endif
+	stmt.Close();
+	
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Try to write another KBigBlobSize bytes big blob value using the old API.
+    // Check that the at some point the blob write fails (because there is
+    // not enough server-side memory to store the whole KBigBlobSize bytes).
+	err = stmt.Prepare(TheDb2, _L("INSERT INTO table2 values(99, 'text', :Val)"));
+	TEST2(err, KErrNone);	
+	paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);
+	RSqlParamWriteStream strm;
+	err = strm.BindBinary(stmt, paramIndex);
+	TEST2(err, KErrNone);
+	for(TInt i3=0;i3<KBlobWrPartCnt && err==KErrNone;++i3)
+	    {
+	    TRAP(err, strm.WriteL(blobWrChunk, KBufferSize1)); 
+	    }
+#if defined __WINS__ ||	defined __WINSCW__
+	TEST2(err, KErrNoMemory);
+#else	
+	TEST2(err, KErrNone);
+#endif
+	strm.Close();
+	stmt.Close();
+	CleanupStack::PopAndDestroy(blobWrBuf);
+	blobWrBuf = NULL;
+
+    /////////////////////////////////////////////////////////////////////////////////////////////////////
+	// Use the TSqlBlob APIs to insert another big blob of size 85Kb
+	// (to test the block algorithm used by TSqlBlob 'set')
+	const TInt KBigBlobSize2 = 85 * 1024;
+	err = TheDb2.Exec(_L("BEGIN"));
+    TEST(err >= 0);
+	
+	err = stmt.Prepare(TheDb2, _L("INSERT INTO table2 values(189, 'another big blob', :Val)"));
+	TEST2(err, KErrNone);
+	paramIndex = stmt.ParameterIndex(_L(":Val"));
+	TEST(paramIndex >= 0);	
+	err = stmt.BindZeroBlob(paramIndex, KBigBlobSize2);
+	TEST2(err, KErrNone);
+	err = stmt.Exec();	
+	TEST2(err, 1);
+	stmt.Close();
+
+	blobWrBuf = HBufC8::NewLC(KBigBlobSize2);
+	blobWrChunk.Set(blobWrBuf->Des());
+	blobWrChunk.SetLength(KBigBlobSize2);
+    blobWrChunk.Fill('F');
+    TPtr8 p((TUint8*)blobWrChunk.Ptr() + blobWrChunk.Length() / 2, blobWrChunk.Length() / 2);
+    p.Fill('E');// blobWrBuf now contains 42.5Kb of 'E's followed by 42.5Kb of 'F's
+
+	TRAP(err, TSqlBlob::SetL(TheDb2, _L("table2"), _L("blob"), blobWrChunk));
+	TEST2(err, KErrNone);
+	
+	err = TheDb2.Exec(_L("COMMIT"));
+	TEST2(err, 1);
+
+	// Read the blob value back from the record	
+	// (to test the block algorithm used by TSqlBlob 'get')
+	HBufC8* buf = TSqlBlob::GetLC(TheDb2, _L("table2"), _L("blob"));
+	TEST(buf->Des().Compare(blobWrChunk) == 0);
+    CleanupStack::PopAndDestroy(buf);
+    buf = NULL;
+	
+	buf = HBufC8::NewLC(KBigBlobSize2);
+	blobRdBufPtr.Set(buf->Des());
+	err = TSqlBlob::Get(TheDb2, _L("table2"), _L("blob"), blobRdBufPtr);	
+	TEST2(err, KErrNone);
+	TEST(blobRdBufPtr.Compare(blobWrChunk) == 0);
+	
+	CleanupStack::PopAndDestroy(2, blobWrBuf); // buf, blobWrBuf
+	}
+	
+void DoTestsL()
+	{	
+	CreateTestDbs();
+
+	// Insert a zeroblob using RSqlStatement::BindZeroBlob() and read and write to it using streams
+	TheTest.Start(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4099: Stream BindZeroBlob() test"));
+	StreamBindZeroBlobTestL();
+			
+	// Insert a zeroblob using SQLite's zeroblob() function and read and write to it using streams
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4100: Stream zeroblob() test"));
+	StreamSqliteZeroBlobTestL();
+		
+	// Insert a record containing a 'real' blob and read and write to it using streams
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4101: Stream real blob test"));
+	StreamRealBlobTestL();
+	
+	// Get a whole blob object
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4102: Whole value blob retrieval test"));
+	GetWholeBlob3L();
+		
+	// Set a whole blob object
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4104: Whole value blob set test"));
+	SetWholeBlob3L();
+		
+	// Attached database test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4106: Attached database test"));
+	AttachDbTestL();
+	
+	// Bad parameter test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4107: Bad parameter test"));
+	BadParamTestL();
+	
+	// Indexed column test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4108: Indexed column test"));
+	IndexedColumnTestL();
+	
+	// End Of File test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4109: Eof test"));
+	EofTestL();
+	
+	// Private secure database test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4110: Private secure db test"));	
+	PrivateSecureDbTestL();
+	
+	// Concurrent read and write test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4111: Concurrent read and write test"));	
+	ConcurrentReadAndWriteTestL();
+	
+	// UTF-16 read and write test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4112: UTF-16 format test"));	
+	UTF16FormatTestL();
+	
+	// Big blob test
+	TheTest.Next(_L(" @SYMTestCaseID:SYSLIB-SQL-UT-4114: Big blob test"));
+	BigBlobTestL();
+		
+	DeleteTestDbs();
+	}
+
+TInt E32Main()
+	{
+	TheTest.Title();
+	
+	CTrapCleanup* tc = CTrapCleanup::New();
+	
+	__UHEAP_MARK;
+		
+	CreateTestDir();
+	DeleteTestDbs();
+	FillLargeDataBuf();
+	TRAPD(err, DoTestsL());
+	TEST2(err, KErrNone);
+
+	__UHEAP_MARKEND;
+	
+	TheTest.End();
+	TheTest.Close();
+	
+	delete tc;
+
+	User::Heap().Check();
+	return KErrNone;
+	}
+