1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-16 23:02:26 +03:00

Merge the incr-vacuum-opt branch with the trunk.

FossilOrigin-Name: 26e235b7a4cd4d0dc9725774d70174c4d369cb98
This commit is contained in:
dan
2013-02-25 13:31:30 +00:00
11 changed files with 412 additions and 197 deletions

View File

@@ -1,5 +1,5 @@
C On\sMinix,\sdisable\sthe\s".timer"\scommand\sin\sthe\sshell\sin\sorder\sto\savoid\ncalling\sgetrusage(). C Merge\sthe\sincr-vacuum-opt\sbranch\swith\sthe\strunk.
D 2013-02-20T00:54:21.855 D 2013-02-25T13:31:30.464
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282 F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -118,12 +118,12 @@ F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410 F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410
F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1 F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 32e35a3a4ea55b45c0e5f74eeb793aec71491517 F src/backup.c b2cac9f7993f3f9588827b824b1501d0c820fa68
F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1 F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7 F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 7a80e4a67f32a2494c383a28a495bf3bd71cc230 F src/btree.c cbad71970cfadfa342fc137ca5e319f98b2d0da1
F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd
F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621 F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2
F src/build.c 73ca65f32938e4e0d94e831b61b5749b211b79be F src/build.c 73ca65f32938e4e0d94e831b61b5749b211b79be
F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
@@ -162,7 +162,7 @@ F src/os.h 027491c77d2404c0a678bb3fb06286f331eb9b57
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04 F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_unix.c dfdc04b126f7b05dcb2e2cc5c1262f98acbb49d9 F src/os_unix.c dfdc04b126f7b05dcb2e2cc5c1262f98acbb49d9
F src/os_win.c eabd00b813577d36bd66271cb08dd64ea0589dac F src/os_win.c eabd00b813577d36bd66271cb08dd64ea0589dac
F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f F src/pager.c 0dbf5ff5d5d7d3a21fcab82e9e4d129b6fe6314f
F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0 F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0
F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95 F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346 F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
@@ -229,7 +229,7 @@ F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4 F src/test_thread.c e286f2173563f2a1747c24bcda6b9d030bf4f4e4
F src/test_vfs.c c6260ef238c1142c8f8bd402db02216afd182ae3 F src/test_vfs.c fb16b2d9938cf0c1afc5a423b55b952fcc024275
F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2 F src/test_vfstrace.c 34b544e80ba7fb77be15395a609c669df2e660a2
F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251 F src/test_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
@@ -534,7 +534,8 @@ F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0 F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32 F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b F test/incrvacuum2.test 379eeb8740b0ef60c372c439ad4cbea20b34bb9b
F test/incrvacuum_ioerr.test 22f208d01c528403240e05beecc41dc98ed01637 F test/incrvacuum3.test 1159ec2b3e7bafbde5718867bc7328ba58863313
F test/incrvacuum_ioerr.test 293f2714571255539c8c789da2f7de4ec3f7101e
F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097 F test/index.test b5429732b3b983fa810e3ac867d7ca85dae35097
F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6 F test/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7 F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
@@ -752,7 +753,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2 F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
F test/tester.tcl 0560b09498876da7714fff680c5d892b9228862f F test/tester.tcl 2918ebca150b67ca25b1682f8ecd857af77fab05
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5 F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58 F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7 F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -871,7 +872,7 @@ F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595 F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33 F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df F test/tkt3762.test 4d439ff7abdc8d9323150269d182c37c2d514576
F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789 F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789
F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267 F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449 F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449
@@ -1034,7 +1035,7 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
P 06bd91305ed6752315c5224be5f89e87cafa6687 P 9bd9bd9cab8c804c1a51d472199459176044a633 bf57534188e044fb341315bfc05b7927e66a04e0
R 14c0fedf89f00b14b7845aeac312ffca R 4eb400824b32ad5749b8b0c556f7d2a9
U drh U dan
Z 673b617e6189db9a51be1e6b0160e6d4 Z 245da16ecbcaa5ac23033dda88925107

View File

@@ -1 +1 @@
9bd9bd9cab8c804c1a51d472199459176044a633 26e235b7a4cd4d0dc9725774d70174c4d369cb98

View File

@@ -462,7 +462,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
nDestTruncate = nSrcPage * (pgszSrc/pgszDest); nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
} }
assert( nDestTruncate>0 ); assert( nDestTruncate>0 );
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
if( pgszSrc<pgszDest ){ if( pgszSrc<pgszDest ){
/* If the source page-size is smaller than the destination page-size, /* If the source page-size is smaller than the destination page-size,
@@ -476,6 +475,8 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
*/ */
const i64 iSize = (i64)pgszSrc * (i64)nSrcPage; const i64 iSize = (i64)pgszSrc * (i64)nSrcPage;
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager); sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
Pgno iPg;
int nDstPage;
i64 iOff; i64 iOff;
i64 iEnd; i64 iEnd;
@@ -486,13 +487,26 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
&& iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest && iSize>=PENDING_BYTE && iSize<=PENDING_BYTE+pgszDest
)); ));
/* This call ensures that all data required to recreate the original /* This block ensures that all data required to recreate the original
** database has been stored in the journal for pDestPager and the ** database has been stored in the journal for pDestPager and the
** journal synced to disk. So at this point we may safely modify ** journal synced to disk. So at this point we may safely modify
** the database file in any way, knowing that if a power failure ** the database file in any way, knowing that if a power failure
** occurs, the original database will be reconstructed from the ** occurs, the original database will be reconstructed from the
** journal file. */ ** journal file. */
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1); sqlite3PagerPagecount(pDestPager, &nDstPage);
for(iPg=nDestTruncate; rc==SQLITE_OK && iPg<=(Pgno)nDstPage; iPg++){
if( iPg!=PENDING_BYTE_PAGE(p->pDest->pBt) ){
DbPage *pPg;
rc = sqlite3PagerGet(pDestPager, iPg, &pPg);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg);
sqlite3PagerUnref(pPg);
}
}
}
if( rc==SQLITE_OK ){
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 1);
}
/* Write the extra pages and truncate the database file as required */ /* Write the extra pages and truncate the database file as required */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize); iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
@@ -519,6 +533,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
rc = sqlite3PagerSync(pDestPager); rc = sqlite3PagerSync(pDestPager);
} }
}else{ }else{
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0); rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
} }

