diff --git a/manifest b/manifest index d4f99325ab..3539df3f2f 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C All\stests\spass\seven\sif\sOMIT_TRIGGER\sis\sdefined.\s(CVS\s2053) -D 2004-11-04T04:42:28 +C Support\sroot-page\sallocation/deallocation\sin\sauto-vacuum\sdatabases.\sStill\sa\sfew\sproblems.\s(CVS\s2054) +D 2004-11-04T14:30:05 F Makefile.in c4d2416860f472a1e3393714d0372074197565df F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -29,9 +29,9 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea -F src/btree.c 93163198e6fb666b92c893fba5edb3ef6f335c0f -F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029 -F src/build.c bb896c5f85ab749d17ae5d730235134c12c08033 +F src/btree.c a3e45d54eb1a81698f609693c22df382dfbf9151 +F src/btree.h 3166388fa58c5594d8064d38b43440d79da38fb6 +F src/build.c 89d1ace10837e61d11cf9818750d8782369ac3f5 F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad F src/delete.c 52980e594e69e80374fb928fe611d5f75ca4e390 F src/expr.c 3a43e508a3dc213703808bbcbb17633b88b57d17 @@ -52,7 +52,7 @@ F src/os_unix.c 5824b22ba41fe9d514ef9169aac1b5fde73af229 F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13 F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b -F src/pager.c 9ce238f9540eb56b21fef085dc038dffca75835b +F src/pager.c a43e2a392be51966129e9afb18b81551c9f222b8 F src/pager.h cbe4ba356d9dd3f30260f322b3dc77408164df14 F src/parse.y 4a27450611ed2b8c359078e04daf93c50b1d22dd F src/pragma.c 44e192eb5928157bdb015926f858a7c6e3ef6c98 @@ -61,12 +61,12 @@ F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3 F src/select.c 156990c636102bb6b8de85e7ff3396a62568476b F src/shell.c 55adda3cf3c1cc2f6c1919aac17b2318f9c2a96f F src/sqlite.h.in 4f97b5907acfd2a5068cb0cec9d5178816734db7 -F src/sqliteInt.h 84d5ca7b02793697641a74fb125fcee3995ea2ff +F src/sqliteInt.h 8b93c9d7b7343b9013ffb73cbd2cb6ea4f546c62 F src/table.c 25b3ff2b39b7d87e8d4a5da0713d68dfc06cbee9 F src/tclsqlite.c 0302e3f42f015d132d1291f3388c06e86c24a008 F src/test1.c df1d1ca2c40cafefb9a29860f072c4d0fee1a7b5 F src/test2.c b11fa244fff02190707dd0879987c37c75e61fc8 -F src/test3.c f423597e220b3d446a65c9cc0c49cb4eb00c0215 +F src/test3.c b6aece10ee51579d0f75d09137b3c94c80b3c278 F src/test4.c 7c6b9fc33dd1f3f93c7f1ee6e5e6d016afa6c1df F src/test5.c b001fa7f1b9e2dc5c2331de62fc641b5ab2bd7a1 F src/tokenize.c bf9de9689b3bb813d65784bf54472804bf9595e6 @@ -75,7 +75,7 @@ F src/update.c 7b17b281d600bf3e220b3c5718e0883442dee722 F src/utf.c f4f83acd73389090e32d6589d307fc55d794c7ed F src/util.c 005fdf2d008f3429d081766ad6098fdd86d8d8e6 F src/vacuum.c ecb4a2c6f1ac5cc9b394dc64d3bb14ca650c4f60 -F src/vdbe.c a156e1a2f324e5e11d82af3fbbf41df7a174c860 +F src/vdbe.c cf7eb35b5a649c11345c5f85ad7b1511253431cc F src/vdbe.h 067ca8d6750ba4f69a50284765e5883dee860181 F src/vdbeInt.h 6017100adff362b8dfa37a69e3f1431f084bfa5b F src/vdbeapi.c 3965bf4678ae32c05f73550c1b5be3268f9f3006 @@ -87,7 +87,7 @@ F test/attach.test ff7fc16b4518a448fed47dfb3694bf57f522d552 F test/attach2.test f7795123d3051ace1672b6d23973da6435de3745 F test/attach3.test 742c932d7130e0e699a5d9f265cb831e0a824633 F test/auth.test 1cc252d9e7b3bdc1314199cbf3a0d3c5ed026c21 -F test/autovacuum.test 9211914801ad35ad8f0fc15711b12461850ef2ac +F test/autovacuum.test a5b11269daac313bea6694b04473fdd0e16e439a F test/bigfile.test d3744a8821ce9abb8697f2826a3e3d22b719e89f F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747 F test/bind.test fa74f98417cd313f28272acff832a8a7d04a0916 @@ -252,7 +252,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25 F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9 F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0 F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c -P da045bd183335a112f9a6c805c12efe12d0a25ca -R 3020d6f48bce04fcef7f5133e21d3db2 -U drh -Z 625b1d515abab0cebf7c5f6337f4ff8c +P c33b3a613751057e8a46fdcd428b8448329d414d +R 904dc0805c4ad385b004eaf63c77188e +U danielk1977 +Z d7dc31bf4e5f4e3f4969266ba871dca0 diff --git a/manifest.uuid b/manifest.uuid index bfcf2e9e7a..780cdb4cdb 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -c33b3a613751057e8a46fdcd428b8448329d414d \ No newline at end of file +1da361fae82d420be63c53f8e3efaccac24f348a \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index 218ad74d43..a91b234f8f 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.203 2004/11/04 02:57:34 danielk1977 Exp $ +** $Id: btree.c,v 1.204 2004/11/04 14:30:05 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1624,8 +1624,8 @@ static int relocatePage( Pager *pPager = pBt->pPager; int rc; - assert( eType==PTRMAP_OVERFLOW2 - || eType==PTRMAP_OVERFLOW1 || eType==PTRMAP_BTREE ); + assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 || + eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ); /* Move page iDbPage from it's current location to page number iFreePage */ TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n", @@ -1644,7 +1644,7 @@ static int relocatePage( ** pointer to a subsequent overflow page. If this is the case, then ** the pointer map needs to be updated for the subsequent overflow page. */ - if( eType==PTRMAP_BTREE ){ + if( eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE ){ rc = setChildPtrmaps(pDbPage); if( rc!=SQLITE_OK ){ return rc; @@ -1664,18 +1664,20 @@ static int relocatePage( ** that it points at iFreePage. Also fix the pointer map entry for ** iPtrPage. */ - rc = getPage(pBt, iPtrPage, &pPtrPage); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = sqlite3pager_write(pPtrPage->aData); - if( rc!=SQLITE_OK ){ + if( eType!=PTRMAP_ROOTPAGE ){ + rc = getPage(pBt, iPtrPage, &pPtrPage); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlite3pager_write(pPtrPage->aData); + if( rc!=SQLITE_OK ){ + releasePage(pPtrPage); + return rc; + } + modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); + rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); releasePage(pPtrPage); - return rc; } - modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); - rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); - releasePage(pPtrPage); return rc; } @@ -1723,6 +1725,7 @@ static int autoVacuumCommit(Btree *pBt){ TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize)); +#if 0 /* Note: This is temporary code for use during development of auto-vacuum. ** ** Inspect the pointer map to make sure there are no root pages with a @@ -1738,6 +1741,7 @@ static int autoVacuumCommit(Btree *pBt){ return SQLITE_OK; } } +#endif /* Variable 'finSize' will be the size of the file in pages after ** the auto-vacuum has completed (the current file size minus the number @@ -4287,6 +4291,7 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + assert( eType!=PTRMAP_ROOTPAGE ); if( rc!=SQLITE_OK ){ releasePage(pRoot); return rc; @@ -4408,29 +4413,90 @@ int sqlite3BtreeClearTable(Btree *pBt, int iTable){ ** This routine will fail with SQLITE_LOCKED if there are any open ** cursors on the table. */ -int sqlite3BtreeDropTable(Btree *pBt, int iTable){ +int sqlite3BtreeDropTable(Btree *pBt, int iTable, int *piMoved){ int rc; - MemPage *pPage; + MemPage *pPage = 0; BtCursor *pCur; -/* TODO: Disallow schema modifications if there are open cursors */ + if( pBt->inTrans!=TRANS_WRITE ){ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } + +/* TODO: Disallow schema modifications if there are open cursors */ for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ if( pCur->pgnoRoot==(Pgno)iTable ){ return SQLITE_LOCKED; /* Cannot drop a table that has a cursor */ } } + rc = getPage(pBt, (Pgno)iTable, &pPage); if( rc ) return rc; rc = sqlite3BtreeClearTable(pBt, iTable); if( rc ) return rc; + + if( piMoved ) *piMoved = 0; + if( iTable>1 ){ +#ifdef SQLITE_OMIT_AUTOVACUUM rc = freePage(pPage); + releasePage(pPage); +#else + if( pBt->autoVacuum ){ + Pgno maxRootPgno; + rc = sqlite3BtreeGetMeta(pBt, 4, &maxRootPgno); + if( rc!=SQLITE_OK ){ + releasePage(pPage); + return rc; + } + + if( iTable==maxRootPgno ){ + /* If the table being dropped is the table with the largest root-page + ** number in the database, put the root page on the free list. + */ + rc = freePage(pPage); + releasePage(pPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + /* The table being dropped does not have the largest root-page + ** number in the database. So move the page that does into the + ** gap left by the deleted root-page. + */ + MemPage *pMove; + releasePage(pPage); + rc = getPage(pBt, maxRootPgno, &pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = relocatePage(pBt, pMove, PTRMAP_ROOTPAGE, 0, iTable); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = getPage(pBt, maxRootPgno, &pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = freePage(pMove); + releasePage(pMove); + if( rc!=SQLITE_OK ){ + return rc; + } + *piMoved = maxRootPgno; + } + + rc = sqlite3BtreeUpdateMeta(pBt, 4, maxRootPgno-1); + }else{ + rc = freePage(pPage); + releasePage(pPage); + } +#endif }else{ + /* If sqlite3BtreeDropTable was called on page 1. */ zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); + releasePage(pPage); } - releasePage(pPage); return rc; } diff --git a/src/btree.h b/src/btree.h index 3f247b77ae..8df9871f21 100644 --- a/src/btree.h +++ b/src/btree.h @@ -13,7 +13,7 @@ ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. ** -** @(#) $Id: btree.h,v 1.58 2004/07/23 00:01:39 drh Exp $ +** @(#) $Id: btree.h,v 1.59 2004/11/04 14:30:05 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -72,7 +72,7 @@ int sqlite3BtreeCopyFile(Btree *, Btree *); #define BTREE_ZERODATA 2 /* Table has keys only - no data */ #define BTREE_LEAFDATA 4 /* Data stored in leaves only. Implies INTKEY */ -int sqlite3BtreeDropTable(Btree*, int); +int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int); int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); diff --git a/src/build.c b/src/build.c index 250ca28338..027ae58c07 100644 --- a/src/build.c +++ b/src/build.c @@ -23,7 +23,7 @@ ** ROLLBACK ** PRAGMA ** -** $Id: build.c,v 1.258 2004/10/31 02:22:49 drh Exp $ +** $Id: build.c,v 1.259 2004/11/04 14:30:05 danielk1977 Exp $ */ #include "sqliteInt.h" #include @@ -1554,6 +1554,141 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ # define sqliteViewResetAll(A,B) #endif /* SQLITE_OMIT_VIEW */ +/* +** This function is called by the VDBE to adjust the internal schema +** used by SQLite when the btree layer moves a table root page. The +** root-page of a table or index in database iDb has changed from iFrom +** to iTo. +*/ +#ifndef SQLITE_OMIT_AUTOVACUUM +void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ + HashElem *pElem; + + for(pElem=sqliteHashFirst(&pDb->tblHash); pElem; pElem=sqliteHashNext(pElem)){ + Table *pTab = sqliteHashData(pElem); + if( pTab->tnum==iFrom ){ + pTab->tnum = iTo; + return; + } + } + for(pElem=sqliteHashFirst(&pDb->idxHash); pElem; pElem=sqliteHashNext(pElem)){ + Index *pIdx = sqliteHashData(pElem); + if( pIdx->tnum==iFrom ){ + pIdx->tnum = iTo; + return; + } + } + assert(0); +} +#endif + +/* +** Write code to erase the table with root-page iTable from database iDb. +** Also write code to modify the sqlite_master table and internal schema +** if a root-page of another table is moved by the btree-layer whilst +** erasing iTable (this can happen with an auto-vacuum database). +*/ +static void destroyRootPage(Vdbe *v, int iTable, int iDb){ +#ifndef SQLITE_OMIT_AUTOVACUUM + int base; +#endif + sqlite3VdbeAddOp(v, OP_Destroy, iTable, iDb); +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If SQLITE_OMIT_AUTOVACUUM is not defined, then OP_Destroy pushes + ** an integer onto the stack. If this integer is non-zero, then it is + ** the root page number of a table moved to location iTable. The + ** following writes VDBE code to modify the sqlite_master table to + ** reflect this. It is assumed that cursor number 0 is a write-cursor + ** opened on the sqlite_master table. + */ + static const VdbeOpList updateMaster[] = { + /* If the Op_Destroy pushed a 0 onto the stack, then skip the following + ** code. sqlite_master does not need updating in this case. + */ + { OP_Dup, 0, 0, 0}, + { OP_Integer, 0, 0, 0}, + { OP_Eq, 0, ADDR(17), 0}, + + /* Search for the sqlite_master row containing root-page iTable. */ + { OP_Rewind, 0, ADDR(8), 0}, + { OP_Dup, 0, 0, 0}, /* 4 */ + { OP_Column, 0, 3, 0}, /* 5 */ + { OP_Eq, 0, ADDR(9), 0}, + { OP_Next, 0, ADDR(4), 0}, + { OP_Halt, SQLITE_CORRUPT, OE_Fail, 0}, /* 8 */ + { OP_Recno, 0, 0, 0}, /* 9 */ + + /* Cursor 0 now points at the row that will be updated. The top of + ** the stack is the rowid of that row. The next value on the stack is + ** the new value for the root-page field. + */ + { OP_Column, 0, 0, 0}, /* 10 */ + { OP_Column, 0, 1, 0}, + { OP_Column, 0, 2, 0}, + { OP_Integer, 4, 0, 0}, /* 13 */ + { OP_Column, 0, 4, 0}, + { OP_MakeRecord, 5, 0, 0}, + { OP_PutIntKey, 0, 0, 0} /* 16 */ + }; + + base = sqlite3VdbeAddOpList(v, ArraySize(updateMaster), updateMaster); + sqlite3VdbeChangeP1(v, base+13, iTable); +#endif +} + +/* +** Write VDBE code to erase table pTab and all associated indices on disk. +** Code to update the sqlite_master tables and internal schema definitions +** in case a root-page belonging to another table is moved by the btree layer +** is also added (this can happen with an auto-vacuum database). +*/ +static void destroyTable(Vdbe *v, Table *pTab){ +#ifdef SQLITE_OMIT_AUTOVACUUM + destroyRootPage(v, pTab->tnum, pTab->iDb); + for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + destroyRootPage(v, pIdx->tnum, pIdx->iDb); + } +#else + /* If the database may be auto-vacuum capable (if SQLITE_OMIT_AUTOVACUUM + ** is not defined), then it is important to call OP_Destroy on the + ** table and index root-pages in order, starting with the numerically + ** largest root-page number. This guarantees that none of the root-pages + ** to be destroyed is relocated by an earlier OP_Destroy. i.e. if the + ** following were coded: + ** + ** OP_Destroy 4 0 + ** ... + ** OP_Destroy 5 0 + ** + ** and root page 5 happened to be the largest root-page number in the + ** database, then root page 5 would be moved to page 4 by the + ** "OP_Destroy 4 0" opcode. The subsequent "OP_Destroy 5 0" would hit + ** a free-list page. + */ + int iTab = pTab->tnum; + int iDestroyed = 0; + + while( 1 ){ + Index *pIdx; + int iLargest = 0; + + if( iDestroyed==0 || iTabpIndex; pIdx; pIdx=pIdx->pNext){ + int iIdx = pIdx->tnum; + assert( pIdx->iDb==pTab->iDb ); + if( (iDestroyed==0 || (iIdxiLargest ){ + iLargest = iIdx; + } + } + if( iLargest==0 ) return; + destroyRootPage(v, iLargest, pTab->iDb); + iDestroyed = iLargest; + } +#endif +} + /* ** This routine is called to do the work of a DROP TABLE statement. ** pName is the name of the table to be dropped. @@ -1635,7 +1770,7 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView){ { OP_Goto, 0, ADDR(3), 0}, { OP_Next, 0, ADDR(3), 0}, /* 12 */ }; - Index *pIdx; + /* Index *pIdx; */ Trigger *pTrigger; sqlite3BeginWriteOperation(pParse, 0, pTab->iDb); @@ -1661,13 +1796,16 @@ void sqlite3DropTable(Parse *pParse, SrcList *pName, int isView){ base = sqlite3VdbeAddOpList(v, ArraySize(dropTable), dropTable); sqlite3VdbeChangeP3(v, base+1, pTab->zName, 0); sqlite3ChangeCookie(db, v, pTab->iDb); - sqlite3VdbeAddOp(v, OP_Close, 0, 0); if( !isView ){ +/* sqlite3VdbeAddOp(v, OP_Destroy, pTab->tnum, pTab->iDb); for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ sqlite3VdbeAddOp(v, OP_Destroy, pIdx->tnum, pIdx->iDb); } +*/ + destroyTable(v, pTab); } + sqlite3VdbeAddOp(v, OP_Close, 0, 0); sqlite3VdbeOp3(v, OP_DropTable, pTab->iDb, 0, pTab->zName, 0); } sqliteViewResetAll(db, iDb); @@ -2237,8 +2375,9 @@ void sqlite3DropIndex(Parse *pParse, SrcList *pName){ base = sqlite3VdbeAddOpList(v, ArraySize(dropIndex), dropIndex); sqlite3VdbeChangeP3(v, base+1, pIndex->zName, 0); sqlite3ChangeCookie(db, v, pIndex->iDb); + /* sqlite3VdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb); */ + destroyRootPage(v, pIndex->tnum, pIndex->iDb); sqlite3VdbeAddOp(v, OP_Close, 0, 0); - sqlite3VdbeAddOp(v, OP_Destroy, pIndex->tnum, pIndex->iDb); sqlite3VdbeOp3(v, OP_DropIndex, pIndex->iDb, 0, pIndex->zName, 0); } diff --git a/src/pager.c b/src/pager.c index fcbd407c35..0a5249ed6e 100644 --- a/src/pager.c +++ b/src/pager.c @@ -18,7 +18,7 @@ ** file simultaneously, or one process from reading the database while ** another is writing. ** -** @(#) $Id: pager.c,v 1.171 2004/11/03 08:44:06 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.172 2004/11/04 14:30:05 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -2588,115 +2588,115 @@ int sqlite3pager_write(void *pData){ pPg->dirty = 1; if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){ pPager->dirtyCache = 1; - return SQLITE_OK; - } + }else{ - /* If we get this far, it means that the page needs to be - ** written to the transaction journal or the ckeckpoint journal - ** or both. - ** - ** First check to see that the transaction journal exists and - ** create it if it does not. - */ - assert( pPager->state!=PAGER_UNLOCK ); - rc = sqlite3pager_begin(pData, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - assert( pPager->state>=PAGER_RESERVED ); - if( !pPager->journalOpen && pPager->useJournal ){ - rc = pager_open_journal(pPager); - if( rc!=SQLITE_OK ) return rc; - } - assert( pPager->journalOpen || !pPager->useJournal ); - pPager->dirtyCache = 1; - - /* The transaction journal now exists and we have a RESERVED or an - ** EXCLUSIVE lock on the main database file. Write the current page to - ** the transaction journal if it is not there already. - */ - if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){ - if( (int)pPg->pgno <= pPager->origDbSize ){ - int szPg; - u32 saved; - if( MEMDB ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - TRACE3("JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - assert( pHist->pOrig==0 ); - pHist->pOrig = sqliteMallocRaw( pPager->pageSize ); - if( pHist->pOrig ){ - memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize); + /* If we get this far, it means that the page needs to be + ** written to the transaction journal or the ckeckpoint journal + ** or both. + ** + ** First check to see that the transaction journal exists and + ** create it if it does not. + */ + assert( pPager->state!=PAGER_UNLOCK ); + rc = sqlite3pager_begin(pData, 0); + if( rc!=SQLITE_OK ){ + return rc; + } + assert( pPager->state>=PAGER_RESERVED ); + if( !pPager->journalOpen && pPager->useJournal ){ + rc = pager_open_journal(pPager); + if( rc!=SQLITE_OK ) return rc; + } + assert( pPager->journalOpen || !pPager->useJournal ); + pPager->dirtyCache = 1; + + /* The transaction journal now exists and we have a RESERVED or an + ** EXCLUSIVE lock on the main database file. Write the current page to + ** the transaction journal if it is not there already. + */ + if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){ + if( (int)pPg->pgno <= pPager->origDbSize ){ + int szPg; + u32 saved; + if( MEMDB ){ + PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); + TRACE3("JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); + assert( pHist->pOrig==0 ); + pHist->pOrig = sqliteMallocRaw( pPager->pageSize ); + if( pHist->pOrig ){ + memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize); + } + }else{ + u32 cksum; + CODEC(pPager, pData, pPg->pgno, 7); + cksum = pager_cksum(pPager, pPg->pgno, pData); + saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager); + store32bits(cksum, pPg, pPager->pageSize); + szPg = pPager->pageSize+8; + store32bits(pPg->pgno, pPg, -4); + rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); + pPager->journalOff += szPg; + TRACE4("JOURNAL %d page %d needSync=%d\n", + pPager->fd.h, pPg->pgno, pPg->needSync); + CODEC(pPager, pData, pPg->pgno, 0); + *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved; + if( rc!=SQLITE_OK ){ + sqlite3pager_rollback(pPager); + pPager->errMask |= PAGER_ERR_FULL; + return rc; + } + pPager->nRec++; + assert( pPager->aInJournal!=0 ); + pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); + pPg->needSync = !pPager->noSync; + if( pPager->stmtInUse ){ + pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); + page_add_to_stmt_list(pPg); + } } }else{ - u32 cksum; - CODEC(pPager, pData, pPg->pgno, 7); - cksum = pager_cksum(pPager, pPg->pgno, pData); - saved = *(u32*)PGHDR_TO_EXTRA(pPg, pPager); - store32bits(cksum, pPg, pPager->pageSize); - szPg = pPager->pageSize+8; - store32bits(pPg->pgno, pPg, -4); - rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg); - pPager->journalOff += szPg; - TRACE4("JOURNAL %d page %d needSync=%d\n", + pPg->needSync = !pPager->journalStarted && !pPager->noSync; + TRACE4("APPEND %d page %d needSync=%d\n", pPager->fd.h, pPg->pgno, pPg->needSync); + } + if( pPg->needSync ){ + pPager->needSync = 1; + } + pPg->inJournal = 1; + } + + /* If the statement journal is open and the page is not in it, + ** then write the current page to the statement journal. Note that + ** the statement journal format differs from the standard journal format + ** in that it omits the checksums and the header. + */ + if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){ + assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); + if( MEMDB ){ + PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); + assert( pHist->pStmt==0 ); + pHist->pStmt = sqliteMallocRaw( pPager->pageSize ); + if( pHist->pStmt ){ + memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize); + } + TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); + }else{ + store32bits(pPg->pgno, pPg, -4); + CODEC(pPager, pData, pPg->pgno, 7); + rc = sqlite3OsWrite(&pPager->stfd,((char*)pData)-4, pPager->pageSize+4); + TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); CODEC(pPager, pData, pPg->pgno, 0); - *(u32*)PGHDR_TO_EXTRA(pPg, pPager) = saved; if( rc!=SQLITE_OK ){ sqlite3pager_rollback(pPager); pPager->errMask |= PAGER_ERR_FULL; return rc; } - pPager->nRec++; - assert( pPager->aInJournal!=0 ); - pPager->aInJournal[pPg->pgno/8] |= 1<<(pPg->pgno&7); - pPg->needSync = !pPager->noSync; - if( pPager->stmtInUse ){ - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - page_add_to_stmt_list(pPg); - } + pPager->stmtNRec++; + assert( pPager->aInStmt!=0 ); + pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); } - }else{ - pPg->needSync = !pPager->journalStarted && !pPager->noSync; - TRACE4("APPEND %d page %d needSync=%d\n", - pPager->fd.h, pPg->pgno, pPg->needSync); + page_add_to_stmt_list(pPg); } - if( pPg->needSync ){ - pPager->needSync = 1; - } - pPg->inJournal = 1; - } - - /* If the statement journal is open and the page is not in it, - ** then write the current page to the statement journal. Note that - ** the statement journal format differs from the standard journal format - ** in that it omits the checksums and the header. - */ - if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){ - assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ); - if( MEMDB ){ - PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager); - assert( pHist->pStmt==0 ); - pHist->pStmt = sqliteMallocRaw( pPager->pageSize ); - if( pHist->pStmt ){ - memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize); - } - TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - }else{ - store32bits(pPg->pgno, pPg, -4); - CODEC(pPager, pData, pPg->pgno, 7); - rc = sqlite3OsWrite(&pPager->stfd, ((char*)pData)-4, pPager->pageSize+4); - TRACE3("STMT-JOURNAL %d page %d\n", pPager->fd.h, pPg->pgno); - CODEC(pPager, pData, pPg->pgno, 0); - if( rc!=SQLITE_OK ){ - sqlite3pager_rollback(pPager); - pPager->errMask |= PAGER_ERR_FULL; - return rc; - } - pPager->stmtNRec++; - assert( pPager->aInStmt!=0 ); - pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7); - } - page_add_to_stmt_list(pPg); } /* Update the database size and return. diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6e2bf58904..9244476c82 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.329 2004/10/31 02:22:49 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.330 2004/11/04 14:30:05 danielk1977 Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -1432,5 +1432,6 @@ void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(); sqlite3_value *sqlite3GetTransientValue(sqlite3*db); extern const unsigned char sqlite3UpperToLower[]; +void sqlite3RootPageMoved(Db*, int, int); #endif diff --git a/src/test3.c b/src/test3.c index cc62b8c23a..fc443e63f9 100644 --- a/src/test3.c +++ b/src/test3.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test3.c,v 1.53 2004/10/31 02:22:49 drh Exp $ +** $Id: test3.c,v 1.54 2004/11/04 14:30:06 danielk1977 Exp $ */ #include "sqliteInt.h" #include "pager.h" @@ -322,7 +322,7 @@ static int btree_drop_table( } pBt = sqlite3TextToPtr(argv[1]); if( Tcl_GetInt(interp, argv[2], &iTable) ) return TCL_ERROR; - rc = sqlite3BtreeDropTable(pBt, iTable); + rc = sqlite3BtreeDropTable(pBt, iTable, 0); if( rc!=SQLITE_OK ){ Tcl_AppendResult(interp, errorName(rc), 0); return TCL_ERROR; diff --git a/src/vdbe.c b/src/vdbe.c index 95e6bd38a6..9e4ef43b4e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.422 2004/11/03 16:27:01 drh Exp $ +** $Id: vdbe.c,v 1.423 2004/11/04 14:30:06 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -3610,7 +3610,16 @@ case OP_IdxIsNull: { ** See also: Clear */ case OP_Destroy: { - rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1); + int iMoved; + rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1, &iMoved); +#ifndef SQLITE_OMIT_AUTOVACUUM + pTos++; + pTos->flags = MEM_Int; + pTos->i = iMoved; + if( iMoved!=0 ){ + sqlite3RootPageMoved(&db->aDb[pOp->p2], iMoved, pOp->p1); + } +#endif break; } diff --git a/test/autovacuum.test b/test/autovacuum.test index 9af1ab6522..6b80173b5d 100644 --- a/test/autovacuum.test +++ b/test/autovacuum.test @@ -11,7 +11,7 @@ # This file implements regression tests for SQLite library. The # focus of this file is testing the SELECT statement. # -# $Id: autovacuum.test,v 1.5 2004/11/04 02:57:35 danielk1977 Exp $ +# $Id: autovacuum.test,v 1.6 2004/11/04 14:30:06 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -144,6 +144,5 @@ for {set i 5} {$i < 15} {incr i} { } {ok} } - finish_test