mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-08 14:02:16 +03:00
Code to auto-vacuum the database if all root pages happen to be in the right place. Not active by default and largely untested. (CVS 2037)
FossilOrigin-Name: d12481f09cbe51c7ea499bc22afec5de3af14ad4
This commit is contained in:
21
manifest
21
manifest
@@ -1,5 +1,5 @@
|
|||||||
C Updates\sto\sthe\ssupport.html\spage.\s(CVS\s2036)
|
C Code\sto\sauto-vacuum\sthe\sdatabase\sif\sall\sroot\spages\shappen\sto\sbe\sin\sthe\sright\splace.\sNot\sactive\sby\sdefault\sand\slargely\suntested.\s(CVS\s2037)
|
||||||
D 2004-11-01T16:03:12
|
D 2004-11-02T12:56:41
|
||||||
F Makefile.in 9e90c685d69f09039015a6b1f3b0a48e9738c9e5
|
F Makefile.in 9e90c685d69f09039015a6b1f3b0a48e9738c9e5
|
||||||
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 231a0e7a00b96bafd4a7a0c774eece1510aec2fd
|
F src/btree.c 92713a104c11abe0826a33590f1d4147332fd9a5
|
||||||
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
|
||||||
@@ -52,8 +52,8 @@ F src/os_unix.c 5824b22ba41fe9d514ef9169aac1b5fde73af229
|
|||||||
F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13
|
F src/os_unix.h f3097815e041e82e24d92505e1ff61ba24172d13
|
||||||
F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb
|
F src/os_win.c 9482dfc92f289b68205bb2c9315757c7e3946bfb
|
||||||
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
|
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
|
||||||
F src/pager.c 6e19f9a64a9fae60bcf00140cecb5981765f3d95
|
F src/pager.c 6b00c0d5aac601b9f556b8fdba25e69438245f1a
|
||||||
F src/pager.h 774d1973acbda341827d21b0da0150575d69f7d9
|
F src/pager.h cbe4ba356d9dd3f30260f322b3dc77408164df14
|
||||||
F src/parse.y 08f4971f89e651f47b3f83fe7369c7edde254331
|
F src/parse.y 08f4971f89e651f47b3f83fe7369c7edde254331
|
||||||
F src/pragma.c 44e192eb5928157bdb015926f858a7c6e3ef6c98
|
F src/pragma.c 44e192eb5928157bdb015926f858a7c6e3ef6c98
|
||||||
F src/printf.c 7a92adc00b758cd5ce087dae80181a8bbdb70ed2
|
F src/printf.c 7a92adc00b758cd5ce087dae80181a8bbdb70ed2
|
||||||
@@ -87,6 +87,7 @@ F test/attach.test 6ad560eb3e77751a4faecd77da09deac9e38cc41
|
|||||||
F test/attach2.test f7795123d3051ace1672b6d23973da6435de3745
|
F test/attach2.test f7795123d3051ace1672b6d23973da6435de3745
|
||||||
F test/attach3.test 6d060986ff004ebb89e1876a331d96c6bb62269e
|
F test/attach3.test 6d060986ff004ebb89e1876a331d96c6bb62269e
|
||||||
F test/auth.test 1cc252d9e7b3bdc1314199cbf3a0d3c5ed026c21
|
F test/auth.test 1cc252d9e7b3bdc1314199cbf3a0d3c5ed026c21
|
||||||
|
F test/autovacuum.test 77eec318b1be7764b8dcb3198c035edc30cd319f
|
||||||
F test/bigfile.test d3744a8821ce9abb8697f2826a3e3d22b719e89f
|
F test/bigfile.test d3744a8821ce9abb8697f2826a3e3d22b719e89f
|
||||||
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
F test/bigrow.test f0aeb7573dcb8caaafea76454be3ade29b7fc747
|
||||||
F test/bind.test a8682ba41433b93bb36a4213a43f282ca9aec5a9
|
F test/bind.test a8682ba41433b93bb36a4213a43f282ca9aec5a9
|
||||||
@@ -157,7 +158,7 @@ F test/pagesize.test 56d11f4d6df9949d646bf87da1d6d995ed37cd78
|
|||||||
F test/pragma.test 66a66b7f3b273b93325c9a5794acb418f52fdcbf
|
F test/pragma.test 66a66b7f3b273b93325c9a5794acb418f52fdcbf
|
||||||
F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57
|
F test/printf.test 92ba4c510b4fc61120ffa4a01820446ed917ae57
|
||||||
F test/progress.test 5ddba78cb6011fba36093973cfb3ac473b8fb96a x
|
F test/progress.test 5ddba78cb6011fba36093973cfb3ac473b8fb96a x
|
||||||
F test/quick.test 212a9cd4c40c72c7c4780fef1c2fbe5d1cb34ce6
|
F test/quick.test 2dca186ebd5c418a7699944ba3b5e437d765eddd
|
||||||
F test/quote.test 6d75cf635d93ba2484dc9cb378d88cbae9dc2c62
|
F test/quote.test 6d75cf635d93ba2484dc9cb378d88cbae9dc2c62
|
||||||
F test/rollback.test 4097328d44510277244ef4fa51b22b2f11d7ef4c
|
F test/rollback.test 4097328d44510277244ef4fa51b22b2f11d7ef4c
|
||||||
F test/rowid.test b3d059f5c8d8874fa1c31030e0636f67405d20ea
|
F test/rowid.test b3d059f5c8d8874fa1c31030e0636f67405d20ea
|
||||||
@@ -251,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 bebd967f3627220c3ce0352c8ca9c7c17b722ce6
|
P 5515accee348c6364cd58903a19029519797e123
|
||||||
R 05340acf98e3d142b518cfefba2b2bd8
|
R d09732475e8e7fd32816b925d21968c5
|
||||||
U drh
|
U danielk1977
|
||||||
Z ee6b8d03ade5cb666f8adaa6773746cd
|
Z 3ba6d76f77fb940670c05d63aad699f9
|
||||||
|
@@ -1 +1 @@
|
|||||||
5515accee348c6364cd58903a19029519797e123
|
d12481f09cbe51c7ea499bc22afec5de3af14ad4
|
414
src/btree.c
414
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.195 2004/10/31 16:25:43 danielk1977 Exp $
|
** $Id: btree.c,v 1.196 2004/11/02 12:56:41 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
|
||||||
@@ -414,23 +414,42 @@ static void put4byte(unsigned char *p, u32 v){
|
|||||||
#define PTRMAP_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5)
|
#define PTRMAP_PTROFFSET(pgsz, pgno) (((pgno-2)%(pgsz/5+1)-1)*5)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** The first byte of each 5-byte pointer map entry identifies the type
|
** The pointer map is a lookup table that contains an entry for each database
|
||||||
** of page that the following 4-byte page number refers to (either a
|
** page in the file except for page 1. In this context 'database page' refers
|
||||||
** regular btree page or an overflow page).
|
** to any page that is not part of the pointer map itself. Each pointer map
|
||||||
|
** entry consists of a single byte 'type' and a 4 byte page number. The
|
||||||
|
** PTRMAP_XXX identifiers below are the valid types. The interpretation
|
||||||
|
** of the page-number depends on the type, as follows:
|
||||||
**
|
**
|
||||||
** If the type is PTRMAP_OVERFLOW, then the page is an overflow page.
|
** PTRMAP_ROOTPAGE: The database page is a root-page. The page-number is not
|
||||||
** In this case the pointer is always the first 4 bytes of the page.
|
** used in this case.
|
||||||
**
|
**
|
||||||
** If the type is PTRMAP_BTREE, then the page is a btree page. In this
|
** PTRMAP_FREEPAGE: The database page is an unused (free) page. The page-number
|
||||||
** case the pointer may be a 'left-pointer' (stored following a cell-header),
|
** is not used in this case.
|
||||||
** a pointer to an overflow page (stored after a cell's data payload),
|
**
|
||||||
** or the 'right pointer' of a btree page.
|
** PTRMAP_OVERFLOW1: The database page is the first page in a list of
|
||||||
|
** overflow pages. The page number identifies the page that
|
||||||
|
** contains the cell with a pointer to this overflow page.
|
||||||
|
**
|
||||||
|
** PTRMAP_OVERFLOW2: The database page is the second or later page in a list of
|
||||||
|
** overflow pages. The page-number identifies the previous
|
||||||
|
** page in the overflow page list.
|
||||||
|
**
|
||||||
|
** PTRMAP_BTREE: The database page is a non-root btree page. The page number
|
||||||
|
** identifies the parent page in the btree.
|
||||||
*/
|
*/
|
||||||
#define PTRMAP_BTREE 1
|
#define PTRMAP_ROOTPAGE 1
|
||||||
#define PTRMAP_OVERFLOW 2
|
#define PTRMAP_FREEPAGE 2
|
||||||
|
#define PTRMAP_OVERFLOW1 3
|
||||||
|
#define PTRMAP_OVERFLOW2 4
|
||||||
|
#define PTRMAP_BTREE 5
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Write an entry into the pointer map.
|
** Write an entry into the pointer map.
|
||||||
|
**
|
||||||
|
** This routine updates the pointer map entry for page number 'key'
|
||||||
|
** so that it maps to type 'eType' and parent page number 'pgno'.
|
||||||
|
** An error code is returned if something goes wrong, otherwise SQLITE_OK.
|
||||||
*/
|
*/
|
||||||
static int ptrmapPut(Btree *pBt, Pgno key, u8 eType, Pgno pgno){
|
static int ptrmapPut(Btree *pBt, Pgno key, u8 eType, Pgno pgno){
|
||||||
u8 *pPtrmap; /* The pointer map page */
|
u8 *pPtrmap; /* The pointer map page */
|
||||||
@@ -440,7 +459,7 @@ static int ptrmapPut(Btree *pBt, Pgno key, u8 eType, Pgno pgno){
|
|||||||
|
|
||||||
iPtrmap = PTRMAP_PAGENO(pBt->pageSize, key);
|
iPtrmap = PTRMAP_PAGENO(pBt->pageSize, key);
|
||||||
rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
|
rc = sqlite3pager_get(pBt->pPager, iPtrmap, (void **)&pPtrmap);
|
||||||
if( rc!=0 ){
|
if( rc!=SQLITE_OK ){
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
|
offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
|
||||||
@@ -460,6 +479,10 @@ static int ptrmapPut(Btree *pBt, Pgno key, u8 eType, Pgno pgno){
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
** Read an entry from the pointer map.
|
** Read an entry from the pointer map.
|
||||||
|
**
|
||||||
|
** This routine retrieves the pointer map entry for page 'key', writing
|
||||||
|
** the type and parent page number to *pEType and *pPgno respectively.
|
||||||
|
** An error code is returned if something goes wrong, otherwise SQLITE_OK.
|
||||||
*/
|
*/
|
||||||
static int ptrmapGet(Btree *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
static int ptrmapGet(Btree *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
||||||
int iPtrmap; /* Pointer map page index */
|
int iPtrmap; /* Pointer map page index */
|
||||||
@@ -474,8 +497,8 @@ static int ptrmapGet(Btree *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
|
|||||||
}
|
}
|
||||||
|
|
||||||
offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
|
offset = PTRMAP_PTROFFSET(pBt->pageSize, key);
|
||||||
*pEType = pPtrmap[offset];
|
if( pEType ) *pEType = pPtrmap[offset];
|
||||||
*pPgno = get4byte(&pPtrmap[offset+1]);
|
if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]);
|
||||||
|
|
||||||
sqlite3pager_unref(pPtrmap);
|
sqlite3pager_unref(pPtrmap);
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
@@ -1181,7 +1204,9 @@ int sqlite3BtreeOpen(
|
|||||||
*ppBtree = pBt;
|
*ppBtree = pBt;
|
||||||
#ifdef SQLITE_AUTOVACUUM
|
#ifdef SQLITE_AUTOVACUUM
|
||||||
/* Note: This is temporary code for use during development of auto-vacuum. */
|
/* Note: This is temporary code for use during development of auto-vacuum. */
|
||||||
pBt->autoVacuum = 1;
|
if( zFilename && 0!=strcmp(zFilename, ":memory:") ){
|
||||||
|
pBt->autoVacuum = 1;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}
|
}
|
||||||
@@ -1453,6 +1478,272 @@ int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** The TRACE macro will print high-level status information about the
|
||||||
|
** btree operation when the global variable sqlite3_btree_trace is
|
||||||
|
** enabled.
|
||||||
|
*/
|
||||||
|
#if SQLITE_TEST
|
||||||
|
# define TRACE(X) if( sqlite3_btree_trace )\
|
||||||
|
{ sqlite3DebugPrintf X; fflush(stdout); }
|
||||||
|
#else
|
||||||
|
# define TRACE(X)
|
||||||
|
#endif
|
||||||
|
int sqlite3_btree_trace=0; /* True to enable tracing */
|
||||||
|
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Set the pointer-map entries for all children of page pPage. Also, if
|
||||||
|
** pPage contains cells that point to overflow pages, set the pointer
|
||||||
|
** map entries for the overflow pages as well.
|
||||||
|
*/
|
||||||
|
static int setChildPtrmaps(MemPage *pPage){
|
||||||
|
int i; /* Counter variable */
|
||||||
|
int nCell; /* Number of cells in page pPage */
|
||||||
|
int rc = SQLITE_OK; /* Return code */
|
||||||
|
Btree *pBt = pPage->pBt;
|
||||||
|
int isInitOrig = pPage->isInit;
|
||||||
|
Pgno pgno = pPage->pgno;
|
||||||
|
|
||||||
|
initPage(pPage, 0);
|
||||||
|
nCell = pPage->nCell;
|
||||||
|
|
||||||
|
for(i=0; i<nCell; i++){
|
||||||
|
CellInfo info;
|
||||||
|
u8 *pCell = findCell(pPage, i);
|
||||||
|
|
||||||
|
parseCellPtr(pPage, pCell, &info);
|
||||||
|
if( info.iOverflow ){
|
||||||
|
Pgno ovflPgno = get4byte(&pCell[info.iOverflow]);
|
||||||
|
rc = ptrmapPut(pBt, ovflPgno, PTRMAP_OVERFLOW1, pgno);
|
||||||
|
if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out;
|
||||||
|
}
|
||||||
|
if( !pPage->leaf ){
|
||||||
|
Pgno childPgno = get4byte(pCell);
|
||||||
|
rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno);
|
||||||
|
if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !pPage->leaf ){
|
||||||
|
Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
|
||||||
|
rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno);
|
||||||
|
}
|
||||||
|
|
||||||
|
set_child_ptrmaps_out:
|
||||||
|
pPage->isInit = isInitOrig;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Somewhere on pPage, which is guarenteed to be a btree page, not an overflow
|
||||||
|
** page, is a pointer to page iFrom. Modify this pointer so that it points to
|
||||||
|
** iTo. Parameter eType describes the type of pointer to be modified, as
|
||||||
|
** follows:
|
||||||
|
**
|
||||||
|
** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child
|
||||||
|
** page of pPage.
|
||||||
|
**
|
||||||
|
** PTRMAP_OVERFLOW1: pPage is a btree-page. The pointer points at an overflow
|
||||||
|
** page pointed to by one of the cells on pPage.
|
||||||
|
**
|
||||||
|
** PTRMAP_OVERFLOW2: pPage is an overflow-page. The pointer points at the next
|
||||||
|
** overflow page in the list.
|
||||||
|
*/
|
||||||
|
static void modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
|
||||||
|
|
||||||
|
if( eType==PTRMAP_OVERFLOW2 ){
|
||||||
|
assert( get4byte(pPage->aData)==iFrom );
|
||||||
|
put4byte(pPage->aData, iFrom);
|
||||||
|
}else{
|
||||||
|
int isInitOrig = pPage->isInit;
|
||||||
|
int i;
|
||||||
|
int nCell;
|
||||||
|
|
||||||
|
initPage(pPage, 0);
|
||||||
|
nCell = pPage->nCell;
|
||||||
|
|
||||||
|
/* assert( !pPage->leaf && eType==PTRMAP_BTREE ); */
|
||||||
|
|
||||||
|
for(i=0; i<nCell; i++){
|
||||||
|
u8 *pCell = findCell(pPage, i);
|
||||||
|
if( eType==PTRMAP_OVERFLOW1 ){
|
||||||
|
CellInfo info;
|
||||||
|
parseCellPtr(pPage, pCell, &info);
|
||||||
|
if( info.iOverflow ){
|
||||||
|
if( iFrom==get4byte(&pCell[info.iOverflow]) ){
|
||||||
|
put4byte(&pCell[info.iOverflow], iTo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
if( get4byte(pCell)==iFrom ){
|
||||||
|
put4byte(pCell, iTo);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( i==nCell ){
|
||||||
|
assert( eType==PTRMAP_BTREE );
|
||||||
|
assert( get4byte(&pPage->aData[pPage->hdrOffset+8])==iFrom );
|
||||||
|
put4byte(&pPage->aData[pPage->hdrOffset+8], iTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
pPage->isInit = isInitOrig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forward declaration required by autoVacuumCommit(). */
|
||||||
|
static int allocatePage(Btree *, MemPage **, Pgno *, Pgno);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** This routine is called prior to sqlite3pager_commit when a transaction
|
||||||
|
** is commited for an auto-vacuum database.
|
||||||
|
*/
|
||||||
|
static int autoVacuumCommit(Btree *pBt){
|
||||||
|
Pager *pPager = pBt->pPager;
|
||||||
|
Pgno nFreeList; /* Number of pages remaining on the free-list. */
|
||||||
|
Pgno origDbSize; /* Pages in the database file */
|
||||||
|
Pgno finDbSize; /* Pages in the database file after truncation */
|
||||||
|
int i; /* Counter variable */
|
||||||
|
int rc; /* Return code */
|
||||||
|
u8 eType;
|
||||||
|
|
||||||
|
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; /* "" */
|
||||||
|
|
||||||
|
#ifndef NDEBUG
|
||||||
|
int nRef = *sqlite3pager_stats(pPager);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert( pBt->autoVacuum );
|
||||||
|
|
||||||
|
/* Figure out how many free-pages are in the database. If there are no
|
||||||
|
** free pages, then auto-vacuum is a no-op.
|
||||||
|
*/
|
||||||
|
nFreeList = get4byte(&pBt->pPage1->aData[36]);
|
||||||
|
if( nFreeList==0 ) return SQLITE_OK;
|
||||||
|
|
||||||
|
origDbSize = sqlite3pager_pagecount(pPager);
|
||||||
|
finDbSize = origDbSize - nFreeList;
|
||||||
|
TRACE(("AUTOVACUUM: Begin (db size %d->%d)\n", origDbSize, finDbSize));
|
||||||
|
|
||||||
|
/* 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 finDbSize. If so, the auto-vacuum cannot
|
||||||
|
** proceed. This limitation will be fixed when root pages are automatically
|
||||||
|
** allocated at the start of the database file.
|
||||||
|
*/
|
||||||
|
for( i=finDbSize+1; i<=origDbSize; i++ ){
|
||||||
|
rc = ptrmapGet(pBt, i, &eType, 0);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
if( eType==PTRMAP_ROOTPAGE ){
|
||||||
|
TRACE(("AUTOVACUUM: Cannot proceed due to root-page on page %d\n", i));
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Variable 'finDbSize' 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
|
||||||
|
** to a free page earlier in the file (somewhere before finDbSize).
|
||||||
|
*/
|
||||||
|
for( iDbPage=finDbSize+1; iDbPage<=origDbSize; iDbPage++ ){
|
||||||
|
rc = ptrmapGet(pBt, iDbPage, &eType, &iPtrPage);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
assert( eType!=PTRMAP_ROOTPAGE );
|
||||||
|
|
||||||
|
/* If iDbPage is already on the free-list, do not swap it. */
|
||||||
|
if( eType==PTRMAP_FREEPAGE ){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
** allocatePage() routine.
|
||||||
|
*/
|
||||||
|
do{
|
||||||
|
if( pFreeMemPage ){
|
||||||
|
releasePage(pFreeMemPage);
|
||||||
|
pFreeMemPage = 0;
|
||||||
|
}
|
||||||
|
rc = allocatePage(pBt, &pFreeMemPage, &iFreePage, 0);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
assert( iFreePage<=origDbSize );
|
||||||
|
}while( iFreePage>finDbSize );
|
||||||
|
|
||||||
|
/* Move page iDbPage from it's current location to page number iFreePage */
|
||||||
|
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d)\n",
|
||||||
|
iDbPage, iFreePage, iPtrPage));
|
||||||
|
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( eType==PTRMAP_BTREE ){
|
||||||
|
rc = setChildPtrmaps(pDbMemPage);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
** truncate the database file to finDbSize pages and consider the
|
||||||
|
** free-list empty.
|
||||||
|
*/
|
||||||
|
rc = sqlite3pager_write(pBt->pPage1->aData);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
put4byte(&pBt->pPage1->aData[32], 0);
|
||||||
|
put4byte(&pBt->pPage1->aData[36], 0);
|
||||||
|
rc = sqlite3pager_truncate(pBt->pPager, finDbSize);
|
||||||
|
if( rc!=SQLITE_OK ) goto autovacuum_out;
|
||||||
|
|
||||||
|
autovacuum_out:
|
||||||
|
/* TODO: A goto autovacuum_out; will fail to call releasePage() on
|
||||||
|
** outstanding references. Fix.
|
||||||
|
*/
|
||||||
|
if( nRef!=*sqlite3pager_stats(pPager) ){
|
||||||
|
sqlite3pager_refdump(pPager);
|
||||||
|
}
|
||||||
|
assert( nRef==*sqlite3pager_stats(pPager) );
|
||||||
|
if( rc!=SQLITE_OK ){
|
||||||
|
sqlite3pager_rollback(pPager);
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Commit the transaction currently in progress.
|
** Commit the transaction currently in progress.
|
||||||
**
|
**
|
||||||
@@ -2472,19 +2763,6 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
** The TRACE macro will print high-level status information about the
|
|
||||||
** btree operation when the global variable sqlite3_btree_trace is
|
|
||||||
** enabled.
|
|
||||||
*/
|
|
||||||
#if SQLITE_TEST
|
|
||||||
# define TRACE(X) if( sqlite3_btree_trace )\
|
|
||||||
{ sqlite3DebugPrintf X; fflush(stdout); }
|
|
||||||
#else
|
|
||||||
# define TRACE(X)
|
|
||||||
#endif
|
|
||||||
int sqlite3_btree_trace=0; /* True to enable tracing */
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Allocate a new page from the database file.
|
** Allocate a new page from the database file.
|
||||||
**
|
**
|
||||||
@@ -2615,6 +2893,15 @@ static int freePage(MemPage *pPage){
|
|||||||
n = get4byte(&pPage1->aData[36]);
|
n = get4byte(&pPage1->aData[36]);
|
||||||
put4byte(&pPage1->aData[36], n+1);
|
put4byte(&pPage1->aData[36], n+1);
|
||||||
|
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
/* If the database supports auto-vacuum, write an entry in the pointer-map
|
||||||
|
** to indicate that the page is free.
|
||||||
|
*/
|
||||||
|
if( pBt->autoVacuum ){
|
||||||
|
rc = ptrmapPut(pBt, pPage->pgno, PTRMAP_FREEPAGE, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if( n==0 ){
|
if( n==0 ){
|
||||||
/* This is the first free page */
|
/* This is the first free page */
|
||||||
rc = sqlite3pager_write(pPage->aData);
|
rc = sqlite3pager_write(pPage->aData);
|
||||||
@@ -2757,9 +3044,9 @@ static int fillInCell(
|
|||||||
*/
|
*/
|
||||||
if( pBt->autoVacuum && rc==0 ){
|
if( pBt->autoVacuum && rc==0 ){
|
||||||
if( pgnoPtrmap!=0 ){
|
if( pgnoPtrmap!=0 ){
|
||||||
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW, pgnoPtrmap);
|
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW2, pgnoPtrmap);
|
||||||
}else{
|
}else{
|
||||||
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_BTREE, pPage->pgno);
|
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -2867,7 +3154,7 @@ static int reparentChildPages(MemPage *pPage){
|
|||||||
parseCellPtr(pPage, pCell, &info);
|
parseCellPtr(pPage, pCell, &info);
|
||||||
if( info.iOverflow ){
|
if( info.iOverflow ){
|
||||||
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
||||||
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_BTREE, pPage->pgno);
|
rc = ptrmapPut(pBt, pgnoOvfl, PTRMAP_OVERFLOW1, pPage->pgno);
|
||||||
if( rc!=SQLITE_OK ) return rc;
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3649,7 +3936,7 @@ static int balance(MemPage *pPage){
|
|||||||
if( pPage->nOverflow>0 ){
|
if( pPage->nOverflow>0 ){
|
||||||
rc = balance_deeper(pPage);
|
rc = balance_deeper(pPage);
|
||||||
}
|
}
|
||||||
if( pPage->nCell==0 ){
|
if( rc==SQLITE_OK && pPage->nCell==0 ){
|
||||||
rc = balance_shallower(pPage);
|
rc = balance_shallower(pPage);
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
@@ -3762,7 +4049,9 @@ int sqlite3BtreeInsert(
|
|||||||
rc = balance(pPage);
|
rc = balance(pPage);
|
||||||
/* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
|
/* sqlite3BtreePageDump(pCur->pBt, pCur->pgnoRoot, 1); */
|
||||||
/* fflush(stdout); */
|
/* fflush(stdout); */
|
||||||
moveToRoot(pCur);
|
if( rc==SQLITE_OK ){
|
||||||
|
moveToRoot(pCur);
|
||||||
|
}
|
||||||
end_insert:
|
end_insert:
|
||||||
sqliteFree(newCell);
|
sqliteFree(newCell);
|
||||||
return rc;
|
return rc;
|
||||||
@@ -3878,6 +4167,18 @@ int sqlite3BtreeCreateTable(Btree *pBt, int *piTable, int flags){
|
|||||||
}
|
}
|
||||||
rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
|
rc = allocatePage(pBt, &pRoot, &pgnoRoot, 1);
|
||||||
if( rc ) return rc;
|
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.
|
||||||
|
*/
|
||||||
|
if( pBt->autoVacuum ){
|
||||||
|
rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0);
|
||||||
|
if( rc ){
|
||||||
|
sqlite3pager_unref(pRoot->aData);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
assert( sqlite3pager_iswriteable(pRoot->aData) );
|
assert( sqlite3pager_iswriteable(pRoot->aData) );
|
||||||
zeroPage(pRoot, flags | PTF_LEAF);
|
zeroPage(pRoot, flags | PTF_LEAF);
|
||||||
sqlite3pager_unref(pRoot->aData);
|
sqlite3pager_unref(pRoot->aData);
|
||||||
@@ -4342,25 +4643,38 @@ static void checkList(
|
|||||||
}
|
}
|
||||||
if( isFreeList ){
|
if( isFreeList ){
|
||||||
int n = get4byte(&pOvfl[4]);
|
int n = get4byte(&pOvfl[4]);
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
if( pCheck->pBt->autoVacuum ){
|
||||||
|
checkPtrmap(pCheck, iPage, PTRMAP_FREEPAGE, 0, zContext);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if( n>pCheck->pBt->usableSize/4-8 ){
|
if( n>pCheck->pBt->usableSize/4-8 ){
|
||||||
checkAppendMsg(pCheck, zContext,
|
checkAppendMsg(pCheck, zContext,
|
||||||
"freelist leaf count too big on page %d", iPage);
|
"freelist leaf count too big on page %d", iPage);
|
||||||
N--;
|
N--;
|
||||||
}else{
|
}else{
|
||||||
for(i=0; i<n; i++){
|
for(i=0; i<n; i++){
|
||||||
checkRef(pCheck, get4byte(&pOvfl[8+i*4]), zContext);
|
Pgno iFreePage = get4byte(&pOvfl[8+i*4]);
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
if( pCheck->pBt->autoVacuum ){
|
||||||
|
checkPtrmap(pCheck, iFreePage, PTRMAP_FREEPAGE, 0, zContext);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
checkRef(pCheck, iFreePage, zContext);
|
||||||
}
|
}
|
||||||
N -= n;
|
N -= n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
/* If this database supports auto-vacuum and iPage is not the last
|
else{
|
||||||
** page in this overflow list, check that the pointer-map entry for
|
/* If this database supports auto-vacuum and iPage is not the last
|
||||||
** the following page matches iPage.
|
** page in this overflow list, check that the pointer-map entry for
|
||||||
*/
|
** the following page matches iPage.
|
||||||
if( pCheck->pBt->autoVacuum && !isFreeList && N>0 ){
|
*/
|
||||||
i = get4byte(pOvfl);
|
if( pCheck->pBt->autoVacuum && N>0 ){
|
||||||
checkPtrmap(pCheck, i, PTRMAP_OVERFLOW, iPage, zContext);
|
i = get4byte(pOvfl);
|
||||||
|
checkPtrmap(pCheck, i, PTRMAP_OVERFLOW2, iPage, zContext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
iPage = get4byte(pOvfl);
|
iPage = get4byte(pOvfl);
|
||||||
@@ -4448,7 +4762,7 @@ static int checkTreePage(
|
|||||||
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
Pgno pgnoOvfl = get4byte(&pCell[info.iOverflow]);
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
if( pBt->autoVacuum ){
|
if( pBt->autoVacuum ){
|
||||||
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_BTREE, iPage, zContext);
|
checkPtrmap(pCheck, pgnoOvfl, PTRMAP_OVERFLOW1, iPage, zContext);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
|
checkList(pCheck, 0, pgnoOvfl, nPage, zContext);
|
||||||
@@ -4475,7 +4789,7 @@ static int checkTreePage(
|
|||||||
sprintf(zContext, "On page %d at right child: ", iPage);
|
sprintf(zContext, "On page %d at right child: ", iPage);
|
||||||
#ifndef SQLITE_OMIT_AUTOVACUUM
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
if( pBt->autoVacuum ){
|
if( pBt->autoVacuum ){
|
||||||
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, zContext);
|
checkPtrmap(pCheck, pgno, PTRMAP_BTREE, iPage, 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0);
|
checkTreePage(pCheck, pgno, pPage, zContext,0,0,0,0);
|
||||||
@@ -4569,6 +4883,12 @@ char *sqlite3BtreeIntegrityCheck(Btree *pBt, int *aRoot, int nRoot){
|
|||||||
*/
|
*/
|
||||||
for(i=0; i<nRoot; i++){
|
for(i=0; i<nRoot; i++){
|
||||||
if( aRoot[i]==0 ) continue;
|
if( aRoot[i]==0 ) continue;
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
/* Note: This is temporary code for use during development of auto-vacuum. */
|
||||||
|
if( pBt->autoVacuum && aRoot[i]>1 ){
|
||||||
|
checkPtrmap(&sCheck, aRoot[i], PTRMAP_ROOTPAGE, 0, 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0);
|
checkTreePage(&sCheck, aRoot[i], 0, "List of tree roots: ", 0,0,0,0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4711,6 +5031,12 @@ int sqlite3BtreeIsInStmt(Btree *pBt){
|
|||||||
*/
|
*/
|
||||||
int sqlite3BtreeSync(Btree *pBt, const char *zMaster){
|
int sqlite3BtreeSync(Btree *pBt, const char *zMaster){
|
||||||
if( pBt->inTrans==TRANS_WRITE ){
|
if( pBt->inTrans==TRANS_WRITE ){
|
||||||
|
#ifndef SQLITE_OMIT_AUTOVACUUM
|
||||||
|
if( pBt->autoVacuum ){
|
||||||
|
int rc = autoVacuumCommit(pBt);
|
||||||
|
if( rc!=SQLITE_OK ) return rc;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return sqlite3pager_sync(pBt->pPager, zMaster);
|
return sqlite3pager_sync(pBt->pPager, zMaster);
|
||||||
}
|
}
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
|
97
src/pager.c
97
src/pager.c
@@ -18,7 +18,7 @@
|
|||||||
** file simultaneously, or one process from reading the database while
|
** file simultaneously, or one process from reading the database while
|
||||||
** another is writing.
|
** another is writing.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.c,v 1.169 2004/10/31 02:22:49 drh Exp $
|
** @(#) $Id: pager.c,v 1.170 2004/11/02 12:56:41 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
#include "sqliteInt.h"
|
#include "sqliteInt.h"
|
||||||
#include "os.h"
|
#include "os.h"
|
||||||
@@ -2017,10 +2017,22 @@ static int pager_write_pagelist(PgHdr *pList){
|
|||||||
while( pList ){
|
while( pList ){
|
||||||
assert( pList->dirty );
|
assert( pList->dirty );
|
||||||
sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize);
|
sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(i64)pPager->pageSize);
|
||||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
|
/* If there are dirty pages in the page cache with page numbers greater
|
||||||
TRACE3("STORE %d page %d\n", pPager->fd.h, pList->pgno);
|
** than Pager.dbSize, this means sqlite3pager_truncate() was called to
|
||||||
rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize);
|
** make the file smaller (presumably by auto-vacuum code). Do not write
|
||||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
|
** any such pages to the file.
|
||||||
|
*/
|
||||||
|
if( pList->pgno<=pPager->dbSize ){
|
||||||
|
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
|
||||||
|
TRACE3("STORE %d page %d\n", pPager->fd.h, pList->pgno);
|
||||||
|
rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), pPager->pageSize);
|
||||||
|
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
|
||||||
|
}
|
||||||
|
#ifndef NDEBUG
|
||||||
|
else{
|
||||||
|
TRACE3("NOSTORE %d page %d\n", pPager->fd.h, pList->pgno);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if( rc ) return rc;
|
if( rc ) return rc;
|
||||||
pList->dirty = 0;
|
pList->dirty = 0;
|
||||||
pList = pList->pDirty;
|
pList = pList->pDirty;
|
||||||
@@ -3202,6 +3214,81 @@ sync_exit:
|
|||||||
return rc;
|
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
|
||||||
|
** by this routine. The same applies to the page pData refers to on entry to
|
||||||
|
** this routine.
|
||||||
|
**
|
||||||
|
** References to the page refered to by pData remain valid. Updating any
|
||||||
|
** meta-data associated with page pData (i.e. data stored in the nExtra bytes
|
||||||
|
** allocated along with the page) is the responsibility of the caller.
|
||||||
|
**
|
||||||
|
** A transaction must be active when this routine is called, however it is
|
||||||
|
** 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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
pPg->dirty = 1;
|
||||||
|
pPager->dirtyCache = 1;
|
||||||
|
|
||||||
|
return SQLITE_OK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||||
/*
|
/*
|
||||||
** Return the current state of the file lock for the given pager.
|
** Return the current state of the file lock for the given pager.
|
||||||
|
@@ -13,7 +13,7 @@
|
|||||||
** subsystem. The page cache subsystem reads and writes a file a page
|
** subsystem. The page cache subsystem reads and writes a file a page
|
||||||
** at a time and provides a journal for rollback.
|
** at a time and provides a journal for rollback.
|
||||||
**
|
**
|
||||||
** @(#) $Id: pager.h,v 1.38 2004/10/05 02:41:43 drh Exp $
|
** @(#) $Id: pager.h,v 1.39 2004/11/02 12:56:41 danielk1977 Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -91,6 +91,7 @@ const char *sqlite3pager_dirname(Pager*);
|
|||||||
const char *sqlite3pager_journalname(Pager*);
|
const char *sqlite3pager_journalname(Pager*);
|
||||||
int sqlite3pager_rename(Pager*, const char *zNewName);
|
int sqlite3pager_rename(Pager*, const char *zNewName);
|
||||||
void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
|
void sqlite3pager_set_codec(Pager*,void(*)(void*,void*,Pgno,int),void*);
|
||||||
|
int sqlite3pager_movepage(Pager*,void*,Pgno);
|
||||||
|
|
||||||
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
|
||||||
int sqlite3pager_lockstate(Pager*);
|
int sqlite3pager_lockstate(Pager*);
|
||||||
|
99
test/autovacuum.test
Normal file
99
test/autovacuum.test
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# 2001 September 15
|
||||||
|
#
|
||||||
|
# The author disclaims copyright to this source code. In place of
|
||||||
|
# a legal notice, here is a blessing:
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
#***********************************************************************
|
||||||
|
# This file implements regression tests for SQLite library. The
|
||||||
|
# focus of this file is testing the SELECT statement.
|
||||||
|
#
|
||||||
|
# $Id: autovacuum.test,v 1.1 2004/11/02 12:56:41 danielk1977 Exp $
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
|
||||||
|
proc make_str {char len} {
|
||||||
|
set str [string repeat $char. $len]
|
||||||
|
return [string range $str 0 [expr $len-1]]
|
||||||
|
}
|
||||||
|
|
||||||
|
proc file_pages {} {
|
||||||
|
return [expr [file size test.db] / 1024]
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test autovacuum-1.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE av1(a);
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
set ENTRY_LEN 1100
|
||||||
|
|
||||||
|
set delete_orders [list]
|
||||||
|
lappend delete_orders {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20}
|
||||||
|
lappend delete_orders {20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1}
|
||||||
|
lappend delete_orders {8 18 2 4 14 11 13 3 10 7 9 5 12 17 19 15 20 6 16 1}
|
||||||
|
lappend delete_orders {10 3 11 17 19 20 7 4 13 6 1 14 16 12 9 18 8 15 5 2}
|
||||||
|
|
||||||
|
lappend delete_orders {{1 2 3 4 5 6 7 8 9 10} {11 12 13 14 15 16 17 18 19 20}}
|
||||||
|
lappend delete_orders \
|
||||||
|
{{19 8 17 15} {16 11 9 14} {18 5 3 1} {13 20 7 2} {6 12 4 10}}
|
||||||
|
|
||||||
|
|
||||||
|
set tn 0
|
||||||
|
foreach delete_order $delete_orders {
|
||||||
|
incr tn
|
||||||
|
|
||||||
|
# Set up the table.
|
||||||
|
set ::tbl_data [list]
|
||||||
|
foreach i [lsort -integer [eval concat $delete_order]] {
|
||||||
|
execsql "INSERT INTO av1 (oid, a) VALUES($i, '[make_str $i $ENTRY_LEN]')"
|
||||||
|
lappend ::tbl_data [make_str $i $ENTRY_LEN]
|
||||||
|
}
|
||||||
|
|
||||||
|
# puts "File has [file_pages] pages"
|
||||||
|
|
||||||
|
do_test autovacuum-1.$tn.1 {
|
||||||
|
execsql {
|
||||||
|
pragma integrity_check
|
||||||
|
}
|
||||||
|
} {ok}
|
||||||
|
|
||||||
|
foreach delete $delete_order {
|
||||||
|
# if {$delete==6} { set btree_trace 1 ; breakpoint }
|
||||||
|
do_test autovacuum-1.$tn.($delete).1 {
|
||||||
|
execsql "
|
||||||
|
DELETE FROM av1 WHERE oid IN ([join $delete ,])
|
||||||
|
"
|
||||||
|
} {}
|
||||||
|
set btree_trace 0
|
||||||
|
|
||||||
|
do_test autovacuum-1.$tn.($delete).2 {
|
||||||
|
execsql {
|
||||||
|
pragma integrity_check
|
||||||
|
}
|
||||||
|
} {ok}
|
||||||
|
|
||||||
|
foreach d $delete {
|
||||||
|
set idx [lsearch $::tbl_data [make_str $d $ENTRY_LEN]]
|
||||||
|
set ::tbl_data [lreplace $::tbl_data $idx $idx]
|
||||||
|
}
|
||||||
|
do_test autovacuum-1.$tn.($delete).3 {
|
||||||
|
execsql {
|
||||||
|
select a from av1
|
||||||
|
}
|
||||||
|
} $::tbl_data
|
||||||
|
# if {$::nErr>0} finish_test
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test autovacuum-1.$tn.3 {
|
||||||
|
file_pages
|
||||||
|
} {3}
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_test
|
||||||
|
|
@@ -10,7 +10,7 @@
|
|||||||
#***********************************************************************
|
#***********************************************************************
|
||||||
# This file runs all tests.
|
# This file runs all tests.
|
||||||
#
|
#
|
||||||
# $Id: quick.test,v 1.30 2004/09/02 14:57:09 drh Exp $
|
# $Id: quick.test,v 1.31 2004/11/02 12:56:41 danielk1977 Exp $
|
||||||
|
|
||||||
set testdir [file dirname $argv0]
|
set testdir [file dirname $argv0]
|
||||||
source $testdir/tester.tcl
|
source $testdir/tester.tcl
|
||||||
@@ -32,6 +32,8 @@ set EXCLUDE {
|
|||||||
misuse.test
|
misuse.test
|
||||||
quick.test
|
quick.test
|
||||||
utf16.test
|
utf16.test
|
||||||
|
|
||||||
|
autovacuum.test
|
||||||
}
|
}
|
||||||
|
|
||||||
if {[sqlite3 -has-codec]} {
|
if {[sqlite3 -has-codec]} {
|
||||||
|
Reference in New Issue
Block a user