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.170 2004/11/02 12:56:41 danielk1977 Exp $ +** @(#) $Id: pager.c,v 1.171 2004/11/03 08:44:06 danielk1977 Exp $ */ #include "sqliteInt.h" #include "os.h" #include "pager.h" #include @@ -1632,10 +1632,37 @@ /* ** Forward declaration */ static int syncJournal(Pager*); + +/* +** Unlink pPg from it's hash chain. Also set the page number to 0 to indicate +** that the page is not part of any hash chain. This is required because the +** sqlite3pager_movepage() routine can leave a page in the +** pNextFree/pPrevFree list that is not a part of any hash-chain. +*/ +static void unlinkHashChain(Pager *pPager, PgHdr *pPg){ + if( pPg->pgno==0 ){ + /* If the page number is zero, then this page is not in any hash chain. */ + return; + } + if( pPg->pNextHash ){ + pPg->pNextHash->pPrevHash = pPg->pPrevHash; + } + if( pPg->pPrevHash ){ + assert( pPager->aHash[pager_hash(pPg->pgno)]!=pPg ); + pPg->pPrevHash->pNextHash = pPg->pNextHash; + }else{ + int h = pager_hash(pPg->pgno); + assert( pPager->aHash[h]==pPg ); + pPager->aHash[h] = pPg->pNextHash; + } + + pPg->pgno = 0; + pPg->pNextHash = pPg->pPrevHash = 0; +} /* ** Unlink a page from the free list (the list of all pages where nRef==0) ** and from its hash collision chain. */ @@ -1663,10 +1690,12 @@ pPager->pLast = pPg->pPrevFree; } pPg->pNextFree = pPg->pPrevFree = 0; /* Unlink from the pgno hash table */ + unlinkHashChain(pPager, pPg); +/* if( pPg->pNextHash ){ pPg->pNextHash->pPrevHash = pPg->pPrevHash; } if( pPg->pPrevHash ){ pPg->pPrevHash->pNextHash = pPg->pNextHash; @@ -1674,10 +1703,11 @@ int h = pager_hash(pPg->pgno); assert( pPager->aHash[h]==pPg ); pPager->aHash[h] = pPg->pNextHash; } pPg->pNextHash = pPg->pPrevHash = 0; +*/ } /* ** This routine is used to truncate an in-memory database. Delete ** all pages whose pgno is larger than pPager->dbSize and is unreferenced. @@ -3213,10 +3243,11 @@ sync_exit: return rc; } #ifndef SQLITE_OMIT_AUTOVACUUM + /* ** Move the page identified by pData to location pgno in the file. ** ** There must be no references to the current page pgno. If current page ** pgno is not already in the rollback journal, it is not written there by @@ -3231,59 +3262,39 @@ ** illegal to call this routine if a statment transaction is active. */ int sqlite3pager_movepage(Pager *pPager, void *pData, Pgno pgno){ PgHdr *pPg = DATA_TO_PGHDR(pData); PgHdr *pPgOld; + int h; assert( !pPager->stmtInUse ); - /* assert( pPg->pNextFree==0 && pPg->pPrevFree==0 && pPg->nRef>0 ); */ assert( pPg->nRef>0 ); /* Unlink pPg from it's hash-chain */ - if( pPg->pNextHash ){ - pPg->pNextHash->pPrevHash = pPg->pPrevHash; - } - if( pPg->pPrevHash ){ - pPg->pPrevHash->pNextHash = pPg->pNextHash; - }else{ - int h = pager_hash(pPg->pgno); - assert( pPager->aHash[h]==pPg ); - pPager->aHash[h] = pPg->pNextHash; - } - - /* Change the page number for pPg */ - pPg->pgno = pgno; - - pPgOld = pager_lookup(pPager, pgno); - if( pPgOld ){ - /* Remove pPgOld from the page number hash-chain and insert pPg. */ - assert(pPgOld->nRef==0 && !pPgOld->pNextStmt && !pPgOld->pPrevStmt ); - if( pPgOld->pNextHash ){ - pPgOld->pNextHash->pPrevHash = pPg; - } - if( pPgOld->pPrevHash ){ - pPgOld->pPrevHash->pNextHash = pPg; - }else{ - int h = pager_hash(pgno); - assert( pPager->aHash[h]==pPgOld ); - pPager->aHash[h] = pPg; - } - pPgOld->pNextHash = pPgOld->pPrevHash = 0; - }else{ - /* Insert pPg into it's new hash-chain. */ - int h = pager_hash(pgno); - if( pPager->aHash[h] ){ - pPager->aHash[h]->pNextHash = pPg; - } - pPg->pNextHash = pPager->aHash[h]; - pPg->pPrevHash = 0; - } - - /* Don't write the old page when sqlite3pager_sync() is called. Do write - ** the new one. - */ - pPgOld->dirty = 0; + unlinkHashChain(pPager, pPg); + + /* If the cache contains a page with page-number pgno exists, remove it + ** from it's hash chain. + */ + pPgOld = pager_lookup(pPager, pgno); + if( pPgOld ){ + assert( pPgOld->nRef==0 ); + unlinkHashChain(pPager, pPgOld); + pPgOld->dirty = 0; + } + + /* Change the page number for pPg and insert it into the new hash-chain. */ + pPg->pgno = pgno; + h = pager_hash(pgno); + if( pPager->aHash[h] ){ + assert( pPager->aHash[h]->pPrevHash==0 ); + pPager->aHash[h]->pPrevHash = pPg; + } + pPg->pNextHash = pPager->aHash[h]; + pPager->aHash[h] = pPg; + pPg->pPrevHash = 0; + pPg->dirty = 1; pPager->dirtyCache = 1; return SQLITE_OK; }