--- /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;
+ }
+