Index: src/analyze.c ================================================================== --- src/analyze.c +++ src/analyze.c @@ -546,10 +546,14 @@ } if( i==0 ) pTable->nRowEst = v; if( pIndex==0 ) break; pIndex->aiRowEst[i] = v; if( *z==' ' ) z++; + if( memcmp(z, "unordered", 10)==0 ){ + pIndex->bUnordered = 1; + break; + } } return 0; } /* Index: src/attach.c ================================================================== --- src/attach.c +++ src/attach.c @@ -174,11 +174,13 @@ break; case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + if( nKey>0 || sqlite3BtreeGetReserve(db->aDb[0].pBt)>0 ){ + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + } break; } } #endif Index: src/backup.c ================================================================== --- src/backup.c +++ src/backup.c @@ -217,10 +217,14 @@ Pager * const pDestPager = sqlite3BtreePager(p->pDest); const int nSrcPgsz = sqlite3BtreeGetPageSize(p->pSrc); int nDestPgsz = sqlite3BtreeGetPageSize(p->pDest); const int nCopy = MIN(nSrcPgsz, nDestPgsz); const i64 iEnd = (i64)iSrcPg*(i64)nSrcPgsz; +#ifdef SQLITE_HAS_CODEC + int nSrcReserve = sqlite3BtreeGetReserve(p->pSrc); + int nDestReserve = sqlite3BtreeGetReserve(p->pDest); +#endif int rc = SQLITE_OK; i64 iOff; assert( p->bDestLocked ); @@ -235,15 +239,26 @@ rc = SQLITE_READONLY; } #ifdef SQLITE_HAS_CODEC /* Backup is not possible if the page size of the destination is changing - ** a a codec is in use. + ** and a codec is in use. */ if( nSrcPgsz!=nDestPgsz && sqlite3PagerGetCodec(pDestPager)!=0 ){ rc = SQLITE_READONLY; } + + /* Backup is not possible if the number of bytes of reserve space differ + ** between source and destination. If there is a difference, try to + ** fix the destination to agree with the source. If that is not possible, + ** then the backup cannot proceed. + */ + if( nSrcReserve!=nDestReserve ){ + u32 newPgsz = nSrcPgsz; + rc = sqlite3PagerSetPagesize(pDestPager, &newPgsz, nSrcReserve); + if( rc==SQLITE_OK && newPgsz!=nSrcPgsz ) rc = SQLITE_READONLY; + } #endif /* This loop runs once for each destination page spanned by the source ** page. For each iteration, variable iOff is set to the byte offset ** of the destination page. @@ -605,11 +620,15 @@ if( !isFatalError(p->rc) && iPageiNext ){ /* The backup process p has already copied page iPage. But now it ** has been modified by a transaction on the source pager. Copy ** the new data into the backup. */ - int rc = backupOnePage(p, iPage, aData); + int rc; + assert( p->pDestDb ); + sqlite3_mutex_enter(p->pDestDb->mutex); + rc = backupOnePage(p, iPage, aData); + sqlite3_mutex_leave(p->pDestDb->mutex); assert( rc!=SQLITE_BUSY && rc!=SQLITE_LOCKED ); if( rc!=SQLITE_OK ){ p->rc = rc; } } Index: src/btmutex.c ================================================================== --- src/btmutex.c +++ src/btmutex.c @@ -200,10 +200,18 @@ for(i=0; inDb; i++){ p = db->aDb[i].pBt; if( p ) sqlite3BtreeLeave(p); } } + +/* +** Return true if a particular Btree requires a lock. Return FALSE if +** no lock is ever required since it is not sharable. +*/ +int sqlite3BtreeSharable(Btree *p){ + return p->sharable; +} #ifndef NDEBUG /* ** Return true if the current thread holds the database connection ** mutex and all required BtShared mutexes. Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -2125,11 +2125,10 @@ rc = sqlite3PagerNosync(pBt->pPager); sqlite3BtreeLeave(p); return rc; } -#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Change the default pages size and the number of reserved bytes per page. ** Or, if the page size has already been fixed, return SQLITE_READONLY ** without changing anything. ** @@ -2180,10 +2179,11 @@ */ int sqlite3BtreeGetPageSize(Btree *p){ return p->pBt->pageSize; } +#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) /* ** Return the number of bytes of space at the end of every page that ** are intentually left unused. This is the "reserved" space that is ** sometimes used by extensions. */ @@ -4817,11 +4817,11 @@ if( rc ){ pTrunk = 0; goto end_allocate_page; } - k = get4byte(&pTrunk->aData[4]); + k = get4byte(&pTrunk->aData[4]); /* # of leaves on this trunk page */ if( k==0 && !searchList ){ /* The trunk has no leaves and the list is not being searched. ** So extract the trunk page itself and use it as the newly ** allocated page */ assert( pPrevTrunk==0 ); @@ -4902,14 +4902,10 @@ }else if( k>0 ){ /* Extract a leaf from the trunk */ u32 closest; Pgno iPage; unsigned char *aData = pTrunk->aData; - rc = sqlite3PagerWrite(pTrunk->pDbPage); - if( rc ){ - goto end_allocate_page; - } if( nearby>0 ){ u32 i; int dist; closest = 0; dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); @@ -4935,15 +4931,16 @@ int noContent; *pPgno = iPage; TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" ": %d more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); + rc = sqlite3PagerWrite(pTrunk->pDbPage); + if( rc ) goto end_allocate_page; if( closestpDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ @@ -5008,10 +5005,11 @@ } (*ppPage)->isInit = 0; }else{ *ppPage = 0; } + assert( rc!=SQLITE_OK || sqlite3PagerIswriteable((*ppPage)->pDbPage) ); return rc; } /* ** This function is used to add page iPage to the database file free-list. Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -210,10 +210,11 @@ # define sqlite3BtreeEnter(X) # define sqlite3BtreeEnterAll(X) #endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && SQLITE_THREADSAFE + int sqlite3BtreeSharable(Btree*); void sqlite3BtreeLeave(Btree*); void sqlite3BtreeEnterCursor(BtCursor*); void sqlite3BtreeLeaveCursor(BtCursor*); void sqlite3BtreeLeaveAll(sqlite3*); #ifndef NDEBUG @@ -222,10 +223,11 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3*); int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); #endif #else +# define sqlite3BtreeSharable(X) 0 # define sqlite3BtreeLeave(X) # define sqlite3BtreeEnterCursor(X) # define sqlite3BtreeLeaveCursor(X) # define sqlite3BtreeLeaveAll(X) Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -811,10 +811,13 @@ } pTable = sqlite3FindTable(db, zName, zDb); if( pTable ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "table %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto begin_table_error; } if( sqlite3FindIndex(db, zName, zDb)!=0 ){ sqlite3ErrorMsg(pParse, "there is already an index named %s", zName); @@ -1998,10 +2001,11 @@ pTab = sqlite3LocateTable(pParse, isView, pName->a[0].zName, pName->a[0].zDatabase); if( noErr ) db->suppressErr--; if( pTab==0 ){ + if( noErr ) sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); goto exit_drop_table; } iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 && iDbnDb ); @@ -2516,10 +2520,13 @@ } } if( sqlite3FindIndex(db, zName, pDb->zName)!=0 ){ if( !ifNotExist ){ sqlite3ErrorMsg(pParse, "index %s already exists", zName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto exit_create_index; } }else{ int n; @@ -2909,10 +2916,12 @@ } pIndex = sqlite3FindIndex(db, pName->a[0].zName, pName->a[0].zDatabase); if( pIndex==0 ){ if( !ifExists ){ sqlite3ErrorMsg(pParse, "no such index: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, pName->a[0].zDatabase); } pParse->checkSchema = 1; goto exit_drop_index; } if( pIndex->autoIndex ){ @@ -3497,10 +3506,25 @@ sqlite3OpenTempDatabase(pToplevel); } } } } + +/* +** If argument zDb is NULL, then call sqlite3CodeVerifySchema() for each +** attached database. Otherwise, invoke it for the database named zDb only. +*/ +void sqlite3CodeVerifyNamedSchema(Parse *pParse, const char *zDb){ + sqlite3 *db = pParse->db; + int i; + for(i=0; inDb; i++){ + Db *pDb = &db->aDb[i]; + if( pDb->pBt && (!zDb || 0==sqlite3StrICmp(zDb, pDb->zName)) ){ + sqlite3CodeVerifySchema(pParse, i); + } + } +} /* ** Generate VDBE code that prepares for doing an operation that ** might change the database. ** Index: src/ctime.c ================================================================== --- src/ctime.c +++ src/ctime.c @@ -300,13 +300,10 @@ "OMIT_TRIGGER", #endif #ifdef SQLITE_OMIT_TRUNCATE_OPTIMIZATION "OMIT_TRUNCATE_OPTIMIZATION", #endif -#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT - "OMIT_UNIQUE_ENFORCEMENT", -#endif #ifdef SQLITE_OMIT_UTF16 "OMIT_UTF16", #endif #ifdef SQLITE_OMIT_VACUUM "OMIT_VACUUM", Index: src/insert.c ================================================================== --- src/insert.c +++ src/insert.c @@ -1310,13 +1310,12 @@ ** index and making sure that duplicate entries do not already exist. ** Add the new records to the indices as we go. */ for(iCur=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, iCur++){ int regIdx; -#ifndef SQLITE_OMIT_UNIQUE_ENFORCEMENT int regR; -#endif + if( aRegIdx[iCur]==0 ) continue; /* Skip unused indices */ /* Create a key for accessing the index entry */ regIdx = sqlite3GetTempRange(pParse, pIdx->nColumn+1); for(i=0; inColumn; i++){ @@ -1330,15 +1329,10 @@ sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), P4_TRANSIENT); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); -#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT - sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); - continue; /* Treat pIdx as if it is not a UNIQUE index */ -#else - /* Find out what action to take in case there is an indexing conflict */ onError = pIdx->onError; if( onError==OE_None ){ sqlite3ReleaseTempRange(pParse, regIdx, pIdx->nColumn+1); continue; /* pIdx is not a UNIQUE index */ @@ -1408,11 +1402,10 @@ break; } } sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); -#endif } if( pbMayReplace ){ *pbMayReplace = seenReplace; } Index: src/loadext.c ================================================================== --- src/loadext.c +++ src/loadext.c @@ -68,10 +68,15 @@ #ifdef SQLITE_OMIT_COMPLETE # define sqlite3_complete 0 # define sqlite3_complete16 0 #endif + +#ifdef SQLITE_OMIT_DECLTYPE +# define sqlite3_column_decltype16 0 +# define sqlite3_column_decltype 0 +#endif #ifdef SQLITE_OMIT_PROGRESS_CALLBACK # define sqlite3_progress_handler 0 #endif Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -516,11 +516,11 @@ va_list ap; int rc; va_start(ap, op); switch( op ){ case SQLITE_DBCONFIG_LOOKASIDE: { - void *pBuf = va_arg(ap, void*); /* IMP: R-21112-12275 */ + void *pBuf = va_arg(ap, void*); /* IMP: R-26835-10964 */ int sz = va_arg(ap, int); /* IMP: R-47871-25994 */ int cnt = va_arg(ap, int); /* IMP: R-04460-53386 */ rc = setupLookaside(db, pBuf, sz, cnt); break; } Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -3411,22 +3411,21 @@ ** is the same technique used by glibc to implement posix_fallocate() ** on systems that do not have a real fallocate() system call. */ int nBlk = buf.st_blksize; /* File-system block size */ i64 iWrite; /* Next offset to write to */ - int nWrite; /* Return value from seekAndWrite() */ if( robust_ftruncate(pFile->h, nSize) ){ pFile->lastErrno = errno; return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); } iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1; - do { - nWrite = seekAndWrite(pFile, iWrite, "", 1); + while( iWritepzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg, "%s - %s", *pData->pzErrMsg, zExtra); } } - pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT; + pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT_BKPT; } /* ** This is the callback routine for the code that initializes the ** database. See sqlite3Init() below for additional information. Index: src/select.c ================================================================== --- src/select.c +++ src/select.c @@ -804,10 +804,26 @@ char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } +/* +** Assign expression b to lvalue a. A second, no-op, version of this macro +** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code +** in sqlite3Select() to assign values to structure member variables that +** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the +** code with #ifndef directives. +*/ +# define explainSetInteger(a, b) a = b + +#else +/* No-op versions of the explainXXX() functions and macros. */ +# define explainTempTable(y,z) +# define explainSetInteger(y,z) +#endif + +#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT) /* ** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function ** is a no-op. Otherwise, it adds a single row of output to the EQP result, ** where the caption is of one of the two forms: ** @@ -835,25 +851,13 @@ bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op) ); sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC); } } - -/* -** Assign expression b to lvalue a. A second, no-op, version of this macro -** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code -** in sqlite3Select() to assign values to structure member variables that -** only exist if SQLITE_OMIT_EXPLAIN is not defined without polluting the -** code with #ifndef directives. -*/ -# define explainSetInteger(a, b) a = b - #else /* No-op versions of the explainXXX() functions and macros. */ -# define explainTempTable(y,z) # define explainComposite(v,w,x,y,z) -# define explainSetInteger(y,z) #endif /* ** If the inner loop was generated using a non-null pOrderBy argument, ** then the results were placed in a sorter. After the loop is terminated Index: src/shell.c ================================================================== --- src/shell.c +++ src/shell.c @@ -2197,11 +2197,11 @@ int i, n; open_db(p); /* convert testctrl text option to value. allow any unique prefix ** of the option name, or a numerical value. */ - n = strlen(azArg[1]); + n = strlen30(azArg[1]); for(i=0; i<(int)(sizeof(aCtrl)/sizeof(aCtrl[0])); i++){ if( strncmp(azArg[1], aCtrl[i].zCtrlName, n)==0 ){ if( testctrl<0 ){ testctrl = aCtrl[i].ctrlCode; }else{ Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -725,11 +725,11 @@ ** when the database connection has [PRAGMA synchronous] set to OFF.)^ ** Some specialized VFSes need this signal in order to operate correctly ** when [PRAGMA synchronous | PRAGMA synchronous=OFF] is set, but most ** VFSes do not need this signal and should silently ignore this opcode. ** Applications should not call [sqlite3_file_control()] with this -** opcode as doing so may disrupt the operation of the specilized VFSes +** opcode as doing so may disrupt the operation of the specialized VFSes ** that do require it. */ #define SQLITE_FCNTL_LOCKSTATE 1 #define SQLITE_GET_LOCKPROXYFILE 2 #define SQLITE_SET_LOCKPROXYFILE 3 @@ -1307,11 +1307,11 @@ ** ** **
SQLITE_CONFIG_SCRATCH
**
^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte -** aligned memory buffer from which the scrach allocations will be +** aligned memory buffer from which the scratch allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. ** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. @@ -1459,11 +1459,11 @@ **
**
SQLITE_DBCONFIG_LOOKASIDE
**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. ** ^The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an memory buffer to use for lookaside memory. +** pointer to a memory buffer to use for lookaside memory. ** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb ** may be NULL in which case SQLite will allocate the ** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the ** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than @@ -1491,11 +1491,11 @@ ** **
SQLITE_DBCONFIG_ENABLE_TRIGGER
**
^This option is used to enable or disable [CREATE TRIGGER | triggers]. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable triggers, -** positive to enable trigers or negative to leave the setting unchanged. +** positive to enable triggers or negative to leave the setting unchanged. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back.
** @@ -2103,11 +2103,11 @@ void sqlite3_randomness(int N, void *P); /* ** CAPI3REF: Compile-Time Authorization Callbacks ** -** ^This routine registers a authorizer callback with a particular +** ^This routine registers an authorizer callback with a particular ** [database connection], supplied in the first argument. ** ^The authorizer callback is invoked as SQL statements are being compiled ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()], ** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()]. ^At various ** points during the compilation process, as logic is being created @@ -2749,11 +2749,11 @@ ** will accept either a protected or an unprotected sqlite3_value. ** Every interface that accepts sqlite3_value arguments specifies ** whether or not it requires a protected sqlite3_value. ** ** The terms "protected" and "unprotected" refer to whether or not -** a mutex is held. A internal mutex is held for a protected +** a mutex is held. An internal mutex is held for a protected ** sqlite3_value object but no mutex is held for an unprotected ** sqlite3_value object. If SQLite is compiled to be single-threaded ** (with [SQLITE_THREADSAFE=0] and with [sqlite3_threadsafe()] returning 0) ** or if SQLite is run in one of reduced mutex modes ** [SQLITE_CONFIG_SINGLETHREAD] or [SQLITE_CONFIG_MULTITHREAD] @@ -3435,11 +3435,11 @@ ** ** ^These functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only differences between ** these routines are the text encoding expected for -** the the second parameter (the name of the function being created) +** the second parameter (the name of the function being created) ** and the presence or absence of a destructor callback for ** the application data pointer. ** ** ^The first parameter is the [database connection] to which the SQL ** function is to be added. ^If an application uses more than one database @@ -3480,11 +3480,11 @@ ** pointers to C-language functions that implement the SQL function or ** aggregate. ^A scalar SQL function requires an implementation of the xFunc ** callback only; NULL pointers must be passed as the xStep and xFinal ** parameters. ^An aggregate SQL function requires an implementation of xStep ** and xFinal and NULL pointer must be passed for xFunc. ^To delete an existing -** SQL function or aggregate, pass NULL poiners for all three function +** SQL function or aggregate, pass NULL pointers for all three function ** callbacks. ** ** ^(If the ninth parameter to sqlite3_create_function_v2() is not NULL, ** then it is destructor for the application data pointer. ** The destructor is invoked when the function is deleted, either by being @@ -3914,11 +3914,11 @@ ** ^The [SQLITE_UTF16] and [SQLITE_UTF16_ALIGNED] values for eTextRep ** force strings to be UTF16 with native byte order. ** ^The [SQLITE_UTF16_ALIGNED] value for eTextRep forces strings to begin ** on an even byte address. ** -** ^The fourth argument, pArg, is a application data pointer that is passed +** ^The fourth argument, pArg, is an application data pointer that is passed ** through as the first argument to the collating function callback. ** ** ^The fifth argument, xCallback, is a pointer to the collating function. ** ^Multiple collating functions can be registered using the same name but ** with different eTextRep parameters and SQLite will use whichever @@ -3930,11 +3930,11 @@ ** ^The collating function callback is invoked with a copy of the pArg ** application data pointer and with two strings in the encoding specified ** by the eTextRep argument. The collating function must return an ** integer that is negative, zero, or positive ** if the first string is less than, equal to, or greater than the second, -** respectively. A collating function must alway return the same answer +** respectively. A collating function must always return the same answer ** given the same inputs. If two or more collating functions are registered ** to the same collation name (using different eTextRep values) then all ** must give an equivalent answer when invoked with equivalent strings. ** The collating function must obey the following properties for all ** strings A, B, and C: @@ -4342,11 +4342,11 @@ **
    **
  • The soft heap limit is set to zero. **
  • Memory accounting is disabled using a combination of the ** [sqlite3_config]([SQLITE_CONFIG_MEMSTATUS],...) start-time option and ** the [SQLITE_DEFAULT_MEMSTATUS] compile-time option. -**
  • An alternative page cache implementation is specifed using +**
  • An alternative page cache implementation is specified using ** [sqlite3_config]([SQLITE_CONFIG_PCACHE],...). **
  • The page cache allocates from its own memory pool supplied ** by [sqlite3_config]([SQLITE_CONFIG_PAGECACHE],...) rather than ** from the heap. **
)^ @@ -4563,11 +4563,11 @@ /* ** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** -** This structure, sometimes called a a "virtual table module", +** This structure, sometimes called a "virtual table module", ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** ** ^A virtual table module is created by filling in a persistent ** instance of this structure and passing a pointer to that instance @@ -4875,11 +4875,11 @@ ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column ** other than the one the BLOB handle is open on.)^ ** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for -** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. +** an expired BLOB handle fail with a return code of [SQLITE_ABORT]. ** ^(Changes written into a BLOB prior to the BLOB expiring are not ** rolled back by the expiration of the BLOB. Such changes will eventually ** commit if the transaction continues to completion.)^ ** ** ^Use the [sqlite3_blob_bytes()] interface to determine the size of Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -1474,10 +1474,11 @@ unsigned *aiRowEst; /* Result of ANALYZE: Est. rows selected by each column */ Table *pTable; /* The SQL table being indexed */ int tnum; /* Page containing root of this index in database file */ u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ u8 autoIndex; /* True if is automatically created (ex: by UNIQUE) */ + u8 bUnordered; /* Use this index for == or IN queries only */ char *zColAff; /* String defining the affinity of each column */ Index *pNext; /* The next index associated with the same table */ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ @@ -2765,10 +2766,11 @@ void sqlite3PrngSaveState(void); void sqlite3PrngRestoreState(void); void sqlite3PrngResetState(void); void sqlite3RollbackAll(sqlite3*); void sqlite3CodeVerifySchema(Parse*, int); +void sqlite3CodeVerifyNamedSchema(Parse*, const char *zDb); void sqlite3BeginTransaction(Parse*, int); void sqlite3CommitTransaction(Parse*); void sqlite3RollbackTransaction(Parse*); void sqlite3Savepoint(Parse*, int, Token*); void sqlite3CloseSavepoints(sqlite3 *); Index: src/sqliteLimit.h ================================================================== --- src/sqliteLimit.h +++ src/sqliteLimit.h @@ -116,11 +116,11 @@ # define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000 #endif /* ** The maximum number of attached databases. This must be between 0 -** and 30. The upper bound on 30 is because a 32-bit integer bitmap +** and 62. The upper bound on 62 is because a 64-bit integer bitmap ** is used internally to track attached databases. */ #ifndef SQLITE_MAX_ATTACHED # define SQLITE_MAX_ATTACHED 10 #endif Index: src/test_config.c ================================================================== --- src/test_config.c +++ src/test_config.c @@ -88,10 +88,16 @@ #ifdef SQLITE_MUTEX_OMIT Tcl_SetVar2(interp, "sqlite_options", "mutex", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "mutex", "1", TCL_GLOBAL_ONLY); #endif + +#ifdef SQLITE_MUTEX_NOOP + Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "1", TCL_GLOBAL_ONLY); +#else + Tcl_SetVar2(interp, "sqlite_options", "mutex_noop", "0", TCL_GLOBAL_ONLY); +#endif #ifdef SQLITE_OMIT_ALTERTABLE Tcl_SetVar2(interp, "sqlite_options", "altertable", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "altertable", "1", TCL_GLOBAL_ONLY); @@ -473,16 +479,10 @@ Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "truncate_opt", "1", TCL_GLOBAL_ONLY); #endif -#ifdef SQLITE_OMIT_UNIQUE_ENFORCEMENT - Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "0", TCL_GLOBAL_ONLY); -#else - Tcl_SetVar2(interp, "sqlite_options", "unique_enforcement", "1", TCL_GLOBAL_ONLY); -#endif - #ifdef SQLITE_OMIT_UTF16 Tcl_SetVar2(interp, "sqlite_options", "utf16", "0", TCL_GLOBAL_ONLY); #else Tcl_SetVar2(interp, "sqlite_options", "utf16", "1", TCL_GLOBAL_ONLY); #endif Index: src/test_server.c ================================================================== --- src/test_server.c +++ src/test_server.c @@ -451,11 +451,10 @@ */ pMsg->op = MSG_Done; pthread_mutex_unlock(&pMsg->clientMutex); pthread_cond_signal(&pMsg->clientWakeup); } - sqlite3_thread_cleanup(); pthread_mutex_unlock(&g.serverMutex); return 0; } /* Index: src/trigger.c ================================================================== --- src/trigger.c +++ src/trigger.c @@ -169,10 +169,13 @@ assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName, sqlite3Strlen30(zName)) ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "trigger %T already exists", pName); + }else{ + assert( !db->init.busy ); + sqlite3CodeVerifySchema(pParse, iDb); } goto trigger_cleanup; } /* Do not create a trigger on a system table */ @@ -497,10 +500,12 @@ if( pTrigger ) break; } if( !pTrigger ){ if( !noErr ){ sqlite3ErrorMsg(pParse, "no such trigger: %S", pName, 0); + }else{ + sqlite3CodeVerifyNamedSchema(pParse, zDb); } pParse->checkSchema = 1; goto drop_trigger_cleanup; } sqlite3DropTriggerPtr(pParse, pTrigger); Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -4643,12 +4643,10 @@ #ifdef SQLITE_DEBUG for(iDb=0; iDbnDb; iDb++){ assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); } #endif - assert( p->btreeMask == ~(yDbMask)0 ); - iDb = pOp->p1; assert( iDb>=0 && iDbnDb ); assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); /* Used to be a conditional */ { Index: src/vdbeInt.h ================================================================== --- src/vdbeInt.h +++ src/vdbeInt.h @@ -301,10 +301,11 @@ u8 usesStmtJournal; /* True if uses a statement journal */ u8 readOnly; /* True for read-only statements */ u8 isPrepareV2; /* True if prepared with prepare_v2() */ int nChange; /* Number of db changes made since last reset */ yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */ + yDbMask lockMask; /* Subset of btreeMask that requires a lock */ int iStatement; /* Statement number (or 0 if has not opened stmt) */ int aCounter[3]; /* Counters used by sqlite3_stmt_status() */ #ifndef SQLITE_OMIT_TRACE i64 startTime; /* Time when query started - used for profiling */ #endif Index: src/vdbeaux.c ================================================================== --- src/vdbeaux.c +++ src/vdbeaux.c @@ -158,11 +158,12 @@ pOp->p4type = P4_NOTUSED; p->expired = 0; if( op==OP_ParseSchema ){ /* Any program that uses the OP_ParseSchema opcode needs to lock ** all btrees. */ - p->btreeMask = ~(yDbMask)0; + int j; + for(j=0; jdb->nDb; j++) sqlite3VdbeUsesBtree(p, j); } #ifdef SQLITE_DEBUG pOp->zComment = 0; if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]); #endif @@ -956,10 +957,13 @@ */ void sqlite3VdbeUsesBtree(Vdbe *p, int i){ assert( i>=0 && idb->nDb && i<(int)sizeof(yDbMask)*8 ); assert( i<(int)sizeof(p->btreeMask)*8 ); p->btreeMask |= ((yDbMask)1)<db->aDb[i].pBt) ){ + p->lockMask |= ((yDbMask)1)<0 /* ** If SQLite is compiled to support shared-cache mode and to be threadsafe, @@ -983,15 +987,19 @@ ** be a problem. */ void sqlite3VdbeEnter(Vdbe *p){ int i; yDbMask mask; - sqlite3 *db = p->db; - Db *aDb = db->aDb; - int nDb = db->nDb; + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; for(i=0, mask=1; ibtreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ sqlite3BtreeEnter(aDb[i].pBt); } } } #endif @@ -1001,16 +1009,19 @@ ** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter(). */ void sqlite3VdbeLeave(Vdbe *p){ int i; yDbMask mask; - sqlite3 *db = p->db; - Db *aDb = db->aDb; - int nDb = db->nDb; - + sqlite3 *db; + Db *aDb; + int nDb; + if( p->lockMask==0 ) return; /* The common case */ + db = p->db; + aDb = db->aDb; + nDb = db->nDb; for(i=0, mask=1; ibtreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ + if( i!=1 && (mask & p->lockMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){ sqlite3BtreeLeave(aDb[i].pBt); } } } #endif Index: src/vdbetrace.c ================================================================== --- src/vdbetrace.c +++ src/vdbetrace.c @@ -83,11 +83,11 @@ if( db->vdbeExecCnt>1 ){ while( *zRawSql ){ const char *zStart = zRawSql; while( *(zRawSql++)!='\n' && *zRawSql ); sqlite3StrAccumAppend(&out, "-- ", 3); - sqlite3StrAccumAppend(&out, zStart, zRawSql-zStart); + sqlite3StrAccumAppend(&out, zStart, (int)(zRawSql-zStart)); } }else{ while( zRawSql[0] ){ n = findNextHostParameter(zRawSql, &nToken); assert( n>0 ); Index: src/where.c ================================================================== --- src/where.c +++ src/where.c @@ -1340,11 +1340,14 @@ ** Note that the virtual term must be tagged with TERM_VNULL. This ** TERM_VNULL tag will suppress the not-null check at the beginning ** of the loop. Without the TERM_VNULL flag, the not-null check at ** the start of the loop will prevent any results from being returned. */ - if( pExpr->op==TK_NOTNULL && pExpr->pLeft->iColumn>=0 ){ + if( pExpr->op==TK_NOTNULL + && pExpr->pLeft->op==TK_COLUMN + && pExpr->pLeft->iColumn>=0 + ){ Expr *pNewExpr; Expr *pLeft = pExpr->pLeft; int idxNew; WhereTerm *pNewTerm; @@ -2530,11 +2533,11 @@ ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ -int whereEqualScanEst( +static int whereEqualScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ Expr *pExpr, /* Expression for VALUE in the x=VALUE constraint */ double *pnRow /* Write the revised row estimate here */ ){ @@ -2587,11 +2590,11 @@ ** This routine can fail if it is unable to load a collating sequence ** required for string comparison, or if unable to allocate memory ** for a UTF conversion required for comparison. The error is stored ** in the pParse structure. */ -int whereInScanEst( +static int whereInScanEst( Parse *pParse, /* Parsing & code generating context */ Index *p, /* The index whose left-most column is pTerm */ ExprList *pList, /* The value list on the RHS of "x IN (v1,v2,v3,...)" */ double *pnRow /* Write the revised row estimate here */ ){ @@ -2858,11 +2861,11 @@ #endif used |= pTerm->prereqRight; } /* Determine the value of estBound. */ - if( nEqnColumn ){ + if( nEqnColumn && pProbe->bUnordered==0 ){ int j = pProbe->aiColumn[nEq]; if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &estBound); @@ -2890,10 +2893,11 @@ ** naturally scan rows in the required order, set the appropriate flags ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ if( (wsFlags & WHERE_COLUMN_IN)==0 + && pProbe->bUnordered==0 && isSortingIndex(pParse, pWC->pMaskSet, pProbe, iCur, pOrderBy, nEq, wsFlags, &rev) ){ wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; wsFlags |= (rev ? WHERE_REVERSE : 0); Index: test/analyze5.test ================================================================== --- test/analyze5.test +++ test/analyze5.test @@ -207,10 +207,12 @@ 500 {x IS NULL AND u='charlie'} t1u 20 501 {x=1 AND u='charlie'} t1x 5 502 {x IS NULL} {} 100 503 {x=1} t1x 50 504 {x IS NOT NULL} t1x 25 + 505 {+x IS NOT NULL} {} 500 + 506 {upper(x) IS NOT NULL} {} 500 } { # Verify that the expected index is used with the expected row count do_test analyze5-1.${testid}a { set x [lindex [eqp "SELECT * FROM t1 WHERE $where"] 3] Index: test/analyze7.test ================================================================== --- test/analyze7.test +++ test/analyze7.test @@ -85,11 +85,11 @@ ifcapable stat2 { # If ENABLE_STAT2 is defined, SQLite comes up with a different estimated # row count for (c=2) than it does for (c=?). do_test analyze7-3.2.2 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} - } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~102 rows)}} + } {0 0 0 {SEARCH TABLE t1 USING INDEX t1cd (c=?) (~51 rows)}} } else { # If ENABLE_STAT2 is not defined, the expected row count for (c=2) is the # same as that for (c=?). do_test analyze7-3.2.3 { execsql {EXPLAIN QUERY PLAN SELECT * FROM t1 WHERE c=2;} ADDED test/attach4.test Index: test/attach4.test ================================================================== --- /dev/null +++ test/attach4.test @@ -0,0 +1,116 @@ +# 200 July 1 +# +# 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. The +# focus of this script is attaching many database files to a single +# connection. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +set testprefix attach4 + +ifcapable !attach { + finish_test + return +} + +puts "Testing with SQLITE_MAX_ATTACHED=$SQLITE_MAX_ATTACHED" + +set files {main test.db} +for {set ii 0} {$ii < $SQLITE_MAX_ATTACHED} {incr ii} { + lappend files aux$ii "test.db$ii" +} + +do_test 1.1 { + sqlite3_limit db SQLITE_LIMIT_ATTACHED -1 +} $SQLITE_MAX_ATTACHED + +do_test 1.2.1 { + db close + foreach {name f} $files { forcedelete $f } + sqlite3 db test.db + + foreach {name f} $files { + if {$name == "main"} continue + execsql "ATTACH '$f' AS $name" + } + + db eval {PRAGMA database_list} { + lappend L $name [file tail $file] + } + set L +} $files + +do_catchsql_test 1.2.2 { + ATTACH 'x.db' AS next; +} [list 1 "too many attached databases - max $SQLITE_MAX_ATTACHED"] + +do_test 1.3 { + execsql BEGIN; + foreach {name f} $files { + execsql "CREATE TABLE $name.tbl(x)" + execsql "INSERT INTO $name.tbl VALUES('$f')" + } + execsql COMMIT; +} {} + +do_test 1.4 { + set L [list] + foreach {name f} $files { + lappend L $name [execsql "SELECT x FROM $name.tbl"] + } + set L +} $files + +set L [list] +set S "" +foreach {name f} $files { + if {[permutation] == "journaltest"} { + lappend L delete + } else { + lappend L wal + } + append S " + PRAGMA $name.journal_mode = WAL; + UPDATE $name.tbl SET x = '$name'; + " +} +do_execsql_test 1.5 $S $L + +do_test 1.6 { + set L [list] + foreach {name f} $files { + lappend L [execsql "SELECT x FROM $name.tbl"] $f + } + set L +} $files + +do_test 1.7 { + execsql BEGIN; + foreach {name f} $files { + execsql "UPDATE $name.tbl SET x = '$f'" + } + execsql COMMIT; +} {} + +do_test 1.8 { + set L [list] + foreach {name f} $files { + lappend L $name [execsql "SELECT x FROM $name.tbl"] + } + set L +} $files + +db close +foreach {name f} $files { forcedelete $f } + +finish_test Index: test/backcompat.test ================================================================== --- test/backcompat.test +++ test/backcompat.test @@ -364,9 +364,82 @@ 7 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH '44'" 8 "SELECT offsets(t1) FROM t1 WHERE t1 MATCH 'a*'" } { do_test backcompat-3.7 [list sql1 $q] [sql2 $q] } + } +} + +#------------------------------------------------------------------------- +# Test that Rtree tables may be read/written by different versions of +# SQLite. +# +set contents { + CREATE VIRTUAL TABLE t1 USING rtree(id, x1, x2, y1, y2); +} +foreach {id x1 x2 y1 y2} { + 1 -47.64 43.87 33.86 34.42 2 -21.51 17.32 2.05 31.04 + 3 -43.67 -38.33 -19.79 3.43 4 32.41 35.16 9.12 19.82 + 5 33.28 34.87 14.78 28.26 6 49.31 116.59 -9.87 75.09 + 7 -14.93 34.51 -17.64 64.09 8 -43.05 23.43 -1.19 69.44 + 9 44.79 133.56 28.09 80.30 10 -2.66 81.47 -41.38 -10.46 + 11 -42.89 -3.54 15.76 71.63 12 -3.50 84.96 -11.64 64.95 + 13 -45.69 26.25 11.14 55.06 14 -44.09 11.23 17.52 44.45 + 15 36.23 133.49 -19.38 53.67 16 -17.89 81.54 14.64 50.61 + 17 -41.97 -24.04 -39.43 28.95 18 -5.85 7.76 -6.38 47.02 + 19 18.82 27.10 42.82 100.09 20 39.17 113.45 26.14 73.47 + 21 22.31 103.17 49.92 106.05 22 -43.06 40.38 -1.75 76.08 + 23 2.43 57.27 -14.19 -3.83 24 -47.57 -4.35 8.93 100.06 + 25 -37.47 49.14 -29.11 8.81 26 -7.86 75.72 49.34 107.42 + 27 1.53 45.49 20.36 49.74 28 -48.48 32.54 28.81 54.45 + 29 2.67 39.77 -4.05 13.67 30 4.11 62.88 -47.44 -5.72 + 31 -21.47 51.75 37.25 116.09 32 45.59 111.37 -6.43 43.64 + 33 35.23 48.29 23.54 113.33 34 16.61 68.35 -14.69 65.97 + 35 13.98 16.60 48.66 102.87 36 19.74 23.84 31.15 77.27 + 37 -27.61 24.43 7.96 94.91 38 -34.77 12.05 -22.60 -6.29 + 39 -25.83 8.71 -13.48 -12.53 40 -17.11 -1.01 18.06 67.89 + 41 14.13 71.72 -3.78 39.25 42 23.75 76.00 -16.30 8.23 + 43 -39.15 28.63 38.12 125.88 44 48.62 86.09 36.49 102.95 + 45 -31.39 -21.98 2.52 89.78 46 5.65 56.04 15.94 89.10 + 47 18.28 95.81 46.46 143.08 48 30.93 102.82 -20.08 37.36 + 49 -20.78 -3.48 -5.58 35.46 50 49.85 90.58 -24.48 46.29 +} { +if {$x1 >= $x2 || $y1 >= $y2} { error "$x1 $x2 $y1 $y2" } + append contents "INSERT INTO t1 VALUES($id, $x1, $x2, $y1, $y2);" +} +set queries { + 1 "SELECT id FROM t1 WHERE x1>10 AND x2<44" + 2 "SELECT id FROM t1 WHERE y1<100" + 3 "SELECT id FROM t1 WHERE y1<100 AND x1>0" + 4 "SELECT id FROM t1 WHERE y1>10 AND x1>0 AND x2<50 AND y2<550" +} +do_allbackcompat_test { + if {[code1 {set ::sqlite_options(fts3)}] + && [code2 {set ::sqlite_options(fts3)}] + } { + + do_test backcompat-4.1 { sql1 $contents } {} + + foreach {n q} $::queries { + do_test backcompat-4.2.$n [list sql1 $q] [sql2 $q] + } + + do_test backcompat-4.3 { sql1 { + INSERT INTO t1 SELECT id+100, x1+10.0, x2+10.0, y1-10.0, y2-10.0 FROM t1; + } } {} + + foreach {n q} $::queries { + do_test backcompat-4.4.$n [list sql1 $q] [sql2 $q] + } + + do_test backcompat-4.5 { sql2 { + INSERT INTO t1 SELECT id+200, x1+20.0, x2+20.0, y1-20.0, y2-20.0 FROM t1; + } } {} + + foreach {n q} $::queries { + do_test backcompat-4.6.$n [list sql1 $q] [sql2 $q] + } + } } finish_test ADDED test/exists.test Index: test/exists.test ================================================================== --- /dev/null +++ test/exists.test @@ -0,0 +1,192 @@ +# 2011 April 9 +# +# 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. The +# focus of this file is testing the various schema modification statements +# that feature "IF EXISTS" or "IF NOT EXISTS" clauses. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl + +set testprefix exists + +# This block of tests is targeted at CREATE XXX IF NOT EXISTS statements. +# +do_multiclient_test tn { + + # TABLE objects. + # + do_test 1.$tn.1.1 { + sql2 { CREATE TABLE t1(x) } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { DROP TABLE t1 } + sql1 { CREATE TABLE IF NOT EXISTS t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1} + + do_test 1.$tn.1.2 { + sql2 { CREATE TABLE t2(x) } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { DROP TABLE t2 } + sql1 { CREATE TABLE IF NOT EXISTS t2 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {t1 t2} + + + # INDEX objects. + # + do_test 1.$tn.2 { + sql2 { CREATE INDEX i1 ON t1(a) } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { DROP INDEX i1 } + sql1 { CREATE INDEX IF NOT EXISTS i1 ON t1(a, b) } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {i1} + + # VIEW objects. + # + do_test 1.$tn.3 { + sql2 { CREATE VIEW v1 AS SELECT * FROM t1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { DROP VIEW v1 } + sql1 { CREATE VIEW IF NOT EXISTS v1 AS SELECT * FROM t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {v1} + + # TRIGGER objects. + # + do_test $tn.4 { + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } +sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { DROP TRIGGER tr1 } +sql1 { CREATE TRIGGER IF NOT EXISTS tr1 AFTER INSERT ON t1 BEGIN SELECT 1; END } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {tr1} +} + +# This block of tests is targeted at DROP XXX IF EXISTS statements. +# +do_multiclient_test tn { + + # TABLE objects. + # + do_test 2.$tn.1 { + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'table' } + } {} + + # INDEX objects. + # + do_test 2.$tn.2 { + sql1 { CREATE TABLE t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'index' } + } {} + + # VIEW objects. + # + do_test 2.$tn.3 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'view' } + } {} + + # TRIGGER objects. + # + do_test 2.$tn.4 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM sqlite_master WHERE type = 'trigger' } + } {} +} + +# This block of tests is targeted at DROP XXX IF EXISTS statements with +# attached databases. +# +do_multiclient_test tn { + + forcedelete test.db2 + do_test 3.$tn.0 { + sql1 { ATTACH 'test.db2' AS aux } + sql2 { ATTACH 'test.db2' AS aux } + } {} + + # TABLE objects. + # + do_test 3.$tn.1.1 { + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS aux.t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} + do_test 3.$tn.1.2 { + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { CREATE TABLE aux.t1(x) } + sql1 { DROP TABLE IF EXISTS t1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'table' } + } {} + + # INDEX objects. + # + do_test 3.$tn.2.1 { + sql1 { CREATE TABLE aux.t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS aux.i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} + do_test 3.$tn.2.2 { + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { CREATE INDEX aux.i2 ON t2(x) } + sql1 { DROP INDEX IF EXISTS i2 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'index' } + } {} + + # VIEW objects. + # + do_test 3.$tn.3.1 { + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS aux.v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} + do_test 3.$tn.3.2 { + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { CREATE VIEW aux.v1 AS SELECT * FROM t2 } + sql1 { DROP VIEW IF EXISTS v1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'view' } + } {} + + # TRIGGER objects. + # + do_test 3.$tn.4.1 { + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS aux.tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} + do_test 3.$tn.4.2 { + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { CREATE TRIGGER aux.tr1 AFTER INSERT ON t2 BEGIN SELECT 1; END } + sql1 { DROP TRIGGER IF EXISTS tr1 } + sql2 { SELECT name FROM aux.sqlite_master WHERE type = 'trigger' } + } {} +} + + +finish_test Index: test/incrvacuum2.test ================================================================== --- test/incrvacuum2.test +++ test/incrvacuum2.test @@ -21,10 +21,11 @@ ifcapable {!autovacuum || !pragma} { finish_test return } +set testprefix incrvacuum2 # Create a database in incremental vacuum mode that has many # pages on the freelist. # do_test incrvacuum2-1.1 { @@ -129,8 +130,82 @@ PRAGMA incremental_vacuum; COMMIT; } } {} -integrity_check incremental2-3.3 +integrity_check incrvacuum2-3.3 + +ifcapable wal { + # At one point, when a specific page was being extracted from the b-tree + # free-list (e.g. during an incremental-vacuum), all trunk pages that + # occurred before the specific page in the free-list trunk were being + # written to the journal or wal file. This is not necessary. Only the + # extracted page and the page that contains the pointer to it need to + # be journalled. + # + # This problem was fixed by [d03d63d77e] (just before 3.7.6 release). + # + # This test case builds a database containing many free pages. Then runs + # "PRAGMA incremental_vacuum(1)" until the db contains zero free pages. + # Each "PRAGMA incremental_vacuum(1)" should modify at most 4 pages. The + # worst case is when a trunk page is removed from the end of the db file. + # In this case pages written are: + # + # 1. The previous trunk page (that contains a pointer to the recycled + # trunk page), and + # 2. The leaf page transformed into a trunk page to replace the recycled + # page, and + # 3. The trunk page that contained a pointer to the leaf page used + # in (2), and + # 4. Page 1. Page 1 is always updated, even in WAL mode, since it contains + # the "number of free-list pages" field. + # + db close + forcedelete test.db + sqlite3 db test.db + + do_execsql_test 4.1 { + PRAGMA page_size = 512; + PRAGMA auto_vacuum = 2; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(400)); + INSERT INTO t1 SELECT * FROM t1; -- 2 + INSERT INTO t1 SELECT * FROM t1; -- 4 + INSERT INTO t1 SELECT * FROM t1; -- 8 + INSERT INTO t1 SELECT * FROM t1; -- 16 + INSERT INTO t1 SELECT * FROM t1; -- 32 + INSERT INTO t1 SELECT * FROM t1; -- 128 + INSERT INTO t1 SELECT * FROM t1; -- 256 + INSERT INTO t1 SELECT * FROM t1; -- 512 + INSERT INTO t1 SELECT * FROM t1; -- 1024 + INSERT INTO t1 SELECT * FROM t1; -- 2048 + INSERT INTO t1 SELECT * FROM t1; -- 4096 + INSERT INTO t1 SELECT * FROM t1; -- 8192 + DELETE FROM t1 WHERE oid>512; + DELETE FROM t1; + } + + do_test 4.2 { + execsql { + PRAGMA journal_mode = WAL; + PRAGMA incremental_vacuum(1); + PRAGMA wal_checkpoint; + } + file size test.db-wal + } {1640} + + do_test 4.3 { + db close + sqlite3 db test.db + set maxsz 0 + while {[file size test.db] > [expr 512*3]} { + execsql { PRAGMA journal_mode = WAL } + execsql { PRAGMA wal_checkpoint } + execsql { PRAGMA incremental_vacuum(1) } + set newsz [file size test.db-wal] + if {$newsz>$maxsz} {set maxsz $newsz} + } + set maxsz + } {2176} +} finish_test DELETED test/omitunique.test Index: test/omitunique.test ================================================================== --- test/omitunique.test +++ /dev/null @@ -1,170 +0,0 @@ -# 2011 March 10 -# -# 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. The -# focus of this file is testing the SQLITE_OMIT_UNIQUE_ENFORCEMENT -# compiler option. -# - -set testdir [file dirname $argv0] -source $testdir/tester.tcl - -set uniq_enforced 1 -ifcapable !unique_enforcement { - set uniq_enforced 0 -} - -# table with UNIQUE keyword on column -do_test omitunique-1.1 { - catchsql { CREATE TABLE t1(a TEXT UNIQUE); } -} {0 {}} - -# table with UNIQUE clause on column -do_test omitunique-1.2 { - catchsql { CREATE TABLE t2(a TEXT, UNIQUE(a)); } -} {0 {}} - -# table with UNIQUE index on column -do_test omitunique-1.3 { - catchsql { - CREATE TABLE t3(a TEXT); - CREATE UNIQUE INDEX t3a ON t3(a); - } -} {0 {}} - -# table with regular index on column -do_test omitunique-1.4 { - catchsql { - CREATE TABLE t4(a TEXT); - CREATE INDEX t4a ON t4(a); - } -} {0 {}} - -# table with no index on column -do_test omitunique-1.5 { - catchsql { CREATE TABLE t5(a TEXT); } -} {0 {}} - -# run our tests using several table/index forms -foreach {j tbl uniq cnt qp_est stat_enforce stat_omit } { -1 {t1} 1 1 1 {2 1} {9 9} -2 {t2} 1 1 1 {2 1} {9 9} -3 {t3} 1 1 1 {2 1} {9 9} -4 {t4} 0 9 10 {9 9} {9 9} -5 {t5} 0 9 100000 9 9 -} { - - do_test omitunique-2.0.$j.1 { - catchsql [ subst {INSERT INTO $tbl (a) VALUES('abc'); }] - } {0 {}} - do_test omitunique-2.0.$j.2 { - catchsql [ subst {INSERT INTO $tbl (a) VALUES('123'); }] - } {0 {}} - - # check various INSERT commands - foreach {i cmd err} { - 1 {INSERT} 1 - 2 {INSERT OR IGNORE} 0 - 3 {INSERT OR REPLACE} 0 - 4 {REPLACE} 0 - 5 {INSERT OR FAIL} 1 - 6 {INSERT OR ABORT} 1 - 7 {INSERT OR ROLLBACK} 1 - } { - - ifcapable explain { - set x [execsql [ subst { EXPLAIN $cmd INTO $tbl (a) VALUES('abc'); }]] - ifcapable unique_enforcement { - do_test omitunique-2.1.$j.$i.1 { - regexp { IsUnique } $x - } $uniq - } - ifcapable !unique_enforcement { - do_test omitunique-2.1.$j.$i.1 { - regexp { IsUnique } $x - } {0} - } - } - - if { $uniq_enforced==0 || $uniq==0 || $err==0 } { - set msg {0 {}} - } { - set msg {1 {column a is not unique}} - } - do_test omitunique-2.1.$j.$i.3 { - catchsql [ subst {$cmd INTO $tbl (a) VALUES('abc'); }] - } $msg - - } - # end foreach cmd - - # check UPDATE command - ifcapable explain { - set x [execsql [ subst { EXPLAIN UPDATE $tbl SET a='abc'; }]] - ifcapable unique_enforcement { - do_test omitunique-2.2.$j.1 { - regexp { IsUnique } $x - } $uniq - } - ifcapable !unique_enforcement { - do_test omitunique-2.2.$j.1 { - regexp { IsUnique } $x - } {0} - } - } - if { $uniq_enforced==0 || $uniq==0 } { - set msg {0 {}} - } { - set msg {1 {column a is not unique}} - } - do_test omitunique-2.2.$j.3 { - catchsql [ subst { UPDATE $tbl SET a='abc'; }] - } $msg - - # check record counts - do_test omitunique-2.3.$j { - execsql [ subst { SELECT count(*) FROM $tbl WHERE a='abc'; }] - } $cnt - - # make sure the query planner row estimate not affected because of omit enforcement - ifcapable explain { - do_test omitunique-2.4.$j { - set x [ execsql [ subst { EXPLAIN QUERY PLAN SELECT count(*) FROM $tbl WHERE a='abc'; }]] - set y [ subst {~$qp_est row} ] - regexp $y $x - } {1} - } - - # make sure we omit extra OP_Next opcodes when the UNIQUE constraints - # mean there will only be a single pass through the code - ifcapable explain { - set x [execsql [ subst { EXPLAIN SELECT * FROM $tbl WHERE a='abc'; }]] - do_test omitunique-2.5.$j { - if { [ regexp { Next } $x ] } { expr { 0 } } { expr { 1 } } - } $uniq - } - - # make sure analyze index stats correct - ifcapable analyze { - if { $uniq_enforced==0 } { - set msg [ list $stat_omit ] - } { - set msg [ list $stat_enforce ] - } - do_test omitunique-2.6.$j { - execsql [ subst { ANALYZE $tbl; } ] - execsql [ subst { SELECT stat FROM sqlite_stat1 WHERE tbl='$tbl'; } ] - } $msg - } - -} -# end foreach tbl - -finish_test Index: test/releasetest.tcl ================================================================== --- test/releasetest.tcl +++ test/releasetest.tcl @@ -58,10 +58,15 @@ array set ::Configs { "Default" { -O2 } + "Ftrapv" { + -O2 -ftrapv + -DSQLITE_MAX_ATTACHED=55 + -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 + } "Unlock-Notify" { -O2 -DSQLITE_ENABLE_UNLOCK_NOTIFY -DSQLITE_THREADSAFE -DSQLITE_TCL_DEFAULT_FULLMUTEX=1 @@ -142,10 +147,11 @@ -DSQLITE_DEBUG=1 -DSQLITE_PREFER_PROXY_LOCKING=1 } "Extra-Robustness" { -DSQLITE_ENABLE_OVERSIZE_CELL_CHECK=1 + -DSQLITE_MAX_ATTACHED=62 } } array set ::Platforms { Linux-x86_64 { @@ -153,10 +159,11 @@ "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" "Update-Delete-Limit" test "Debug-One" test "Extra-Robustness" test "Device-Two" test + "Ftrapv" test "Default" "threadtest test" "Device-One" fulltest } Linux-i686 { "Unlock-Notify" "QUICKTEST_INCLUDE=notify2.test test" Index: test/syscall.test ================================================================== --- test/syscall.test +++ test/syscall.test @@ -237,19 +237,38 @@ do_test 8.1 { sqlite3 db test.db file_control_chunksize_test db main 4096 file size test.db } {0} - foreach {tn hint size} { 1 1000 4096 2 1000 4096 3 3000 4096 4 4096 4096 5 4197 8192 } { do_test 8.2.$tn { + file_control_sizehint_test db main $hint + file size test.db + } $size +} + +do_test 8.3 { + db close + forcedelete test.db test.db2 + sqlite3 db test.db + file_control_chunksize_test db main 16 + file size test.db +} {0} +foreach {tn hint size} { + 1 5 16 + 2 13 16 + 3 45 48 + 4 48 48 + 5 49 64 +} { + do_test 8.4.$tn { file_control_sizehint_test db main $hint file size test.db } $size } Index: test/thread1.test ================================================================== --- test/thread1.test +++ test/thread1.test @@ -17,14 +17,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl # Skip this whole file if the thread testing code is not enabled # -ifcapable !mutex { - finish_test - return -} +if {[run_thread_tests]==0} { finish_test ; return } if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} { finish_test return } Index: test/thread2.test ================================================================== --- test/thread2.test +++ test/thread2.test @@ -15,15 +15,11 @@ set testdir [file dirname $argv0] source $testdir/tester.tcl -ifcapable !mutex { - finish_test - return -} - +if {[run_thread_tests]==0} { finish_test ; return } # Skip this whole file if the thread testing code is not enabled # if {[llength [info command thread_step]]==0 || [sqlite3 -has-codec]} { finish_test Index: test/thread_common.tcl ================================================================== --- test/thread_common.tcl +++ test/thread_common.tcl @@ -87,10 +87,13 @@ # proc run_thread_tests {{print_warning 0}} { ifcapable !mutex { set zProblem "SQLite build is not threadsafe" } + ifcapable mutex_noop { + set zProblem "SQLite build uses SQLITE_MUTEX_NOOP" + } if {[info commands sqlthread] eq ""} { set zProblem "SQLite build is not threadsafe" } if {![info exists ::tcl_platform(threaded)]} { set zProblem "Linked against a non-threadsafe Tcl build" Index: test/wal.test ================================================================== --- test/wal.test +++ test/wal.test @@ -1532,30 +1532,31 @@ INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; INSERT INTO t1 SELECT * FROM t1; } {wal} - do_execsql_test 24.2 { - DELETE FROM t1; - PRAGMA wal_checkpoint; - } {0 109 109} - do_test 24.3 { + do_test 24.2 { + execsql { + DELETE FROM t1; + PRAGMA wal_checkpoint; + } db close sqlite3 db test.db file exists test.db-wal } 0 - do_test 24.4 { + do_test 24.3 { file size test.db } [expr 84 * 1024] - do_test 24.5 { + do_test 24.4 { execsql { + PRAGMA cache_size = 200; PRAGMA incremental_vacuum; PRAGMA wal_checkpoint; } file size test.db } [expr 3 * 1024] - do_test 24.6 { + do_test 24.5 { file size test.db-wal } 2128 } db close Index: tool/omittest.tcl ================================================================== --- tool/omittest.tcl +++ tool/omittest.tcl @@ -6,11 +6,11 @@ set ::USAGE_MESSAGE { This Tcl script is used to test the various compile time options available for omitting code (the SQLITE_OMIT_xxx options). It should be invoked as follows: -