1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-18 10:21:03 +03:00

Merge the experimental UNDELETABLE_WHEN_OPEN optimization into the trunk.

FossilOrigin-Name: ee0acef1faffd480fd2136f81fb2b6f6a17b5388
This commit is contained in:
drh
2010-06-21 12:47:41 +00:00
18 changed files with 1379 additions and 116 deletions

View File

@@ -1,8 +1,8 @@
-----BEGIN PGP SIGNED MESSAGE----- -----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1 Hash: SHA1
C Fix\san\suninitialized\svariable\sin\sos_unix.c. C Merge\sthe\sexperimental\sUNDELETABLE_WHEN_OPEN\soptimization\sinto\sthe\strunk.
D 2010-06-19T23:53:11 D 2010-06-21T12:47:41
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3 F Makefile.in a5cad1f8f3e021356bfcc6c77dc16f6f1952bbc3
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
@@ -157,9 +157,9 @@ F src/os.c 9c4a2f82a50306a33907678ec0187b6ad1486bfe
F src/os.h d7775504a51e6e0d40315aa427b3e229ff9ff9ca F src/os.h d7775504a51e6e0d40315aa427b3e229ff9ff9ca
F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f F src/os_common.h a8f95b81eca8a1ab8593d23e94f8a35f35d4078f
F src/os_os2.c 665876d5eec7585226b0a1cf5e18098de2b2da19 F src/os_os2.c 665876d5eec7585226b0a1cf5e18098de2b2da19
F src/os_unix.c 838e0c0cabb722ac573c8cbd666b47bdf44eb8f9 F src/os_unix.c 5231a75a3799872b1250bc70c0e6a1a5960bc865
F src/os_win.c dfde7d33c446e89dd9a277c036f2c4cc564b3138 F src/os_win.c 73608839342de32280cb378d3c2fc85a5dd80bd2
F src/pager.c 4fe451d68950002eb985e6325d666ab54956a37f F src/pager.c 9f138b79b47090c1e31efe3d9ea191cc92981643
F src/pager.h ca1f23c0cf137ac26f8908df2427c8b308361efd F src/pager.h ca1f23c0cf137ac26f8908df2427c8b308361efd
F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07 F src/pcache.c 1e9aa2dbc0845b52e1b51cc39753b6d1e041cb07
@@ -173,7 +173,7 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b
F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714 F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714
F src/sqlite.h.in 46c01e55cea31b91565ae41276c6310ee4032be8 F src/sqlite.h.in 301476d8556cbb1c5d4bc906370b2dafe4d98a44
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
F src/sqliteInt.h 242987ebd2366ea36650a09cdab04a9163c62109 F src/sqliteInt.h 242987ebd2366ea36650a09cdab04a9163c62109
F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3 F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
@@ -212,7 +212,7 @@ F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6 F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c aa9919c885a1fe53eafc73492f0898ee6c0a0726 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/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb
F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d
@@ -220,12 +220,12 @@ F src/update.c 9859f2056c7739a1db0d9774ccb6c2f0cee6d1de
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f
F src/vdbe.c e115585b14d2cc4128cb53a7e42f207750e80f55 F src/vdbe.c 290d20ed92b560dc0b602ac50b252f9553a2d8e8
F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3 F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3
F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1 F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1
F src/vdbeapi.c dc3138f10afbc95ed3c21dd25abb154504b1db9d F src/vdbeapi.c dc3138f10afbc95ed3c21dd25abb154504b1db9d
F src/vdbeaux.c 4e96a5169b988a8697d4a417f902277b4152e43e F src/vdbeaux.c 4e96a5169b988a8697d4a417f902277b4152e43e
F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e F src/vdbeblob.c 258a6010ba7a82b72b327fb24c55790655689256
F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1 F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
@@ -469,8 +469,9 @@ F test/join4.test 1a352e4e267114444c29266ce79e941af5885916
F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe F test/join5.test 86675fc2919269aa923c84dd00ee4249b97990fe
F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901 F test/join6.test bf82cf3f979e9eade83ad0d056a66c5ed71d1901
F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19 F test/journal1.test 36f2d1bb9bf03f790f43fbdb439e44c0657fab19
F test/journal2.test a08ea6545d987385e7cbb1d4e7dc2eaacd83b00b
F test/jrnlmode.test 76f94d61528c5ff32102a12f8cf34f4cc36f7849 F test/jrnlmode.test 76f94d61528c5ff32102a12f8cf34f4cc36f7849
F test/jrnlmode2.test fe79ea1f0375c926b8de0362ddf94f34a64135fd F test/jrnlmode2.test a19e28de1a6ec898067e46a122f1b71c9323bf00
F test/jrnlmode3.test cfcdb12b90e640a23b92785a002d96c0624c8710 F test/jrnlmode3.test cfcdb12b90e640a23b92785a002d96c0624c8710
F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05 F test/keyword1.test a2400977a2e4fde43bf33754c2929fda34dbca05
F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51 F test/lastinsert.test 474d519c68cb79d07ecae56a763aa7f322c72f51
@@ -511,7 +512,7 @@ F test/mallocH.test 79b65aed612c9b3ed2dcdaa727c85895fd1bfbdb
F test/mallocI.test e3ea401904d010cb7c1e4b2ee8803f4a9f5b999d F test/mallocI.test e3ea401904d010cb7c1e4b2ee8803f4a9f5b999d
F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e F test/mallocJ.test b5d1839da331d96223e5f458856f8ffe1366f62e
F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9 F test/mallocK.test d79968641d1b70d88f6c01bdb9a7eb4a55582cc9
F test/malloc_common.tcl fbf369eb2828825c5f319c101917aff91ea87556 F test/malloc_common.tcl 58caffc4be307b56c5b1438b5eba3eb278bd81f5
F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c F test/manydb.test b3d3bc4c25657e7f68d157f031eb4db7b3df0d3c
F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498 F test/memdb.test 0825155b2290e900264daaaf0334b6dfe69ea498
F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2 F test/memleak.test 10b9c6c57e19fc68c32941495e9ba1c50123f6e2
@@ -536,13 +537,14 @@ F test/notify2.test 195a467e021f74197be2c4fb02d6dee644b8d8db
F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347 F test/notnull.test cc7c78340328e6112a13c3e311a9ab3127114347
F test/null.test a8b09b8ed87852742343b33441a9240022108993 F test/null.test a8b09b8ed87852742343b33441a9240022108993
F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec F test/openv2.test af02ed0a9cbc0d2a61b8f35171d4d117e588e4ec
F test/pager1.test 60dec408563461f9fbf04d4d301b1b4db23f7525 F test/pager1.test 86d034bf3ffe4e99648714443776440d0555f705
F test/pagerfault.test 16e560bc4332d5b089b369d82ae4b65b8805b5eb F test/pager2.test ad062a51030dc1e2749f506528db4cc5bae6474c
F test/pagerfault.test e67e9c18bf7b4bb8cc8d458d3a5ecc980f18a225
F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806 F test/pageropt.test 8146bf448cf09e87bb1867c2217b921fb5857806
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
F test/pcache.test eebc4420b37cb07733ae9b6e99c9da7c40dd6d58 F test/pcache.test eebc4420b37cb07733ae9b6e99c9da7c40dd6d58
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16 F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
F test/permutations.test f044eaba204ff13d530ceb72a22b0ed2c43562ef F test/permutations.test 9296368f1d14d9e042f146a804ca38f551d35435
F test/pragma.test 6960f9efbce476f70ba9ee2171daf5042f9e3d8a F test/pragma.test 6960f9efbce476f70ba9ee2171daf5042f9e3d8a
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47 F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
@@ -613,7 +615,7 @@ F test/tclsqlite.test 8c154101e704170c2be10f137a5499ac2c6da8d3
F test/tempdb.test 800c36623d67a2ad1f58784b9c5644e0405af6e6 F test/tempdb.test 800c36623d67a2ad1f58784b9c5644e0405af6e6
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05 F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl 7912c3c8768320fd7bcb217637c2f0a607fbbc24 F test/tester.tcl ab89e8e592ff26e2b65ff3cae9de5f26863ae766
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -780,7 +782,7 @@ F test/walcrash.test f6d5fb2bb108876f04848720a488065d9deef69f
F test/walcrash2.test 14585ad1a2c85da2de721caa3b4deeea55213008 F test/walcrash2.test 14585ad1a2c85da2de721caa3b4deeea55213008
F test/walfault.test c2b524299dede269282a0795e11396cc446ca9af F test/walfault.test c2b524299dede269282a0795e11396cc446ca9af
F test/walhook.test 67e675127f4acb72f061a12667ce6e5460b06b78 F test/walhook.test 67e675127f4acb72f061a12667ce6e5460b06b78
F test/walmode.test 6ca9d710cc9f6545b913abcded6d6b0b15641048 F test/walmode.test b54e2f91f34179c65cab02a6916578617a33eef0
F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933 F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c F test/walthread.test a25a393c068a2b42b44333fa3fdaae9072f1617c
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330 F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
@@ -826,14 +828,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 2241788bc85fbc48e9cfecb95fe0a858338e37cb P 822a0283c6bc1c75001f3d1c528a4ff89c6b039c a64d96db09ef2b7651fa4e98d3c7bf3ae5d3fe96
R 519d2c82f8071bfeedb3eae3a50de5e0 R aa81338105a8066f92038016780c9df7
U drh U drh
Z fb28c7b4632831ff9010ebcbf771882d Z e0549d09ee79c82d436d5aa9bbd618a3
-----BEGIN PGP SIGNATURE----- -----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux) Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFMHVhqoxKgR168RlERAl0KAJ0Yf2pBemdWub4Zh3gc9Vg3H0bRrgCfb73m iD8DBQFMH19xoxKgR168RlERAt6AAJ96RqboVXAAENNG3NTV/9NSo4zbEgCghTxO
7DvKYVz1pMDVAwrfS/3PYDY= GuKlnNnL+XTagpq68iLKUhk=
=DvU8 =R4ik
-----END PGP SIGNATURE----- -----END PGP SIGNATURE-----

