/* ** 2001 September 15 ** ** 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 ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. */ #include "sqliteInt.h" #include "vdbeInt.h" #include "tcl.h" #include "testInt.h" #include #include /* ** This is a copy of the first part of the SqliteDb structure in ** tclsqlite.c. We need it here so that the get_sqlite_pointer routine ** can extract the sqlite4* pointer from an existing Tcl SQLite ** connection. */ struct SqliteDb { sqlite4 *db; }; /* ** Convert text generated by the "%p" conversion format back into ** a pointer. */ static int testHexToInt(int h){ if( h>='0' && h<='9' ){ return h - '0'; }else if( h>='a' && h<='f' ){ return h - 'a' + 10; }else{ assert( h>='A' && h<='F' ); return h - 'A' + 10; } } void *sqlite4TestTextToPtr(const char *z){ void *p; u64 v; u32 v2; if( z[0]=='0' && z[1]=='x' ){ z += 2; } v = 0; while( *z ){ v = (v<<4) + testHexToInt(*z); z++; } if( sizeof(p)==sizeof(v) ){ memcpy(&p, &v, sizeof(p)); }else{ assert( sizeof(p)==sizeof(v2) ); v2 = (u32)v; memcpy(&p, &v2, sizeof(p)); } return p; } /* ** A TCL command that returns the address of the sqlite* pointer ** for an sqlite connection instance. Bad things happen if the ** input is not an sqlite connection. */ static int get_sqlite_pointer( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ struct SqliteDb *p; Tcl_CmdInfo cmdInfo; char zBuf[100]; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SQLITE-CONNECTION"); return TCL_ERROR; } if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ Tcl_AppendResult(interp, "command not found: ", Tcl_GetString(objv[1]), (char*)0); return TCL_ERROR; } p = (struct SqliteDb*)cmdInfo.objClientData; sprintf(zBuf, "%p", p->db); if( strncmp(zBuf,"0x",2) ){ sprintf(zBuf, "0x%p", p->db); } Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Decode a pointer to an sqlite4 object. */ int getDbPointer(Tcl_Interp *interp, const char *zA, sqlite4 **ppDb){ struct SqliteDb *p; Tcl_CmdInfo cmdInfo; if( Tcl_GetCommandInfo(interp, zA, &cmdInfo) ){ p = (struct SqliteDb*)cmdInfo.objClientData; *ppDb = p->db; }else{ *ppDb = (sqlite4*)sqlite4TestTextToPtr(zA); } return TCL_OK; } int sqlite4TestDbHandle(Tcl_Interp *interp, Tcl_Obj *pObj, sqlite4 **ppDb){ return getDbPointer(interp, Tcl_GetString(pObj), ppDb); } const char *sqlite4TestErrorName(int rc){ const char *zName = 0; switch( rc ){ case SQLITE4_OK: zName = "SQLITE4_OK"; break; case SQLITE4_ERROR: zName = "SQLITE4_ERROR"; break; case SQLITE4_INTERNAL: zName = "SQLITE4_INTERNAL"; break; case SQLITE4_PERM: zName = "SQLITE4_PERM"; break; case SQLITE4_ABORT: zName = "SQLITE4_ABORT"; break; case SQLITE4_BUSY: zName = "SQLITE4_BUSY"; break; case SQLITE4_LOCKED: zName = "SQLITE4_LOCKED"; break; case SQLITE4_LOCKED_SHAREDCACHE: zName = "SQLITE4_LOCKED_SHAREDCACHE";break; case SQLITE4_NOMEM: zName = "SQLITE4_NOMEM"; break; case SQLITE4_READONLY: zName = "SQLITE4_READONLY"; break; case SQLITE4_INTERRUPT: zName = "SQLITE4_INTERRUPT"; break; case SQLITE4_IOERR: zName = "SQLITE4_IOERR"; break; case SQLITE4_CORRUPT: zName = "SQLITE4_CORRUPT"; break; case SQLITE4_NOTFOUND: zName = "SQLITE4_NOTFOUND"; break; case SQLITE4_FULL: zName = "SQLITE4_FULL"; break; case SQLITE4_CANTOPEN: zName = "SQLITE4_CANTOPEN"; break; case SQLITE4_PROTOCOL: zName = "SQLITE4_PROTOCOL"; break; case SQLITE4_EMPTY: zName = "SQLITE4_EMPTY"; break; case SQLITE4_SCHEMA: zName = "SQLITE4_SCHEMA"; break; case SQLITE4_TOOBIG: zName = "SQLITE4_TOOBIG"; break; case SQLITE4_CONSTRAINT: zName = "SQLITE4_CONSTRAINT"; break; case SQLITE4_MISMATCH: zName = "SQLITE4_MISMATCH"; break; case SQLITE4_MISUSE: zName = "SQLITE4_MISUSE"; break; case SQLITE4_NOLFS: zName = "SQLITE4_NOLFS"; break; case SQLITE4_AUTH: zName = "SQLITE4_AUTH"; break; case SQLITE4_FORMAT: zName = "SQLITE4_FORMAT"; break; case SQLITE4_RANGE: zName = "SQLITE4_RANGE"; break; case SQLITE4_NOTADB: zName = "SQLITE4_NOTADB"; break; case SQLITE4_ROW: zName = "SQLITE4_ROW"; break; case SQLITE4_DONE: zName = "SQLITE4_DONE"; break; case SQLITE4_INEXACT: zName = "SQLITE4_INEXACT"; break; case SQLITE4_IOERR_READ: zName = "SQLITE4_IOERR_READ"; break; case SQLITE4_IOERR_SHORT_READ: zName = "SQLITE4_IOERR_SHORT_READ"; break; case SQLITE4_IOERR_WRITE: zName = "SQLITE4_IOERR_WRITE"; break; case SQLITE4_IOERR_FSYNC: zName = "SQLITE4_IOERR_FSYNC"; break; case SQLITE4_IOERR_DIR_FSYNC: zName = "SQLITE4_IOERR_DIR_FSYNC"; break; case SQLITE4_IOERR_TRUNCATE: zName = "SQLITE4_IOERR_TRUNCATE"; break; case SQLITE4_IOERR_FSTAT: zName = "SQLITE4_IOERR_FSTAT"; break; case SQLITE4_IOERR_UNLOCK: zName = "SQLITE4_IOERR_UNLOCK"; break; case SQLITE4_IOERR_RDLOCK: zName = "SQLITE4_IOERR_RDLOCK"; break; case SQLITE4_IOERR_DELETE: zName = "SQLITE4_IOERR_DELETE"; break; case SQLITE4_IOERR_BLOCKED: zName = "SQLITE4_IOERR_BLOCKED"; break; case SQLITE4_IOERR_NOMEM: zName = "SQLITE4_IOERR_NOMEM"; break; case SQLITE4_IOERR_ACCESS: zName = "SQLITE4_IOERR_ACCESS"; break; case SQLITE4_IOERR_CHECKRESERVEDLOCK: zName = "SQLITE4_IOERR_CHECKRESERVEDLOCK"; break; case SQLITE4_IOERR_LOCK: zName = "SQLITE4_IOERR_LOCK"; break; case SQLITE4_CORRUPT_VTAB: zName = "SQLITE4_CORRUPT_VTAB"; break; case SQLITE4_READONLY_RECOVERY: zName = "SQLITE4_READONLY_RECOVERY"; break; case SQLITE4_READONLY_CANTLOCK: zName = "SQLITE4_READONLY_CANTLOCK"; break; default: zName = "SQLITE4_Unknown"; break; } return zName; } #define t1ErrorName sqlite4TestErrorName int sqlite4TestSetResult(Tcl_Interp *interp, int rc){ const char *z = sqlite4TestErrorName(rc); Tcl_SetObjResult(interp, Tcl_NewStringObj(z, -1)); return TCL_OK; } /* ** Convert an sqlite4_stmt* into an sqlite4*. This depends on the ** fact that the sqlite4* is the first field in the Vdbe structure. */ #define StmtToDb(X) sqlite4_db_handle(X) /* ** Check a return value to make sure it agrees with the results ** from sqlite4_errcode. */ int sqlite4TestErrCode(Tcl_Interp *interp, sqlite4 *db, int rc){ if( rc!=SQLITE4_MISUSE && rc!=SQLITE4_OK && sqlite4_errcode(db)!=rc ){ char zBuf[200]; int r2 = sqlite4_errcode(db); sprintf(zBuf, "error code %s (%d) does not match sqlite4_errcode %s (%d)", t1ErrorName(rc), rc, t1ErrorName(r2), r2); Tcl_ResetResult(interp); Tcl_AppendResult(interp, zBuf, 0); return 1; } return 0; } /* ** Decode a pointer to an sqlite4_stmt object. */ static int getStmtPointer( Tcl_Interp *interp, const char *zArg, sqlite4_stmt **ppStmt ){ *ppStmt = (sqlite4_stmt*)sqlite4TestTextToPtr(zArg); return TCL_OK; } /* ** Generate a text representation of a pointer that can be understood ** by the getDbPointer and getVmPointer routines above. ** ** The problem is, on some machines (Solaris) if you do a printf with ** "%p" you cannot turn around and do a scanf with the same "%p" and ** get your pointer back. You have to prepend a "0x" before it will ** work. Or at least that is what is reported to me (drh). But this ** behavior varies from machine to machine. The solution used her is ** to test the string right after it is generated to see if it can be ** understood by scanf, and if not, try prepending an "0x" to see if ** that helps. If nothing works, a fatal error is generated. */ int sqlite4TestMakePointerStr(Tcl_Interp *interp, char *zPtr, void *p){ sqlite4_snprintf(zPtr, 100, "%p", p); return TCL_OK; } /* ** The callback routine for sqlite4_exec_printf(). */ static int exec_printf_cb( void *pArg, int nVal, sqlite4_value **apVal, const char **azCol ){ Tcl_DString *str = (Tcl_DString*)pArg; int i; if( Tcl_DStringLength(str)==0 ){ for(i=0; imutex); return TCL_OK; } static int db_leave( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; sqlite4_mutex_leave(db->mutex); return TCL_OK; } /* ** Usage: sqlite4_exec DB SQL ** ** Invoke the sqlite4_exec interface using the open database DB */ static int test_exec( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; Tcl_DString str; int rc; char *zSql; int i, j; char zBuf[30]; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB SQL", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; Tcl_DStringInit(&str); zSql = sqlite4_mprintf(0, "%s", argv[2]); for(i=j=0; zSql[i];){ if( zSql[i]=='%' ){ zSql[j++] = (testHexToInt(zSql[i+1])<<4) + testHexToInt(zSql[i+2]); i += 3; }else{ zSql[j++] = zSql[i++]; } } zSql[j] = 0; rc = sqlite4_exec(db, zSql, exec_printf_cb, &str); sqlite4_free(0, zSql); sprintf(zBuf, "%d", rc); Tcl_AppendElement(interp, zBuf); Tcl_AppendElement(interp, !rc ? Tcl_DStringValue(&str) : sqlite4_errmsg(db)); Tcl_DStringFree(&str); if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; return TCL_OK; } /* ** Usage: sqlite4_exec_nr DB SQL ** ** Invoke the sqlite4_exec interface using the open database DB. Discard ** all results */ static int test_exec_nr( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; int rc; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB SQL", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_exec(db, argv[2], 0, 0); if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; return TCL_OK; } /* ** Usage: sqlite4_mprintf_z_test SEPARATOR ARG0 ARG1 ... ** ** Test the %z format of sqlite_mprintf(). Use multiple mprintf() calls to ** concatenate arg0 through argn using separator as the separator. ** Return the result. */ static int test_mprintf_z( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char *zResult = 0; int i; for(i=2; isizeof(zStr) ) n = sizeof(zStr); sqlite4_snprintf(zStr, sizeof(zStr), "abcdefghijklmnopqrstuvwxyz"); sqlite4_snprintf(zStr, n, zFormat, a1); Tcl_AppendResult(interp, zStr, 0); return TCL_OK; } /* ** Usage: sqlite4_key DB KEY ** ** Set the codec key. */ static int test_key( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ #ifdef SQLITE4_HAS_CODEC sqlite4 *db; const char *zKey; int nKey; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME\"", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; zKey = argv[2]; nKey = strlen(zKey); sqlite4_key(db, zKey, nKey); #endif return TCL_OK; } /* ** Usage: sqlite4_rekey DB KEY ** ** Change the codec key. */ static int test_rekey( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ #ifdef SQLITE4_HAS_CODEC sqlite4 *db; const char *zKey; int nKey; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME\"", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; zKey = argv[2]; nKey = strlen(zKey); sqlite4_rekey(db, zKey, nKey); #endif return TCL_OK; } /* ** Usage: sqlite4_close DB ** ** Closes the database opened by sqlite4_open. */ static int sqlite_test_close( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME\"", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_close(db, 0); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** Implementation of the x_coalesce() function. ** Return the first argument non-NULL argument. */ static void t1_ifnullFunc( sqlite4_context *context, int argc, sqlite4_value **argv ){ int i; for(i=0; inUsed + n + 2 > p->nAlloc ){ char *zNew; p->nAlloc = p->nAlloc*2 + n + 200; zNew = sqlite4_realloc(0, p->z, p->nAlloc); if( zNew==0 ){ sqlite4_free(0, p->z); memset(p, 0, sizeof(*p)); return; } p->z = zNew; } if( divider && p->nUsed>0 ){ p->z[p->nUsed++] = divider; } memcpy(&p->z[p->nUsed], z, n+1); p->nUsed += n; } /* ** Invoked for each callback from sqlite4ExecFunc */ static int execFuncCallback( void *pData, int nVal, sqlite4_value **apVal, const char **azCol ){ struct dstr *p = (struct dstr*)pData; int i; for(i=0; in++; } if( argc>0 ){ int v = sqlite4_value_int(argv[0]); if( v==40 ){ sqlite4_result_error(context, "value of 40 handed to x_count", -1); #ifndef SQLITE4_OMIT_UTF16 }else if( v==41 ){ const char zUtf16ErrMsg[] = { 0, 0x61, 0, 0x62, 0, 0x63, 0, 0, 0}; sqlite4_result_error16(context, &zUtf16ErrMsg[1-SQLITE4_BIGENDIAN], -1); #endif } } } static void t1CountFinalize(sqlite4_context *context){ t1CountCtx *p; p = sqlite4_aggregate_context(context, sizeof(*p)); if( p ){ if( p->n==42 ){ sqlite4_result_error(context, "x_count totals to 42", -1); }else{ sqlite4_result_int(context, p ? p->n : 0); } } } /* ** Usage: sqlite4_create_aggregate DB ** ** Call the sqlite4_create_function API on the given database in order ** to create a function named "x_count". This function is similar ** to the built-in count() function, with a few special quirks ** for testing the sqlite4_result_error() APIs. ** ** The original motivation for this routine was to be able to call the ** sqlite4_create_aggregate function while a query is in progress in order ** to test the SQLITE4_MISUSE detection logic. See misuse.test. ** ** This routine was later extended to test the use of sqlite4_result_error() ** within aggregate functions. ** ** Later: It is now also extended to register the aggregate function ** "legacy_count()" with the supplied database handle. This is used ** to test the deprecated sqlite4_aggregate_count() API. */ static int test_create_aggregate( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; int rc; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FILENAME\"", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_function(db, "x_count", 0, 0, 0, t1CountStep, t1CountFinalize, 0); if( rc==SQLITE4_OK ){ rc = sqlite4_create_function(db, "x_count", 1, 0, 0, t1CountStep, t1CountFinalize, 0); } if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } /* ** Usage: printf TEXT ** ** Send output to printf. Use this rather than puts to merge the output ** in the correct sequence with debugging printfs inserted into C code. ** Puts uses a separate buffer and debugging statements will be out of ** sequence if it is used. */ static int test_printf( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " TEXT\"", 0); return TCL_ERROR; } printf("%s\n", argv[1]); return TCL_OK; } /* ** Usage: sqlite4_mprintf_int FORMAT INTEGER INTEGER INTEGER ** ** Call mprintf with three integer arguments */ static int sqlite4_mprintf_int( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int a[3], i; char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT INT\"", 0); return TCL_ERROR; } for(i=2; i<5; i++){ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; } z = sqlite4_mprintf(0, argv[1], a[0], a[1], a[2]); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_int64 FORMAT INTEGER INTEGER INTEGER ** ** Call mprintf with three 64-bit integer arguments */ static int sqlite4_mprintf_int64( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int i; sqlite4_int64 a[3]; char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT INT\"", 0); return TCL_ERROR; } for(i=2; i<5; i++){ if( sqlite4Atoi64(argv[i], &a[i-2], 1000000, SQLITE4_UTF8) ){ Tcl_AppendResult(interp, "argument is not a valid 64-bit integer", 0); return TCL_ERROR; } } z = sqlite4_mprintf(0, argv[1], a[0], a[1], a[2]); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_long FORMAT INTEGER INTEGER INTEGER ** ** Call mprintf with three long integer arguments. This might be the ** same as sqlite4_mprintf_int or sqlite4_mprintf_int64, depending on ** platform. */ static int sqlite4_mprintf_long( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int i; long int a[3]; int b[3]; char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT INT\"", 0); return TCL_ERROR; } for(i=2; i<5; i++){ if( Tcl_GetInt(interp, argv[i], &b[i-2]) ) return TCL_ERROR; a[i-2] = (long int)b[i-2]; a[i-2] &= (((u64)1)<<(sizeof(int)*8))-1; } z = sqlite4_mprintf(0, argv[1], a[0], a[1], a[2]); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_str FORMAT INTEGER INTEGER STRING ** ** Call mprintf with two integer arguments and one string argument */ static int sqlite4_mprintf_str( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int a[3], i; char *z; if( argc<4 || argc>5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT ?STRING?\"", 0); return TCL_ERROR; } for(i=2; i<4; i++){ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; } z = sqlite4_mprintf(0, argv[1], a[0], a[1], argc>4 ? argv[4] : NULL); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_snprintf_str INTEGER FORMAT INTEGER INTEGER STRING ** ** Call mprintf with two integer arguments and one string argument */ static int sqlite4_snprintf_str( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int a[3], i; int n; char *z; if( argc<5 || argc>6 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " INT FORMAT INT INT ?STRING?\"", 0); return TCL_ERROR; } if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR; if( n<0 ){ Tcl_AppendResult(interp, "N must be non-negative", 0); return TCL_ERROR; } for(i=3; i<5; i++){ if( Tcl_GetInt(interp, argv[i], &a[i-3]) ) return TCL_ERROR; } z = sqlite4_malloc(0, n+1); sqlite4_snprintf(z, n, argv[2], a[0], a[1], argc>4 ? argv[5] : NULL); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_double FORMAT INTEGER INTEGER DOUBLE ** ** Call mprintf with two integer arguments and one double argument */ static int sqlite4_mprintf_double( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int a[3], i; double r; char *z; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT INT INT DOUBLE\"", 0); return TCL_ERROR; } for(i=2; i<4; i++){ if( Tcl_GetInt(interp, argv[i], &a[i-2]) ) return TCL_ERROR; } if( Tcl_GetDouble(interp, argv[4], &r) ) return TCL_ERROR; z = sqlite4_mprintf(0, argv[1], a[0], a[1], r); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_scaled FORMAT DOUBLE DOUBLE ** ** Call mprintf with a single double argument which is the product of the ** two arguments given above. This is used to generate overflow and underflow ** doubles to test that they are converted properly. */ static int sqlite4_mprintf_scaled( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ int i; double r[2]; char *z; if( argc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT DOUBLE DOUBLE\"", 0); return TCL_ERROR; } for(i=2; i<4; i++){ if( Tcl_GetDouble(interp, argv[i], &r[i-2]) ) return TCL_ERROR; } z = sqlite4_mprintf(0, argv[1], r[0]*r[1]); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_stronly FORMAT STRING ** ** Call mprintf with a single double argument which is the product of the ** two arguments given above. This is used to generate overflow and underflow ** doubles to test that they are converted properly. */ static int sqlite4_mprintf_stronly( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char *z; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT STRING\"", 0); return TCL_ERROR; } z = sqlite4_mprintf(0, argv[1], argv[2]); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_mprintf_hexdouble FORMAT HEX ** ** Call mprintf with a single double argument which is derived from the ** hexadecimal encoding of an IEEE double. */ static int sqlite4_mprintf_hexdouble( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ char *z; double r; unsigned int x1, x2; sqlite4_uint64 d; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " FORMAT STRING\"", 0); return TCL_ERROR; } if( sscanf(argv[2], "%08x%08x", &x2, &x1)!=2 ){ Tcl_AppendResult(interp, "2nd argument should be 16-characters of hex", 0); return TCL_ERROR; } d = x2; d = (d<<32) + x1; memcpy(&r, &d, sizeof(r)); z = sqlite4_mprintf(0, argv[1], r); Tcl_AppendResult(interp, z, 0); sqlite4_free(0, z); return TCL_OK; } /* ** Usage: sqlite4_libversion_number ** */ static int test_libversion_number( ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_libversion_number())); return TCL_OK; } /* ** Usage: sqlite4_table_column_metadata DB dbname tblname colname ** */ #ifdef SQLITE4_ENABLE_COLUMN_METADATA static int test_table_column_metadata( ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite4 *db; const char *zDb; const char *zTbl; const char *zCol; int rc; Tcl_Obj *pRet; const char *zDatatype; const char *zCollseq; int notnull; int primarykey; int autoincrement; if( objc!=5 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB dbname tblname colname"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zDb = Tcl_GetString(objv[2]); zTbl = Tcl_GetString(objv[3]); zCol = Tcl_GetString(objv[4]); if( strlen(zDb)==0 ) zDb = 0; rc = sqlite4_table_column_metadata(db, zDb, zTbl, zCol, &zDatatype, &zCollseq, ¬null, &primarykey, &autoincrement); if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp, sqlite4_errmsg(db), 0); return TCL_ERROR; } pRet = Tcl_NewObj(); Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zDatatype, -1)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zCollseq, -1)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(notnull)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(primarykey)); Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(autoincrement)); Tcl_SetObjResult(interp, pRet); return TCL_OK; } #endif /* ** Usage: sqlite4_create_collation DB NAME CMP-PROC MKKEY-PROC DEL-PROC */ struct TestCollationX { Tcl_Interp *interp; Tcl_Obj *pCmp; Tcl_Obj *pMkkey; Tcl_Obj *pDel; }; typedef struct TestCollationX TestCollationX; static void testCreateCollationDel(void *pCtx){ TestCollationX *p = (TestCollationX *)pCtx; int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } Tcl_DecrRefCount(p->pCmp); Tcl_DecrRefCount(p->pDel); Tcl_DecrRefCount(p->pMkkey); sqlite4_free(0, (void *)p); } static int testCreateCollationCmp( void *pCtx, sqlite4_value *pLeft, sqlite4_value *pRight, int *pRes ){ int nLeft; int nRight; const char *zLeft; const char *zRight; TestCollationX *p = (TestCollationX *)pCtx; Tcl_Obj *pScript; /* Tcl script to evaluate */ int iRes = 0; /* Result of comparison */ int rc; /* Tcl_Eval() return code */ zLeft = sqlite4_value_text(pLeft, &nLeft); zRight = sqlite4_value_text(pRight, &nRight); if( !zLeft || !zRight ) return SQLITE4_NOMEM; pScript = Tcl_DuplicateObj(p->pCmp); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zLeft, nLeft)); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zRight,nRight)); rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); if( rc==TCL_OK ){ rc = Tcl_GetIntFromObj(p->interp, Tcl_GetObjResult(p->interp), &iRes); } Tcl_DecrRefCount(pScript); if( rc!=TCL_OK ){ return SQLITE4_ERROR; } *pRes = iRes; return SQLITE4_OK; } static int testCreateCollationMkkey( void *pCtx, /* Context pointer */ sqlite4_value *pVal, /* Value to create sort-key for */ int nBuf, /* Size of output buffer in bytes */ void *pBuf, /* Output buffer */ int *pnOut /* Size of sort-key in bytes */ ){ TestCollationX *p = (TestCollationX *)pCtx; Tcl_Obj *pScript; const char *zIn; int nIn; int rc; const char *zOut; int nOut; zIn = sqlite4_value_text(pVal, &nIn); if( !zIn ) return SQLITE4_NOMEM; pScript = Tcl_DuplicateObj(p->pMkkey); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zIn, nIn)); rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); if( rc!=TCL_OK ){ return SQLITE4_ERROR; } zOut = Tcl_GetStringFromObj(Tcl_GetObjResult(p->interp), &nOut); if( nOut<=nBuf ){ memcpy(pBuf, zOut, nOut); } *pnOut = nOut; return SQLITE4_OK; } static int test_create_collation( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ /* Callback functions */ int(*xCompare)(void*, sqlite4_value*, sqlite4_value*, int*); int(*xMakeKey)(void*, sqlite4_value*, int, void*, int*); int nByte; /* Size of callback scripts in bytes */ TestCollationX *p; /* New context object */ sqlite4 *db; /* Database handle */ int rc; /* create_collation() return code */ if( objc!=6 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB NAME CMP-PROC MKKEY-PROC DEL-PROC"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; Tcl_GetStringFromObj(objv[3], &nByte); xCompare = (nByte>0 ? testCreateCollationCmp : 0); Tcl_GetStringFromObj(objv[4], &nByte); xMakeKey = (nByte>0 ? testCreateCollationMkkey : 0); if( xCompare==0 && xMakeKey==0 ){ rc = sqlite4_create_collation(db, Tcl_GetString(objv[2]), 0, 0, 0, 0); }else{ p = (TestCollationX *)sqlite4_malloc(0, sizeof(TestCollationX)); p->pCmp = objv[3]; p->pMkkey = objv[4]; p->pDel = objv[5]; p->interp = interp; Tcl_IncrRefCount(p->pCmp); Tcl_IncrRefCount(p->pMkkey); Tcl_IncrRefCount(p->pDel); rc = sqlite4_create_collation(db, Tcl_GetString(objv[2]), (void *)p, testCreateCollationCmp, testCreateCollationMkkey, testCreateCollationDel ); } if( rc!=SQLITE4_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } /* End of [sqlite4_create_collation] implementation. ********************************************************************/ /* ** Usage: sqlite4_collation_needed DB CALLBACK-PROC DEL-PROC */ struct TestNeededX { Tcl_Interp *interp; Tcl_Obj *pNeeded; Tcl_Obj *pDel; }; typedef struct TestNeededX TestNeededX; static void testCollationNeeded(void *pCtx, sqlite4 *db, const char *zReq){ TestNeededX *p = (TestNeededX *)pCtx; Tcl_Obj *pScript; int rc; pScript = Tcl_DuplicateObj(p->pNeeded); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(zReq, -1)); rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } } static void testCollationNeededDel(void *pCtx){ TestNeededX *p = (TestNeededX*)pCtx; int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } Tcl_DecrRefCount(p->pNeeded); Tcl_DecrRefCount(p->pDel); sqlite4_free(0, (void *)p); } static int test_collation_needed( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int nByte; /* Size of callback scripts in bytes */ TestNeededX *p; /* New context object */ sqlite4 *db; /* Database handle */ int rc; /* create_collation() return code */ if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB CALLBACK-PROC DEL-PROC"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; Tcl_GetStringFromObj(objv[2], &nByte); if( nByte==0 ){ rc = sqlite4_collation_needed(db, 0, 0, 0); }else{ p = (TestNeededX*)sqlite4_malloc(0, sizeof(TestNeededX)); p->pNeeded = objv[2]; p->pDel = objv[3]; p->interp = interp; Tcl_IncrRefCount(p->pNeeded); Tcl_IncrRefCount(p->pDel); rc = sqlite4_collation_needed( db, (void*)p, testCollationNeeded, testCollationNeededDel ); } if( rc!=SQLITE4_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4TestErrorName(rc), -1)); return TCL_ERROR; } Tcl_ResetResult(interp); return TCL_OK; } /* End of [sqlite4_collation_needed] implementation. ********************************************************************/ /* ** Usage: sqlite4_profile DB PROFILE-CMD DEL-CMD */ struct TestProfileX { Tcl_Interp *interp; Tcl_Obj *pProfile; Tcl_Obj *pDel; }; typedef struct TestProfileX TestProfileX; static void testProfile(void *pCtx, const char *z, sqlite4_uint64 i){ TestProfileX *p = (TestProfileX *)pCtx; Tcl_Obj *pScript; int rc; pScript = Tcl_DuplicateObj(p->pProfile); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(z, -1)); Tcl_ListObjAppendElement(0, pScript, Tcl_NewWideIntObj(i)); rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } } static void testProfileDel(void *pCtx){ TestProfileX *p = (TestProfileX*)pCtx; int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } Tcl_DecrRefCount(p->pProfile); Tcl_DecrRefCount(p->pDel); sqlite4_free(0, (void *)p); } static int test_profile( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int nByte; /* Size of callback scripts in bytes */ TestProfileX *p; /* New context object */ sqlite4 *db; /* Database handle */ if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB PROFILE-CMD DEL-CMD"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; Tcl_GetStringFromObj(objv[2], &nByte); if( nByte==0 ){ sqlite4_profile(db, 0, 0, 0); }else{ p = (TestProfileX*)sqlite4_malloc(0, sizeof(TestProfileX)); p->pProfile = objv[2]; p->pDel = objv[3]; p->interp = interp; Tcl_IncrRefCount(p->pProfile); Tcl_IncrRefCount(p->pDel); sqlite4_profile(db, (void*)p, testProfile, testProfileDel); } Tcl_ResetResult(interp); return TCL_OK; } /* End of [sqlite4_profile] implementation. ********************************************************************/ /* ** Usage: sqlite4_trace DB TRACE-CMD DEL-CMD */ struct TestTraceX { Tcl_Interp *interp; Tcl_Obj *pTrace; Tcl_Obj *pDel; }; typedef struct TestTraceX TestTraceX; static void testTrace(void *pCtx, const char *z){ TestTraceX *p = (TestTraceX *)pCtx; Tcl_Obj *pScript; int rc; pScript = Tcl_DuplicateObj(p->pTrace); Tcl_IncrRefCount(pScript); Tcl_ListObjAppendElement(0, pScript, Tcl_NewStringObj(z, -1)); rc = Tcl_EvalObjEx(p->interp, pScript, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); Tcl_DecrRefCount(pScript); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } } static void testTraceDel(void *pCtx){ TestTraceX *p = (TestTraceX*)pCtx; int rc = Tcl_EvalObjEx(p->interp, p->pDel, TCL_EVAL_DIRECT|TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); } Tcl_DecrRefCount(p->pTrace); Tcl_DecrRefCount(p->pDel); sqlite4_free(0, (void *)p); } static int test_trace( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ int nByte; /* Size of callback scripts in bytes */ TestTraceX *p; /* New context object */ sqlite4 *db; /* Database handle */ if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB PROFILE-CMD DEL-CMD"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; Tcl_GetStringFromObj(objv[2], &nByte); if( nByte==0 ){ sqlite4_trace(db, 0, 0, 0); }else{ p = (TestTraceX*)sqlite4_malloc(0, sizeof(TestTraceX)); p->pTrace = objv[2]; p->pDel = objv[3]; p->interp = interp; Tcl_IncrRefCount(p->pTrace); Tcl_IncrRefCount(p->pDel); sqlite4_trace(db, (void*)p, testTrace, testTraceDel); } Tcl_ResetResult(interp); return TCL_OK; } /* End of [sqlite4_profile] implementation. ********************************************************************/ /* ** USAGE: sqlite4_create_function_v2 DB NAME NARG ENC ?SWITCHES? ** ** Available switches are: ** ** -func SCRIPT ** -step SCRIPT ** -final SCRIPT ** -destroy SCRIPT */ typedef struct CreateFunctionV2 CreateFunctionV2; struct CreateFunctionV2 { Tcl_Interp *interp; Tcl_Obj *pFunc; /* Script for function invocation */ Tcl_Obj *pStep; /* Script for agg. step invocation */ Tcl_Obj *pFinal; /* Script for agg. finalization invocation */ Tcl_Obj *pDestroy; /* Destructor script */ }; static void cf2Func(sqlite4_context *ctx, int nArg, sqlite4_value **aArg){ } static void cf2Step(sqlite4_context *ctx, int nArg, sqlite4_value **aArg){ } static void cf2Final(sqlite4_context *ctx){ } static void cf2Destroy(void *pUser){ CreateFunctionV2 *p = (CreateFunctionV2 *)pUser; if( p->interp && p->pDestroy ){ int rc = Tcl_EvalObjEx(p->interp, p->pDestroy, 0); if( rc!=TCL_OK ) Tcl_BackgroundError(p->interp); } if( p->pFunc ) Tcl_DecrRefCount(p->pFunc); if( p->pStep ) Tcl_DecrRefCount(p->pStep); if( p->pFinal ) Tcl_DecrRefCount(p->pFinal); if( p->pDestroy ) Tcl_DecrRefCount(p->pDestroy); sqlite4_free(0, p); } static int test_create_function_v2( ClientData clientData, /* Not used */ Tcl_Interp *interp, /* The invoking TCL interpreter */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite4 *db; const char *zFunc; int nArg; CreateFunctionV2 *p; int i; int rc; if( objc<4 || (objc%2) ){ Tcl_WrongNumArgs(interp, 1, objv, "DB NAME NARG SWITCHES..."); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zFunc = Tcl_GetString(objv[2]); if( Tcl_GetIntFromObj(interp, objv[3], &nArg) ) return TCL_ERROR; p = sqlite4_malloc(0, sizeof(CreateFunctionV2)); assert( p ); memset(p, 0, sizeof(CreateFunctionV2)); p->interp = interp; for(i=4; ipFunc = objv[i+1]; break; case 1: p->pStep = objv[i+1]; break; case 2: p->pFinal = objv[i+1]; break; case 3: p->pDestroy = objv[i+1]; break; } } if( p->pFunc ) p->pFunc = Tcl_DuplicateObj(p->pFunc); if( p->pStep ) p->pStep = Tcl_DuplicateObj(p->pStep); if( p->pFinal ) p->pFinal = Tcl_DuplicateObj(p->pFinal); if( p->pDestroy ) p->pDestroy = Tcl_DuplicateObj(p->pDestroy); if( p->pFunc ) Tcl_IncrRefCount(p->pFunc); if( p->pStep ) Tcl_IncrRefCount(p->pStep); if( p->pFinal ) Tcl_IncrRefCount(p->pFinal); if( p->pDestroy ) Tcl_IncrRefCount(p->pDestroy); rc = sqlite4_create_function(db, zFunc, nArg, (void *)p, (p->pFunc ? cf2Func : 0), (p->pStep ? cf2Step : 0), (p->pFinal ? cf2Final : 0), cf2Destroy ); if( rc!=SQLITE4_OK ){ Tcl_ResetResult(interp); Tcl_AppendResult(interp, sqlite4TestErrorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite_abort ** ** Shutdown the process immediately. This is not a clean shutdown. ** This command is used to test the recoverability of a database in ** the event of a program crash. */ static int sqlite_abort( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ #if defined(_MSC_VER) /* We do this, otherwise the test will halt with a popup message * that we have to click away before the test will continue. */ _set_abort_behavior( 0, _CALL_REPORTFAULT ); #endif exit(255); assert( interp==0 ); /* This will always fail */ return TCL_OK; } /* ** The following routine is a user-defined SQL function whose purpose ** is to test the sqlite_set_result() API. */ static void testFunc(sqlite4_context *context, int argc, sqlite4_value **argv){ while( argc>=2 ){ const char *zArg0 = (char*)sqlite4_value_text(argv[0], 0); if( zArg0 ){ if( 0==sqlite4_stricmp(zArg0, "int") ){ sqlite4_result_int(context, sqlite4_value_int(argv[1])); }else if( sqlite4_stricmp(zArg0,"int64")==0 ){ sqlite4_result_int64(context, sqlite4_value_int64(argv[1])); }else if( sqlite4_stricmp(zArg0,"string")==0 ){ sqlite4_result_text(context, (char*)sqlite4_value_text(argv[1], 0), -1, SQLITE4_TRANSIENT, 0); }else if( sqlite4_stricmp(zArg0,"double")==0 ){ sqlite4_result_double(context, sqlite4_value_double(argv[1])); }else if( sqlite4_stricmp(zArg0,"null")==0 ){ sqlite4_result_null(context); }else if( sqlite4_stricmp(zArg0,"value")==0 ){ sqlite4_result_value(context, argv[sqlite4_value_int(argv[1])]); }else{ goto error_out; } }else{ goto error_out; } argc -= 2; argv += 2; } return; error_out: sqlite4_result_error(context,"first argument should be one of: " "int int64 string double null value", -1); } /* ** Usage: sqlite_register_test_function DB NAME ** ** Register the test SQL function on the database DB under the name NAME. */ static int test_register_func( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4 *db; int rc; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB FUNCTION-NAME", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_function(db, argv[2], -1, 0, testFunc, 0, 0, 0); if( rc!=0 ){ Tcl_AppendResult(interp, sqlite4ErrStr(rc), 0); return TCL_ERROR; } if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; return TCL_OK; } /* ** Usage: sqlite4_finalize STMT ** ** Finalize a statement handle. */ static int test_finalize( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int rc; sqlite4 *db = 0; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " ", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( pStmt ){ db = StmtToDb(pStmt); } rc = sqlite4_finalize(pStmt); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); if( db && sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; return TCL_OK; } /* ** Usage: sqlite4_stmt_status STMT CODE RESETFLAG ** ** Get the value of a status counter from a statement. */ static int test_stmt_status( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int iValue; int i, op, resetFlag; const char *zOpName; sqlite4_stmt *pStmt; static const struct { const char *zName; int op; } aOp[] = { { "SQLITE4_STMTSTATUS_FULLSCAN_STEP", SQLITE4_STMTSTATUS_FULLSCAN_STEP }, { "SQLITE4_STMTSTATUS_SORT", SQLITE4_STMTSTATUS_SORT }, { "SQLITE4_STMTSTATUS_AUTOINDEX", SQLITE4_STMTSTATUS_AUTOINDEX }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT PARAMETER RESETFLAG"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; zOpName = Tcl_GetString(objv[2]); for(i=0; i=ArraySize(aOp) ){ if( Tcl_GetIntFromObj(interp, objv[2], &op) ) return TCL_ERROR; } if( Tcl_GetBooleanFromObj(interp, objv[3], &resetFlag) ) return TCL_ERROR; iValue = sqlite4_stmt_status(pStmt, op, resetFlag); Tcl_SetObjResult(interp, Tcl_NewIntObj(iValue)); return TCL_OK; } /* ** Usage: sqlite4_next_stmt DB STMT ** ** Return the next statment in sequence after STMT. */ static int test_next_stmt( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; sqlite4 *db = 0; char zBuf[50]; if( objc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " DB STMT", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; if( getStmtPointer(interp, Tcl_GetString(objv[2]), &pStmt) ) return TCL_ERROR; pStmt = sqlite4_next_stmt(db, pStmt); if( pStmt ){ if( sqlite4TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } /* ** Usage: sqlite4_stmt_readonly STMT ** ** Return true if STMT is a NULL pointer or a pointer to a statement ** that is guaranteed to leave the database unmodified. */ static int test_stmt_readonly( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = sqlite4_stmt_readonly(pStmt); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc)); return TCL_OK; } /* ** Usage: sqlite4_stmt_busy STMT ** ** Return true if STMT is a non-NULL pointer to a statement ** that has been stepped but not to completion. */ static int test_stmt_busy( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = sqlite4_stmt_busy(pStmt); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(rc)); return TCL_OK; } /* ** Usage: uses_stmt_journal STMT ** ** Return true if STMT uses a statement journal. */ static int uses_stmt_journal( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; sqlite4_stmt_readonly(pStmt); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(((Vdbe *)pStmt)->needSavepoint)); return TCL_OK; } /* ** Usage: sqlite4_reset STMT ** ** Reset a statement handle. */ static int test_reset( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " ", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = sqlite4_reset(pStmt); if( pStmt && sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ){ return TCL_ERROR; } Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); /* if( rc ){ return TCL_ERROR; } */ return TCL_OK; } /* ** Usage: sqlite4_changes DB ** ** Return the number of changes made to the database by the last SQL ** execution. */ static int test_changes( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_changes(db))); return TCL_OK; } /* ** This is the "static_bind_value" that variables are bound to when ** the FLAG option of sqlite4_bind is "static" */ static char *sqlite_static_bind_value = 0; static int sqlite_static_bind_nbyte = 0; /* ** Usage: sqlite4_bind VM IDX VALUE FLAGS ** ** Sets the value of the IDX-th occurance of "?" in the original SQL ** string. VALUE is the new value. If FLAGS=="null" then VALUE is ** ignored and the value is set to NULL. If FLAGS=="static" then ** the value is set to the value of a static variable named ** "sqlite_static_bind_value". If FLAGS=="normal" then a copy ** of the VALUE is made. If FLAGS=="blob10" then a VALUE is ignored ** an a 10-byte blob "abc\000xyz\000pq" is inserted. */ static int test_bind( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ sqlite4_stmt *pStmt; int rc; int idx; if( argc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " VM IDX VALUE (null|static|normal)\"", 0); return TCL_ERROR; } if( getStmtPointer(interp, argv[1], &pStmt) ) return TCL_ERROR; if( Tcl_GetInt(interp, argv[2], &idx) ) return TCL_ERROR; if( strcmp(argv[4],"null")==0 ){ rc = sqlite4_bind_null(pStmt, idx); }else if( strcmp(argv[4],"static")==0 ){ rc = sqlite4_bind_text(pStmt, idx, sqlite_static_bind_value, -1, 0, 0); }else if( strcmp(argv[4],"static-nbytes")==0 ){ rc = sqlite4_bind_text(pStmt, idx, sqlite_static_bind_value, sqlite_static_bind_nbyte, 0, 0); }else if( strcmp(argv[4],"normal")==0 ){ rc = sqlite4_bind_text(pStmt, idx, argv[3], -1, SQLITE4_TRANSIENT, 0); }else if( strcmp(argv[4],"blob10")==0 ){ rc = sqlite4_bind_text(pStmt, idx, "abc\000xyz\000pq", 10,SQLITE4_STATIC,0); }else{ Tcl_AppendResult(interp, "4th argument should be " "\"null\" or \"static\" or \"normal\"", 0); return TCL_ERROR; } if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc ){ char zBuf[50]; sprintf(zBuf, "(%d) ", rc); Tcl_AppendResult(interp, zBuf, sqlite4ErrStr(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: add_test_function ** ** This function is used to test that SQLite selects the correct user ** function callback when multiple versions (for different text encodings) ** are available. ** ** Calling this routine registers up to three versions of the user function ** "test_function" with database handle . If the second argument is ** true, then a version of test_function is registered for UTF-8, if the ** third is true, a version is registered for UTF-16le, if the fourth is ** true, a UTF-16be version is available. Previous versions of ** test_function are deleted. ** ** The user function is implemented by calling the following TCL script: ** ** "test_function " ** ** Where is one of UTF-8, UTF-16LE or UTF16BE, and is the ** single argument passed to the SQL function. The value returned by ** the TCL script is used as the return value of the SQL function. It ** is passed to SQLite using UTF-16BE for a UTF-8 test_function(), UTF-8 ** for a UTF-16LE test_function(), and UTF-16LE for an implementation that ** prefers UTF-16BE. */ #ifndef SQLITE4_OMIT_UTF16 static void test_function_utf8( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ Tcl_Interp *interp; Tcl_Obj *pX; sqlite4_value *pVal; interp = (Tcl_Interp *)sqlite4_context_appdata(pCtx); pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-8", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj((char*)sqlite4_value_text(argv[0], 0), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); sqlite4_result_text(pCtx, Tcl_GetStringResult(interp), -1, SQLITE4_TRANSIENT, 0); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text16be(pCtx, sqlite4_value_text16be(pVal, 0), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } static void test_function_utf16le( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ Tcl_Interp *interp; Tcl_Obj *pX; sqlite4_value *pVal; interp = (Tcl_Interp *)sqlite4_context_appdata(pCtx); pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16LE", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj(sqlite4_value_text(argv[0], 0), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text(pCtx, sqlite4_value_text(pVal, 0), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } static void test_function_utf16be( sqlite4_context *pCtx, int nArg, sqlite4_value **argv ){ Tcl_Interp *interp; Tcl_Obj *pX; sqlite4_value *pVal; interp = (Tcl_Interp *)sqlite4_context_appdata(pCtx); pX = Tcl_NewStringObj("test_function", -1); Tcl_IncrRefCount(pX); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj("UTF-16BE", -1)); Tcl_ListObjAppendElement(interp, pX, Tcl_NewStringObj(sqlite4_value_text(argv[0], 0), -1)); Tcl_EvalObjEx(interp, pX, 0); Tcl_DecrRefCount(pX); pVal = sqlite4ValueNew(0); sqlite4ValueSetStr(pVal, -1, Tcl_GetStringResult(interp), SQLITE4_UTF8, SQLITE4_STATIC, 0); sqlite4_result_text16(pCtx, sqlite4_value_text16le(pVal, 0), -1, SQLITE4_TRANSIENT, 0); sqlite4_result_text16be(pCtx, sqlite4_value_text16le(pVal, 0), -1, SQLITE4_TRANSIENT, 0); sqlite4_result_text16le(pCtx, sqlite4_value_text16le(pVal, 0), -1, SQLITE4_TRANSIENT, 0); sqlite4ValueFree(pVal); } #endif /* SQLITE4_OMIT_UTF16 */ static int test_function( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifndef SQLITE4_OMIT_UTF16 sqlite4 *db; int val; if( objc!=5 ) goto bad_args; if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR; if( val ){ sqlite4_create_function(db, "test_function", 1, interp, test_function_utf8, 0, 0, 0); } if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[3], &val) ) return TCL_ERROR; if( val ){ sqlite4_create_function(db, "test_function", 1, interp, test_function_utf16le, 0, 0, 0); } if( TCL_OK!=Tcl_GetBooleanFromObj(interp, objv[4], &val) ) return TCL_ERROR; if( val ){ sqlite4_create_function(db, "test_function", 1, interp, test_function_utf16be, 0, 0, 0); } return TCL_OK; bad_args: Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " ", 0); #endif /* SQLITE4_OMIT_UTF16 */ return TCL_ERROR; } /* ** Usage: sqlite4_test_errstr ** ** Test that the english language string equivalents for sqlite error codes ** are sane. The parameter is an integer representing an sqlite error code. ** The result is a list of two elements, the string representation of the ** error code and the english language explanation. */ static int test_errstr( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ char *zCode; int i; if( objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); } zCode = Tcl_GetString(objv[1]); for(i=0; i<200; i++){ if( 0==strcmp(t1ErrorName(i), zCode) ) break; } Tcl_SetResult(interp, (char *)sqlite4ErrStr(i), 0); return TCL_OK; } /* ** Usage: breakpoint ** ** This routine exists for one purpose - to provide a place to put a ** breakpoint with GDB that can be triggered using TCL code. The use ** for this is when a particular test fails on (say) the 1485th iteration. ** In the TCL test script, we can add code like this: ** ** if {$i==1485} breakpoint ** ** Then run testfixture in the debugger and wait for the breakpoint to ** fire. Then additional breakpoints can be set to trace down the bug. */ static int test_breakpoint( void *NotUsed, Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int argc, /* Number of arguments */ char **argv /* Text of each argument */ ){ return TCL_OK; /* Do nothing */ } /* ** Usage: sqlite4_bind_int STMT N VALUE ** ** Test the sqlite4_bind_int interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 32-bit integer VALUE to that wildcard. */ static int test_bind_int( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; int value; int rc; if( objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; rc = sqlite4_bind_int(pStmt, idx, value); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_int64 STMT N VALUE ** ** Test the sqlite4_bind_int64 interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 64-bit integer VALUE to that wildcard. */ static int test_bind_int64( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; i64 value; int rc; if( objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; if( Tcl_GetWideIntFromObj(interp, objv[3], &value) ) return TCL_ERROR; rc = sqlite4_bind_int64(pStmt, idx, value); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_double STMT N VALUE ** ** Test the sqlite4_bind_double interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a 64-bit integer VALUE to that wildcard. */ static int test_bind_double( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; double value; int rc; const char *zVal; int i; static const struct { const char *zName; /* Name of the special floating point value */ unsigned int iUpper; /* Upper 32 bits */ unsigned int iLower; /* Lower 32 bits */ } aSpecialFp[] = { { "NaN", 0x7fffffff, 0xffffffff }, { "SNaN", 0x7ff7ffff, 0xffffffff }, { "-NaN", 0xffffffff, 0xffffffff }, { "-SNaN", 0xfff7ffff, 0xffffffff }, { "+Inf", 0x7ff00000, 0x00000000 }, { "-Inf", 0xfff00000, 0x00000000 }, { "Epsilon", 0x00000000, 0x00000001 }, { "-Epsilon", 0x80000000, 0x00000001 }, { "NaN0", 0x7ff80000, 0x00000000 }, { "-NaN0", 0xfff80000, 0x00000000 }, }; if( objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; /* Intercept the string "NaN" and generate a NaN value for it. ** All other strings are passed through to Tcl_GetDoubleFromObj(). ** Tcl_GetDoubleFromObj() should understand "NaN" but some versions ** contain a bug. */ zVal = Tcl_GetString(objv[3]); for(i=0; i=sizeof(aSpecialFp)/sizeof(aSpecialFp[0]) && Tcl_GetDoubleFromObj(interp, objv[3], &value) ){ return TCL_ERROR; } rc = sqlite4_bind_double(pStmt, idx, value); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_null STMT N ** ** Test the sqlite4_bind_null interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a NULL to the wildcard. */ static int test_bind_null( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; int rc; if( objc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; rc = sqlite4_bind_null(pStmt, idx); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_text STMT N STRING BYTES ** ** Test the sqlite4_bind_text interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a UTF-8 string STRING to the wildcard. The string is BYTES bytes ** long. */ static int test_bind_text( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; int bytes; char *value; int rc; if( objc!=5 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE BYTES", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(objv[3], &bytes); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; rc = sqlite4_bind_text(pStmt, idx, value, bytes, SQLITE4_TRANSIENT, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp, sqlite4TestErrorName(rc), 0); return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_text16 ?-static? STMT N STRING BYTES ** ** Test the sqlite4_bind_text16 interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a UTF-16 string STRING to the wildcard. The string is BYTES bytes ** long. */ static int test_bind_text16( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ #ifndef SQLITE4_OMIT_UTF16 sqlite4_stmt *pStmt; int idx; int bytes; char *value; int rc; void (*xDel)() = (objc==6?SQLITE4_STATIC:SQLITE4_TRANSIENT); Tcl_Obj *oStmt = objv[objc-4]; Tcl_Obj *oN = objv[objc-3]; Tcl_Obj *oString = objv[objc-2]; Tcl_Obj *oBytes = objv[objc-1]; if( objc!=5 && objc!=6){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N VALUE BYTES", 0); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(oStmt), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, oN, &idx) ) return TCL_ERROR; value = (char*)Tcl_GetByteArrayFromObj(oString, 0); if( Tcl_GetIntFromObj(interp, oBytes, &bytes) ) return TCL_ERROR; rc = sqlite4_bind_text16(pStmt, idx, (void *)value, bytes, xDel, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ Tcl_AppendResult(interp, sqlite4TestErrorName(rc), 0); return TCL_ERROR; } #endif /* SQLITE4_OMIT_UTF16 */ return TCL_OK; } /* ** Usage: sqlite4_bind_blob ?-static? STMT N DATA BYTES ** ** Test the sqlite4_bind_blob interface. STMT is a prepared statement. ** N is the index of a wildcard in the prepared statement. This command ** binds a BLOB to the wildcard. The BLOB is BYTES bytes in size. */ static int test_bind_blob( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int idx; int bytes; char *value; int rc; sqlite4_destructor_type xDestructor = SQLITE4_TRANSIENT; if( objc!=5 && objc!=6 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " STMT N DATA BYTES", 0); return TCL_ERROR; } if( objc==6 ){ xDestructor = SQLITE4_STATIC; objv++; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &idx) ) return TCL_ERROR; value = Tcl_GetString(objv[3]); if( Tcl_GetIntFromObj(interp, objv[4], &bytes) ) return TCL_ERROR; rc = sqlite4_bind_blob(pStmt, idx, value, bytes, xDestructor, 0); if( sqlite4TestErrCode(interp, StmtToDb(pStmt), rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_bind_parameter_count STMT ** ** Return the number of wildcards in the given statement. */ static int test_bind_parameter_count( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_bind_parameter_count(pStmt))); return TCL_OK; } /* ** Usage: sqlite4_bind_parameter_name STMT N ** ** Return the name of the Nth wildcard. The first wildcard is 1. ** An empty string is returned if N is out of range or if the wildcard ** is nameless. */ static int test_bind_parameter_name( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; int i; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT N"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[2], &i) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite4_bind_parameter_name(pStmt,i),-1) ); return TCL_OK; } /* ** Usage: sqlite4_bind_parameter_index STMT NAME ** ** Return the index of the wildcard called NAME. Return 0 if there is ** no such wildcard. */ static int test_bind_parameter_index( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT NAME"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewIntObj( sqlite4_bind_parameter_index(pStmt,Tcl_GetString(objv[2])) ) ); return TCL_OK; } /* ** Usage: sqlite4_clear_bindings STMT ** */ static int test_clear_bindings( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_stmt *pStmt; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_clear_bindings(pStmt))); return TCL_OK; } /* ** Usage: sqlite4_sleep MILLISECONDS */ static int test_sleep( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int ms; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "MILLISECONDS"); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[1], &ms) ){ return TCL_ERROR; } Tcl_SetObjResult(interp, Tcl_NewIntObj(sqlite4_sleep(ms))); return TCL_OK; } /* ** Usage: sqlite4_errcode DB ** ** Return the string representation of the most recent sqlite4_* API ** error code. e.g. "SQLITE4_ERROR". */ static int test_errcode( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite4_errcode(db); Tcl_AppendResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } /* ** Usage: sqlite4_errmsg DB ** ** Returns the UTF-8 representation of the error message string for the ** most recent sqlite4_* API call. */ static int test_errmsg( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; const char *zErr; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zErr = sqlite4_errmsg(db); Tcl_SetObjResult(interp, Tcl_NewStringObj(zErr, -1)); return TCL_OK; } /* ** Usage: sqlite4_prepare DB sql bytes ?tailvar? ** ** Compile up to bytes of the supplied SQL string using ** database handle . The parameter is the name of a global ** variable that is set to the unused portion of (if any). A ** STMT handle is returned. */ static int test_prepare( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; const char *zSql; int bytes; int nUsed; sqlite4_stmt *pStmt = 0; char zBuf[50]; int rc; if( objc!=5 && objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB sql bytes ?tailvar?", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zSql = Tcl_GetString(objv[2]); if( Tcl_GetIntFromObj(interp, objv[3], &bytes) ) return TCL_ERROR; rc = sqlite4_prepare(db, zSql, bytes, &pStmt, objc>=5 ? &nUsed : 0); Tcl_ResetResult(interp); if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; if( objc>=5 ){ const char *zTail = &zSql[nUsed]; int nTail = -1; if( bytes>=0 ){ nTail = (bytes - nUsed); if( nTail>strlen(zTail) ) nTail = strlen(zTail); } Tcl_ObjSetVar2(interp, objv[4], 0, Tcl_NewStringObj(zTail, nTail), 0); } if( rc!=SQLITE4_OK ){ assert( pStmt==0 ); sprintf(zBuf, "(%d) ", rc); Tcl_AppendResult(interp, zBuf, sqlite4_errmsg(db), 0); return TCL_ERROR; } if( pStmt ){ if( sqlite4TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } /* ** Usage: sqlite4_prepare_tkt3134 DB ** ** Generate a prepared statement for a zero-byte string as a test ** for ticket #3134. The string should be preceeded by a zero byte. */ static int test_prepare_tkt3134( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; static const char zSql[] = "\000SELECT 1"; sqlite4_stmt *pStmt = 0; char zBuf[50]; int rc; if( objc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " DB sql bytes tailvar", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite4_prepare(db, &zSql[1], 0, &pStmt, 0); assert(rc==SQLITE4_OK || pStmt==0); if( sqlite4TestErrCode(interp, db, rc) ) return TCL_ERROR; if( rc!=SQLITE4_OK ){ assert( pStmt==0 ); sprintf(zBuf, "(%d) ", rc); Tcl_AppendResult(interp, zBuf, sqlite4_errmsg(db), 0); return TCL_ERROR; } if( pStmt ){ if( sqlite4TestMakePointerStr(interp, zBuf, pStmt) ) return TCL_ERROR; Tcl_AppendResult(interp, zBuf, 0); } return TCL_OK; } /* ** Usage: sqlite4_open filename ?options-list? */ static int test_open( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zFilename; sqlite4 *db; int rc; char zBuf[100]; if( objc!=3 && objc!=2 && objc!=1 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetString(objv[0]), " filename options-list", 0); return TCL_ERROR; } zFilename = objc>1 ? Tcl_GetString(objv[1]) : 0; rc = sqlite4_open(0, zFilename, &db, 0); if( rc!=SQLITE4_OK || sqlite4TestMakePointerStr(interp, zBuf, db) ){ return TCL_ERROR; } Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: sqlite4_open_v2 FILENAME FLAGS VFS */ static int test_open_v2( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ const char *zFilename; const char *zVfs; int flags = 0; sqlite4 *db; int rc; char zBuf[100]; int nFlag; Tcl_Obj **apFlag; int i; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME FLAGS VFS"); return TCL_ERROR; } zFilename = Tcl_GetString(objv[1]); zVfs = Tcl_GetString(objv[3]); if( zVfs[0]==0x00 ) zVfs = 0; rc = Tcl_ListObjGetElements(interp, objv[2], &nFlag, &apFlag); if( rc!=TCL_OK ) return rc; for(i=0; imagic value. This is used to test error recovery logic. */ static int sqlite_set_magic( void * clientData, Tcl_Interp *interp, int argc, char **argv ){ sqlite4 *db; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB MAGIC", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; if( strcmp(argv[2], "SQLITE4_MAGIC_OPEN")==0 ){ db->magic = SQLITE4_MAGIC_OPEN; }else if( strcmp(argv[2], "SQLITE4_MAGIC_CLOSED")==0 ){ db->magic = SQLITE4_MAGIC_CLOSED; }else if( strcmp(argv[2], "SQLITE4_MAGIC_BUSY")==0 ){ db->magic = SQLITE4_MAGIC_BUSY; }else if( strcmp(argv[2], "SQLITE4_MAGIC_ERROR")==0 ){ db->magic = SQLITE4_MAGIC_ERROR; }else if( Tcl_GetInt(interp, argv[2], (int*)&db->magic) ){ return TCL_ERROR; } return TCL_OK; } /* ** Usage: sqlite4_interrupt DB ** ** Trigger an interrupt on DB */ static int test_interrupt( void * clientData, Tcl_Interp *interp, int argc, char **argv ){ sqlite4 *db; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; sqlite4_interrupt(db); return TCL_OK; } static u8 *sqlite4_stack_baseline = 0; /* ** Fill the stack with a known bitpattern. */ static void prepStack(void){ int i; u32 bigBuf[65536]; for(i=0; i=0 && ((u32*)sqlite4_stack_baseline)[-i]==0xdeadbeef; i--){} Tcl_SetObjResult(interp, Tcl_NewIntObj(i*4)); return TCL_OK; } /* ** Usage: sqlite_delete_function DB function-name ** ** Delete the user function 'function-name' from database handle DB. It ** is assumed that the user function was created as UTF8, any number of ** arguments (the way the TCL interface does it). */ static int delete_function( void * clientData, Tcl_Interp *interp, int argc, char **argv ){ int rc; sqlite4 *db; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB function-name", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_function(db, argv[2], -1, 0, 0, 0, 0, 0); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** Usage: sqlite_delete_collation DB collation-name ** ** Delete the collation sequence 'collation-name' from database handle DB. */ static int delete_collation( void * clientData, Tcl_Interp *interp, int argc, char **argv ){ int rc; sqlite4 *db; if( argc!=3 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB function-name", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; rc = sqlite4_create_collation(db, argv[2], 0, 0, 0, 0); Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC); return TCL_OK; } /* ** Usage: sqlite4_db_transaction_status DB ** ** Return true if the database DB currently has an open transaction. ** Return false if not. */ static int db_transaction_status( void * clientData, Tcl_Interp *interp, int argc, char **argv ){ char zBuf[30]; sqlite4 *db; if( argc!=2 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0], " DB", 0); return TCL_ERROR; } if( getDbPointer(interp, argv[1], &db) ) return TCL_ERROR; sprintf(zBuf, "%d", sqlite4_db_transaction_status(db)); Tcl_AppendResult(interp, zBuf, 0); return TCL_OK; } /* ** Usage: tcl_variable_type VARIABLENAME ** ** Return the name of the internal representation for the ** value of the given variable. */ static int tcl_variable_type( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_Obj *pVar; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "VARIABLE"); return TCL_ERROR; } pVar = Tcl_GetVar2Ex(interp, Tcl_GetString(objv[1]), 0, TCL_LEAVE_ERR_MSG); if( pVar==0 ) return TCL_ERROR; if( pVar->typePtr ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(pVar->typePtr->name, -1)); } return TCL_OK; } /* ** Usage: sqlite4_db_release_memory DB ** ** Attempt to release memory currently held by database DB. Return the ** result code (which in the current implementation is always zero). */ static int test_db_release_memory( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4 *db; int rc; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; rc = sqlite4_db_release_memory(db); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } /* ** tclcmd: working_64bit_int ** ** Some TCL builds (ex: cygwin) do not support 64-bit integers. This ** leads to a number of test failures. The present command checks the ** TCL build to see whether or not it supports 64-bit integers. It ** returns TRUE if it does and FALSE if not. ** ** This command is used to warn users that their TCL build is defective ** and that the errors they are seeing in the test scripts might be ** a result of their defective TCL rather than problems in SQLite. */ static int working_64bit_int( ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ Tcl_Obj *pTestObj; int working = 0; pTestObj = Tcl_NewWideIntObj(1000000*(i64)1234567890); working = strcmp(Tcl_GetString(pTestObj), "1234567890000000")==0; Tcl_DecrRefCount(pTestObj); Tcl_SetObjResult(interp, Tcl_NewBooleanObj(working)); return TCL_OK; } /* ** tclcmd: sqlite4_limit DB ID VALUE ** ** This TCL command runs the sqlite4_limit interface and ** verifies correct operation of the same. */ static int test_limit( ClientData clientData, /* Pointer to sqlite4_enable_XXX function */ Tcl_Interp *interp, /* The TCL interpreter that invoked this command */ int objc, /* Number of arguments */ Tcl_Obj *CONST objv[] /* Command arguments */ ){ sqlite4 *db; int rc; static const struct { char *zName; int id; } aId[] = { { "SQLITE4_LIMIT_LENGTH", SQLITE4_LIMIT_LENGTH }, { "SQLITE4_LIMIT_SQL_LENGTH", SQLITE4_LIMIT_SQL_LENGTH }, { "SQLITE4_LIMIT_COLUMN", SQLITE4_LIMIT_COLUMN }, { "SQLITE4_LIMIT_EXPR_DEPTH", SQLITE4_LIMIT_EXPR_DEPTH }, { "SQLITE4_LIMIT_COMPOUND_SELECT", SQLITE4_LIMIT_COMPOUND_SELECT }, { "SQLITE4_LIMIT_VDBE_OP", SQLITE4_LIMIT_VDBE_OP }, { "SQLITE4_LIMIT_FUNCTION_ARG", SQLITE4_LIMIT_FUNCTION_ARG }, { "SQLITE4_LIMIT_ATTACHED", SQLITE4_LIMIT_ATTACHED }, { "SQLITE4_LIMIT_LIKE_PATTERN_LENGTH", SQLITE4_LIMIT_LIKE_PATTERN_LENGTH }, { "SQLITE4_LIMIT_VARIABLE_NUMBER", SQLITE4_LIMIT_VARIABLE_NUMBER }, { "SQLITE4_LIMIT_TRIGGER_DEPTH", SQLITE4_LIMIT_TRIGGER_DEPTH }, /* Out of range test cases */ { "SQLITE4_LIMIT_TOOSMALL", -1, }, { "SQLITE4_LIMIT_TOOBIG", SQLITE4_LIMIT_TRIGGER_DEPTH+1 }, }; int i, id; int val; const char *zId; if( objc!=4 ){ Tcl_AppendResult(interp, "wrong # args: should be \"", Tcl_GetStringFromObj(objv[0], 0), " DB ID VALUE", 0); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; zId = Tcl_GetString(objv[2]); for(i=0; i=sizeof(aId)/sizeof(aId[0]) ){ Tcl_AppendResult(interp, "unknown limit type: ", zId, (char*)0); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[3], &val) ) return TCL_ERROR; rc = sqlite4_limit(db, id, val); Tcl_SetObjResult(interp, Tcl_NewIntObj(rc)); return TCL_OK; } #ifdef SQLITE4_ENABLE_UNLOCK_NOTIFY static void test_unlock_notify_cb(void **aArg, int nArg){ int ii; for(ii=0; ii2 ){ Tcl_WrongNumArgs(interp, 1, objv, "SCRIPT"); return TCL_ERROR; } if( logcallback.pObj ){ Tcl_DecrRefCount(logcallback.pObj); logcallback.pObj = 0; logcallback.pInterp = 0; sqlite4_env_config(0, SQLITE4_ENVCONFIG_LOG, 0, 0); } if( objc>1 ){ logcallback.pObj = objv[1]; Tcl_IncrRefCount(logcallback.pObj); logcallback.pInterp = interp; sqlite4_env_config(0, SQLITE4_ENVCONFIG_LOG, xLogcallback, 0); } return TCL_OK; } /* ** tcl_objproc COMMANDNAME ARGS... ** ** Run a TCL command using its objProc interface. Throw an error if ** the command has no objProc interface. */ static int runAsObjProc( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ Tcl_CmdInfo cmdInfo; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "COMMAND ..."); return TCL_ERROR; } if( !Tcl_GetCommandInfo(interp, Tcl_GetString(objv[1]), &cmdInfo) ){ Tcl_AppendResult(interp, "command not found: ", Tcl_GetString(objv[1]), (char*)0); return TCL_ERROR; } if( cmdInfo.objProc==0 ){ Tcl_AppendResult(interp, "command has no objProc: ", Tcl_GetString(objv[1]), (char*)0); return TCL_ERROR; } return cmdInfo.objProc(cmdInfo.objClientData, interp, objc-1, objv+1); } #ifndef SQLITE4_OMIT_EXPLAIN /* ** WARNING: The following function, printExplainQueryPlan() is an exact ** copy of example code from eqp.in (eqp.html). If this code is modified, ** then the documentation copy needs to be modified as well. */ /* ** Argument pStmt is a prepared SQL statement. This function compiles ** an EXPLAIN QUERY PLAN command to report on the prepared statement, ** and prints the report to stdout using printf(). */ int printExplainQueryPlan(sqlite4_stmt *pStmt){ const char *zSql; /* Input SQL */ char *zExplain; /* SQL with EXPLAIN QUERY PLAN prepended */ sqlite4_stmt *pExplain; /* Compiled EXPLAIN QUERY PLAN command */ int rc; /* Return code from sqlite4_prepare() */ zSql = sqlite4_stmt_sql(pStmt); if( zSql==0 ) return SQLITE4_ERROR; zExplain = sqlite4_mprintf(0, "EXPLAIN QUERY PLAN %s", zSql); if( zExplain==0 ) return SQLITE4_NOMEM; rc = sqlite4_prepare(sqlite4_db_handle(pStmt), zExplain, -1, &pExplain, 0); sqlite4_free(0, zExplain); if( rc!=SQLITE4_OK ) return rc; while( SQLITE4_ROW==sqlite4_step(pExplain) ){ int iSelectid = sqlite4_column_int(pExplain, 0); int iOrder = sqlite4_column_int(pExplain, 1); int iFrom = sqlite4_column_int(pExplain, 2); const char *zDetail = (const char *)sqlite4_column_text(pExplain, 3, 0); printf("%d %d %d %s\n", iSelectid, iOrder, iFrom, zDetail); } return sqlite4_finalize(pExplain); } static int test_print_eqp( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int rc; sqlite4_stmt *pStmt; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "STMT"); return TCL_ERROR; } if( getStmtPointer(interp, Tcl_GetString(objv[1]), &pStmt) ) return TCL_ERROR; rc = printExplainQueryPlan(pStmt); /* This is needed on Windows so that a test case using this ** function can open a read pipe and get the output of ** printExplainQueryPlan() immediately. */ fflush(stdout); Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } #endif /* SQLITE4_OMIT_EXPLAIN */ static int test_prng_state_get( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_int64 iVal; if( objc!=0 ){ Tcl_WrongNumArgs(interp, 1, objv, ""); return TCL_ERROR; } sqlite4_test_control(SQLITE4_TESTCTRL_PRNG_SET, (sqlite4_env *)0, &iVal); Tcl_SetObjResult(interp, Tcl_NewWideIntObj(iVal)); return TCL_OK; } static int test_prng_state_set( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ sqlite4_int64 iVal; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "VALUE"); return TCL_ERROR; } if( Tcl_GetWideIntFromObj(interp, objv[1], &iVal) ){ return TCL_ERROR; } sqlite4_test_control(SQLITE4_TESTCTRL_PRNG_SET, (sqlite4_env *)0, iVal); return TCL_OK; } /* ** sqlite4_test_control VERB ARGS... */ static int test_test_control( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ struct Verb { const char *zName; int i; } aVerb[] = { { "SQLITE4_TESTCTRL_LOCALTIME_FAULT", SQLITE4_TESTCTRL_LOCALTIME_FAULT }, }; int iVerb; int iFlag; int rc; if( objc<2 ){ Tcl_WrongNumArgs(interp, 1, objv, "VERB ARGS..."); return TCL_ERROR; } rc = Tcl_GetIndexFromObjStruct( interp, objv[1], aVerb, sizeof(aVerb[0]), "VERB", 0, &iVerb ); if( rc!=TCL_OK ) return rc; iFlag = aVerb[iVerb].i; switch( iFlag ){ case SQLITE4_TESTCTRL_LOCALTIME_FAULT: { int val; if( objc!=3 ){ Tcl_WrongNumArgs(interp, 2, objv, "ONOFF"); return TCL_ERROR; } if( Tcl_GetBooleanFromObj(interp, objv[2], &val) ) return TCL_ERROR; sqlite4_test_control(SQLITE4_TESTCTRL_LOCALTIME_FAULT, val); break; } } Tcl_ResetResult(interp); return TCL_OK; } #if SQLITE4_OS_WIN /* ** Information passed from the main thread into the windows file locker ** background thread. */ struct win32FileLocker { char *evName; /* Name of event to signal thread startup */ HANDLE h; /* Handle of the file to be locked */ int delay1; /* Delay before locking */ int delay2; /* Delay before unlocking */ int ok; /* Finished ok */ int err; /* True if an error occurs */ }; #endif #if SQLITE4_OS_WIN /* ** The background thread that does file locking. */ static void win32_file_locker(void *pAppData){ struct win32FileLocker *p = (struct win32FileLocker*)pAppData; if( p->evName ){ HANDLE ev = OpenEvent(EVENT_MODIFY_STATE, FALSE, p->evName); if ( ev ){ SetEvent(ev); CloseHandle(ev); } } if( p->delay1 ) Sleep(p->delay1); if( LockFile(p->h, 0, 0, 100000000, 0) ){ Sleep(p->delay2); UnlockFile(p->h, 0, 0, 100000000, 0); p->ok = 1; }else{ p->err = 1; } CloseHandle(p->h); p->h = 0; p->delay1 = 0; p->delay2 = 0; } #endif #if SQLITE4_OS_WIN /* ** lock_win32_file FILENAME DELAY1 DELAY2 ** ** Get an exclusive manditory lock on file for DELAY2 milliseconds. ** Wait DELAY1 milliseconds before acquiring the lock. */ static int win32_file_lock( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ static struct win32FileLocker x = { "win32_file_lock", 0, 0, 0, 0, 0 }; const char *zFilename; char zBuf[200]; int retry = 0; HANDLE ev; DWORD wResult; if( objc!=4 && objc!=1 ){ Tcl_WrongNumArgs(interp, 1, objv, "FILENAME DELAY1 DELAY2"); return TCL_ERROR; } if( objc==1 ){ sqlite4_snprintf(zBuf, sizeof(zBuf), "%d %d %d %d %d", x.ok, x.err, x.delay1, x.delay2, x.h); Tcl_AppendResult(interp, zBuf, (char*)0); return TCL_OK; } while( x.h && retry<30 ){ retry++; Sleep(100); } if( x.h ){ Tcl_AppendResult(interp, "busy", (char*)0); return TCL_ERROR; } if( Tcl_GetIntFromObj(interp, objv[2], &x.delay1) ) return TCL_ERROR; if( Tcl_GetIntFromObj(interp, objv[3], &x.delay2) ) return TCL_ERROR; zFilename = Tcl_GetString(objv[1]); x.h = CreateFile(zFilename, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if( !x.h ){ Tcl_AppendResult(interp, "cannot open file: ", zFilename, (char*)0); return TCL_ERROR; } ev = CreateEvent(NULL, TRUE, FALSE, x.evName); if ( !ev ){ Tcl_AppendResult(interp, "cannot create event: ", x.evName, (char*)0); return TCL_ERROR; } _beginthread(win32_file_locker, 0, (void*)&x); Sleep(0); if ( (wResult = WaitForSingleObject(ev, 10000))!=WAIT_OBJECT_0 ){ sqlite4_snprintf(zBuf, sizeof(zBuf), "0x%x", wResult); Tcl_AppendResult(interp, "wait failed: ", zBuf, (char*)0); CloseHandle(ev); return TCL_ERROR; } CloseHandle(ev); return TCL_OK; } #endif /* ** optimization_control DB OPT BOOLEAN ** ** Enable or disable query optimizations using the sqlite4_test_control() ** interface. Disable if BOOLEAN is false and enable if BOOLEAN is true. ** OPT is the name of the optimization to be disabled. */ static int optimization_control( void * clientData, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ int i; sqlite4 *db; const char *zOpt; int onoff; int mask; static const struct { const char *zOptName; int mask; } aOpt[] = { { "all", SQLITE4_OptMask }, { "query-flattener", SQLITE4_QueryFlattener }, { "column-cache", SQLITE4_ColumnCache }, { "index-sort", SQLITE4_IndexSort }, { "index-search", SQLITE4_IndexSearch }, { "index-cover", SQLITE4_IndexCover }, { "groupby-order", SQLITE4_GroupByOrder }, { "factor-constants", SQLITE4_FactorOutConst }, { "real-as-int", SQLITE4_IdxRealAsInt }, }; if( objc!=4 ){ Tcl_WrongNumArgs(interp, 1, objv, "DB OPT BOOLEAN"); return TCL_ERROR; } if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR; if( Tcl_GetBooleanFromObj(interp, objv[3], &onoff) ) return TCL_ERROR; zOpt = Tcl_GetString(objv[2]); for(i=0; i=sizeof(aOpt)/sizeof(aOpt[0]) ){ Tcl_AppendResult(interp, "unknown optimization - should be one of:", (char*)0); for(i=0; i