Merge the write-ahead-logging changes into the trunk.
FossilOrigin-Name: de9ae443cc8eebfb0692dcdac93bde9e4c54199a
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -1 +1 @@
|
||||
946e06cd2a2cc209b0786b7e9d28c0746320d646
|
||||
de9ae443cc8eebfb0692dcdac93bde9e4c54199a
|
||||
+66
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -781,7 +781,7 @@ static int crashEnableCmd(
|
||||
cfDlClose, /* xDlClose */
|
||||
cfRandomness, /* xRandomness */
|
||||
cfSleep, /* xSleep */
|
||||
cfCurrentTime /* xCurrentTime */
|
||||
cfCurrentTime, /* xCurrentTime */
|
||||
};
|
||||
|
||||
if( objc!=2 ){
|
||||
|
||||
@@ -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
@@ -623,7 +623,7 @@ sqlite3_vfs *sqlite3_demovfs(void){
|
||||
demoDlClose, /* xDlClose */
|
||||
demoRandomness, /* xRandomness */
|
||||
demoSleep, /* xSleep */
|
||||
demoCurrentTime /* xCurrentTime */
|
||||
demoCurrentTime, /* xCurrentTime */
|
||||
};
|
||||
return &demovfs;
|
||||
}
|
||||
|
||||
+53
-2
@@ -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
@@ -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
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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
@@ -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 * * * * *
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
#############################################################################
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user