View File

@@ -1 +1 @@
822a0283c6bc1c75001f3d1c528a4ff89c6b039c ee0acef1faffd480fd2136f81fb2b6f6a17b5388

View File

@@ -4604,6 +4604,12 @@ static int unixAccess(
assert(!"Invalid flags argument"); assert(!"Invalid flags argument");
} }
*pResOut = (access(zPath, amode)==0); *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; return SQLITE_OK;
} }

View File

@@ -1151,7 +1151,7 @@ static int winSectorSize(sqlite3_file *id){
*/ */
static int winDeviceCharacteristics(sqlite3_file *id){ static int winDeviceCharacteristics(sqlite3_file *id){
UNUSED_PARAMETER(id); UNUSED_PARAMETER(id);
return 0; return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
} }
/**************************************************************************** /****************************************************************************

View File

@@ -1219,12 +1219,24 @@ static int pagerUseWal(Pager *pPager){
static void pager_unlock(Pager *pPager){ static void pager_unlock(Pager *pPager){
if( !pPager->exclusiveMode ){ if( !pPager->exclusiveMode ){
int rc = SQLITE_OK; /* Return code */ 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. /* Always close the journal file when dropping the database lock.
** Otherwise, another connection with journal_mode=delete might ** Otherwise, another connection with journal_mode=delete might
** delete the file out from under us. ** delete the file out from under us.
*/ */
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); sqlite3OsClose(pPager->jfd);
}
sqlite3BitvecDestroy(pPager->pInJournal); sqlite3BitvecDestroy(pPager->pInJournal);
pPager->pInJournal = 0; pPager->pInJournal = 0;
releaseAllSavepoints(pPager); releaseAllSavepoints(pPager);
@@ -3115,6 +3127,7 @@ int sqlite3PagerClose(Pager *pPager){
enable_simulated_io_errors(); enable_simulated_io_errors();
PAGERTRACE(("CLOSE %d\n", PAGERID(pPager))); PAGERTRACE(("CLOSE %d\n", PAGERID(pPager)));
IOTRACE(("CLOSE %p\n", pPager)) IOTRACE(("CLOSE %p\n", pPager))
sqlite3OsClose(pPager->jfd);
sqlite3OsClose(pPager->fd); sqlite3OsClose(pPager->fd);
sqlite3PageFree(pTmp); sqlite3PageFree(pTmp);
sqlite3PcacheClose(pPager->pPCache); sqlite3PcacheClose(pPager->pPCache);
@@ -3908,17 +3921,22 @@ int sqlite3PagerOpen(
*/ */
static int hasHotJournal(Pager *pPager, int *pExists){ static int hasHotJournal(Pager *pPager, int *pExists){
sqlite3_vfs * const pVfs = pPager->pVfs; sqlite3_vfs * const pVfs = pPager->pVfs;
int rc; /* Return code */ int rc = SQLITE_OK; /* Return code */
int exists; /* True if a journal file is present */ int exists = 1; /* True if a journal file is present */
int jrnlOpen = !!isOpen(pPager->jfd);
assert( pPager!=0 ); assert( pPager!=0 );
assert( pPager->useJournal ); assert( pPager->useJournal );
assert( isOpen(pPager->fd) ); assert( isOpen(pPager->fd) );
assert( !isOpen(pPager->jfd) );
assert( pPager->state <= PAGER_SHARED ); assert( pPager->state <= PAGER_SHARED );
assert( jrnlOpen==0 || ( sqlite3OsDeviceCharacteristics(pPager->jfd) &
SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
));
*pExists = 0; *pExists = 0;
if( !jrnlOpen ){
rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists);
}
if( rc==SQLITE_OK && exists ){ if( rc==SQLITE_OK && exists ){
int locked; /* True if some process holds a RESERVED lock */ 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, ** If there is, then we consider this journal to be hot. If not,
** it can be ignored. ** it can be ignored.
*/ */
if( !jrnlOpen ){
int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL; int f = SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL;
rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f); rc = sqlite3OsOpen(pVfs, pPager->zJournal, pPager->jfd, f, &f);
}
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
u8 first = 0; u8 first = 0;
rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0); rc = sqlite3OsRead(pPager->jfd, (void *)&first, 1, 0);
if( rc==SQLITE_IOERR_SHORT_READ ){ if( rc==SQLITE_IOERR_SHORT_READ ){
rc = SQLITE_OK; rc = SQLITE_OK;
} }
if( !jrnlOpen ){
sqlite3OsClose(pPager->jfd); sqlite3OsClose(pPager->jfd);
}
*pExists = (first!=0); *pExists = (first!=0);
}else if( rc==SQLITE_CANTOPEN ){ }else if( rc==SQLITE_CANTOPEN ){
/* If we cannot open the rollback journal file in order to see if /* If we cannot open the rollback journal file in order to see if

View File

@@ -508,6 +508,7 @@ int sqlite3_exec(
#define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_ATOMIC64K 0x00000100
#define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200
#define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400
#define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800
/* /*
** CAPI3REF: File Locking Levels ** CAPI3REF: File Locking Levels

View File

@@ -58,6 +58,13 @@ struct Testvfs {
int iIoerrCnt; int iIoerrCnt;
int ioerr; int ioerr;
int nIoerrFail; 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_OPEN_MASK 0x00000100
#define TESTVFS_SYNC_MASK 0x00000200 #define TESTVFS_SYNC_MASK 0x00000200
#define TESTVFS_DELETE_MASK 0x00000400 #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 #define TESTVFS_MAX_PAGES 256
@@ -187,6 +197,30 @@ static int tvfsResultCode(Testvfs *p, int *pRc){
return 0; 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( static void tvfsExecTcl(
Testvfs *p, Testvfs *p,
@@ -245,15 +279,23 @@ static void tvfsExecTcl(
** Close an tvfs-file. ** Close an tvfs-file.
*/ */
static int tvfsClose(sqlite3_file *pFile){ static int tvfsClose(sqlite3_file *pFile){
TestvfsFile *p = (TestvfsFile *)pFile; TestvfsFile *pFd = (TestvfsFile *)pFile;
if( p->pShmId ){ Testvfs *p = (Testvfs *)pFd->pVfs->pAppData;
Tcl_DecrRefCount(p->pShmId);
p->pShmId = 0; 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 ){ if( pFile->pMethods ){
ckfree((char *)pFile->pMethods); ckfree((char *)pFile->pMethods);
} }
return sqlite3OsClose(p->pReal); return sqlite3OsClose(pFd->pReal);
} }
/* /*
@@ -278,16 +320,44 @@ static int tvfsWrite(
int iAmt, int iAmt,
sqlite_int64 iOfst sqlite_int64 iOfst
){ ){
TestvfsFile *p = (TestvfsFile *)pFile; int rc = SQLITE_OK;
return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); 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. ** Truncate an tvfs-file.
*/ */
static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){
TestvfsFile *p = (TestvfsFile *)pFile; int rc = SQLITE_OK;
return sqlite3OsTruncate(p->pReal, size); 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); tvfsResultCode(p, &rc);
} }
if( rc==SQLITE_OK && tvfsInjectFullerr(p) ) rc = SQLITE_FULL;
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3OsSync(pFd->pReal, flags); 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. ** Return the sector-size in bytes for an tvfs-file.
*/ */
static int tvfsSectorSize(sqlite3_file *pFile){ static int tvfsSectorSize(sqlite3_file *pFile){
TestvfsFile *p = (TestvfsFile *)pFile; TestvfsFile *pFd = (TestvfsFile *)pFile;
return sqlite3OsSectorSize(p->pReal); 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. ** Return the device characteristic flags supported by an tvfs-file.
*/ */
static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ static int tvfsDeviceCharacteristics(sqlite3_file *pFile){
TestvfsFile *p = (TestvfsFile *)pFile; TestvfsFile *pFd = (TestvfsFile *)pFile;
return sqlite3OsDeviceCharacteristics(p->pReal); 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); 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( static int tvfsShmOpen(
sqlite3_file *pFileDes sqlite3_file *pFileDes
){ ){
@@ -782,25 +850,38 @@ static int testvfs_obj_cmd(
){ ){
Testvfs *p = (Testvfs *)cd; Testvfs *p = (Testvfs *)cd;
static const char *CMD_strs[] = {
"shm", "delete", "filter", "ioerr", "script", 0
};
enum DB_enum { 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; int i;
if( objc<2 ){ if( objc<2 ){
Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ..."); Tcl_WrongNumArgs(interp, 1, objv, "SUBCOMMAND ...");
return TCL_ERROR; 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; return TCL_ERROR;
} }
Tcl_ResetResult(interp); Tcl_ResetResult(interp);
switch( (enum DB_enum)i ){ switch( aSubcmd[i].eCmd ){
case CMD_SHM: { case CMD_SHM: {
Tcl_Obj *pObj; Tcl_Obj *pObj;
int i; int i;
@@ -857,7 +938,10 @@ static int testvfs_obj_cmd(
{ "xShmMap", TESTVFS_SHMMAP_MASK }, { "xShmMap", TESTVFS_SHMMAP_MASK },
{ "xSync", TESTVFS_SYNC_MASK }, { "xSync", TESTVFS_SYNC_MASK },
{ "xDelete", TESTVFS_DELETE_MASK }, { "xDelete", TESTVFS_DELETE_MASK },
{ "xWrite", TESTVFS_WRITE_MASK },
{ "xTruncate", TESTVFS_TRUNCATE_MASK },
{ "xOpen", TESTVFS_OPEN_MASK }, { "xOpen", TESTVFS_OPEN_MASK },
{ "xClose", TESTVFS_CLOSE_MASK },
}; };
Tcl_Obj **apElem = 0; Tcl_Obj **apElem = 0;
int nElem = 0; int nElem = 0;
@@ -915,6 +999,34 @@ static int testvfs_obj_cmd(
break; break;
} }
/*
** 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? ** TESTVFS ioerr ?IFAIL PERSIST?
** **
@@ -948,6 +1060,89 @@ static int testvfs_obj_cmd(
Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
break; 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; return TCL_OK;
@@ -1067,6 +1262,8 @@ static int testvfs_cmd(
nByte = sizeof(Testvfs) + strlen(zVfs)+1; nByte = sizeof(Testvfs) + strlen(zVfs)+1;
p = (Testvfs *)ckalloc(nByte); p = (Testvfs *)ckalloc(nByte);
memset(p, 0, nByte); memset(p, 0, nByte);
p->iDevchar = -1;
p->iSectorsize = -1;
/* Create the new object command before querying SQLite for a default VFS /* 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 ** to use for 'real' IO operations. This is because creating the new VFS

View File

@@ -5248,8 +5248,6 @@ case OP_JournalMode: { /* out2-prerelease */
rc = sqlite3PagerCloseWal(pPager); rc = sqlite3PagerCloseWal(pPager);
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
sqlite3PagerSetJournalMode(pPager, eNew); 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 ); assert( sqlite3BtreeIsInTrans(pBt)==0 );
if( rc==SQLITE_OK ){ if( rc==SQLITE_OK ){
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1)); 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 */ #endif /* ifndef SQLITE_OMIT_WAL */
if( rc ){
if( rc==SQLITE_BUSY && pOp->p5!=0 ) rc = SQLITE_OK;
eNew = eOld;
}
eNew = sqlite3PagerSetJournalMode(pPager, eNew); eNew = sqlite3PagerSetJournalMode(pPager, eNew);
pOut = &aMem[pOp->p2]; pOut = &aMem[pOp->p2];

View File

@@ -191,10 +191,14 @@ int sqlite3_blob_open(
sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeUsesBtree(v, iDb);
/* Configure the OP_TableLock instruction */ /* Configure the OP_TableLock instruction */
#ifdef SQLITE_OMIT_SHARED_CACHE
sqlite3VdbeChangeToNoop(v, 2, 1);
#else
sqlite3VdbeChangeP1(v, 2, iDb); sqlite3VdbeChangeP1(v, 2, iDb);
sqlite3VdbeChangeP2(v, 2, pTab->tnum); sqlite3VdbeChangeP2(v, 2, pTab->tnum);
sqlite3VdbeChangeP3(v, 2, flags); sqlite3VdbeChangeP3(v, 2, flags);
sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT);
#endif
/* Remove either the OP_OpenWrite or OpenRead. Set the P2 /* Remove either the OP_OpenWrite or OpenRead. Set the P2
** parameter of the other to pTab->tnum. */ ** parameter of the other to pTab->tnum. */

228
test/journal2.test Normal file
View File

@@ -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

View File

@@ -9,7 +9,6 @@
# #
#*********************************************************************** #***********************************************************************
# #
# $Id: jrnlmode2.test,v 1.6 2009/06/05 17:09:12 drh Exp $
set testdir [file dirname $argv0] set testdir [file dirname $argv0]
source $testdir/tester.tcl 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-1.*: Demonstrate bug #3745:
# jrnlmode2-2.*: Demonstrate bug #3751 #
# 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 { do_test jrnlmode2-1.1 {
@@ -46,6 +68,8 @@ do_test jrnlmode2-1.3 {
do_test jrnlmode2-1.4 { do_test jrnlmode2-1.4 {
execsql { execsql {
INSERT INTO t1 VALUES(3, 4); INSERT INTO t1 VALUES(3, 4);
}
execsql {
BEGIN; BEGIN;
SELECT * FROM t1; SELECT * FROM t1;
} }
@@ -87,9 +111,9 @@ do_test jrnlmode2-2.4 {
} {0 {1 2 3 4 5 6}} } {0 {1 2 3 4 5 6}}
do_test jrnlmode2-2.5 { do_test jrnlmode2-2.5 {
db close
file delete test.db-journal file delete test.db-journal
} {} } {}
do_test jrnlmode2-2.6 { do_test jrnlmode2-2.6 {
sqlite3 db2 test.db -readonly 1 sqlite3 db2 test.db -readonly 1
catchsql { SELECT * FROM t1 } db2 catchsql { SELECT * FROM t1 } db2

View File

@@ -49,6 +49,16 @@ set FAULTSIM(ioerr-persistent) [list \
-injecterrlist {{1 {disk I/O error}}} \ -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: # Transient and persistent SHM errors:
# #
set FAULTSIM(shmerr-transient) [list \ set FAULTSIM(shmerr-transient) [list \
@@ -126,14 +136,14 @@ proc faultsim_save_and_close {} {
catch { db close } catch { db close }
return "" return ""
} }
proc faultsim_restore_and_reopen {} { proc faultsim_restore_and_reopen {{dbfile test.db}} {
catch { db close } catch { db close }
foreach f [glob -nocomplain test.db*] { file delete -force $f } foreach f [glob -nocomplain test.db*] { file delete -force $f }
foreach f2 [glob -nocomplain sv_test.db*] { foreach f2 [glob -nocomplain sv_test.db*] {
set f [string range $f2 3 end] set f [string range $f2 3 end]
file copy -force $f2 $f file copy -force $f2 $f
} }
sqlite3 db test.db sqlite3 db $dbfile
sqlite3_extended_result_codes db 1 sqlite3_extended_result_codes db 1
sqlite3_db_config_lookaside db 0 0 0 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}} { proc faultsim_delete_and_reopen {{file test.db}} {
catch { db close } catch { db close }
foreach f [glob -nocomplain test.db*] { file delete -force $f } 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 return $sv
} }
# The following procs are used as [do_one_faultsim_test] callbacks when # The following procs are used as [do_one_faultsim_test] callbacks when
# injecting shared-memory related error faults into test cases. # injecting shared-memory related error faults into test cases.
# #
@@ -195,6 +206,22 @@ proc shmerr_injectstop {} {
shmfault ioerr 0 0 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 # This command is not called directly. It is used by the
# [faultsim_test_result] command created by [do_faultsim_test] and used # [faultsim_test_result] command created by [do_faultsim_test] and used
# by -test scripts. # by -test scripts.

View File

@@ -14,6 +14,7 @@ set testdir [file dirname $argv0]
source $testdir/tester.tcl source $testdir/tester.tcl
source $testdir/lock_common.tcl source $testdir/lock_common.tcl
source $testdir/malloc_common.tcl source $testdir/malloc_common.tcl
source $testdir/wal_common.tcl
# #
# pager1-1.*: Test inter-process locking (clients in multiple processes). # pager1-1.*: Test inter-process locking (clients in multiple processes).
@@ -22,13 +23,14 @@ source $testdir/malloc_common.tcl
# #
# pager1-3.*: Savepoint related tests. # pager1-3.*: Savepoint related tests.
# #
# pager1-4.*: Hot-journal related tests.
proc do_execsql_test {testname sql result} { #
uplevel do_test $testname [list "execsql {$sql}"] [list $result] # pager1-5.*: Cases related to multi-file commits.
} #
proc do_catchsql_test {testname sql result} { # pager1-6.*: Cases related to "PRAGMA max_page_count"
uplevel do_test $testname [list "catchsql {$sql}"] [list $result] #
} # pager1-7.*: Cases specific to "PRAGMA journal_mode=TRUNCATE"
#
set a_string_counter 1 set a_string_counter 1
proc a_string {n} { proc a_string {n} {
@@ -222,6 +224,17 @@ do_execsql_test pager1-3.6 { COMMIT } {}
# xAccess() call to check for the presence of any master # xAccess() call to check for the presence of any master
# journal file is made). # 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 { do_test pager1.4.1.1 {
faultsim_delete_and_reopen faultsim_delete_and_reopen
execsql { execsql {
@@ -308,5 +321,426 @@ do_test pager1.4.2.5 {
} }
} {4 ok} } {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 finish_test

117
test/pager2.test Normal file
View File

@@ -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

View File

@@ -54,11 +54,56 @@ 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 # Test fault-injection while rolling back hot-journals that were created
# as part of a multi-file transaction. # as part of a multi-file transaction.
# #
do_test pagerfault-2-pre1 { do_test pagerfault-3-pre1 {
testvfs tstvfs -default 1 testvfs tstvfs -default 1
tstvfs filter xDelete tstvfs filter xDelete
tstvfs script xDeleteCallback tstvfs script xDeleteCallback
@@ -96,7 +141,7 @@ do_test pagerfault-2-pre1 {
db close db close
tstvfs delete tstvfs delete
} {} } {}
do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep { do_faultsim_test pagerfault-3 -faults ioerr-persistent -prep {
faultsim_restore_and_reopen faultsim_restore_and_reopen
} -body { } -body {
execsql { execsql {
@@ -107,7 +152,6 @@ do_faultsim_test pagerfault-2 -faults ioerr-persistent -prep {
} -test { } -test {
faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}} faultsim_test_result {0 {4 4}} {1 {unable to open database: test.db2}}
faultsim_integrity_check faultsim_integrity_check
catchsql { ATTACH 'test.db2' AS aux } catchsql { ATTACH 'test.db2' AS aux }
if {[db one { SELECT count(*) FROM t1 }] != 4 if {[db one { SELECT count(*) FROM t1 }] != 4
|| [db one { SELECT count(*) FROM t2 }] != 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 finish_test

View File

@@ -169,7 +169,9 @@ test_suite "coverage-pager" -description {
Coverage tests for file pager.c. Coverage tests for file pager.c.
} -files { } -files {
pager1.test pager1.test
pager2.test
pagerfault.test pagerfault.test
journal2.test
} }

View File

@@ -49,6 +49,8 @@
# crashsql ARGS... # crashsql ARGS...
# integrity_check TESTNAME ?DB? # integrity_check TESTNAME ?DB?
# do_test TESTNAME SCRIPT EXPECTED # 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: # Commands providing a lower level interface to the global test counters:
# #
@@ -317,6 +319,14 @@ proc do_test {name cmd expected} {
flush stdout 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. # Run an SQL script.
# Return the number of microseconds per statement. # Return the number of microseconds per statement.
# #

View File

@@ -124,46 +124,48 @@ do_test walmode-4.5 {
# from WAL to rollback mode because a second connection has the database # from WAL to rollback mode because a second connection has the database
# open. Or from rollback to WAL. # open. Or from rollback to WAL.
# #
do_test walmode-4.1 { do_test walmode-4.6 {
sqlite3 db2 test.db sqlite3 db2 test.db
execsql { PRAGMA main.journal_mode } db2 execsql { PRAGMA main.journal_mode } db2
} {delete} } {delete}
do_test walmode-4.2 { do_test walmode-4.7 {
execsql { PRAGMA main.journal_mode = wal } db execsql { PRAGMA main.journal_mode = wal } db
} {wal} } {wal}
do_test walmode-4.3 { do_test walmode-4.8 {
execsql { SELECT * FROM t1 } db2 execsql { SELECT * FROM t1 } db2
} {1 2} } {1 2}
do_test walmode-4.4 { do_test walmode-4.9 {
catchsql { PRAGMA journal_mode = delete } db catchsql { PRAGMA journal_mode = delete } db
} {1 {database is locked}} } {1 {database is locked}}
do_test walmode-4.5 { do_test walmode-4.10 {
execsql { PRAGMA main.journal_mode } db execsql { PRAGMA main.journal_mode } db
} {wal} } {wal}
do_test walmode-4.6 {
do_test walmode-4.11 {
db2 close db2 close
execsql { PRAGMA journal_mode = delete } db execsql { PRAGMA journal_mode = delete } db
} {delete} } {delete}
do_test walmode-4.7 { do_test walmode-4.12 {
execsql { PRAGMA main.journal_mode } db execsql { PRAGMA main.journal_mode } db
} {delete} } {delete}
do_test walmode-4.8 { do_test walmode-4.13 {
list [file exists test.db-journal] [file exists test.db-wal] list [file exists test.db-journal] [file exists test.db-wal]
} {0 0} } {0 0}
do_test walmode-4.9 { do_test walmode-4.14 {
sqlite3 db2 test.db sqlite3 db2 test.db
execsql { execsql {
BEGIN; BEGIN;
SELECT * FROM t1; SELECT * FROM t1;
} db2 } db2
} {1 2} } {1 2}
do_test walmode-4.11 {
execsql { PRAGMA main.journal_mode } db do_test walmode-4.16 { execsql { PRAGMA main.journal_mode } db } {delete}
} {delete} do_test walmode-4.17 { execsql { PRAGMA main.journal_mode } db2 } {delete}
do_test walmode-4.10 {
do_test walmode-4.17 {
catchsql { PRAGMA main.journal_mode = wal } db catchsql { PRAGMA main.journal_mode = wal } db
} {1 {database is locked}} } {1 {database is locked}}
do_test walmode-4.11 { do_test walmode-4.18 {
execsql { PRAGMA main.journal_mode } db execsql { PRAGMA main.journal_mode } db
} {delete} } {delete}
catch { db close } catch { db close }
@@ -180,7 +182,6 @@ do_test walmode-5.1.1 {
sqlite3 db :memory: sqlite3 db :memory:
execsql { PRAGMA main.journal_mode } execsql { PRAGMA main.journal_mode }
} {memory} } {memory}
breakpoint
do_test walmode-5.1.2 { do_test walmode-5.1.2 {
execsql { PRAGMA main.journal_mode = wal } execsql { PRAGMA main.journal_mode = wal }
} {memory} } {memory}