persistentstorage/sqlite3api/TEST/t_sqliteapi.c
changeset 0 08ec8eefde2f
child 23 26645d81f48d
--- /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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <unistd.h>
+#include <sqlite3.h>
+#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<? AND F4=?", -1, &TheStmt, &tail);
+	TEST2(err, SQLITE_OK);
+	TEST((unsigned int)TheStmt);
+	TEST(!tail || strlen(tail) == 0);
+	
+	err = sqlite3_bind_int(TheStmt, 1, 1);
+	TEST2(err, SQLITE_OK);
+	
+	err = sqlite3_bind_int64(TheStmt, 2, 1234567891234LL);
+	TEST2(err, SQLITE_OK);
+	
+	err = sqlite3_bind_double(TheStmt, 3, 70.0);
+	TEST2(err, SQLITE_OK);
+	
+	err = sqlite3_bind_text(TheStmt, 4, "TEXT", -1, SQLITE_STATIC);
+	TEST2(err, SQLITE_OK);
+	
+	DoTestStatement();
+	
+	err = sqlite3_finalize(TheStmt);
+	TEST2(err, SQLITE_OK);
+	TheStmt = 0;
+	}
+
+/* ///////////////////////////////////////////////////////////////////////////////////// */
+
+/**
+@SYMTestCaseID			PDS-SQLITE3-UT-4038
+@SYMTestCaseDesc		Database handle SQLITE3 tests.
+						List of called SQLITE3 functions:
+						 - sqlite3_db_status;
+						 - sqlite3_file_control;
+						 - sqlite3_limit;
+						 - sqlite3_next_stmt;
+						 - sqlite3_randomness;
+@SYMTestPriority		High
+@SYMTestActions			Database handle SQLITE3 tests.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ10424
+*/
+static void TestSqliteApi2()
+	{
+	int used = 0;
+	int high = 0;
+	int lock = -1;
+	int limit = 0;
+	sqlite3_stmt* next = 0;
+	int err;
+	unsigned char buf[10];
+	
+	TEST(TheDb != 0);
+	
+	err = sqlite3_db_status(TheDb, SQLITE_DBSTATUS_LOOKASIDE_USED, &used, &high, 0);
+	TEST2(err, SQLITE_OK);
+	PrintI("Lookaside slots: %d\r\n", used);
+	PrintI("Max used lookaside slots: %d\r\n", high);
+		
+	err = sqlite3_file_control(TheDb, "main", SQLITE_FCNTL_LOCKSTATE, &lock);
+	TEST2(err, SQLITE_OK);
+	TEST2(lock, SQLITE_LOCK_NONE);
+	
+	limit = sqlite3_limit(TheDb, SQLITE_LIMIT_LENGTH, -1);
+	TEST(limit > 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<KTestThreadCount;++i)
+		{
+		if(strcmp(pname, KThreadNames[i]) == 0)
+			{
+			threadIdx = i;
+			break;
+			}
+		}
+	
+	PrintS("Thread \"%s\" - begin\r\n", (char*)pname);
+	err = sqlite3_open(TheTestDbName, &db);
+	TEST2(err, SQLITE_OK);
+	TEST(db != 0);
+	
+	while(records < KRecordsCount)
+		{
+		err = sqlite3_exec(db, "BEGIN", 0, 0, &errmsg);
+		if(err == SQLITE_OK)
+			{
+			err = sqlite3_exec(db, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, &errmsg);
+			if(err == SQLITE_OK)
+				{
+				err = sqlite3_exec(db, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, &errmsg);
+				if(err == SQLITE_OK)
+					{
+					err = sqlite3_exec(db, "COMMIT", 0, 0, &errmsg);
+					}
+				}
+			}
+		TEST(err == SQLITE_OK || err == SQLITE_BUSY);
+		if(err == SQLITE_OK)
+			{
+			TheInsertRecCnt[threadIdx]	+= KCommitRecordsCount;
+			records += 2;
+			}
+		else if(err == SQLITE_BUSY)
+			{
+			++TheLockErrCnt[threadIdx];
+			(void)sqlite3_exec(db, "ROLLBACK", 0, 0, 0);
+			if(errmsg)
+				{
+				PrintSI("Err msg: %s. Err: %d.\r\n", errmsg, err);
+				sqlite3_free(errmsg);
+				errmsg = 0;
+				}
+			usleep(100);
+			}
+		}
+
+	err = sqlite3_close(db);
+	TEST2(err, SQLITE_OK);
+	
+	PrintS("Thread \"%s\" - end\r\n", (char*)pname);
+	return &TheInsertRecCnt[threadIdx];
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQLITE3-UT-4007
+@SYMTestCaseDesc		Two database connections from different threads - "write operations" test.
+						The test creates two connections to the same database from two different threads.
+						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).
+						The threads do not use any thread synchronisation objects (mutexes, critical sections, etc...).
+						List of called SQLITE3 functions:
+						 - sqlite3_open;
+						 - sqlite3_exec;
+						 - sqlite3_free;
+						 - sqlite3_close;
+						Test case steps:
+						 - Two threads created;
+						 - Each thread opens a connection to the same database;
+						 - No thread synchronisation is used;
+						 - Each database connection attempts to insert records in a loop using an explicit transaction;
+						 - If the database file is currently locked by the other database connection, the INSERT operation
+						   should fail with SQLITE_BUSY error. Otherwise the operation should complete successfully.
+@SYMTestPriority		High
+@SYMTestActions			Two database connections from different threads - "write operations" test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ8782
+*/
+static void ThreadsTest()
+	{
+	pthread_t threadIds[KTestThreadCount];
+	int err, i;
+	
+	TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4007: Test two writers in two threads");
+
+	CreateTestDb();
+	
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		err = pthread_create(&threadIds[i], 0, &ThreadFunc, (void*)KThreadNames[i]);
+		TEST2(err, 0);
+		}
+	
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		(void)pthread_join(threadIds[i], 0);
+		PrintIII("Thread %d records written: %d, \"db locked\" errors: %d\r\n", i + 1, TheInsertRecCnt[i], TheLockErrCnt[i]);
+		}
+	
+	Print("Test two writers in two threads - end\r\n");
+	(void)remove(TheTestDbName);
+	}
+
+/* ///////////////////////////////////////////////////////////////////////////////////// */
+	
+static void* ThreadFunc1(void* parg)
+	{
+	int err;
+	sqlite3* db;
+
+	UNUSED_ARG(parg);
+	PrintS("Thread \"%s\" - begin\r\n", KThreadNames[0]);
+	
+	err = sqlite3_open(TheTestDbName, &db);
+	TEST2(err, SQLITE_OK);
+	TEST(db != 0);
+	
+	(void)sem_wait(&TheSemaphores[0]);/* Wait for a notification from the main thread to begin */
+	
+	err = sqlite3_exec(db, "BEGIN", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+	
+	err = sqlite3_exec(db, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+	
+	(void)sem_post(&TheSemaphores[1]);/* The database is locked now. Notify thread 2 to attempt an INSERT operation */
+	(void)sem_wait(&TheSemaphores[0]);/* Wait for a notification from thread 2 to continue with the COMMIT operation */
+
+	err = sqlite3_exec(db, "COMMIT", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+	
+	(void)sem_post(&TheSemaphores[1]);/* The database is unlocked. Notify thread 2 to attempt the INSERT operation again */
+	
+	err = sqlite3_close(db);
+	TEST2(err, SQLITE_OK);
+	
+	PrintS("Thread \"%s\" - end\r\n", KThreadNames[0]);
+	return 0;
+	}
+
+static void* ThreadFunc2(void* parg)
+	{
+	int err;
+	sqlite3* db;
+	
+	UNUSED_ARG(parg);
+	PrintS("Thread \"%s\" - begin\r\n", KThreadNames[1]);
+	
+	err = sqlite3_open(TheTestDbName, &db);
+	TEST2(err, SQLITE_OK);
+	TEST(db != 0);
+	
+	(void)sem_wait(&TheSemaphores[1]);/* Wait for a notification from thread 1 to attempt an INSERT operation */
+	
+	err = sqlite3_exec(db, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, 0);
+	TEST2(err, SQLITE_BUSY);
+	
+	(void)sem_post(&TheSemaphores[0]);/* The database is locked. Notify thread 1 to commit and unlock the database */
+	(void)sem_wait(&TheSemaphores[1]);/* Wait for a notification from thread 1 to attempt the INSERT operation again */
+
+	err = sqlite3_exec(db, "INSERT INTO A VALUES(0,0,0.0,'',x'00')", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+	
+	err = sqlite3_close(db);
+	TEST2(err, SQLITE_OK);
+	
+	PrintS("Thread \"%s\" - end\r\n", KThreadNames[1]);
+	return 0;
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQLITE3-UT-4008
+@SYMTestCaseDesc		Two database connections from different threads - synchronised "write operations" test.
+						The test creates two connections to the same database from two different threads.
+						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).
+						The threads use semaphores in order to synchronise the INSERT statements execution.
+						List of called SQLITE3 functions:
+						 - sqlite3_open;
+						 - sqlite3_exec;
+						 - sqlite3_free;
+						 - sqlite3_close;
+						Test case steps:
+						 - Two threads created;
+						 - Each thread opens a connection to the same database;
+						 - The first thread begins an explicit transaction and inserts one record to the database. 
+						   The database file is locked. The first thread notifies the second thread and blocks
+						   waiting for a notification from the second thread;
+						 - The second thread attempts to execute an INSERT statement. Since the database file is locked 
+						   by the first thread the INSERT statement execution fails with SQLITE_BUSY. The second thread 
+						   sends a notification to the first thread to continue and blocks waiting for a notification;
+						 - The first thread commits the transaction thus unlocking the database file. A notification is
+						   sent to the second thread;
+						 - The second thread makes second attempt to INSERT a record and this time the operation should 
+						   complete successfully;
+@SYMTestPriority		High
+@SYMTestActions			Two database connections from different threads - synchronised "write operations" test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ8782
+*/
+static void TwoSyncThreadsTest()
+	{
+	const int KTestThreadCount = 2;
+	int i, err;
+	pthread_t threadIds[KTestThreadCount] = {0};
+	thread_begin_routine threadFuncs[KTestThreadCount] = {&ThreadFunc1, &ThreadFunc2};
+
+	TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4008: Test two writers in two synchronized threads");
+	
+	CreateTestDb();
+
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		err = sem_init(&TheSemaphores[i], 0, 0);
+		TEST2(err, 0);
+		}
+	
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		err = pthread_create(&threadIds[i], 0, threadFuncs[i], 0);
+		TEST2(err, 0);
+		}
+	
+	(void)sem_post(&TheSemaphores[0]);/* Notify thread 1 to begin */
+
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		(void)pthread_join(threadIds[i], 0);
+		}
+	
+	(void)remove(TheTestDbName);
+	
+	for(i=0;i<KTestThreadCount;++i)
+		{
+		err = sem_destroy(&TheSemaphores[i]);
+		TEST2(err, 0);
+		}
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQLITE3-UT-4009
+@SYMTestCaseDesc		Two database connections "RFileBuf64" test.
+						This test verifies that the file buffer used by the SQLITE OS porting layer works properly.
+						Test steps (in time order):
+						1) Two connections to the same database are created.
+						2) The first connection creates a table and executes a couple of INSERT statements.
+						3) The second connection prepares a SELECT statement against the table, created in (2)
+						Now, if the file buffer in the OS porting layer does not work as expected, step (3) fails,
+						because at step (1) the database size is 0 and that is what the file buffer, used by the second 
+						connection, "knows". The "prepare SQL statement" call will fail with a "no such table" error.
+						But if the file buffer works properly, the buffer will be flushed during the "lock file" operation
+						inside the OS porting layer and reloaded with the database data later.
+@SYMTestPriority		High
+@SYMTestActions			Two database connections "RFileBuf64" test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ8782
+*/
+static void TwoConnectionsTest(void)
+	{
+	int err;
+  	const char* tail = 0;
+  	const char* errmsg = 0;
+
+	TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4009: Two database connections test (OS porting layer, RFileBuf64 related)");
+	(void)remove(TheTestDbName);
+	
+	TEST(!TheDb);
+	err = sqlite3_open(TheTestDbName, &TheDb);
+	TEST2(err, SQLITE_OK);
+	TEST(TheDb != 0);
+
+	TEST(!TheDb2);
+	err = sqlite3_open(TheTestDbName, &TheDb2);
+	TEST2(err, SQLITE_OK);
+	TEST(TheDb2 != 0);
+	
+	err = sqlite3_exec(TheDb, "CREATE TABLE t1(x);INSERT INTO t1 VALUES(1);INSERT INTO t1 VALUES(2);SELECT * FROM t1", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+	
+	TEST(!TheStmt);
+	err = sqlite3_prepare(TheDb2, "SELECT * FROM t1", -1, &TheStmt, &tail);
+	if(err != SQLITE_OK)
+		{
+		errmsg = sqlite3_errmsg(TheDb2);
+		PrintSI("*** Stmt prepare err msg: \"%s\". Error: %d\r\n", errmsg, err);
+		}
+	TEST2(err, SQLITE_OK);
+	TEST((unsigned int)TheStmt);
+	TEST(!tail || strlen(tail) == 0);
+
+	err = sqlite3_step(TheStmt);
+	TEST2(err, SQLITE_ROW);
+
+	err = sqlite3_finalize(TheStmt);
+	TEST2(err, SQLITE_OK);
+	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);
+	}
+
+static void UdfInsertFunc(sqlite3_context* aCtx, int aCnt, sqlite3_value** aValues)
+	{
+	int err;
+  	const char* tail = 0;
+  	sqlite3* db = 0;
+  	
+	TEST2(aCnt, 1);
+	
+	db = sqlite3_context_db_handle(aCtx);/* to test that sqlite3_context_db_handle() can be called */
+	TEST(db != 0);
+	
+	TEST(!TheStmt);
+	err = sqlite3_prepare(TheDb, "INSERT INTO t1(x) VALUES(:Val)", -1, &TheStmt, &tail);
+	if(err == SQLITE_OK)
+		{
+		err = sqlite3_bind_value(TheStmt, 1, aValues[0]);
+		if(err == SQLITE_OK)
+			{
+			err = sqlite3_step(TheStmt);
+			}
+		}
+	(void)sqlite3_finalize(TheStmt);
+	TheStmt = 0;
+	
+	sqlite3_result_int(aCtx, err);		
+	}
+
+/**
+@SYMTestCaseID			SYSLIB-SQLITE3-UT-4027
+@SYMTestCaseDesc		sqlite3_bind_value() and sqlite3_column_value() test.
+						List of covered SQLITE3 functions:
+						 - sqlite3_bind_value();
+						 - sqlite3_column_value();
+						Test case steps: 
+						 - The test creates and calls a user defined function - UdfInsertFunc().
+						 - The user defined function prepares and executes an INSERT statement,
+							where the sqlite3_bind_value() call is used to bind the passed from the caller
+							integer column value.
+						 - After the user defined function call completes, the test prepares a SELECT
+						    statement to verify the just inserted column value using sqlite3_column_int()
+@SYMTestPriority		High
+@SYMTestActions			sqlite3_bind_value() and sqlite3_column_int() test.
+@SYMTestExpectedResults Test must not fail
+@SYMREQ					REQ8782
+*/
+static void UdfTest()
+	{
+	int err;
+	int val;
+  	const char* tail = 0;
+  	const int KTestColumnValue = 11234;
+  	char sqlBuf[100];
+
+	TestNext("@SYMTestCaseID:SYSLIB-SQLITE3-UT-4027: User defined function test");
+
+	(void)remove(TheTestDbName);
+	TEST(!TheDb);
+	err = sqlite3_open(TheTestDbName, &TheDb);
+	TEST2(err, SQLITE_OK);
+	TEST(TheDb != 0);
+
+	err = sqlite3_exec(TheDb, "CREATE TABLE t1(x INTEGER)", 0, 0, 0);
+	TEST2(err, SQLITE_OK);
+
+	err = sqlite3_create_function(TheDb, "UdfInsert", 1, SQLITE_UTF8, NULL, &UdfInsertFunc, NULL, NULL);
+	TEST2(err, SQLITE_OK);
+
+	//Execute an INSERT statement via UDF
+	TEST(!TheStmt2);
+	sprintf(sqlBuf, "SELECT UdfInsert(%d)", KTestColumnValue);
+	err = sqlite3_prepare(TheDb, sqlBuf, -1, &TheStmt2, &tail);
+	TEST2(err, SQLITE_OK);
+	err = sqlite3_step(TheStmt2);
+	TEST2(err, SQLITE_ROW);
+	val = sqlite3_column_int(TheStmt2, 0);
+	TEST2(val, SQLITE_DONE);
+	(void)sqlite3_finalize(TheStmt2);
+	TheStmt2 = 0;
+
+	//Verify the inserted column value
+	TEST(!TheStmt2);
+	err = sqlite3_prepare(TheDb, "SELECT x FROM t1", -1, &TheStmt2, &tail);
+	TEST2(err, SQLITE_OK);
+	err = sqlite3_step(TheStmt2);
+	TEST2(err, SQLITE_ROW);
+	val = sqlite3_column_int(TheStmt2,0);
+	TEST2(val, KTestColumnValue);
+	(void)sqlite3_finalize(TheStmt2);
+	TheStmt2 = 0;
+
+	err = sqlite3_close(TheDb);
+	TEST2(err, SQLITE_OK);
+	TheDb = 0;
+	(void)remove(TheTestDbName);
+	}
+
+/* ///////////////////////////////////////////////////////////////////////////////////// */
+
+int main(int argc, void** argv)
+	{
+	UNUSED_ARG(argc);
+	UNUSED_ARG(argv);
+
+	TestTitle();
+
+	TestHeapMark();
+		
+	TestEnvCreate();
+	
+	TestSqliteApi();
+	TwoReadersTest();
+	TwoWritersTest();
+	ThreadsTest();
+	TwoSyncThreadsTest();
+	TwoConnectionsTest();
+	UdfTest();
+	
+	TestEnvDestroy();
+
+	TestHeapMarkEnd();
+
+	TestEnd();
+	TestClose();
+	
+	return 0;	
+	}