diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sql/TEST/t_sqlblob.cpp --- /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 +#include +#include +#include + +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 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;i1Des()); + 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= 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= 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; + } +