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:
29
manifest
29
manifest
@@ -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
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
9bd9bd9cab8c804c1a51d472199459176044a633
|
26e235b7a4cd4d0dc9725774d70174c4d369cb98
|
||||||
21
src/backup.c
21
src/backup.c
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
229
src/btree.c
229
src/btree.c
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
|||||||
65
src/pager.c
65
src/pager.c
@@ -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.
|
||||||
|
|||||||
@@ -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
137
test/incrvacuum3.test
Normal 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
|
||||||
|
|
||||||
@@ -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}]
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user