diff --git a/manifest b/manifest index 61c6228494..4756873a5c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sthe\sorigin\sAPIs\sso\sthat\sthey\scorrectly\shandle\sviews\sand\ssubqueries\sthat\scannot\sbe\sflattened.\s(CVS\s3072) -D 2006-02-10T07:07:14 +C Work\saround\sthe\scase\swhere\sthe\spending-byte\spage\sis\salso\sa\sa\spointer-map\spage.\sTicket\s#1667.\s(CVS\s3073) +D 2006-02-10T08:24:21 F Makefile.in 5d8dff443383918b700e495de42ec65bc1c8865b F Makefile.linux-gcc 74ba0eadf88748a9ce3fd03d2a3ede2e6715baec F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -34,7 +34,7 @@ F src/alter.c 451b34fc4eb2475ca76a2e86b21e1030a9428091 F src/analyze.c 7d2b7ab9a9c2fd6e55700f69064dfdd3e36d7a8a F src/attach.c d73a3505de3fb9e373d0a158978116c4212031d0 F src/auth.c 9ae84d2d94eb96195e04515715e08e85963e96c2 -F src/btree.c 66112ae6a5caab384010344b514e98b346f550c0 +F src/btree.c 579594eff6b200ee6e653f4da192d33c50413d56 F src/btree.h 5663c4f43e8521546ccebc8fc95acb013b8f3184 F src/build.c 11798f165cdcf3b46f4b10b2ba3853b8c370eac2 F src/callback.c 1bf497306c32229114f826707054df7ebe10abf2 @@ -257,6 +257,7 @@ F test/tkt1536.test 83ff7a7b6e248016f8d682d4f7a4ae114070d466 F test/tkt1537.test e3a14332de9770be8ff14bd15c19a49cbec10808 F test/tkt1567.test 18023cc3626a365f0118e17b66decedec93b1a6f F test/tkt1644.test 8926ebffd19950a7cce22734a06d8282a69c3a20 +F test/tkt1667.test c7faac5050b45d81d0b9f6294e4570b588106e19 F test/trace.test 75ffc1b992c780d054748a656e3e7fd674f18567 F test/trans.test b25eae982d156867eac338409905fd4ca589b7f8 F test/trigger1.test 0c1d44882dba5c92e4efee4dd133cc979f0b1860 @@ -351,7 +352,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513 -P a6c30be214bb575f9ecfa299b7a597d21e3d3aca -R 41b2424fdff209e4871bfdf191620183 +P 5e8611e13de08d704cea6c9c4466c3af842a7a1a +R 0a57b51faa251c36e1169634bc62a68d U danielk1977 -Z d531a3addcf6506678bf78af10fce292 +Z 733d67f3b503a31d4190158ec7a34bd0 diff --git a/manifest.uuid b/manifest.uuid index c4d6312f4c..531ea696e7 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -5e8611e13de08d704cea6c9c4466c3af842a7a1a \ No newline at end of file +5ea87fbbe09279e70d3a22b8cd9994befffeac53 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index ea6339f0a6..6633ae9383 100644 --- a/src/btree.c +++ b/src/btree.c @@ -9,7 +9,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.312 2006/02/10 02:27:42 danielk1977 Exp $ +** $Id: btree.c,v 1.313 2006/02/10 08:24:21 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -756,9 +756,19 @@ static void unlockAllTables(Btree *p){ ** used to test if pgno is a pointer-map page. PTRMAP_ISPAGE implements ** this test. */ -#define PTRMAP_PAGENO(pgsz, pgno) (((pgno-2)/(pgsz/5+1))*(pgsz/5+1)+2) -#define PTRMAP_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5) -#define PTRMAP_ISPAGE(pgsz, pgno) (PTRMAP_PAGENO(pgsz,pgno)==pgno) +#define PTRMAP_PAGENO(pBt, pgno) ptrmapPageno(pBt, pgno) +#define PTRMAP_PTROFFSET(pBt, pgno) (5*(pgno-ptrmapPageno(pBt, pgno)-1)) +#define PTRMAP_ISPAGE(pBt, pgno) (PTRMAP_PAGENO((pBt),(pgno))==(pgno)) + +static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){ + int nPagesPerMapPage = (pBt->usableSize/5)+1; + int iPtrMap = (pgno-2)/nPagesPerMapPage; + int ret = (iPtrMap*nPagesPerMapPage) + 2; + if( ret==PENDING_BYTE_PAGE(pBt) ){ + ret++; + } + return ret; +} /* ** The pointer map is a lookup table that identifies the parent page for @@ -810,16 +820,19 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){ int offset; /* Offset in pointer map page */ int rc; + /* The master-journal page number must never be used as a pointer map page */ + assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) ); + assert( pBt->autoVacuum ); if( key==0 ){ return SQLITE_CORRUPT_BKPT; } - iPtrmap = PTRMAP_PAGENO(pBt->usableSize, key); + iPtrmap = PTRMAP_PAGENO(pBt, key); rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap); if( rc!=SQLITE_OK ){ return rc; } - offset = PTRMAP_PTROFFSET(pBt->usableSize, key); + offset = PTRMAP_PTROFFSET(pBt, key); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); @@ -847,13 +860,13 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ int offset; /* Offset of entry in pointer map */ int rc; - iPtrmap = PTRMAP_PAGENO(pBt->usableSize, key); + iPtrmap = PTRMAP_PAGENO(pBt, key); rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap); if( rc!=0 ){ return rc; } - offset = PTRMAP_PTROFFSET(pBt->usableSize, key); + offset = PTRMAP_PTROFFSET(pBt, key); if( pEType ) *pEType = pPtrmap[offset]; if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); @@ -2344,7 +2357,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){ #endif assert( pBt->autoVacuum ); - if( PTRMAP_ISPAGE(pgsz, sqlite3pager_pagecount(pPager)) ){ + if( PTRMAP_ISPAGE(pBt, sqlite3pager_pagecount(pPager)) ){ return SQLITE_CORRUPT_BKPT; } @@ -2357,14 +2370,26 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){ return SQLITE_OK; } + /* This block figures out how many pages there are in the database + ** now (variable origSize), and how many there will be after the + ** truncation (variable finSize). + ** + ** The final size is the original size, less the number of free pages + ** in the database, less any pointer-map pages that will no longer + ** be required, less 1 if the pending-byte page was part of the database + ** but is not after the truncation. + **/ origSize = sqlite3pager_pagecount(pPager); - nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pgsz, origSize)+pgsz/5)/(pgsz/5); + if( origSize==PENDING_BYTE_PAGE(pBt) ){ + origSize--; + } + nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pBt, origSize)+pgsz/5)/(pgsz/5); finSize = origSize - nFreeList - nPtrMap; - if( origSize>=PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){ + if( origSize>PENDING_BYTE_PAGE(pBt) && finSize<=PENDING_BYTE_PAGE(pBt) ){ + finSize--; + } + while( PTRMAP_ISPAGE(pBt, finSize) || finSize==PENDING_BYTE_PAGE(pBt) ){ finSize--; - if( PTRMAP_ISPAGE(pBt->usableSize, finSize) ){ - finSize--; - } } TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize)); @@ -2376,7 +2401,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){ */ for( iDbPage=finSize+1; iDbPage<=origSize; iDbPage++ ){ /* If iDbPage is a pointer map page, or the pending-byte page, skip it. */ - if( PTRMAP_ISPAGE(pgsz, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){ + if( PTRMAP_ISPAGE(pBt, iDbPage) || iDbPage==PENDING_BYTE_PAGE(pBt) ){ continue; } @@ -2434,6 +2459,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *nTrunc){ put4byte(&pBt->pPage1->aData[36], 0); if( rc!=SQLITE_OK ) goto autovacuum_out; *nTrunc = finSize; + assert( finSize!=PENDING_BYTE_PAGE(pBt) ); autovacuum_out: assert( nRef==*sqlite3pager_stats(pPager) ); @@ -3806,7 +3832,7 @@ static int allocatePage( *pPgno = sqlite3pager_pagecount(pBt->pPager) + 1; #ifndef SQLITE_OMIT_AUTOVACUUM - if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt->usableSize, *pPgno) ){ + if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt, *pPgno) ){ /* If *pPgno refers to a pointer-map page, allocate two new pages ** at the end of the file instead of one. The first allocated page ** becomes a new pointer-map page, the second is used by the caller. @@ -5440,7 +5466,7 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){ /* The new root-page may not be allocated on a pointer-map page, or the ** PENDING_BYTE page. */ - if( pgnoRoot==PTRMAP_PAGENO(pBt->usableSize, pgnoRoot) || + if( pgnoRoot==PTRMAP_PAGENO(pBt, pgnoRoot) || pgnoRoot==PENDING_BYTE_PAGE(pBt) ){ pgnoRoot++; } @@ -5713,7 +5739,7 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){ if( maxRootPgno==PENDING_BYTE_PAGE(pBt) ){ maxRootPgno--; } - if( maxRootPgno==PTRMAP_PAGENO(pBt->usableSize, maxRootPgno) ){ + if( maxRootPgno==PTRMAP_PAGENO(pBt, maxRootPgno) ){ maxRootPgno--; } assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); @@ -6390,11 +6416,11 @@ char *sqlite3BtreeIntegrityCheck(Btree *p, int *aRoot, int nRoot){ ** references to pointer-map pages. */ if( sCheck.anRef[i]==0 && - (PTRMAP_PAGENO(pBt->usableSize, i)!=i || !pBt->autoVacuum) ){ + (PTRMAP_PAGENO(pBt, i)!=i || !pBt->autoVacuum) ){ checkAppendMsg(&sCheck, 0, "Page %d is never used", i); } if( sCheck.anRef[i]!=0 && - (PTRMAP_PAGENO(pBt->usableSize, i)==i && pBt->autoVacuum) ){ + (PTRMAP_PAGENO(pBt, i)==i && pBt->autoVacuum) ){ checkAppendMsg(&sCheck, 0, "Pointer map page %d is referenced", i); } #endif @@ -6522,19 +6548,21 @@ int sqlite3BtreeIsInStmt(Btree *p){ ** the write-transaction for this database file is to delete the journal. */ int sqlite3BtreeSync(Btree *p, const char *zMaster){ + int rc = SQLITE_OK; if( p->inTrans==TRANS_WRITE ){ BtShared *pBt = p->pBt; -#ifndef SQLITE_OMIT_AUTOVACUUM Pgno nTrunc = 0; +#ifndef SQLITE_OMIT_AUTOVACUUM if( pBt->autoVacuum ){ - int rc = autoVacuumCommit(pBt, &nTrunc); - if( rc!=SQLITE_OK ) return rc; + rc = autoVacuumCommit(pBt, &nTrunc); + if( rc!=SQLITE_OK ){ + return rc; + } } - return sqlite3pager_sync(pBt->pPager, zMaster, nTrunc); #endif - return sqlite3pager_sync(pBt->pPager, zMaster, 0); + rc = sqlite3pager_sync(pBt->pPager, zMaster, nTrunc); } - return SQLITE_OK; + return rc; } /* diff --git a/test/tkt1667.test b/test/tkt1667.test new file mode 100644 index 0000000000..e1fd2fbcb3 --- /dev/null +++ b/test/tkt1667.test @@ -0,0 +1,85 @@ +# 2006 February 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. +# +# This file implements tests to verify that ticket #1667 has been +# fixed. +# +# +# $Id: tkt1667.test,v 1.1 2006/02/10 08:24:21 danielk1977 Exp $ + +set testdir [file dirname $argv0] +source $testdir/tester.tcl + +ifcapable !autovacuum { + finish_test + return +} + +db close +file delete -force test.db test.db-journal + +# Set the pending byte offset such that the page it is on is +# the first autovacuum pointer map page in the file (assume a page +# size of 1024). + +set first_ptrmap_page [expr 1024/5 + 3] +set sqlite_pending_byte [expr 1024 * ($first_ptrmap_page-1)] + +sqlite db test.db + +do_test tkt1667-1 { + execsql { + PRAGMA auto_vacuum = 1; + BEGIN; + CREATE TABLE t1(a, b); + } + for {set i 0} {$i < 500} {incr i} { + execsql { + INSERT INTO t1 VALUES($i, randstr(1000, 2000)) + } + } + execsql { + COMMIT; + } +} {} +for {set i 0} {$i < 500} {incr i} { + do_test tkt1667-2.$i.1 { + execsql { + DELETE FROM t1 WHERE a = $i; + } + } {} + integrity_check tkt1667-2.$i.2 +} + +do_test tkt1667-3 { + execsql { + BEGIN; + } + for {set i 0} {$i < 500} {incr i} { + execsql { + INSERT INTO t1 VALUES($i, randstr(1000, 2000)) + } + } + execsql { + COMMIT; + } +} {} +do_test tkt1667-4.1 { + execsql { + DELETE FROM t1; + } +} {} +integrity_check tkt1667-4.2 + +finish_test + +