Index: ext/rtree/rtree.c ================================================================== --- ext/rtree/rtree.c +++ ext/rtree/rtree.c @@ -80,10 +80,11 @@ #endif #include #include #include +#include /* The following macro is used to suppress compiler warnings. */ #ifndef UNUSED_PARAMETER # define UNUSED_PARAMETER(x) (void)(x) @@ -417,10 +418,27 @@ */ #ifndef SQLITE_AMALGAMATION # define testcase(X) #endif +/* +** Make sure that the compiler intrinsics we desire are enabled when +** compiling with an appropriate version of MSVC unless prevented by +** the SQLITE_DISABLE_INTRINSIC define. +*/ +#if !defined(SQLITE_DISABLE_INTRINSIC) +# if defined(_MSC_VER) && _MSC_VER>=1400 +# if !defined(_WIN32_WCE) +# include +# pragma intrinsic(_byteswap_ulong) +# pragma intrinsic(_byteswap_uint64) +# else +# include +# endif +# endif +#endif + /* ** Macros to determine whether the machine is big or little endian, ** and whether or not that determination is run-time or compile-time. ** ** For best performance, an attempt is made to guess at the byte-order Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -5945,11 +5945,11 @@ ** shows that the page 'nearby' is somewhere on the free-list, then ** the entire-list will be searched for that page. */ #ifndef SQLITE_OMIT_AUTOVACUUM if( eMode==BTALLOC_EXACT ){ - if( ALWAYS(nearby<=mxPage) ){ + if( nearby<=mxPage ){ u8 eType; assert( nearby>0 ); assert( pBt->autoVacuum ); rc = ptrmapGet(pBt, nearby, &eType, 0); if( rc ) return rc; @@ -6241,11 +6241,11 @@ assert( sqlite3_mutex_held(pBt->mutex) ); assert( CORRUPT_DB || iPage>1 ); assert( !pMemPage || pMemPage->pgno==iPage ); - if( iPage<2 || NEVER(iPage>pBt->nPage) ){ + if( iPage<2 || iPage>pBt->nPage ){ return SQLITE_CORRUPT_BKPT; } if( pMemPage ){ pPage = pMemPage; sqlite3PagerRef(pPage->pDbPage); Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -2842,11 +2842,11 @@ ** erasing iTable (this can happen with an auto-vacuum database). */ static void destroyRootPage(Parse *pParse, int iTable, int iDb){ Vdbe *v = sqlite3GetVdbe(pParse); int r1 = sqlite3GetTempReg(pParse); - if( NEVER(iTable<2) ) return; + if( iTable<2 ) sqlite3ErrorMsg(pParse, "corrupt schema"); sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); sqlite3MayAbort(pParse); #ifndef SQLITE_OMIT_AUTOVACUUM /* OP_Destroy stores an in integer r1. If this integer ** is non-zero, then it is the root page number of a table moved to Index: src/shell.c.in ================================================================== --- src/shell.c.in +++ src/shell.c.in @@ -3049,29 +3049,42 @@ */ static void exec_prepared_stmt_columnar( ShellState *p, /* Pointer to ShellState */ sqlite3_stmt *pStmt /* Statment to run */ ){ - int nRow = 0; + sqlite3_int64 nRow = 0; int nColumn = 0; char **azData = 0; - char *zMsg = 0; + sqlite3_int64 nAlloc = 0; const char *z; int rc; - int i, j, nTotal, w, n; + sqlite3_int64 i, nData; + int j, nTotal, w, n; const char *colSep = 0; const char *rowSep = 0; - rc = sqlite3_get_table(p->db, sqlite3_sql(pStmt), - &azData, &nRow, &nColumn, &zMsg); - if( rc ){ - utf8_printf(p->out, "ERROR: %s\n", zMsg); - sqlite3_free(zMsg); - sqlite3_free_table(azData); - return; - } - if( nRow==0 || nColumn==0 ) goto columnar_end; + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ) return; + nColumn = sqlite3_column_count(pStmt); + nAlloc = nColumn*4; + azData = sqlite3_malloc64( nAlloc*sizeof(char*) ); + if( azData==0 ) shell_out_of_memory(); + for(i=0; i= nAlloc ){ + nAlloc *= 2; + azData = sqlite3_realloc64(azData, nAlloc*sizeof(char*)); + if( azData==0 ) shell_out_of_memory(); + } + nRow++; + for(i=0; ip->nWidth ){ p->colWidth = realloc(p->colWidth, nColumn*2*sizeof(int)); if( p->colWidth==0 ) shell_out_of_memory(); for(i=p->nWidth; icolWidth[i] = 0; p->nWidth = nColumn; @@ -3177,11 +3190,13 @@ } columnar_end: if( seenInterrupt ){ utf8_printf(p->out, "Interrupt\n"); } - sqlite3_free_table(azData); + nData = (nRow+1)*nColumn; + for(i=0; ip1; p2 = pOp->p2; #ifdef SQLITE_DEBUG if( aPermute ){ int k, mx = 0; - for(k=0; kmx ) mx = aPermute[k]; + for(k=0; k(u32)mx ) mx = aPermute[k]; assert( p1>0 && p1+mx<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+mx<=(p->nMem+1 - p->nCursor)+1 ); }else{ assert( p1>0 && p1+n<=(p->nMem+1 - p->nCursor)+1 ); assert( p2>0 && p2+n<=(p->nMem+1 - p->nCursor)+1 ); @@ -2621,11 +2621,11 @@ assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pDest = &aMem[pOp->p3]; memAboutToChange(p, pDest); assert( pC!=0 ); - assert( p2nField ); + assert( p2<(u32)pC->nField ); aOffset = pC->aOffset; assert( pC->eCurType!=CURTYPE_VTAB ); assert( pC->eCurType!=CURTYPE_PSEUDO || pC->nullRow ); assert( pC->eCurType!=CURTYPE_SORTER ); @@ -3810,11 +3810,11 @@ }else{ wrFlag = 0; } if( pOp->p5 & OPFLAG_P2ISREG ){ assert( p2>0 ); - assert( p2<=(p->nMem+1 - p->nCursor) ); + assert( p2<=(u32)(p->nMem+1 - p->nCursor) ); assert( pOp->opcode==OP_OpenWrite ); pIn2 = &aMem[p2]; assert( memIsValid(pIn2) ); assert( (pIn2->flags & MEM_Int)!=0 ); sqlite3VdbeMemIntegerify(pIn2); @@ -6243,11 +6243,11 @@ assert( p->bIsReader ); nRoot = pOp->p2; aRoot = pOp->p4.ai; assert( nRoot>0 ); - assert( aRoot[0]==nRoot ); + assert( aRoot[0]==(Pgno)nRoot ); assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) ); pnErr = &aMem[pOp->p3]; assert( (pnErr->flags & MEM_Int)!=0 ); assert( (pnErr->flags & (MEM_Str|MEM_Blob))==0 ); pIn1 = &aMem[pOp->p1]; Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -1280,11 +1280,38 @@ } } pWal->apWiData[iPg] = aShare; nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0); nHdr32 = nHdr / sizeof(u32); +#ifndef SQLITE_SAFER_WALINDEX_RECOVERY + /* Memcpy() should work fine here, on all reasonable implementations. + ** Technically, memcpy() might change the destination to some + ** intermediate value before setting to the final value, and that might + ** cause a concurrent reader to malfunction. Memcpy() is allowed to + ** do that, according to the spec, but no memcpy() implementation that + ** we know of actually does that, which is why we say that memcpy() + ** is safe for this. Memcpy() is certainly a lot faster. + */ memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr); +#else + /* In the event that some platform is found for which memcpy() + ** changes the destination to some intermediate value before + ** setting the final value, this alternative copy routine is + ** provided. + */ + { + int i; + for(i=nHdr32; ipDbFd, SQLITE_FCNTL_CKPT_START, 0); rc = sqlite3OsFileSize(pWal->pDbFd, &nSize); if( rc==SQLITE_OK && nSizepDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq); + if( (nSize+(i64)pWal->hdr.mxFrame*szPage)pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq); + } } + } - /* Iterate through the contents of the WAL, copying data to the db file */ while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ i64 iOffset; assert( walFramePgno(pWal, iFrame)==iDbpage ); Index: test/corruptL.test ================================================================== --- test/corruptL.test +++ test/corruptL.test @@ -1291,7 +1291,43 @@ do_catchsql_test 16.1 { PRAGMA writable_schema = ON; INSERT INTO t1(rowid, w, x, y, z) VALUES(5, 10, 11, 10, NULL); } {1 {database disk image is malformed}} + +#------------------------------------------------------------------------- +# Test that corruption is reported from within a checkpoint if the +# expected final size of the database (according to the last commit +# frame in the wal file) is greater than the combined initial sizes +# of the database and wal file. +# +reset_db +do_execsql_test 17.0 { + CREATE TABLE t1(o INTEGER PRIMARY KEY, t UNIQUE); + INSERT INTO t1(t) VALUES(randomblob(123)); + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + INSERT INTO t1(t) SELECT randomblob(123) FROM t1; + + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(-1, 'b'); +} {wal} + +do_test 17.1 { + set fd [open test.db r+] + chan truncate $fd 2048 + file size test.db +} {2048} + +do_catchsql_test 17.2 { + PRAGMA wal_checkpoint +} {1 {database disk image is malformed}} + +do_test 17.3 { + close $fd +} {} + finish_test Index: test/fuzzdata8.db ================================================================== --- test/fuzzdata8.db +++ test/fuzzdata8.db cannot compute difference between binary files Index: test/speedtest1.c ================================================================== --- test/speedtest1.c +++ test/speedtest1.c @@ -6,10 +6,11 @@ static const char zHelp[] = "Usage: %s [--options] DATABASE\n" "Options:\n" " --autovacuum Enable AUTOVACUUM mode\n" " --cachesize N Set the cache size to N\n" + " --checkpoint Run PRAGMA wal_checkpoint after each test case\n" " --exclusive Enable locking_mode=EXCLUSIVE\n" " --explain Like --sqlonly but with added EXPLAIN keywords\n" " --heap SZ MIN Memory allocator uses SZ bytes & min allocation MIN\n" " --incrvacuum Enable incremenatal vacuum mode\n" " --journal M Set the journal_mode to M\n" @@ -89,10 +90,11 @@ int bVerify; /* Try to verify that results are correct */ int bMemShrink; /* Call sqlite3_db_release_memory() often */ int eTemp; /* 0: no TEMP. 9: always TEMP. */ int szTest; /* Scale factor for test iterations */ int nRepeat; /* Repeat selects this many times */ + int doCheckpoint; /* Run PRAGMA wal_checkpoint after each trans */ const char *zWR; /* Might be WITHOUT ROWID */ const char *zNN; /* Might be NOT NULL */ const char *zPK; /* Might be UNIQUE or PRIMARY KEY */ unsigned int x, y; /* Pseudo-random number generator state */ u64 nResByte; /* Total number of result bytes */ @@ -388,13 +390,17 @@ g.iStart = speedtest1_timestamp(); g.x = 0xad131d0b; g.y = 0x44f9eac8; } +/* Forward reference */ +void speedtest1_exec(const char*,...); + /* Complete a test case */ void speedtest1_end_test(void){ sqlite3_int64 iElapseTime = speedtest1_timestamp() - g.iStart; + if( g.doCheckpoint ) speedtest1_exec("PRAGMA wal_checkpoint;"); if( !g.bSqlOnly ){ g.iTotal += iElapseTime; printf("%4d.%03ds\n", (int)(iElapseTime/1000), (int)(iElapseTime%1000)); } if( g.pStmt ){ @@ -2183,10 +2189,12 @@ if( i>=argc-1 ) fatal_error("missing argument on %s\n", argv[i]); i++; cacheSize = integerValue(argv[i]); }else if( strcmp(z,"exclusive")==0 ){ doExclusive = 1; + }else if( strcmp(z,"checkpoint")==0 ){ + g.doCheckpoint = 1; }else if( strcmp(z,"explain")==0 ){ g.bSqlOnly = 1; g.bExplain = 1; }else if( strcmp(z,"heap")==0 ){ if( i>=argc-2 ) fatal_error("missing arguments on %s\n", argv[i]); Index: tool/speed-check.sh ================================================================== --- tool/speed-check.sh +++ tool/speed-check.sh @@ -91,10 +91,13 @@ shift; SIZE=$1 ;; --cachesize) shift; SPEEDTEST_OPTS="$SPEEDTEST_OPTS --cachesize $1" ;; + --checkpoint) + SPEEDTEST_OPTS="$SPEEDTEST_OPTS --checkpoint" + ;; --explain) doExplain=1 ;; --vdbeprofile) rm -f vdbe_profile.out