Index: src/btree.c ================================================================== --- src/btree.c +++ src/btree.c @@ -7,11 +7,11 @@ ** 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. ** ************************************************************************* -** $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 ** ** Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3: @@ -1622,12 +1622,12 @@ MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ Pgno iDbPage = pDbPage->pgno; 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", iDbPage, iFreePage, iPtrPage, eType)); rc = sqlite3pager_movepage(pPager, pDbPage->aData, iFreePage); @@ -1642,11 +1642,11 @@ ** ** If pDbPage is an overflow page, then the first 4 bytes may store a ** 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; } }else{ @@ -1662,22 +1662,24 @@ /* Fix the database pointer on page iPtrPage that pointed at iDbPage so ** 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 ){ - releasePage(pPtrPage); - return rc; - } - modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); - rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); - releasePage(pPtrPage); + 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; } /* Forward declaration required by autoVacuumCommit(). */ static int allocatePage(Btree *, MemPage **, Pgno *, Pgno); @@ -1721,10 +1723,11 @@ nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pgsz, origSize)+pgsz/5)/(pgsz/5); finSize = origSize - nFreeList - nPtrMap; 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 ** page number greater than finSize. If so, the auto-vacuum cannot ** proceed. This limitation will be fixed when root pages are automatically @@ -1736,10 +1739,11 @@ if( eType==PTRMAP_ROOTPAGE ){ TRACE(("AUTOVACUUM: Cannot proceed due to root-page on page %d\n", i)); 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 ** of pages on the free list). Loop through the pages that lie beyond ** this mark, and if they are not already on the free list, move them @@ -4285,10 +4289,11 @@ rc = getPage(pBt, pgnoRoot, &pRoot); if( rc!=SQLITE_OK ){ return rc; } rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + assert( eType!=PTRMAP_ROOTPAGE ); if( rc!=SQLITE_OK ){ releasePage(pRoot); return rc; } rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove); @@ -4406,33 +4411,94 @@ ** page 1) is never added to the freelist. ** ** 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; } /* Index: src/btree.h ================================================================== --- src/btree.h +++ src/btree.h @@ -11,11 +11,11 @@ ************************************************************************* ** This header file defines the interface that the sqlite B-Tree file ** 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_ /* TODO: This definition is just included so other modules compile. It @@ -70,11 +70,11 @@ */ #define BTREE_INTKEY 1 /* Table has only 64-bit signed integer keys */ #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); int sqlite3BtreeCursor( Index: src/build.c ================================================================== --- src/build.c +++ src/build.c @@ -21,11 +21,11 @@ ** BEGIN TRANSACTION ** COMMIT ** 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 /* @@ -1551,10 +1551,145 @@ DbClearProperty(db, idx, DB_UnresetViews); } #else # 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. */ @@ -1633,11 +1768,11 @@ { OP_Delete, 0, 0, 0}, { OP_Rewind, 0, ADDR(13), 0}, { OP_Goto, 0, ADDR(3), 0}, { OP_Next, 0, ADDR(3), 0}, /* 12 */ }; - Index *pIdx; + /* Index *pIdx; */ Trigger *pTrigger; sqlite3BeginWriteOperation(pParse, 0, pTab->iDb); /* Drop all triggers associated with the table being dropped. Code ** is generated to remove entries from sqlite_master and/or @@ -1659,17 +1794,20 @@ */ sqlite3OpenMasterTable(v, pTab->iDb); 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); exit_drop_table: @@ -2235,12 +2373,13 @@ sqlite3BeginWriteOperation(pParse, 0, pIndex->iDb); sqlite3OpenMasterTable(v, pIndex->iDb); 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); } exit_drop_index: sqlite3SrcListDelete(pName); Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -16,11 +16,11 @@ ** is separate from the database file. The pager also implements file ** locking to prevent two processes from writing the same database ** 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" #include "pager.h" #include @@ -2586,119 +2586,119 @@ ** to the journal then we can return right away. */ pPg->dirty = 1; if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){ pPager->dirtyCache = 1; - return SQLITE_OK; - } - - /* 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{ - 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); - 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); + }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); + } + }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{ + 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); + 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. */ if( pPager->dbSize<(int)pPg->pgno ){ Index: src/sqliteInt.h ================================================================== --- src/sqliteInt.h +++ src/sqliteInt.h @@ -9,11 +9,11 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* ** 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_ /* @@ -1430,7 +1430,8 @@ void sqlite3ValueSetStr(sqlite3_value*, int, const void *,u8, void(*)(void*)); void sqlite3ValueFree(sqlite3_value*); sqlite3_value *sqlite3ValueNew(); sqlite3_value *sqlite3GetTransientValue(sqlite3*db); extern const unsigned char sqlite3UpperToLower[]; +void sqlite3RootPageMoved(Db*, int, int); #endif Index: src/test3.c ================================================================== --- src/test3.c +++ src/test3.c @@ -11,11 +11,11 @@ ************************************************************************* ** Code for testing the btree.c module in SQLite. This code ** 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" #include "btree.h" #include "tcl.h" @@ -320,11 +320,11 @@ " ID TABLENUM\"", 0); return TCL_ERROR; } 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; } return TCL_OK; Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -41,11 +41,11 @@ ** documentation, headers files, or other derived files. The formatting ** of the code in this file is, therefore, important. See other comments ** 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" #include #include "vdbeInt.h" @@ -3608,11 +3608,20 @@ ** that is used to store tables create using CREATE TEMPORARY TABLE. ** ** 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; } /* Opcode: Clear P1 P2 * ** Index: test/autovacuum.test ================================================================== --- test/autovacuum.test +++ test/autovacuum.test @@ -9,11 +9,11 @@ # #*********************************************************************** # 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 # Return a string $len characters long. The returned string is $char repeated @@ -141,9 +141,8 @@ execsql { pragma integrity_check } } {ok} } - finish_test