Merge the write-ahead-logging changes into the trunk.

FossilOrigin-Name: de9ae443cc8eebfb0692dcdac93bde9e4c54199a
This commit is contained in:
drh
2010-05-03 14:08:27 +00:00
57 changed files with 6614 additions and 659 deletions
+5
View File
@@ -276,6 +276,8 @@ SRC = \
$(TOP)/src/vdbetrace.c \
$(TOP)/src/vdbeInt.h \
$(TOP)/src/vtab.c \
$(TOP)/src/wal.c \
$(TOP)/src/wal.h \
$(TOP)/src/walker.c \
$(TOP)/src/where.c
@@ -742,6 +744,9 @@ vdbetrace.lo: $(TOP)/src/vdbetrace.c $(HDR)
vtab.lo: $(TOP)/src/vtab.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/vtab.c
wal.lo: $(TOP)/src/wal.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/wal.c
walker.lo: $(TOP)/src/walker.c $(HDR)
$(LTCOMPILE) $(TEMP_STORE) -c $(TOP)/src/walker.c
+1 -1
View File
@@ -1 +1 @@
3.6.23
3.7.0
+5 -3
View File
@@ -66,7 +66,7 @@ LIBOBJ+= alter.o analyze.o attach.o auth.o \
table.o tokenize.o trigger.o \
update.o util.o vacuum.o \
vdbe.o vdbeapi.o vdbeaux.o vdbeblob.o vdbemem.o vdbetrace.o \
walker.o where.o utf.o vtab.o
wal.o walker.o where.o utf.o vtab.o
@@ -158,6 +158,8 @@ SRC = \
$(TOP)/src/vdbetrace.c \
$(TOP)/src/vdbeInt.h \
$(TOP)/src/vtab.c \
$(TOP)/src/wal.c \
$(TOP)/src/wal.h \
$(TOP)/src/walker.c \
$(TOP)/src/where.c
@@ -255,8 +257,8 @@ TESTSRC = \
TESTSRC2 = \
$(TOP)/src/attach.c $(TOP)/src/backup.c $(TOP)/src/btree.c \
$(TOP)/src/build.c $(TOP)/src/date.c \
$(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/mem5.c \
$(TOP)/src/os.c \
$(TOP)/src/expr.c $(TOP)/src/func.c $(TOP)/src/insert.c $(TOP)/src/wal.c \
$(TOP)/src/mem5.c $(TOP)/src/os.c \
$(TOP)/src/os_os2.c $(TOP)/src/os_unix.c $(TOP)/src/os_win.c \
$(TOP)/src/pager.c $(TOP)/src/pragma.c $(TOP)/src/prepare.c \
$(TOP)/src/printf.c $(TOP)/src/random.c $(TOP)/src/pcache.c \
+63 -53
View File
@@ -1,14 +1,14 @@
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
C Avoid\sassertion\sfaults\sin\squeries\susing\sindices\swith\sredundant\scolumns.\nTicket\s[3dbdcdb14e7f41].
D 2010-04-30T21:03:24
C Merge\sthe\swrite-ahead-logging\schanges\sinto\sthe\strunk.
D 2010-05-03T14:08:28
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
F Makefile.in 4f2f967b7e58a35bb74fb7ec8ae90e0f4ca7868b
F Makefile.in d83a0ffef3dcbfb08b410a6c6dd6c009ec9167fb
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
F Makefile.vxworks ab005d301296c40e021ccd0133ce49ca811e319f
F README cd04a36fbc7ea56932a4052d7d0b7f09f27c33d6
F VERSION 3e18b3dd7290883913c25d66c0ddb409ce318654
F VERSION 4dce4379514b12d6bc5c30f7d1f64582ccb4f412
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F addopcodes.awk 17dc593f791f874d2c23a0f9360850ded0286531
F art/2005osaward.gif 0d1851b2a7c1c9d0ccce545f3e14bca42d7fd248
@@ -92,7 +92,7 @@ F ext/rtree/tkt3363.test 2bf324f7908084a5f463de3109db9c6e607feb1b
F ext/rtree/viewrtree.tcl eea6224b3553599ae665b239bd827e182b466024
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F main.mk d286b99eb87db41cfc5394e346604ef49509867d
F main.mk b681194d6bf557b80ca363306e9c172f81f15b1d
F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac
@@ -112,9 +112,9 @@ F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
F src/backup.c e86634da8c48357a759694c9c7c471125cd8d5a8
F src/bitvec.c 06ad2c36a9c3819c0b9cbffec7b15f58d5d834e0
F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
F src/btree.c 01559397cbd4a5aa62a822e8ca9ac94b6db14743
F src/btree.h ad6cff92286f9b02ec32f0b97136e9a544249f37
F src/btreeInt.h 22447d259639271774a931cbf66aa55112846681
F src/btree.c d0414a5f09b0cacb64bd60b91c5a3720585925aa
F src/btree.h dd83041eda10c17daf023257c1fc883b5f71f85a
F src/btreeInt.h b0c87f6725b06a0aa194a6d25d54b16ce9d6e291
F src/build.c 11100b66fb97638d2d874c1d34d8db90650bb1d7
F src/callback.c 01843bdf4b0420fd28046525d150fcd9802931a9
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
@@ -134,7 +134,7 @@ F src/journal.c b0ea6b70b532961118ab70301c00a33089f9315c
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581
F src/main.c c0e7192bad5b90544508b241eb2487ac661de890
F src/main.c 6bc746f6016e0a3e6a7089078d9feffc0aa24a5e
F src/malloc.c a08f16d134f0bfab6b20c3cd142ebf3e58235a6a
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2
@@ -142,7 +142,7 @@ F src/mem2.c 2ee7bdacda8299b5a91cff9f7ee3e46573195c38
F src/mem3.c 9b237d911ba9904142a804be727cc6664873f8a3
F src/mem5.c eb7a5cb98915dd7a086fa415ce3a5a0f20d0acff
F src/memjournal.c f3be374af30588de297dcf678925b2a4758e4135
F src/mutex.c 581a272e09098040ca3ef543cb5f3d643eff7d50
F src/mutex.c c04412513619b3a763f3484bb412336fa3a3fbd9
F src/mutex.h 6fde601e55fa6c3fae768783c439797ab84c87c6
F src/mutex_noop.c 5f58eaa31f2d742cb8957a747f7887ae98f16053
F src/mutex_os2.c 20477db50cf3817c2f1cd3eb61e5c177e50231db
@@ -151,17 +151,17 @@ F src/mutex_w32.c 4cc201c1bfd11d1562810554ff5500e735559d7e
F src/notify.c cbfa66a836da3a51567209636e6a94059c137930
F src/os.c 8bc63cf91e9802e2b807198e54e50227fa889306
F src/os.h 534b082c3cb349ad05fa6fa0b06087e022af282c
F src/os_common.h 240c88b163b02c21a9f21f87d49678a0aa21ff30
F src/os_os2.c 75a8c7b9a00a2cf1a65f9fa4afbc27d46634bb2f
F src/os_unix.c 148d2f625db3727250c0b880481ae7630b6d0eb0
F src/os_win.c 1c7453c2df4dab26d90ff6f91272aea18bcf7053
F src/pager.c da5ed17bb729c27a16c45fe38e9531c240a1c6a4
F src/pager.h ef8a2cf10084f60ab45ee2dfded8bf8b0c655ddf
F src/os_common.h 0d6ee583b6ee3185eb9d951f890c6dd03021a08d
F src/os_os2.c 8ad77a418630d7dee91d1bb04f79c2096301d3a0
F src/os_unix.c 5c821e4eb04093102d901c22f40341bc1d30aef0
F src/os_win.c a8fc01d8483be472e495793c01064fd87e56a5c1
F src/pager.c e9c7fe979b32a3c5bf4216d8fbe1cf8beff8a1b8
F src/pager.h 934b598583a9d936bb13c37d62a2fe68ac48781c
F src/parse.y ace5c7a125d9f2a410e431ee3209034105045f7e
F src/pcache.c ace8f6a5ecd4711cc66a1b23053be7109bd437cf
F src/pcache.h c683390d50f856d4cd8e24342ae62027d1bb6050
F src/pcache1.c 6dc1871ce8ead9187161c370a58cd06c84221f76
F src/pragma.c 71ba42f5a6f1c91266ca065c2a55c168c7846d3a
F src/pragma.c 5fa66bc24bd4e842d3edb86a319223b86ba5493c
F src/prepare.c fd1398cb1da54385ba5bd68d93928f10d10a1d9c
F src/printf.c 5f5b65a83e63f2096a541a340722a509fa0240a7
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
@@ -169,19 +169,19 @@ F src/resolve.c ac5f1a713cd1ae77f08b83cc69581e11bf5ae6f9
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c c03d8a0565febcde8c6a12c5d77d065fddae889b
F src/shell.c c40427c7245535a04a9cb4a417b6cc05c022e6a4
F src/sqlite.h.in dc98616304e3e776008655671d81e3ad3028ada7
F src/sqlite.h.in 8097161ad63e2a00e26217da43327d396371c2e5
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
F src/sqliteInt.h 4303fdad2e96c56f510cc8e31a647b1b8c5a5254
F src/sqliteLimit.h 3afab2291762b5d09ae20c18feb8e9fa935a60a6
F src/sqliteInt.h e7f1368eb56d8a410e510bcffb9ca5795060c58c
F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
F src/status.c 4df6fe7dce2d256130b905847c6c60055882bdbe
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
F src/tclsqlite.c ebf7a964f8311bc52266cf75afedae98cd743df3
F src/test1.c 8b4c246c41e75c3ff033edb2e8c2cf15820861ae
F src/tclsqlite.c 4de81521174fedacd8393ea7b70b730ce17f8eae
F src/test1.c ff95ca772d1df51618f9f1ef7ea432cdf851f97b
F src/test2.c b6b43413d495addd039a88b87d65c839f86b18cb
F src/test3.c 4c21700c73a890a47fc685c1097bfb661346ac94
F src/test4.c ad03bb987ddedce928f4258c1e7fa4109a73497d
F src/test5.c cc55900118fa4add8ec9cf69fc4225a4662f76b1
F src/test6.c a8ece4284d0e34477f349ac05655db73c48e0926
F src/test6.c 49b39daf8e88ed6baab25c524ca5635304f6f315
F src/test7.c 3f2d63e4ccf97f8c2cf1a7fa0a3c8e2e2a354e6e
F src/test8.c f959db9a22d882013b64c92753fa793b2ce3bdea
F src/test9.c bea1e8cf52aa93695487badedd6e1886c321ea60
@@ -189,25 +189,25 @@ F src/test_async.c c1656facbaf43cb2e71b62621e5b9eb080e2621c
F src/test_autoext.c 30e7bd98ab6d70a62bb9ba572e4c7df347fe645e
F src/test_backup.c c129c91127e9b46e335715ae2e75756e25ba27de
F src/test_btree.c 47cd771250f09cdc6e12dda5bc71bc0b3abc96e2
F src/test_config.c 8c408fbffbe8082d1d3dc55044ddfd5580f3c9b9
F src/test_demovfs.c 301a047287021940136a3a52d2366dc51f8072d5
F src/test_devsym.c 3adf69c716e5c90bf5b7a1afe817e3ecac1adf6f
F src/test_config.c 6210f501d358bde619ae761f06f123529c6ba24f
F src/test_demovfs.c da81a5f7785bb352bda7911c332a983ec4f17f27
F src/test_devsym.c 3d324248b80f31b3b29ab9bca39c4d856a2c9160
F src/test_func.c 13b582345fb1185a93e46c53310fae8547dcce20
F src/test_hexio.c 1237f000ec7a491009b1233f5c626ea71bce1ea2
F src/test_init.c 5d624ffd0409d424cf9adbfe1f056b200270077c
F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99
F src/test_intarray.h 489edb9068bb926583445cb02589344961054207
F src/test_journal.c adc0ce3840ed19b49feb1d583b2212f560ef7866
F src/test_journal.c 0537fcf9d4ecb394ec8b27d887a004ecaab85f16
F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e
F src/test_malloc.c 2842c922b8e8d992aba722214952204ca025b411
F src/test_mutex.c ce06b59aca168cd8c520b77159a24352a7469bd3
F src/test_onefile.c 06da7e085dce42924cf062b91763dd4bb84c6101
F src/test_osinst.c 90fb03d396f39956897dfb4bd0e62c6711db1cca
F src/test_onefile.c d9585f6e2056868f208b0c21378a05b68c9ceae2
F src/test_osinst.c f5d1a4ee8b80fc58d1430c56146de748584013a9
F src/test_pcache.c 7bf828972ac0d2403f5cfa4cd14da41f8ebe73d8
F src/test_schema.c 8c06ef9ddb240c7a0fcd31bc221a6a2aade58bf0
F src/test_server.c bbba05c144b5fc4b52ff650a4328027b3fa5fcc6
F src/test_tclvar.c f4dc67d5f780707210d6bb0eb6016a431c04c7fa
F src/test_thread.c 00fed80690ae7f1525483a35861511c48bc579f2
F src/test_thread.c aa9919c885a1fe53eafc73492f0898ee6c0a0726
F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
F src/tokenize.c 25ceb0f0a746ea1d0f9553787f3f0a56853cfaeb
F src/trigger.c 8927588cb9e6d47f933b53bfe74200fbb504100d
@@ -215,15 +215,17 @@ F src/update.c c0dc6b75ad28b76b619042d934f337b02acee208
F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685
F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b
F src/vacuum.c 8e7d9a6fd076774bb6e211cad335c7337948a164
F src/vdbe.c 2abd931ea2aec3eacc6426677f40cc5a1071d34e
F src/vdbe.c 8be37a1b18786b5c026adcb2e9edc93e3a940885
F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3
F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1
F src/vdbeapi.c 11bcc381e81e797fcf3e81fa6a14ec16a04801cc
F src/vdbeapi.c cf1ff7cbcbd44d2082af71b98762407ae01ce009
F src/vdbeaux.c 395d21a1617553ea9b9f4abe0d302449d8549e1d
F src/vdbeblob.c 5327132a42a91e8b7acfb60b9d2c3b1c5c863e0e
F src/vdbemem.c 2a82f455f6ca6f78b59fb312f96054c04ae0ead1
F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2
F src/vtab.c a0f8a40274e4261696ef57aa806de2776ab72cda
F src/wal.c dadd533e0103883f72bf541eaf1ef34adb4e3c99
F src/wal.h d6d4a6809151e30bed5b01dd05cf27858f5a7bc8
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
F src/where.c 75fee9e255b62f773fcadd1d1f25b6f63ac7a356
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
@@ -253,8 +255,8 @@ F test/autoinc.test 85ef3180a737e6580086a018c09c6f1a52759b46
F test/autoindex1.test ffb06a246e2c1f89cfbe3d93eca513c9e78d4063
F test/autovacuum.test 25f891bc343a8bf5d9229e2e9ddab9f31a9ab5ec
F test/autovacuum_ioerr2.test 598b0663074d3673a9c1bc9a16e80971313bafe6
F test/avtrans.test 1e901d8102706b63534dbd2bdd4d8f16c4082650
F test/backup.test 3549ea8f541a08205c0eb813b21e81ea8301f6ed
F test/avtrans.test 7a26ffc6a355b7fbff2a082f56180ede863b126c
F test/backup.test 5f7f66d067e695a6040be3295a5abc289f823838
F test/backup2.test 159419073d9769fdb1780ed7e5b391a046f898d5
F test/backup_ioerr.test 1f012e692f42c0442ae652443258f70e9f20fa38
F test/backup_malloc.test 1e063c6d75143d0d6e0ae77971dd690070369387
@@ -306,7 +308,7 @@ F test/corrupt6.test e69b877d478224deab7b66844566258cecacd25e
F test/corrupt7.test 1eb2214f29474fa6b155aa3da8a7d46bf52089e1
F test/corrupt8.test 9992ef7f67cefc576b92373f6bf5ab8775280f51
F test/corrupt9.test 4aa1cb1ef091cb0e13e89a819c72911631b5176a
F test/corruptA.test 99e95620b980161cb3e79f06a884a4bb8ae265ff
F test/corruptA.test 38b4f81c16099f6d8fa8b37e188fde76b8243994
F test/corruptB.test d88b500f7994cb2eb9646410c263111e9831f889
F test/corruptC.test 691ed070baef5e1345939caadf270a52837a5064
F test/corruptD.test 3ae6e2dc6e2226c6935a8a40d4b5ee3eba75f8c0
@@ -474,12 +476,13 @@ F test/limit.test 2db7b3b34fb925b8e847d583d2eb67531d0ce67e
F test/loadext.test 0393ce12d9616aa87597dd0ec88181de181f6db0
F test/loadext2.test 0bcaeb4d81cd5b6e883fdfea3c1bdbe1f173cbca
F test/lock.test 842e80b6be816c79525a20b098cca066989feed7
F test/lock2.test 7bb642551df59b3de135291d62ee82409420181e
F test/lock2.test 64ba8f1c7709abf91287d564984a6b8c5209e755
F test/lock3.test f271375930711ae044080f4fe6d6eda930870d00
F test/lock4.test f4f36271aa5ae1da449646bf43c7341f6b2b4c4e
F test/lock5.test 6b1f78f09ad1522843dad571b76b321e6f439bf7
F test/lock6.test 862aa71e97b288d6b3f92ba3313f51bd0b003776
F test/lock7.test 64006c84c1c616657e237c7ad6532b765611cf64
F test/lock_common.tcl bbc4e15ee5334cc4d01fcac08d7c9de7d8906e55
F test/lookaside.test 1dd350dc6dff015c47c07fcc5a727a72fc5bae02
F test/main.test 2be2352ac77ac5b238c6337a5469aeeef57677e6
F test/make-where7.tcl 05c16b5d4f5d6512881dfec560cb793915932ef9
@@ -532,28 +535,28 @@ F test/pageropt.test 3ee6578891baaca967f0bd349e4abfa736229e1a
F test/pagesize.test 76aa9f23ecb0741a4ed9d2e16c5fa82671f28efb
F test/pcache.test eebc4420b37cb07733ae9b6e99c9da7c40dd6d58
F test/pcache2.test 0d85f2ab6963aee28c671d4c71bec038c00a1d16
F test/permutations.test 6117bae41935a787cc92faee81e91f813580eccb
F test/permutations.test 7d9064978f0ca90d0ec827a7ada9e8e2615b1143
F test/pragma.test 6960f9efbce476f70ba9ee2171daf5042f9e3d8a
F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47
F test/printf.test 05970cde31b1a9f54bd75af60597be75a5c54fea
F test/progress.test 5b075c3c790c7b2a61419bc199db87aaf48b8301
F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
F test/quick.test d6591e74f3ac19da7fd076845f06dca48fd43cff
F test/quick.test 6f202befe1cfae0b63df96b3120a8022ab11f574
F test/quote.test 215897dbe8de1a6f701265836d6601cc6ed103e6
F test/randexpr1.tcl 40dec52119ed3a2b8b2a773bce24b63a3a746459
F test/randexpr1.test 1084050991e9ba22c1c10edd8d84673b501cc25a
F test/rdonly.test bd054831f8a3078e765a0657e247182486f0cb47
F test/rdonly.test c243623b26c311faab2aee311a26af372e123a65
F test/reindex.test 44edd3966b474468b823d481eafef0c305022254
F test/rollback.test 73355ad4492ff9a3a31e61c7e5eb5e01a1de94ca
F test/rowhash.test 97f56043ba11f0679920416c0cdbc72e5272267b
F test/rowid.test e58e0acef38b527ed1b0b70d3ada588f804af287
F test/rtree.test 55466a200af3591946c5da77ad5dbfbc1e5e05f9
F test/savepoint.test f2ede4b643ad87ead36c041c72d774a1f5c8a564
F test/savepoint2.test 427c8b20f43d3edf17a290c6788ae9e2703ac51c
F test/savepoint.test 14f78c2c2538764850b38446957048113cc24f75
F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
F test/savepoint3.test e328085853b14898d78ceea00dfe7db18bb6a9ec
F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
F test/savepoint6.test 2df1d093e59e78d688c64eb20e0457aaea7d08f9
F test/savepoint6.test bdc653c5c08328c84a74471b5b23fd483b919dfe
F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
F test/securedel.test 328d2921c0ca49bdd3352e516b0377fc07143254
@@ -599,11 +602,11 @@ F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
F test/sync.test ded6b39d8d8ca3c0c5518516c6371b3316d3e3a3
F test/table.test bf102a5669c4db7a41330802f24a4a81a4204f83
F test/tableapi.test 7262a8cbaa9965d429f1cbd2747edc185fa56516
F test/tclsqlite.test bf4227eb236a4c097aa7974a2bf7d3225acf34be
F test/tclsqlite.test 013133fa83128569c6fb8a7a48dc7c4507e6ff1c
F test/tempdb.test 1bf52da28a9c24e29717362a87722dff08feb72b
F test/temptable.test f42121a0d29a62f00f93274464164177ab1cc24a
F test/temptrigger.test b0273db072ce5f37cf19140ceb1f0d524bbe9f05
F test/tester.tcl e1f581c7a2648a0aaa51135c4d2e7be68f4b9292
F test/tester.tcl 17082172a11795f9bc3e843aadb463ff76050c68
F test/thread001.test a3e6a7254d1cb057836cb3145b60c10bf5b7e60f
F test/thread002.test afd20095e6e845b405df4f2c920cb93301ca69db
F test/thread003.test b824d4f52b870ae39fc5bae4d8070eca73085dca
@@ -611,7 +614,7 @@ F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f
F test/thread005.test bf5c374ca65dd89fd56c8fe511ccfb46875bda5e
F test/thread1.test 862dd006d189e8b0946935db17399dcac2f8ef91
F test/thread2.test 6e0997f7beabb6a7e471bd18740ed04805c785f4
F test/thread_common.tcl b65e6b1d1d90dc885e10ad080896c6c56eef0819
F test/thread_common.tcl 0b07423d29ddb73d4bacbac69268c8d37b6cc5d2
F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
F test/threadtest2.c ace893054fa134af3fc8d6e7cfecddb8e3acefb9
F test/tkt-02a8e81d44.test 58494de77be2cf249228ada3f313fa399821c6ab
@@ -712,7 +715,7 @@ F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
F test/tkt4018.test 7c2c9ba4df489c676a0a7a0e809a1fb9b2185bd1
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
F test/trace.test 4b36a41a3e9c7842151af6da5998f5080cdad9e5
F test/trans.test d887cb07630dc39879a322d958ad8b006137485c
F test/trans.test f19bd2c8b5ccc89295454a42b35319b1381b9a3d
F test/trans2.test d5337e61de45e66b1fcbf9db833fa8c82e624b22
F test/trans3.test d728abaa318ca364dc370e06576aa7e5fbed7e97
F test/trigger1.test 2e18561f85e448bb633c9c9de792e9bbf7b2dd3e
@@ -758,6 +761,13 @@ F test/vtabE.test 7c4693638d7797ce2eda17af74292b97e705cc61
F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5
F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
F test/vtab_shared.test 0eff9ce4f19facbe0a3e693f6c14b80711a4222d
F test/wal.test 0c7c059c194871dd42811c31599cf74f1e1123e4
F test/walbak.test a0e45187c7d8928df035dfea29b99b016b21ca3c
F test/walcrash.test 63edc6a9e05f645b54d649186a5818fc82953e2e
F test/walhook.test 5f18e0fc8787f1f8889d7a9971af18f334f83786
F test/walmode.test bac6f06544a8554588a1543def996bbe2fc41792
F test/walslow.test d21625e2e99e11c032ce949e8a94661576548933
F test/walthread.test cc3c5302dfd56361def4023b7f29d361f1f1602d
F test/where.test de337a3fe0a459ec7c93db16a519657a90552330
F test/where2.test 43d4becaf5a5df854e6c21d624a1cb84c6904554
F test/where3.test aa44a9b29e8c9f3d7bb94a3bb3a95b31627d520d
@@ -781,7 +791,7 @@ F tool/lempar.c 01ca97f87610d1dac6d8cd96ab109ab1130e76dc
F tool/mkkeywordhash.c d2e6b4a5965e23afb80fbe74bb54648cd371f309
F tool/mkopts.tcl 66ac10d240cc6e86abd37dc908d50382f84ff46e
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c.tcl 4c6924c7e877defa8f9a12ef1e6867de614acf3f
F tool/mksqlite3c.tcl 42b6ba8b997a5f5a98f7f1df946c21c173eef586
F tool/mksqlite3h.tcl eb100dce83f24b501b325b340f8b5eb8e5106b3b
F tool/mksqlite3internalh.tcl 7b43894e21bcb1bb39e11547ce7e38a063357e87
F tool/omittest.tcl 27d6f6e3b1e95aeb26a1c140e6eb57771c6d794a
@@ -801,14 +811,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P f660be615a0c1f4641782a83744533f5d3119218
R 037eba7c61c44ed46ed186528ab88ba6
P 946e06cd2a2cc209b0786b7e9d28c0746320d646 27a5c09ce8a35039d844d08cfe5698e8b438abfe
R 496e930fc5a775a552ace6c40959352e
U drh
Z f87382a5b2e28a51f3f0db30682d2137
Z 6de75408ba1e125bd7e7fd0306efd7dd
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
iD8DBQFL20WioxKgR168RlERAlxiAJ4w8aexa2GTqhDb3cG3AfHpTGYYtACeMOWV
+0spJvIkBg57uJ7hlauNec4=
=n48A
iD8DBQFL3tjgoxKgR168RlERAn8zAJ4myOF3FCYW6KNideNeI9JIkyQm6ACfbPiT
qZo9Et3fACK9ap0KUhaVVwE=
=UJ+t
-----END PGP SIGNATURE-----
+1 -1
View File
@@ -1 +1 @@
946e06cd2a2cc209b0786b7e9d28c0746320d646
de9ae443cc8eebfb0692dcdac93bde9e4c54199a
+66
View File
@@ -2261,12 +2261,42 @@ static int lockBtree(BtShared *pBt){
if( memcmp(page1, zMagicHeader, 16)!=0 ){
goto page1_init_failed;
}
#ifdef SQLITE_OMIT_WAL
if( page1[18]>1 ){
pBt->readOnly = 1;
}
if( page1[19]>1 ){
goto page1_init_failed;
}
#else
if( page1[18]>2 ){
pBt->readOnly = 1;
}
if( page1[19]>2 ){
goto page1_init_failed;
}
/* If the write version is set to 2, this database should be accessed
** in WAL mode. If the log is not already open, open it now. Then
** return SQLITE_OK and return without populating BtShared.pPage1.
** The caller detects this and calls this function again. This is
** required as the version of page 1 currently in the page1 buffer
** may not be the latest version - there may be a newer one in the log
** file.
*/
if( page1[19]==2 && pBt->doNotUseWAL==0 ){
int isOpen = 0;
rc = sqlite3PagerOpenWal(pBt->pPager, &isOpen);
if( rc!=SQLITE_OK ){
goto page1_init_failed;
}else if( isOpen==0 ){
releasePage(pPage1);
return SQLITE_OK;
}
rc = SQLITE_NOTADB;
}
#endif
/* The maximum embedded fraction must be exactly 25%. And the minimum
** embedded fraction must be 12.5% for both leaf-data and non-leaf-data.
@@ -7963,3 +7993,39 @@ void sqlite3BtreeCacheOverflow(BtCursor *pCur){
pCur->isIncrblobHandle = 1;
}
#endif
/*
** Set both the "read version" (single byte at byte offset 18) and
** "write version" (single byte at byte offset 19) fields in the database
** header to iVersion.
*/
int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
BtShared *pBt = pBtree->pBt;
int rc; /* Return code */
assert( pBtree->inTrans==TRANS_NONE );
assert( iVersion==1 || iVersion==2 );
/* If setting the version fields to 1, do not automatically open the
** WAL connection, even if the version fields are currently set to 2.
*/
pBt->doNotUseWAL = (iVersion==1);
rc = sqlite3BtreeBeginTrans(pBtree, 0);
if( rc==SQLITE_OK ){
u8 *aData = pBt->pPage1->aData;
if( aData[18]!=(u8)iVersion || aData[19]!=(u8)iVersion ){
rc = sqlite3BtreeBeginTrans(pBtree, 2);
if( rc==SQLITE_OK ){
rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
if( rc==SQLITE_OK ){
aData[18] = (u8)iVersion;
aData[19] = (u8)iVersion;
}
}
}
}
pBt->doNotUseWAL = 0;
return rc;
}
+2
View File
@@ -186,6 +186,8 @@ int sqlite3BtreePutData(BtCursor*, u32 offset, u32 amt, void*);
void sqlite3BtreeCacheOverflow(BtCursor *);
void sqlite3BtreeClearCursor(BtCursor *);
int sqlite3BtreeSetVersion(Btree *pBt, int iVersion);
#ifndef NDEBUG
int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
+1
View File
@@ -420,6 +420,7 @@ struct BtShared {
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
u8 inTransaction; /* Transaction state */
u8 doNotUseWAL; /* If true, do not open write-ahead-log file */
int nTransaction; /* Number of open transactions (read + write) */
u32 nPage; /* Number of pages in the database */
void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
+138
View File
@@ -1186,6 +1186,142 @@ void *sqlite3_rollback_hook(
return pRet;
}
#ifndef SQLITE_OMIT_WAL
/*
** The sqlite3_wal_hook() callback registered by sqlite3_wal_autocheckpoint().
** Return non-zero, indicating to the caller that a checkpoint should be run,
** if the number of frames in the log file is greater than
** sqlite3.pWalArg cast to an integer (the value configured by
** wal_autocheckpoint()).
*/
int sqlite3WalDefaultHook(
void *p, /* Argument */
sqlite3 *db, /* Connection */
const char *zNotUsed, /* Database */
int nFrame /* Size of WAL */
){
UNUSED_PARAMETER(zNotUsed);
return ( nFrame>=SQLITE_PTR_TO_INT(p));
}
#endif /* SQLITE_OMIT_WAL */
/*
** Configure an sqlite3_wal_hook() callback to automatically checkpoint
** a database after committing a transaction if there are nFrame or
** more frames in the log file. Passing zero or a negative value as the
** nFrame parameter disables automatic checkpoints entirely.
**
** The callback registered by this function replaces any existing callback
** registered using sqlite3_wal_hook(). Likewise, registering a callback
** using sqlite3_wal_hook() disables the automatic checkpoint mechanism
** configured by this function.
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int nFrame){
#ifndef SQLITE_OMIT_WAL
sqlite3_mutex_enter(db->mutex);
if( nFrame>0 ){
sqlite3_wal_hook(db, sqlite3WalDefaultHook, SQLITE_INT_TO_PTR(nFrame));
}else{
sqlite3_wal_hook(db, 0, 0);
}
sqlite3_mutex_leave(db->mutex);
#endif
return SQLITE_OK;
}
/*
** Register a callback to be invoked each time a transaction is written
** into the write-ahead-log by this database connection.
*/
void *sqlite3_wal_hook(
sqlite3 *db, /* Attach the hook to this db handle */
int(*xCallback)(void *, sqlite3*, const char*, int),
void *pArg /* First argument passed to xCallback() */
){
#ifndef SQLITE_OMIT_WAL
void *pRet;
sqlite3_mutex_enter(db->mutex);
pRet = db->pWalArg;
db->xWalCallback = xCallback;
db->pWalArg = pArg;
sqlite3_mutex_leave(db->mutex);
return pRet;
#else
return 0;
#endif
}
/*
** Checkpoint database zDb. If zDb is NULL, the main database is checkpointed.
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb){
#ifdef SQLITE_OMIT_WAL
return SQLITE_OK;
#else
int rc; /* Return code */
int iDb = SQLITE_MAX_ATTACHED; /* sqlite3.aDb[] index of db to checkpoint */
sqlite3_mutex_enter(db->mutex);
if( zDb ){
iDb = sqlite3FindDbName(db, zDb);
}
if( iDb<0 ){
rc = SQLITE_ERROR;
sqlite3Error(db, SQLITE_ERROR, "unknown database: %s", zDb);
}else{
rc = sqlite3Checkpoint(db, iDb);
sqlite3Error(db, rc, 0);
}
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
#endif
}
#ifndef SQLITE_OMIT_WAL
/*
** Run a checkpoint on database iDb. This is a no-op if database iDb is
** not currently open in WAL mode.
**
** If a transaction is open on the database being checkpointed, this
** function returns SQLITE_LOCKED and a checkpoint is not attempted. If
** an error occurs while running the checkpoint, an SQLite error code is
** returned (i.e. SQLITE_IOERR). Otherwise, SQLITE_OK.
**
** The mutex on database handle db should be held by the caller. The mutex
** associated with the specific b-tree being checkpointed is taken by
** this function while the checkpoint is running.
**
** If iDb is passed SQLITE_MAX_ATTACHED, then all attached databases are
** checkpointed. If an error is encountered it is returned immediately -
** no attempt is made to checkpoint any remaining databases.
*/
int sqlite3Checkpoint(sqlite3 *db, int iDb){
int rc = SQLITE_OK; /* Return code */
int i; /* Used to iterate through attached dbs */
assert( sqlite3_mutex_held(db->mutex) );
for(i=0; i<db->nDb && rc==SQLITE_OK; i++){
if( i==iDb || iDb==SQLITE_MAX_ATTACHED ){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
if( sqlite3BtreeIsInReadTrans(pBt) ){
rc = SQLITE_LOCKED;
}else{
sqlite3BtreeEnter(pBt);
rc = sqlite3PagerCheckpoint(sqlite3BtreePager(pBt));
sqlite3BtreeLeave(pBt);
}
}
}
}
return rc;
}
#endif /* SQLITE_OMIT_WAL */
/*
** This function returns true if main-memory should be used instead of
** a temporary file for transient pager files and statement journals.
@@ -1746,6 +1882,8 @@ static int openDatabase(
setupLookaside(db, 0, sqlite3GlobalConfig.szLookaside,
sqlite3GlobalConfig.nLookaside);
sqlite3_wal_autocheckpoint(db, SQLITE_DEFAULT_WAL_AUTOCHECKPOINT);
opendb_out:
if( db ){
assert( db->mutex!=0 || isThreadsafe==0 || sqlite3GlobalConfig.bFullMutex==0 );
+1
View File
@@ -77,6 +77,7 @@ int sqlite3MutexEnd(void){
** Retrieve a pointer to a static mutex or allocate a new dynamic one.
*/
sqlite3_mutex *sqlite3_mutex_alloc(int id){
if( !sqlite3GlobalConfig.bCoreMutex ) return 0;
#ifndef SQLITE_OMIT_AUTOINIT
if( sqlite3_initialize() ) return 0;
#endif
+2
View File
@@ -40,6 +40,7 @@ int sqlite3OSTrace = 0;
if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
#define OSTRACE7(X,Y,Z,A,B,C,D) \
if(sqlite3OSTrace) sqlite3DebugPrintf(X,Y,Z,A,B,C,D)
#define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
#else
#define OSTRACE1(X)
#define OSTRACE2(X,Y)
@@ -48,6 +49,7 @@ int sqlite3OSTrace = 0;
#define OSTRACE5(X,Y,Z,A,B)
#define OSTRACE6(X,Y,Z,A,B,C)
#define OSTRACE7(X,Y,Z,A,B,C,D)
#define OSTRACE(X)
#endif
/*
+1 -1
View File
@@ -1111,7 +1111,7 @@ int sqlite3_os_init(void){
os2Randomness, /* xRandomness */
os2Sleep, /* xSleep */
os2CurrentTime, /* xCurrentTime */
os2GetLastError /* xGetLastError */
os2GetLastError, /* xGetLastError */
};
sqlite3_vfs_register(&os2Vfs, 1);
initUconvObjects();
+831 -2
View File
@@ -119,6 +119,7 @@
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/mman.h>
#if SQLITE_ENABLE_LOCKING_STYLE
# include <sys/ioctl.h>
@@ -1536,9 +1537,11 @@ static int _posixUnlock(sqlite3_file *id, int locktype, int handleNFSUnlock){
** the file has changed and hence might not know to flush their
** cache. The use of a stale cache can lead to database corruption.
*/
#if 0
assert( pFile->inNormalWrite==0
|| pFile->dbUpdate==0
|| pFile->transCntrChng==1 );
#endif
pFile->inNormalWrite = 0;
#endif
@@ -2956,10 +2959,12 @@ static int unixRead(
/* If this is a database file (not a journal, master-journal or temp
** file), the bytes in the locking range should never be read or written. */
#if 0
assert( pFile->pUnused==0
|| offset>=PENDING_BYTE+512
|| offset+amt<=PENDING_BYTE
);
#endif
got = seekAndRead(pFile, offset, pBuf, amt);
if( got==amt ){
@@ -3031,10 +3036,12 @@ static int unixWrite(
/* If this is a database file (not a journal, master-journal or temp
** file), the bytes in the locking range should never be read or written. */
#if 0
assert( pFile->pUnused==0
|| offset>=PENDING_BYTE+512
|| offset+amt<=PENDING_BYTE
);
#endif
#ifndef NDEBUG
/* If we are doing a normal write to a database file (as opposed to
@@ -4555,6 +4562,820 @@ static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){
return 0;
}
#ifndef SQLITE_OMIT_WAL
/* Forward reference */
typedef struct unixShm unixShm;
typedef struct unixShmFile unixShmFile;
/*
** Object used to represent a single file opened and mmapped to provide
** shared memory. When multiple threads all reference the same
** log-summary, each thread has its own unixFile object, but they all
** point to a single instance of this object. In other words, each
** log-summary is opened only once per process.
**
** unixMutexHeld() must be true when creating or destroying
** this object or while reading or writing the following fields:
**
** nRef
** pNext
**
** The following fields are read-only after the object is created:
**
** fid
** zFilename
**
** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
**
** To avoid deadlocks, mutex and mutexBuf are always released in the
** reverse order that they are acquired. mutexBuf is always acquired
** first and released last. This invariant is check by asserting
** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or
** released.
*/
struct unixShmFile {
struct unixFileId fid; /* Unique file identifier */
sqlite3_mutex *mutex; /* Mutex to access this object */
sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */
char *zFilename; /* Name of the file */
int h; /* Open file descriptor */
int szMap; /* Size of the mapping of file into memory */
char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */
int nRef; /* Number of unixShm objects pointing to this */
unixShm *pFirst; /* All unixShm objects pointing to this */
unixShmFile *pNext; /* Next in list of all unixShmFile objects */
#ifdef SQLITE_DEBUG
u8 exclMask; /* Mask of exclusive locks held */
u8 sharedMask; /* Mask of shared locks held */
u8 nextShmId; /* Next available unixShm.id value */
#endif
};
/*
** A global array of all unixShmFile objects.
**
** The unixMutexHeld() must be true while reading or writing this list.
*/
static unixShmFile *unixShmFileList = 0;
/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
**
** unixShm.pFile->mutex must be held while reading or writing the
** unixShm.pNext and unixShm.locks[] elements.
**
** The unixShm.pFile element is initialized when the object is created
** and is read-only thereafter.
*/
struct unixShm {
unixShmFile *pFile; /* The underlying unixShmFile object */
unixShm *pNext; /* Next unixShm with the same unixShmFile */
u8 lockState; /* Current lock state */
u8 readLock; /* Which of the two read-lock states to use */
u8 hasMutex; /* True if holding the unixShmFile mutex */
u8 hasMutexBuf; /* True if holding pFile->mutexBuf */
u8 sharedMask; /* Mask of shared locks held */
u8 exclMask; /* Mask of exclusive locks held */
#ifdef SQLITE_DEBUG
u8 id; /* Id of this connection with its unixShmFile */
#endif
};
/*
** Size increment by which shared memory grows
*/
#define SQLITE_UNIX_SHM_INCR 4096
/*
** Constants used for locking
*/
#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */
#define UNIX_SHM_MUTEX 0x01 /* Mask for MUTEX lock */
#define UNIX_SHM_DMS 0x04 /* Mask for Dead-Man-Switch lock */
#define UNIX_SHM_A 0x10 /* Mask for region locks... */
#define UNIX_SHM_B 0x20
#define UNIX_SHM_C 0x40
#define UNIX_SHM_D 0x80
#ifdef SQLITE_DEBUG
/*
** Return a pointer to a nul-terminated string in static memory that
** describes a locking mask. The string is of the form "MSABCD" with
** each character representing a lock. "M" for MUTEX, "S" for DMS,
** and "A" through "D" for the region locks. If a lock is held, the
** letter is shown. If the lock is not held, the letter is converted
** to ".".
**
** This routine is for debugging purposes only and does not appear
** in a production build.
*/
static const char *unixShmLockString(u8 mask){
static char zBuf[48];
static int iBuf = 0;
char *z;
z = &zBuf[iBuf];
iBuf += 8;
if( iBuf>=sizeof(zBuf) ) iBuf = 0;
z[0] = (mask & UNIX_SHM_MUTEX) ? 'M' : '.';
z[1] = (mask & UNIX_SHM_DMS) ? 'S' : '.';
z[2] = (mask & UNIX_SHM_A) ? 'A' : '.';
z[3] = (mask & UNIX_SHM_B) ? 'B' : '.';
z[4] = (mask & UNIX_SHM_C) ? 'C' : '.';
z[5] = (mask & UNIX_SHM_D) ? 'D' : '.';
z[6] = 0;
return z;
}
#endif /* SQLITE_DEBUG */
/*
** Apply posix advisory locks for all bytes identified in lockMask.
**
** lockMask might contain multiple bits but all bits are guaranteed
** to be contiguous.
**
** Locks block if the UNIX_SHM_MUTEX bit is set and are non-blocking
** otherwise.
*/
static int unixShmSystemLock(
unixShmFile *pFile, /* Apply locks to this open shared-memory segment */
int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
u8 lockMask /* Which bytes to lock or unlock */
){
struct flock f; /* The posix advisory locking structure */
int lockOp; /* The opcode for fcntl() */
int i; /* Offset into the locking byte range */
int rc; /* Result code form fcntl() */
u8 mask; /* Mask of bits in lockMask */
/* Access to the unixShmFile object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 );
/* Initialize the locking parameters */
memset(&f, 0, sizeof(f));
f.l_type = lockType;
f.l_whence = SEEK_SET;
if( (lockMask & UNIX_SHM_MUTEX)!=0 && lockType!=F_UNLCK ){
lockOp = F_SETLKW;
OSTRACE(("SHM-LOCK requesting blocking lock\n"));
}else{
lockOp = F_SETLK;
}
/* Find the first bit in lockMask that is set */
for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){}
assert( mask!=0 );
f.l_start = i+UNIX_SHM_BASE;
f.l_len = 1;
/* Extend the locking range for each additional bit that is set */
mask <<= 1;
while( mask!=0 && (lockMask & mask)!=0 ){
f.l_len++;
mask <<= 1;
}
/* Verify that all bits set in lockMask are contiguous */
assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 );
/* Acquire the system-level lock */
rc = fcntl(pFile->h, lockOp, &f);
rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
/* Update the global lock state and do debug tracing */
#ifdef SQLITE_DEBUG
OSTRACE(("SHM-LOCK "));
if( rc==SQLITE_OK ){
if( lockType==F_UNLCK ){
OSTRACE(("unlock ok"));
pFile->exclMask &= ~lockMask;
pFile->sharedMask &= ~lockMask;
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock ok"));
pFile->exclMask &= ~lockMask;
pFile->sharedMask |= lockMask;
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock ok"));
pFile->exclMask |= lockMask;
pFile->sharedMask &= ~lockMask;
}
}else{
if( lockType==F_UNLCK ){
OSTRACE(("unlock failed"));
}else if( lockType==F_RDLCK ){
OSTRACE(("read-lock failed"));
}else{
assert( lockType==F_WRLCK );
OSTRACE(("write-lock failed"));
}
}
OSTRACE((" - change requested %s - afterwards %s:%s\n",
unixShmLockString(lockMask),
unixShmLockString(pFile->sharedMask),
unixShmLockString(pFile->exclMask)));
#endif
return rc;
}
/*
** For connection p, unlock all of the locks identified by the unlockMask
** parameter.
*/
static int unixShmUnlock(
unixShmFile *pFile, /* The underlying shared-memory file */
unixShm *p, /* The connection to be unlocked */
u8 unlockMask /* Mask of locks to be unlocked */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
u8 allMask; /* Union of locks held by connections other than "p" */
/* Access to the unixShmFile object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) );
/* Compute locks held by sibling connections */
allMask = 0;
for(pX=pFile->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 );
allMask |= pX->sharedMask;
}
/* Unlock the system-level locks */
if( (unlockMask & allMask)!=unlockMask ){
rc = unixShmSystemLock(pFile, F_UNLCK, unlockMask & ~allMask);
}else{
rc = SQLITE_OK;
}
/* Undo the local locks */
if( rc==SQLITE_OK ){
p->exclMask &= ~unlockMask;
p->sharedMask &= ~unlockMask;
}
return rc;
}
/*
** Get reader locks for connection p on all locks in the readMask parameter.
*/
static int unixShmSharedLock(
unixShmFile *pFile, /* The underlying shared-memory file */
unixShm *p, /* The connection to get the shared locks */
u8 readMask /* Mask of shared locks to be acquired */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
u8 allShared; /* Union of locks held by connections other than "p" */
/* Access to the unixShmFile object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) );
/* Find out which shared locks are already held by sibling connections.
** If any sibling already holds an exclusive lock, go ahead and return
** SQLITE_BUSY.
*/
allShared = 0;
for(pX=pFile->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY;
allShared |= pX->sharedMask;
}
/* Get shared locks at the system level, if necessary */
if( (~allShared) & readMask ){
rc = unixShmSystemLock(pFile, F_RDLCK, readMask);
}else{
rc = SQLITE_OK;
}
/* Get the local shared locks */
if( rc==SQLITE_OK ){
p->sharedMask |= readMask;
}
return rc;
}
/*
** For connection p, get an exclusive lock on all locks identified in
** the writeMask parameter.
*/
static int unixShmExclusiveLock(
unixShmFile *pFile, /* The underlying shared-memory file */
unixShm *p, /* The connection to get the exclusive locks */
u8 writeMask /* Mask of exclusive locks to be acquired */
){
int rc; /* Result code */
unixShm *pX; /* For looping over all sibling connections */
/* Access to the unixShmFile object is serialized by the caller */
assert( sqlite3_mutex_held(pFile->mutex) );
/* Make sure no sibling connections hold locks that will block this
** lock. If any do, return SQLITE_BUSY right away.
*/
for(pX=pFile->pFirst; pX; pX=pX->pNext){
if( pX==p ) continue;
if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY;
if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY;
}
/* Get the exclusive locks at the system level. Then if successful
** also mark the local connection as being locked.
*/
rc = unixShmSystemLock(pFile, F_WRLCK, writeMask);
if( rc==SQLITE_OK ){
p->sharedMask &= ~writeMask;
p->exclMask |= writeMask;
}
return rc;
}
/*
** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0.
**
** This is not a VFS shared-memory method; it is a utility function called
** by VFS shared-memory methods.
*/
static void unixShmPurge(void){
unixShmFile **pp;
unixShmFile *p;
assert( unixMutexHeld() );
pp = &unixShmFileList;
while( (p = *pp)!=0 ){
if( p->nRef==0 ){
if( p->mutex ) sqlite3_mutex_free(p->mutex);
if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf);
if( p->h>=0 ) close(p->h);
*pp = p->pNext;
sqlite3_free(p);
}else{
pp = &p->pNext;
}
}
}
/*
** Open a shared-memory area. This particular implementation uses
** mmapped files.
**
** zName is a filename used to identify the shared-memory area. The
** implementation does not (and perhaps should not) use this name
** directly, but rather use it as a template for finding an appropriate
** name for the shared-memory storage. In this implementation, the
** string "-index" is appended to zName and used as the name of the
** mmapped file.
**
** When opening a new shared-memory file, if no other instances of that
** file are currently open, in this process or in other processes, then
** the file must be truncated to zero length or have its header cleared.
*/
static int unixShmOpen(
sqlite3_vfs *pVfs, /* The VFS */
const char *zName, /* Base name of file to mmap */
sqlite3_shm **pShm /* Write the unixShm object created here */
){
struct unixShm *p = 0; /* The connection to be opened */
struct unixShmFile *pFile = 0; /* The underlying mmapped file */
int rc; /* Result code */
struct unixFileId fid; /* Unix file identifier */
struct unixShmFile *pNew; /* Newly allocated pFile */
struct stat sStat; /* Result from stat() an fstat() */
int nName; /* Size of zName in bytes */
/* Allocate space for the new sqlite3_shm object. Also speculatively
** allocate space for a new unixShmFile and filename.
*/
p = sqlite3_malloc( sizeof(*p) );
if( p==0 ) return SQLITE_NOMEM;
memset(p, 0, sizeof(*p));
nName = strlen(zName);
pNew = sqlite3_malloc( sizeof(*pFile) + nName + 10 );
if( pNew==0 ){
rc = SQLITE_NOMEM;
goto shm_open_err;
}
memset(pNew, 0, sizeof(*pNew));
pNew->zFilename = (char*)&pNew[1];
sqlite3_snprintf(nName+10, pNew->zFilename, "%s-index", zName);
/* Look to see if there is an existing unixShmFile that can be used.
** If no matching unixShmFile currently exists, create a new one.
*/
unixEnterMutex();
rc = stat(pNew->zFilename, &sStat);
if( rc==0 ){
memset(&fid, 0, sizeof(fid));
fid.dev = sStat.st_dev;
fid.ino = sStat.st_ino;
for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){
if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break;
}
}
if( pFile ){
sqlite3_free(pNew);
}else{
pFile = pNew;
pNew = 0;
pFile->h = -1;
pFile->pNext = unixShmFileList;
unixShmFileList = pFile;
pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pFile->mutex==0 ){
rc = SQLITE_NOMEM;
goto shm_open_err;
}
pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pFile->mutexBuf==0 ){
rc = SQLITE_NOMEM;
goto shm_open_err;
}
pFile->h = open(pFile->zFilename, O_RDWR|O_CREAT, 0664);
if( pFile->h<0 ){
rc = SQLITE_CANTOPEN_BKPT;
goto shm_open_err;
}
rc = fstat(pFile->h, &sStat);
if( rc ){
rc = SQLITE_CANTOPEN_BKPT;
goto shm_open_err;
}
pFile->fid.dev = sStat.st_dev;
pFile->fid.ino = sStat.st_ino;
/* Check to see if another process is holding the dead-man switch.
** If not, truncate the file to zero length.
*/
if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_MUTEX) ){
rc = SQLITE_IOERR_LOCK;
goto shm_open_err;
}
if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){
if( ftruncate(pFile->h, 0) ){
rc = SQLITE_IOERR;
}
}
if( rc==SQLITE_OK ){
rc = unixShmSystemLock(pFile, F_RDLCK, UNIX_SHM_DMS);
}
unixShmSystemLock(pFile, F_UNLCK, UNIX_SHM_MUTEX);
if( rc ) goto shm_open_err;
}
/* Make the new connection a child of the unixShmFile */
p->pFile = pFile;
p->pNext = pFile->pFirst;
#ifdef SQLITE_DEBUG
p->id = pFile->nextShmId++;
#endif
pFile->pFirst = p;
pFile->nRef++;
*pShm = (sqlite3_shm*)p;
unixLeaveMutex();
return SQLITE_OK;
/* Jump here on any error */
shm_open_err:
unixShmPurge();
sqlite3_free(p);
sqlite3_free(pFile);
sqlite3_free(pNew);
*pShm = 0;
unixLeaveMutex();
return rc;
}
/*
** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
*/
static int unixShmClose(sqlite3_shm *pSharedMem, int deleteFlag){
unixShm *p; /* The connection to be closed */
unixShmFile *pFile; /* The underlying shared-memory file */
unixShm **pp; /* For looping over sibling connections */
if( pSharedMem==0 ) return SQLITE_OK;
p = (struct unixShm*)pSharedMem;
pFile = p->pFile;
/* Verify that the connection being closed holds no locks */
assert( p->exclMask==0 );
assert( p->sharedMask==0 );
/* Remove connection p from the set of connections associated with pFile */
sqlite3_mutex_enter(pFile->mutex);
for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){}
*pp = p->pNext;
/* Free the connection p */
sqlite3_free(p);
sqlite3_mutex_leave(pFile->mutex);
/* If pFile->nRef has reached 0, then close the underlying
** shared-memory file, too */
unixEnterMutex();
assert( pFile->nRef>0 );
pFile->nRef--;
if( pFile->nRef==0 ){
if( deleteFlag ) unlink(pFile->zFilename);
unixShmPurge();
}
unixLeaveMutex();
return SQLITE_OK;
}
/*
** Query and/or changes the size of the underlying storage for
** a shared-memory segment. The reqSize parameter is the new size
** of the underlying storage, or -1 to do just a query. The size
** of the underlying storage (after resizing if resizing occurs) is
** written into pNewSize.
**
** This routine does not (necessarily) change the size of the mapping
** of the underlying storage into memory. Use xShmGet() to change
** the mapping size.
**
** The reqSize parameter is the minimum size requested. The implementation
** is free to expand the storage to some larger amount if it chooses.
*/
static int unixShmSize(
sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */
int reqSize, /* Requested size. -1 for query only */
int *pNewSize /* Write new size here */
){
unixShm *p = (unixShm*)pSharedMem;
unixShmFile *pFile = p->pFile;
int rc = SQLITE_OK;
struct stat sStat;
if( reqSize>=0 ){
reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR;
reqSize *= SQLITE_UNIX_SHM_INCR;
rc = ftruncate(pFile->h, reqSize);
}
if( fstat(pFile->h, &sStat)==0 ){
*pNewSize = (int)sStat.st_size;
}else{
*pNewSize = 0;
rc = SQLITE_IOERR;
}
return rc;
}
/*
** Map the shared storage into memory. The minimum size of the
** mapping should be reqMapSize if reqMapSize is positive. If
** reqMapSize is zero or negative, the implementation can choose
** whatever mapping size is convenient.
**
** *ppBuf is made to point to the memory which is a mapping of the
** underlying storage. A mutex is acquired to prevent other threads
** from running while *ppBuf is in use in order to prevent other threads
** remapping *ppBuf out from under this thread. The unixShmRelease()
** call will release the mutex. However, if the lock state is CHECKPOINT,
** the mutex is not acquired because CHECKPOINT will never remap the
** buffer. RECOVER might remap, though, so CHECKPOINT will acquire
** the mutex if and when it promotes to RECOVER.
**
** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from
** being remapped also prevents more than one thread from being in
** RECOVER at a time. But, RECOVER sometimes wants to remap itself.
** To prevent RECOVER from losing its lock while remapping, the
** mutex is not released by unixShmRelease() when in RECOVER.
**
** *pNewMapSize is set to the size of the mapping.
**
** *ppBuf and *pNewMapSize might be NULL and zero if no space has
** yet been allocated to the underlying storage.
*/
static int unixShmGet(
sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */
int reqMapSize, /* Requested size of mapping. -1 means don't care */
int *pNewMapSize, /* Write new size of mapping here */
void **ppBuf /* Write mapping buffer origin here */
){
unixShm *p = (unixShm*)pSharedMem;
unixShmFile *pFile = p->pFile;
int rc = SQLITE_OK;
if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){
assert( sqlite3_mutex_notheld(pFile->mutex) );
sqlite3_mutex_enter(pFile->mutexBuf);
p->hasMutexBuf = 1;
}
sqlite3_mutex_enter(pFile->mutex);
if( pFile->szMap==0 || reqMapSize>pFile->szMap ){
int actualSize;
if( unixShmSize(pSharedMem, -1, &actualSize)==SQLITE_OK
&& reqMapSize<actualSize
){
reqMapSize = actualSize;
}
if( pFile->pMMapBuf ){
munmap(pFile->pMMapBuf, pFile->szMap);
}
pFile->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED,
pFile->h, 0);
pFile->szMap = pFile->pMMapBuf ? reqMapSize : 0;
}
*pNewMapSize = pFile->szMap;
*ppBuf = pFile->pMMapBuf;
sqlite3_mutex_leave(pFile->mutex);
return rc;
}
/*
** Release the lock held on the shared memory segment to that other
** threads are free to resize it if necessary.
**
** If the lock is not currently held, this routine is a harmless no-op.
**
** If the shared-memory object is in lock state RECOVER, then we do not
** really want to release the lock, so in that case too, this routine
** is a no-op.
*/
static int unixShmRelease(sqlite3_shm *pSharedMem){
unixShm *p = (unixShm*)pSharedMem;
if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){
unixShmFile *pFile = p->pFile;
assert( sqlite3_mutex_notheld(pFile->mutex) );
sqlite3_mutex_leave(pFile->mutexBuf);
p->hasMutexBuf = 0;
}
return SQLITE_OK;
}
/*
** Symbolic names for LOCK states used for debugging.
*/
#ifdef SQLITE_DEBUG
static const char *azLkName[] = {
"UNLOCK",
"READ",
"READ_FULL",
"WRITE",
"PENDING",
"CHECKPOINT",
"RECOVER"
};
#endif
/*
** Change the lock state for a shared-memory segment.
*/
static int unixShmLock(
sqlite3_shm *pSharedMem, /* Pointer from unixShmOpen() */
int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */
int *pGotLock /* The lock you actually got */
){
unixShm *p = (unixShm*)pSharedMem;
unixShmFile *pFile = p->pFile;
int rc = SQLITE_PROTOCOL;
/* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never
** directly requested; they are side effects from requesting
** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively.
*/
assert( desiredLock==SQLITE_SHM_QUERY
|| desiredLock==SQLITE_SHM_UNLOCK
|| desiredLock==SQLITE_SHM_READ
|| desiredLock==SQLITE_SHM_WRITE
|| desiredLock==SQLITE_SHM_CHECKPOINT
|| desiredLock==SQLITE_SHM_RECOVER );
/* Return directly if this is just a lock state query, or if
** the connection is already in the desired locking state.
*/
if( desiredLock==SQLITE_SHM_QUERY
|| desiredLock==p->lockState
|| (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL)
){
OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n",
p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState]));
if( pGotLock ) *pGotLock = p->lockState;
return SQLITE_OK;
}
OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n",
p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock]));
if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){
assert( sqlite3_mutex_notheld(pFile->mutex) );
sqlite3_mutex_enter(pFile->mutexBuf);
p->hasMutexBuf = 1;
}
sqlite3_mutex_enter(pFile->mutex);
switch( desiredLock ){
case SQLITE_SHM_UNLOCK: {
assert( p->lockState!=SQLITE_SHM_RECOVER );
unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D);
rc = SQLITE_OK;
p->lockState = SQLITE_SHM_UNLOCK;
break;
}
case SQLITE_SHM_READ: {
if( p->lockState==SQLITE_SHM_UNLOCK ){
int nAttempt;
rc = SQLITE_BUSY;
assert( p->lockState==SQLITE_SHM_UNLOCK );
for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){
rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B);
if( rc==SQLITE_BUSY ){
rc = unixShmSharedLock(pFile, p, UNIX_SHM_D);
if( rc==SQLITE_OK ){
p->lockState = p->readLock = SQLITE_SHM_READ_FULL;
}
}else{
unixShmUnlock(pFile, p, UNIX_SHM_B);
p->lockState = p->readLock = SQLITE_SHM_READ;
}
}
}else if( p->lockState==SQLITE_SHM_WRITE ){
rc = unixShmSharedLock(pFile, p, UNIX_SHM_A);
unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D);
p->lockState = p->readLock = SQLITE_SHM_READ;
}else{
assert( p->lockState==SQLITE_SHM_RECOVER );
unixShmUnlock(pFile, p, UNIX_SHM_MUTEX);
p->lockState = p->readLock;
rc = SQLITE_OK;
}
break;
}
case SQLITE_SHM_WRITE: {
assert( p->lockState==SQLITE_SHM_READ
|| p->lockState==SQLITE_SHM_READ_FULL );
rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_WRITE;
}
break;
}
case SQLITE_SHM_CHECKPOINT: {
assert( p->lockState==SQLITE_SHM_UNLOCK
|| p->lockState==SQLITE_SHM_PENDING
|| p->lockState==SQLITE_SHM_RECOVER );
if( p->lockState==SQLITE_SHM_RECOVER ){
unixShmUnlock(pFile, p, UNIX_SHM_MUTEX);
p->lockState = SQLITE_SHM_CHECKPOINT;
rc = SQLITE_OK;
}
if( p->lockState==SQLITE_SHM_UNLOCK ){
rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_PENDING;
}
}
if( p->lockState==SQLITE_SHM_PENDING ){
rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_CHECKPOINT;
}
}
break;
}
default: {
assert( desiredLock==SQLITE_SHM_RECOVER );
assert( p->lockState==SQLITE_SHM_READ
|| p->lockState==SQLITE_SHM_READ_FULL
|| p->lockState==SQLITE_SHM_CHECKPOINT );
assert( sqlite3_mutex_held(pFile->mutexBuf) );
rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_MUTEX);
if( rc==SQLITE_OK ){
p->lockState = SQLITE_SHM_RECOVER;
}
break;
}
}
sqlite3_mutex_leave(pFile->mutex);
OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n",
p->id, getpid(), azLkName[p->lockState]));
if( pGotLock ) *pGotLock = p->lockState;
return rc;
}
#else
# define unixShmOpen 0
# define unixShmSize 0
# define unixShmGet 0
# define unixShmRelease 0
# define unixShmLock 0
# define unixShmClose 0
#endif /* #ifndef SQLITE_OMIT_WAL */
/*
************************ End of sqlite3_vfs methods ***************************
******************************************************************************/
@@ -5755,7 +6576,7 @@ int sqlite3_os_init(void){
** that filesystem time.
*/
#define UNIXVFS(VFSNAME, FINDER) { \
1, /* iVersion */ \
2, /* iVersion */ \
sizeof(unixFile), /* szOsFile */ \
MAX_PATHNAME, /* mxPathname */ \
0, /* pNext */ \
@@ -5772,7 +6593,15 @@ int sqlite3_os_init(void){
unixRandomness, /* xRandomness */ \
unixSleep, /* xSleep */ \
unixCurrentTime, /* xCurrentTime */ \
unixGetLastError /* xGetLastError */ \
unixGetLastError, /* xGetLastError */ \
unixShmOpen, /* xShmOpen */ \
unixShmSize, /* xShmSize */ \
unixShmGet, /* xShmGet */ \
unixShmRelease, /* xShmRelease */ \
unixShmLock, /* xShmLock */ \
unixShmClose, /* xShmClose */ \
0, /* xRename */ \
0, /* xCurrentTimeInt64 */ \
}
/*
+1 -1
View File
@@ -1912,7 +1912,7 @@ int sqlite3_os_init(void){
winRandomness, /* xRandomness */
winSleep, /* xSleep */
winCurrentTime, /* xCurrentTime */
winGetLastError /* xGetLastError */
winGetLastError, /* xGetLastError */
};
sqlite3_vfs_register(&winVfs, 1);
+706 -290
View File
File diff suppressed because it is too large Load Diff
+6
View File
@@ -76,6 +76,7 @@ typedef struct PgHdr DbPage;
#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
/*
** The remainder of this file contains the declarations of the functions
@@ -133,6 +134,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n);
int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint);
int sqlite3PagerSharedLock(Pager *pPager);
int sqlite3PagerCheckpoint(Pager *pPager);
int sqlite3PagerWalCallback(Pager *pPager);
int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
int sqlite3PagerCloseWal(Pager *pPager);
/* Functions used to query pager state and configuration. */
u8 sqlite3PagerIsreadonly(Pager*);
int sqlite3PagerRefcount(Pager*);
+80 -29
View File
@@ -257,6 +257,31 @@ static const char *actionName(u8 action){
}
#endif
/*
** Parameter eMode must be one of the PAGER_JOURNALMODE_XXX constants
** defined in pager.h. This function returns the associated lowercase
** journal-mode name.
*/
const char *sqlite3JournalModename(int eMode){
static char * const azModeName[] = {
"delete", "persist", "off", "truncate", "memory"
#ifndef SQLITE_OMIT_WAL
, "wal"
#endif
};
assert( PAGER_JOURNALMODE_DELETE==0 );
assert( PAGER_JOURNALMODE_PERSIST==1 );
assert( PAGER_JOURNALMODE_OFF==2 );
assert( PAGER_JOURNALMODE_TRUNCATE==3 );
assert( PAGER_JOURNALMODE_MEMORY==4 );
assert( PAGER_JOURNALMODE_WAL==5 );
assert( eMode>=0 && eMode<=ArraySize(azModeName) );
if( eMode==ArraySize(azModeName) ) return 0;
return azModeName[eMode];
}
/*
** Process a pragma statement.
**
@@ -509,18 +534,21 @@ void sqlite3Pragma(
** PRAGMA [database.]journal_mode = (delete|persist|off|truncate|memory)
*/
if( sqlite3StrICmp(zLeft,"journal_mode")==0 ){
int eMode;
static char * const azModeName[] = {
"delete", "persist", "off", "truncate", "memory"
};
int eMode; /* One of the PAGER_JOURNALMODE_XXX symbols */
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
if( zRight==0 ){
eMode = PAGER_JOURNALMODE_QUERY;
}else{
const char *zMode;
int n = sqlite3Strlen30(zRight);
eMode = sizeof(azModeName)/sizeof(azModeName[0]) - 1;
while( eMode>=0 && sqlite3StrNICmp(zRight, azModeName[eMode], n)!=0 ){
eMode--;
for(eMode=0; (zMode = sqlite3JournalModename(eMode)); eMode++){
if( sqlite3StrNICmp(zRight, zMode, n)==0 ) break;
}
if( !zMode ){
eMode = PAGER_JOURNALMODE_QUERY;
}
}
if( pId2->n==0 && eMode==PAGER_JOURNALMODE_QUERY ){
@@ -529,39 +557,32 @@ void sqlite3Pragma(
** the journal-mode of the main database).
*/
eMode = db->dfltJournalMode;
sqlite3VdbeAddOp2(v, OP_String8, 0, 1);
sqlite3VdbeChangeP4(v, -1, sqlite3JournalModename(eMode), P4_STATIC);
}else{
Pager *pPager;
if( pId2->n==0 ){
int ii;
if( pId2->n==0 && eMode!=PAGER_JOURNALMODE_WAL ){
/* This indicates that no database name was specified as part
** of the PRAGMA command. In this case the journal-mode must be
** set on all attached databases, as well as the main db file.
**
** Also, the sqlite3.dfltJournalMode variable is set so that
** any subsequently attached databases also use the specified
** journal mode.
** journal mode. Except, the default journal mode is never set
** to WAL.
*/
int ii;
assert(pDb==&db->aDb[0]);
for(ii=1; ii<db->nDb; ii++){
if( db->aDb[ii].pBt ){
pPager = sqlite3BtreePager(db->aDb[ii].pBt);
sqlite3PagerJournalMode(pPager, eMode);
}
}
db->dfltJournalMode = (u8)eMode;
}
pPager = sqlite3BtreePager(pDb->pBt);
eMode = sqlite3PagerJournalMode(pPager, eMode);
for(ii=db->nDb-1; ii>=0; ii--){
if( db->aDb[ii].pBt && (ii==iDb || pId2->n==0) ){
sqlite3VdbeUsesBtree(v, ii);
sqlite3VdbeAddOp3(v, OP_JournalMode, ii, 1, eMode);
}
}
}
assert( eMode==PAGER_JOURNALMODE_DELETE
|| eMode==PAGER_JOURNALMODE_TRUNCATE
|| eMode==PAGER_JOURNALMODE_PERSIST
|| eMode==PAGER_JOURNALMODE_OFF
|| eMode==PAGER_JOURNALMODE_MEMORY );
sqlite3VdbeSetNumCols(v, 1);
sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "journal_mode", SQLITE_STATIC);
sqlite3VdbeAddOp4(v, OP_String8, 0, 1, 0,
azModeName[eMode], P4_STATIC);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 1);
}else
@@ -1379,6 +1400,36 @@ void sqlite3Pragma(
}else
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
#ifndef SQLITE_OMIT_WAL
/*
** PRAGMA [database.]wal_checkpoint
**
** Checkpoint the database.
*/
if( sqlite3StrICmp(zLeft, "wal_checkpoint")==0 ){
if( sqlite3ReadSchema(pParse) ) goto pragma_out;
sqlite3VdbeAddOp3(v, OP_Checkpoint, iDb, 0, 0);
}else
/*
** PRAGMA wal_autocheckpoint
** PRAGMA wal_autocheckpoint = N
**
** Configure a database connection to automatically checkpoint a database
** after accumulating N frames in the log. Or query for the current value
** of N.
*/
if( sqlite3StrICmp(zLeft, "wal_autocheckpoint")==0 ){
if( zRight ){
int nAuto = atoi(zRight);
sqlite3_wal_autocheckpoint(db, nAuto);
}
returnSingleInt(pParse, "wal_autocheckpoint",
db->xWalCallback==sqlite3WalDefaultHook ?
SQLITE_PTR_TO_INT(db->pWalArg) : 0);
}else
#endif
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
/*
** Report the current state of file logs for all databases
+81 -3
View File
@@ -818,8 +818,9 @@ typedef struct sqlite3_mutex sqlite3_mutex;
**
*/
typedef struct sqlite3_vfs sqlite3_vfs;
typedef struct sqlite3_shm sqlite3_shm;
struct sqlite3_vfs {
int iVersion; /* Structure version number */
int iVersion; /* Structure version number (currently 2) */
int szOsFile; /* Size of subclassed sqlite3_file */
int mxPathname; /* Maximum file pathname length */
sqlite3_vfs *pNext; /* Next registered VFS */
@@ -838,8 +839,23 @@ struct sqlite3_vfs {
int (*xSleep)(sqlite3_vfs*, int microseconds);
int (*xCurrentTime)(sqlite3_vfs*, double*);
int (*xGetLastError)(sqlite3_vfs*, int, char *);
/* New fields may be appended in figure versions. The iVersion
** value will increment whenever this happens. */
/*
** The methods above are in version 1 of the sqlite_vfs object
** definition. Those that follow are added in version 2 or later
*/
int (*xShmOpen)(sqlite3_vfs*, const char *zName, sqlite3_shm**);
int (*xShmSize)(sqlite3_shm*, int reqSize, int *pNewSize);
int (*xShmGet)(sqlite3_shm*, int reqMapSize, int *pMapSize, void**);
int (*xShmRelease)(sqlite3_shm*);
int (*xShmLock)(sqlite3_shm*, int desiredLock, int *gotLock);
int (*xShmClose)(sqlite3_shm*, int deleteFlag);
int (*xRename)(sqlite3_vfs*, const char *zOld, const char *zNew, int dirSync);
int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*);
/*
** The methods above are in versions 1 and 2 of the sqlite_vfs object.
** New fields may be appended in figure versions. The iVersion
** value will increment whenever this happens.
*/
};
/*
@@ -859,6 +875,24 @@ struct sqlite3_vfs {
#define SQLITE_ACCESS_READWRITE 1
#define SQLITE_ACCESS_READ 2
/*
** CAPI3REF: Flags for the xShmLock VFS method
**
** These integer constants define the various locking states that
** an sqlite3_shm object can be in. The SQLITE_SHM_QUERY integer
** is not a valid data - it is a constant pasted to the
** sqlite3_vfs.xShmLock() method for querying the current lock
** state.
*/
#define SQLITE_SHM_UNLOCK 0
#define SQLITE_SHM_READ 1
#define SQLITE_SHM_READ_FULL 2
#define SQLITE_SHM_WRITE 3
#define SQLITE_SHM_PENDING 4
#define SQLITE_SHM_CHECKPOINT 5
#define SQLITE_SHM_RECOVER 6
#define SQLITE_SHM_QUERY (-1)
/*
** CAPI3REF: Initialize The SQLite Library
**
@@ -5726,6 +5760,50 @@ int sqlite3_strnicmp(const char *, const char *, int);
*/
void sqlite3_log(int iErrCode, const char *zFormat, ...);
/*
** CAPI3REF: Write-Ahead Log Commit Hook
**
** The [sqlite3_wal_hook()] function is used to register a callback that
** will be invoked each time a database connection commits data to a
** write-ahead-log (i.e. whenever a transaction is committed in
** journal_mode=WAL mode).
**
** The callback is invoked by SQLite after the commit has taken place and
** the associated write-lock on the database released, so the implementation
** may read, write or checkpoint the database as required.
**
** The first parameter passed to the callback function when it is invoked
** is a copy of the third parameter passed to sqlite3_wal_hook() when
** registering the callback. The second is a copy of the database handle.
** The third parameter is the name of the database that was written to -
** either "main" or the name of an ATTACHed database. The fourth parameter
** is the number of pages currently in the log file, including those that
** were just committed.
**
** If an invocation of the callback function returns non-zero, then a
** checkpoint is automatically run on the database. If zero is returned,
** no special action is taken.
**
** A single database handle may have at most a single log callback
** registered at one time. Calling [sqlite3_wal_hook()] replaces any
** previously registered log callback.
*/
void *sqlite3_wal_hook(
sqlite3*,
int(*)(void *,sqlite3*,const char*,int),
void*
);
/*
** CAPI3REF: Configure an auto-checkpoint
*/
int sqlite3_wal_autocheckpoint(sqlite3 *db, int N);
/*
** CAPI3REF: Checkpoint a database
*/
int sqlite3_wal_checkpoint(sqlite3 *db, const char *zDb);
/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
+7
View File
@@ -823,6 +823,10 @@ struct sqlite3 {
void (*xRollbackCallback)(void*); /* Invoked at every commit. */
void *pUpdateArg;
void (*xUpdateCallback)(void*,int, const char*,const char*,sqlite_int64);
#ifndef SQLITE_OMIT_WAL
int (*xWalCallback)(void *, sqlite3 *, const char *, int);
void *pWalArg;
#endif
void(*xCollNeeded)(void*,sqlite3*,int eTextRep,const char*);
void(*xCollNeeded16)(void*,sqlite3*,int eTextRep,const void*);
void *pCollNeededArg;
@@ -2993,6 +2997,9 @@ void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
int sqlite3TempInMemory(const sqlite3*);
VTable *sqlite3GetVTable(sqlite3*, Table*);
const char *sqlite3JournalModename(int);
int sqlite3Checkpoint(sqlite3*, int);
int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
+8
View File
@@ -108,6 +108,14 @@
# define SQLITE_DEFAULT_TEMP_CACHE_SIZE 500
#endif
/*
** The default number of frames to accumulate in the log file before
** checkpointing the database in WAL mode.
*/
#ifndef SQLITE_DEFAULT_WAL_AUTOCHECKPOINT
# define SQLITE_DEFAULT_WAL_AUTOCHECKPOINT 1000
#endif
/*
** The maximum number of attached databases. This must be between 0
** and 30. The upper bound on 30 is because a 32-bit integer bitmap
+37 -2
View File
@@ -123,6 +123,7 @@ struct SqliteDb {
SqlFunc *pFunc; /* List of SQL functions */
Tcl_Obj *pUpdateHook; /* Update hook script (if any) */
Tcl_Obj *pRollbackHook; /* Rollback hook script (if any) */
Tcl_Obj *pLogHook; /* WAL hook script (if any) */
Tcl_Obj *pUnlockNotify; /* Unlock notify script (if any) */
SqlCollate *pCollate; /* List of SQL collation functions */
int rc; /* Return code of most recent sqlite3_exec() */
@@ -485,6 +486,9 @@ static void DbDeleteCmd(void *db){
if( pDb->pRollbackHook ){
Tcl_DecrRefCount(pDb->pRollbackHook);
}
if( pDb->pLogHook ){
Tcl_DecrRefCount(pDb->pLogHook);
}
if( pDb->pCollateNeeded ){
Tcl_DecrRefCount(pDb->pCollateNeeded);
}
@@ -589,6 +593,32 @@ static void DbRollbackHandler(void *clientData){
}
}
static int DbLogHandler(
void *clientData,
sqlite3 *db,
const char *zDb,
int nEntry
){
int ret = 0;
Tcl_Obj *p;
SqliteDb *pDb = (SqliteDb*)clientData;
Tcl_Interp *interp = pDb->interp;
assert(pDb->pLogHook);
p = Tcl_DuplicateObj(pDb->pLogHook);
Tcl_IncrRefCount(p);
Tcl_ListObjAppendElement(interp, p, Tcl_NewStringObj(zDb, -1));
Tcl_ListObjAppendElement(interp, p, Tcl_NewIntObj(nEntry));
if( TCL_OK!=Tcl_EvalObjEx(interp, p, 0)
|| TCL_OK!=Tcl_GetIntFromObj(interp, Tcl_GetObjResult(interp), &ret)
){
Tcl_BackgroundError(interp);
}
Tcl_DecrRefCount(p);
return ret;
}
#if defined(SQLITE_TEST) && defined(SQLITE_ENABLE_UNLOCK_NOTIFY)
static void setTestUnlockNotifyVars(Tcl_Interp *interp, int iArg, int nArg){
char zBuf[64];
@@ -1545,7 +1575,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
"restore", "rollback_hook", "status",
"timeout", "total_changes", "trace",
"transaction", "unlock_notify", "update_hook",
"version", 0
"version", "wal_hook", 0
};
enum DB_enum {
DB_AUTHORIZER, DB_BACKUP, DB_BUSY,
@@ -1559,7 +1589,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
DB_RESTORE, DB_ROLLBACK_HOOK, DB_STATUS,
DB_TIMEOUT, DB_TOTAL_CHANGES, DB_TRACE,
DB_TRANSACTION, DB_UNLOCK_NOTIFY, DB_UPDATE_HOOK,
DB_VERSION,
DB_VERSION, DB_WAL_HOOK
};
/* don't leave trailing commas on DB_enum, it confuses the AIX xlc compiler */
@@ -2730,9 +2760,11 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
}
/*
** $db wal_hook ?script?
** $db update_hook ?script?
** $db rollback_hook ?script?
*/
case DB_WAL_HOOK:
case DB_UPDATE_HOOK:
case DB_ROLLBACK_HOOK: {
@@ -2742,6 +2774,8 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
Tcl_Obj **ppHook;
if( choice==DB_UPDATE_HOOK ){
ppHook = &pDb->pUpdateHook;
}else if( choice==DB_WAL_HOOK ){
ppHook = &pDb->pLogHook;
}else{
ppHook = &pDb->pRollbackHook;
}
@@ -2767,6 +2801,7 @@ static int DbObjCmd(void *cd, Tcl_Interp *interp, int objc,Tcl_Obj *const*objv){
sqlite3_update_hook(pDb->db, (pDb->pUpdateHook?DbUpdateHandler:0), pDb);
sqlite3_rollback_hook(pDb->db,(pDb->pRollbackHook?DbRollbackHandler:0),pDb);
sqlite3_wal_hook(pDb->db,(pDb->pLogHook?DbLogHandler:0),pDb);
break;
}
+30
View File
@@ -4868,6 +4868,35 @@ static int test_unlock_notify(
}
#endif
/*
** tclcmd: sqlite3_wal_checkpoint db ?NAME?
*/
static int test_wal_checkpoint(
ClientData clientData, /* Unused */
Tcl_Interp *interp, /* The TCL interpreter that invoked this command */
int objc, /* Number of arguments */
Tcl_Obj *CONST objv[] /* Command arguments */
){
char *zDb = 0;
sqlite3 *db;
int rc;
if( objc!=3 && objc!=2 ){
Tcl_WrongNumArgs(interp, 1, objv, "DB ?NAME?");
return TCL_ERROR;
}
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ){
return TCL_ERROR;
}
if( objc==3 ){
zDb = Tcl_GetString(objv[2]);
}
rc = sqlite3_wal_checkpoint(db, zDb);
Tcl_SetResult(interp, (char *)t1ErrorName(rc), TCL_STATIC);
return TCL_OK;
}
/*
** tcl_objproc COMMANDNAME ARGS...
@@ -5089,6 +5118,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
{ "sqlite3_unlock_notify", test_unlock_notify, 0 },
#endif
{ "sqlite3_wal_checkpoint", test_wal_checkpoint, 0 },
};
static int bitmask_size = sizeof(Bitmask)*8;
int i;
+1 -1
View File
@@ -781,7 +781,7 @@ static int crashEnableCmd(
cfDlClose, /* xDlClose */
cfRandomness, /* xRandomness */
cfSleep, /* xSleep */
cfCurrentTime /* xCurrentTime */
cfCurrentTime, /* xCurrentTime */
};
if( objc!=2 ){
+6
View File
@@ -499,6 +499,12 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "vtab", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_WAL
Tcl_SetVar2(interp, "sqlite_options", "wal", "0", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "wal", "1", TCL_GLOBAL_ONLY);
#endif
#ifdef SQLITE_OMIT_WSD
Tcl_SetVar2(interp, "sqlite_options", "wsd", "0", TCL_GLOBAL_ONLY);
#else
+1 -1
View File
@@ -623,7 +623,7 @@ sqlite3_vfs *sqlite3_demovfs(void){
demoDlClose, /* xDlClose */
demoRandomness, /* xRandomness */
demoSleep, /* xSleep */
demoCurrentTime /* xCurrentTime */
demoCurrentTime, /* xCurrentTime */
};
return &demovfs;
}
+53 -2
View File
@@ -68,8 +68,15 @@ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut);
static int devsymSleep(sqlite3_vfs*, int microseconds);
static int devsymCurrentTime(sqlite3_vfs*, double*);
static int devsymShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **);
static int devsymShmSize(sqlite3_shm *, int , int *);
static int devsymShmGet(sqlite3_shm *, int , int *, void **);
static int devsymShmRelease(sqlite3_shm *);
static int devsymShmLock(sqlite3_shm *, int , int *);
static int devsymShmClose(sqlite3_shm *, int);
static sqlite3_vfs devsym_vfs = {
1, /* iVersion */
2, /* iVersion */
sizeof(devsym_file), /* szOsFile */
DEVSYM_MAX_PATHNAME, /* mxPathname */
0, /* pNext */
@@ -92,7 +99,16 @@ static sqlite3_vfs devsym_vfs = {
#endif /* SQLITE_OMIT_LOAD_EXTENSION */
devsymRandomness, /* xRandomness */
devsymSleep, /* xSleep */
devsymCurrentTime /* xCurrentTime */
devsymCurrentTime, /* xCurrentTime */
0, /* xGetLastError */
devsymShmOpen,
devsymShmSize,
devsymShmGet,
devsymShmRelease,
devsymShmLock,
devsymShmClose,
0,
0,
};
static sqlite3_io_methods devsym_io_methods = {
@@ -333,6 +349,35 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
return sqlite3OsCurrentTime(g.pVfs, pTimeOut);
}
static int devsymShmOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_shm **pp
){
return g.pVfs->xShmOpen(g.pVfs, zName, pp);
}
static int devsymShmSize(sqlite3_shm *p, int reqSize, int *pNewSize){
return g.pVfs->xShmSize(p, reqSize, pNewSize);
}
static int devsymShmGet(
sqlite3_shm *p,
int reqMapSize,
int *pMapSize,
void **pp
){
return g.pVfs->xShmGet(p, reqMapSize, pMapSize, pp);
}
static int devsymShmRelease(sqlite3_shm *p){
return g.pVfs->xShmRelease(p);
}
static int devsymShmLock(sqlite3_shm *p, int desiredLock, int *gotLock){
return g.pVfs->xShmLock(p, desiredLock, gotLock);
}
static int devsymShmClose(sqlite3_shm *p, int deleteFlag){
return g.pVfs->xShmClose(p, deleteFlag);
}
/*
** This procedure registers the devsym vfs with SQLite. If the argument is
** true, the devsym vfs becomes the new default vfs. It is the only publicly
@@ -342,6 +387,12 @@ void devsym_register(int iDeviceChar, int iSectorSize){
if( g.pVfs==0 ){
g.pVfs = sqlite3_vfs_find(0);
devsym_vfs.szOsFile += g.pVfs->szOsFile;
devsym_vfs.xShmOpen = (g.pVfs->xShmOpen ? devsymShmOpen : 0);
devsym_vfs.xShmSize = (g.pVfs->xShmSize ? devsymShmSize : 0);
devsym_vfs.xShmGet = (g.pVfs->xShmGet ? devsymShmGet : 0);
devsym_vfs.xShmRelease = (g.pVfs->xShmRelease ? devsymShmRelease : 0);
devsym_vfs.xShmLock = (g.pVfs->xShmLock ? devsymShmLock : 0);
devsym_vfs.xShmClose = (g.pVfs->xShmClose ? devsymShmClose : 0);
sqlite3_vfs_register(&devsym_vfs, 0);
}
if( iDeviceChar>=0 ){
+1 -1
View File
@@ -179,7 +179,7 @@ static sqlite3_vfs jt_vfs = {
jtDlClose, /* xDlClose */
jtRandomness, /* xRandomness */
jtSleep, /* xSleep */
jtCurrentTime /* xCurrentTime */
jtCurrentTime, /* xCurrentTime */
};
static sqlite3_io_methods jt_io_methods = {
+8 -1
View File
@@ -198,7 +198,14 @@ static fs_vfs_t fs_vfs = {
fsDlClose, /* xDlClose */
fsRandomness, /* xRandomness */
fsSleep, /* xSleep */
fsCurrentTime /* xCurrentTime */
fsCurrentTime, /* xCurrentTime */
0, /* xShmOpen */
0, /* xShmSize */
0, /* xShmLock */
0, /* xShmClose */
0, /* xShmDelete */
0, /* xRename */
0 /* xCurrentTimeInt64 */
},
0, /* pFileList */
0 /* pParent */
+1 -1
View File
@@ -203,7 +203,7 @@ static sqlite3_vfs inst_vfs = {
instDlClose, /* xDlClose */
instRandomness, /* xRandomness */
instSleep, /* xSleep */
instCurrentTime /* xCurrentTime */
instCurrentTime, /* xCurrentTime */
};
static sqlite3_io_methods inst_io_methods = {
+2
View File
@@ -58,6 +58,7 @@ static Tcl_ObjCmdProc blocking_step_proc;
static Tcl_ObjCmdProc blocking_prepare_v2_proc;
#endif
int Sqlitetest1_Init(Tcl_Interp *);
int Sqlite3_Init(Tcl_Interp *);
/* Functions from test1.c */
void *sqlite3TestTextToPtr(const char *);
@@ -124,6 +125,7 @@ static Tcl_ThreadCreateType tclScriptThread(ClientData pSqlThread){
#endif
Sqlitetest1_Init(interp);
Sqlitetest_mutex_Init(interp);
Sqlite3_Init(interp);
rc = Tcl_Eval(interp, p->zScript);
pRes = Tcl_GetObjResult(interp);
+106
View File
@@ -5186,6 +5186,112 @@ case OP_AggFinal: {
break;
}
#ifndef SQLITE_OMIT_WAL
/* Opcode: Checkpoint P1 * * * *
**
** Checkpoint database P1. This is a no-op if P1 is not currently in
** WAL mode.
*/
case OP_Checkpoint: {
rc = sqlite3Checkpoint(db, pOp->p1);
break;
};
#endif
/* Opcode: JournalMode P1 P2 P3 * *
**
** Change the journal mode of database P1 to P3. P3 must be one of the
** PAGER_JOURNALMODE_XXX values. If changing between the various rollback
** modes (delete, truncate, persist, off and memory), this is a simple
** operation. No IO is required.
**
** If changing into or out of WAL mode the procedure is more complicated.
**
** Write a string containing the final journal-mode to register P2.
*/
case OP_JournalMode: { /* out2-prerelease */
Btree *pBt; /* Btree to change journal mode of */
Pager *pPager; /* Pager associated with pBt */
int eNew; /* New journal mode */
int eOld; /* The old journal mode */
const sqlite3_vfs *pVfs; /* The VFS of pPager */
const char *zFilename; /* Name of database file for pPager */
eNew = pOp->p3;
assert( eNew==PAGER_JOURNALMODE_DELETE
|| eNew==PAGER_JOURNALMODE_TRUNCATE
|| eNew==PAGER_JOURNALMODE_PERSIST
|| eNew==PAGER_JOURNALMODE_OFF
|| eNew==PAGER_JOURNALMODE_MEMORY
|| eNew==PAGER_JOURNALMODE_WAL
|| eNew==PAGER_JOURNALMODE_QUERY
);
assert( pOp->p1>=0 && pOp->p1<db->nDb );
assert( (p->btreeMask & (1<<pOp->p1))!=0 );
pBt = db->aDb[pOp->p1].pBt;
pPager = sqlite3BtreePager(pBt);
#ifndef SQLITE_OMIT_WAL
zFilename = sqlite3PagerFilename(pPager);
pVfs = sqlite3PagerVfs(pPager);
/* Do not allow a transition to journal_mode=WAL for a database
** in temporary storage or if the VFS does not support xShmOpen.
*/
if( eNew==PAGER_JOURNALMODE_WAL
&& (zFilename[0]==0 /* Temp file */
|| pVfs->iVersion<2 || pVfs->xShmOpen==0) /* No xShmOpen support */
){
eNew = PAGER_JOURNALMODE_QUERY;
}
if( eNew!=PAGER_JOURNALMODE_QUERY ){
eOld = sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_QUERY);
if( (eNew!=eOld)
&& (eOld==PAGER_JOURNALMODE_WAL || eNew==PAGER_JOURNALMODE_WAL)
){
if( !db->autoCommit || db->activeVdbeCnt>1 ){
rc = SQLITE_ERROR;
sqlite3SetString(&p->zErrMsg, db,
"cannot change %s wal mode from within a transaction",
(eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
);
}else{
if( eOld==PAGER_JOURNALMODE_WAL ){
/* If leaving WAL mode, close the log file. If successful, the call
** to PagerCloseWal() checkpoints and deletes the write-ahead-log
** file. An EXCLUSIVE lock may still be held on the database file
** after a successful return.
*/
rc = sqlite3PagerCloseWal(pPager);
if( rc!=SQLITE_OK ) goto abort_due_to_error;
sqlite3PagerJournalMode(pPager, eNew);
}else{
sqlite3PagerJournalMode(pPager, PAGER_JOURNALMODE_DELETE);
}
/* Open a transaction on the database file. Regardless of the journal
** mode, this transaction always uses a rollback journal.
*/
assert( sqlite3BtreeIsInTrans(pBt)==0 );
rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
if( rc!=SQLITE_OK ) goto abort_due_to_error;
}
}
}
#endif /* ifndef SQLITE_OMIT_WAL */
eNew = sqlite3PagerJournalMode(pPager, eNew);
pOut = &aMem[pOp->p2];
pOut->flags = MEM_Str|MEM_Static|MEM_Term;
pOut->z = (char *)sqlite3JournalModename(eNew);
pOut->n = sqlite3Strlen30(pOut->z);
pOut->enc = SQLITE_UTF8;
sqlite3VdbeChangeEncoding(pOut, encoding);
break;
};
#if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH)
/* Opcode: Vacuum * * * * *
+31
View File
@@ -306,6 +306,29 @@ void sqlite3_result_error_nomem(sqlite3_context *pCtx){
pCtx->s.db->mallocFailed = 1;
}
/*
** This function is called after a transaction has been committed. It
** invokes callbacks registered with sqlite3_wal_hook() as required.
*/
static int doWalCallbacks(sqlite3 *db){
int rc = SQLITE_OK;
#ifndef SQLITE_OMIT_WAL
int i;
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
int nEntry = sqlite3PagerWalCallback(sqlite3BtreePager(pBt));
if( db->xWalCallback && nEntry>0 && rc==SQLITE_OK
&& db->xWalCallback(db->pWalArg, db, db->aDb[i].zName, nEntry)
){
rc = sqlite3Checkpoint(db, i);
}
}
}
#endif
return rc;
}
/*
** Execute the statement pStmt, either until a row of data is ready, the
** statement is completely executed or an error occurs.
@@ -387,6 +410,14 @@ static int sqlite3Step(Vdbe *p){
}
#endif
if( rc==SQLITE_DONE ){
assert( p->rc==SQLITE_OK );
p->rc = doWalCallbacks(db);
if( p->rc!=SQLITE_OK ){
rc = SQLITE_ERROR;
}
}
db->errCode = rc;
if( SQLITE_NOMEM==sqlite3ApiExit(p->db, p->rc) ){
p->rc = SQLITE_NOMEM;
+1316
View File
File diff suppressed because it is too large Load Diff
+99
View File
@@ -0,0 +1,99 @@
/*
** 2010 February 1
**
** 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 header file defines the interface to the write-ahead logging
** system. Refer to the comments below and the header comment attached to
** the implementation of each function in log.c for further details.
*/
#ifndef _WAL_H_
#define _WAL_H_
#include "sqliteInt.h"
#ifdef SQLITE_OMIT_WAL
# define sqlite3WalOpen(x,y,z) 0
# define sqlite3WalClose(w,x,y,z) 0
# define sqlite3WalOpenSnapshot(y,z) 0
# define sqlite3WalCloseSnapshot(z)
# define sqlite3WalRead(w,x,y,z) 0
# define sqlite3WalDbsize(y,z)
# define sqlite3WalWriteLock(y,z) 0
# define sqlite3WalUndo(x,y,z) 0
# define sqlite3WalSavepoint(z) 0
# define sqlite3WalSavepointUndo(y,z) 0
# define sqlite3WalFrames(u,v,w,x,y,z) 0
# define sqlite3WalCheckpoint(u,v,w,x,y,z) 0
# define sqlite3WalCallback(z) 0
#else
/* Connection to a write-ahead log (WAL) file.
** There is one object of this type for each pager.
*/
typedef struct Wal Wal;
/* Open and close a connection to a write-ahead log. */
int sqlite3WalOpen(sqlite3_vfs*, const char *zDb, Wal **ppWal);
int sqlite3WalClose(Wal *pWal, sqlite3_file *pFd, int sync_flags, u8 *zBuf);
/* Used by readers to open (lock) and close (unlock) a snapshot. A
** snapshot is like a read-transaction. It is the state of the database
** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and
** preserves the current state even if the other threads or processes
** write to or checkpoint the WAL. sqlite3WalCloseSnapshot() closes the
** transaction and releases the lock.
*/
int sqlite3WalOpenSnapshot(Wal *pWal, int *);
void sqlite3WalCloseSnapshot(Wal *pWal);
/* Read a page from the write-ahead log, if it is present. */
int sqlite3WalRead(Wal *pWal, Pgno pgno, int *pInWal, u8 *pOut);
/* Return the size of the database as it existed at the beginning
** of the snapshot */
void sqlite3WalDbsize(Wal *pWal, Pgno *pPgno);
/* Obtain or release the WRITER lock. */
int sqlite3WalWriteLock(Wal *pWal, int op);
/* Undo any frames written (but not committed) to the log */
int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx);
/* Return an integer that records the current (uncommitted) write
** position in the WAL */
u32 sqlite3WalSavepoint(Wal *pWal);
/* Move the write position of the WAL back to iFrame. Called in
** response to a ROLLBACK TO command. */
int sqlite3WalSavepointUndo(Wal *pWal, u32 iFrame);
/* Write a frame or frames to the log. */
int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int);
/* Copy pages from the log to the database file */
int sqlite3WalCheckpoint(
Wal *pWal, /* Write-ahead log connection */
sqlite3_file *pFd, /* File descriptor open on db file */
int sync_flags, /* Flags to sync db file with (or 0) */
u8 *zBuf, /* Temporary buffer to use */
int (*xBusyHandler)(void *), /* Pointer to busy-handler function */
void *pBusyHandlerArg /* Argument to pass to xBusyHandler */
);
/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called. If no commits have occurred since
** the last call, then return 0.
*/
int sqlite3WalCallback(Wal *pWal);
#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* _WAL_H_ */
+7 -2
View File
@@ -22,8 +22,9 @@ source $testdir/tester.tcl
# Create several tables to work with.
#
do_test avtrans-1.0 {
execsql {
PRAGMA auto_vacuum=ON;
execsql { PRAGMA auto_vacuum=ON }
wal_set_journal_mode
execsql {
CREATE TABLE one(a int PRIMARY KEY, b text);
INSERT INTO one VALUES(1,'one');
INSERT INTO one VALUES(2,'two');
@@ -48,6 +49,7 @@ do_test avtrans-1.10 {
execsql {SELECT b FROM two ORDER BY a} altdb
} {I V X}
integrity_check avtrans-1.11
wal_check_journal_mode avtrans-1.12
# Basic transactions
#
@@ -84,6 +86,7 @@ do_test avtrans-2.10 {
}
} {1 2 3 1 5 10}
integrity_check avtrans-2.11
wal_check_journal_mode avtrans-2.12
# Check the locking behavior
#
@@ -913,9 +916,11 @@ for {set i 2} {$i<=$limit} {incr i} {
} {1}
}
}
wal_check_journal_mode avtrans-9.$i-6.$cnt
}
set ::pager_old_format 0
}
integrity_check avtrans-10.1
wal_check_journal_mode avtrans-10.2
finish_test
+4
View File
@@ -38,6 +38,8 @@ source $testdir/tester.tcl
#
# backup-9.*: Test that passing a negative argument to backup_step() is
# interpreted as "copy the whole file".
#
# backup-10.*: Test writing the source database mid backup.
#
proc data_checksum {db file} { $db one "SELECT md5sum(a, b) FROM ${file}.t1" }
@@ -487,6 +489,7 @@ db2 close
# 3) Backing up memory-to-file.
#
set iTest 0
file delete -force bak.db-wal
foreach {writer file} {db test.db db3 test.db db :memory:} {
incr iTest
catch { file delete bak.db }
@@ -905,6 +908,7 @@ ifcapable memorymanage {
}
#-----------------------------------------------------------------------
# Test that if the database is written to via the same database handle being
# used as the source by a backup operation:
#
+3 -1
View File
@@ -37,9 +37,11 @@ integrity_check corruptA-1.2
db close
file copy -force test.db test.db-template
set unreadable_version 02
ifcapable wal { set unreadable_version 03 }
do_test corruptA-2.1 {
file copy -force test.db-template test.db
hexio_write test.db 19 02 ;# the read format number
hexio_write test.db 19 $unreadable_version ;# the read format number
sqlite3 db test.db
catchsql {SELECT * FROM t1}
} {1 {file is encrypted or is not a database}}
+1 -63
View File
@@ -16,69 +16,8 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
# Launch another testfixture process to be controlled by this one. A
# channel name is returned that may be passed as the first argument to proc
# 'testfixture' to execute a command. The child testfixture process is shut
# down by closing the channel.
proc launch_testfixture {} {
set prg [info nameofexec]
if {$prg eq ""} {
set prg [file join . testfixture]
}
set chan [open "|$prg tf_main.tcl" r+]
fconfigure $chan -buffering line
return $chan
}
# Execute a command in a child testfixture process, connected by two-way
# channel $chan. Return the result of the command, or an error message.
proc testfixture {chan cmd} {
puts $chan $cmd
puts $chan OVER
set r ""
while { 1 } {
set line [gets $chan]
if { $line == "OVER" } {
return $r
}
if {[eof $chan]} {
return "ERROR: Child process hung up"
}
append r $line
}
}
# Write the main loop for the child testfixture processes into file
# tf_main.tcl. The parent (this script) interacts with the child processes
# via a two way pipe. The parent writes a script to the stdin of the child
# process, followed by the word "OVER" on a line of its own. The child
# process evaluates the script and writes the results to stdout, followed
# by an "OVER" of its own.
set f [open tf_main.tcl w]
puts $f {
set l [open log w]
set script ""
while {![eof stdin]} {
flush stdout
set line [gets stdin]
puts $l "READ $line"
if { $line == "OVER" } {
catch {eval $script} result
puts $result
puts $l "WRITE $result"
puts OVER
puts $l "WRITE OVER"
flush stdout
set script ""
} else {
append script $line
append script " ; "
}
}
close $l
}
close $f
# Simple locking test case:
#
@@ -95,7 +34,6 @@ close $f
#
do_test lock2-1.1 {
set ::tf1 [launch_testfixture]
testfixture $::tf1 "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
testfixture $::tf1 {
sqlite3 db test.db -key xyzzy
db eval {select * from sqlite_master}
+99
View File
@@ -0,0 +1,99 @@
# 2010 April 14
#
# 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 contains code used by several different test scripts. The
# code in this file allows testfixture to control another process (or
# processes) to test locking.
#
# Launch another testfixture process to be controlled by this one. A
# channel name is returned that may be passed as the first argument to proc
# 'testfixture' to execute a command. The child testfixture process is shut
# down by closing the channel.
proc launch_testfixture {} {
set prg [info nameofexec]
if {$prg eq ""} {
set prg [file join . testfixture]
}
set chan [open "|$prg tf_main.tcl" r+]
fconfigure $chan -buffering line
testfixture $chan "sqlite3_test_control_pending_byte $::sqlite_pending_byte"
return $chan
}
# Execute a command in a child testfixture process, connected by two-way
# channel $chan. Return the result of the command, or an error message.
proc testfixture {chan cmd} {
puts $chan $cmd
puts $chan OVER
set r ""
while { 1 } {
set line [gets $chan]
if { $line == "OVER" } {
return $r
}
if {[eof $chan]} {
return "ERROR: Child process hung up"
}
append r $line
}
}
proc testfixture_nb_cb {varname chan} {
set line [gets $chan]
if { $line == "OVER" } {
set $varname $::tfnb($chan)
unset ::tfnb($chan)
close $chan
} else {
append ::tfnb($chan) $line
}
}
proc testfixture_nb {varname cmd} {
set chan [launch_testfixture]
set ::tfnb($chan) ""
fconfigure $chan -blocking 0 -buffering none
puts $chan $cmd
puts $chan OVER
fileevent $chan readable [list testfixture_nb_cb $varname $chan]
return ""
}
# Write the main loop for the child testfixture processes into file
# tf_main.tcl. The parent (this script) interacts with the child processes
# via a two way pipe. The parent writes a script to the stdin of the child
# process, followed by the word "OVER" on a line of its own. The child
# process evaluates the script and writes the results to stdout, followed
# by an "OVER" of its own.
set f [open tf_main.tcl w]
puts $f {
set l [open log w]
set script ""
while {![eof stdin]} {
flush stdout
set line [gets stdin]
puts $l "READ $line"
if { $line == "OVER" } {
set rc [catch {eval $script} result]
puts $result
puts $l "WRITE $result"
puts OVER
puts $l "WRITE OVER"
flush stdout
set script ""
} else {
append script $line
append script "\n"
}
}
close $l
}
close $f
+11
View File
@@ -756,6 +756,17 @@ if {[info commands register_demovfs] != ""} {
}
}
run_tests "wal" -description {
Run tests with journal_mode=WAL
} -initialize {
set ::savepoint6_iterations 100
} -shutdown {
unset -nocomplain ::savepoint6_iterations
} -include {
savepoint.test savepoint2.test savepoint6.test
trans.test avtrans.test
}
# End of tests
#############################################################################
+4
View File
@@ -101,6 +101,10 @@ set EXCLUDE {
vtab_err.test
veryquick.test
mallocAll.test
walslow.test
walcrash.test
walthread.test
}
if {[sqlite3 -has-codec]} {
+5 -3
View File
@@ -29,7 +29,7 @@ do_test rdonly-1.1 {
}
} {1}
# Changes the write version from 1 to 2. Verify that the database
# Changes the write version from 1 to 3. Verify that the database
# can be read but not written.
#
do_test rdonly-1.2 {
@@ -37,7 +37,7 @@ do_test rdonly-1.2 {
hexio_get_int [hexio_read test.db 18 1]
} 1
do_test rdonly-1.3 {
hexio_write test.db 18 02
hexio_write test.db 18 03
sqlite3 db test.db
execsql {
SELECT * FROM t1;
@@ -67,8 +67,10 @@ do_test rdonly-1.5 {
# write-version is reloaded). This way, SQLite does not discover that
# the database is read-only until after it is locked.
#
set ro_version 02
ifcapable wal { set ro_version 03 }
do_test rdonly-1.6 {
hexio_write test.db 18 02 ; # write-version
hexio_write test.db 18 $ro_version ; # write-version
hexio_write test.db 24 11223344 ; # change-counter
catchsql {
INSERT INTO t1 VALUES(2);
+237 -192
View File
@@ -14,13 +14,13 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
#----------------------------------------------------------------------
# The following tests - savepoint-1.* - test that the SAVEPOINT, RELEASE
# and ROLLBACK TO comands are correctly parsed, and that the auto-commit
# flag is correctly set and unset as a result.
#
do_test savepoint-1.1 {
wal_set_journal_mode
execsql {
SAVEPOINT sp1;
RELEASE sp1;
@@ -93,6 +93,7 @@ do_test savepoint-1.5 {
do_test savepoint-1.6 {
execsql COMMIT
} {}
wal_check_journal_mode savepoint-1.7
#------------------------------------------------------------------------
# These tests - savepoint-2.* - test rollbacks and releases of savepoints
@@ -175,36 +176,42 @@ do_test savepoint-2.11 {
}
execsql { SELECT * FROM t1 }
} {}
wal_check_journal_mode savepoint-2.12
#------------------------------------------------------------------------
# This block of tests - savepoint-3.* - test that when a transaction
# savepoint is rolled back, locks are not released from database files.
# And that when a transaction savepoint is released, they are released.
#
# These tests do not work in WAL mode. WAL mode does not take RESERVED
# locks on the database file.
#
do_test savepoint-3.1 {
execsql { SAVEPOINT "transaction" }
execsql { PRAGMA lock_status }
} {main unlocked temp closed}
do_test savepoint-3.2 {
execsql { INSERT INTO t1 VALUES(1, 2, 3) }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.3 {
execsql { ROLLBACK TO "transaction" }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.4 {
execsql { INSERT INTO t1 VALUES(1, 2, 3) }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.5 {
execsql { RELEASE "transaction" }
execsql { PRAGMA lock_status }
} {main unlocked temp closed}
if {[wal_is_wal_mode]==0} {
do_test savepoint-3.1 {
execsql { SAVEPOINT "transaction" }
execsql { PRAGMA lock_status }
} {main unlocked temp closed}
do_test savepoint-3.2 {
execsql { INSERT INTO t1 VALUES(1, 2, 3) }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.3 {
execsql { ROLLBACK TO "transaction" }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.4 {
execsql { INSERT INTO t1 VALUES(1, 2, 3) }
execsql { PRAGMA lock_status }
} {main reserved temp closed}
do_test savepoint-3.5 {
execsql { RELEASE "transaction" }
execsql { PRAGMA lock_status }
} {main unlocked temp closed}
}
#------------------------------------------------------------------------
# Test that savepoints that include schema modifications are handled
@@ -264,6 +271,7 @@ do_test savepoint-4.7 {
do_test savepoint-4.8 {
execsql COMMIT
} {}
wal_check_journal_mode savepoint-4.9
#------------------------------------------------------------------------
# Test some logic errors to do with the savepoint feature.
@@ -312,6 +320,18 @@ ifcapable incrblob {
execsql {release abc}
} {}
# Rollback mode:
#
# Open a savepoint transaction and insert a row into the database. Then,
# using a second database handle, open a read-only transaction on the
# database file. Check that the savepoint transaction cannot be committed
# until after the read-only transaction has been closed.
#
# WAL mode:
#
# As above, except that the savepoint transaction can be successfully
# committed before the read-only transaction has been closed.
#
do_test savepoint-5.4.1 {
execsql {
SAVEPOINT main;
@@ -320,17 +340,28 @@ ifcapable incrblob {
} {}
do_test savepoint-5.4.2 {
sqlite3 db2 test.db
execsql { BEGIN ; SELECT * FROM blobs } db2
catchsql { RELEASE main }
} {1 {database is locked}}
do_test savepoint-5.4.3 {
db2 close
catchsql { RELEASE main }
} {0 {}}
do_test savepoint-5.4.4 {
execsql { BEGIN ; SELECT count(*) FROM blobs } db2
} {1}
if {[wal_is_wal_mode]} {
do_test savepoint-5.4.3 { catchsql "RELEASE main" } {0 {}}
do_test savepoint-5.4.4 { db2 close } {}
} else {
do_test savepoint-5.4.3 {
catchsql { RELEASE main }
} {1 {database is locked}}
do_test savepoint-5.4.4 {
db2 close
catchsql { RELEASE main }
} {0 {}}
}
do_test savepoint-5.4.5 {
execsql { SELECT x FROM blobs WHERE rowid = 2 }
} {{another blob}}
do_test savepoint-5.4.6 {
execsql { SELECT count(*) FROM blobs }
} {2}
}
wal_check_journal_mode savepoint-5.5
#-------------------------------------------------------------------------
# The following tests, savepoint-6.*, test an incr-vacuum inside of a
@@ -342,8 +373,9 @@ ifcapable {autovacuum && pragma} {
sqlite3 db test.db
do_test savepoint-6.1 {
execsql {
PRAGMA auto_vacuum = incremental;
execsql { PRAGMA auto_vacuum = incremental }
wal_set_journal_mode
execsql {
CREATE TABLE t1(a, b, c);
CREATE INDEX i1 ON t1(a, b);
BEGIN;
@@ -376,6 +408,8 @@ ifcapable {autovacuum && pragma} {
} {}
integrity_check savepoint-6.4
wal_check_journal_mode savepoint-6.5
}
#-------------------------------------------------------------------------
@@ -387,8 +421,9 @@ file delete -force test.db
sqlite3 db test.db
do_test savepoint-7.1 {
execsql { PRAGMA auto_vacuum = incremental }
wal_set_journal_mode
execsql {
PRAGMA auto_vacuum = incremental;
PRAGMA cache_size = 10;
BEGIN;
CREATE TABLE t1(a PRIMARY KEY, b);
@@ -449,13 +484,15 @@ do_test savepoint-7.3.2 {
}
execsql { PRAGMA integrity_check }
} {ok}
wal_check_journal_mode savepoint-7.3.3
do_test savepoint-7.4.1 {
db close
file delete -force test.db
sqlite3 db test.db
execsql { PRAGMA auto_vacuum = incremental }
wal_set_journal_mode
execsql {
PRAGMA auto_vacuum = incremental;
CREATE TABLE t1(a, b, PRIMARY KEY(a, b));
INSERT INTO t1 VALUES(randstr(1000,1000), randstr(1000,1000));
BEGIN;
@@ -497,6 +534,7 @@ do_test savepoint-7.5.2 {
DROP TABLE t5;
}
} {}
wal_check_journal_mode savepoint-7.5.3
# Test oddly named and quoted savepoints.
#
@@ -598,120 +636,122 @@ do_test savepoint-10.1.3 {
set templockstate [lindex [db eval {PRAGMA lock_status}] 3]
do_test savepoint-10.2.1 {
file delete -force test3.db
file delete -force test2.db
execsql {
ATTACH 'test2.db' AS aux1;
ATTACH 'test3.db' AS aux2;
DROP TABLE t1;
CREATE TABLE main.t1(x, y);
CREATE TABLE aux1.t2(x, y);
CREATE TABLE aux2.t3(x, y);
SELECT name FROM sqlite_master
UNION ALL
SELECT name FROM aux1.sqlite_master
UNION ALL
SELECT name FROM aux2.sqlite_master;
}
} {t1 t2 t3}
do_test savepoint-10.2.2 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.3 {
execsql {
SAVEPOINT one;
INSERT INTO t1 VALUES(1, 2);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.4 {
execsql {
INSERT INTO t3 VALUES(3, 4);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 unlocked aux2 reserved]
do_test savepoint-10.2.5 {
execsql {
SAVEPOINT two;
INSERT INTO t2 VALUES(5, 6);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 reserved aux2 reserved]
do_test savepoint-10.2.6 {
execsql { SELECT * FROM t2 }
} {5 6}
do_test savepoint-10.2.7 {
execsql { ROLLBACK TO two }
execsql { SELECT * FROM t2 }
} {}
do_test savepoint-10.2.8 {
execsql { PRAGMA lock_status }
} [list main reserved temp $templockstate aux1 reserved aux2 reserved]
do_test savepoint-10.2.9 {
execsql { SELECT 'a', * FROM t1 UNION ALL SELECT 'b', * FROM t3 }
} {a 1 2 b 3 4}
do_test savepoint-10.2.9 {
execsql {
INSERT INTO t2 VALUES(5, 6);
RELEASE one;
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 5 6 3 4}
do_test savepoint-10.2.9 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.10 {
execsql {
SAVEPOINT one;
INSERT INTO t1 VALUES('a', 'b');
if {[wal_is_wal_mode]==0} {
do_test savepoint-10.2.1 {
file delete -force test3.db
file delete -force test2.db
execsql {
ATTACH 'test2.db' AS aux1;
ATTACH 'test3.db' AS aux2;
DROP TABLE t1;
CREATE TABLE main.t1(x, y);
CREATE TABLE aux1.t2(x, y);
CREATE TABLE aux2.t3(x, y);
SELECT name FROM sqlite_master
UNION ALL
SELECT name FROM aux1.sqlite_master
UNION ALL
SELECT name FROM aux2.sqlite_master;
}
} {t1 t2 t3}
do_test savepoint-10.2.2 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.3 {
execsql {
SAVEPOINT one;
INSERT INTO t1 VALUES(1, 2);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.4 {
execsql {
INSERT INTO t3 VALUES(3, 4);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 unlocked aux2 reserved]
do_test savepoint-10.2.5 {
execsql {
SAVEPOINT two;
INSERT INTO t2 VALUES('c', 'd');
SAVEPOINT three;
INSERT INTO t3 VALUES('e', 'f');
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 c d 3 4 e f}
do_test savepoint-10.2.11 {
execsql { ROLLBACK TO two }
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 3 4}
do_test savepoint-10.2.12 {
execsql {
INSERT INTO t3 VALUES('g', 'h');
ROLLBACK TO two;
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 3 4}
do_test savepoint-10.2.13 {
execsql { ROLLBACK }
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 5 6 3 4}
do_test savepoint-10.2.14 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
INSERT INTO t2 VALUES(5, 6);
PRAGMA lock_status;
}
} [list main reserved temp $templockstate aux1 reserved aux2 reserved]
do_test savepoint-10.2.6 {
execsql { SELECT * FROM t2 }
} {5 6}
do_test savepoint-10.2.7 {
execsql { ROLLBACK TO two }
execsql { SELECT * FROM t2 }
} {}
do_test savepoint-10.2.8 {
execsql { PRAGMA lock_status }
} [list main reserved temp $templockstate aux1 reserved aux2 reserved]
do_test savepoint-10.2.9 {
execsql { SELECT 'a', * FROM t1 UNION ALL SELECT 'b', * FROM t3 }
} {a 1 2 b 3 4}
do_test savepoint-10.2.9 {
execsql {
INSERT INTO t2 VALUES(5, 6);
RELEASE one;
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 5 6 3 4}
do_test savepoint-10.2.9 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
do_test savepoint-10.2.10 {
execsql {
SAVEPOINT one;
INSERT INTO t1 VALUES('a', 'b');
SAVEPOINT two;
INSERT INTO t2 VALUES('c', 'd');
SAVEPOINT three;
INSERT INTO t3 VALUES('e', 'f');
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 c d 3 4 e f}
do_test savepoint-10.2.11 {
execsql { ROLLBACK TO two }
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 3 4}
do_test savepoint-10.2.12 {
execsql {
INSERT INTO t3 VALUES('g', 'h');
ROLLBACK TO two;
}
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 a b 5 6 3 4}
do_test savepoint-10.2.13 {
execsql { ROLLBACK }
execsql {
SELECT * FROM t1;
SELECT * FROM t2;
SELECT * FROM t3;
}
} {1 2 5 6 3 4}
do_test savepoint-10.2.14 {
execsql { PRAGMA lock_status }
} [list main unlocked temp $templockstate aux1 unlocked aux2 unlocked]
}
#-------------------------------------------------------------------------
# The following tests - savepoint-11.* - test the interaction of
@@ -722,8 +762,9 @@ do_test savepoint-11.1 {
db close
file delete -force test.db
sqlite3 db test.db
execsql { PRAGMA auto_vacuum = full; }
wal_set_journal_mode
execsql {
PRAGMA auto_vacuum = full;
CREATE TABLE t1(a, b, UNIQUE(a, b));
INSERT INTO t1 VALUES(1, randstr(1000,1000));
INSERT INTO t1 VALUES(2, randstr(1000,1000));
@@ -751,10 +792,10 @@ do_test savepoint-11.6 {
integrity_check savepoint-11.7
do_test savepoint-11.8 {
execsql { ROLLBACK }
execsql { PRAGMA wal_checkpoint }
file size test.db
} {8192}
do_test savepoint-11.9 {
execsql {
DROP TABLE IF EXISTS t1;
@@ -782,6 +823,7 @@ do_test savepoint-11.11 {
do_test savepoint-11.12 {
execsql {SELECT * FROM t2}
} {1 2 3 4}
wal_check_journal_mode savepoint-11.13
#-------------------------------------------------------------------------
# The following tests - savepoint-12.* - test the interaction of
@@ -815,50 +857,53 @@ do_test savepoint-12.3 {
do_test savepoint-12.4 {
execsql { SAVEPOINT one }
} {}
wal_check_journal_mode savepoint-12.5
#-------------------------------------------------------------------------
# The following tests - savepoint-13.* - test the interaction of
# savepoints and "journal_mode = off".
#
do_test savepoint-13.1 {
db close
catch {file delete -force test.db}
sqlite3 db test.db
execsql {
BEGIN;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
PRAGMA journal_mode = off;
}
} {off}
do_test savepoint-13.2 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 SELECT a+4,b+4 FROM t1;
COMMIT;
}
} {}
do_test savepoint-13.3 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(9, 10);
SAVEPOINT s1;
INSERT INTO t1 VALUES(11, 12);
COMMIT;
}
} {}
do_test savepoint-13.4 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(13, 14);
SAVEPOINT s1;
INSERT INTO t1 VALUES(15, 16);
ROLLBACK TO s1;
ROLLBACK;
SELECT * FROM t1;
}
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
if {[wal_is_wal_mode]==0} {
do_test savepoint-13.1 {
db close
catch {file delete -force test.db}
sqlite3 db test.db
execsql {
BEGIN;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
PRAGMA journal_mode = off;
}
} {off}
do_test savepoint-13.2 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 SELECT a+4,b+4 FROM t1;
COMMIT;
}
} {}
do_test savepoint-13.3 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(9, 10);
SAVEPOINT s1;
INSERT INTO t1 VALUES(11, 12);
COMMIT;
}
} {}
do_test savepoint-13.4 {
execsql {
BEGIN;
INSERT INTO t1 VALUES(13, 14);
SAVEPOINT s1;
INSERT INTO t1 VALUES(15, 16);
ROLLBACK TO s1;
ROLLBACK;
SELECT * FROM t1;
}
} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16}
}
finish_test
+6
View File
@@ -14,6 +14,7 @@
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# Tests in this file are quite similar to those run by trans.test and
# avtrans.test.
#
@@ -23,6 +24,7 @@ proc signature {} {
}
do_test savepoint2-1 {
wal_set_journal_mode
execsql {
PRAGMA cache_size=10;
BEGIN;
@@ -42,6 +44,7 @@ do_test savepoint2-1 {
SELECT count(*) FROM t3;
}
} {1024}
wal_check_journal_mode savepoint2-1.1
unset -nocomplain ::sig
unset -nocomplain SQL
@@ -140,6 +143,9 @@ for {set ii 2} {$ii < ($iterations+2)} {incr ii} {
sqlite3_get_autocommit db
} {1}
integrity_check savepoint2-$ii.6.1
# Check that the connection is still running in WAL mode.
wal_check_journal_mode savepoint2-$ii.7
}
unset -nocomplain ::sig
+11 -2
View File
@@ -26,6 +26,10 @@ set DATABASE_SCHEMA {
CREATE INDEX i2 ON t1(y);
}
if {0==[info exists ::savepoint6_iterations]} {
set ::savepoint6_iterations 1000
}
#--------------------------------------------------------------------------
# In memory database state.
#
@@ -222,6 +226,7 @@ foreach zSetup [list {
set testname normal
sqlite3 db test.db
} {
if {[wal_is_wal_mode]} continue
set testname tempdb
sqlite3 db ""
} {
@@ -241,10 +246,12 @@ foreach zSetup [list {
unset -nocomplain ::aEntry
catch { db close }
file delete -force test.db
file delete -force test.db test.db-wal test.db-journal
eval $zSetup
sql $DATABASE_SCHEMA
wal_set_journal_mode
do_test savepoint6-$testname.setup {
savepoint one
insert_rows [random_integers 100 1000]
@@ -252,7 +259,7 @@ foreach zSetup [list {
checkdb
} {ok}
for {set i 0} {$i < 1000} {incr i} {
for {set i 0} {$i < $::savepoint6_iterations} {incr i} {
do_test savepoint6-$testname.$i.1 {
savepoint_op
checkdb
@@ -264,6 +271,8 @@ foreach zSetup [list {
checkdb
} {ok}
}
wal_check_journal_mode savepoint6-$testname.walok
}
unset -nocomplain ::lSavepoint
+1 -1
View File
@@ -35,7 +35,7 @@ do_test tcl-1.1 {
do_test tcl-1.2 {
set v [catch {db bogus} msg]
lappend v $msg
} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, or version}}
} {1 {bad option "bogus": must be authorizer, backup, busy, cache, changes, close, collate, collation_needed, commit_hook, complete, copy, enable_load_extension, errorcode, eval, exists, function, incrblob, interrupt, last_insert_rowid, nullvalue, onecolumn, profile, progress, rekey, restore, rollback_hook, status, timeout, total_changes, trace, transaction, unlock_notify, update_hook, version, or wal_hook}}
do_test tcl-1.2.1 {
set v [catch {db cache bogus} msg]
lappend v $msg
+37
View File
@@ -143,6 +143,7 @@ proc reset_db {} {
catch {db close}
file delete -force test.db
file delete -force test.db-journal
file delete -force test.db-wal
sqlite3 db ./test.db
set ::DB [sqlite3_connection_pointer db]
if {[info exists ::SETUP_SQL]} {
@@ -982,6 +983,42 @@ proc drop_all_tables {{db db}} {
}
}
#-------------------------------------------------------------------------
# If a test script is executed with global variable
# $::permutations_test_prefix set to "wal", then the tests are run
# in WAL mode. Otherwise, they should be run in rollback mode. The
# following Tcl procs are used to make this less intrusive:
#
# wal_set_journal_mode ?DB?
#
# If running a WAL test, execute "PRAGMA journal_mode = wal" using
# connection handle DB. Otherwise, this command is a no-op.
#
# wal_check_journal_mode TESTNAME ?DB?
#
# If running a WAL test, execute a tests case that fails if the main
# database for connection handle DB is not currently a WAL database.
# Otherwise (if not running a WAL permutation) this is a no-op.
#
# wal_is_wal_mode
#
# Returns true if this test should be run in WAL mode. False otherwise.
#
proc wal_is_wal_mode {} {
expr { [catch {set ::permutations_test_prefix} v]==0 && $v == "wal" }
}
proc wal_set_journal_mode {{db db}} {
if { [wal_is_wal_mode] } {
$db eval "PRAGMA journal_mode = WAL"
}
}
proc wal_check_journal_mode {testname {db db}} {
if { [wal_is_wal_mode] } {
$db eval { SELECT * FROM sqlite_master }
do_test $testname [list $db eval "PRAGMA main.journal_mode"] {wal}
}
}
# If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
# to non-zero, then set the global variable $AUTOVACUUM to 1.
+1 -1
View File
@@ -80,7 +80,7 @@ set thread_procs {
}
proc thread_spawn {varname args} {
sqlthread spawn $varname [join $args ;]
sqlthread spawn $varname [join $args {;}]
}
# Return true if this build can run the multi-threaded tests.
+11 -1
View File
@@ -19,6 +19,7 @@ source $testdir/tester.tcl
# Create several tables to work with.
#
wal_set_journal_mode
do_test trans-1.0 {
execsql {
CREATE TABLE one(a int PRIMARY KEY, b text);
@@ -46,6 +47,7 @@ do_test trans-1.10 {
execsql {SELECT b FROM two ORDER BY a} altdb
} {I V X}
integrity_check trans-1.11
wal_check_journal_mode trans-1.12
# Basic transactions
#
@@ -82,6 +84,7 @@ do_test trans-2.10 {
}
} {1 2 3 1 5 10}
integrity_check trans-2.11
wal_check_journal_mode trans-2.12
# Check the locking behavior
#
@@ -162,6 +165,7 @@ do_test trans-3.14 {
lappend v $msg
} {0 {1 2 3 4}}
integrity_check trans-3.15
wal_check_journal_mode trans-3.16
do_test trans-4.1 {
set v [catch {execsql {
@@ -228,6 +232,8 @@ do_test trans-4.11 {
lappend v $msg
} {0 {1 2 3 4}}
integrity_check trans-4.12
wal_check_journal_mode trans-4.13
wal_check_journal_mode trans-4.14 altdb
do_test trans-4.98 {
altdb close
execsql {
@@ -775,6 +781,7 @@ do_test trans-7.14 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
integrity_check trans-7.15
wal_check_journal_mode trans-7.16
# Arrange for another process to begin modifying the database but abort
# and die in the middle of the modification. Then have this process read
@@ -824,7 +831,7 @@ do_test trans-8.5 {
execsql {SELECT md5sum(type,name,tbl_name,rootpage,sql) FROM sqlite_master}
} $checksum2
integrity_check trans-8.6
wal_check_journal_mode trans-8.7
# In the following sequence of tests, compute the MD5 sum of the content
# of a table, make lots of modifications to that table, then do a rollback.
@@ -854,6 +861,7 @@ do_test trans-9.1 {
SELECT count(*) FROM t3;
}
} {1024}
wal_check_journal_mode trans-9.1.1
# The following procedure computes a "signature" for table "t3". If
# T3 changes in any way, the signature should change.
@@ -940,6 +948,8 @@ for {set i 2} {$i<=$limit} {incr i} {
}
}
}
wal_check_journal_mode trans-9.$i.6-$cnt
set ::pager_old_format 0
}
+1053
View File
File diff suppressed because it is too large Load Diff
+186
View File
@@ -0,0 +1,186 @@
# 2010 April 22
#
# 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. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
proc log_file_size {nFrame pgsz} {
expr {12 + ($pgsz+16)*$nFrame}
}
# Test organization:
#
# walback-1.*: Simple tests.
# walback-2.*: Test backups when the source db is modified mid-backup.
#
# Make sure a simple backup from a WAL database works.
#
do_test walbak-1.0 {
execsql {
PRAGMA synchronous = NORMAL;
PRAGMA page_size = 1024;
PRAGMA auto_vacuum = 0;
PRAGMA journal_mode = wal;
BEGIN;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES('I', 'one');
COMMIT;
}
} {wal}
do_test walbak-1.1 {
file delete -force bak.db bak.db-journal bak.db-wal
db backup bak.db
file size bak.db
} [expr 3*1024]
do_test walbak-1.2 {
sqlite3 db2 bak.db
execsql {
SELECT * FROM t1;
PRAGMA main.journal_mode;
} db2
} {I one wal}
do_test walbak-1.3 {
execsql { PRAGMA integrity_check } db2
} {ok}
db2 close
# Try a VACUUM on a WAL database.
#
do_test walbak-1.4 {
execsql {
VACUUM;
PRAGMA main.journal_mode;
}
} {wal}
do_test walbak-1.5 {
list [file size test.db] [file size test.db-wal]
} [list 1024 [log_file_size 6 1024]]
do_test walbak-1.6 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 6 1024]]
do_test walbak-1.7 {
execsql {
CREATE TABLE t2(a, b);
INSERT INTO t2 SELECT * FROM t1;
DROP TABLE t1;
}
list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 6 1024]]
do_test walbak-1.8 {
execsql { VACUUM }
list [file size test.db] [file size test.db-wal]
} [list [expr 3*1024] [log_file_size 8 1024]]
do_test walbak-1.9 {
execsql { PRAGMA wal_checkpoint }
list [file size test.db] [file size test.db-wal]
} [list [expr 2*1024] [log_file_size 8 1024]]
#-------------------------------------------------------------------------
# Backups when the source db is modified mid-backup.
#
proc sig {{db db}} {
$db eval {
PRAGMA integrity_check;
SELECT md5sum(a, b) FROM t1;
}
}
db close
file delete test.db
sqlite3 db test.db
do_test walbak-2.1 {
execsql { PRAGMA journal_mode = WAL }
execsql {
CREATE TABLE t1(a PRIMARY KEY, b);
BEGIN;
INSERT INTO t1 VALUES(randomblob(500), randomblob(500));
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 2 */
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 4 */
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 8 */
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 16 */
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 32 */
INSERT INTO t1 SELECT randomblob(500), randomblob(500) FROM t1; /* 64 */
COMMIT;
}
} {}
do_test walbak-2.2 {
db backup abc.db
sqlite3 db2 abc.db
string compare [sig db] [sig db2]
} {0}
do_test walbak-2.3 {
sqlite3_backup B db2 main db main
B step 50
execsql { UPDATE t1 SET b = randomblob(500) }
list [B step 1000] [B finish]
} {SQLITE_DONE SQLITE_OK}
do_test walbak-2.4 {
string compare [sig db] [sig db2]
} {0}
do_test walbak-2.5 {
db close
sqlite3 db test.db
execsql { PRAGMA cache_size = 10 }
sqlite3_backup B db2 main db main
B step 50
execsql {
BEGIN;
UPDATE t1 SET b = randomblob(500);
}
expr [file size test.db-wal] > 10*1024
} {1}
do_test walbak-2.6 {
B step 1000
} {SQLITE_BUSY}
do_test walbak-2.7 {
execsql COMMIT
list [B step 1000] [B finish]
} {SQLITE_DONE SQLITE_OK}
do_test walbak-2.8 {
string compare [sig db] [sig db2]
} {0}
do_test walbak-2.9 {
db close
sqlite3 db test.db
execsql { PRAGMA cache_size = 10 }
sqlite3_backup B db2 main db main
B step 50
execsql {
BEGIN;
UPDATE t1 SET b = randomblob(500);
}
expr [file size test.db-wal] > 10*1024
} {1}
do_test walbak-2.10 {
B step 1000
} {SQLITE_BUSY}
do_test walbak-2.11 {
execsql ROLLBACK
set sigB [sig db]
list [B step 1000] [B finish]
} {SQLITE_DONE SQLITE_OK}
do_test walbak-2.12 {
string compare [sig db] [sig db2]
} {0}
db2 close
finish_test
+280
View File
@@ -0,0 +1,280 @@
# 2010 February 8
#
# 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. The
# focus of this file is testing the operation of the library when
# recovering a database following a simulated system failure in
# "PRAGMA journal_mode=WAL" mode.
#
#
# These are 'warm-body' tests of database recovery used while developing
# the WAL code. They serve to prove that a few really simple cases work:
#
# walcrash-1.*: Recover a database.
# walcrash-2.*: Recover a database where the failed transaction spanned more
# than one page.
# walcrash-3.*: Recover multiple databases where the failed transaction
# was a multi-file transaction.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
db close
set seed 0
set REPEATS 100
# walcrash-1.*
#
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
do_test walcrash-1.$i.1 {
crashsql -delay 4 -file test.db-wal -seed [incr seed] {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 1);
INSERT INTO t1 VALUES(2, 3);
INSERT INTO t1 VALUES(3, 6);
}
} {1 {child process exited abnormally}}
do_test walcrash-1.$i.2 {
sqlite3 db test.db
execsql { SELECT sum(a)==max(b) FROM t1 }
} {1}
integrity_check walcrash-1.$i.3
db close
do_test walcrash-1.$i.4 {
crashsql -delay 2 -file test.db-wal -seed [incr seed] {
INSERT INTO t1 VALUES(4, (SELECT sum(a) FROM t1) + 4);
INSERT INTO t1 VALUES(5, (SELECT sum(a) FROM t1) + 5);
}
} {1 {child process exited abnormally}}
do_test walcrash-1.$i.5 {
sqlite3 db test.db
execsql { SELECT sum(a)==max(b) FROM t1 }
} {1}
integrity_check walcrash-1.$i.6
do_test walcrash-1.$i.5 {
execsql { PRAGMA main.journal_mode }
} {wal}
db close
}
# walcrash-2.*
#
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
do_test walcrash-2.$i.1 {
crashsql -delay 4 -file test.db-wal -seed [incr seed] {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
INSERT INTO t1 VALUES(5, 9);
}
} {1 {child process exited abnormally}}
do_test walcrash-2.$i.2 {
sqlite3 db test.db
execsql { SELECT sum(a)==max(b) FROM t1 }
} {1}
integrity_check walcrash-2.$i.3
db close
do_test walcrash-2.$i.4 {
crashsql -delay 2 -file test.db-wal -seed [incr seed] {
INSERT INTO t1 VALUES(6, (SELECT sum(a) FROM t1) + 6);
INSERT INTO t1 VALUES(7, (SELECT sum(a) FROM t1) + 7);
}
} {1 {child process exited abnormally}}
do_test walcrash-2.$i.5 {
sqlite3 db test.db
execsql { SELECT sum(a)==max(b) FROM t1 }
} {1}
integrity_check walcrash-2.$i.6
do_test walcrash-2.$i.6 {
execsql { PRAGMA main.journal_mode }
} {wal}
db close
}
# walcrash-3.*
#
# for {set i 1} {$i < $REPEATS} {incr i} {
# file delete -force test.db test.db-wal
# file delete -force test2.db test2.db-wal
#
# do_test walcrash-3.$i.1 {
# crashsql -delay 2 -file test2.db-wal -seed [incr seed] {
# PRAGMA journal_mode = WAL;
# ATTACH 'test2.db' AS aux;
# CREATE TABLE t1(a PRIMARY KEY, b);
# CREATE TABLE aux.t2(a PRIMARY KEY, b);
# BEGIN;
# INSERT INTO t1 VALUES(1, 2);
# INSERT INTO t2 VALUES(1, 2);
# COMMIT;
# }
# } {1 {child process exited abnormally}}
#
# do_test walcrash-3.$i.2 {
# sqlite3_wal db test.db
# execsql {
# ATTACH 'test2.db' AS aux;
# SELECT * FROM t1 EXCEPT SELECT * FROM t2;
# }
# } {}
# do_test walcrash-3.$i.3 { execsql { PRAGMA main.integrity_check } } {ok}
# do_test walcrash-3.$i.4 { execsql { PRAGMA aux.integrity_check } } {ok}
#
# db close
# }
# walcrash-4.*
#
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
file delete -force test2.db test2.db-wal
do_test walcrash-4.$i.1 {
crashsql -delay 3 -file test.db-wal -seed [incr seed] -blocksize 4096 {
PRAGMA journal_mode = WAL;
PRAGMA page_size = 1024;
CREATE TABLE t1(a PRIMARY KEY, b);
INSERT INTO t1 VALUES(1, 2);
INSERT INTO t1 VALUES(3, 4);
}
} {1 {child process exited abnormally}}
do_test walcrash-4.$i.2 {
sqlite3 db test.db
execsql {
SELECT * FROM t1 WHERE a = 1;
}
} {1 2}
do_test walcrash-4.$i.3 { execsql { PRAGMA main.integrity_check } } {ok}
do_test walcrash-4.$i.4 { execsql { PRAGMA main.journal_mode } } {wal}
db close
}
# walcrash-5.*
#
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
file delete -force test2.db test2.db-wal
do_test walcrash-5.$i.1 {
crashsql -delay 11 -file test.db-wal -seed [incr seed] -blocksize 4096 {
PRAGMA journal_mode = WAL;
PRAGMA page_size = 1024;
BEGIN;
CREATE TABLE t1(x PRIMARY KEY);
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */
COMMIT;
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 8 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 12 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 16 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 20 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 24 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 28 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 32 */
PRAGMA wal_checkpoint;
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
}
} {1 {child process exited abnormally}}
do_test walcrash-5.$i.2 {
sqlite3 db test.db
execsql { SELECT count(*)==33 OR count(*)==34 FROM t1 WHERE x != 1 }
} {1}
do_test walcrash-5.$i.3 { execsql { PRAGMA main.integrity_check } } {ok}
do_test walcrash-5.$i.4 { execsql { PRAGMA main.journal_mode } } {wal}
db close
}
# walcrash-6.*
#
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
file delete -force test2.db test2.db-wal
do_test walcrash-6.$i.1 {
crashsql -delay 12 -file test.db-wal -seed [incr seed] -blocksize 512 {
PRAGMA journal_mode = WAL;
PRAGMA page_size = 1024;
BEGIN;
CREATE TABLE t1(x PRIMARY KEY);
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */
COMMIT;
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 8 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 12 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 16 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 20 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 24 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 28 */
INSERT INTO t1 SELECT randomblob(900) FROM t1 LIMIT 4; /* 32 */
PRAGMA wal_checkpoint;
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 VALUES(randomblob(900));
}
} {1 {child process exited abnormally}}
do_test walcrash-6.$i.2 {
sqlite3 db test.db
execsql { SELECT count(*)==34 OR count(*)==35 FROM t1 WHERE x != 1 }
} {1}
do_test walcrash-6.$i.3 { execsql { PRAGMA main.integrity_check } } {ok}
do_test walcrash-6.$i.4 { execsql { PRAGMA main.journal_mode } } {wal}
db close
}
for {set i 1} {$i < $REPEATS} {incr i} {
file delete -force test.db test.db-wal
do_test walcrash-7.$i.1 {
crashsql -delay 3 -file test.db -seed [incr seed] -blocksize 512 {
PRAGMA journal_mode = wal;
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
PRAGMA wal_checkpoint;
CREATE INDEX i1 ON t1(a);
PRAGMA wal_checkpoint;
}
} {1 {child process exited abnormally}}
do_test walcrash-7.$i.2 {
sqlite3 db test.db
execsql { SELECT b FROM t1 WHERE a = 1 }
} {2}
do_test walcrash-7.$i.3 { execsql { PRAGMA main.integrity_check } } {ok}
do_test walcrash-7.$i.4 { execsql { PRAGMA main.journal_mode } } {wal}
db close
}
finish_test
+111
View File
@@ -0,0 +1,111 @@
# 2010 April 19
#
# 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. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#
# More specifically, this file contains regression tests for the
# sqlite3_wal_hook() mechanism, including the sqlite3_wal_autocheckpoint()
# and "PRAGMA wal_autocheckpoint" convenience interfaces.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
proc log_file_size {nFrame pgsz} {
expr {12 + ($pgsz+16)*$nFrame}
}
set ::wal_hook [list]
proc wal_hook {zDb nEntry} {
lappend ::wal_hook $zDb $nEntry
return 0
}
db wal_hook wal_hook
do_test walhook-1.1 {
execsql {
PRAGMA page_size = 1024;
PRAGMA journal_mode = wal;
PRAGMA synchronous = normal;
CREATE TABLE t1(i PRIMARY KEY, j);
}
set ::wal_hook
} {main 3}
do_test walhook-1.2 {
set ::wal_hook [list]
execsql { INSERT INTO t1 VALUES(1, 'one') }
set ::wal_hook
} {main 5}
do_test walhook-1.3 {
proc wal_hook {args} { return 1 }
execsql { INSERT INTO t1 VALUES(2, 'two') }
file size test.db
} [expr 3*1024]
do_test walhook-1.4 {
proc wal_hook {zDb nEntry} {
execsql { PRAGMA wal_checkpoint }
return 0
}
execsql { CREATE TABLE t2(a, b) }
file size test.db
} [expr 4*1024]
do_test walhook-1.5 {
sqlite3 db2 test.db
proc wal_hook {zDb nEntry} {
execsql { PRAGMA wal_checkpoint } db2
return 0
}
execsql { CREATE TABLE t3(a PRIMARY KEY, b) }
file size test.db
} [expr 6*1024]
db2 close
db close
sqlite3 db test.db
do_test walhook-2.1 {
execsql { PRAGMA synchronous = NORMAL }
execsql { PRAGMA wal_autocheckpoint }
} {1000}
do_test walhook-2.2 {
execsql { PRAGMA wal_autocheckpoint = 10}
} {10}
do_test walhook-2.3 {
execsql { PRAGMA wal_autocheckpoint }
} {10}
#
# The database connection is configured with "PRAGMA wal_autocheckpoint = 10".
# Check that transactions are written to the log file until it contains at
# least 10 frames, then the database is checkpointed. Subsequent transactions
# are written into the start of the log file.
#
foreach {tn sql dbpages logpages} {
4 "CREATE TABLE t4(x PRIMARY KEY, y)" 6 3
5 "INSERT INTO t4 VALUES(1, 'one')" 6 5
6 "INSERT INTO t4 VALUES(2, 'two')" 6 7
7 "INSERT INTO t4 VALUES(3, 'three')" 6 9
8 "INSERT INTO t4 VALUES(4, 'four')" 8 11
9 "INSERT INTO t4 VALUES(5, 'five')" 8 11
} {
do_test walhook-2.$tn {
execsql $sql
list [file size test.db] [file size test.db-wal]
} [list [expr $dbpages*1024] [log_file_size $logpages 1024]]
}
catch { db2 close }
catch { db close }
finish_test
+261
View File
@@ -0,0 +1,261 @@
# 2010 April 19
#
# 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. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
# If the library was compiled without WAL support, check that the
# "PRAGMA journal_mode=WAL" treats "WAL" as an unrecognized mode.
#
ifcapable !wal {
do_test walmode-0.1 {
execsql { PRAGMA journal_mode = wal }
} {delete}
do_test walmode-0.2 {
execsql { PRAGMA main.journal_mode = wal }
} {delete}
do_test walmode-0.3 {
execsql { PRAGMA main.journal_mode }
} {delete}
finish_test
return
}
do_test walmode-1.1 {
set sqlite_sync_count 0
execsql { PRAGMA page_size = 1024 }
execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-1.2 {
file size test.db
} {1024}
do_test walmode-1.3 {
set sqlite_sync_count
} {4}
do_test walmode-1.4 {
file exists test.db-wal
} {0}
do_test walmode-1.5 {
execsql { CREATE TABLE t1(a, b) }
file size test.db
} {1024}
do_test walmode-1.6 {
file exists test.db-wal
} {1}
do_test walmode-1.7 {
db close
file exists test.db-wal
} {0}
# There is now a database file with the read and write versions set to 2
# in the file system. This file should default to WAL mode.
#
do_test walmode-2.1 {
sqlite3 db test.db
file exists test.db-wal
} {0}
do_test walmode-2.2 {
execsql { SELECT * FROM sqlite_master }
file exists test.db-wal
} {1}
do_test walmode-2.3 {
db close
file exists test.db-wal
} {0}
# If the first statement executed is "PRAGMA journal_mode = wal", and
# the file is already configured for WAL (read and write versions set
# to 2), then there should be no need to write the database. The
# statement should cause the client to connect to the log file.
#
set sqlite_sync_count 0
do_test walmode-3.1 {
sqlite3 db test.db
execsql { PRAGMA journal_mode = wal }
} {wal}
do_test walmode-3.2 {
list $sqlite_sync_count [file exists test.db-wal] [file size test.db-wal]
} {0 1 0}
# Test that changing back to journal_mode=persist works.
#
do_test walmode-4.1 {
execsql { INSERT INTO t1 VALUES(1, 2) }
execsql { PRAGMA journal_mode = persist }
} {persist}
do_test walmode-4.2 {
list [file exists test.db-journal] [file exists test.db-wal]
} {1 0}
do_test walmode-4.3 {
execsql { SELECT * FROM t1 }
} {1 2}
do_test walmode-4.4 {
db close
sqlite3 db test.db
execsql { SELECT * FROM t1 }
} {1 2}
do_test walmode-4.5 {
list [file exists test.db-journal] [file exists test.db-wal]
} {1 0}
# Test that nothing goes wrong if a connection is prevented from changing
# from WAL to rollback mode because a second connection has the database
# open. Or from rollback to WAL.
#
do_test walmode-4.1 {
sqlite3 db2 test.db
execsql { PRAGMA main.journal_mode } db2
} {delete}
do_test walmode-4.2 {
execsql { PRAGMA main.journal_mode = wal } db
} {wal}
do_test walmode-4.3 {
execsql { SELECT * FROM t1 } db2
} {1 2}
do_test walmode-4.4 {
catchsql { PRAGMA journal_mode = delete } db
} {1 {database is locked}}
do_test walmode-4.5 {
execsql { PRAGMA main.journal_mode } db
} {wal}
do_test walmode-4.6 {
db2 close
execsql { PRAGMA journal_mode = delete } db
} {delete}
do_test walmode-4.7 {
execsql { PRAGMA main.journal_mode } db
} {delete}
do_test walmode-4.8 {
list [file exists test.db-journal] [file exists test.db-wal]
} {0 0}
do_test walmode-4.9 {
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 {
catchsql { PRAGMA main.journal_mode = wal } db
} {1 {database is locked}}
do_test walmode-4.11 {
execsql { PRAGMA main.journal_mode } db
} {delete}
catch { db close }
catch { db2 close }
# Test that it is not possible to change a temporary or in-memory database
# to WAL mode. WAL mode is for persistent file-backed databases only.
#
# walmode-5.1.*: Try to set journal_mode=WAL on [sqlite3 db :memory:] database.
# walmode-5.2.*: Try to set journal_mode=WAL on [sqlite3 db ""] database.
# walmode-5.3.*: Try to set temp.journal_mode=WAL.
#
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}
do_test walmode-5.1.3 {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
SELECT * FROM t1;
PRAGMA main.journal_mode;
}
} {1 2 memory}
do_test walmode-5.1.4 {
execsql { PRAGMA main.journal_mode = wal }
} {memory}
do_test walmode-5.1.5 {
execsql {
INSERT INTO t1 VALUES(3, 4);
SELECT * FROM t1;
PRAGMA main.journal_mode;
}
} {1 2 3 4 memory}
do_test walmode-5.2.1 {
sqlite3 db ""
execsql { PRAGMA main.journal_mode }
} {delete}
do_test walmode-5.2.2 {
execsql { PRAGMA main.journal_mode = wal }
} {delete}
do_test walmode-5.2.3 {
execsql {
BEGIN;
CREATE TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
SELECT * FROM t1;
PRAGMA main.journal_mode;
}
} {1 2 delete}
do_test walmode-5.2.4 {
execsql { PRAGMA main.journal_mode = wal }
} {delete}
do_test walmode-5.2.5 {
execsql {
INSERT INTO t1 VALUES(3, 4);
SELECT * FROM t1;
PRAGMA main.journal_mode;
}
} {1 2 3 4 delete}
if {$TEMP_STORE>=2} {
set tempJrnlMode memory
} else {
set tempJrnlMode delete
}
do_test walmode-5.3.1 {
sqlite3 db test.db
execsql { PRAGMA temp.journal_mode }
} $tempJrnlMode
do_test walmode-5.3.2 {
execsql { PRAGMA temp.journal_mode = wal }
} $tempJrnlMode
do_test walmode-5.3.3 {
execsql {
BEGIN;
CREATE TEMP TABLE t1(a, b);
INSERT INTO t1 VALUES(1, 2);
COMMIT;
SELECT * FROM t1;
PRAGMA temp.journal_mode;
}
} [list 1 2 $tempJrnlMode]
do_test walmode-5.3.4 {
execsql { PRAGMA temp.journal_mode = wal }
} $tempJrnlMode
do_test walmode-5.3.5 {
execsql {
INSERT INTO t1 VALUES(3, 4);
SELECT * FROM t1;
PRAGMA temp.journal_mode;
}
} [list 1 2 3 4 $tempJrnlMode]
finish_test
+73
View File
@@ -0,0 +1,73 @@
# 2010 March 17
#
# 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. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode. The tests in this file use
# brute force methods, so may take a while to run.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
ifcapable !wal {finish_test ; return }
proc reopen_db {} {
catch { db close }
file delete -force test.db test.db-wal
sqlite3 db test.db
execsql { PRAGMA journal_mode = wal }
}
db close
save_prng_state
for {set seed 1} {$seed<10} {incr seed} {
expr srand($seed)
restore_prng_state
reopen_db
do_test walslow-1.seed=$seed.0 {
execsql { CREATE TABLE t1(a, b) }
execsql { CREATE INDEX i1 ON t1(a) }
execsql { CREATE INDEX i2 ON t1(b) }
} {}
for {set iTest 1} {$iTest < 100} {incr iTest} {
do_test walslow-1.seed=$seed.$iTest.1 {
set w [expr int(rand()*2000)]
set x [expr int(rand()*2000)]
execsql { INSERT INTO t1 VALUES(randomblob($w), randomblob($x)) }
execsql { PRAGMA integrity_check }
} {ok}
do_test walslow-1.seed=$seed.$iTest.2 {
execsql "PRAGMA wal_checkpoint;"
execsql { PRAGMA integrity_check }
} {ok}
do_test walslow-1.seed=$seed.$iTest.3 {
file delete -force testX.db testX.db-wal
file copy test.db testX.db
file copy test.db-wal testX.db-wal
sqlite3 db2 testX.db
execsql { PRAGMA journal_mode = WAL } db2
execsql { PRAGMA integrity_check } db2
} {ok}
do_test walslow-1.seed=$seed.$iTest.4 {
execsql { SELECT count(*) FROM t1 WHERE a!=b } db2
} [execsql { SELECT count(*) FROM t1 WHERE a!=b }]
db2 close
}
}
finish_test
+512
View File
@@ -0,0 +1,512 @@
# 2010 April 13
#
# 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. The
# focus of this file is testing the operation of the library in
# "PRAGMA journal_mode=WAL" mode with multiple threads.
#
set testdir [file dirname $argv0]
source $testdir/tester.tcl
source $testdir/lock_common.tcl
if {[run_thread_tests]==0} { finish_test ; return }
ifcapable !wal { finish_test ; return }
set sqlite_walsummary_mmap_incr 64
# How long, in seconds, to run each test for. If a test is set to run for
# 0 seconds, it is omitted entirely.
#
set seconds(walthread-1) 20
set seconds(walthread-2) 20
set seconds(walthread-3) 20
set seconds(walthread-4) 20
set seconds(walthread-5) 1
# The parameter is the name of a variable in the callers context. The
# variable may or may not exist when this command is invoked.
#
# If the variable does exist, its value is returned. Otherwise, this
# command uses [vwait] to wait until it is set, then returns the value.
# In other words, this is a version of the [set VARNAME] command that
# blocks until a variable exists.
#
proc wait_for_var {varname} {
if {0==[uplevel [list info exists $varname]]} {
uplevel [list vwait $varname]
}
uplevel [list set $varname]
}
# The argument is the name of a list variable in the callers context. The
# first element of the list is removed and returned. For example:
#
# set L {a b c}
# set x [lshift L]
# assert { $x == "a" && $L == "b c" }
#
proc lshift {lvar} {
upvar $lvar L
set ret [lindex $L 0]
set L [lrange $L 1 end]
return $ret
}
#-------------------------------------------------------------------------
# do_thread_test TESTNAME OPTIONS...
#
# where OPTIONS are:
#
# -seconds SECONDS How many seconds to run the test for
# -init SCRIPT Script to run before test.
# -thread NAME COUNT SCRIPT Scripts to run in threads (or processes).
# -processes BOOLEAN True to use processes instead of threads.
# -check SCRIPT Script to run after test.
#
proc do_thread_test {args} {
set A $args
set P(testname) [lshift A]
set P(seconds) 5
set P(init) ""
set P(threads) [list]
set P(processes) 0
set P(check) {
set ic [db eval "PRAGMA integrity_check"]
if {$ic != "ok"} { error $ic }
}
unset -nocomplain ::done
while {[llength $A]>0} {
set a [lshift A]
switch -glob -- $a {
-seconds {
set P(seconds) [lshift A]
}
-init {
set P(init) [lshift A]
}
-processes {
set P(processes) [lshift A]
}
-check {
set P(check) [lshift A]
}
-thread {
set name [lshift A]
set count [lshift A]
set prg [lshift A]
lappend P(threads) [list $name $count $prg]
}
default {
error "Unknown option: $a"
}
}
}
if {$P(seconds) == 0} {
puts "Skipping $P(testname)"
return
}
puts "Running $P(testname) for $P(seconds) seconds..."
catch { db close }
file delete -force test.db test.db-journal test.db-wal
sqlite3 db test.db
eval $P(init)
catch { db close }
foreach T $P(threads) {
set name [lindex $T 0]
set count [lindex $T 1]
set prg [lindex $T 2]
for {set i 1} {$i <= $count} {incr i} {
set vars "
set E(pid) $i
set E(nthread) $count
set E(seconds) $P(seconds)
"
set program [string map [list %TEST% $prg %VARS% $vars] {
%VARS%
proc usleep {ms} {
set ::usleep 0
after $ms {set ::usleep 1}
vwait ::usleep
}
proc integrity_check {{db db}} {
set ic [$db eval {PRAGMA integrity_check}]
if {$ic != "ok"} {error $ic}
}
proc busyhandler {n} { usleep 10 ; return 0 }
sqlite3 db test.db
db busy busyhandler
db eval { SELECT randomblob($E(pid)*5) }
set ::finished 0
after [expr $E(seconds) * 1000] {set ::finished 1}
proc tt_continue {} { update ; expr ($::finished==0) }
set rc [catch { %TEST% } msg]
catch { db close }
list $rc $msg
}]
if {$P(processes)==0} {
sqlthread spawn ::done($name,$i) $program
} else {
testfixture_nb ::done($name,$i) $program
}
}
}
set report " Results:"
foreach T $P(threads) {
set name [lindex $T 0]
set count [lindex $T 1]
set prg [lindex $T 2]
set reslist [list]
for {set i 1} {$i <= $count} {incr i} {
set res [wait_for_var ::done($name,$i)]
lappend reslist [lindex $res 1]
do_test $P(testname).$name.$i [list lindex $res 0] 0
}
append report " $name $reslist"
}
puts $report
sqlite3 db test.db
set res ""
if {[catch $P(check) msg]} { set res $msg }
do_test $P(testname).check [list set {} $res] ""
}
# A wrapper around [do_thread_test] which runs the specified test twice.
# Once using processes, once using threads. This command takes the same
# arguments as [do_thread_test], except specifying the -processes switch
# is illegal.
#
proc do_thread_test2 {args} {
set name [lindex $args 0]
if {[lsearch $args -processes]>=0} { error "bad option: -processes"}
uplevel [lreplace $args 0 0 do_thread_test "$name-threads" -processes 0]
uplevel [lreplace $args 0 0 do_thread_test "$name-processes" -processes 1]
}
#--------------------------------------------------------------------------
# Start 10 threads. Each thread performs both read and write
# transactions. Each read transaction consists of:
#
# 1) Reading the md5sum of all but the last table row,
# 2) Running integrity check.
# 3) Reading the value stored in the last table row,
# 4) Check that the values read in steps 1 and 3 are the same, and that
# the md5sum of all but the last table row has not changed.
#
# Each write transaction consists of:
#
# 1) Modifying the contents of t1 (inserting, updating, deleting rows).
# 2) Appending a new row to the table containing the md5sum() of all
# rows in the table.
#
# Each of the N threads runs N read transactions followed by a single write
# transaction in a loop as fast as possible.
#
# There is also a single checkpointer thread. It runs the following loop:
#
# 1) Execute "PRAGMA wal_checkpoint"
# 2) Sleep for 500 ms.
#
do_thread_test2 walthread-1 -seconds $seconds(walthread-1) -init {
execsql {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(x PRIMARY KEY);
PRAGMA lock_status;
INSERT INTO t1 VALUES(randomblob(100));
INSERT INTO t1 VALUES(randomblob(100));
INSERT INTO t1 SELECT md5sum(x) FROM t1;
}
} -thread main 10 {
proc read_transaction {} {
set results [db eval {
BEGIN;
PRAGMA integrity_check;
SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1);
SELECT x FROM t1 WHERE rowid = (SELECT max(rowid) FROM t1);
SELECT md5sum(x) FROM t1 WHERE rowid != (SELECT max(rowid) FROM t1);
COMMIT;
}]
if {[llength $results]!=4
|| [lindex $results 0] != "ok"
|| [lindex $results 1] != [lindex $results 2]
|| [lindex $results 2] != [lindex $results 3]
} {
error "Failed read transaction: $results"
}
}
proc write_transaction {} {
db eval {
BEGIN;
INSERT INTO t1 VALUES(randomblob(100));
INSERT INTO t1 VALUES(randomblob(100));
INSERT INTO t1 SELECT md5sum(x) FROM t1;
COMMIT;
}
}
set nRun 0
while {[tt_continue]} {
read_transaction
write_transaction
incr nRun
}
set nRun
} -thread ckpt 1 {
set nRun 0
while {[tt_continue]} {
db eval "PRAGMA wal_checkpoint"
usleep 500
incr nRun
}
set nRun
}
#--------------------------------------------------------------------------
# This test has clients run the following procedure as fast as possible
# in a loop:
#
# 1. Open a database handle.
# 2. Execute a read-only transaction on the db.
# 3. Do "PRAGMA journal_mode = XXX", where XXX is one of WAL or DELETE.
# Ignore any SQLITE_BUSY error.
# 4. Execute a write transaction to insert a row into the db.
# 5. Run "PRAGMA integrity_check"
#
# At present, there are 4 clients in total. 2 do "journal_mode = WAL", and
# two do "journal_mode = DELETE".
#
# Each client returns a string of the form "W w, R r", where W is the
# number of write-transactions performed using a WAL journal, and D is
# the number of write-transactions performed using a rollback journal.
# For example, "192 w, 185 r".
#
do_thread_test2 walthread-2 -seconds $seconds(walthread-2) -init {
execsql { CREATE TABLE t1(x INTEGER PRIMARY KEY, y UNIQUE) }
} -thread RB 2 {
db close
set nRun 0
set nDel 0
while {[tt_continue]} {
sqlite3 db test.db
db busy busyhandler
db eval { SELECT * FROM sqlite_master }
catch { db eval { PRAGMA journal_mode = DELETE } }
db eval {
BEGIN;
INSERT INTO t1 VALUES(NULL, randomblob(100+$E(pid)));
}
incr nRun 1
incr nDel [file exists test.db-journal]
if {[file exists test.db-journal] + [file exists test.db-wal] != 1} {
error "File-system looks bad..."
}
db eval COMMIT
integrity_check
db close
}
list $nRun $nDel
set {} "[expr $nRun-$nDel] w, $nDel r"
} -thread WAL 2 {
db close
set nRun 0
set nDel 0
while {[tt_continue]} {
sqlite3 db test.db
db busy busyhandler
db eval { SELECT * FROM sqlite_master }
catch { db eval { PRAGMA journal_mode = WAL } }
db eval {
BEGIN;
INSERT INTO t1 VALUES(NULL, randomblob(110+$E(pid)));
}
incr nRun 1
incr nDel [file exists test.db-journal]
if {[file exists test.db-journal] + [file exists test.db-wal] != 1} {
error "File-system looks bad..."
}
db eval COMMIT
integrity_check
db close
}
set {} "[expr $nRun-$nDel] w, $nDel r"
}
do_thread_test walthread-3 -seconds $seconds(walthread-3) -init {
execsql {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(cnt PRIMARY KEY, sum1, sum2);
CREATE INDEX i1 ON t1(sum1);
CREATE INDEX i2 ON t1(sum2);
INSERT INTO t1 VALUES(0, 0, 0);
}
} -thread t 10 {
set nextwrite $E(pid)
proc wal_hook {zDb nEntry} {
if {$nEntry>10} { return 1 }
return 0
}
db wal_hook wal_hook
while {[tt_continue]} {
set max 0
while { $max != ($nextwrite-1) && [tt_continue] } {
set max [db eval { SELECT max(cnt) FROM t1 }]
}
if {[tt_continue]} {
set sum1 [db eval { SELECT sum(cnt) FROM t1 }]
set sum2 [db eval { SELECT sum(sum1) FROM t1 }]
db eval { INSERT INTO t1 VALUES($nextwrite, $sum1, $sum2) }
incr nextwrite $E(nthread)
integrity_check
}
}
set {} ok
} -check {
puts " Final db contains [db eval {SELECT count(*) FROM t1}] rows"
puts " Final integrity-check says: [db eval {PRAGMA integrity_check}]"
# Check that the contents of the database are Ok.
set c 0
set s1 0
set s2 0
db eval { SELECT cnt, sum1, sum2 FROM t1 ORDER BY cnt } {
if {$c != $cnt || $s1 != $sum1 || $s2 != $sum2} {
error "database content is invalid"
}
incr s2 $s1
incr s1 $c
incr c 1
}
}
do_thread_test2 walthread-4 -seconds $seconds(walthread-4) -init {
execsql {
PRAGMA journal_mode = WAL;
CREATE TABLE t1(a INTEGER PRIMARY KEY, b UNIQUE);
}
} -thread r 1 {
# This connection only ever reads the database. Therefore the
# busy-handler is not required. Disable it to check that this is true.
db busy {}
while {[tt_continue]} integrity_check
set {} ok
} -thread w 1 {
proc wal_hook {zDb nEntry} {
if {$nEntry>15} { return 1 }
return 0
}
db wal_hook wal_hook
set row 1
while {[tt_continue]} {
db eval { REPLACE INTO t1 VALUES($row, randomblob(300)) }
incr row
if {$row == 10} { set row 1 }
}
set {} ok
}
# This test case attempts to provoke a deadlock condition that existed in
# the unix VFS at one point. The problem occurred only while recovering a
# very large wal file (one that requires a wal-index larger than the
# initial default allocation of 64KB).
#
do_thread_test walthread-5 -seconds $seconds(walthread-5) -init {
proc log_file_size {nFrame pgsz} {
expr {12 + ($pgsz+16)*$nFrame}
}
execsql {
PRAGMA page_size = 1024;
PRAGMA journal_mode = WAL;
CREATE TABLE t1(x);
BEGIN;
INSERT INTO t1 VALUES(randomblob(900));
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 64 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 128 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 256 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 512 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 1024 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 2048 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 4096 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 8192 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 16384 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 32768 */
INSERT INTO t1 SELECT randomblob(900) FROM t1; /* 65536 */
COMMIT;
}
file copy -force test.db-wal bak.db-wal
file copy -force test.db bak.db
db close
file copy -force bak.db-wal test.db-wal
file copy -force bak.db test.db
if {[file size test.db-wal] < [log_file_size [expr 64*1024] 1024]} {
error "Somehow failed to create a large log file"
}
puts "Database with large log file recovered. Now running clients..."
} -thread T 5 {
db eval { SELECT count(*) FROM t1 }
}
finish_test
+2
View File
@@ -109,6 +109,7 @@ foreach hdr {
sqliteLimit.h
vdbe.h
vdbeInt.h
wal.h
} {
set available_hdr($hdr) 1
}
@@ -243,6 +244,7 @@ foreach file {
pcache.c
pcache1.c
rowset.c
wal.c
pager.c
btmutex.c