using System; using System.Diagnostics; using System.Text; using i64 = System.Int64; using u8 = System.Byte; namespace Community.CsharpSqlite { #if TCLSH using tcl.lang; using sqlite3_stmt = Sqlite3.Vdbe; using sqlite3_value = Sqlite3.Mem; using Tcl_Interp = tcl.lang.Interp; using Tcl_Obj = tcl.lang.TclObject; public partial class Sqlite3 { /* ** 2008 March 19 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** Code for testing all sorts of SQLite interfaces. This code ** implements new SQL functions used by the test scripts. ************************************************************************* ** Included in SQLite3 port to C#-SQLite; 2008 Noah B Hart ** C#-SQLite is an independent reimplementation of the SQLite software library ** ** SQLITE_SOURCE_ID: 2011-05-19 13:26:54 ed1da510a239ea767a01dc332b667119fa3c908e ** ************************************************************************* */ //#include "sqlite3.h" //#include "tcl.h" //#include //#include //#include /* ** Allocate nByte bytes of space using sqlite3Malloc(). If the ** allocation fails, call sqlite3_result_error_nomem() to notify ** the database handle that malloc() has failed. */ static Object testContextMalloc( sqlite3_context context, int nByte ) { Object z = new Object();// sqlite3Malloc( nByte ); if ( z == null && nByte > 0 ) { sqlite3_result_error_nomem( context ); } return z; } /* ** This function generates a string of random characters. Used for ** generating test data. */ static void randStr( sqlite3_context context, int argc, sqlite3_value[] argv ) { string zSrc = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + ".-!,:*^+=_|?/<> "; int iMin, iMax, n, i; i64 r = 0; StringBuilder zBuf = new StringBuilder( 1000 ); /* It used to be possible to call randstr() with any number of arguments, ** but now it is registered with SQLite as requiring exactly 2. */ Debug.Assert( argc == 2 ); iMin = sqlite3_value_int( argv[0] ); if ( iMin < 0 ) iMin = 0; if ( iMin >= zBuf.Capacity ) iMin = zBuf.Capacity - 1; iMax = sqlite3_value_int( argv[1] ); if ( iMax < iMin ) iMax = iMin; if ( iMax >= zBuf.Capacity ) iMax = zBuf.Capacity - 1; n = iMin; if ( iMax > iMin ) { sqlite3_randomness( sizeof( i64 ), ref r ); r &= 0x7fffffff; n += (int)( r % ( iMax + 1 - iMin ) ); } Debug.Assert( n < zBuf.Capacity );//sizeof( zBuf ) ); i64 zRan = 0; for ( i = 0; i < n; i++ ) { sqlite3_randomness( 1, ref zRan ); zBuf.Append( zSrc[(int)( Math.Abs( zRan ) % ( zSrc.Length - 1 ) )] ); } //zBuf[n] = 0; sqlite3_result_text( context, zBuf, n, SQLITE_TRANSIENT ); } /* ** The following two SQL functions are used to test returning a text ** result with a destructor. Function 'test_destructor' takes one argument ** and returns the same argument interpreted as TEXT. A destructor is ** passed with the sqlite3_result_text() call. ** ** SQL function 'test_destructor_count' returns the number of outstanding ** allocations made by 'test_destructor'; ** ** WARNING: Not threadsafe. */ static int test_destructor_count_var = 0; static void destructor( ref string p ) { string zVal = p; Debug.Assert( zVal != null ); //zVal--; sqlite3DbFree( null, ref zVal ); test_destructor_count_var--; } static void test_destructor( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { String zVal; int len; test_destructor_count_var++; Debug.Assert( nArg == 1 ); if ( sqlite3_value_type( argv[0] ) == SQLITE_NULL ) return; len = sqlite3_value_bytes( argv[0] ); zVal = "";//testContextMalloc( pCtx, len + 3 ); if ( null == zVal ) { return; } //zVal[len+1] = 0; //zVal[len+2] = 0; //zVal++; zVal = sqlite3_value_text( argv[0] );//memcpy(zVal, sqlite3_value_text(argv[0]), len); sqlite3_result_text( pCtx, zVal, -1, destructor ); } #if !SQLITE_OMIT_UTF16 static void test_destructor16( //sqlite3_context pCtx, /* Function context */ //int nArg, /* Number of function arguments */ //sqlite3_value[] argv /* Values for all function arguments */ ){ string zVal; int len; test_destructor_count_var++; Debug.Assert(nArg==1 ); if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; len = sqlite3_value_bytes16(argv[0]); zVal = testContextMalloc(pCtx, len+3); if( null==zVal ){ return; } zVal[len+1] = 0; zVal[len+2] = 0; zVal++; memcpy(zVal, sqlite3_value_text16(argv[0]), len); sqlite3_result_text16(pCtx, zVal, -1, destructor); } #endif static void test_destructor_count( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { sqlite3_result_int( pCtx, test_destructor_count_var ); } /* ** The following aggregate function, test_agg_errmsg16(), takes zero ** arguments. It returns the text value returned by the sqlite3_errmsg16() ** API function. */ #if SQLITE_OMIT_BUILTIN_TEST //void sqlite3BeginBenignMalloc(void); //void sqlite3EndBenignMalloc(void); #else //#define sqlite3BeginBenignMalloc() //#define sqlite3EndBenignMalloc() #endif static void test_agg_errmsg16_step( sqlite3_context a, int b, sqlite3_value[] c ) { } static void test_agg_errmsg16_final( sqlite3_context ctx ) { #if !SQLITE_OMIT_UTF16 string z; sqlite3 db = sqlite3_context_db_handle( ctx ); sqlite3_aggregate_context( ctx, 2048 ); sqlite3BeginBenignMalloc(); z = sqlite3_errmsg16( db ); sqlite3EndBenignMalloc(); sqlite3_result_text16( ctx, z, -1, SQLITE_TRANSIENT ); #endif } /* ** Routines for testing the sqlite3_get_auxdata() and sqlite3_set_auxdata() ** interface. ** ** The test_auxdata() SQL function attempts to register each of its arguments ** as auxiliary data. If there are no prior registrations of aux data for ** that argument (meaning the argument is not a constant or this is its first ** call) then the result for that argument is 0. If there is a prior ** registration, the result for that argument is 1. The overall result ** is the individual argument results separated by spaces. */ static void free_test_auxdata( ref string p ) { p = null; sqlite3DbFree( null, ref p ); } static void test_auxdata( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { int i; StringBuilder zRet = new StringBuilder( nArg * 2 );//testContextMalloc( pCtx, nArg * 2 ); if ( null == zRet ) return; //memset(zRet, 0, nArg*2); for ( i = 0; i < nArg; i++ ) { string z = sqlite3_value_text( argv[i] ); if ( z != null ) { int n; string zAux = (string)sqlite3_get_auxdata( pCtx, i ); if ( zAux != null ) { zRet.Append( '1' );//[i * 2] = '1'; Debug.Assert( zAux == z );//strcmp( zAux, z ) == 0 ); } else { zRet.Append( '0' );//[i * 2] = '0'; } n = z.Length;// strlen( z ) + 1; zAux = "";//testContextMalloc( pCtx, n ); if ( zAux != null ) { zAux = z.Substring( 0, n );// memcpy( zAux, z, n ); sqlite3_set_auxdata( pCtx, i, zAux ); } zRet.Append( ' ' );// zRet[i * 2 + 1] = ' '; } } sqlite3_result_text( pCtx, zRet, 2 * nArg - 1, free_test_auxdata ); } /* ** A function to test error reporting from user functions. This function ** returns a copy of its first argument as the error message. If the ** second argument exists, it becomes the error code. */ static void test_error( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { sqlite3_result_error( pCtx, sqlite3_value_text( argv[0] ), -1 ); if ( nArg == 2 ) { sqlite3_result_error_code( pCtx, sqlite3_value_int( argv[1] ) ); } } /* ** Implementation of the counter(X) function. If X is an integer ** constant, then the first invocation will return X. The second X+1. ** and so forth. Can be used (for example) to provide a sequence number ** in a result set. */ //static void counterFunc( //sqlite3_context pCtx, /* Function context */ //int nArg, /* Number of function arguments */ //sqlite3_value[] argv /* Values for all function arguments */ //){ // int *pCounter = (int)sqlite3_get_auxdata(pCtx, 0); // if( pCounter==0 ){ // pCounter = sqlite3_malloc( sizeof(*pCounter) ); // if( pCounter==0 ){ // sqlite3_result_error_nomem(pCtx); // return; // } // *pCounter = sqlite3_value_int(argv[0]); // sqlite3_set_auxdata(pCtx, 0, pCounter, //sqlite3_free); // }else{ // ++*pCounter; // } // sqlite3_result_int(pCtx, *pCounter); //} // // /* ** This function takes two arguments. It performance UTF-8/16 type ** conversions on the first argument then returns a copy of the second ** argument. ** ** This function is used in cases such as the following: ** ** SELECT test_isolation(x,x) FROM t1; ** ** We want to verify that the type conversions that occur on the ** first argument do not invalidate the second argument. */ static void test_isolation( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { #if !SQLITE_OMIT_UTF16 sqlite3_value_text16(argv[0]); sqlite3_value_text(argv[0]); sqlite3_value_text16(argv[0]); sqlite3_value_text(argv[0]); #endif sqlite3_result_value( pCtx, argv[1] ); } /* ** Invoke an SQL statement recursively. The function result is the ** first column of the first row of the result set. */ static void test_eval( sqlite3_context pCtx, /* Function context */ int nArg, /* Number of function arguments */ sqlite3_value[] argv /* Values for all function arguments */ ) { sqlite3_stmt pStmt = new sqlite3_stmt(); int rc; sqlite3 db = sqlite3_context_db_handle( pCtx ); string zSql; zSql = sqlite3_value_text( argv[0] ); rc = sqlite3_prepare_v2( db, zSql, -1, ref pStmt, 0 ); if ( rc == SQLITE_OK ) { rc = sqlite3_step( pStmt ); if ( rc == SQLITE_ROW ) { sqlite3_result_value( pCtx, sqlite3_column_value( pStmt, 0 ) ); } rc = sqlite3_finalize( pStmt ); } if ( rc != 0 ) { string zErr; Debug.Assert( pStmt == null ); zErr = sqlite3_mprintf( "sqlite3_prepare_v2() error: %s", sqlite3_errmsg( db ) ); sqlite3_result_text( pCtx, zErr, -1, null );//sqlite3_free ); sqlite3_result_error_code( pCtx, rc ); } } class _aFuncs { public string zName; public int nArg; public u8 eTextRep; /* 1: UTF-16. 0: UTF-8 */ public dxFunc xFunc; public _aFuncs( string zName, int nArg, u8 eTextRep, dxFunc xFunc ) { this.zName = zName; this.nArg = nArg; this.eTextRep = eTextRep; this.xFunc = xFunc; } } /* ** convert one character from hex to binary */ static int testHexChar( char c ) { if ( c >= '0' && c <= '9' ) { return c - '0'; } else if ( c >= 'a' && c <= 'f' ) { return c - 'a' + 10; } else if ( c >= 'A' && c <= 'F' ) { return c - 'A' + 10; } return 0; } /* ** Convert hex to binary. */ static void testHexToBin( string zIn, ref string zOut ) { for ( int zIx = 0; zIx < zIn.Length - 1; zIx += 2 )// zIn[0] && zIn[1] ) { //*(zOut++) = (testHexChar(zIn[0])<<4) + testHexChar(zIn[1]); zOut += ( testHexChar( zIn[zIx] ) << 4 ) + testHexChar( zIn[zIx + 1] ); //zIn += 2; } } #if !SQLITE_OMIT_UTF16 /* ** hex_to_utf16be(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ static void testHexToUtf16be( sqlite3_context *pCtx, int nArg, sqlite3_value **argv ){ int n; string zIn; string zOut; Debug.Assert( nArg==1 ); n = sqlite3_value_bytes(argv[0]); zIn = (const char)sqlite3_value_text(argv[0]); zOut = sqlite3_malloc( n/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite3_result_text16be(pCtx, zOut, n/2, sqlite3_free); } } #endif /* ** hex_to_utf8(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ static void testHexToUtf8( sqlite3_context pCtx, int nArg, sqlite3_value[] argv ) { int n; string zIn; string zOut = ""; Debug.Assert( nArg == 1 ); n = sqlite3_value_bytes( argv[0] ); zIn = sqlite3_value_text( argv[0] ); //zOut = sqlite3_malloc( n/2 ); //if( zOut==0 ){ // sqlite3_result_error_nomem(pCtx); //}else{ testHexToBin( zIn, ref zOut ); sqlite3_result_text( pCtx, zOut, n / 2, null );//sqlite3_free ); //} } #if !SQLITE_OMIT_UTF16 /* ** hex_to_utf16le(HEX) ** ** Convert the input string from HEX into binary. Then return the ** result using sqlite3_result_text16le(). */ static void testHexToUtf16le( sqlite3_context *pCtx, int nArg, sqlite3_value **argv ){ int n; string zIn; string zOut; Debug.Assert( nArg==1 ); n = sqlite3_value_bytes(argv[0]); zIn = (const char)sqlite3_value_text(argv[0]); zOut = sqlite3_malloc( n/2 ); if( zOut==0 ){ sqlite3_result_error_nomem(pCtx); }else{ testHexToBin(zIn, zOut); sqlite3_result_text16le(pCtx, zOut, n/2, sqlite3_free); } } #endif static int registerTestFunctions( sqlite3 db, ref string dummy1, sqlite3_api_routines dummy2 ) { _aFuncs[] aFuncs = new _aFuncs[] { new _aFuncs( "randstr", 2, SQLITE_UTF8, randStr ), new _aFuncs( "test_destructor", 1, SQLITE_UTF8, test_destructor), #if !SQLITE_OMIT_UTF16 { "test_destructor16", 1, SQLITE_UTF8, test_destructor16}, { "hex_to_utf16be", 1, SQLITE_UTF8, testHexToUtf16be}, { "hex_to_utf16le", 1, SQLITE_UTF8, testHexToUtf16le}, #endif new _aFuncs( "hex_to_utf8", 1, SQLITE_UTF8, testHexToUtf8), new _aFuncs( "test_destructor_count", 0, SQLITE_UTF8, test_destructor_count), new _aFuncs( "test_auxdata", -1, SQLITE_UTF8, test_auxdata), new _aFuncs( "test_error", 1, SQLITE_UTF8, test_error), new _aFuncs( "test_error", 2, SQLITE_UTF8, test_error), new _aFuncs( "test_eval", 1, SQLITE_UTF8, test_eval), new _aFuncs( "test_isolation", 2, SQLITE_UTF8, test_isolation), //{ "test_counter", 2, SQLITE_UTF8, counterFunc}, }; int i; for ( i = 0; i < aFuncs.Length; i++ ) {//sizeof(aFuncs)/sizeof(aFuncs[0]); i++){ sqlite3_create_function( db, aFuncs[i].zName, aFuncs[i].nArg, aFuncs[i].eTextRep, 0, aFuncs[i].xFunc, null, null ); } sqlite3_create_function( db, "test_agg_errmsg16", 0, SQLITE_ANY, 0, null, test_agg_errmsg16_step, test_agg_errmsg16_final ); return SQLITE_OK; } /* ** TCLCMD: autoinstall_test_functions ** ** Invoke this TCL command to use sqlite3_auto_extension() to cause ** the standard set of test functions to be loaded into each new ** database connection. */ static int autoinstall_test_funcs( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { //extern int Md5_Register(sqlite3); int rc = sqlite3_auto_extension( (dxInit)registerTestFunctions ); if ( rc == SQLITE_OK ) { rc = sqlite3_auto_extension( (dxInit)Md5_Register ); } TCL.Tcl_SetObjResult( interp, TCL.Tcl_NewIntObj( rc ) ); return TCL.TCL_OK; } /* ** A bogus step function and finalizer function. */ static void tStep( sqlite3_context a, int b, sqlite3_value[] c ) { } static void tFinal( sqlite3_context a ) { } /* ** tclcmd: abuse_create_function ** ** Make various calls to sqlite3_create_function that do not have valid ** parameters. Verify that the error condition is detected and reported. */ static int abuse_create_function( object clientdata, Tcl_Interp interp, int objc, Tcl_Obj[] objv ) { //extern int getDbPointer(Tcl_Interp*, const char*, sqlite3*); sqlite3 db = null; int rc; int mxArg; if ( getDbPointer( interp, TCL.Tcl_GetString( objv[1] ), out db ) != 0 ) return TCL.TCL_ERROR; rc = sqlite3_create_function( db, "tx", 1, SQLITE_UTF8, 0, tStep, tStep, tFinal ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", 1, SQLITE_UTF8, 0, tStep, tStep, null ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", 1, SQLITE_UTF8, 0, tStep, null, tFinal ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", 1, SQLITE_UTF8, 0, null, null, tFinal ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", 1, SQLITE_UTF8, 0, null, tStep, null ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", -2, SQLITE_UTF8, 0, tStep, null, null ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "tx", 128, SQLITE_UTF8, 0, tStep, null, null ); if ( rc != SQLITE_MISUSE ) goto abuse_err; rc = sqlite3_create_function( db, "funcxx" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789", 1, SQLITE_UTF8, 0, tStep, null, null ); if ( rc != SQLITE_MISUSE ) goto abuse_err; /* This last function registration should actually work. Generate ** a no-op function (that always returns NULL) and which has the ** maximum-length function name and the maximum number of parameters. */ sqlite3_limit( db, SQLITE_LIMIT_FUNCTION_ARG, 10000 ); mxArg = sqlite3_limit( db, SQLITE_LIMIT_FUNCTION_ARG, -1 ); rc = sqlite3_create_function( db, "nullx" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789" + "_123456789_123456789_123456789_123456789_123456789", mxArg, SQLITE_UTF8, 0, tStep, null, null ); if ( rc != SQLITE_OK ) goto abuse_err; return TCL.TCL_OK; abuse_err: TCL.Tcl_AppendResult( interp, "sqlite3_create_function abused test failed" ); return TCL.TCL_ERROR; } /* ** Register commands with the TCL interpreter. */ public static int Sqlitetest_func_Init( Tcl_Interp interp ) { //static struct { // string zName; // Tcl_ObjCmdProc *xProc; //} _aObjCmd[] aObjCmd = new _aObjCmd[] { new _aObjCmd( "autoinstall_test_functions", autoinstall_test_funcs ), new _aObjCmd( "abuse_create_function", abuse_create_function ), }; int i; //extern int Md5_Register(sqlite3); for ( i = 0; i < aObjCmd.Length; i++ ) {//sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ TCL.Tcl_CreateObjCommand( interp, aObjCmd[i].zName, aObjCmd[i].xProc, null, null ); } sqlite3_initialize(); sqlite3_auto_extension( (dxInit)registerTestFunctions ); sqlite3_auto_extension( (dxInit)Md5_Register ); return TCL.TCL_OK; } } #endif }