Compare commits

..

16 Commits

Author SHA1 Message Date
mistachkin 8d0b81d764 Prevent Tcl file encoding issues in the tests for the unicode() and char() functions.
FossilOrigin-Name: d2e7dfca5a92074a7984032deb6a4e3681389c72
2013-02-26 05:44:33 +00:00
mistachkin c9545442f9 Remove extra use of the sqlite3_value_int64() function.
FossilOrigin-Name: 6d7973524a7d3bf3158fdac58975945da7f51740
2013-02-26 05:42:30 +00:00
drh fbc1ddf079 Add new SQL functions unicode() and char().
FossilOrigin-Name: be2493905281e12c7f4c146ab17c8872e52da350
2013-02-25 14:39:47 +00:00
drh c6a67173bd Strengthen the final test case in index5.test. Also provide additional
diagnostic information out the output.

FossilOrigin-Name: 47b6418242bb2cd718d1a73b0cb73a43ee74e503
2013-02-25 13:55:59 +00:00
dan 132d93838d Fix an error in test script index5.test.
FossilOrigin-Name: d87e5acf2802d2887e20f79a8bd4990b2cd47b91
2013-02-25 13:44:56 +00:00
dan f2874b0e1d Merge the incr-vacuum-opt branch with the trunk.
FossilOrigin-Name: 26e235b7a4cd4d0dc9725774d70174c4d369cb98
2013-02-25 13:31:30 +00:00
dan 295fc442b5 Add test file incrvacuum3.test. No code changes.
FossilOrigin-Name: bf57534188e044fb341315bfc05b7927e66a04e0
2013-02-25 12:06:55 +00:00
dan 896f99e942 Catch a dropped error code in backup.c.
FossilOrigin-Name: ac8ca3ecee4d81bf522b330033e5d85638063670
2013-02-25 07:12:40 +00:00
dan 9138471141 Fix a case where database corruption may cause an assert() to fail.
FossilOrigin-Name: ba33bb059ed4f4547da2880dbc8bd827c06fae34
2013-02-24 11:50:43 +00:00
dan e0b605917e Fix a case in the incremental vacuum code where database corruption was going unreported.
FossilOrigin-Name: 4cd30c72629a7f44b18026a70103aa0bb8a3b959
2013-02-23 19:43:47 +00:00
dan 22d2730047 Fix a cut-and-paste bug causing the library to fail to report database corruption in a few cases.
FossilOrigin-Name: f921df59560d536f5b80eef8c995cbe3ff591bae
2013-02-23 19:11:47 +00:00
dan b88e24fd59 Enhancements to test scripts. No code changes.
FossilOrigin-Name: ccab94c10d54e585de918bbf82dec188287d93b2
2013-02-23 18:58:11 +00:00
dan 87ade19eb9 Fix off-by-one bug in [c3939d2491] uncovered by th3.
FossilOrigin-Name: 66f9faa9a969d004486ee38d492fb3eda6cdebab
2013-02-23 17:49:16 +00:00
dan bc1a3c6ce2 If a rollback mode transaction reduces the size of the database file, avoid actually truncating the file until after the transaction has been committed (but before the db has been unlocked). This means pages that are removed from the database by truncating the file need not be journalled.
FossilOrigin-Name: b73847f17b7ae1298dfc52de6c4c4bc809bc77a3
2013-02-23 16:40:46 +00:00
dan f38b65a85d Fix a problem with the previous commit.
FossilOrigin-Name: 720a3ceafc35b81936ed2eb1f07a7187d104f0a0
2013-02-22 20:57:47 +00:00
dan 51f0b6d568 Avoid moving pages more than once in an incremental vacuum operation.
FossilOrigin-Name: c3939d249119b47bd57baa11a5ed7cc6014fc795
2013-02-22 20:16:34 +00:00
14 changed files with 436 additions and 210 deletions
+20 -19
View File
@@ -1,5 +1,5 @@
C Two\snew\sSQL\sfunctions:\sunicode()\sand\schar().
D 2013-02-22T19:34:25.685
C Prevent\sTcl\sfile\sencoding\sissues\sin\sthe\stests\sfor\sthe\sunicode()\sand\schar()\sfunctions.
D 2013-02-26T05:44:33.955
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in a48faa9e7dd7d556d84f5456eabe5825dd8a6282
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -118,12 +118,12 @@ F src/alter.c f8db986c03eb0bfb221523fc9bbb9d0b70de3168
F src/analyze.c 7553068d21e32a57fc33ab6b2393fc8c1ba41410
F src/attach.c ea5247f240e2c08afd608e9beb380814b86655e1
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c 32e35a3a4ea55b45c0e5f74eeb793aec71491517
F src/backup.c b2cac9f7993f3f9588827b824b1501d0c820fa68
F src/bitvec.c 26675fe8e431dc555e6f2d0e11e651d172234aa1
F src/btmutex.c 976f45a12e37293e32cae0281b15a21d48a8aaa7
F src/btree.c 7a80e4a67f32a2494c383a28a495bf3bd71cc230
F src/btree.c cbad71970cfadfa342fc137ca5e319f98b2d0da1
F src/btree.h 3ad7964d6c5b1c7bff569aab6adfa075f8bf06cd
F src/btreeInt.h 4e5c2bd0f9b36b2a815a6d84f771a61a65830621
F src/btreeInt.h eecc84f02375b2bb7a44abbcbbe3747dde73edb2
F src/build.c 73ca65f32938e4e0d94e831b61b5749b211b79be
F src/callback.c d7e46f40c3cf53c43550b7da7a1d0479910b62cc
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
@@ -133,7 +133,7 @@ F src/delete.c 9b8d308979114991e5dc7cee958316e07186941d
F src/expr.c f6c20285bd36e87ec47f4d840e90a32755e2a90c
F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
F src/fkey.c e16942bd5c8a868ac53287886464a5ed0e72b179
F src/func.c 18b120b9a34daf6d38d50969b6f3471c09b2934a
F src/func.c cac45cca7bbe29bbefef46116174e89e1284763b
F src/global.c e59ecd2c553ad0d4bfbc84ca71231336f8993a7a
F src/hash.c ac3470bbf1ca4ae4e306a8ecb0fdf1731810ffe4
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
@@ -162,7 +162,7 @@ F src/os.h 027491c77d2404c0a678bb3fb06286f331eb9b57
F src/os_common.h 92815ed65f805560b66166e3583470ff94478f04
F src/os_unix.c dfdc04b126f7b05dcb2e2cc5c1262f98acbb49d9
F src/os_win.c eabd00b813577d36bd66271cb08dd64ea0589dac
F src/pager.c 4092c907222cfd451c74fe6bd2fd64b342f7190f
F src/pager.c 0dbf5ff5d5d7d3a21fcab82e9e4d129b6fe6314f
F src/pager.h 1109a06578ec5574dc2c74cf8d9f69daf36fe3e0
F src/parse.y 5d5e12772845805fdfeb889163516b84fbb9ae95
F src/pcache.c f8043b433a57aba85384a531e3937a804432a346
@@ -229,7 +229,7 @@ F src/test_superlock.c 2b97936ca127d13962c3605dbc9a4ef269c424cd
F src/test_syscall.c a992d8c80ea91fbf21fb2dd570db40e77dd7e6ae
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
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_wholenumber.c 3d2b9ed1505c40ad5c5ca2ad16ae7a289d6cc251
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
@@ -508,7 +508,7 @@ F test/fts4merge2.test 5faa558d1b672f82b847d2a337465fa745e46891
F test/fts4merge3.test aab02a09f50fe6baaddc2e159c3eabc116d45fc7
F test/fts4unicode.test 25ccad45896f8e50f6a694cff738a35f798cdb40
F test/full.test 6b3c8fb43c6beab6b95438c1675374b95fab245d
F test/func.test 0ea54895539c0586e86e2dcea59b05cc9e7dd6b2
F test/func.test b058483c17952eff7797b837bbb61e27e6b05606
F test/func2.test 772d66227e4e6684b86053302e2d74a2500e1e0f
F test/func3.test 001021e5b88bd02a3b365a5c5fd8f6f49d39744a
F test/fuzz-oss1.test 4912e528ec9cf2f42134456933659d371c9e0d74
@@ -534,12 +534,13 @@ F test/incrblob_err.test d2562d2771ebffd4b3af89ef64c140dd44371597
F test/incrblobfault.test 917c0292224c64a56ef7215fd633a3a82f805be0
F test/incrvacuum.test d2a6ddf5e429720b5fe502766af747915ccf6c32
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/index2.test ee83c6b5e3173a3d7137140d945d9a5d4fdfb9d6
F test/index3.test 423a25c789fc8cc51aaf2a4370bbdde2d9e9eed7
F test/index4.test 2983216eb8c86ee62d9ed7cb206b5cc3331c0026
F test/index5.test edc8c64ca78bee140c21ce3836820fadf47906bb
F test/index5.test 5616266b2387003bf83c39df626c7292622d58de
F test/indexedby.test be501e381b82b2f8ab406309ba7aac46e221f4ad
F test/indexfault.test 31d4ab9a7d2f6e9616933eb079722362a883eb1d
F test/init.test 15c823093fdabbf7b531fe22cf037134d09587a7
@@ -752,7 +753,7 @@ F test/tclsqlite.test 37a61c2da7e3bfe3b8c1a2867199f6b860df5d43
F test/tempdb.test 19d0f66e2e3eeffd68661a11c83ba5e6ace9128c
F test/temptable.test 51edd31c65ed1560dd600b1796e8325df96318e2
F test/temptrigger.test 26670ed7a39cf2296a7f0a9e0a1d7bdb7abe936d
F test/tester.tcl 0560b09498876da7714fff680c5d892b9228862f
F test/tester.tcl 2918ebca150b67ca25b1682f8ecd857af77fab05
F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
@@ -871,7 +872,7 @@ F test/tkt3718.test 3b59dcb5c4e7754dacd91e7fd353a61492cc402a
F test/tkt3731.test 0c5f4cbffe102d43c3b2188af91a9e36348f974b
F test/tkt3757.test 10cd679a88675c880533083fc79ac04324525595
F test/tkt3761.test b95ea9c98f21cf91325f18a984887e62caceab33
F test/tkt3762.test 2a9f3b03df44ec49ec0cfa8d5da6574c2a7853df
F test/tkt3762.test 4d439ff7abdc8d9323150269d182c37c2d514576
F test/tkt3773.test 7bca904d2a647a6a4a291bd86d7fd7c73855b789
F test/tkt3791.test a6624b9a80b216a26cf473607f42f3e51898c267
F test/tkt3793.test d90ffd75c52413908d15e1c44fc2ea9c80fcc449
@@ -1034,10 +1035,10 @@ F tool/vdbe-compress.tcl f12c884766bd14277f4fcedcae07078011717381
F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4
F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
F tool/win/sqlite.vsix 97894c2790eda7b5bce3cc79cb2a8ec2fde9b3ac
P 9bd9bd9cab8c804c1a51d472199459176044a633
R 57460c96631f0b503379401f124964ab
T *branch * unicode-function
T *sym-unicode-function *
P 6d7973524a7d3bf3158fdac58975945da7f51740
R 49ff1f2b0a98496405857ab66db0f4ad
T *branch * funcTestEncoding
T *sym-funcTestEncoding *
T -sym-trunk *
U drh
Z ff410097880c23c389e99514cc7f5a15
U mistachkin
Z 6ef7a794e2bcbed68706686e53ccd98f
+1 -1
View File
@@ -1 +1 @@
209b21085b9767f10f6ffb7c7cac756fcb74ded5
d2e7dfca5a92074a7984032deb6a4e3681389c72
+18 -3
View File
@@ -462,7 +462,6 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
nDestTruncate = nSrcPage * (pgszSrc/pgszDest);
}
assert( nDestTruncate>0 );
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
if( pgszSrc<pgszDest ){
/* 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;
sqlite3_file * const pFile = sqlite3PagerFile(pDestPager);
Pgno iPg;
int nDstPage;
i64 iOff;
i64 iEnd;
@@ -486,13 +487,26 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
&& 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
** journal synced to disk. So at this point we may safely modify
** the database file in any way, knowing that if a power failure
** occurs, the original database will be reconstructed from the
** 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 */
iEnd = MIN(PENDING_BYTE + pgszDest, iSize);
@@ -519,6 +533,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){
rc = sqlite3PagerSync(pDestPager);
}
}else{
sqlite3PagerTruncateImage(pDestPager, nDestTruncate);
rc = sqlite3PagerCommitPhaseOne(pDestPager, 0, 0);
}
+143 -86
View File
@@ -2595,6 +2595,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
goto trans_begun;
}
assert( pBt->bDoTruncate==0 );
/* Write transactions are not possible on a read-only database */
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
@@ -2909,26 +2910,28 @@ static int relocatePage(
/* Forward declaration required by incrVacuumStep(). */
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,
** return SQLITE_OK. If there is no work to do (and therefore no
** point in calling this function again), return SQLITE_DONE.
** Perform a single step of an incremental-vacuum. If successful, return
** SQLITE_OK. If there is no work to do (and therefore no point in
** 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
** database so that the last page of the file currently in use
** is no longer in use.
** More specificly, this function attempts to re-organize the database so
** that the last page of the file currently in use is no longer in use.
**
** If the nFin parameter is non-zero, this function assumes
** that the caller will keep calling incrVacuumStep() until
** it returns SQLITE_DONE or an error, and that nFin is the
** number of pages the database file will contain after this
** process is complete. If nFin is zero, it is assumed that
** incrVacuumStep() will be called a finite amount of times
** which may or may not empty the freelist. A full autovacuum
** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0.
** Parameter nFin is the number of pages that this database would contain
** were this function called until it returns SQLITE_DONE.
**
** If the bCommit parameter is non-zero, this function assumes that the
** caller will keep calling incrVacuumStep() until it returns SQLITE_DONE
** or an error. bCommit is passed true for an auto-vacuum-on-commmit
** operation, or false for an incremental vacuum.
*/
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 */
int rc;
@@ -2953,15 +2956,15 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
if( eType==PTRMAP_FREEPAGE ){
if( nFin==0 ){
if( bCommit==0 ){
/* 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
** matter if it still contains some garbage entries.
*/
Pgno iFreePg;
MemPage *pFreePg;
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, 1);
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iLastPg, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
@@ -2971,34 +2974,37 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
} else {
Pgno iFreePg; /* Index of free page to move pLastPg to */
MemPage *pLastPg;
u8 eMode = BTALLOC_ANY; /* Mode parameter for allocateBtreePage() */
Pgno iNear = 0; /* nearby parameter for allocateBtreePage() */
rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0);
if( rc!=SQLITE_OK ){
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.
**
** 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
** of the file is found.
*/
if( bCommit==0 ){
eMode = BTALLOC_LE;
iNear = nFin;
}
do {
MemPage *pFreePg;
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, 0, 0);
rc = allocateBtreePage(pBt, &pFreePg, &iFreePg, iNear, eMode);
if( rc!=SQLITE_OK ){
releasePage(pLastPg);
return rc;
}
releasePage(pFreePg);
}while( nFin!=0 && iFreePg>nFin );
}while( bCommit && iFreePg>nFin );
assert( iFreePg<iLastPg );
rc = sqlite3PagerWrite(pLastPg->pDbPage);
if( rc==SQLITE_OK ){
rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
}
rc = relocatePage(pBt, pLastPg, eType, iPtrPage, iFreePg, nFin!=0);
releasePage(pLastPg);
if( rc!=SQLITE_OK ){
return rc;
@@ -3006,29 +3012,39 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){
}
}
if( nFin==0 ){
iLastPg--;
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;
}
}
if( bCommit==0 ){
do {
iLastPg--;
}
sqlite3PagerTruncateImage(pBt->pPager, iLastPg);
}while( iLastPg==PENDING_BYTE_PAGE(pBt) || PTRMAP_ISPAGE(pBt, iLastPg) );
pBt->bDoTruncate = 1;
pBt->nPage = iLastPg;
}
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.
** It performs a single unit of work towards an incremental vacuum.
@@ -3046,11 +3062,21 @@ int sqlite3BtreeIncrVacuum(Btree *p){
if( !pBt->autoVacuum ){
rc = SQLITE_DONE;
}else{
invalidateAllOverflowCache(pBt);
rc = incrVacuumStep(pBt, 0, btreePagecount(pBt));
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[28], pBt->nPage);
Pgno nOrig = btreePagecount(pBt);
Pgno nFree = get4byte(&pBt->pPage1->aData[36]);
Pgno nFin = finalDbSize(pBt, nOrig, nFree);
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);
@@ -3077,9 +3103,7 @@ static int autoVacuumCommit(BtShared *pBt){
if( !pBt->incrVacuum ){
Pgno nFin; /* Number of pages in database after autovacuuming */
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 */
int nEntry; /* Number of entries on one ptrmap page */
Pgno nOrig; /* Database size before freeing */
nOrig = btreePagecount(pBt);
@@ -3092,26 +3116,18 @@ static int autoVacuumCommit(BtShared *pBt){
}
nFree = get4byte(&pBt->pPage1->aData[36]);
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--;
}
nFin = finalDbSize(pBt, nOrig, nFree);
if( nFin>nOrig ) return SQLITE_CORRUPT_BKPT;
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 ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
put4byte(&pBt->pPage1->aData[32], 0);
put4byte(&pBt->pPage1->aData[36], 0);
put4byte(&pBt->pPage1->aData[28], nFin);
sqlite3PagerTruncateImage(pBt->pPager, nFin);
pBt->bDoTruncate = 1;
pBt->nPage = nFin;
}
if( rc!=SQLITE_OK ){
@@ -3166,6 +3182,9 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
return rc;
}
}
if( pBt->bDoTruncate ){
sqlite3PagerTruncateImage(pBt->pPager, pBt->nPage);
}
#endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, 0);
sqlite3BtreeLeave(p);
@@ -3181,6 +3200,9 @@ static void btreeEndTransaction(Btree *p){
BtShared *pBt = p->pBt;
assert( sqlite3BtreeHoldsMutex(p) );
#ifndef SQLITE_OMIT_AUTOVACUUM
pBt->bDoTruncate = 0;
#endif
btreeClearHasContent(pBt);
if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){
/* If there are other active statements that belong to this database
@@ -4867,7 +4889,7 @@ static int allocateBtreePage(
MemPage **ppPage,
Pgno *pPgno,
Pgno nearby,
u8 exact
u8 eMode
){
MemPage *pPage1;
int rc;
@@ -4895,16 +4917,19 @@ static int allocateBtreePage(
** the entire-list will be searched for that page.
*/
#ifndef SQLITE_OMIT_AUTOVACUUM
if( exact && nearby<=mxPage ){
u8 eType;
assert( nearby>0 );
assert( pBt->autoVacuum );
rc = ptrmapGet(pBt, nearby, &eType, 0);
if( rc ) return rc;
if( eType==PTRMAP_FREEPAGE ){
searchList = 1;
if( eMode==BTALLOC_EXACT ){
if( nearby<=mxPage ){
u8 eType;
assert( nearby>0 );
assert( pBt->autoVacuum );
rc = ptrmapGet(pBt, nearby, &eType, 0);
if( rc ) return rc;
if( eType==PTRMAP_FREEPAGE ){
searchList = 1;
}
}
*pPgno = nearby;
}else if( eMode==BTALLOC_LE ){
searchList = 1;
}
#endif
@@ -4959,11 +4984,13 @@ static int allocateBtreePage(
rc = SQLITE_CORRUPT_BKPT;
goto end_allocate_page;
#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
** to allocate, regardless of whether it has leaves.
*/
assert( *pPgno==iTrunk );
*pPgno = iTrunk;
*ppPage = pTrunk;
searchList = 0;
rc = sqlite3PagerWrite(pTrunk->pDbPage);
@@ -5026,14 +5053,24 @@ static int allocateBtreePage(
unsigned char *aData = pTrunk->aData;
if( nearby>0 ){
u32 i;
int dist;
closest = 0;
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;
if( eMode==BTALLOC_LE ){
for(i=0; i<k; i++){
iPage = get4byte(&aData[8+i*4]);
if( iPage<=nearby ){
closest = i;
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{
@@ -5047,7 +5084,9 @@ static int allocateBtreePage(
goto end_allocate_page;
}
testcase( iPage==mxPage );
if( !searchList || iPage==nearby ){
if( !searchList
|| (iPage==nearby || (iPage<nearby && eMode==BTALLOC_LE))
){
int noContent;
*pPgno = iPage;
TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d"
@@ -5074,8 +5113,26 @@ static int allocateBtreePage(
pPrevTrunk = 0;
}while( searchList );
}else{
/* There are no pages on the freelist, so create a new page at the
** end of the file */
/* There are no pages on the freelist, so append a new page to the
** 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);
if( rc ) return rc;
pBt->nPage++;
@@ -5090,7 +5147,7 @@ static int allocateBtreePage(
MemPage *pPg = 0;
TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", pBt->nPage));
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 ){
rc = sqlite3PagerWrite(pPg->pDbPage);
releasePage(pPg);
@@ -5104,7 +5161,7 @@ static int allocateBtreePage(
*pPgno = pBt->nPage;
assert( *pPgno!=PENDING_BYTE_PAGE(pBt) );
rc = btreeGetPage(pBt, *pPgno, ppPage, 1);
rc = btreeGetPage(pBt, *pPgno, ppPage, bNoContent);
if( rc ) return rc;
rc = sqlite3PagerWrite((*ppPage)->pDbPage);
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
** to reside at pgnoRoot).
*/
rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, 1);
rc = allocateBtreePage(pBt, &pPageMove, &pgnoMove, pgnoRoot, BTALLOC_EXACT);
if( rc!=SQLITE_OK ){
return rc;
}
+1
View File
@@ -411,6 +411,7 @@ struct BtShared {
#ifndef SQLITE_OMIT_AUTOVACUUM
u8 autoVacuum; /* True if auto-vacuum is enabled */
u8 incrVacuum; /* True if incr-vacuum is enabled */
u8 bDoTruncate; /* True to truncate db on commit */
#endif
u8 inTransaction; /* Transaction state */
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
+1 -1
View File
@@ -993,7 +993,7 @@ static void charFunc(
return;
}
for(i=0; i<argc; i++){
sqlite3_int64 x = sqlite3_value_int64(argv[i]);
sqlite3_int64 x;
unsigned c;
x = sqlite3_value_int64(argv[i]);
if( x<0 || x>0x10ffff ) x = 0xfffd;
+26 -41
View File
@@ -1838,6 +1838,8 @@ static int pager_error(Pager *pPager, int rc){
return rc;
}
static int pager_truncate(Pager *pPager, Pgno nPage);
/*
** This routine ends a transaction. A transaction is usually ended by
** 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
** 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 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);
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
&& (!pagerUseWal(pPager) || sqlite3WalExclusiveMode(pPager->pWal, 0))
){
@@ -2016,7 +2028,7 @@ static void pagerUnlockAndRollback(Pager *pPager){
sqlite3EndBenignMalloc();
}else if( !pPager->exclusiveMode ){
assert( pPager->eState==PAGER_READER );
pager_end_transaction(pPager, 0);
pager_end_transaction(pPager, 0, 0);
}
}
pager_unlock(pPager);
@@ -2791,7 +2803,7 @@ end_playback:
rc = sqlite3PagerSync(pPager);
}
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 );
}
if( rc==SQLITE_OK && zMaster[0] && res ){
@@ -5885,36 +5897,6 @@ int sqlite3PagerCommitPhaseOne(
#endif
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
** 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.
@@ -5942,11 +5924,14 @@ int sqlite3PagerCommitPhaseOne(
goto commit_phase_one_exit;
}
sqlite3PcacheCleanAll(pPager->pPCache);
/* If the file on disk is not the same size as the database image,
** then use pager_truncate to grow or shrink the file here.
*/
if( pPager->dbSize!=pPager->dbFileSize ){
/* If the file on disk is smaller than the database image, use
** 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
** 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));
assert( pPager->eState==PAGER_WRITER_DBMOD );
rc = pager_truncate(pPager, nNew);
@@ -6019,7 +6004,7 @@ int sqlite3PagerCommitPhaseTwo(Pager *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);
}
@@ -6064,11 +6049,11 @@ int sqlite3PagerRollback(Pager *pPager){
if( pagerUseWal(pPager) ){
int rc2;
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;
}else if( !isOpen(pPager->jfd) || pPager->eState==PAGER_WRITER_LOCKED ){
int eState = pPager->eState;
rc = pager_end_transaction(pPager, 0);
rc = pager_end_transaction(pPager, 0, 0);
if( !MEMDB && eState>PAGER_WRITER_LOCKED ){
/* 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.
+17 -15
View File
@@ -265,7 +265,8 @@ static void tvfsExecTcl(
const char *zMethod,
Tcl_Obj *arg1,
Tcl_Obj *arg2,
Tcl_Obj *arg3
Tcl_Obj *arg3,
Tcl_Obj *arg4
){
int rc; /* Return code from Tcl_EvalObj() */
Tcl_Obj *pEval;
@@ -282,6 +283,7 @@ static void tvfsExecTcl(
if( arg1 ) Tcl_ListObjAppendElement(p->interp, pEval, arg1);
if( arg2 ) Tcl_ListObjAppendElement(p->interp, pEval, arg2);
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);
if( rc!=TCL_OK ){
@@ -302,7 +304,7 @@ static int tvfsClose(sqlite3_file *pFile){
if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
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;
if( p->pScript && p->mask&TESTVFS_READ_MASK ){
tvfsExecTcl(p, "xRead",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
);
tvfsResultCode(p, &rc);
}
@@ -362,7 +364,7 @@ static int tvfsWrite(
if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
tvfsExecTcl(p, "xWrite",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
Tcl_NewWideIntObj(iOfst)
Tcl_NewWideIntObj(iOfst), Tcl_NewIntObj(iAmt)
);
tvfsResultCode(p, &rc);
}
@@ -390,7 +392,7 @@ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
tvfsExecTcl(p, "xTruncate",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0, 0
);
tvfsResultCode(p, &rc);
}
@@ -431,7 +433,7 @@ static int tvfsSync(sqlite3_file *pFile, int flags){
tvfsExecTcl(p, "xSync",
Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId,
Tcl_NewStringObj(zFlags, -1)
Tcl_NewStringObj(zFlags, -1), 0
);
tvfsResultCode(p, &rc);
}
@@ -578,7 +580,7 @@ static int tvfsOpen(
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);
if( tvfsResultCode(p, &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 ){
tvfsExecTcl(p, "xDelete",
Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0
Tcl_NewStringObj(zPath, -1), Tcl_NewIntObj(dirSync), 0, 0
);
tvfsResultCode(p, &rc);
}
@@ -663,7 +665,7 @@ static int tvfsAccess(
if( flags==SQLITE_ACCESS_READWRITE ) zArg = "SQLITE_ACCESS_READWRITE";
if( flags==SQLITE_ACCESS_READ ) zArg = "SQLITE_ACCESS_READ";
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( rc!=SQLITE_OK ) return rc;
@@ -691,7 +693,7 @@ static int tvfsFullPathname(
Testvfs *p = (Testvfs *)pVfs->pAppData;
if( p->pScript && p->mask&TESTVFS_FULLPATHNAME_MASK ){
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( rc!=SQLITE_OK ) return rc;
}
@@ -771,7 +773,7 @@ static int tvfsShmOpen(sqlite3_file *pFile){
*/
Tcl_ResetResult(p->interp);
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( 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(isWrite));
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);
Tcl_DecrRefCount(pArg);
@@ -891,7 +893,7 @@ static int tvfsShmLock(
}
tvfsExecTcl(p, "xShmLock",
Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId,
Tcl_NewStringObj(zLock, -1)
Tcl_NewStringObj(zLock, -1), 0
);
tvfsResultCode(p, &rc);
}
@@ -937,7 +939,7 @@ static void tvfsShmBarrier(sqlite3_file *pFile){
if( p->pScript && p->mask&TESTVFS_SHMBARRIER_MASK ){
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 ){
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);
}
+14 -3
View File
@@ -1291,8 +1291,19 @@ do_test func-29.6 {
} {1}
do_execsql_test func-30.1 {SELECT unicode('$');} 36
do_execsql_test func-30.2 {SELECT unicode('¢');} 162
do_execsql_test func-30.3 {SELECT unicode('');} 8364
do_execsql_test func-30.4 {SELECT char(36,162,8364);} {$¢€}
do_execsql_test func-30.2 [subst {SELECT unicode('\u00A2');}] 162
do_execsql_test func-30.3 [subst {SELECT unicode('\u20AC');}] 8364
do_execsql_test func-30.4 {SELECT char(36,162,8364);} [subst {$\u00A2\u20AC}]
for {set i 1} {$i<0xd800} {incr i 13} {
do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i
}
for {set i 57344} {$i<=0xfffd} {incr i 17} {
if {$i==0xfeff} continue
do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i
}
for {set i 65536} {$i<=0x10ffff} {incr i 139} {
do_execsql_test func-30.5.$i {SELECT unicode(char($i))} $i
}
finish_test
+137
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
+2 -2
View File
@@ -139,8 +139,8 @@ ifcapable shared_cache {
# Figure out how big the database is and how many free pages it
# has before running incremental-vacuum.
#
set nPage [expr {[file size test.db]/1024}]
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.
# The iTest'th I/O call is set to fail.
@@ -158,7 +158,7 @@ ifcapable shared_cache {
set ::sqlite_io_error_hardhit 0
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 {
set shrink [expr {$nPage-$nPage2}]
+4 -4
View File
@@ -36,11 +36,10 @@ db close
testvfs tvfs
tvfs filter xWrite
tvfs script write_cb
proc write_cb {xCall file handle iOfst} {
proc write_cb {xCall file handle iOfst args} {
if {[file tail $file]=="test.db"} {
lappend ::write_list [expr $iOfst/1024]
}
puts "$xCall $file $args"
}
do_test 1.2 {
@@ -65,11 +64,12 @@ do_test 1.3 {
}
set iPrev $iNext
}
puts -nonewline \
" (forward=$nForward, back=$nBackward, noncontiguous=$nNoncont)"
expr {$nForward > $nBackward}
expr {$nForward > 25*($nBackward + $nNoncont)}
} {1}
db close
tvfs delete
finish_test
+50 -33
View File
@@ -1118,6 +1118,25 @@ proc crashsql {args} {
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...>
#
# This proc is used to implement test cases that check that IO errors
@@ -1150,10 +1169,26 @@ proc do_ioerr_test {testname args} {
# TEMPORARY: For 3.5.9, disable testing of extended result codes. There are
# a couple of obscure IO errors that do not return them.
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
#reset_prng_state
save_prng_state
for {set n $::ioerropts(-start)} {$::go} {incr n} {
set ::TN $n
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
# SQL (in that order) to prepare for the test case.
do_test $testname.$n.1 {
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
run_ioerr_prep
} {0}
# Read the 'checksum' of the database.
if {$::ioerropts(-cksum)} {
set checksum [cksum]
set ::checksum [cksum]
}
# Set the Nth IO error to fail.
@@ -1198,20 +1218,10 @@ proc do_ioerr_test {testname args} {
set ::sqlite_io_error_persist $::ioerropts(-persist)
set ::sqlite_io_error_pending $n
}] $n
# 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)}"
}
# 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.
# Execute the TCL script created for the body of this test. If
# at least N IO operations performed by SQLite as a result of
# the script, the Nth will fail.
do_test $testname.$n.3 {
set ::sqlite_io_error_hit 0
set ::sqlite_io_error_hardhit 0
@@ -1315,8 +1325,15 @@ proc do_ioerr_test {testname args} {
catch {db close}
catch {db2 close}
set ::DB [sqlite3 db test.db; sqlite3_connection_pointer db]
cksum
} $checksum
set nowcksum [cksum]
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
+2 -2
View File
@@ -10,8 +10,8 @@
#***********************************************************************
#
# Ticket #3762: Make sure that an incremental vacuum that reduces the
# size of the database file such that a pointer-map page is elemented
# can be correctly rolled back.
# size of the database file such that if a pointer-map page is eliminated
# it can be correctly rolled back.
#
# That ticket #3762 has been fixed has already been verified by the
# savepoint6.test test script. But this script is simplier and a