Index: VERSION ================================================================== --- VERSION +++ VERSION @@ -1,1 +1,1 @@ -3.7.6.1 +3.7.6.3 Index: configure ================================================================== --- configure +++ configure @@ -1,8 +1,8 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.62 for sqlite 3.7.6.1. +# Generated by GNU Autoconf 2.62 for sqlite 3.7.6.3. # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. @@ -741,12 +741,12 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='sqlite' PACKAGE_TARNAME='sqlite' -PACKAGE_VERSION='3.7.6.1' -PACKAGE_STRING='sqlite 3.7.6.1' +PACKAGE_VERSION='3.7.6.3' +PACKAGE_STRING='sqlite 3.7.6.3' PACKAGE_BUGREPORT='' # Factoring default headers for most tests. ac_includes_default="\ #include @@ -1483,11 +1483,11 @@ # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures sqlite 3.7.6.1 to adapt to many kinds of systems. +\`configure' configures sqlite 3.7.6.3 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. @@ -1548,11 +1548,11 @@ _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of sqlite 3.7.6.1:";; + short | recursive ) echo "Configuration of sqlite 3.7.6.3:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options @@ -1664,11 +1664,11 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -sqlite configure 3.7.6.1 +sqlite configure 3.7.6.3 generated by GNU Autoconf 2.62 Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation @@ -1678,11 +1678,11 @@ fi cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by sqlite $as_me 3.7.6.1, which was +It was created by sqlite $as_me 3.7.6.3, which was generated by GNU Autoconf 2.62. Invocation command line was $ $0 $@ _ACEOF @@ -13940,11 +13940,11 @@ # Save the log message, to keep $[0] and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by sqlite $as_me 3.7.6.1, which was +This file was extended by sqlite $as_me 3.7.6.3, which was generated by GNU Autoconf 2.62. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS @@ -13993,11 +13993,11 @@ Report bugs to ." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_version="\\ -sqlite config.status 3.7.6.1 +sqlite config.status 3.7.6.3 configured by $0, generated by GNU Autoconf 2.62, with options \\"`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" Copyright (C) 2008 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation Index: ext/fts3/fts3_write.c ================================================================== --- ext/fts3/fts3_write.c +++ ext/fts3/fts3_write.c @@ -1168,11 +1168,10 @@ sqlite3_int64 iEndBlock, /* Final block of segment */ const char *zRoot, /* Buffer containing root node */ int nRoot, /* Size of buffer containing root node */ Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ ){ - int rc = SQLITE_OK; /* Return code */ Fts3SegReader *pReader; /* Newly allocated SegReader object */ int nExtra = 0; /* Bytes to allocate segment root node */ assert( iStartLeaf<=iEndLeaf ); if( iStartLeaf==0 ){ @@ -1196,17 +1195,12 @@ memcpy(pReader->aNode, zRoot, nRoot); memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING); }else{ pReader->iCurrentBlock = iStartLeaf-1; } - - if( rc==SQLITE_OK ){ - *ppReader = pReader; - }else{ - sqlite3Fts3SegReaderFree(pReader); - } - return rc; + *ppReader = pReader; + return SQLITE_OK; } /* ** This is a comparison function used as a qsort() callback when sorting ** an array of pending terms by term. This occurs as part of flushing @@ -1236,17 +1230,17 @@ int nTerm, /* Size of buffer zTerm */ int isPrefix, /* True for a term-prefix query */ Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ ){ Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ + Fts3HashElem *pE; /* Iterator variable */ Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ int nElem = 0; /* Size of array at aElem */ int rc = SQLITE_OK; /* Return Code */ if( isPrefix ){ int nAlloc = 0; /* Size of allocated array at aElem */ - Fts3HashElem *pE = 0; /* Iterator variable */ for(pE=fts3HashFirst(&p->pendingTerms); pE; pE=fts3HashNext(pE)){ char *zKey = (char *)fts3HashKey(pE); int nKey = fts3HashKeysize(pE); if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){ @@ -1274,11 +1268,11 @@ if( nElem>1 ){ qsort(aElem, nElem, sizeof(Fts3HashElem *), fts3CompareElemByTerm); } }else{ - Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm); + pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm); if( pE ){ aElem = &pE; nElem = 1; } } Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -1191,11 +1191,11 @@ RtreeMatchArg *p; sqlite3_rtree_geometry *pGeom; int nBlob; /* Check that value is actually a blob. */ - if( !sqlite3_value_type(pValue)==SQLITE_BLOB ) return SQLITE_ERROR; + if( sqlite3_value_type(pValue)!=SQLITE_BLOB ) return SQLITE_ERROR; /* Check that the blob is roughly the right size. */ nBlob = sqlite3_value_bytes(pValue); if( nBlob<(int)sizeof(RtreeMatchArg) || ((nBlob-sizeof(RtreeMatchArg))%sizeof(double))!=0 Index: src/date.c ================================================================== --- src/date.c +++ src/date.c @@ -48,26 +48,10 @@ #include #include #ifndef SQLITE_OMIT_DATETIME_FUNCS -/* -** On recent Windows platforms, the localtime_s() function is available -** as part of the "Secure CRT". It is essentially equivalent to -** localtime_r() available under most POSIX platforms, except that the -** order of the parameters is reversed. -** -** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. -** -** If the user has not indicated to use localtime_r() or localtime_s() -** already, check for an MSVC build environment that provides -** localtime_s(). -*/ -#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ - defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) -#define HAVE_LOCALTIME_S 1 -#endif /* ** A structure for holding a single date and time. */ typedef struct DateTime DateTime; @@ -408,20 +392,85 @@ static void clearYMD_HMS_TZ(DateTime *p){ p->validYMD = 0; p->validHMS = 0; p->validTZ = 0; } + +/* +** On recent Windows platforms, the localtime_s() function is available +** as part of the "Secure CRT". It is essentially equivalent to +** localtime_r() available under most POSIX platforms, except that the +** order of the parameters is reversed. +** +** See http://msdn.microsoft.com/en-us/library/a442x3ye(VS.80).aspx. +** +** If the user has not indicated to use localtime_r() or localtime_s() +** already, check for an MSVC build environment that provides +** localtime_s(). +*/ +#if !defined(HAVE_LOCALTIME_R) && !defined(HAVE_LOCALTIME_S) && \ + defined(_MSC_VER) && defined(_CRT_INSECURE_DEPRECATE) +#define HAVE_LOCALTIME_S 1 +#endif + +#ifndef SQLITE_OMIT_LOCALTIME +/* +** The following routine implements the rough equivalent of localtime_r() +** using whatever operating-system specific localtime facility that +** is available. This routine returns 0 on success and +** non-zero on any kind of error. +** +** If the sqlite3GlobalConfig.bLocaltimeFault variable is true then this +** routine will always fail. +*/ +static int osLocaltime(time_t *t, struct tm *pTm){ + int rc; +#if (!defined(HAVE_LOCALTIME_R) || !HAVE_LOCALTIME_R) \ + && (!defined(HAVE_LOCALTIME_S) || !HAVE_LOCALTIME_S) + struct tm *pX; + sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex_enter(mutex); + pX = localtime(t); +#ifndef SQLITE_OMIT_BUILTIN_TEST + if( sqlite3GlobalConfig.bLocaltimeFault ) pX = 0; +#endif + if( pX ) *pTm = *pX; + sqlite3_mutex_leave(mutex); + rc = pX==0; +#else +#ifndef SQLITE_OMIT_BUILTIN_TEST + if( sqlite3GlobalConfig.bLocaltimeFault ) return 1; +#endif +#if defined(HAVE_LOCALTIME_R) && HAVE_LOCALTIME_R + rc = localtime_r(t, pTm)==0; +#else + rc = localtime_s(pTm, t); +#endif /* HAVE_LOCALTIME_R */ +#endif /* HAVE_LOCALTIME_R || HAVE_LOCALTIME_S */ + return rc; +} +#endif /* SQLITE_OMIT_LOCALTIME */ + #ifndef SQLITE_OMIT_LOCALTIME /* -** Compute the difference (in milliseconds) -** between localtime and UTC (a.k.a. GMT) -** for the time value p where p is in UTC. +** Compute the difference (in milliseconds) between localtime and UTC +** (a.k.a. GMT) for the time value p where p is in UTC. If no error occurs, +** return this value and set *pRc to SQLITE_OK. +** +** Or, if an error does occur, set *pRc to SQLITE_ERROR. The returned value +** is undefined in this case. */ -static sqlite3_int64 localtimeOffset(DateTime *p){ +static sqlite3_int64 localtimeOffset( + DateTime *p, /* Date at which to calculate offset */ + sqlite3_context *pCtx, /* Write error here if one occurs */ + int *pRc /* OUT: Error code. SQLITE_OK or ERROR */ +){ DateTime x, y; time_t t; + struct tm sLocal; + x = *p; computeYMD_HMS(&x); if( x.Y<1971 || x.Y>=2038 ){ x.Y = 2000; x.M = 1; @@ -435,51 +484,27 @@ } x.tz = 0; x.validJD = 0; computeJD(&x); t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); -#ifdef HAVE_LOCALTIME_R - { - struct tm sLocal; - localtime_r(&t, &sLocal); - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - } -#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S - { - struct tm sLocal; - localtime_s(&sLocal, &t); - y.Y = sLocal.tm_year + 1900; - y.M = sLocal.tm_mon + 1; - y.D = sLocal.tm_mday; - y.h = sLocal.tm_hour; - y.m = sLocal.tm_min; - y.s = sLocal.tm_sec; - } -#else - { - struct tm *pTm; - sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - pTm = localtime(&t); - y.Y = pTm->tm_year + 1900; - y.M = pTm->tm_mon + 1; - y.D = pTm->tm_mday; - y.h = pTm->tm_hour; - y.m = pTm->tm_min; - y.s = pTm->tm_sec; - sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); - } -#endif + if( osLocaltime(&t, &sLocal) ){ + sqlite3_result_error(pCtx, "local time unavailable", -1); + *pRc = SQLITE_ERROR; + return 0; + } + y.Y = sLocal.tm_year + 1900; + y.M = sLocal.tm_mon + 1; + y.D = sLocal.tm_mday; + y.h = sLocal.tm_hour; + y.m = sLocal.tm_min; + y.s = sLocal.tm_sec; y.validYMD = 1; y.validHMS = 1; y.validJD = 0; y.validTZ = 0; computeJD(&y); + *pRc = SQLITE_OK; return y.iJD - x.iJD; } #endif /* SQLITE_OMIT_LOCALTIME */ /* @@ -499,13 +524,16 @@ ** weekday N ** unixepoch ** localtime ** utc ** -** Return 0 on success and 1 if there is any kind of error. +** Return 0 on success and 1 if there is any kind of error. If the error +** is in a system call (i.e. localtime()), then an error message is written +** to context pCtx. If the error is an unrecognized modifier, no error is +** written to pCtx. */ -static int parseModifier(const char *zMod, DateTime *p){ +static int parseModifier(sqlite3_context *pCtx, const char *zMod, DateTime *p){ int rc = 1; int n; double r; char *z, zBuf[30]; z = zBuf; @@ -521,13 +549,12 @@ ** Assuming the current time value is UTC (a.k.a. GMT), shift it to ** show local time. */ if( strcmp(z, "localtime")==0 ){ computeJD(p); - p->iJD += localtimeOffset(p); + p->iJD += localtimeOffset(p, pCtx, &rc); clearYMD_HMS_TZ(p); - rc = 0; } break; } #endif case 'u': { @@ -544,15 +571,16 @@ } #ifndef SQLITE_OMIT_LOCALTIME else if( strcmp(z, "utc")==0 ){ sqlite3_int64 c1; computeJD(p); - c1 = localtimeOffset(p); - p->iJD -= c1; - clearYMD_HMS_TZ(p); - p->iJD += c1 - localtimeOffset(p); - rc = 0; + c1 = localtimeOffset(p, pCtx, &rc); + if( rc==SQLITE_OK ){ + p->iJD -= c1; + clearYMD_HMS_TZ(p); + p->iJD += c1 - localtimeOffset(p, pCtx, &rc); + } } #endif break; } case 'w': { @@ -729,13 +757,12 @@ if( !z || parseDateOrTime(context, (char*)z, p) ){ return 1; } } for(i=1; ipBackup); }else{ if( pagerUseWal(pPager) ){ PgHdr *pList = sqlite3PcacheDirtyList(pPager->pPCache); + PgHdr *pPageOne = 0; + if( pList==0 ){ + /* Must have at least one page for the WAL commit flag. + ** Ticket [2d1a5c67dfc2363e44f29d9bbd57f] 2011-05-18 */ + rc = sqlite3PagerGet(pPager, 1, &pPageOne); + pList = pPageOne; + pList->pDirty = 0; + } + assert( pList!=0 || rc!=SQLITE_OK ); if( pList ){ rc = pagerWalFrames(pPager, pList, pPager->dbSize, 1, (pPager->fullSync ? pPager->syncFlags : 0) ); } + sqlite3PagerUnref(pPageOne); if( rc==SQLITE_OK ){ sqlite3PcacheCleanAll(pPager->pPCache); } }else{ /* The following block updates the change-counter. Exactly how it Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -4327,11 +4327,12 @@ ** below the limit, it will exceed the limit rather than generate ** an [SQLITE_NOMEM] error. In other words, the soft heap limit ** is advisory only. ** ** ^The return value from sqlite3_soft_heap_limit64() is the size of -** the soft heap limit prior to the call. ^If the argument N is negative +** the soft heap limit prior to the call, or negative in the case of an +** error. ^If the argument N is negative ** then no change is made to the soft heap limit. Hence, the current ** size of the soft heap limit can be determined by invoking ** sqlite3_soft_heap_limit64() with a negative argument. ** ** ^If the argument N is zero then the soft heap limit is disabled. @@ -5408,11 +5409,12 @@ #define SQLITE_TESTCTRL_RESERVE 14 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15 #define SQLITE_TESTCTRL_ISKEYWORD 16 #define SQLITE_TESTCTRL_PGHDRSZ 17 #define SQLITE_TESTCTRL_SCRATCHMALLOC 18 -#define SQLITE_TESTCTRL_LAST 18 +#define SQLITE_TESTCTRL_LOCALTIME_FAULT 19 +#define SQLITE_TESTCTRL_LAST 19 /* ** CAPI3REF: SQLite Runtime Status ** ** ^This interface is used to retrieve runtime status information Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -2446,10 +2446,11 @@ int isPCacheInit; /* True after malloc is initialized */ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */ int nRefInitMutex; /* Number of users of pInitMutex */ void (*xLog)(void*,int,const char*); /* Function for logging */ void *pLogArg; /* First argument to xLog() */ + int bLocaltimeFault; /* True to fail localtime() calls */ }; /* ** Context pointer passed down through the tree-walk. */ Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -5419,10 +5419,58 @@ rc = printExplainQueryPlan(pStmt); Tcl_SetResult(interp, (char *)t1ErrorName(rc), 0); return TCL_OK; } #endif /* SQLITE_OMIT_EXPLAIN */ + +/* +** sqlite3_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[] = { + { "SQLITE_TESTCTRL_LOCALTIME_FAULT", SQLITE_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 SQLITE_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; + sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, val); + break; + } + } + + Tcl_ResetResult(interp); + return TCL_OK; +} + /* ** optimization_control DB OPT BOOLEAN ** ** Enable or disable query optimizations using the sqlite3_test_control() @@ -5681,10 +5729,11 @@ { "sqlite3_wal_checkpoint_v2",test_wal_checkpoint_v2, 0 }, { "test_sqlite3_log", test_sqlite3_log, 0 }, #ifndef SQLITE_OMIT_EXPLAIN { "print_explain_query_plan", test_print_eqp, 0 }, #endif + { "sqlite3_test_control", test_test_control }, }; static int bitmask_size = sizeof(Bitmask)*8; int i; extern int sqlite3_sync_count, sqlite3_fullsync_count; extern int sqlite3_opentemp_count; Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -8,14 +8,10 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** -*/ -#if SQLITE_TEST /* This file is used for testing only */ - -/* ** This file contains the implementation of the Tcl [testvfs] command, ** used to create SQLite VFS implementations with various properties and ** instrumentation to support testing SQLite. ** ** testvfs VFSNAME ?OPTIONS? @@ -26,10 +22,11 @@ ** -default BOOLEAN (True to make the vfs default. Default false) ** -szosfile INTEGER (Value for sqlite3_vfs.szOsFile) ** -mxpathname INTEGER (Value for sqlite3_vfs.mxPathname) ** -iversion INTEGER (Value for sqlite3_vfs.iVersion) */ +#if SQLITE_TEST /* This file is used for testing only */ #include "sqlite3.h" #include "sqliteInt.h" typedef struct Testvfs Testvfs; @@ -80,12 +77,10 @@ char *zName; /* Name of this VFS */ sqlite3_vfs *pParent; /* The VFS to use for file IO */ sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */ Tcl_Interp *interp; /* Interpreter to run script in */ Tcl_Obj *pScript; /* Script to execute */ - int nScript; /* Number of elements in array apScript */ - Tcl_Obj **apScript; /* Array version of pScript */ TestvfsBuffer *pBuffer; /* List of shared buffers */ int isNoshm; int mask; /* Mask controlling [script] and [ioerr] */ @@ -112,24 +107,27 @@ ** corresponding VFS method is ignored for purposes of: ** ** + Simulating IO errors, and ** + Invoking the Tcl callback script. */ -#define TESTVFS_SHMOPEN_MASK 0x00000001 -#define TESTVFS_SHMLOCK_MASK 0x00000010 -#define TESTVFS_SHMMAP_MASK 0x00000020 -#define TESTVFS_SHMBARRIER_MASK 0x00000040 -#define TESTVFS_SHMCLOSE_MASK 0x00000080 - -#define TESTVFS_OPEN_MASK 0x00000100 -#define TESTVFS_SYNC_MASK 0x00000200 -#define TESTVFS_DELETE_MASK 0x00000400 -#define TESTVFS_CLOSE_MASK 0x00000800 -#define TESTVFS_WRITE_MASK 0x00001000 -#define TESTVFS_TRUNCATE_MASK 0x00002000 -#define TESTVFS_ACCESS_MASK 0x00004000 -#define TESTVFS_ALL_MASK 0x00007FFF +#define TESTVFS_SHMOPEN_MASK 0x00000001 +#define TESTVFS_SHMLOCK_MASK 0x00000010 +#define TESTVFS_SHMMAP_MASK 0x00000020 +#define TESTVFS_SHMBARRIER_MASK 0x00000040 +#define TESTVFS_SHMCLOSE_MASK 0x00000080 + +#define TESTVFS_OPEN_MASK 0x00000100 +#define TESTVFS_SYNC_MASK 0x00000200 +#define TESTVFS_DELETE_MASK 0x00000400 +#define TESTVFS_CLOSE_MASK 0x00000800 +#define TESTVFS_WRITE_MASK 0x00001000 +#define TESTVFS_TRUNCATE_MASK 0x00002000 +#define TESTVFS_ACCESS_MASK 0x00004000 +#define TESTVFS_FULLPATHNAME_MASK 0x00008000 +#define TESTVFS_READ_MASK 0x00010000 + +#define TESTVFS_ALL_MASK 0x0001FFFF #define TESTVFS_MAX_PAGES 1024 /* @@ -267,52 +265,30 @@ Tcl_Obj *arg1, Tcl_Obj *arg2, Tcl_Obj *arg3 ){ int rc; /* Return code from Tcl_EvalObj() */ - int nArg; /* Elements in eval'd list */ - int nScript; - Tcl_Obj ** ap; - + Tcl_Obj *pEval; assert( p->pScript ); - if( !p->apScript ){ - int nByte; - int i; - if( TCL_OK!=Tcl_ListObjGetElements(p->interp, p->pScript, &nScript, &ap) ){ - Tcl_BackgroundError(p->interp); - Tcl_ResetResult(p->interp); - return; - } - p->nScript = nScript; - nByte = (nScript+TESTVFS_MAX_ARGS)*sizeof(Tcl_Obj *); - p->apScript = (Tcl_Obj **)ckalloc(nByte); - memset(p->apScript, 0, nByte); - for(i=0; iapScript[i] = ap[i]; - } - } - - p->apScript[p->nScript] = Tcl_NewStringObj(zMethod, -1); - p->apScript[p->nScript+1] = arg1; - p->apScript[p->nScript+2] = arg2; - p->apScript[p->nScript+3] = arg3; - - for(nArg=p->nScript; p->apScript[nArg]; nArg++){ - Tcl_IncrRefCount(p->apScript[nArg]); - } - - rc = Tcl_EvalObjv(p->interp, nArg, p->apScript, TCL_EVAL_GLOBAL); + assert( zMethod ); + assert( p ); + assert( arg2==0 || arg1!=0 ); + assert( arg3==0 || arg2!=0 ); + + pEval = Tcl_DuplicateObj(p->pScript); + Tcl_IncrRefCount(p->pScript); + Tcl_ListObjAppendElement(p->interp, pEval, Tcl_NewStringObj(zMethod, -1)); + if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1); + if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2); + if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3); + + rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); if( rc!=TCL_OK ){ Tcl_BackgroundError(p->interp); Tcl_ResetResult(p->interp); } - - for(nArg=p->nScript; p->apScript[nArg]; nArg++){ - Tcl_DecrRefCount(p->apScript[nArg]); - p->apScript[nArg] = 0; - } } /* ** Close an tvfs-file. @@ -349,12 +325,26 @@ sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ - TestvfsFd *p = tvfsGetFd(pFile); - return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); + int rc = SQLITE_OK; + TestvfsFd *pFd = tvfsGetFd(pFile); + Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; + if( p->pScript && p->mask&TESTVFS_READ_MASK ){ + tvfsExecTcl(p, "xRead", + Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 + ); + tvfsResultCode(p, &rc); + } + if( rc==SQLITE_OK && p->mask&TESTVFS_READ_MASK && tvfsInjectIoerr(p) ){ + rc = SQLITE_IOERR; + } + if( rc==SQLITE_OK ){ + rc = sqlite3OsRead(pFd->pReal, zBuf, iAmt, iOfst); + } + return rc; } /* ** Write data to an tvfs-file. */ @@ -543,20 +533,32 @@ memset(pTestfile, 0, sizeof(TestvfsFile)); pTestfile->pFd = pFd; /* Evaluate the Tcl script: ** - ** SCRIPT xOpen FILENAME + ** SCRIPT xOpen FILENAME KEY-VALUE-ARGS ** ** If the script returns an SQLite error code other than SQLITE_OK, an ** error is returned to the caller. If it returns SQLITE_OK, the new ** connection is named "anon". Otherwise, the value returned by the ** script is used as the connection name. */ Tcl_ResetResult(p->interp); if( p->pScript && p->mask&TESTVFS_OPEN_MASK ){ - tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); + Tcl_Obj *pArg = Tcl_NewObj(); + Tcl_IncrRefCount(pArg); + if( flags&SQLITE_OPEN_MAIN_DB ){ + const char *z = &zName[strlen(zName)+1]; + while( *z ){ + Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); + z += strlen(z) + 1; + Tcl_ListObjAppendElement(0, pArg, Tcl_NewStringObj(z, -1)); + z += strlen(z) + 1; + } + } + tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0); + Tcl_DecrRefCount(pArg); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; }else{ pId = Tcl_GetObjResult(p->interp); } @@ -661,10 +663,18 @@ sqlite3_vfs *pVfs, const char *zPath, int nOut, char *zOut ){ + Testvfs *p = (Testvfs *)pVfs->pAppData; + if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ + int rc; + tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0); + if( tvfsResultCode(p, &rc) ){ + if( rc!=SQLITE_OK ) return rc; + } + } return sqlite3OsFullPathname(PARENTVFS(pVfs), zPath, nOut, zOut); } #ifndef SQLITE_OMIT_LOAD_EXTENSION /* @@ -976,22 +986,28 @@ Tcl_ResetResult(interp); switch( aSubcmd[i].eCmd ){ case CMD_SHM: { Tcl_Obj *pObj; - int i; + int i, rc; TestvfsBuffer *pBuffer; char *zName; if( objc!=3 && objc!=4 ){ Tcl_WrongNumArgs(interp, 2, objv, "FILE ?VALUE?"); return TCL_ERROR; } zName = ckalloc(p->pParent->mxPathname); - p->pParent->xFullPathname( + rc = p->pParent->xFullPathname( p->pParent, Tcl_GetString(objv[2]), p->pParent->mxPathname, zName ); + if( rc!=SQLITE_OK ){ + Tcl_AppendResult(interp, "failed to get full path: ", + Tcl_GetString(objv[2]), 0); + ckfree(zName); + return TCL_ERROR; + } for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ if( 0==strcmp(pBuffer->zFile, zName) ) break; } ckfree(zName); if( !pBuffer ){ @@ -1026,22 +1042,24 @@ case CMD_FILTER: { static struct VfsMethod { char *zName; int mask; } vfsmethod [] = { - { "xShmOpen", TESTVFS_SHMOPEN_MASK }, - { "xShmLock", TESTVFS_SHMLOCK_MASK }, - { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, - { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, - { "xShmMap", TESTVFS_SHMMAP_MASK }, - { "xSync", TESTVFS_SYNC_MASK }, - { "xDelete", TESTVFS_DELETE_MASK }, - { "xWrite", TESTVFS_WRITE_MASK }, - { "xTruncate", TESTVFS_TRUNCATE_MASK }, - { "xOpen", TESTVFS_OPEN_MASK }, - { "xClose", TESTVFS_CLOSE_MASK }, - { "xAccess", TESTVFS_ACCESS_MASK }, + { "xShmOpen", TESTVFS_SHMOPEN_MASK }, + { "xShmLock", TESTVFS_SHMLOCK_MASK }, + { "xShmBarrier", TESTVFS_SHMBARRIER_MASK }, + { "xShmUnmap", TESTVFS_SHMCLOSE_MASK }, + { "xShmMap", TESTVFS_SHMMAP_MASK }, + { "xSync", TESTVFS_SYNC_MASK }, + { "xDelete", TESTVFS_DELETE_MASK }, + { "xWrite", TESTVFS_WRITE_MASK }, + { "xRead", TESTVFS_READ_MASK }, + { "xTruncate", TESTVFS_TRUNCATE_MASK }, + { "xOpen", TESTVFS_OPEN_MASK }, + { "xClose", TESTVFS_CLOSE_MASK }, + { "xAccess", TESTVFS_ACCESS_MASK }, + { "xFullPathname", TESTVFS_FULLPATHNAME_MASK }, }; Tcl_Obj **apElem = 0; int nElem = 0; int i; int mask = 0; @@ -1074,13 +1092,10 @@ case CMD_SCRIPT: { if( objc==3 ){ int nByte; if( p->pScript ){ Tcl_DecrRefCount(p->pScript); - ckfree((char *)p->apScript); - p->apScript = 0; - p->nScript = 0; p->pScript = 0; } Tcl_GetStringFromObj(objv[2], &nByte); if( nByte>0 ){ p->pScript = Tcl_DuplicateObj(objv[2]); @@ -1230,11 +1245,10 @@ static void testvfs_obj_del(ClientData cd){ Testvfs *p = (Testvfs *)cd; if( p->pScript ) Tcl_DecrRefCount(p->pScript); sqlite3_vfs_unregister(p->pVfs); - ckfree((char *)p->apScript); ckfree((char *)p->pVfs); ckfree((char *)p); } /* Index: test/pager1.test ================================================================== --- test/pager1.test +++ test/pager1.test @@ -1651,11 +1651,11 @@ # to the associated journal file will be longer than sqlite3_vfs.mxPathname. # testvfs tv -default 1 tv script xOpenCb tv filter xOpen -proc xOpenCb {method filename} { +proc xOpenCb {method filename args} { set ::file_len [string length $filename] } sqlite3 db test.db db close tv delete ADDED test/tkt-2d1a5c67d.test Index: test/tkt-2d1a5c67d.test ================================================================== --- /dev/null +++ test/tkt-2d1a5c67d.test @@ -0,0 +1,73 @@ +# 2011 May 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. +# +#*********************************************************************** +# This file implements regression tests for SQLite library. Specifically, +# it tests that ticket [2d1a5c67dfc2363e44f29d9bbd57f7331851390a] has +# been resolved. +# +# +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !wal {finish_test; return} + +for {set ii 1} {$ii<=10} {incr ii} { + do_test tkt-2d1a5c67d.1.$ii { + db close + forcedelete test.db test.db-wal + sqlite3 db test.db + db eval "PRAGMA cache_size=$::ii" + db eval { + PRAGMA journal_mode=WAL; + CREATE TABLE t1(a,b); + CREATE INDEX t1b ON t1(b); + CREATE TABLE t2(x,y UNIQUE); + INSERT INTO t2 VALUES(3,4); + BEGIN; + INSERT INTO t1(a,b) VALUES(1,2); + SELECT 'A', * FROM t2 WHERE y=4; + SELECT 'B', * FROM t1; + COMMIT; + SELECT 'C', * FROM t1; + } + } {wal A 3 4 B 1 2 C 1 2} +} + +db close +forcedelete test.db test.db-wal +sqlite3 db test.db +register_wholenumber_module db +db eval { + PRAGMA journal_mode=WAL; + CREATE TABLE t1(a,b); + CREATE INDEX t1b ON t1(b); + CREATE TABLE t2(x,y); + CREATE VIRTUAL TABLE nums USING wholenumber; + INSERT INTO t2 SELECT value, randomblob(1000) FROM nums + WHERE value BETWEEN 1 AND 1000; +} + +for {set ii 1} {$ii<=10} {incr ii} { + do_test tkt-2d1a5c67d.2.$ii { + db eval "PRAGMA cache_size=$::ii" + db eval { + DELETE FROM t1; + BEGIN; + INSERT INTO t1(a,b) VALUES(1,2); + SELECT sum(length(y)) FROM t2; + COMMIT; + SELECT * FROM t1; + } + } {1000000 1 2} +} + +finish_test ADDED test/tkt-bd484a090c.test Index: test/tkt-bd484a090c.test ================================================================== --- /dev/null +++ test/tkt-bd484a090c.test @@ -0,0 +1,42 @@ +# 2011 June 21 +# +# 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. +# +#*********************************************************************** +# +# This file contains tests for SQLite. Specifically, it tests that SQLite +# does not crash and an error is returned if localhost() fails. This +# is the problem reported by ticket bd484a090c. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix tkt-bd484a090c + + +do_test 1.1 { + lindex [catchsql { SELECT datetime('now', 'localtime') }] 0 +} {0} +do_test 1.2 { + lindex [catchsql { SELECT datetime('now', 'utc') }] 0 +} {0} + +sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 1 + +do_test 2.1 { + foreach {rc msg} [catchsql { SELECT datetime('now', 'localtime') }] {} + set res [string match {local time unavailable} $msg] + list $rc $res +} {1 1} +do_test 2.2 { + foreach {rc msg} [catchsql { SELECT datetime('now', 'utc') }] {} + set res [string match {local time unavailable} $msg] + list $rc $res +} {1 1} + +sqlite3_test_control SQLITE_TESTCTRL_LOCALTIME_FAULT 0 + +finish_test Index: tool/shell1.test ================================================================== --- tool/shell1.test +++ tool/shell1.test @@ -198,11 +198,11 @@ } {1 1} # -version show SQLite version do_test shell1-1.16.1 { catchcmd "-version test.db" "" -} {0 3.7.6.1} +} {0 3.7.6.3} #---------------------------------------------------------------------------- # Test cases shell1-2.*: Basic "dot" command token parsing. #