View File

@@ -2595,6 +2595,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){ if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun; goto trans_begun;
} }
assert( pBt->bDoTruncate==0 );
/* Write transactions are not possible on a read-only database */ /* Write transactions are not possible on a read-only database */
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){ if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
@@ -2909,26 +2910,28 @@ static int relocatePage(
/* Forward declaration required by incrVacuumStep(). */ /* Forward declaration required by incrVacuumStep(). */
static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8); static int allocateBtreePage(BtShared *, MemPage **, Pgno *, Pgno, u8);
#define BTALLOC_ANY 0 /* Allocate any page */
#define BTALLOC_EXACT 1 /* Allocate exact page if possible */
#define BTALLOC_LE 2 /* Allocate any page <= the parameter */
/* /*
** Perform a single step of an incremental-vacuum. If successful, ** Perform a single step of an incremental-vacuum. If successful, return
** return SQLITE_OK. If there is no work to do (and therefore no ** SQLITE_OK. If there is no work to do (and therefore no point in
** point in calling this function again), return SQLITE_DONE. ** calling this function again), return SQLITE_DONE. Or, if an error
** occurs, return some other error code.
** **
** More specificly, this function attempts to re-organize the ** More specificly, this function attempts to re-organize the database so
** database so that the last page of the file currently in use ** that the last page of the file currently in use is no longer in use.
** is no longer in use.
** **
** If the nFin parameter is non-zero, this function assumes ** Parameter nFin is the number of pages that this database would contain
** that the caller will keep calling incrVacuumStep() until ** were this function called until it returns SQLITE_DONE.
** it returns SQLITE_DONE or an error, and that nFin is the **
** number of pages the database file will contain after this ** If the bCommit parameter is non-zero, this function assumes that the
** process is complete. If nFin is zero, it is assumed that ** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
** incrVacuumStep() will be called a finite amount of times ** or an error. bCommit is passed true for an auto-vacuum-on-commmit
** which may or may not empty the freelist. A full autovacuum ** operation, or false for an incremental vacuum.
** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0.
*/ */
static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg, int bCommit){
Pgno nFreeList; /* Number of pages still on the free-list */ Pgno nFreeList; /* Number of pages still on the free-list */
int rc; int rc;
@@ -2953,15 +2956,15 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} }
if( eType==PTRMAP_FREEPAGE ){ if( eType==PTRMAP_FREEPAGE ){
if( nFin==0 ){ if( bCommit==0 ){
/* Remove the page from the files free-list. This is not required /* Remove the page from the files free-list. This is not required
** if nFin is non-zero. In that case, the free-list will be ** if bCommit is non-zero. In that case, the free-list will be
** truncated to zero after this function returns, so it doesn't ** truncated to zero after this function returns, so it doesn't
** matter if it still contains some garbage entries. ** matter if it still contains some garbage entries.
*/ */
Pgno iFreePg; Pgno iFreePg;
MemPage *pFreePg; MemPage *pFreePg;
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1); rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }
@@ -2971,34 +2974,37 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} else { } else {
Pgno iFreePg; /* Index of free page to move pLastPg to */ Pgno iFreePg; /* Index of free page to move pLastPg to */
MemPage *pLastPg; MemPage *pLastPg;
u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }
/* If nFin is zero, this loop runs exactly once and page pLastPg /* If bCommit is zero, this loop runs exactly once and page pLastPg
** is swapped with the first free page pulled off the free list. ** is swapped with the first free page pulled off the free list.
** **
** On the other hand, if nFin is greater than zero, then keep ** On the other hand, if bCommit is greater than zero, then keep
** looping until a free-page located within the first nFin pages ** looping until a free-page located within the first nFin pages
** of the file is found. ** of the file is found.
*/ */
if( bCommit==0 ){
eMode = BTALLOC_LE;
iNear = nFin;
}
do { do {
MemPage *pFreePg; MemPage *pFreePg;
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0); rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
releasePage(pLastPg); releasePage(pLastPg);
return rc; return rc;
} }
releasePage(pFreePg); releasePage(pFreePg);
}while( nFin!=0 && iFreePg>nFin ); }while( bCommit && iFreePg>nFin );
assert( iFreePg<iLastPg ); assert( iFreePg<iLastPg );
rc = sqlite3PagerWrite(pLastPg->pDbPage); rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
if( rc==SQLITE_OK ){
rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
}
releasePage(pLastPg); releasePage(pLastPg);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
@@ -3006,29 +3012,39 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} }
} }
if( nFin==0 ){ if( bCommit==0 ){
iLastPg--; do {
while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){
if( PTRMAP_ISPAGE(pBt, iLastPg) ){
MemPage *pPg;
rc = btreeGetPage(pBt, iLastPg, &pPg, 0);
if( rc!=SQLITE_OK ){
return rc;
}
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
if( rc!=SQLITE_OK ){
return rc;
}
}
iLastPg--; iLastPg--;
} }while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) );
sqlite3PagerTruncateImage(pBt->pPager, iLastPg); pBt->bDoTruncate = 1;
pBt->nPage = iLastPg; pBt->nPage = iLastPg;
} }
return SQLITE_OK; return SQLITE_OK;
} }
/*
** The database opened by the first argument is an auto-vacuum database
** nOrig pages in size containing nFree free pages. Return the expected
** size of the database in pages following an auto-vacuum operation.
*/
static Pgno finalDbSize(BtShared *pBt, Pgno nOrig, Pgno nFree){
int nEntry; /* Number of entries on one ptrmap page */
Pgno nPtrmap; /* Number of PtrMap pages to be freed */
Pgno nFin; /* Return value */
nEntry = pBt->usableSize/5;
nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
nFin = nOrig - nFree - nPtrmap;
if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
nFin--;
}
while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
nFin--;
}
return nFin;
}
/* /*
** A write-transaction must be opened before calling this function. ** A write-transaction must be opened before calling this function.
** It performs a single unit of work towards an incremental vacuum. ** It performs a single unit of work towards an incremental vacuum.
@@ -3046,11 +3062,21 @@ int sqlite3BtreeIncrVacuum(Btree *p){
if( !pBt->autoVacuum ){ if( !pBt->autoVacuum ){
rc = SQLITE_DONE; rc = SQLITE_DONE;
}else{ }else{
invalidateAllOverflowCache(pBt); Pgno nOrig = btreePagecount(pBt);
rc = incrVacuumStep(pBt, 0, btreePagecount(pBt)); Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
if( rc==SQLITE_OK ){ Pgno nFin = finalDbSize(pBt, nOrig, nFree);
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[28], pBt->nPage); if( nOrig<nFin ){
rc = SQLITE_CORRUPT_BKPT;
}else if( nFree>0 ){
invalidateAllOverflowCache(pBt);
rc = incrVacuumStep(pBt, nFin, nOrig, 0);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[28], pBt->nPage);
}
}else{
rc = SQLITE_DONE;
} }
} }
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
@@ -3077,9 +3103,7 @@ static int autoVacuumCommit(BtShared *pBt){
if( !pBt->incrVacuum ){ if( !pBt->incrVacuum ){
Pgno nFin; /* Number of pages in database after autovacuuming */ Pgno nFin; /* Number of pages in database after autovacuuming */
Pgno nFree; /* Number of pages on the freelist initially */ Pgno nFree; /* Number of pages on the freelist initially */
Pgno nPtrmap; /* Number of PtrMap pages to be freed */
Pgno iFree; /* The next page to be freed */ Pgno iFree; /* The next page to be freed */
int nEntry; /* Number of entries on one ptrmap page */
Pgno nOrig; /* Database size before freeing */ Pgno nOrig; /* Database size before freeing */
nOrig = btreePagecount(pBt); nOrig = btreePagecount(pBt);
@@ -3092,26 +3116,18 @@ static int autoVacuumCommit(BtShared *pBt){
} }
nFree = get4byte(&pBt->pPage1->aData[36]); nFree = get4byte(&pBt->pPage1->aData[36]);
nEntry = pBt->usableSize/5; nFin = finalDbSize(pBt, nOrig, nFree);
nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry;
nFin = nOrig - nFree - nPtrmap;
if( nOrig>PENDING_BYTE_PAGE(pBt) && nFin<PENDING_BYTE_PAGE(pBt) ){
nFin--;
}
while( PTRMAP_ISPAGE(pBt, nFin) || nFin==PENDING_BYTE_PAGE(pBt) ){
nFin--;
}
if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT; if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){ for(iFree=nOrig; iFree>nFin && rc==SQLITE_OK; iFree--){
rc = incrVacuumStep(pBt, nFin, iFree); rc = incrVacuumStep(pBt, nFin, iFree, 1);
} }
if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){ if( (rc==SQLITE_DONE || rc==SQLITE_OK) && nFree>0 ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[32], 0); put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0); put4byte(&pBt->pPage1->aData[36], 0);
put4byte(&pBt->pPage1->aData[28], nFin); put4byte(&pBt->pPage1->aData[28], nFin);
sqlite3PagerTruncateImage(pBt->pPager, nFin); pBt->bDoTruncate = 1;
pBt->nPage = nFin; pBt->nPage = nFin;
} }
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@@ -3166,6 +3182,9 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
return rc; return rc;
} }
} }
if( pBt->bDoTruncate ){
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
}
#endif #endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0); rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
sqlite3BtreeLeave(p); sqlite3BtreeLeave(p);
@@ -3181,6 +3200,9 @@ static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt; BtShared *pBt = p->pBt;
assert( sqlite3BtreeHoldsMutex(p) ); assert( sqlite3BtreeHoldsMutex(p) );
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->bDoTruncate = 0;
#endif
btreeClearHasContent(pBt); btreeClearHasContent(pBt);
if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
/* If there are other active statements that belong to this database /* If there are other active statements that belong to this database
@@ -4867,7 +4889,7 @@ static int allocateBtreePage(
MemPage **ppPage, MemPage **ppPage,
Pgno *pPgno, Pgno *pPgno,
Pgno nearby, Pgno nearby,
u8 exact u8 eMode
){ ){
MemPage *pPage1; MemPage *pPage1;
int rc; int rc;
@@ -4895,16 +4917,19 @@ static int allocateBtreePage(
** the entire-list will be searched for that page. ** the entire-list will be searched for that page.
*/ */
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
if( exact && nearby<=mxPage ){ if( eMode==BTALLOC_EXACT ){
u8 eType; if( nearby<=mxPage ){
assert( nearby>0 ); u8 eType;
assert( pBt->autoVacuum ); assert( nearby>0 );
rc = ptrmapGet(pBt, nearby, &eType, 0); assert( pBt->autoVacuum );
if( rc ) return rc; rc = ptrmapGet(pBt, nearby, &eType, 0);
if( eType==PTRMAP_FREEPAGE ){ if( rc ) return rc;
searchList = 1; if( eType==PTRMAP_FREEPAGE ){
searchList = 1;
}
} }
*pPgno = nearby; }else if( eMode==BTALLOC_LE ){
searchList = 1;
} }
#endif #endif
@@ -4959,11 +4984,13 @@ static int allocateBtreePage(
rc = SQLITE_CORRUPT_BKPT; rc = SQLITE_CORRUPT_BKPT;
goto end_allocate_page; goto end_allocate_page;
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
}else if( searchList && nearby==iTrunk ){ }else if( searchList
&& (nearby==iTrunk || (iTrunk<nearby && eMode==BTALLOC_LE))
){
/* The list is being searched and this trunk page is the page /* The list is being searched and this trunk page is the page
** to allocate, regardless of whether it has leaves. ** to allocate, regardless of whether it has leaves.
*/ */
assert( *pPgno==iTrunk ); *pPgno = iTrunk;
*ppPage = pTrunk; *ppPage = pTrunk;
searchList = 0; searchList = 0;
rc = sqlite3PagerWrite(pTrunk->pDbPage); rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -5026,14 +5053,24 @@ static int allocateBtreePage(
unsigned char *aData = pTrunk->aData; unsigned char *aData = pTrunk->aData;
if( nearby>0 ){ if( nearby>0 ){
u32 i; u32 i;
int dist;
closest = 0; closest = 0;
dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby); if( eMode==BTALLOC_LE ){
for(i=1; i<k; i++){ for(i=0; i<k; i++){
int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby); iPage = get4byte(&aData[8+i*4]);
if( d2<dist ){ if( iPage<=nearby ){
closest = i; closest = i;
dist = d2; break;
}
}
}else{
int dist;
dist = sqlite3AbsInt32(get4byte(&aData[8]) - nearby);
for(i=1; i<k; i++){
int d2 = sqlite3AbsInt32(get4byte(&aData[8+i*4]) - nearby);
if( d2<dist ){
closest = i;
dist = d2;
}
} }
} }
}else{ }else{
@@ -5047,7 +5084,9 @@ static int allocateBtreePage(
goto end_allocate_page; goto end_allocate_page;
} }
testcase( iPage==mxPage ); testcase( iPage==mxPage );
if( !searchList || iPage==nearby ){ if( !searchList
|| (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
){
int noContent; int noContent;
*pPgno = iPage; *pPgno = iPage;
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
@@ -5074,8 +5113,26 @@ static int allocateBtreePage(
pPrevTrunk = 0; pPrevTrunk = 0;
}while( searchList ); }while( searchList );
}else{ }else{
/* There are no pages on the freelist, so create a new page at the /* There are no pages on the freelist, so append a new page to the
** end of the file */ ** database image.
**
** Normally, new pages allocated by this block can be requested from the
** pager layer with the 'no-content' flag set. This prevents the pager
** from trying to read the pages content from disk. However, if the
** current transaction has already run one or more incremental-vacuum
** steps, then the page we are about to allocate may contain content
** that is required in the event of a rollback. In this case, do
** not set the no-content flag. This causes the pager to load and journal
** the current page content before overwriting it.
**
** Note that the pager will not actually attempt to load or journal
** content for any page that really does lie past the end of the database
** file on disk. So the effects of disabling the no-content optimization
** here are confined to those pages that lie between the end of the
** database image and the end of the database file.
*/
int bNoContent = (0==pBt->bDoTruncate);
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage); rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc ) return rc; if( rc ) return rc;
pBt->nPage++; pBt->nPage++;
@@ -5090,7 +5147,7 @@ static int allocateBtreePage(
MemPage *pPg = 0; MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage)); TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) ); assert( pBt->nPage!=PENDING_BYTE_PAGE(pBt) );
rc = btreeGetPage(pBt, pBt->nPage, &pPg, 1); rc = btreeGetPage(pBt, pBt->nPage, &pPg, bNoContent);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pPg->pDbPage); rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg); releasePage(pPg);
@@ -5104,7 +5161,7 @@ static int allocateBtreePage(
*pPgno = pBt->nPage; *pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
rc = btreeGetPage(pBt, *pPgno, ppPage, 1); rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc; if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage); rc = sqlite3PagerWrite((*ppPage)->pDbPage);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
@@ -7119,7 +7176,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int createTabFlags){
** be moved to the allocated page (unless the allocated page happens ** be moved to the allocated page (unless the allocated page happens
** to reside at pgnoRoot). ** to reside at pgnoRoot).
*/ */
rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1); rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){ if( rc!=SQLITE_OK ){
return rc; return rc;
} }

