diff --git a/manifest b/manifest index beb337c43e..43c7861a5c 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Update\stests\sto\swork\seven\sif\ssome\sfeatures\sof\sthe\slibrary\sare\sdisabled.\s(CVS\s2050) -D 2004-11-03T16:27:01 +C Add\ssupport\sfor\stable\sallocation\s(not\sdeallocation)\sin\sauto-vacuum\sdatabases.\s(CVS\s2051) +D 2004-11-04T02:57:34 F Makefile.in c4d2416860f472a1e3393714d0372074197565df F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457 F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1 @@ -29,7 +29,7 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689 F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea -F src/btree.c d7cc9fdb0fa91b1c0817a3352c3a45d1a1434685 +F src/btree.c 93163198e6fb666b92c893fba5edb3ef6f335c0f F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029 F src/build.c bb896c5f85ab749d17ae5d730235134c12c08033 F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad @@ -87,7 +87,7 @@ F test/attach.test 6ad560eb3e77751a4faecd77da09deac9e38cc41 F test/attach2.test f7795123d3051ace1672b6d23973da6435de3745 F test/attach3.test 6d060986ff004ebb89e1876a331d96c6bb62269e F test/auth.test 1cc252d9e7b3bdc1314199cbf3a0d3c5ed026c21 -F test/autovacuum.test 832bcbb0086b7a1a5af24f32399e02304f0056d4 +F test/autovacuum.test 9211914801ad35ad8f0fc15711b12461850ef2ac 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 a82980fd70285820c64b42393ef85a9e21addc5d -R 8b665ecae63deee85dcecde69c7e3c11 -U drh -Z 748c6cf073ccedb84a7443f14a915c73 +P b11fc9b3f3a2711f98e7e45724aa1d30081197f3 +R 242e2153cebd7e4c43dc16ab1542d7f2 +U danielk1977 +Z 7eb34cc29f203e9a41d2637315e55a85 diff --git a/manifest.uuid b/manifest.uuid index 66bd16c5bb..ad44c2c969 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -b11fc9b3f3a2711f98e7e45724aa1d30081197f3 \ No newline at end of file +571de52376f52999268ba5e0cd05c6c6eff1ebbf \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index fbb45d2018..218ad74d43 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.202 2004/11/03 11:37:08 danielk1977 Exp $ +** $Id: btree.c,v 1.203 2004/11/04 02:57:34 danielk1977 Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -1426,6 +1426,11 @@ static int newDatabase(Btree *pBt){ memset(&data[24], 0, 100-24); zeroPage(pP1, PTF_INTKEY|PTF_LEAF|PTF_LEAFDATA ); pBt->pageSizeFixed = 1; +#ifndef SQLITE_OMIT_AUTOVACUUM + if( pBt->autoVacuum ){ + put4byte(&data[36 + 4*4], 1); + } +#endif return SQLITE_OK; } @@ -1606,6 +1611,74 @@ static void modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ } } + +static int relocatePage( + Btree *pBt, + MemPage *pDbPage, + u8 eType, + Pgno iPtrPage, + Pgno iFreePage +){ + 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 ); + + /* 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); + if( rc!=SQLITE_OK ){ + return rc; + } + pDbPage->pgno = iFreePage; + + /* If pDbPage was a btree-page, then it may have child pages and/or cells + ** that point to overflow pages. The pointer map entries for all these + ** pages need to be changed. + ** + ** 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 ){ + rc = setChildPtrmaps(pDbPage); + if( rc!=SQLITE_OK ){ + return rc; + } + }else{ + Pgno nextOvfl = get4byte(pDbPage->aData); + if( nextOvfl!=0 ){ + assert( nextOvfl<=sqlite3pager_pagecount(pPager) ); + rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); + if( rc!=SQLITE_OK ){ + return rc; + } + } + } + + /* 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); + return rc; +} + /* Forward declaration required by autoVacuumCommit(). */ static int allocatePage(Btree *, MemPage **, Pgno *, Pgno); @@ -1624,10 +1697,8 @@ static int autoVacuumCommit(Btree *pBt){ u8 eType; int pgsz = pBt->pageSize; /* Page size for this database */ Pgno iDbPage; /* The database page to move */ - u8 *pDbPage = 0; /* "" */ MemPage *pDbMemPage = 0; /* "" */ Pgno iPtrPage; /* The page that contains a pointer to iDbPage */ - MemPage *pPtrMemPage = 0; /* "" */ Pgno iFreePage; /* The free-list page to move iDbPage to */ MemPage *pFreeMemPage = 0; /* "" */ @@ -1685,7 +1756,6 @@ static int autoVacuumCommit(Btree *pBt){ } rc = getPage(pBt, iDbPage, &pDbMemPage); if( rc!=SQLITE_OK ) goto autovacuum_out; - pDbPage = pDbMemPage->aData; /* Find the next page in the free-list that is not already at the end ** of the file. A page can be pulled off the free list using the @@ -1700,50 +1770,12 @@ static int autoVacuumCommit(Btree *pBt){ if( rc!=SQLITE_OK ) goto autovacuum_out; assert( iFreePage<=origSize ); }while( iFreePage>finSize ); - - /* 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)); releasePage(pFreeMemPage); pFreeMemPage = 0; - rc = sqlite3pager_movepage(pPager, pDbPage, iFreePage); - if( rc!=SQLITE_OK ) goto autovacuum_out; - pDbMemPage->pgno = iFreePage; - /* If pDbPage was a btree-page, then it may have child pages and/or cells - ** that point to overflow pages. The pointer map entries for all these - ** pages need to be changed. - ** - ** 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 ){ - rc = setChildPtrmaps(pDbMemPage); - if( rc!=SQLITE_OK ) goto autovacuum_out; - }else{ - Pgno nextOvfl = get4byte(pDbPage); - if( nextOvfl!=0 ){ - assert( nextOvfl<=origSize ); - rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); - if( rc!=SQLITE_OK ) goto autovacuum_out; - } - } + rc = relocatePage(pBt, pDbMemPage, eType, iPtrPage, iFreePage); releasePage(pDbMemPage); - pDbMemPage = 0; - - /* 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, &pPtrMemPage); if( rc!=SQLITE_OK ) goto autovacuum_out; - rc = sqlite3pager_write(pPtrMemPage->aData); - if( rc!=SQLITE_OK ) goto autovacuum_out; - modifyPagePointer(pPtrMemPage, iDbPage, iFreePage, eType); - rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); - if( rc!=SQLITE_OK ) goto autovacuum_out; - releasePage(pPtrMemPage); } /* The entire free-list has been swapped to the end of the file. So @@ -4198,6 +4230,7 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ MemPage *pRoot; Pgno pgnoRoot; int rc; +/* TODO: Disallow schema modifications if there are open cursors */ if( pBt->inTrans!=TRANS_WRITE ){ /* Must start a transaction first */ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; @@ -4205,18 +4238,90 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){ if( pBt->readOnly ){ return SQLITE_READONLY; } +#ifdef SQLITE_OMIT_AUTOVACUUM rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1); if( rc ) return rc; -#ifndef SQLITE_OMIT_AUTOVACUUM - /* Note: This is temporary code for use during development of auto-vacuum. - ** There should be no need for a pointer map entry for root-pages. - */ +#else if( pBt->autoVacuum ){ - rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0); - if( rc ){ - sqlite3pager_unref(pRoot->aData); + Pgno pgnoMove; /* Move a page here to make room for the root-page */ + MemPage *pPageMove; /* The page to move to. */ + + /* Run the auto-vacuum code to ensure the free-list is empty. This is + ** not really necessary, but it avoids complications in dealing with + ** a free-list in the code below. + ** TODO: This may need to be revisited. + */ + rc = autoVacuumCommit(pBt); + if( rc!=SQLITE_OK ) return rc; + + /* Read the value of meta[3] from the database to determine where the + ** root page of the new table should go. meta[3] is the largest root-page + ** created so far, so the new root-page is (meta[3]+1). + */ + rc = sqlite3BtreeGetMeta(pBt, 4, &pgnoRoot); + if( rc!=SQLITE_OK ) return rc; + pgnoRoot++; + + /* The new root-page may not be allocated on a pointer-map page. */ + if( pgnoRoot==PTRMAP_PAGENO(pBt->pageSize, pgnoRoot) ){ + pgnoRoot++; + } + assert( pgnoRoot>=3 ); + + /* Allocate a page. The page that currently resides at pgnoRoot will + ** be moved to the allocated page (unless the allocated page happens + ** to reside at pgnoRoot). + */ + rc = allocatePage(pBt, &pPageMove, &pgnoMove, 0); + if( rc!=SQLITE_OK ){ return rc; } + + if( pgnoMove!=pgnoRoot ){ + u8 eType; + Pgno iPtrPage; + + releasePage(pPageMove); + rc = getPage(pBt, pgnoRoot, &pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = ptrmapGet(pBt, pgnoRoot, &eType, &iPtrPage); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } + rc = relocatePage(pBt, pRoot, eType, iPtrPage, pgnoMove); + releasePage(pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = getPage(pBt, pgnoRoot, &pRoot); + if( rc!=SQLITE_OK ){ + return rc; + } + rc = sqlite3pager_write(pRoot->aData); + if( rc!=SQLITE_OK ){ + releasePage(pRoot); + return rc; + } + }else{ + pRoot = pPageMove; + } + + rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0); + if( rc ){ + releasePage(pRoot); + return rc; + } + rc = sqlite3BtreeUpdateMeta(pBt, 4, pgnoRoot); + if( rc ){ + releasePage(pRoot); + return rc; + } + }else{ + rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1); + if( rc ) return rc; } #endif assert( sqlite3pager_iswriteable(pRoot->aData) ); @@ -4307,6 +4412,7 @@ int sqlite3BtreeDropTable(Btree *pBt, int iTable){ int rc; MemPage *pPage; BtCursor *pCur; +/* TODO: Disallow schema modifications if there are open cursors */ if( pBt->inTrans!=TRANS_WRITE ){ return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR; } @@ -4351,7 +4457,9 @@ int sqlite3BtreeGetMeta(Btree *pBt, int idx, u32 *pMeta){ /* The current implementation is unable to handle writes to an autovacuumed ** database. So make such a database readonly. */ +#ifdef SQLITE_OMIT_AUTOVACUUM if( idx==4 && *pMeta>0 ) pBt->readOnly = 1; +#endif return SQLITE_OK; } diff --git a/test/autovacuum.test b/test/autovacuum.test index 341eb92ef0..9af1ab6522 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.4 2004/11/03 09:30:55 danielk1977 Exp $ +# $Id: autovacuum.test,v 1.5 2004/11/04 02:57:35 danielk1977 Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -112,5 +112,38 @@ foreach delete_order $delete_orders { } {4} } +# Tests autovacuum-2.* test that root pages are allocated correctly at +# the start of the file. +do_test autovacuum-2.1 { + for {set i 0} {$i<5} {incr i} { + execsql " + INSERT INTO av1 VALUES('[make_str abc 1000]') + " + } + file_pages +} {14} + +for {set i 5} {$i < 15} {incr i} { + set tablename "av$i" + + do_test autovacuum-2.$i.2 { + execsql " + CREATE TABLE $tablename (a); + SELECT rootpage FROM sqlite_master WHERE name = '$tablename'; + " + } $i + + do_test autovacuum-2.$i.3 { + file_pages + } [expr $i+10] + + do_test autovacuum-2.$i.4 { + execsql { + pragma integrity_check + } + } {ok} +} + + finish_test