mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Auto-vacuum bug: Deallocate pointer-map pages when shrinking a database file. (CVS 2048)
FossilOrigin-Name: bec6a65acaa8bfd8fe2cb475ba2e992a1993e4e7
This commit is contained in:
12
manifest
12
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Comment\sthe\sautovacuum.test\sscript.\sNo\scode\sor\stest-case\schanges.\s(CVS\s2047)
|
C Auto-vacuum\sbug:\sDeallocate\spointer-map\spages\swhen\sshrinking\sa\sdatabase\sfile.\s(CVS\s2048)
|
||||||
D 2004-11-03T09:30:55
|
D 2004-11-03T11:37:08
|
||||||
F Makefile.in c4d2416860f472a1e3393714d0372074197565df
|
F Makefile.in c4d2416860f472a1e3393714d0372074197565df
|
||||||
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
||||||
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
F README a01693e454a00cc117967e3f9fdab2d4d52e9bc1
|
||||||
@@ -29,7 +29,7 @@ F sqlite3.def dbaeb20c153e1d366e8f421b55a573f5dfc00863
|
|||||||
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
|
||||||
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
|
F src/attach.c e49d09dad9f5f9fb10b4b0c1be5a70ae4c45e689
|
||||||
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
|
F src/auth.c 3b81f2a42f48a62c2c9c9b0eda31a157c681edea
|
||||||
F src/btree.c e243f8927f827337ecd316da9f4a15790b7b33cf
|
F src/btree.c d7cc9fdb0fa91b1c0817a3352c3a45d1a1434685
|
||||||
F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029
|
F src/btree.h 94dfec0a1722d33359b23e7e310f2b64ffedf029
|
||||||
F src/build.c bb896c5f85ab749d17ae5d730235134c12c08033
|
F src/build.c bb896c5f85ab749d17ae5d730235134c12c08033
|
||||||
F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad
|
F src/date.c 34bdb0082db7ec2a83ef00063f7b44e61ee19dad
|
||||||
@@ -252,7 +252,7 @@ F www/tclsqlite.tcl 560ecd6a916b320e59f2917317398f3d59b7cc25
|
|||||||
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
|
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
|
||||||
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
F www/version3.tcl 092a01f5ef430d2c4acc0ae558d74c4bb89638a0
|
||||||
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
|
F www/whentouse.tcl fdacb0ba2d39831e8a6240d05a490026ad4c4e4c
|
||||||
P 719c1b79671c8cd7c5a6b5967ad4265b65e433d3
|
P 2eacd38620f7270467b16e9e2f350aa0b4869a10
|
||||||
R b36063b6d6cae6f63b6c4ea7219e3ded
|
R 1a71d8fcc37c235323656a96ea8a7620
|
||||||
U danielk1977
|
U danielk1977
|
||||||
Z 2b831eb943422ab301a0d60aaa718fbd
|
Z 59b70c0385282e17202d4be1d5800074
|
||||||
|
@@ -1 +1 @@
|
|||||||
2eacd38620f7270467b16e9e2f350aa0b4869a10
|
bec6a65acaa8bfd8fe2cb475ba2e992a1993e4e7
|
96
src/btree.c
96
src/btree.c
@@ -9,7 +9,7 @@
|
|||||||
** May you share freely, never taking more than you give.
|
** May you share freely, never taking more than you give.
|
||||||
**
|
**
|
||||||
*************************************************************************
|
*************************************************************************
|
||||||
** $Id: btree.c,v 1.201 2004/11/03 03:52:37 danielk1977 Exp $
|
** $Id: btree.c,v 1.202 2004/11/03 11:37:08 danielk1977 Exp $
|
||||||
**
|
**
|
||||||
** This file implements a external (disk-based) database using BTrees.
|
** This file implements a external (disk-based) database using BTrees.
|
||||||
** For a detailed discussion of BTrees, refer to
|
** For a detailed discussion of BTrees, refer to
|
||||||
@@ -413,6 +413,8 @@ static void put4byte(unsigned char *p, u32 v){
|
|||||||
#define PTRMAP_PAGENO(pgsz, pgno) (((pgno-2)/(pgsz/5+1))*(pgsz/5+1)+2)
|
#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_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5)
|
||||||
|
|
||||||
|
#define PTRMAP_ISPAGE(pgsz, pgno) (PTRMAP_PAGENO(pgsz,pgno)==pgno)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The pointer map is a lookup table that contains an entry for each database
|
** The pointer map is a lookup table that contains an entry for each database
|
||||||
** page in the file except for page 1. In this context 'database page' refers
|
** page in the file except for page 1. In this context 'database page' refers
|
||||||
@@ -1614,12 +1616,13 @@ static int allocatePage(Btree *, MemPage **, Pgno *, Pgno);
|
|||||||
static int autoVacuumCommit(Btree *pBt){
|
static int autoVacuumCommit(Btree *pBt){
|
||||||
Pager *pPager = pBt->pPager;
|
Pager *pPager = pBt->pPager;
|
||||||
Pgno nFreeList; /* Number of pages remaining on the free-list. */
|
Pgno nFreeList; /* Number of pages remaining on the free-list. */
|
||||||
Pgno origDbSize; /* Pages in the database file */
|
int nPtrMap; /* Number of pointer-map pages deallocated */
|
||||||
Pgno finDbSize; /* Pages in the database file after truncation */
|
Pgno origSize; /* Pages in the database file */
|
||||||
|
Pgno finSize; /* Pages in the database file after truncation */
|
||||||
int i; /* Counter variable */
|
int i; /* Counter variable */
|
||||||
int rc; /* Return code */
|
int rc; /* Return code */
|
||||||
u8 eType;
|
u8 eType;
|
||||||
|
int pgsz = pBt->pageSize; /* Page size for this database */
|
||||||
Pgno iDbPage; /* The database page to move */
|
Pgno iDbPage; /* The database page to move */
|
||||||
u8 *pDbPage = 0; /* "" */
|
u8 *pDbPage = 0; /* "" */
|
||||||
MemPage *pDbMemPage = 0; /* "" */
|
MemPage *pDbMemPage = 0; /* "" */
|
||||||
@@ -1633,28 +1636,30 @@ static int autoVacuumCommit(Btree *pBt){
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert( pBt->autoVacuum );
|
assert( pBt->autoVacuum );
|
||||||
|
assert( 0==PTRMAP_ISPAGE(pgsz, sqlite3pager_pagecount(pPager)) );
|
||||||
|
|
||||||
/* Figure out how many free-pages are in the database. If there are no
|
/* Figure out how many free-pages are in the database. If there are no
|
||||||
** free pages, then auto-vacuum is a no-op.
|
** free pages, then auto-vacuum is a no-op.
|
||||||
*/
|
*/
|
||||||
nFreeList = get4byte(&pBt->pPage1->aData[36]);
|
nFreeList = get4byte(&pBt->pPage1->aData[36]);
|
||||||
if( nFreeList==0 ) return SQLITE_OK;
|
if( nFreeList==0 ){
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: This does not calculate finDbSize correctly for the case where
|
origSize = sqlite3pager_pagecount(pPager);
|
||||||
** pointer-map pages must be deallocated.
|
nPtrMap = (nFreeList-origSize+PTRMAP_PAGENO(pgsz, origSize)+pgsz/5)/(pgsz/5);
|
||||||
*/
|
finSize = origSize - nFreeList - nPtrMap;
|
||||||
origDbSize = sqlite3pager_pagecount(pPager);
|
|
||||||
finDbSize = origDbSize - nFreeList;
|
TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origSize, finSize));
|
||||||
TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origDbSize, finDbSize));
|
|
||||||
|
|
||||||
/* Note: This is temporary code for use during development of auto-vacuum.
|
/* 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
|
** Inspect the pointer map to make sure there are no root pages with a
|
||||||
** page number greater than finDbSize. If so, the auto-vacuum cannot
|
** page number greater than finSize. If so, the auto-vacuum cannot
|
||||||
** proceed. This limitation will be fixed when root pages are automatically
|
** proceed. This limitation will be fixed when root pages are automatically
|
||||||
** allocated at the start of the database file.
|
** allocated at the start of the database file.
|
||||||
*/
|
*/
|
||||||
for( i=finDbSize+1; i<=origDbSize; i++ ){
|
for( i=finSize+1; i<=origSize; i++ ){
|
||||||
rc = ptrmapGet(pBt, i, &eType, 0);
|
rc = ptrmapGet(pBt, i, &eType, 0);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
if( eType==PTRMAP_ROOTPAGE ){
|
if( eType==PTRMAP_ROOTPAGE ){
|
||||||
@@ -1663,20 +1668,19 @@ static int autoVacuumCommit(Btree *pBt){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Variable 'finDbSize' will be the size of the file in pages after
|
/* Variable 'finSize' will be the size of the file in pages after
|
||||||
** the auto-vacuum has completed (the current file size minus the number
|
** 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
|
** 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
|
** this mark, and if they are not already on the free list, move them
|
||||||
** to a free page earlier in the file (somewhere before finDbSize).
|
** to a free page earlier in the file (somewhere before finSize).
|
||||||
*/
|
*/
|
||||||
for( iDbPage=finDbSize+1; iDbPage<=origDbSize; iDbPage++ ){
|
for( iDbPage=finSize+1; iDbPage<=origSize; iDbPage++ ){
|
||||||
rc = ptrmapGet(pBt, iDbPage, &eType, &iPtrPage);
|
rc = ptrmapGet(pBt, iDbPage, &eType, &iPtrPage);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
assert( eType!=PTRMAP_ROOTPAGE );
|
assert( eType!=PTRMAP_ROOTPAGE );
|
||||||
|
|
||||||
/* If iDbPage is a free or pointer map page, do not swap it. */
|
/* If iDbPage is a free or pointer map page, do not swap it. */
|
||||||
if( eType==PTRMAP_FREEPAGE ||
|
if( eType==PTRMAP_FREEPAGE || PTRMAP_ISPAGE(pgsz, iDbPage) ){
|
||||||
iDbPage==PTRMAP_PAGENO(pBt->pageSize, iDbPage) ){
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
rc = getPage(pBt, iDbPage, &pDbMemPage);
|
rc = getPage(pBt, iDbPage, &pDbMemPage);
|
||||||
@@ -1694,8 +1698,8 @@ static int autoVacuumCommit(Btree *pBt){
|
|||||||
}
|
}
|
||||||
rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0);
|
rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
assert( iFreePage<=origDbSize );
|
assert( iFreePage<=origSize );
|
||||||
}while( iFreePage>finDbSize );
|
}while( iFreePage>finSize );
|
||||||
|
|
||||||
/* Move page iDbPage from it's current location to page number iFreePage */
|
/* 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",
|
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
|
||||||
@@ -1720,7 +1724,7 @@ static int autoVacuumCommit(Btree *pBt){
|
|||||||
}else{
|
}else{
|
||||||
Pgno nextOvfl = get4byte(pDbPage);
|
Pgno nextOvfl = get4byte(pDbPage);
|
||||||
if( nextOvfl!=0 ){
|
if( nextOvfl!=0 ){
|
||||||
assert( nextOvfl<=origDbSize );
|
assert( nextOvfl<=origSize );
|
||||||
rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage);
|
rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
}
|
}
|
||||||
@@ -1743,14 +1747,14 @@ static int autoVacuumCommit(Btree *pBt){
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* The entire free-list has been swapped to the end of the file. So
|
/* The entire free-list has been swapped to the end of the file. So
|
||||||
** truncate the database file to finDbSize pages and consider the
|
** truncate the database file to finSize pages and consider the
|
||||||
** free-list empty.
|
** free-list empty.
|
||||||
*/
|
*/
|
||||||
rc = sqlite3pager_write(pBt->pPage1->aData);
|
rc = sqlite3pager_write(pBt->pPage1->aData);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
put4byte(&pBt->pPage1->aData[32], 0);
|
put4byte(&pBt->pPage1->aData[32], 0);
|
||||||
put4byte(&pBt->pPage1->aData[36], 0);
|
put4byte(&pBt->pPage1->aData[36], 0);
|
||||||
rc = sqlite3pager_truncate(pBt->pPager, finDbSize);
|
rc = sqlite3pager_truncate(pBt->pPager, finSize);
|
||||||
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
|
||||||
autovacuum_out:
|
autovacuum_out:
|
||||||
@@ -2874,7 +2878,7 @@ static int allocatePage(Btree *pBt, MemPage **ppPage, Pgno *pPgno, Pgno nearby){
|
|||||||
*pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
|
*pPgno = sqlite3pager_pagecount(pBt->pPager) + 1;
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
if( pBt->autoVacuum && *pPgno==PTRMAP_PAGENO(pBt->pageSize, *pPgno) ){
|
if( pBt->autoVacuum && PTRMAP_ISPAGE(pBt->pageSize, *pPgno) ){
|
||||||
/* If *pPgno refers to a pointer-map page, allocate two new pages
|
/* 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
|
** 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.
|
** becomes a new pointer-map page, the second is used by the caller.
|
||||||
@@ -3058,17 +3062,13 @@ static int fillInCell(
|
|||||||
#endif
|
#endif
|
||||||
rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl);
|
rc = allocatePage(pBt, &pOvfl, &pgnoOvfl, pgnoOvfl);
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
/* If the database supports auto-vacuum, add an entry to the
|
/* If the database supports auto-vacuum, and the second or subsequent
|
||||||
** pointer-map for the overflow page just allocated. If the page just
|
** overflow page is being allocated, add an entry to the pointer-map
|
||||||
** allocated was the first in the overflow list, then the balance()
|
** for that page now. The entry for the first overflow page will be
|
||||||
** routine may adjust the pointer-map entry later.
|
** added later, by the insertCell() routine.
|
||||||
*/
|
*/
|
||||||
if( pBt->autoVacuum && rc==0 ){
|
if( pBt->autoVacuum && pgnoPtrmap!=0 && rc==SQLITE_OK ){
|
||||||
if( pgnoPtrmap!=0 ){
|
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap);
|
||||||
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap);
|
|
||||||
}else{
|
|
||||||
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if( rc ){
|
if( rc ){
|
||||||
@@ -3289,22 +3289,22 @@ static int insertCell(
|
|||||||
put2byte(&data[hdr+3], pPage->nCell);
|
put2byte(&data[hdr+3], pPage->nCell);
|
||||||
pPage->idxShift = 1;
|
pPage->idxShift = 1;
|
||||||
pageIntegrity(pPage);
|
pageIntegrity(pPage);
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
if( pPage->pBt->autoVacuum ){
|
||||||
|
/* The cell may contain a pointer to an overflow page. If so, write
|
||||||
|
** the entry for the overflow page into the pointer map.
|
||||||
|
*/
|
||||||
|
CellInfo info;
|
||||||
|
parseCellPtr(pPage, pCell, &info);
|
||||||
|
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
|
||||||
|
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
||||||
|
int rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
||||||
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
|
||||||
if( pPage->pBt->autoVacuum ){
|
|
||||||
/* The cell may contain a pointer to an overflow page. If so, write
|
|
||||||
** the entry for the overflow page into the pointer map.
|
|
||||||
*/
|
|
||||||
CellInfo info;
|
|
||||||
parseCellPtr(pPage, pCell, &info);
|
|
||||||
if( (info.nData+(pPage->intKey?0:info.nKey))>info.nLocal ){
|
|
||||||
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
|
||||||
int rc = ptrmapPut(pPage->pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
|
||||||
if( rc!=SQLITE_OK ) return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user