View File

@@ -411,6 +411,7 @@ struct BtShared {
#ifndef SQLITE_OMIT_AUTOVACUUM #ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */ u8 autoVacuum; /* True if auto-vacuum is enabled */
u8 incrVacuum; /* True if incr-vacuum is enabled */ u8 incrVacuum; /* True if incr-vacuum is enabled */
u8 bDoTruncate; /* True to truncate db on commit */
#endif #endif
u8 inTransaction; /* Transaction state */ u8 inTransaction; /* Transaction state */
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */ u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */

View File

@@ -1838,6 +1838,8 @@ static int pager_error(Pager *pPager, int rc){
return rc; return rc;
} }
static int pager_truncate(Pager *pPager, Pgno nPage);
/* /*
** This routine ends a transaction. A transaction is usually ended by ** This routine ends a transaction. A transaction is usually ended by
** either a COMMIT or a ROLLBACK operation. This routine may be called ** either a COMMIT or a ROLLBACK operation. This routine may be called
@@ -1891,7 +1893,7 @@ static int pager_error(Pager *pPager, int rc){
** to the first error encountered (the journal finalization one) is ** to the first error encountered (the journal finalization one) is
** returned. ** returned.
*/ */
static int pager_end_transaction(Pager *pPager, int hasMaster){ static int pager_end_transaction(Pager *pPager, int hasMaster, int bCommit){
int rc = SQLITE_OK; /* Error code from journal finalization operation */ int rc = SQLITE_OK; /* Error code from journal finalization operation */
int rc2 = SQLITE_OK; /* Error code from db file unlock operation */ int rc2 = SQLITE_OK; /* Error code from db file unlock operation */
@@ -1977,7 +1979,17 @@ static int pager_end_transaction(Pager *pPager, int hasMaster){
*/ */
rc2 = sqlite3WalEndWriteTransaction(pPager->pWal); rc2 = sqlite3WalEndWriteTransaction(pPager->pWal);
assert( rc2==SQLITE_OK ); assert( rc2==SQLITE_OK );
}else if( rc==SQLITE_OK && bCommit && pPager->dbFileSize>pPager->dbSize ){
/* This branch is taken when committing a transaction in rollback-journal
** mode if the database file on disk is larger than the database image.
** At this point the journal has been finalized and the transaction
** successfully committed, but the EXCLUSIVE lock is still held on the
** file. So it is safe to truncate the database file to its minimum
** required size. */
assert( pPager->eLock==EXCLUSIVE_LOCK );
rc = pager_truncate(pPager, pPager->dbSize);
} }
if( !pPager->exclusiveMode if( !pPager->exclusiveMode
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0)) && (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){ ){
@@ -2016,7 +2028,7 @@ static void pagerUnlockAndRollback(Pager *pPager){
sqlite3EndBenignMalloc(); sqlite3EndBenignMalloc();
}else if( !pPager->exclusiveMode ){ }else if( !pPager->exclusiveMode ){
assert( pPager->eState==PAGER_READER ); assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0); pager_end_transaction(pPager, 0, 0);
} }
} }
pager_unlock(pPager); pager_unlock(pPager);
@@ -2791,7 +2803,7 @@ end_playback:
rc = sqlite3PagerSync(pPager); rc = sqlite3PagerSync(pPager);
} }
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = pager_end_transaction(pPager, zMaster[0]!='\0'); rc = pager_end_transaction(pPager, zMaster[0]!='\0', 0);
testcase( rc!=SQLITE_OK ); testcase( rc!=SQLITE_OK );
} }
if( rc==SQLITE_OK && zMaster[0] && res ){ if( rc==SQLITE_OK && zMaster[0] && res ){
@@ -5885,36 +5897,6 @@ int sqlite3PagerCommitPhaseOne(
#endif #endif
if( rc!=SQLITE_OK ) goto commit_phase_one_exit; if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
/* If this transaction has made the database smaller, then all pages
** being discarded by the truncation must be written to the journal
** file.
**
** Before reading the pages with page numbers larger than the
** current value of Pager.dbSize, set dbSize back to the value
** that it took at the start of the transaction. Otherwise, the
** calls to sqlite3PagerGet() return zeroed pages instead of
** reading data from the database file.
*/
if( pPager->dbSize<pPager->dbOrigSize
&& pPager->journalMode!=PAGER_JOURNALMODE_OFF
){
Pgno i; /* Iterator variable */
const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */
const Pgno dbSize = pPager->dbSize; /* Database image size */
pPager->dbSize = pPager->dbOrigSize;
for( i=dbSize+1; i<=pPager->dbOrigSize; i++ ){
if( !sqlite3BitvecTest(pPager->pInJournal, i) && i!=iSkip ){
PgHdr *pPage; /* Page to journal */
rc = sqlite3PagerGet(pPager, i, &pPage);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
rc = sqlite3PagerWrite(pPage);
sqlite3PagerUnref(pPage);
if( rc!=SQLITE_OK ) goto commit_phase_one_exit;
}
}
pPager->dbSize = dbSize;
}
/* Write the master journal name into the journal file. If a master /* Write the master journal name into the journal file. If a master
** journal file name has already been written to the journal file, ** journal file name has already been written to the journal file,
** or if zMaster is NULL (no master journal), then this call is a no-op. ** or if zMaster is NULL (no master journal), then this call is a no-op.
@@ -5943,10 +5925,13 @@ int sqlite3PagerCommitPhaseOne(
} }
sqlite3PcacheCleanAll(pPager->pPCache); sqlite3PcacheCleanAll(pPager->pPCache);
/* If the file on disk is not the same size as the database image, /* If the file on disk is smaller than the database image, use
** then use pager_truncate to grow or shrink the file here. ** pager_truncate to grow the file here. This can happen if the database
*/ ** image was extended as part of the current transaction and then the
if( pPager->dbSize!=pPager->dbFileSize ){ ** last page in the db image moved to the free-list. In this case the
** last page is never written out to disk, leaving the database file
** undersized. Fix this now if it is the case. */
if( pPager->dbSize>pPager->dbFileSize ){
Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager)); Pgno nNew = pPager->dbSize - (pPager->dbSize==PAGER_MJ_PGNO(pPager));
assert( pPager->eState==PAGER_WRITER_DBMOD ); assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew); rc = pager_truncate(pPager, nNew);
@@ -6019,7 +6004,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *pPager){
} }
PAGERTRACE(("COMMIT %d\n", PAGERID(pPager))); PAGERTRACE(("COMMIT %d\n", PAGERID(pPager)));
rc = pager_end_transaction(pPager, pPager->setMaster); rc = pager_end_transaction(pPager, pPager->setMaster, 1);
return pager_error(pPager, rc); return pager_error(pPager, rc);
} }
@@ -6064,11 +6049,11 @@ int sqlite3PagerRollback(Pager *pPager){
if( pagerUseWal(pPager) ){ if( pagerUseWal(pPager) ){
int rc2; int rc2;
rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1); rc = sqlite3PagerSavepoint(pPager, SAVEPOINT_ROLLBACK, -1);
rc2 = pager_end_transaction(pPager, pPager->setMaster); rc2 = pager_end_transaction(pPager, pPager->setMaster, 0);
if( rc==SQLITE_OK ) rc = rc2; if( rc==SQLITE_OK ) rc = rc2;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){ }else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState; int eState = pPager->eState;
rc = pager_end_transaction(pPager, 0); rc = pager_end_transaction(pPager, 0, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){ if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
/* This can happen using journal_mode=off. Move the pager to the error /* This can happen using journal_mode=off. Move the pager to the error
** state to indicate that the contents of the cache may not be trusted. ** state to indicate that the contents of the cache may not be trusted.

View File

@@ -265,7 +265,8 @@ static void tvfsExecTcl(
const char *zMethod, const char *zMethod,
Tcl_Obj *arg1, Tcl_Obj *arg1,
Tcl_Obj *arg2, Tcl_Obj *arg2,
Tcl_Obj *arg3 Tcl_Obj *arg3,
Tcl_Obj *arg4
){ ){
int rc; /* Return code from Tcl_EvalObj() */ int rc; /* Return code from Tcl_EvalObj() */
Tcl_Obj *pEval; Tcl_Obj *pEval;
@@ -282,6 +283,7 @@ static void tvfsExecTcl(
if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1); if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2); if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3); if( arg3 ) Tcl_ListObjAppendElement(p->interp, pEval, arg3);
if( arg4 ) Tcl_ListObjAppendElement(p->interp, pEval, arg4);
rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL); rc = Tcl_EvalObjEx(p->interp, pEval, TCL_EVAL_GLOBAL);
if( rc!=TCL_OK ){ if( rc!=TCL_OK ){
@@ -302,7 +304,7 @@ static int tvfsClose(sqlite3_file *pFile){
if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){ if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
tvfsExecTcl(p, "xClose", tvfsExecTcl(p, "xClose",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
); );
} }
@@ -333,7 +335,7 @@ static int tvfsRead(
Testvfs *p = (Testvfs *)pFd->pVfs->pAppData; Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
if( p->pScript && p->mask&TESTVFS_READ_MASK ){ if( p->pScript && p->mask&TESTVFS_READ_MASK ){
tvfsExecTcl(p, "xRead", tvfsExecTcl(p, "xRead",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -362,7 +364,7 @@ static int tvfsWrite(
if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){ if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
tvfsExecTcl(p, "xWrite", tvfsExecTcl(p, "xWrite",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
Tcl_NewWideIntObj(iOfst) Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt)
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -390,7 +392,7 @@ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){ if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
tvfsExecTcl(p, "xTruncate", tvfsExecTcl(p, "xTruncate",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0 Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -431,7 +433,7 @@ static int tvfsSync(sqlite3_file *pFile, int flags){
tvfsExecTcl(p, "xSync", tvfsExecTcl(p, "xSync",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
Tcl_NewStringObj(zFlags, -1) Tcl_NewStringObj(zFlags, -1), 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -578,7 +580,7 @@ static int tvfsOpen(
z += strlen(z) + 1; z += strlen(z) + 1;
} }
} }
tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0); tvfsExecTcl(p, "xOpen", Tcl_NewStringObj(pFd->zFilename, -1), pArg, 0, 0);
Tcl_DecrRefCount(pArg); Tcl_DecrRefCount(pArg);
if( tvfsResultCode(p, &rc) ){ if( tvfsResultCode(p, &rc) ){
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
@@ -635,7 +637,7 @@ static int tvfsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){ if( p->pScript && p->mask&TESTVFS_DELETE_MASK ){
tvfsExecTcl(p, "xDelete", tvfsExecTcl(p, "xDelete",
Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0 Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -663,7 +665,7 @@ static int tvfsAccess(
if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE"; if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ"; if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
tvfsExecTcl(p, "xAccess", tvfsExecTcl(p, "xAccess",
Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0 Tcl_NewStringObj(zPath, -1), Tcl_NewStringObj(zArg, -1), 0, 0
); );
if( tvfsResultCode(p, &rc) ){ if( tvfsResultCode(p, &rc) ){
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
@@ -691,7 +693,7 @@ static int tvfsFullPathname(
Testvfs *p = (Testvfs *)pVfs->pAppData; Testvfs *p = (Testvfs *)pVfs->pAppData;
if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){ if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
int rc; int rc;
tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0); tvfsExecTcl(p, "xFullPathname", Tcl_NewStringObj(zPath, -1), 0, 0, 0);
if( tvfsResultCode(p, &rc) ){ if( tvfsResultCode(p, &rc) ){
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
} }
@@ -771,7 +773,7 @@ static int tvfsShmOpen(sqlite3_file *pFile){
*/ */
Tcl_ResetResult(p->interp); Tcl_ResetResult(p->interp);
if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){ if( p->pScript && p->mask&TESTVFS_SHMOPEN_MASK ){
tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0, 0);
if( tvfsResultCode(p, &rc) ){ if( tvfsResultCode(p, &rc) ){
if( rc!=SQLITE_OK ) return rc; if( rc!=SQLITE_OK ) return rc;
} }
@@ -841,7 +843,7 @@ static int tvfsShmMap(
Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(pgsz));
Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite)); Tcl_ListObjAppendElement(p->interp, pArg, Tcl_NewIntObj(isWrite));
tvfsExecTcl(p, "xShmMap", tvfsExecTcl(p, "xShmMap",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, pArg, 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
Tcl_DecrRefCount(pArg); Tcl_DecrRefCount(pArg);
@@ -891,7 +893,7 @@ static int tvfsShmLock(
} }
tvfsExecTcl(p, "xShmLock", tvfsExecTcl(p, "xShmLock",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
Tcl_NewStringObj(zLock, -1) Tcl_NewStringObj(zLock, -1), 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }
@@ -937,7 +939,7 @@ static void tvfsShmBarrier(sqlite3_file *pFile){
if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){ if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
tvfsExecTcl(p, "xShmBarrier", tvfsExecTcl(p, "xShmBarrier",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0
); );
} }
} }
@@ -961,7 +963,7 @@ static int tvfsShmUnmap(
if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){ if( p->pScript && p->mask&TESTVFS_SHMCLOSE_MASK ){
tvfsExecTcl(p, "xShmUnmap", tvfsExecTcl(p, "xShmUnmap",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0, 0
); );
tvfsResultCode(p, &rc); tvfsResultCode(p, &rc);
} }

137
test/incrvacuum3.test Normal file
View File

@@ -0,0 +1,137 @@
# 2013 Feb 25
#
# 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 the SQLite library, focusing
# on the incremental vacuum feature.
#
# The tests in this file were added at the same time as optimizations
# were made to:
#
# * Truncate the database after a rollback mode commit, and
#
# * Avoid moving pages to locations from which they may need to be moved
# a second time if an incremental-vacuum proccess is allowed to vacuum
# the entire database.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
set testprefix incrvacuum3
# If this build of the library does not support auto-vacuum, omit this
# whole file.
ifcapable {!autovacuum || !pragma} {
finish_test
return
}
proc check_on_disk {} {
# Copy the files for database "test.db" to "test2.db".
forcedelete test2.db test2.db-journal test2.db-wal
forcecopy test.db test2.db
if {[file exists test.db-journal]} {
forcecopy test.db-journal test2.db-journal
}
if {[file exists test.db-wal]} {
forcecopy test.db-wal test2.db-wal
}
# Open "test2.db" and check it is Ok.
sqlite3 dbcheck test2.db
set ret [dbcheck eval { PRAGMA integrity_check }]
dbcheck close
set ret
}
# Run these tests once in rollback journal mode, and once in wal mode.
#
foreach {T jrnl_mode} {
1 delete
2 wal
} {
catch { db close }
forcedelete test.db test.db-journal test.db-wal
sqlite3 db test.db
db eval {
PRAGMA cache_size = 5;
PRAGMA page_size = 1024;
PRAGMA auto_vacuum = 2;
}
db eval "PRAGMA journal_mode = $jrnl_mode"
foreach {tn sql} {
1 {
CREATE TABLE t1(x UNIQUE);
INSERT INTO t1 VALUES(randomblob(400));
INSERT INTO t1 VALUES(randomblob(400));
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 4
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 8
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 16
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 32
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256
}
2 {
DELETE FROM t1 WHERE rowid%8;
}
3 {
BEGIN;
PRAGMA incremental_vacuum = 100;
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256
ROLLBACK;
}
4 {
BEGIN;
SAVEPOINT one;
PRAGMA incremental_vacuum = 100;
SAVEPOINT two;
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256
}
5 { ROLLBACK to two }
6 { ROLLBACK to one }
7 {
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64
PRAGMA incremental_vacuum = 1000;
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 256
ROLLBACK;
}
8 {
BEGIN;
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 64
PRAGMA incremental_vacuum = 1000;
INSERT INTO t1 SELECT randomblob(400) FROM t1; -- 128
COMMIT;
}
} {
do_execsql_test $T.1.$tn.1 $sql
do_execsql_test $T.1.$tn.2 {PRAGMA integrity_check} ok
do_test $T.1.$tn.3 { check_on_disk } ok
}
do_execsql_test $T.1.x.1 { PRAGMA freelist_count } 0
do_execsql_test $T.1.x.2 { SELECT count(*) FROM t1 } 128
}
finish_test

View File

@@ -139,8 +139,8 @@ ifcapable shared_cache {
# Figure out how big the database is and how many free pages it # Figure out how big the database is and how many free pages it
# has before running incremental-vacuum. # has before running incremental-vacuum.
# #
set nPage [expr {[file size test.db]/1024}]
set nFree [execsql {pragma freelist_count} db1] set nFree [execsql {pragma freelist_count} db1]
set nPage [execsql {pragma page_count} db1]
# Now run incremental-vacuum to vacuum 5 pages from the db file. # Now run incremental-vacuum to vacuum 5 pages from the db file.
# The iTest'th I/O call is set to fail. # The iTest'th I/O call is set to fail.
@@ -158,7 +158,7 @@ ifcapable shared_cache {
set ::sqlite_io_error_hardhit 0 set ::sqlite_io_error_hardhit 0
set nFree2 [execsql {pragma freelist_count} db1] set nFree2 [execsql {pragma freelist_count} db1]
set nPage2 [expr {[file size test.db]/1024}] set nPage2 [execsql {pragma page_count} db1]
do_test incrvacuum-ioerr-4.$iTest.2 { do_test incrvacuum-ioerr-4.$iTest.2 {
set shrink [expr {$nPage-$nPage2}] set shrink [expr {$nPage-$nPage2}]

View File

@@ -1118,6 +1118,25 @@ proc crashsql {args} {
lappend r $msg lappend r $msg
} }
proc run_ioerr_prep {} {
set ::sqlite_io_error_pending 0
catch {db close}
catch {db2 close}
catch {forcedelete test.db}
catch {forcedelete test.db-journal}
catch {forcedelete test2.db}
catch {forcedelete test2.db-journal}
set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
if {[info exists ::ioerropts(-tclprep)]} {
eval $::ioerropts(-tclprep)
}
if {[info exists ::ioerropts(-sqlprep)]} {
execsql $::ioerropts(-sqlprep)
}
expr 0
}
# Usage: do_ioerr_test <test number> <options...> # Usage: do_ioerr_test <test number> <options...>
# #
# This proc is used to implement test cases that check that IO errors # This proc is used to implement test cases that check that IO errors
@@ -1151,9 +1170,25 @@ proc do_ioerr_test {testname args} {
# a couple of obscure IO errors that do not return them. # a couple of obscure IO errors that do not return them.
set ::ioerropts(-erc) 0 set ::ioerropts(-erc) 0
# Create a single TCL script from the TCL and SQL specified
# as the body of the test.
set ::ioerrorbody {}
if {[info exists ::ioerropts(-tclbody)]} {
append ::ioerrorbody "$::ioerropts(-tclbody)\n"
}
if {[info exists ::ioerropts(-sqlbody)]} {
append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
}
save_prng_state
if {$::ioerropts(-cksum)} {
run_ioerr_prep
eval $::ioerrorbody
set ::goodcksum [cksum]
}
set ::go 1 set ::go 1
#reset_prng_state #reset_prng_state
save_prng_state
for {set n $::ioerropts(-start)} {$::go} {incr n} { for {set n $::ioerropts(-start)} {$::go} {incr n} {
set ::TN $n set ::TN $n
incr ::ioerropts(-count) -1 incr ::ioerropts(-count) -1
@@ -1170,27 +1205,12 @@ proc do_ioerr_test {testname args} {
# Delete the files test.db and test2.db, then execute the TCL and # Delete the files test.db and test2.db, then execute the TCL and
# SQL (in that order) to prepare for the test case. # SQL (in that order) to prepare for the test case.
do_test $testname.$n.1 { do_test $testname.$n.1 {
set ::sqlite_io_error_pending 0 run_ioerr_prep
catch {db close}
catch {db2 close}
catch {forcedelete test.db}
catch {forcedelete test.db-journal}
catch {forcedelete test2.db}
catch {forcedelete test2.db-journal}
set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
sqlite3_extended_result_codes $::DB $::ioerropts(-erc)
if {[info exists ::ioerropts(-tclprep)]} {
eval $::ioerropts(-tclprep)
}
if {[info exists ::ioerropts(-sqlprep)]} {
execsql $::ioerropts(-sqlprep)
}
expr 0
} {0} } {0}
# Read the 'checksum' of the database. # Read the 'checksum' of the database.
if {$::ioerropts(-cksum)} { if {$::ioerropts(-cksum)} {
set checksum [cksum] set ::checksum [cksum]
} }
# Set the Nth IO error to fail. # Set the Nth IO error to fail.
@@ -1199,19 +1219,9 @@ proc do_ioerr_test {testname args} {
set ::sqlite_io_error_pending $n set ::sqlite_io_error_pending $n
}] $n }] $n
# Create a single TCL script from the TCL and SQL specified # Execute the TCL script created for the body of this test. If
# as the body of the test. # at least N IO operations performed by SQLite as a result of
set ::ioerrorbody {} # the script, the Nth will fail.
if {[info exists ::ioerropts(-tclbody)]} {
append ::ioerrorbody "$::ioerropts(-tclbody)\n"
}
if {[info exists ::ioerropts(-sqlbody)]} {
append ::ioerrorbody "db eval {$::ioerropts(-sqlbody)}"
}
# Execute the TCL Script created in the above block. If
# there are at least N IO operations performed by SQLite as
# a result of the script, the Nth will fail.
do_test $testname.$n.3 { do_test $testname.$n.3 {
set ::sqlite_io_error_hit 0 set ::sqlite_io_error_hit 0
set ::sqlite_io_error_hardhit 0 set ::sqlite_io_error_hardhit 0
@@ -1315,8 +1325,15 @@ proc do_ioerr_test {testname args} {
catch {db close} catch {db close}
catch {db2 close} catch {db2 close}
set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db] set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
cksum set nowcksum [cksum]
} $checksum set res [expr {$nowcksum==$::checksum || $nowcksum==$::goodcksum}]
if {$res==0} {
puts "now=$nowcksum"
puts "the=$::checksum"
puts "fwd=$::goodcksum"
}
set res
} 1
} }
set ::sqlite_io_error_hardhit 0 set ::sqlite_io_error_hardhit 0

View File

@@ -10,8 +10,8 @@
#*********************************************************************** #***********************************************************************
# #
# Ticket #3762: Make sure that an incremental vacuum that reduces the # Ticket #3762: Make sure that an incremental vacuum that reduces the
# size of the database file such that a pointer-map page is elemented # size of the database file such that if a pointer-map page is eliminated
# can be correctly rolled back. # it can be correctly rolled back.
# #
# That ticket #3762 has been fixed has already been verified by the # That ticket #3762 has been fixed has already been verified by the
# savepoint6.test test script. But this script is simplier and a # savepoint6.test test script. But this script is simplier and a