aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--manifest46
-rw-r--r--manifest.uuid2
-rw-r--r--src/os_unix.c6
-rw-r--r--src/os_win.c2
-rw-r--r--src/pager.c38
-rw-r--r--src/sqlite.h.in23
-rw-r--r--src/test_vfs.c263
-rw-r--r--src/vdbe.c11
-rw-r--r--src/vdbeblob.c4
-rw-r--r--test/journal2.test228
-rw-r--r--test/jrnlmode2.test34
-rw-r--r--test/malloc_common.tcl33
-rw-r--r--test/pager1.test448
-rw-r--r--test/pager2.test117
-rw-r--r--test/pagerfault.test197
-rw-r--r--test/permutations.test2
-rw-r--r--test/tester.tcl10
-rw-r--r--test/walmode.test31
18 files changed, 1379 insertions, 116 deletions
diff --git a/manifest b/manifest
index 11bbba4ce..5b39f49e1 100644
--- a/manifest
+++ b/manifest
@@ -1,8 +1,8 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
-C Fix\san\suninitialized\svariable\sin\sos_unix.c.
-D 2010-06-19T23:53:11
+C Merge\sthe\sexperimental\sUNDELETABLE_WHEN_OPEN\soptimization\sinto\sthe\strunk.
+D 2010-06-21T12:47:41
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -157,9 +157,9 @@ F src/os.c 9c4a2f82a50306a33907678ec0187b6ad1486bfe
F src/os.h d7775504a51e6e0d40315aa427b3e229ff9ff9ca
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 665876d5eec7585226b0a1cf5e18098de2b2da19
-F src/os_unix.c 838e0c0cabb722ac573c8cbd666b47bdf44eb8f9
-F src/os_win.c dfde7d33c446e89dd9a277c036f2c4cc564b3138
-F src/pager.c 4fe451d68950002eb985e6325d666ab54956a37f
+F src/os_unix.c 5231a75a3799872b1250bc70c0e6a1a5960bc865
+F src/os_win.c 73608839342de32280cb378d3c2fc85a5dd80bd2
+F src/pager.c 9f138b79b47090c1e31efe3d9ea191cc92981643
F src/pager.h ca1f23c0cf137ac26f8908df2427c8b308361efd
F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -173,7 +173,7 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b
F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714
-F src/sqlite.h.in 46c01e55cea31b91565ae41276c6310ee4032be8
+F src/sqlite.h.in 301476d8556cbb1c5d4bc906370b2dafe4d98a44
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
F src/sqliteInt.h 242987ebd2366ea36650a09cdab04a9163c62109
F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
@@ -212,7 +212,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c aa9919c885a1fe53eafc73492f0898ee6c0a0726
-F src/test_vfs.c 001c34e08748a4a02cd1c2d5531c160a007a84d8
+F src/test_vfs.c 9ba0bb227f5fa08d7e3533ff21063c5acf13dabb
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb
F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d
@@ -220,12 +220,12 @@ F src/update.c 9859f2056c7739a1db0d9774ccb6c2f0cee6d1de
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f
-F src/vdbe.c e115585b14d2cc4128cb53a7e42f207750e80f55
+F src/vdbe.c 290d20ed92b560dc0b602ac50b252f9553a2d8e8
F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3
F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1
F src/vdbeapi.c dc3138f10afbc95ed3c21dd25abb154504b1db9d
F src/vdbeaux.c 4e96a5169b988a8697d4a417f902277b4152e43e
-F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e
+F src/vdbeblob.c 258a6010ba7a82b72b327fb24c55790655689256
F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
@@ -469,8 +469,9 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19
+F test/journal2.test a08ea6545d987385e7cbb1d4e7dc2eaacd83b00b
F test/jrnlmode.test 76f94d61528c5ff32102a12f8cf34f4cc36f7849
-F test/jrnlmode2.test fe79ea1f0375c926b8de0362ddf94f34a64135fd
+F test/jrnlmode2.test a19e28de1a6ec898067e46a122f1b71c9323bf00
F test/jrnlmode3.test cfcdb12b90e640a23b92785a002d96c0624c8710
F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
@@ -511,7 +512,7 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test e3ea401904d010cb7c1e4b2ee8803f4a9f5b999d
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
-F test/malloc_common.tcl fbf369eb2828825c5f319c101917aff91ea87556
+F test/malloc_common.tcl 58caffc4be307b56c5b1438b5eba3eb278bd81f5
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
@@ -536,13 +537,14 @@ F test/notify2.test 195a467e021f74197be2c4fb02d6dee644b8d8db
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
-F test/pager1.test 60dec408563461f9fbf04d4d301b1b4db23f7525
-F test/pagerfault.test 16e560bc4332d5b089b369d82ae4b65b8805b5eb
+F test/pager1.test 86d034bf3ffe4e99648714443776440d0555f705
+F test/pager2.test ad062a51030dc1e2749f506528db4cc5bae6474c
+F test/pagerfault.test e67e9c18bf7b4bb8cc8d458d3a5ecc980f18a225
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
F test/pcache.test eebc4420b37cb07733ae9b6e99c9da7c40dd6d58
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
-F test/permutations.test f044eaba204ff13d530ceb72a22b0ed2c43562ef
+F test/permutations.test 9296368f1d14d9e042f146a804ca38f551d35435
F test/pragma.test 6960f9efbce476f70ba9ee2171daf5042f9e3d8a
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
@@ -613,7 +615,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
F test/tempdb.test 800c36623d67a2ad1f58784b9c5644e0405af6e6
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
-F test/tester.tcl 7912c3c8768320fd7bcb217637c2f0a607fbbc24
+F test/tester.tcl ab89e8e592ff26e2b65ff3cae9de5f26863ae766
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -780,7 +782,7 @@ F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f
F test/walcrash2.test 14585ad1a2c85da2de721caa3b4deeea55213008
F test/walfault.test c2b524299dede269282a0795e11396cc446ca9af
F test/walhook.test 67e675127f4acb72f061a12667ce6e5460b06b78
-F test/walmode.test 6ca9d710cc9f6545b913abcded6d6b0b15641048
+F test/walmode.test b54e2f91f34179c65cab02a6916578617a33eef0
F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
@@ -826,14 +828,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
-P 2241788bc85fbc48e9cfecb95fe0a858338e37cb
-R 519d2c82f8071bfeedb3eae3a50de5e0
+P 822a0283c6bc1c75001f3d1c528a4ff89c6b039c a64d96db09ef2b7651fa4e98d3c7bf3ae5d3fe96
+R aa81338105a8066f92038016780c9df7
U drh
-Z fb28c7b4632831ff9010ebcbf771882d
+Z e0549d09ee79c82d436d5aa9bbd618a3
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
-iD8DBQFMHVhqoxKgR168RlERAl0KAJ0Yf2pBemdWub4Zh3gc9Vg3H0bRrgCfb73m
-7DvKYVz1pMDVAwrfS/3PYDY=
-=DvU8
+iD8DBQFMH19xoxKgR168RlERAt6AAJ96RqboVXAAENNG3NTV/9NSo4zbEgCghTxO
+GuKlnNnL+XTagpq68iLKUhk=
+=R4ik
-----END PGP SIGNATURE-----
diff --git a/manifest.uuid b/manifest.uuid
index 3af1e5905..e41f233b0 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-822a0283c6bc1c75001f3d1c528a4ff89c6b039c \ No newline at end of file
+ee0acef1faffd480fd2136f81fb2b6f6a17b5388 \ No newline at end of file
diff --git a/src/os_unix.c b/src/os_unix.c
index e949c20cd..77aafccce 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4604,6 +4604,12 @@ static int unixAccess(
assert(!"Invalid flags argument");
}
*pResOut = (access(zPath, amode)==0);
+ if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
+ struct stat buf;
+ if( 0==stat(zPath, &buf) && buf.st_size==0 ){
+ *pResOut = 0;
+ }
+ }
return SQLITE_OK;
}
diff --git a/src/os_win.c b/src/os_win.c
index ec62e394c..24d0e7ca3 100644
--- a/src/os_win.c
+++ b/src/os_win.c
@@ -1151,7 +1151,7 @@ static int winSectorSize(sqlite3_file *id){
*/
static int winDeviceCharacteristics(sqlite3_file *id){
UNUSED_PARAMETER(id);
- return 0;
+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
}
/****************************************************************************
diff --git a/src/pager.c b/src/pager.c
index c875dd9df..a5377d4c3 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -1219,12 +1219,24 @@ static int pagerUseWal(Pager *pPager){
static void pager_unlock(Pager *pPager){
if( !pPager->exclusiveMode ){
int rc = SQLITE_OK; /* Return code */
+ int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
/* Always close the journal file when dropping the database lock.
** Otherwise, another connection with journal_mode=delete might
** delete the file out from under us.
*/
- sqlite3OsClose(pPager->jfd);
+ assert( (PAGER_JOURNALMODE_MEMORY & 5)!=1 );
+ assert( (PAGER_JOURNALMODE_OFF & 5)!=1 );
+ assert( (PAGER_JOURNALMODE_WAL & 5)!=1 );
+ assert( (PAGER_JOURNALMODE_DELETE & 5)!=1 );
+ assert( (PAGER_JOURNALMODE_TRUNCATE & 5)==1 );
+ assert( (PAGER_JOURNALMODE_PERSIST & 5)==1 );
+ if( 0==(iDc & SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN)
+ || 1!=(pPager->journalMode & 5)
+ ){
+ sqlite3OsClose(pPager->jfd);
+ }
+
sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0;
releaseAllSavepoints(pPager);
@@ -3115,6 +3127,7 @@ int sqlite3PagerClose(Pager *pPager){
enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
IOTRACE(("CLOSE %p\n", pPager))
+ sqlite3OsClose(pPager->jfd);
sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache);
@@ -3908,17 +3921,22 @@ int sqlite3PagerOpen(
*/
static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs;
- int rc; /* Return code */
- int exists; /* True if a journal file is present */
+ int rc = SQLITE_OK; /* Return code */
+ int exists = 1; /* True if a journal file is present */
+ int jrnlOpen = !!isOpen(pPager->jfd);
assert( pPager!=0 );
assert( pPager->useJournal );
assert( isOpen(pPager->fd) );
- assert( !isOpen(pPager->jfd) );
assert( pPager->state <= PAGER_SHARED );
+ assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
+ SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
+ ));
*pExists = 0;
- rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ if( !jrnlOpen ){
+ rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
+ }
if( rc==SQLITE_OK && exists ){
int locked; /* True if some process holds a RESERVED lock */
@@ -3956,15 +3974,19 @@ static int hasHotJournal(Pager *pPager, int *pExists){
** If there is, then we consider this journal to be hot. If not,
** it can be ignored.
*/
- int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
- rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
+ if( !jrnlOpen ){
+ int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
+ rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
+ }
if( rc==SQLITE_OK ){
u8 first = 0;
rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK;
}
- sqlite3OsClose(pPager->jfd);
+ if( !jrnlOpen ){
+ sqlite3OsClose(pPager->jfd);
+ }
*pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 28778f7fc..f91f25b3f 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -497,17 +497,18 @@ int sqlite3_exec(
** information is written to disk in the same order as calls
** to xWrite().
*/
-#define SQLITE_IOCAP_ATOMIC 0x00000001
-#define SQLITE_IOCAP_ATOMIC512 0x00000002
-#define SQLITE_IOCAP_ATOMIC1K 0x00000004
-#define SQLITE_IOCAP_ATOMIC2K 0x00000008
-#define SQLITE_IOCAP_ATOMIC4K 0x00000010
-#define SQLITE_IOCAP_ATOMIC8K 0x00000020
-#define SQLITE_IOCAP_ATOMIC16K 0x00000040
-#define SQLITE_IOCAP_ATOMIC32K 0x00000080
-#define SQLITE_IOCAP_ATOMIC64K 0x00000100
-#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
-#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
+#define SQLITE_IOCAP_ATOMIC 0x00000001
+#define SQLITE_IOCAP_ATOMIC512 0x00000002
+#define SQLITE_IOCAP_ATOMIC1K 0x00000004
+#define SQLITE_IOCAP_ATOMIC2K 0x00000008
+#define SQLITE_IOCAP_ATOMIC4K 0x00000010
+#define SQLITE_IOCAP_ATOMIC8K 0x00000020
+#define SQLITE_IOCAP_ATOMIC16K 0x00000040
+#define SQLITE_IOCAP_ATOMIC32K 0x00000080
+#define SQLITE_IOCAP_ATOMIC64K 0x00000100
+#define SQLITE_IOCAP_SAFE_APPEND 0x00000200
+#define SQLITE_IOCAP_SEQUENTIAL 0x00000400
+#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
/*
** CAPI3REF: File Locking Levels
diff --git a/src/test_vfs.c b/src/test_vfs.c
index e94bef7d2..46f3a0630 100644
--- a/src/test_vfs.c
+++ b/src/test_vfs.c
@@ -58,6 +58,13 @@ struct Testvfs {
int iIoerrCnt;
int ioerr;
int nIoerrFail;
+
+ int iFullCnt;
+ int fullerr;
+ int nFullFail;
+
+ int iDevchar;
+ int iSectorsize;
};
/*
@@ -77,7 +84,10 @@ struct Testvfs {
#define TESTVFS_OPEN_MASK 0x00000100
#define TESTVFS_SYNC_MASK 0x00000200
#define TESTVFS_DELETE_MASK 0x00000400
-#define TESTVFS_ALL_MASK 0x000007FF
+#define TESTVFS_CLOSE_MASK 0x00000800
+#define TESTVFS_WRITE_MASK 0x00001000
+#define TESTVFS_TRUNCATE_MASK 0x00002000
+#define TESTVFS_ALL_MASK 0x00003FFF
#define TESTVFS_MAX_PAGES 256
@@ -187,6 +197,30 @@ static int tvfsResultCode(Testvfs *p, int *pRc){
return 0;
}
+static int tvfsInjectIoerr(Testvfs *p){
+ int ret = 0;
+ if( p->ioerr ){
+ p->iIoerrCnt--;
+ if( p->iIoerrCnt==0 || (p->iIoerrCnt<0 && p->ioerr==2) ){
+ ret = 1;
+ p->nIoerrFail++;
+ }
+ }
+ return ret;
+}
+
+static int tvfsInjectFullerr(Testvfs *p){
+ int ret = 0;
+ if( p->fullerr ){
+ p->iFullCnt--;
+ if( p->iFullCnt<=0 ){
+ ret = 1;
+ p->nFullFail++;
+ }
+ }
+ return ret;
+}
+
static void tvfsExecTcl(
Testvfs *p,
@@ -245,15 +279,23 @@ static void tvfsExecTcl(
** Close an tvfs-file.
*/
static int tvfsClose(sqlite3_file *pFile){
- TestvfsFile *p = (TestvfsFile *)pFile;
- if( p->pShmId ){
- Tcl_DecrRefCount(p->pShmId);
- p->pShmId = 0;
+ TestvfsFile *pFd = (TestvfsFile *)pFile;
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+ if( p->pScript && p->mask&TESTVFS_CLOSE_MASK ){
+ tvfsExecTcl(p, "xClose",
+ Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+ );
+ }
+
+ if( pFd->pShmId ){
+ Tcl_DecrRefCount(pFd->pShmId);
+ pFd->pShmId = 0;
}
if( pFile->pMethods ){
ckfree((char *)pFile->pMethods);
}
- return sqlite3OsClose(p->pReal);
+ return sqlite3OsClose(pFd->pReal);
}
/*
@@ -278,16 +320,44 @@ static int tvfsWrite(
int iAmt,
sqlite_int64 iOfst
){
- TestvfsFile *p = (TestvfsFile *)pFile;
- return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst);
+ int rc = SQLITE_OK;
+ TestvfsFile *pFd = (TestvfsFile *)pFile;
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+ if( p->pScript && p->mask&TESTVFS_WRITE_MASK ){
+ tvfsExecTcl(p, "xWrite",
+ Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+ );
+ tvfsResultCode(p, &rc);
+ }
+
+ if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsWrite(pFd->pReal, zBuf, iAmt, iOfst);
+ }
+ return rc;
}
/*
** Truncate an tvfs-file.
*/
static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
- TestvfsFile *p = (TestvfsFile *)pFile;
- return sqlite3OsTruncate(p->pReal, size);
+ int rc = SQLITE_OK;
+ TestvfsFile *pFd = (TestvfsFile *)pFile;
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+
+ if( p->pScript && p->mask&TESTVFS_TRUNCATE_MASK ){
+ tvfsExecTcl(p, "xTruncate",
+ Tcl_NewStringObj(pFd->zFilename, -1), pFd->pShmId, 0
+ );
+ tvfsResultCode(p, &rc);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsTruncate(pFd->pReal, size);
+ }
+ return rc;
}
/*
@@ -325,6 +395,8 @@ static int tvfsSync(sqlite3_file *pFile, int flags){
tvfsResultCode(p, &rc);
}
+ if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
+
if( rc==SQLITE_OK ){
rc = sqlite3OsSync(pFd->pReal, flags);
}
@@ -376,16 +448,24 @@ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){
** Return the sector-size in bytes for an tvfs-file.
*/
static int tvfsSectorSize(sqlite3_file *pFile){
- TestvfsFile *p = (TestvfsFile *)pFile;
- return sqlite3OsSectorSize(p->pReal);
+ TestvfsFile *pFd = (TestvfsFile *)pFile;
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+ if( p->iSectorsize>=0 ){
+ return p->iSectorsize;
+ }
+ return sqlite3OsSectorSize(pFd->pReal);
}
/*
** Return the device characteristic flags supported by an tvfs-file.
*/
static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
- TestvfsFile *p = (TestvfsFile *)pFile;
- return sqlite3OsDeviceCharacteristics(p->pReal);
+ TestvfsFile *pFd = (TestvfsFile *)pFile;
+ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
+ if( p->iDevchar>=0 ){
+ return p->iDevchar;
+ }
+ return sqlite3OsDeviceCharacteristics(pFd->pReal);
}
/*
@@ -555,18 +635,6 @@ static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut);
}
-static int tvfsInjectIoerr(Testvfs *p){
- int ret = 0;
- if( p->ioerr ){
- p->iIoerrCnt--;
- if( p->iIoerrCnt==0 || (p->iIoerrCnt<0 && p->ioerr==2) ){
- ret = 1;
- p->nIoerrFail++;
- }
- }
- return ret;
-}
-
static int tvfsShmOpen(
sqlite3_file *pFileDes
){
@@ -782,25 +850,38 @@ static int testvfs_obj_cmd(
){
Testvfs *p = (Testvfs *)cd;
- static const char *CMD_strs[] = {
- "shm", "delete", "filter", "ioerr", "script", 0
- };
enum DB_enum {
- CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT
+ CMD_SHM, CMD_DELETE, CMD_FILTER, CMD_IOERR, CMD_SCRIPT,
+ CMD_DEVCHAR, CMD_SECTORSIZE, CMD_FULLERR
+ };
+ struct TestvfsSubcmd {
+ char *zName;
+ enum DB_enum eCmd;
+ } aSubcmd[] = {
+ { "shm", CMD_SHM },
+ { "delete", CMD_DELETE },
+ { "filter", CMD_FILTER },
+ { "ioerr", CMD_IOERR },
+ { "fullerr", CMD_FULLERR },
+ { "script", CMD_SCRIPT },
+ { "devchar", CMD_DEVCHAR },
+ { "sectorsize", CMD_SECTORSIZE },
+ { 0, 0 }
};
-
int i;
if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR;
}
- if( Tcl_GetIndexFromObj(interp, objv[1], CMD_strs, "subcommand", 0, &i) ){
+ if( Tcl_GetIndexFromObjStruct(
+ interp, objv[1], aSubcmd, sizeof(aSubcmd[0]), "subcommand", 0, &i)
+ ){
return TCL_ERROR;
}
Tcl_ResetResult(interp);
- switch( (enum DB_enum)i ){
+ switch( aSubcmd[i].eCmd ){
case CMD_SHM: {
Tcl_Obj *pObj;
int i;
@@ -857,7 +938,10 @@ static int testvfs_obj_cmd(
{ "xShmMap", TESTVFS_SHMMAP_MASK },
{ "xSync", TESTVFS_SYNC_MASK },
{ "xDelete", TESTVFS_DELETE_MASK },
+ { "xWrite", TESTVFS_WRITE_MASK },
+ { "xTruncate", TESTVFS_TRUNCATE_MASK },
{ "xOpen", TESTVFS_OPEN_MASK },
+ { "xClose", TESTVFS_CLOSE_MASK },
};
Tcl_Obj **apElem = 0;
int nElem = 0;
@@ -916,6 +1000,34 @@ static int testvfs_obj_cmd(
}
/*
+ ** TESTVFS fullerr ?IFAIL?
+ **
+ ** Where IFAIL is an integer.
+ */
+ case CMD_FULLERR: {
+ int iRet = p->nFullFail;
+
+ p->nFullFail = 0;
+ p->fullerr = 0;
+ p->iFullCnt = 0;
+
+ if( objc==3 ){
+ int iCnt;
+ if( TCL_OK!=Tcl_GetIntFromObj(interp, objv[2], &iCnt) ){
+ return TCL_ERROR;
+ }
+ p->fullerr = (iCnt>0);
+ p->iFullCnt = iCnt;
+ }else if( objc!=2 ){
+ Tcl_AppendResult(interp, "Bad args", 0);
+ return TCL_ERROR;
+ }
+
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(iRet));
+ break;
+ }
+
+ /*
** TESTVFS ioerr ?IFAIL PERSIST?
**
** Where IFAIL is an integer and PERSIST is boolean.
@@ -948,6 +1060,89 @@ static int testvfs_obj_cmd(
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break;
}
+
+ case CMD_DEVCHAR: {
+ struct DeviceFlag {
+ char *zName;
+ int iValue;
+ } aFlag[] = {
+ { "default", -1 },
+ { "atomic", SQLITE_IOCAP_ATOMIC },
+ { "atomic512", SQLITE_IOCAP_ATOMIC512 },
+ { "atomic1k", SQLITE_IOCAP_ATOMIC1K },
+ { "atomic2k", SQLITE_IOCAP_ATOMIC2K },
+ { "atomic4k", SQLITE_IOCAP_ATOMIC4K },
+ { "atomic8k", SQLITE_IOCAP_ATOMIC8K },
+ { "atomic16k", SQLITE_IOCAP_ATOMIC16K },
+ { "atomic32k", SQLITE_IOCAP_ATOMIC32K },
+ { "atomic64k", SQLITE_IOCAP_ATOMIC64K },
+ { "sequential", SQLITE_IOCAP_SEQUENTIAL },
+ { "safe_append", SQLITE_IOCAP_SAFE_APPEND },
+ { "undeletable_when_open", SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN },
+ { 0, 0 }
+ };
+ Tcl_Obj *pRet;
+ int iFlag;
+
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?ATTR-LIST?");
+ return TCL_ERROR;
+ }
+ if( objc==3 ){
+ int j;
+ int iNew = 0;
+ Tcl_Obj **flags = 0;
+ int nFlags = 0;
+
+ if( Tcl_ListObjGetElements(interp, objv[2], &nFlags, &flags) ){
+ return TCL_ERROR;
+ }
+
+ for(j=0; j<nFlags; j++){
+ int idx = 0;
+ if( Tcl_GetIndexFromObjStruct(interp, flags[j], aFlag,
+ sizeof(aFlag[0]), "flag", 0, &idx)
+ ){
+ return TCL_ERROR;
+ }
+ if( aFlag[idx].iValue<0 && nFlags>1 ){
+ Tcl_AppendResult(interp, "bad flags: ", Tcl_GetString(objv[2]), 0);
+ return TCL_ERROR;
+ }
+ iNew |= aFlag[idx].iValue;
+ }
+
+ p->iDevchar = iNew;
+ }
+
+ pRet = Tcl_NewObj();
+ for(iFlag=0; iFlag<sizeof(aFlag)/sizeof(aFlag[0]); iFlag++){
+ if( p->iDevchar & aFlag[iFlag].iValue ){
+ Tcl_ListObjAppendElement(
+ interp, pRet, Tcl_NewStringObj(aFlag[iFlag].zName, -1)
+ );
+ }
+ }
+ Tcl_SetObjResult(interp, pRet);
+
+ break;
+ }
+
+ case CMD_SECTORSIZE: {
+ if( objc>3 ){
+ Tcl_WrongNumArgs(interp, 2, objv, "?VALUE?");
+ return TCL_ERROR;
+ }
+ if( objc==3 ){
+ int iNew = 0;
+ if( Tcl_GetIntFromObj(interp, objv[2], &iNew) ){
+ return TCL_ERROR;
+ }
+ p->iSectorsize = iNew;
+ }
+ Tcl_SetObjResult(interp, Tcl_NewIntObj(p->iSectorsize));
+ break;
+ }
}
return TCL_OK;
@@ -1067,6 +1262,8 @@ static int testvfs_cmd(
nByte = sizeof(Testvfs) + strlen(zVfs)+1;
p = (Testvfs *)ckalloc(nByte);
memset(p, 0, nByte);
+ p->iDevchar = -1;
+ p->iSectorsize = -1;
/* Create the new object command before querying SQLite for a default VFS
** to use for 'real' IO operations. This is because creating the new VFS
diff --git a/src/vdbe.c b/src/vdbe.c
index 6803beafd..aec5712bf 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -5248,8 +5248,6 @@ case OP_JournalMode: { /* out2-prerelease */
rc = sqlite3PagerCloseWal(pPager);
if( rc==SQLITE_OK ){
sqlite3PagerSetJournalMode(pPager, eNew);
- }else if( rc==SQLITE_BUSY && pOp->p5==0 ){
- goto abort_due_to_error;
}
}
@@ -5259,16 +5257,15 @@ case OP_JournalMode: { /* out2-prerelease */
assert( sqlite3BtreeIsInTrans(pBt)==0 );
if( rc==SQLITE_OK ){
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
- if( rc==SQLITE_BUSY && pOp->p5==0 ) goto abort_due_to_error;
- }
- if( rc==SQLITE_BUSY ){
- eNew = eOld;
- rc = SQLITE_OK;
}
}
}
#endif /* ifndef SQLITE_OMIT_WAL */
+ if( rc ){
+ if( rc==SQLITE_BUSY && pOp->p5!=0 ) rc = SQLITE_OK;
+ eNew = eOld;
+ }
eNew = sqlite3PagerSetJournalMode(pPager, eNew);
pOut = &aMem[pOp->p2];
diff --git a/src/vdbeblob.c b/src/vdbeblob.c
index 829b6de6d..b2b9f0ed0 100644
--- a/src/vdbeblob.c
+++ b/src/vdbeblob.c
@@ -191,10 +191,14 @@ int sqlite3_blob_open(
sqlite3VdbeUsesBtree(v, iDb);
/* Configure the OP_TableLock instruction */
+#ifdef SQLITE_OMIT_SHARED_CACHE
+ sqlite3VdbeChangeToNoop(v, 2, 1);
+#else
sqlite3VdbeChangeP1(v, 2, iDb);
sqlite3VdbeChangeP2(v, 2, pTab->tnum);
sqlite3VdbeChangeP3(v, 2, flags);
sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
+#endif
/* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */
diff --git a/test/journal2.test b/test/journal2.test
new file mode 100644
index 000000000..f7145ebdf
--- /dev/null
+++ b/test/journal2.test
@@ -0,0 +1,228 @@
+# 2010 June 16
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+# This file implements regression tests for SQLite library.
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+db close
+
+set a_string_counter 1
+proc a_string {n} {
+ global a_string_counter
+ incr a_string_counter
+ string range [string repeat "${a_string_counter}." $n] 1 $n
+}
+
+# Create a [testvfs] and install it as the default VFS. Set the device
+# characteristics flags to "SAFE_DELETE".
+#
+testvfs tvfs -default 1
+tvfs devchar undeletable_when_open
+
+# Set up a hook so that each time a journal file is opened, closed or
+# deleted, the method name ("xOpen", "xClose" or "xDelete") and the final
+# segment of the journal file-name (i.e. "test.db-journal") are appended to
+# global list variable $::oplog.
+#
+tvfs filter {xOpen xClose xDelete}
+tvfs script journal_op_catcher
+proc journal_op_catcher {method filename args} {
+
+ # If global variable ::tvfs_error_on_write is defined, then return an
+ # IO error to every attempt to modify the file-system. Otherwise, return
+ # SQLITE_OK.
+ #
+ if {[info exists ::tvfs_error_on_write]} {
+ if {[lsearch {xDelete xWrite xTruncate} $method]>=0} {
+ return SQLITE_IOERR
+ }
+ }
+
+ # The rest of this command only deals with xOpen(), xClose() and xDelete()
+ # operations on journal files. If this invocation does not represent such
+ # an operation, return with no further ado.
+ #
+ set f [file tail $filename]
+ if {[string match *journal $f]==0} return
+ if {[lsearch {xOpen xDelete xClose} $method]<0} return
+
+ # Append a record of this operation to global list variable $::oplog.
+ #
+ lappend ::oplog $method $f
+
+ # If this is an attempt to delete a journal file for which there exists
+ # one ore more open handles, return an error. The code in test_vfs.c
+ # will not invoke the xDelete method of the "real" VFS in this case.
+ #
+ if {[info exists ::open_journals($f)]==0} { set ::open_journals($f) 0 }
+ switch -- $method {
+ xOpen { incr ::open_journals($f) +1 }
+ xClose { incr ::open_journals($f) -1 }
+ xDelete { if {$::open_journals($f)>0} { return SQLITE_IOERR } }
+ }
+
+ return ""
+}
+
+
+do_test journal2-1.1 {
+ set ::oplog [list]
+ sqlite3 db test.db
+ execsql { CREATE TABLE t1(a, b) }
+ set ::oplog
+} {xOpen test.db-journal xClose test.db-journal xDelete test.db-journal}
+do_test journal2-1.2 {
+ set ::oplog [list]
+ execsql {
+ PRAGMA journal_mode = truncate;
+ INSERT INTO t1 VALUES(1, 2);
+ }
+ set ::oplog
+} {xOpen test.db-journal}
+do_test journal2-1.3 {
+ set ::oplog [list]
+ execsql { INSERT INTO t1 VALUES(3, 4) }
+ set ::oplog
+} {}
+do_test journal2-1.4 { execsql { SELECT * FROM t1 } } {1 2 3 4}
+
+# Add a second connection. This connection attempts to commit data in
+# journal_mode=DELETE mode. When it tries to delete the journal file,
+# the VFS layer returns an IO error.
+#
+do_test journal2-1.5 {
+ set ::oplog [list]
+ sqlite3 db2 test.db
+ execsql { PRAGMA journal_mode = delete } db2
+ catchsql { INSERT INTO t1 VALUES(5, 6) } db2
+} {1 {disk I/O error}}
+do_test journal2-1.6 { file exists test.db-journal } 1
+do_test journal2-1.7 { execsql { SELECT * FROM t1 } } {1 2 3 4}
+do_test journal2-1.8 {
+ execsql { PRAGMA journal_mode = truncate } db2
+ execsql { INSERT INTO t1 VALUES(5, 6) } db2
+} {}
+do_test journal2-1.9 { execsql { SELECT * FROM t1 } } {1 2 3 4 5 6}
+
+# Grow the database until it is reasonably large.
+#
+do_test journal2-1.10 {
+ db2 close
+ db func a_string a_string
+ execsql {
+ CREATE TABLE t2(a UNIQUE, b UNIQUE);
+ INSERT INTO t2 VALUES(a_string(200), a_string(300));
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 2
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 4
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 8
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 16
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 32
+ INSERT INTO t2 SELECT a_string(200), a_string(300) FROM t2; -- 64
+ }
+ file size test.db-journal
+} {0}
+do_test journal2-1.11 {
+ set sz [expr [file size test.db] / 1024]
+ expr {$sz>120 && $sz<200}
+} 1
+
+# Using new connection [db2] (with journal_mode=DELETE), write a lot of
+# data to the database. So that many pages within the database file are
+# modified before the transaction is committed.
+#
+# Then, enable simulated IO errors in all calls to xDelete, xWrite
+# and xTruncate before committing the transaction and closing the
+# database file. From the point of view of other file-system users, it
+# appears as if the process hosting [db2] unexpectedly exited.
+#
+do_test journal2-1.12 {
+ sqlite3 db2 test.db
+ execsql {
+ PRAGMA cache_size = 10;
+ BEGIN;
+ INSERT INTO t2 SELECT randomblob(200), randomblob(300) FROM t2; -- 128
+ } db2
+} {}
+do_test journal2-1.13 {
+ tvfs filter {xOpen xClose xDelete xWrite xTruncate}
+ set ::tvfs_error_on_write 1
+ catchsql { COMMIT } db2
+} {1 {disk I/O error}}
+db2 close
+unset ::tvfs_error_on_write
+file copy -force test.db testX.db
+
+do_test journal2-1.14 { file exists test.db-journal } 1
+do_test journal2-1.15 {
+ execsql {
+ SELECT count(*) FROM t2;
+ PRAGMA integrity_check;
+ }
+} {64 ok}
+
+# This block checks that in the test case above, connection [db2] really
+# did begin writing to the database file before it hit IO errors. If
+# this is true, then the copy of the database file made before [db]
+# rolled back the hot journal should fail the integrity-check.
+#
+do_test journal2-1.16 {
+ set sz [expr [file size testX.db] / 1024]
+ expr {$sz>240 && $sz<400}
+} 1
+do_test journal2-1.17 {
+ expr {[catchsql { PRAGMA integrity_check } db] == "0 ok"}
+} {1}
+do_test journal2-1.20 {
+ sqlite3 db2 testX.db
+ expr {[catchsql { PRAGMA integrity_check } db2] == "0 ok"}
+} {0}
+do_test journal2-1.21 {
+ db2 close
+} {}
+db close
+
+#-------------------------------------------------------------------------
+# Test that it is possible to switch from journal_mode=truncate to
+# journal_mode=WAL on a SAFE_DELETE file-system. SQLite should close and
+# delete the journal file when committing the transaction that switches
+# the system to WAL mode.
+#
+ifcapable wal {
+ do_test journal2-2.1 {
+ faultsim_delete_and_reopen
+ set ::oplog [list]
+ execsql { PRAGMA journal_mode = persist }
+ set ::oplog
+ } {}
+ do_test journal2-2.2 {
+ execsql {
+ CREATE TABLE t1(x);
+ INSERT INTO t1 VALUES(3.14159);
+ }
+ set ::oplog
+ } {xOpen test.db-journal}
+ do_test journal2-2.3 {
+ expr {[file size test.db-journal] > 512}
+ } {1}
+ do_test journal2-2.4 {
+ set ::oplog [list]
+ execsql { PRAGMA journal_mode = WAL }
+ set ::oplog
+ } {xClose test.db-journal xDelete test.db-journal}
+ db close
+}
+
+tvfs delete
+finish_test
+
diff --git a/test/jrnlmode2.test b/test/jrnlmode2.test
index 9a1fe37c3..dc3bc270b 100644
--- a/test/jrnlmode2.test
+++ b/test/jrnlmode2.test
@@ -9,7 +9,6 @@
#
#***********************************************************************
#
-# $Id: jrnlmode2.test,v 1.6 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0]
source $testdir/tester.tcl
@@ -20,10 +19,33 @@ ifcapable {!pager_pragmas} {
}
#-------------------------------------------------------------------------
-# Test overview:
+# The tests in this file check that the following two bugs (both now fixed)
+# do not reappear.
#
-# jrnlmode2-1.*: Demonstrate bug #3745
-# jrnlmode2-2.*: Demonstrate bug #3751
+# jrnlmode2-1.*: Demonstrate bug #3745:
+#
+# In persistent journal mode, if:
+#
+# * There is a persistent journal in the file-system, AND
+# * there exists a connection with a shared lock on the db file,
+#
+# then a second connection cannot open a read-transaction on the database.
+# The reason is because while determining that the persistent-journal is
+# not a hot-journal, SQLite currently grabs an exclusive lock on the
+# database file. If this fails because another connection has a shared
+# lock, then SQLITE_BUSY is returned to the user.
+#
+# jrnlmode2-2.*: Demonstrate bug #3751:
+#
+# If a connection is opened in SQLITE_OPEN_READONLY mode, the underlying
+# unix file descriptor on the database file is opened in O_RDONLY mode.
+#
+# When SQLite queries the database file for the schema in order to compile
+# the SELECT statement, it sees the empty journal in the file system, it
+# attempts to obtain an exclusive lock on the database file (this is a
+# bug). The attempt to obtain an exclusive (write) lock on a read-only file
+# fails at the OS level. Under unix, fcntl() reports an EBADF - "Bad file
+# descriptor" - error.
#
do_test jrnlmode2-1.1 {
@@ -46,6 +68,8 @@ do_test jrnlmode2-1.3 {
do_test jrnlmode2-1.4 {
execsql {
INSERT INTO t1 VALUES(3, 4);
+ }
+ execsql {
BEGIN;
SELECT * FROM t1;
}
@@ -87,9 +111,9 @@ do_test jrnlmode2-2.4 {
} {0 {1 2 3 4 5 6}}
do_test jrnlmode2-2.5 {
+ db close
file delete test.db-journal
} {}
-
do_test jrnlmode2-2.6 {
sqlite3 db2 test.db -readonly 1
catchsql { SELECT * FROM t1 } db2
diff --git a/test/malloc_common.tcl b/test/malloc_common.tcl
index a24716cd4..8202b19af 100644
--- a/test/malloc_common.tcl
+++ b/test/malloc_common.tcl
@@ -49,6 +49,16 @@ set FAULTSIM(ioerr-persistent) [list \
-injecterrlist {{1 {disk I/O error}}} \
]
+# SQLITE_FULL errors (always persistent):
+#
+set FAULTSIM(full) [list \
+ -injectinstall fullerr_injectinstall \
+ -injectstart fullerr_injectstart \
+ -injectstop fullerr_injectstop \
+ -injecterrlist {{1 {database or disk is full}}} \
+ -injectuninstall fullerr_injectuninstall \
+]
+
# Transient and persistent SHM errors:
#
set FAULTSIM(shmerr-transient) [list \
@@ -126,14 +136,14 @@ proc faultsim_save_and_close {} {
catch { db close }
return ""
}
-proc faultsim_restore_and_reopen {} {
+proc faultsim_restore_and_reopen {{dbfile test.db}} {
catch { db close }
foreach f [glob -nocomplain test.db*] { file delete -force $f }
foreach f2 [glob -nocomplain sv_test.db*] {
set f [string range $f2 3 end]
file copy -force $f2 $f
}
- sqlite3 db test.db
+ sqlite3 db $dbfile
sqlite3_extended_result_codes db 1
sqlite3_db_config_lookaside db 0 0 0
}
@@ -146,7 +156,7 @@ proc faultsim_integrity_check {{db db}} {
proc faultsim_delete_and_reopen {{file test.db}} {
catch { db close }
foreach f [glob -nocomplain test.db*] { file delete -force $f }
- sqlite3 db test.db
+ sqlite3 db $file
}
@@ -177,6 +187,7 @@ proc ioerr_injectstop {} {
return $sv
}
+
# The following procs are used as [do_one_faultsim_test] callbacks when
# injecting shared-memory related error faults into test cases.
#
@@ -195,6 +206,22 @@ proc shmerr_injectstop {} {
shmfault ioerr 0 0
}
+proc fullerr_injectinstall {} {
+ testvfs shmfault -default true
+}
+proc fullerr_injectuninstall {} {
+ catch {db close}
+ catch {db2 close}
+ shmfault delete
+}
+proc fullerr_injectstart {iFail} {
+ shmfault full $iFail
+}
+proc fullerr_injectstop {} {
+ shmfault full 0
+}
+
+
# This command is not called directly. It is used by the
# [faultsim_test_result] command created by [do_faultsim_test] and used
# by -test scripts.
diff --git a/test/pager1.test b/test/pager1.test
index d0caaa9d4..f7a821b6d 100644
--- a/test/pager1.test
+++ b/test/pager1.test
@@ -14,6 +14,7 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl
+source $testdir/wal_common.tcl
#
# pager1-1.*: Test inter-process locking (clients in multiple processes).
@@ -22,13 +23,14 @@ source $testdir/malloc_common.tcl
#
# pager1-3.*: Savepoint related tests.
#
-
-proc do_execsql_test {testname sql result} {
- uplevel do_test $testname [list "execsql {$sql}"] [list $result]
-}
-proc do_catchsql_test {testname sql result} {
- uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
-}
+# pager1-4.*: Hot-journal related tests.
+#
+# pager1-5.*: Cases related to multi-file commits.
+#
+# pager1-6.*: Cases related to "PRAGMA max_page_count"
+#
+# pager1-7.*: Cases specific to "PRAGMA journal_mode=TRUNCATE"
+#
set a_string_counter 1
proc a_string {n} {
@@ -221,6 +223,17 @@ do_execsql_test pager1-3.6 { COMMIT } {}
# compute) the associated journal is rolled back (and no
# xAccess() call to check for the presence of any master
# journal file is made).
+#
+# pager1.4.3.*: Test that the contents of a hot-journal are ignored if the
+# page-size or sector-size in the journal header appear to
+# be invalid (too large, too small or not a power of 2).
+#
+# pager1.4.4.*: Test hot-journal rollback of journal file with a master
+# journal pointer generated in various "PRAGMA synchronous"
+# modes.
+#
+# pager1.4.5.*: Test that hot-journal rollback stops if it encounters a
+# journal-record for which the checksum fails.
#
do_test pager1.4.1.1 {
faultsim_delete_and_reopen
@@ -308,5 +321,426 @@ do_test pager1.4.2.5 {
}
} {4 ok}
+do_test pager1.4.3.1 {
+ testvfs tstvfs -default 1
+ tstvfs filter xSync
+ tstvfs script xSyncCallback
+ proc xSyncCallback {method file args} {
+ set file [file tail $file]
+ if { 0==[string match *journal $file] } { faultsim_save }
+ }
+ faultsim_delete_and_reopen
+ execsql {
+ PRAGMA journal_mode = DELETE;
+ CREATE TABLE t1(a, b);
+ INSERT INTO t1 VALUES(1, 2);
+ INSERT INTO t1 VALUES(3, 4);
+ }
+ db close
+ tstvfs delete
+} {}
+
+foreach {tn ofst value result} {
+ 2 20 31 {1 2 3 4}
+ 3 20 32 {1 2 3 4}
+ 4 20 33 {1 2 3 4}
+ 5 20 65536 {1 2 3 4}
+ 6 20 131072 {1 2 3 4}
+
+ 7 24 511 {1 2 3 4}
+ 8 24 513 {1 2 3 4}
+ 9 24 65536 {1 2 3 4}
+
+ 10 32 65536 {1 2}
+} {
+ do_test pager1.4.3.$tn {
+ faultsim_restore_and_reopen
+ hexio_write test.db-journal $ofst [format %.8x $value]
+ execsql { SELECT * FROM t1 }
+ } $result
+}
+db close
+
+# Set up a VFS that snapshots the file-system just before a master journal
+# file is deleted to commit a multi-file transaction. Specifically, the
+# file-system is saved just before the xDelete() call to remove the
+# master journal file from the file-system.
+#
+testvfs tv -default 1
+tv script copy_on_mj_delete
+set ::mj_filename_length 0
+proc copy_on_mj_delete {method filename args} {
+ if {[string match *mj* [file tail $filename]]} {
+ set ::mj_filename_length [string length $filename]
+ faultsim_save
+ }
+ return SQLITE_OK
+}
+
+set pwd [pwd]
+foreach {tn1 tcl} {
+ 1 { set prefix "test.db" }
+ 2 {
+ # This test depends on the underlying VFS being able to open paths
+ # 512 bytes in length. The idea is to create a hot-journal file that
+ # contains a master-journal pointer so large that it could contain
+ # a valid page record (if the file page-size is 512 bytes). So as to
+ # make sure SQLite doesn't get confused by this.
+ #
+ set nPadding [expr 511 - $::mj_filename_length]
+
+ # We cannot just create a really long database file name to open, as
+ # Linux limits a single component of a path to 255 bytes by default
+ # (and presumably other systems have limits too). So create a directory
+ # hierarchy to work in.
+ #
+ set dirname "d123456789012345678901234567890/"
+ set nDir [expr $nPadding / 32]
+ if { $nDir } {
+ set p [string repeat $dirname $nDir]
+ file mkdir $p
+ cd $p
+ }
+
+ set padding [string repeat x [expr $nPadding %32]]
+ set prefix "test.db${padding}"
+ }
+} {
+ eval $tcl
+ foreach {tn2 sql} {
+ o {
+ PRAGMA main.synchronous=OFF;
+ PRAGMA aux.synchronous=OFF;
+ }
+ o512 {
+ PRAGMA main.synchronous=OFF;
+ PRAGMA aux.synchronous=OFF;
+ PRAGMA main.page_size = 512;
+ PRAGMA aux.page_size = 512;
+ }
+ n {
+ PRAGMA main.synchronous=NORMAL;
+ PRAGMA aux.synchronous=NORMAL;
+ }
+ f {
+ PRAGMA main.synchronous=FULL;
+ PRAGMA aux.synchronous=FULL;
+ }
+ } {
+
+ set tn "${tn1}.${tn2}"
+
+ # Set up a connection to have two databases, test.db (main) and
+ # test.db2 (aux). Then run a multi-file transaction on them. The
+ # VFS will snapshot the file-system just before the master-journal
+ # file is deleted to commit the transaction.
+ #
+ tv filter xDelete
+ do_test pager1-4.4.$tn.1 {
+ faultsim_delete_and_reopen $prefix
+ execsql "
+ ATTACH '${prefix}2' AS aux;
+ $sql
+ CREATE TABLE a(x);
+ CREATE TABLE aux.b(x);
+ INSERT INTO a VALUES('double-you');
+ INSERT INTO a VALUES('why');
+ INSERT INTO a VALUES('zed');
+ INSERT INTO b VALUES('won');
+ INSERT INTO b VALUES('too');
+ INSERT INTO b VALUES('free');
+ "
+ execsql {
+ BEGIN;
+ INSERT INTO a SELECT * FROM b WHERE rowid<=3;
+ INSERT INTO b SELECT * FROM a WHERE rowid<=3;
+ COMMIT;
+ }
+ } {}
+ tv filter {}
+
+ # Check that the transaction was committed successfully.
+ #
+ do_execsql_test pager1-4.4.$tn.2 {
+ SELECT * FROM a
+ } {double-you why zed won too free}
+ do_execsql_test pager1-4.4.$tn.3 {
+ SELECT * FROM b
+ } {won too free double-you why zed}
+
+ # Restore the file-system and reopen the databases. Check that it now
+ # appears that the transaction was not committed (because the file-system
+ # was restored to the state where it had not been).
+ #
+ do_test pager1-4.4.$tn.4 {
+ faultsim_restore_and_reopen $prefix
+ execsql "ATTACH '${prefix}2' AS aux"
+ } {}
+ do_execsql_test pager1-4.4.$tn.5 {SELECT * FROM a} {double-you why zed}
+ do_execsql_test pager1-4.4.$tn.6 {SELECT * FROM b} {won too free}
+
+ # Restore the file-system again. This time, before reopening the databases,
+ # delete the master-journal file from the file-system. It now appears that
+ # the transaction was committed (no master-journal file == no rollback).
+ #
+ do_test pager1-4.4.$tn.7 {
+ faultsim_restore_and_reopen $prefix
+ foreach f [glob ${prefix}-mj*] { file delete -force $f }
+ execsql "ATTACH '${prefix}2' AS aux"
+ } {}
+ do_execsql_test pager1-4.4.$tn.8 {
+ SELECT * FROM a
+ } {double-you why zed won too free}
+ do_execsql_test pager1-4.4.$tn.9 {
+ SELECT * FROM b
+ } {won too free double-you why zed}
+ }
+
+ cd $pwd
+}
+db close
+tv delete
+
+#-------------------------------------------------------------------------
+# The following tests deal with multi-file commits.
+#
+# pager1-5.1.*: The case where a multi-file cannot be committed because
+# another connection is holding a SHARED lock on one of the
+# files. After the SHARED lock is removed, the COMMIT succeeds.
+#
+# pager1-5.2.*: Multi-file commits with journal_mode=memory.
+#
+# pager1-5.3.*: Multi-file commits with journal_mode=memory.
+#
+# pager1-5.4.*: Check that with synchronous=normal, the master-journal file
+# name is added to a journal file immediately after the last
+# journal record. But with synchronous=full, extra unused space
+# is allocated between the last journal record and the
+# master-journal file name so that the master-journal file
+# name does not lie on the same sector as the last journal file
+# record.
+#
+# pager1-5.5.*: Check that in journal_mode=PERSIST mode, a journal file is
+# truncated to zero bytes when a multi-file transaction is
+# committed (instead of the first couple of bytes being zeroed).
+#
+#
+do_test pager1-5.1.1 {
+ faultsim_delete_and_reopen
+ execsql {
+ ATTACH 'test.db2' AS aux;
+ CREATE TABLE t1(a, b);
+ CREATE TABLE aux.t2(a, b);
+ INSERT INTO t1 VALUES(17, 'Lenin');
+ INSERT INTO t1 VALUES(22, 'Stalin');
+ INSERT INTO t1 VALUES(53, 'Khrushchev');
+ }
+} {}
+do_test pager1-5.1.2 {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 VALUES(64, 'Brezhnev');
+ INSERT INTO t2 SELECT * FROM t1;
+ }
+ sqlite3 db2 test.db2
+ execsql {
+ BEGIN;
+ SELECT * FROM t2;
+ } db2
+} {}
+do_test pager1-5.1.3 {
+ catchsql COMMIT
+} {1 {database is locked}}
+do_test pager1-5.1.4 {
+ execsql COMMIT db2
+ execsql COMMIT
+ execsql { SELECT * FROM t2 } db2
+} {17 Lenin 22 Stalin 53 Khrushchev 64 Brezhnev}
+do_test pager1-5.1.5 {
+ db2 close
+} {}
+
+do_test pager1-5.2.1 {
+ execsql {
+ PRAGMA journal_mode = memory;
+ BEGIN;
+ INSERT INTO t1 VALUES(84, 'Andropov');
+ INSERT INTO t2 VALUES(84, 'Andropov');
+ COMMIT;
+ }
+} {memory}
+do_test pager1-5.3.1 {
+ execsql {
+ PRAGMA journal_mode = off;
+ BEGIN;
+ INSERT INTO t1 VALUES(85, 'Gorbachev');
+ INSERT INTO t2 VALUES(85, 'Gorbachev');
+ COMMIT;
+ }
+} {off}
+
+do_test pager1-5.4.1 {
+ db close
+ testvfs tv
+ sqlite3 db test.db -vfs tv
+ execsql { ATTACH 'test.db2' AS aux }
+
+ tv filter xDelete
+ tv script max_journal_size
+ tv sectorsize 512
+ set ::max_journal 0
+ proc max_journal_size {method args} {
+ set sz 0
+ catch { set sz [file size test.db-journal] }
+ if {$sz > $::max_journal} {
+ set ::max_journal $sz
+ }
+ return SQLITE_OK
+ }
+ execsql {
+ PRAGMA journal_mode = DELETE;
+ PRAGMA synchronous = NORMAL;
+ BEGIN;
+ INSERT INTO t1 VALUES(85, 'Gorbachev');
+ INSERT INTO t2 VALUES(85, 'Gorbachev');
+ COMMIT;
+ }
+ set ::max_journal
+} [expr 2615+[string length [pwd]]]
+do_test pager1-5.4.2 {
+ set ::max_journal 0
+ execsql {
+ PRAGMA synchronous = full;
+ BEGIN;
+ DELETE FROM t1 WHERE b = 'Lenin';
+ DELETE FROM t2 WHERE b = 'Lenin';
+ COMMIT;
+ }
+ set ::max_journal
+} [expr 3111+[string length [pwd]]]
+db close
+tv delete
+
+do_test pager1-5.5.1 {
+ sqlite3 db test.db
+ execsql {
+ ATTACH 'test.db2' AS aux;
+ PRAGMA journal_mode = PERSIST;
+ CREATE TABLE t3(a, b);
+ INSERT INTO t3 SELECT randomblob(1500), randomblob(1500) FROM t1;
+ UPDATE t3 SET b = randomblob(1500);
+ }
+ expr [file size test.db-journal] > 15000
+} {1}
+do_test pager1-5.5.2 {
+ execsql {
+ PRAGMA synchronous = full;
+ BEGIN;
+ DELETE FROM t1 WHERE b = 'Stalin';
+ DELETE FROM t2 WHERE b = 'Stalin';
+ COMMIT;
+ }
+ file size test.db-journal
+} {0}
+
+
+#-------------------------------------------------------------------------
+# The following tests work with "PRAGMA max_page_count"
+#
+do_test pager1-6.1 {
+ faultsim_delete_and_reopen
+ execsql {
+ PRAGMA max_page_count = 10;
+ CREATE TABLE t2(a, b);
+ CREATE TABLE t3(a, b);
+ CREATE TABLE t4(a, b);
+ CREATE TABLE t5(a, b);
+ CREATE TABLE t6(a, b);
+ CREATE TABLE t7(a, b);
+ CREATE TABLE t8(a, b);
+ CREATE TABLE t9(a, b);
+ CREATE TABLE t10(a, b);
+ }
+} {10}
+do_test pager1-6.2 {
+ catchsql {
+ CREATE TABLE t11(a, b);
+ }
+} {1 {database or disk is full}}
+
+
+#-------------------------------------------------------------------------
+# The following tests work with "PRAGMA journal_mode=TRUNCATE" and
+# "PRAGMA locking_mode=EXCLUSIVE".
+#
+# Each test is specified with 5 variables. As follows:
+#
+# $tn: Test Number. Used as part of the [do_test] test names.
+# $sql: SQL to execute.
+# $res: Expected result of executing $sql.
+# $js: The expected size of the journal file, in bytes, after executing
+# the SQL script. Or -1 if the journal is not expected to exist.
+# $ws: The expected size of the WAL file, in bytes, after executing
+# the SQL script. Or -1 if the WAL is not expected to exist.
+#
+faultsim_delete_and_reopen
+foreach {tn sql res js ws} [subst {
+
+ 1 {
+ CREATE TABLE t1(a, b);
+ PRAGMA auto_vacuum=OFF;
+ PRAGMA synchronous=NORMAL;
+ PRAGMA page_size=1024;
+ PRAGMA locking_mode=EXCLUSIVE;
+ PRAGMA journal_mode=TRUNCATE;
+ INSERT INTO t1 VALUES(1, 2);
+ } {exclusive truncate} 0 -1
+
+ 2 {
+ BEGIN IMMEDIATE;
+ SELECT * FROM t1;
+ COMMIT;
+ } {1 2} 0 -1
+
+ 3 {
+ BEGIN;
+ SELECT * FROM t1;
+ COMMIT;
+ } {1 2} 0 -1
+
+ 4 { PRAGMA journal_mode = WAL } wal -1 -1
+ 5 { INSERT INTO t1 VALUES(3, 4) } {} -1 [wal_file_size 1 1024]
+ 6 { PRAGMA locking_mode = NORMAL } normal -1 [wal_file_size 1 1024]
+ 7 { INSERT INTO t1 VALUES(5, 6); } {} -1 [wal_file_size 2 1024]
+
+ 8 { PRAGMA journal_mode = TRUNCATE } truncate 0 -1
+ 9 { INSERT INTO t1 VALUES(7, 8) } {} 0 -1
+ 10 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8} 0 -1
+
+}] {
+ do_execsql_test pager1-7.1.$tn.1 $sql $res
+ catch { set J -1 ; set J [file size test.db-journal] }
+ catch { set W -1 ; set W [file size test.db-wal] }
+ do_test pager1-7.1.$tn.2 { list $J $W } [list $js $ws]
+}
+
+do_test pager1-8.1 {
+ faultsim_delete_and_reopen
+ db close
+ sqlite3 db :memory:
+ execsql {
+ CREATE TABLE x1(x);
+ INSERT INTO x1 VALUES('Charles');
+ INSERT INTO x1 VALUES('James');
+ INSERT INTO x1 VALUES('Mary');
+ SELECT * FROM x1;
+ }
+} {Charles James Mary}
+do_test pager1-8.2 {
+ db close
+ sqlite3 db :memory:
+ catchsql { SELECT * FROM x1 }
+} {1 {no such table: x1}}
+
finish_test
diff --git a/test/pager2.test b/test/pager2.test
new file mode 100644
index 000000000..855850f05
--- /dev/null
+++ b/test/pager2.test
@@ -0,0 +1,117 @@
+# 2010 June 15
+#
+# The author disclaims copyright to this source code. In place of
+# a legal notice, here is a blessing:
+#
+# May you do good and not evil.
+# May you find forgiveness for yourself and forgive others.
+# May you share freely, never taking more than you give.
+#
+#***********************************************************************
+#
+
+set testdir [file dirname $argv0]
+source $testdir/tester.tcl
+source $testdir/lock_common.tcl
+source $testdir/malloc_common.tcl
+
+set otn 0
+testvfs tv -default 1
+foreach code [list {
+ set s 512
+} {
+ set s 1024
+ set sql { PRAGMA journal_mode = memory }
+} {
+ set s 1024
+ set sql {
+ PRAGMA journal_mode = memory;
+ PRAGMA locking_mode = exclusive;
+ }
+} {
+ set s 2048
+ tv devchar safe_append
+} {
+ set s 4096
+} {
+ set s 4096
+ set sql { PRAGMA journal_mode = WAL }
+} {
+ set s 8192
+ set sql { PRAGMA synchronous = off }
+}] {
+
+ incr otn
+ set sql ""
+ tv devchar {}
+ eval $code
+ tv sectorsize $s
+
+ do_test pager2-1.$otn.0 {
+ faultsim_delete_and_reopen
+ execsql $sql
+ execsql {
+ PRAGMA cache_size = 10;
+ CREATE TABLE t1(i INTEGER PRIMARY KEY, j blob);
+ }
+ } {}
+
+ set tn 0
+ set lowpoint 0
+ foreach x {
+ 100 x 0 100
+ x
+ 70 22 96 59 96 50 22 56 21 16 37 64 43 40 0 38 22 38 55 0 6
+ 43 62 32 93 54 18 13 29 45 66 29 25 61 31 53 82 75 25 96 86 10 69
+ 2 29 6 60 80 95 42 82 85 50 68 96 90 39 78 69 87 97 48 74 65 43
+ x
+ 86 34 26 50 41 85 58 44 89 22 6 51 45 46 58 32 97 6 1 12 32 2
+ 69 39 48 71 33 31 5 58 90 43 24 54 12 9 18 57 4 38 91 42 27 45
+ 50 38 56 29 10 0 26 37 83 1 78 15 47 30 75 62 46 29 68 5 30 4
+ 27 96 33 95 79 75 56 10 29 70 32 75 52 88 5 36 50 57 46 63 88 65
+ x
+ 44 95 64 20 24 35 69 61 61 2 35 92 42 46 23 98 78 1 38 72 79 35
+ 94 37 13 59 5 93 27 58 80 75 58 7 67 13 10 76 84 4 8 70 81 45
+ 8 41 98 5 60 26 92 29 91 90 2 62 40 4 5 22 80 15 83 76 52 88
+ 29 5 68 73 72 7 54 17 89 32 81 94 51 28 53 71 8 42 54 59 70 79
+ x
+ } {
+ incr tn
+ set now [db one {SELECT count(i) FROM t1}]
+ if {$x == "x"} {
+ execsql { COMMIT ; BEGIN }
+ set lowpoint $now
+ do_test pager2.1.$otn.$tn {
+ sqlite3 db2 test.db
+ execsql {
+ SELECT COALESCE(max(i), 0) FROM t1;
+ PRAGMA integrity_check;
+ }
+ } [list $lowpoint ok]
+ db2 close
+ } else {
+ if {$now > $x } {
+ if { $x>=$lowpoint } {
+ execsql "ROLLBACK TO sp_$x"
+ } else {
+ execsql "DELETE FROM t1 WHERE i>$x"
+ set lowpoint $x
+ }
+ } elseif {$now < $x} {
+ for {set k $now} {$k < $x} {incr k} {
+ execsql "SAVEPOINT sp_$k"
+ execsql { INSERT INTO t1(j) VALUES(randomblob(1500)) }
+ }
+ }
+ do_execsql_test pager2.1.$otn.$tn {
+ SELECT COALESCE(max(i), 0) FROM t1;
+ PRAGMA integrity_check;
+ } [list $x ok]
+ }
+ }
+}
+db close
+tv delete
+
+
+finish_test
diff --git a/test/pagerfault.test b/test/pagerfault.test
index 8dc99e0bc..0af8d5180 100644
--- a/test/pagerfault.test
+++ b/test/pagerfault.test
@@ -55,10 +55,55 @@ do_faultsim_test pagerfault-1 -prep {
}
#-------------------------------------------------------------------------
+# Test fault-injection while rolling back a hot-journal file with a
+# page-size different from the current value stored on page 1 of the
+# database file.
+#
+do_test pagerfault-2-pre1 {
+ testvfs tv -default 1
+ tv filter xSync
+ tv script xSyncCb
+ proc xSyncCb {filename args} {
+ if {[string match *journal filename]==0} faultsim_save
+ }
+ faultsim_delete_and_reopen
+ execsql {
+ PRAGMA page_size = 4096;
+ BEGIN;
+ CREATE TABLE abc(a, b, c);
+ INSERT INTO abc VALUES('o', 't', 't');
+ INSERT INTO abc VALUES('f', 'f', 's');
+ INSERT INTO abc SELECT * FROM abc; -- 4
+ INSERT INTO abc SELECT * FROM abc; -- 8
+ INSERT INTO abc SELECT * FROM abc; -- 16
+ INSERT INTO abc SELECT * FROM abc; -- 32
+ INSERT INTO abc SELECT * FROM abc; -- 64
+ INSERT INTO abc SELECT * FROM abc; -- 128
+ INSERT INTO abc SELECT * FROM abc; -- 256
+ COMMIT;
+ PRAGMA page_size = 1024;
+ VACUUM;
+ }
+ db close
+ tv delete
+} {}
+do_faultsim_test pagerfault-2 -prep {
+ faultsim_restore_and_reopen
+} -body {
+ execsql { SELECT * FROM abc }
+} -test {
+ set answer [split [string repeat "ottffs" 128] ""]
+ faultsim_test_result [list 0 $answer]
+ faultsim_integrity_check
+ set res [db eval { SELECT * FROM abc }]
+ if {$res != $answer} { error "Database content appears incorrect ($res)" }
+} -faults oom-transient
+
+#-------------------------------------------------------------------------
# Test fault-injection while rolling back hot-journals that were created
# as part of a multi-file transaction.
#
-do_test pagerfault-2-pre1 {
+do_test pagerfault-3-pre1 {
testvfs tstvfs -default 1
tstvfs filter xDelete
tstvfs script xDeleteCallback
@@ -96,7 +141,7 @@ do_test pagerfault-2-pre1 {
db close
tstvfs delete
} {}
-do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep {
+do_faultsim_test pagerfault-3 -faults ioerr-persistent -prep {
faultsim_restore_and_reopen
} -body {
execsql {
@@ -107,7 +152,6 @@ do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep {
} -test {
faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}}
faultsim_integrity_check
-
catchsql { ATTACH 'test.db2' AS aux }
if {[db one { SELECT count(*) FROM t1 }] != 4
|| [db one { SELECT count(*) FROM t2 }] != 4
@@ -116,4 +160,151 @@ do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep {
}
}
+#-------------------------------------------------------------------------
+# Test fault-injection as part of a vanilla, no-transaction, INSERT
+# statement.
+#
+do_faultsim_test pagerfault-4 -prep {
+ faultsim_delete_and_reopen
+} -body {
+ execsql {
+ CREATE TABLE x(y);
+ INSERT INTO x VALUES('z');
+ SELECT * FROM x;
+ }
+} -test {
+ faultsim_test_result {0 z}
+ faultsim_integrity_check
+}
+
+#-------------------------------------------------------------------------
+# Test fault-injection as part of a commit when using journal_mode=PERSIST.
+# Three different cases:
+#
+# pagerfault-5.1: With no journal_size_limit configured.
+# pagerfault-5.2: With a journal_size_limit configured.
+# pagerfault-5.4: Multi-file transaction. One connection has a
+# journal_size_limit of 0, the other has no limit.
+#
+do_test pagerfault-5-pre1 {
+ faultsim_delete_and_reopen
+ db func a_string a_string
+ execsql {
+ CREATE TABLE t1(a UNIQUE, b UNIQUE);
+ INSERT INTO t1 VALUES(a_string(200), a_string(300));
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ }
+ faultsim_save_and_close
+} {}
+do_faultsim_test pagerfault-5.1 -prep {
+ faultsim_restore_and_reopen
+ db func a_string a_string
+ execsql { PRAGMA journal_mode = PERSIST }
+} -body {
+ execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
+} -test {
+ faultsim_test_result {0 {}}
+ faultsim_integrity_check
+}
+do_faultsim_test pagerfault-5.2 -prep {
+ faultsim_restore_and_reopen
+ db func a_string a_string
+ execsql {
+ PRAGMA journal_mode = PERSIST;
+ PRAGMA journal_size_limit = 2048;
+ }
+} -body {
+ execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
+} -test {
+ faultsim_test_result {0 {}}
+ faultsim_integrity_check
+}
+do_faultsim_test pagerfault-5.3 -prep {
+ faultsim_restore_and_reopen
+ db func a_string a_string
+ file delete -force test2.db test2.db-journal test2.db-wal
+ execsql {
+ PRAGMA journal_mode = PERSIST;
+ ATTACH 'test2.db' AS aux;
+ PRAGMA aux.journal_mode = PERSIST;
+ PRAGMA aux.journal_size_limit = 0;
+ }
+} -body {
+ execsql {
+ BEGIN;
+ INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1;
+ CREATE TABLE aux.t2 AS SELECT * FROM t1;
+ COMMIT;
+ }
+} -test {
+ faultsim_test_result {0 {}}
+}
+
+#-------------------------------------------------------------------------
+# Test fault-injection as part of a commit when using
+# journal_mode=TRUNCATE.
+#
+do_test pagerfault-6-pre1 {
+ faultsim_delete_and_reopen
+ db func a_string a_string
+ execsql {
+ CREATE TABLE t1(a UNIQUE, b UNIQUE);
+ INSERT INTO t1 VALUES(a_string(200), a_string(300));
+ }
+ faultsim_save_and_close
+} {}
+do_faultsim_test pagerfault-6.1 -prep {
+ faultsim_restore_and_reopen
+ db func a_string a_string
+ execsql { PRAGMA journal_mode = TRUNCATE }
+} -body {
+ execsql { INSERT INTO t1 SELECT a_string(200), a_string(300) FROM t1 }
+} -test {
+ faultsim_test_result {0 {}}
+ faultsim_integrity_check
+}
+
+# The following was an attempt to get a bitvec malloc to fail. Didn't work.
+#
+# do_test pagerfault-6-pre1 {
+# faultsim_delete_and_reopen
+# execsql {
+# CREATE TABLE t1(x, y, UNIQUE(x, y));
+# INSERT INTO t1 VALUES(1, randomblob(1501));
+# INSERT INTO t1 VALUES(2, randomblob(1502));
+# INSERT INTO t1 VALUES(3, randomblob(1503));
+# INSERT INTO t1 VALUES(4, randomblob(1504));
+# INSERT INTO t1
+# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
+# INSERT INTO t1
+# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
+# INSERT INTO t1
+# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
+# INSERT INTO t1
+# SELECT x, randomblob(1500+oid+(SELECT max(oid) FROM t1)) FROM t1;
+# }
+# faultsim_save_and_close
+# } {}
+# do_faultsim_test pagerfault-6 -prep {
+# faultsim_restore_and_reopen
+# } -body {
+# execsql {
+# BEGIN;
+# UPDATE t1 SET x=x+4 WHERE x=1;
+# SAVEPOINT one;
+# UPDATE t1 SET x=x+4 WHERE x=2;
+# SAVEPOINT three;
+# UPDATE t1 SET x=x+4 WHERE x=3;
+# SAVEPOINT four;
+# UPDATE t1 SET x=x+4 WHERE x=4;
+# RELEASE three;
+# COMMIT;
+# SELECT DISTINCT x FROM t1;
+# }
+# } -test {
+# faultsim_test_result {0 {5 6 7 8}}
+# faultsim_integrity_check
+# }
+
finish_test
diff --git a/test/permutations.test b/test/permutations.test
index 921df33a4..e1846c13a 100644
--- a/test/permutations.test
+++ b/test/permutations.test
@@ -169,7 +169,9 @@ test_suite "coverage-pager" -description {
Coverage tests for file pager.c.
} -files {
pager1.test
+ pager2.test
pagerfault.test
+ journal2.test
}
diff --git a/test/tester.tcl b/test/tester.tcl
index 625bdcc3e..d6ce7eac2 100644
--- a/test/tester.tcl
+++ b/test/tester.tcl
@@ -49,6 +49,8 @@
# crashsql ARGS...
# integrity_check TESTNAME ?DB?
# do_test TESTNAME SCRIPT EXPECTED
+# do_execsql_test TESTNAME SQL EXPECTED
+# do_catchsql_test TESTNAME SQL EXPECTED
#
# Commands providing a lower level interface to the global test counters:
#
@@ -316,6 +318,14 @@ proc do_test {name cmd expected} {
}
flush stdout
}
+
+proc do_execsql_test {testname sql result} {
+ uplevel do_test $testname [list "execsql {$sql}"] [list $result]
+}
+proc do_catchsql_test {testname sql result} {
+ uplevel do_test $testname [list "catchsql {$sql}"] [list $result]
+}
+
# Run an SQL script.
# Return the number of microseconds per statement.
diff --git a/test/walmode.test b/test/walmode.test
index 714e0d818..48979b19c 100644
--- a/test/walmode.test
+++ b/test/walmode.test
@@ -124,46 +124,48 @@ do_test walmode-4.5 {
# from WAL to rollback mode because a second connection has the database
# open. Or from rollback to WAL.
#
-do_test walmode-4.1 {
+do_test walmode-4.6 {
sqlite3 db2 test.db
execsql { PRAGMA main.journal_mode } db2
} {delete}
-do_test walmode-4.2 {
+do_test walmode-4.7 {
execsql { PRAGMA main.journal_mode = wal } db
} {wal}
-do_test walmode-4.3 {
+do_test walmode-4.8 {
execsql { SELECT * FROM t1 } db2
} {1 2}
-do_test walmode-4.4 {
+do_test walmode-4.9 {
catchsql { PRAGMA journal_mode = delete } db
} {1 {database is locked}}
-do_test walmode-4.5 {
+do_test walmode-4.10 {
execsql { PRAGMA main.journal_mode } db
} {wal}
-do_test walmode-4.6 {
+
+do_test walmode-4.11 {
db2 close
execsql { PRAGMA journal_mode = delete } db
} {delete}
-do_test walmode-4.7 {
+do_test walmode-4.12 {
execsql { PRAGMA main.journal_mode } db
} {delete}
-do_test walmode-4.8 {
+do_test walmode-4.13 {
list [file exists test.db-journal] [file exists test.db-wal]
} {0 0}
-do_test walmode-4.9 {
+do_test walmode-4.14 {
sqlite3 db2 test.db
execsql {
BEGIN;
SELECT * FROM t1;
} db2
} {1 2}
-do_test walmode-4.11 {
- execsql { PRAGMA main.journal_mode } db
-} {delete}
-do_test walmode-4.10 {
+
+do_test walmode-4.16 { execsql { PRAGMA main.journal_mode } db } {delete}
+do_test walmode-4.17 { execsql { PRAGMA main.journal_mode } db2 } {delete}
+
+do_test walmode-4.17 {
catchsql { PRAGMA main.journal_mode = wal } db
} {1 {database is locked}}
-do_test walmode-4.11 {
+do_test walmode-4.18 {
execsql { PRAGMA main.journal_mode } db
} {delete}
catch { db close }
@@ -180,7 +182,6 @@ do_test walmode-5.1.1 {
sqlite3 db :memory:
execsql { PRAGMA main.journal_mode }
} {memory}
-breakpoint
do_test walmode-5.1.2 {
execsql { PRAGMA main.journal_mode = wal }
} {memory}