diff -r 000000000000 -r 08ec8eefde2f persistentstorage/sqlite3api/TEST/t_sqliteapi.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/persistentstorage/sqlite3api/TEST/t_sqliteapi.c Fri Jan 22 11:06:30 2010 +0200 @@ -0,0 +1,1372 @@ +/* +* Copyright (c) 2007-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 +#include +#include +#include +#include +#include +#include +#include "sqliteTestUtl.h" + +static sqlite3* TheDb = 0; +static sqlite3* TheDb2 = 0; +static sqlite3_stmt* TheStmt = 0; +static sqlite3_stmt* TheStmt2 = 0; +const char* const TheTestDirName = "c:\\test"; +const char* const TheTestDbName = "c:\\test\\a1.db"; + +const int KTestThreadCount = 2; +static int TheInsertRecCnt[KTestThreadCount] = {0, 0}; +static int TheLockErrCnt[KTestThreadCount] = {0, 0}; +const char* const KThreadNames[KTestThreadCount] = {"THRD1", "THRD2"}; +static sem_t TheSemaphores[KTestThreadCount]; + +#define UNUSED_ARG(a) (a) = (a) + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/* The standard C library does not have abs() function with a double argument */ +static double dabs(double arg) + { + return arg < 0.0 ? -arg : arg; + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +static void TestEnvDestroy() + { + if(TheStmt2) + { + (void)sqlite3_finalize(TheStmt2); + TheStmt2 = 0; + } + if(TheStmt) + { + (void)sqlite3_finalize(TheStmt); + TheStmt = 0; + } + if(TheDb2) + { + (void)sqlite3_close(TheDb2); + TheDb2 = 0; + } + if(TheDb) + { + (void)sqlite3_close(TheDb); + TheDb = 0; + } + (void)remove(TheTestDbName); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ +/* Test macros and functions */ + +static void Check1(int aValue, int aLine) + { + if(!aValue) + { + if(TheDb) + { + const char* errmsg = sqlite3_errmsg(TheDb); + PrintS("*** DB1: SQLITE error message: %s\r\n", errmsg); + } + if(TheDb2) + { + const char* errmsg = sqlite3_errmsg(TheDb2); + PrintS("*** DB2: SQLITE error message: %s\r\n", errmsg); + } + PrintI("*** Test check failed! Line=%d\r\n", aLine); + TestEnvDestroy(); + TestAbort(aLine); + } + } + +static void Check2(int aValue, int aExpected, int aLine) + { + if(aValue != aExpected) + { + if(TheDb) + { + const char* errmsg = sqlite3_errmsg(TheDb); + PrintS("*** DB1: SQLITE error message: %s\r\n", errmsg); + } + if(TheDb2) + { + const char* errmsg = sqlite3_errmsg(TheDb2); + PrintS("*** DB2: SQLITE error message: %s\r\n", errmsg); + } + PrintIII("*** Test check failed! Line=%d. Expected error: %d, got: %d\r\n", aLine, aExpected, aValue); + TestEnvDestroy(); + TestAbort(aLine); + } + } + +static void Check64(sqlite_int64 aValue, sqlite_int64 aExpected, int aLine) + { + if(aValue != aExpected) + { + if(TheDb) + { + const char* errmsg = sqlite3_errmsg(TheDb); + PrintS("*** DB1: SQLITE error message: %s\r\n", errmsg); + } + if(TheDb2) + { + const char* errmsg = sqlite3_errmsg(TheDb2); + PrintS("*** DB2: SQLITE error message: %s\r\n", errmsg); + } + PrintII64I64("*** Test check failed! Line=%ld. Expected error: %ld, got: %d\r\n", aLine, aExpected, aValue); + TestEnvDestroy(); + TestAbort(aLine); + } + } +#define TEST(arg) Check1((arg), __LINE__) +#define TEST2(aValue, aExpected) Check2(aValue, aExpected, __LINE__) +#define TEST64(aValue, aExpected) Check64(aValue, aExpected, __LINE__) + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +static void TestEnvCreate() + { + int err; + (void)remove(TheTestDbName); + err = mkdir(TheTestDirName, S_IREAD | S_IWRITE); + if(err != 0) + { + err = errno; + } + TEST(err == 0 || err == EEXIST); + + //Creating the private data cage directory here to suppress a capability warning + CreatePrivateDir(); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ +/* Callbacks */ + +static int exec_callback(void* udata, int argc, char** argv, char** colname) + { + UNUSED_ARG(udata); + UNUSED_ARG(argc); + UNUSED_ARG(argv); + UNUSED_ARG(colname); + return 0; + } + +static int authorizer_callback(void* udata, int optype, const char* name1, const char* name2, const char* name, const char* viewname) + { + UNUSED_ARG(udata); + UNUSED_ARG(optype); + UNUSED_ARG(name1); + UNUSED_ARG(name2); + UNUSED_ARG(name); + UNUSED_ARG(viewname); + return SQLITE_OK; + } + +static int commit_hook(void* udata) + { + UNUSED_ARG(udata); + return SQLITE_OK; + } + +static void rollback_hook(void* udata) + { + UNUSED_ARG(udata); + } + +static void update_hook(void* udata, int type, char const* dbname, char const* tblname, sqlite_int64 rowid) + { + UNUSED_ARG(udata); + UNUSED_ARG(type); + UNUSED_ARG(dbname); + UNUSED_ARG(tblname); + UNUSED_ARG(rowid); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4001 +@SYMTestCaseDesc sqlite3_exec() tests. + List of called SQLITE3 functions: + - sqlite3_exec; + - sqlite3_errcode; + - sqlite3_errmsg; + - sqlite3_last_insert_rowid; + - sqlite3_changes; + - sqlite3_total_changes; + - sqlite3_get_autocommit; +@SYMTestPriority High +@SYMTestActions sqlite3_exec() tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TestExec() + { + int err; + sqlite_int64 lastrowid; + int val; + + TEST(TheDb != 0); + + err = sqlite3_exec(TheDb, "CREATE TABLE A(F1 INTEGER, F2 BIGINT, F3 REAL, F4 TEXT, F5 BLOB)", &exec_callback, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_exec(TheDb, "INSERT INTO A VALUES(1, 1234567891234, 56.12, 'TEXT', x'313233343536')", &exec_callback, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_errcode(TheDb); + TEST2(err, SQLITE_OK); + + (void)sqlite3_errmsg(TheDb); + + lastrowid = sqlite3_last_insert_rowid(TheDb); + TEST(lastrowid > 0LL); + + val = sqlite3_changes(TheDb); + TEST2(val, 1); + + val = sqlite3_total_changes(TheDb); + TEST(val >= 1); + + val = sqlite3_get_autocommit(TheDb); + TEST(val != 0); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +static void DoTestStatement() + { + int err, val; + sqlite_int64 val64; + double dblval; + const unsigned char* textval; + const unsigned short* textval16; + const unsigned char* blob; + const char *coltype, *colname; + const unsigned short *coltype16, *colname16; + sqlite3* db; + + TEST(TheDb != 0); + TEST(TheStmt != 0); + + val = sqlite3_column_count(TheStmt); + TEST2(val, 5); + + db = sqlite3_db_handle(TheStmt); + TEST2((unsigned int)db, (unsigned int)TheDb); + + err = sqlite3_step(TheStmt); + TEST2(err, SQLITE_ROW); + +#ifdef SQLITE_ENABLE_COLUMN_METADATA + sqlite3_column_database_name(TheStmt, 0); + sqlite3_column_database_name16(TheStmt, 1); + sqlite3_column_table_name(TheStmt, 2); + sqlite3_column_table_name16(TheStmt, 3); + sqlite3_column_origin_name(TheStmt, 4); + sqlite3_column_origin_name16(TheStmt, 0); +#endif + + coltype = sqlite3_column_decltype(TheStmt, 0); + TEST2(strcmp(coltype, "INTEGER"), 0); + + coltype16 = (const unsigned short*)sqlite3_column_decltype16(TheStmt, 2); + TEST2(wcscmp(coltype16, L"REAL"), 0); + + colname = sqlite3_column_name(TheStmt, 1); + TEST2(strcmp(colname, "F2"), 0); + + colname16 = (const unsigned short *)sqlite3_column_name16(TheStmt, 4); + TEST2(wcscmp(colname16, L"F5"), 0); + + val = sqlite3_column_int(TheStmt, 0); + TEST2(val, 1); + + val64 = sqlite3_column_int64(TheStmt, 1); + TEST64(val64, 1234567891234LL); + + dblval = sqlite3_column_double(TheStmt, 2); + TEST(dabs(dblval - 56.12) < 0.00001); + + textval = sqlite3_column_text(TheStmt, 3); + TEST2(strcmp((const char*)textval, "TEXT"), 0); + + textval16 = sqlite3_column_text16(TheStmt, 3); + TEST2(wcscmp(textval16, L"TEXT"), 0); + + blob = (const unsigned char*)sqlite3_column_blob(TheStmt, 4); + TEST2(memcmp(blob, "123456", 6), 0); + + err = sqlite3_step(TheStmt); + TEST2(err, SQLITE_DONE); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4002 +@SYMTestCaseDesc Statement handle SQLITE3 tests. + List of called SQLITE3 functions: + - sqlite3_complete; + - sqlite3_complete16; + - sqlite3_prepare; + - sqlite3_finalize; + - sqlite3_column_count; + - sqlite3_db_handle; + - sqlite3_step; + - sqlite3_column_decltype; + - sqlite3_column_decltype16; + - sqlite3_column_name; + - sqlite3_column_name16; + - sqlite3_column_int; + - sqlite3_column_int64; + - sqlite3_column_double; + - sqlite3_column_text; + - sqlite3_column_text16; + - sqlite3_column_blob; +@SYMTestPriority High +@SYMTestActions Statement handle SQLITE3 tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TestStatement1() + { + const char* tail = 0; + int err; + + TEST(TheDb != 0); + TEST(!TheStmt); + + err = sqlite3_complete("SELECT * FROM A;"); + TEST(err != 0); + + err = sqlite3_complete16(L"SELECT * FROM A;"); + TEST(err != 0); + + err = sqlite3_prepare(TheDb, "SELECT * FROM A", -1, &TheStmt, &tail); + TEST2(err, SQLITE_OK); + TEST((unsigned int)TheStmt); + TEST(!tail || strlen(tail) == 0); + + DoTestStatement(); + + err = sqlite3_finalize(TheStmt); + TEST2(err, SQLITE_OK); + TheStmt = 0; + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4003 +@SYMTestCaseDesc Statement handle SQLITE3 tests. + List of called SQLITE3 functions: + - sqlite3_prepare; + - sqlite3_finalize; + - sqlite3_bind_int; + - sqlite3_bind_int64; + - sqlite3_bind_double; + - sqlite3_bind_text; +@SYMTestPriority High +@SYMTestActions Statement handle SQLITE3 tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TestStatement2() + { + const char* tail = 0; + int err; + + TEST(TheDb != 0); + TEST(!TheStmt); + + err = sqlite3_prepare(TheDb, "SELECT * FROM A WHERE F1=? AND F2=? AND F3 0); + + next = sqlite3_next_stmt(TheDb, 0); + TEST(!next); + + memset(buf, 0, sizeof(buf)); + sqlite3_randomness(8, buf); + + memset(buf, 0, sizeof(buf)); + sqlite3_randomness(7, buf); + + memset(buf, 0, sizeof(buf)); + sqlite3_randomness(3, buf); + + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID PDS-SQLITE3-UT-4039 +@SYMTestCaseDesc SQLITE3 - blob API tests. + List of called SQLITE3 functions: + - sqlite3_bind_zeroblob; + - sqlite3_blob_bytes; + - sqlite3_blob_close; + - sqlite3_blob_open; + - sqlite3_blob_read; + - sqlite3_blob_write; + - sqlite3_sql; +@SYMTestPriority High +@SYMTestActions SQLITE3 - blob API tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ10424 +*/ +static void TestSqliteBlobApi() + { + int err; + const char* tail = 0; + sqlite3_blob* blob = 0; + int bytes = 0; + const char KBlobData[] = "ABCDEFGH"; + char sql[100]; + const int KBlobLen = strlen(KBlobData); + const char* sql2 = 0; + const char KSqlFmt[] = "UPDATE BlobTbl SET B=:Prm WHERE ROWID=1"; + + TEST(TheDb != 0); + TEST(!TheStmt); + + /* Create the table, insert one record with a blob */ + + err = sqlite3_exec(TheDb, "CREATE TABLE BlobTbl(I INTEGER PRIMARY KEY, B BLOB)", 0, 0, 0); + TEST2(err, SQLITE_OK); + + sprintf(sql, "INSERT INTO BlobTbl VALUES(1, zeroblob(%d))", KBlobLen); + err = sqlite3_exec(TheDb, sql, 0, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_prepare_v2(TheDb, KSqlFmt, -1, &TheStmt, &tail); + TEST2(err, SQLITE_OK); + TEST(TheStmt != 0); + + sql2 = sqlite3_sql(TheStmt); + TEST(sql2 != NULL); + err = strcmp(sql2, KSqlFmt); + TEST2(err, 0); + + err = sqlite3_bind_zeroblob(TheStmt, 1, KBlobLen); + TEST2(err, SQLITE_OK); + + err = sqlite3_step(TheStmt); + TEST2(err, SQLITE_DONE); + + err = sqlite3_finalize(TheStmt); + TEST2(err, SQLITE_OK); + TheStmt = 0; + + /* Open the blob and write to it */ + + err = sqlite3_blob_open(TheDb, "main", "BlobTbl", "B", 1, 1, &blob); + TEST2(err, SQLITE_OK); + TEST(blob != 0); + + bytes = sqlite3_blob_bytes(blob); + TEST2(bytes, KBlobLen); + + err = sqlite3_blob_write(blob, KBlobData, KBlobLen, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_blob_close(blob); + TEST2(err, SQLITE_OK); + blob = 0; + + /* Open the blob and read from it */ + + err = sqlite3_blob_open(TheDb, "main", "BlobTbl", "B", 1, 1, &blob); + TEST2(err, SQLITE_OK); + TEST(blob != 0); + + bytes = sqlite3_blob_bytes(blob); + TEST2(bytes, KBlobLen); + + err = sqlite3_blob_read(blob, sql, KBlobLen, 0); + TEST2(err, SQLITE_OK); + sql[bytes] = 0; + + err = sqlite3_blob_close(blob); + TEST2(err, SQLITE_OK); + blob = 0; + + err = strcmp(sql, KBlobData); + TEST2(err, 0); + + err = sqlite3_exec(TheDb, "DROP TABLE BlobTbl", 0, 0, 0); + TEST2(err, SQLITE_OK); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID PDS-SQLITE3-UT-4040 +@SYMTestCaseDesc SQLITE3 - mutex API tests. + List of called SQLITE3 functions: + - sqlite3_mutex_alloc; + - sqlite3_mutex_enter; + - sqlite3_mutex_free; + - sqlite3_mutex_held; + - sqlite3_mutex_leave; + - sqlite3_mutex_notheld; + - sqlite3_mutex_try; +@SYMTestPriority High +@SYMTestActions SQLITE3 - mutex API tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ10424 +*/ +static void TestSqliteMutexApi() + { + sqlite3_mutex* mtx = 0; + int err; + int type; + + TEST(TheDb != 0); + + for(type=SQLITE_MUTEX_FAST;type<=SQLITE_MUTEX_STATIC_LRU2;++type) + { + mtx = sqlite3_mutex_alloc(type); + TEST(mtx != NULL); + + err = sqlite3_mutex_try(mtx); + TEST(err == SQLITE_BUSY || err == SQLITE_OK); + + sqlite3_mutex_enter(mtx); + + sqlite3_mutex_leave(mtx); + + if(type <= SQLITE_MUTEX_RECURSIVE) + { + sqlite3_mutex_free(mtx); + } + mtx = 0; + } + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4004 +@SYMTestCaseDesc Database handle SQLITE3 tests. + List of called SQLITE3 functions: + - sqlite3_config; + - sqlite3_initialize; + - sqlite3_threadsafe; + - sqlite3_vfs_find; + - sqlite3_open; + - sqlite3_db_config; + - sqlite3_libversion; + - sqlite3_libversion_number; + - sqlite3_set_authorizer; + - sqlite3_commit_hook; + - sqlite3_rollback_hook; + - sqlite3_update_hook; + - sqlite3_close; + - sqlite3_shutdown; +@SYMTestPriority High +@SYMTestActions Database handle SQLITE3 tests. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TestSqliteApi() + { + void* prev = 0; + const char* libverstr = 0; + int libvernum = 0; + int err; + int threadSafe = -1; + sqlite3_vfs* vfs = 0; + + TEST(!TheDb); + + TestStart("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4004: Test \"sqlite3_config()\""); + err = sqlite3_config(SQLITE_CONFIG_MEMSTATUS, 0); + TEST2(err, SQLITE_OK); + + TestNext("Test \"sqlite3_initialize()\""); + err = sqlite3_initialize(); + TEST2(err, SQLITE_OK); + + TestNext("Test \"sqlite3_threadsafe()\""); + threadSafe = sqlite3_threadsafe(); + PrintI("SQLITE_THREADSAFE=%d\r\n", threadSafe); + + vfs = sqlite3_vfs_find(0); + TEST(vfs != NULL); + PrintS("Vfs name=\"%s\"\r\n", vfs->zName); + + err = sqlite3_open(TheTestDbName, &TheDb); + TEST2(err, SQLITE_OK); + TEST(TheDb != 0); + + err = sqlite3_db_config(TheDb, SQLITE_DBCONFIG_LOOKASIDE, 0, 128, 100); + TEST2(err, SQLITE_OK); + + libverstr = sqlite3_libversion(); + libvernum = sqlite3_libversion_number(); + PrintSI("SQLITE version: \"%s\", Number: %d\r\n", libverstr, libvernum); + + err = sqlite3_set_authorizer(TheDb, &authorizer_callback, 0); + TEST2(err, SQLITE_OK); + + prev = sqlite3_commit_hook(TheDb, &commit_hook, 0); + TEST(!prev); + prev = sqlite3_rollback_hook(TheDb, &rollback_hook, 0); + TEST(!prev); + prev = sqlite3_update_hook(TheDb, &update_hook, 0); + TEST(!prev); + + TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4001: Test \"sqlite3\" handle API"); + TestExec(); + TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4002: Test \"sqlite3_stmt\" handle API-1"); + TestStatement1(); + TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4003: Test \"sqlite3_stmt\" handle API-2"); + TestStatement2(); + TestNext("@SYMTestCaseID:PDS-SQLITE3-UT-4038: Test more sqlite3 API"); + TestSqliteApi2(); + TestNext("@SYMTestCaseID:PDS-SQLITE3-UT-4039: Test blob API"); + TestSqliteBlobApi(); + TestNext("@SYMTestCaseID:PDS-SQLITE3-UT-4040: Test mutex API"); + TestSqliteMutexApi(); + + err = sqlite3_close(TheDb); + TEST2(err, SQLITE_OK); + TheDb = 0; + + TestNext("Test \"sqlite3_shutdown()\""); + err = sqlite3_shutdown(); + TEST2(err, SQLITE_OK); + + err = remove(TheTestDbName); + TEST2(err, 0); + } + +static void CreateTestDb() + { + int err; + + err = sqlite3_open(TheTestDbName, &TheDb); + TEST2(err, SQLITE_OK); + TEST(TheDb != 0); + + err = sqlite3_exec(TheDb, "CREATE TABLE A(F1 INTEGER, F2 BIGINT, F3 REAL, F4 TEXT, F5 BLOB)", &exec_callback, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_exec(TheDb, "INSERT INTO A VALUES(1, 1234567891234, 56.12, 'TEXT', x'313233343536')", &exec_callback, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_close(TheDb); + TEST2(err, SQLITE_OK); + TheDb = 0; + } + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4005 +@SYMTestCaseDesc Two database connections in the same thread - "read operations" test. + The test creates two connections to the same database in the same thread. + Both connections prepare a SELECT statement and call sqlite3_step() each + thus testing that the file locking in the OS porting layer works properly + (the SQLite library permits multiple connections to read from the database simultaneously, + using a shared file locking mode). + List of called SQLITE3 functions: + - sqlite3_open; + - sqlite3_prepare; + - sqlite3_step; + - sqlite3_finalize; + - sqlite3_close; +@SYMTestPriority High +@SYMTestActions Two database connections in the same thread - "read operations" test. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TwoReadersTest() + { + int err; + const char* tail = 0; + + TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4005: Test two readers"); + + CreateTestDb(); + + err = sqlite3_open(TheTestDbName, &TheDb); + TEST2(err, SQLITE_OK); + TEST(TheDb != 0); + + err = sqlite3_open(TheTestDbName, &TheDb2); + TEST2(err, SQLITE_OK); + TEST(TheDb2 != 0); + + /* ------------- */ + err = sqlite3_prepare(TheDb, "SELECT * FROM A", -1, &TheStmt, &tail); + TEST2(err, SQLITE_OK); + TEST((unsigned int)TheStmt); + TEST(!tail || strlen(tail) == 0); + + err = sqlite3_prepare(TheDb2, "SELECT * FROM A", -1, &TheStmt2, &tail); + TEST2(err, SQLITE_OK); + TEST((unsigned int)TheStmt2); + TEST(!tail || strlen(tail) == 0); + + err = sqlite3_step(TheStmt); + TEST2(err, SQLITE_ROW); + + err = sqlite3_step(TheStmt2); + TEST2(err, SQLITE_ROW); + + /* ------------- */ + + (void)sqlite3_finalize(TheStmt2); + TheStmt2 = 0; + + (void)sqlite3_finalize(TheStmt); + TheStmt = 0; + + err = sqlite3_close(TheDb2); + TEST2(err, SQLITE_OK); + TheDb2 = 0; + + err = sqlite3_close(TheDb); + TEST2(err, SQLITE_OK); + TheDb = 0; + + (void)remove(TheTestDbName); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +/** +@SYMTestCaseID SYSLIB-SQLITE3-UT-4006 +@SYMTestCaseDesc Two database connections in the same thread - "write operations" test. + The test creates two connections to the same database in the same thread. + Both connections attempt to execute INSERT statements using an explicit transaction + thus testing that the file locking in the OS porting layer works properly + (the SQLite library permits only one connection at a time to write to the database). + List of called SQLITE3 functions: + - sqlite3_open; + - sqlite3_exec; + - sqlite3_free; + - sqlite3_close; + Test case steps: + - Two connections to the same database created; + - Both connections begin a deferred transaction; + - The first connection executes an INSERT statement. The database file should be locked + for a writing by this connection; + - The second connection attempts to execute an INSERT statement too. The operation should fail, + because the database file has been locked by the first connection; + - The first connection completes the transaction executing a ROLLBACK statement; + - The second connection makes second attempt to execute the INSERT statement. The execution should + complete without errors; +@SYMTestPriority High +@SYMTestActions Two database connections in the same thread - "write operations" test. +@SYMTestExpectedResults Test must not fail +@SYMREQ REQ8782 +*/ +static void TwoWritersTest() + { + int err; + char* errmsg = 0; + + TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4006: Test two writers"); + + CreateTestDb(); + + err = sqlite3_open(TheTestDbName, &TheDb); + TEST2(err, SQLITE_OK); + TEST(TheDb != 0); + + err = sqlite3_open(TheTestDbName, &TheDb2); + TEST2(err, SQLITE_OK); + TEST(TheDb2 != 0); + + /* ------------- */ + Print("Two database connections, begin a transaction in each of them.\r\n"); + err = sqlite3_exec(TheDb, "BEGIN", 0, 0, 0); + TEST2(err, SQLITE_OK); + + err = sqlite3_exec(TheDb2, "BEGIN", 0, 0, 0); + TEST2(err, SQLITE_OK); + + Print("Connection 1. Execute an \"INSERT\" statement. Success.\r\n"); + err = sqlite3_exec(TheDb, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, &errmsg); + if(errmsg) + { + PrintSI("Err msg: %s. Err: %d.\r\n", errmsg, err); + sqlite3_free(errmsg); + } + errmsg = 0; + TEST2(err, SQLITE_OK); + + Print("Connection 2. Execute an \"INSERT\" statement. Failure. The database is locked.\r\n"); + err = sqlite3_exec(TheDb2, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, &errmsg); + if(errmsg) + { + PrintSI("*** %s. Err: %d.\r\n", errmsg, err); + sqlite3_free(errmsg); + } + errmsg = 0; + TEST2(err, SQLITE_BUSY); + + Print("Connection 1. Rollback. The database is unlocked.\r\n"); + err = sqlite3_exec(TheDb, "ROLLBACK", 0, 0, 0); + TEST2(err, SQLITE_OK); + + Print("Connection 2. Execute an \"INSERT\" statement. Success.\r\n"); + err = sqlite3_exec(TheDb2, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, &errmsg); + if(errmsg) + { + PrintSI("*** %s. Err: %d.\r\n", errmsg, err); + sqlite3_free(errmsg); + } + errmsg = 0; + TEST2(err, SQLITE_OK); + + err = sqlite3_exec(TheDb2, "ROLLBACK", 0, 0, 0); + TEST2(err, SQLITE_OK); + + Print("Close database connections.\r\n"); + /* ------------- */ + + err = sqlite3_close(TheDb2); + TEST2(err, SQLITE_OK); + TheDb2 = 0; + + err = sqlite3_close(TheDb); + TEST2(err, SQLITE_OK); + TheDb = 0; + + (void)remove(TheTestDbName); + } + +/* ///////////////////////////////////////////////////////////////////////////////////// */ + +static void* ThreadFunc(void* pname) + { + int records = 0, err, i; + sqlite3* db; + char* errmsg = 0; + int threadIdx = -1; + const int KRecordsCount = 500; + const int KCommitRecordsCount = 2; + + for(i=0;i