From: drh Date: Wed, 2 Dec 2015 20:53:14 +0000 (+0000) Subject: Merge recent enhancements from trunk. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d9497a0d0ac9a3d8353678137e66f5d8dde9a67;p=thirdparty%2Fsqlite.git Merge recent enhancements from trunk. FossilOrigin-Name: 9130661a786e4c158f15103be57467a5cc03875a --- 1d9497a0d0ac9a3d8353678137e66f5d8dde9a67 diff --cc manifest index f1134c5e0b,123da22f21..018ece16c9 --- a/manifest +++ b/manifest @@@ -1,8 -1,8 +1,8 @@@ - C Merge\sall\sthe\slatest\senhancements\sand\sfixes\sfrom\strunk. - D 2015-11-20T13:49:01.510 - F Makefile.in d828db6afa6c1fa060d01e33e4674408df1942a1 -C Remove\sunreachable\sbranches\sfrom\sthe\sdecltype\scomputation\slogic\sin\sthe\squery\nplanner. -D 2015-12-02T19:46:12.775 ++C Merge\srecent\senhancements\sfrom\strunk. ++D 2015-12-02T20:53:14.202 + F Makefile.in 23d9a63484a383fc64951b25ef44067930f98dc6 F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 - F Makefile.msc e928e68168df69b353300ac87c10105206653a03 + F Makefile.msc e8fdca1cb89a1b58b5f4d3a130ea9a3d28cb314d F README.md 8ecc12493ff9f820cdea6520a9016001cb2e59b7 F VERSION 8b9d3ac6f1962f94e06ba05462422a544f9c4e36 F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50 @@@ -280,15 -280,15 +280,15 @@@ F src/analyze.c 977bd50c751bb939ef52917 F src/attach.c e944d0052b577703b9b83aac1638452ff42a8395 F src/auth.c b56c78ebe40a2110fd361379f7e8162d23f92240 F src/backup.c 2869a76c03eb393ee795416e2387005553df72bc - F src/bitvec.c d1f21d7d91690747881f03940584f4cc548c9d3d + F src/bitvec.c 1a78d450a17c5016710eec900bedfc5729bf9bdf F src/btmutex.c 45a968cc85afed9b5e6cf55bf1f42f8d18107f79 -F src/btree.c d3bdd8462a86492e2ebc9aca4a0168429017de25 -F src/btree.h 2d76dee44704c47eed323356a758662724b674a0 -F src/btreeInt.h 3ab435ed27adea54d040584b0bcc488ee7db1e38 -F src/build.c e83da4d004a4e050c01acbb821ff7a7b1019c29b +F src/btree.c ab52539c0c321de2bf4bb1238cff11eaad55c0bb +F src/btree.h beef31274289a103fa811a7d7adc2dcedbcf2077 +F src/btreeInt.h 99ebf5c9716a8dd1ea6c0583b624e8e0de148d61 - F src/build.c a59767596bf5dfa2c9cceb756bff50a17741c2d2 ++F src/build.c a3fa162b06a16e10136e1693b0eae9998a82b324 F src/callback.c 7b44ce59674338ad48b0e84e7b72f935ea4f68b0 F src/complete.c addcd8160b081131005d5bc2d34adf20c1c5c92f - F src/ctime.c 509ef9c64d1321f42448f111da86400b1799218a + F src/ctime.c 58eda76364fb6f374e044aa4493219b13abf9400 F src/date.c fb1c99172017dcc8e237339132c91a21a0788584 F src/dbstat.c ffd63fc8ba7541476ced189b95e95d7f2bc63f78 F src/delete.c 00af9f08a15ddc5cba5962d3d3e5bf2d67b2e7da @@@ -323,28 -323,28 +323,28 @@@ F src/os.c 8fd25588eeba74068d41102d2681 F src/os.h 3e57a24e2794a94d3cf2342c6d9a884888cd96bf F src/os_common.h abdb9a191a367793268fe553d25bab894e986a0e F src/os_setup.h c9d4553b5aaa6f73391448b265b89bed0b890faa - F src/os_unix.c eb24e0340fbe3cfd0eabfb15a71476953e54fa73 - F src/os_win.c 2d77dab5c555a18c0aff379c6a692fc3499044d9 + F src/os_unix.c 60997373a8d90bd17e1c0e49d11ef361b713439b + F src/os_win.c 386fba30419e8458b13209781c2af5590eab2811 F src/os_win.h eb7a47aa17b26b77eb97e4823f20a00b8bda12ca - F src/pager.c 44cc9e8e5df703e52b8e305ff9af2fddf6c62d0f - F src/pager.h f392409a9c64a700c4756b6111b8e73a78d07cf3 -F src/pager.c f92aacd5216d8815136c9e0190041783c602641a -F src/pager.h 9153c71a89dc82a5a77e485f3929792116c70aae -F src/parse.y 23737e649c26ce327603799e57f5c2ff50e5e6ba ++F src/pager.c d4c71e12bf9ac51ee38043aa718eace0b52c7e98 ++F src/pager.h c6b437a6543c7c57d7f85ceb474e176f1e62163e +F src/parse.y b0ad024157be717d43f9e1eb4f377873574153a3 F src/pcache.c 73895411fa6b7bd6f0091212feabbe833b358d23 F src/pcache.h 1ff11adce609ba7de139b6abfabaf9a2bac947b5 - F src/pcache1.c 902e1bc7bdaa81b40f8543407c5e2ac8ef4dc035 + F src/pcache1.c 46a110be31a8d9f9b41431733836822ca0dd27ab F src/pragma.c f3e7147299ca05ef4304a36f1fd6e002729c72c6 F src/pragma.h 3d94aebbebd2089899fecc01909bf2608b39507d F src/prepare.c 82e5db1013846a819f198336fed72c44c974e7b1 - F src/printf.c f8fc8f04e75b1e983ef2793c27ec7a43b287e94a + F src/printf.c ca05561795ad6c2fa47acdd007702586282f7feb F src/random.c ba2679f80ec82c4190062d756f22d0c358180696 - F src/resolve.c 1954a0f01bf65d78d7d559aea3d5c67f33376d91 + F src/resolve.c f4c897ca76ca6d5e0b3f0499c627392ffe657c8e F src/rowset.c eccf6af6d620aaa4579bd3b72c1b6395d9e9fa1e - F src/select.c 2376d320907a5c28c55290f18fd94aa3400bf97c - F src/shell.c f0f59ea60ad297f671b7ae0fb957a736ad17c92c - F src/sqlite.h.in fa62718f73553f06b2f2e362fd09ccb4e1cbb626 + F src/select.c 1611828a7116e5f6cc1e69cd07d59b0d2c662ea9 + F src/shell.c 2796237990d42e6a5a7beafee65ef70cc8767d21 + F src/sqlite.h.in 5bd83191711d3dc85030326daa9e8e5226a495e7 F src/sqlite3.rc 992c9f5fb8285ae285d6be28240a7e8d3a7f2bad - F src/sqlite3ext.h 4b66e3e3435da4b4c8c83696d0349f0c503b3924 + F src/sqlite3ext.h 41ef50b0418a7c5ad1337bb80db5a7928dee764f -F src/sqliteInt.h 64256d193a16a147d9f6317cc4e095fdd3e0a2e9 +F src/sqliteInt.h f298ca1b9fc4826becdec3918f10d216a23ac4d1 F src/sqliteLimit.h 216557999cb45f2e3578ed53ebefe228d779cb46 F src/status.c 70912d7be68e9e2dbc4010c93d344af61d4c59ba F src/table.c 51b46b2a62d1b3a959633d593b89bab5e2c9155e @@@ -363,10 -363,10 +363,10 @@@ F src/test_autoext.c dea8a01a7153b9adc9 F src/test_backup.c 2e6e6a081870150f20c526a2e9d0d29cda47d803 F src/test_blob.c e5a7a81d61a780da79101aeb1e60d300af169e07 F src/test_btree.c 2e9978eca99a9a4bfa8cae949efb00886860a64f - F src/test_config.c a482301b987f859098d5985cc19c239d8cd6e340 -F src/test_config.c 48850687dd5abc8260e23835632511054ccae172 ++F src/test_config.c 311a6bdbd6865d534e7c3cf853bb7dd865d63674 F src/test_demovfs.c 0de72c2c89551629f58486fde5734b7d90758852 F src/test_devsym.c e7498904e72ba7491d142d5c83b476c4e76993bc - F src/test_fs.c ced436e3d4b8e4681328409b8081051ce614e28f + F src/test_fs.c aab47ac456316502faa265daadf9ac832fea12b9 F src/test_func.c 0d9c25956152adefee8881c6fadc8354793764d0 F src/test_hexio.c abfdecb6fa58c354623978efceb088ca18e379cd F src/test_init.c 66b33120ffe9cd853b5a905ec850d51151337b32 @@@ -398,28 -400,28 +400,28 @@@ F src/threads.c bbfb74450643cb5372a43ad F src/tokenize.c 5606871a377f390af7040ec3c12e0d183512d785 F src/treeview.c 78842e90c1f71269e7a73a1d4221b6fe360bab66 F src/trigger.c 322f23aad694e8f31d384dcfa386d52a48d3c52f - F src/update.c 40e51cd0883cb5bfd6abb7d8a7cd8aa47fab2945 + F src/update.c 17332f9fe818cbc0444c36a811800af8498af4c3 F src/utf.c fc6b889ba0779b7722634cdeaa25f1930d93820c - F src/util.c fc612367108b74573c5fd13a85d0a23027f438bd + F src/util.c e802e8e311a0d6c48cd1b3e89db164f6f0248d70 -F src/vacuum.c 2ddd5cad2a7b9cef7f9e431b8c7771634c6b1701 -F src/vdbe.c 4d75375fa8bf911aa76ab8383d6f7eea0dec0fda +F src/vacuum.c 983cc3754718ef169a6ea9aef86798bd28106f21 - F src/vdbe.c 6b125884a09e7fc33b062baa0b44f38fe6f40af8 ++F src/vdbe.c ff46035880b746d7e2b126670078dae012b59c49 F src/vdbe.h efb7a8c1459e31f3ea4377824c6a7e4cb5068637 - F src/vdbeInt.h 33403622c6a8feaaac5f0f3f17f5d1bf6df42286 + F src/vdbeInt.h 75c2e82ee3357e9210c06474f8d9bdf12c81105d F src/vdbeapi.c 020681b943e77766b32ae1cddf86d7831b7374ca - F src/vdbeaux.c 0a6263b83b63843360b676542d5eb5e2846c178f - F src/vdbeblob.c 565fabd302f5fca3bdf3d56cac330483616a39b6 -F src/vdbeaux.c 9a234c9aaab4ad725daf94667cfed441a437c52d ++F src/vdbeaux.c 1236e154f9e1ad37d3d5640f57f147002f3ec00e + F src/vdbeblob.c fdc4a81605ae7a35ae94a55bd768b66d6be16f15 F src/vdbemem.c fdd1578e47bea61390d472de53c565781d81e045 - F src/vdbesort.c 8b23930a1289526f6d2a3a9f2e965bcc963e4a68 + F src/vdbesort.c a7ec02da4494c59dfd071126dd3726be5a11459d F src/vdbetrace.c 8befe829faff6d9e6f6e4dee5a7d3f85cc85f1a0 F src/vtab.c 2a8b44aa372c33f6154208e7a7f6c44254549806 F src/vxworks.h c18586c8edc1bddbc15c004fa16aeb1e1342b4fb - F src/wal.c fd2351d771df2e147b0d6a952314078fa20af171 -F src/wal.c 1569802364cd192bbd5c4a8ea3fd6de593edecbd -F src/wal.h df01efe09c5cb8c8e391ff1715cca294f89668a4 ++F src/wal.c bacf8a69cfed6581600002519e4cd30c2220326e +F src/wal.h 361b16891d2772294b138054c84f5a3bad6e9d05 F src/walker.c 2e14d17f592d176b6dc879c33fbdec4fbccaa2ba - F src/where.c 6aceb72cc58dc06922a9e1604d559c8ca4c3e728 - F src/whereInt.h 7892bb54cf9ca0ae5c7e6094491b94c9286dc647 - F src/wherecode.c 4c96182e7b25e4be54008dee2da5b9c2f8480b9b - F src/whereexpr.c e63244ca06c503e5f3c5b7f3c9aea0db826089ed + F src/where.c b18edbb9e5afabb77f4f27550c471c5c824e0fe7 + F src/whereInt.h e20801d89e34de1912bb6a3babb30c390da27add + F src/wherecode.c dfbfe198e418b01f208b489e088edd230c91a4e7 + F src/whereexpr.c eebba8340c90de73b3d3bbe8c43b84559b8e6e2c F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2 F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@@ -527,10 -529,6 +529,10 @@@ F test/collate9.test 3adcc799229545940d F test/collateA.test b8218ab90d1fa5c59dcf156efabb1b2599c580d6 F test/colmeta.test 2c765ea61ee37bc43bbe6d6047f89004e6508eb1 F test/colname.test 08948a4809d22817e0e5de89c7c0a8bd90cb551b +F test/concfault.test 500f17c3fcfe7705114422bcc6ddd3c740001a43 +F test/concurrent.test 634b6a88f1942f5d68cc89d4d5efa2b11ba7913c - F test/concurrent2.test f20913d376d993a85f28ef3323f09d4cba37206a ++F test/concurrent2.test 77d655c6af93e77803b5c926555a838bb21f922f +F test/concurrent3.test 0a5f7e3036d1eccf0782d7153ac21f5f222e9468 F test/conflict.test 841bcf7cabbfca39c577eb8411ea8601843b46a8 F test/conflict2.test 0d3af4fb534fa1bd020c79960bb56e4d52655f09 F test/conflict3.test dec0634c0f31dec9a4b01c63063e939f0cd21b6b @@@ -1407,7 -1408,7 +1411,7 @@@ F tool/vdbe_profile.tcl 246d0da094856d7 F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh 48bd54594752d5be3337f12c72f28d2080cb630b F tool/win/sqlite.vsix deb315d026cc8400325c5863eef847784a219a2f - P f2cde4cfc58cc372f59ae274bf0c2f7cf6e7ddf9 35c7f6cba6febf2480de01fca9d61b8065bf1c12 - R c1404ace283281f01646ea3d425143a2 -P 3e1d71fcaf57c0223ab9a7366c8607f8f66bb21c -R 0397cf9717330ffe9055735c8207c81a ++P 41c8b8e39bc0483cfbc0b4bfcc8ef8b2737a70a9 4f2bcff94c672312805be1400050a7026f93a9d7 ++R 1055895e20d2661fa554c8715dd55dea U drh - Z 1cdd9c780b66b45977a11df8c67e2bf8 -Z 6381d064415ccbeec9de47fb6017aaa8 ++Z 24ef1b5b92d0b1a13c94dec0494ce832 diff --cc manifest.uuid index 05825a74e9,c4252b6021..4e98908bfe --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 41c8b8e39bc0483cfbc0b4bfcc8ef8b2737a70a9 -4f2bcff94c672312805be1400050a7026f93a9d7 ++9130661a786e4c158f15103be57467a5cc03875a diff --cc src/wal.c index 1ac3b215c8,144db27a30..9adb565a7f --- a/src/wal.c +++ b/src/wal.c @@@ -2543,34 -2508,6 +2542,34 @@@ Pgno sqlite3WalDbsize(Wal *pWal) return 0; } +/* +** Take the WRITER lock on the WAL file. Return SQLITE_OK if successful, +** or an SQLite error code otherwise. This routine does not invoke any +** busy-handler callbacks, that is done at a higher level. +*/ +static int walWriteLock(Wal *pWal){ + int rc; + + /* Cannot start a write transaction without first holding a read lock */ + assert( pWal->readLock>=0 ); + assert( pWal->writeLock==0 ); + + /* If this is a read-only connection, obtaining a write-lock is not + ** possible. In this case return SQLITE_READONLY. Otherwise, attempt + ** to grab the WRITER lock. Set Wal.writeLock to true and return + ** SQLITE_OK if successful, or leave Wal.writeLock clear and return + ** an SQLite error code (possibly SQLITE_BUSY) otherwise. */ + if( pWal->readOnly ){ + rc = SQLITE_READONLY; + }else{ - rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 0); ++ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1); + if( rc==SQLITE_OK ){ + pWal->writeLock = 1; + } + } + + return rc; +} /* ** This function starts a write transaction on the WAL. diff --cc test/concurrent2.test index aa2cd62217,0000000000..e0600ff9c3 mode 100644,000000..100644 --- a/test/concurrent2.test +++ b/test/concurrent2.test @@@ -1,553 -1,0 +1,552 @@@ +# 2015 July 26 +# +# 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. +# +#*********************************************************************** +# +# Miscellaneous tests for transactions started with BEGIN CONCURRENT. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +source $testdir/lock_common.tcl +source $testdir/wal_common.tcl +set ::testprefix concurrent2 + +ifcapable !concurrent { + finish_test + return +} + +do_multiclient_test tn { + + do_test 1.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + CREATE TABLE t2(y); + } + } {wal} + do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} + + # Test that an CONCURRENT transaction that allocates/frees no pages does + # not conflict with a transaction that does allocate pages. + do_test 1.$tn.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(4); + } + sql2 { + INSERT INTO t2 VALUES(randomblob(1500)); + } + sql1 { + COMMIT; + } + } {} + do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} + + # But that an CONCURRENT transaction does conflict with a transaction + # that modifies the db schema. + do_test 1.$tn.3 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(5); + } + sql2 { + CREATE TABLE t3(z); + } + list [catch { sql1 COMMIT } msg] $msg + } {1 {database is locked}} + do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} + + # Test that an CONCURRENT transaction that allocates at least one page + # does not conflict with a transaction that allocates no pages. + do_test 1.$tn.4 { + sql1 { + ROLLBACK; + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(randomblob(1500)); + } + sql2 { + INSERT INTO t2 VALUES(8); + } + sql1 { + COMMIT; + } + } {} + + do_test 1.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} +} + +do_multiclient_test tn { + do_test 2.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x UNIQUE); + CREATE TABLE t2(y UNIQUE); + } + } {wal} + + do_test 2.$tn.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(randomblob(1500)); + } + sql2 { + INSERT INTO t2 VALUES(randomblob(1500)); + } + sql1 COMMIT + } {} + + do_test 2.$tn.3 { sql3 { PRAGMA integrity_check } } {ok} + + do_test 2.$tn.4 { + sql1 { + BEGIN CONCURRENT; + DELETE FROM t1; + } + sql2 { + DELETE FROM t2; + } + sql1 COMMIT + } {} + + do_test 2.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} + + do_test 2.$tn.6 { + sql1 { + INSERT INTO t1 VALUES(randomblob(1500)); + INSERT INTO t1 VALUES(randomblob(1500)); + INSERT INTO t2 VALUES(randomblob(1500)); + DELETE FROM t1 WHERE rowid=1; + } + + sql1 { + BEGIN CONCURRENT; + DELETE FROM t1 WHERE rowid=2; + } + + sql2 { + DELETE FROM t2; + } + + sql1 COMMIT + } {} + + do_test 2.$tn.7 { sql3 { PRAGMA integrity_check } } {ok} +} + +#------------------------------------------------------------------------- +# When an CONCURRENT transaction is opened on a database, the nFree and +# iTrunk header fields of the cached version of page 1 are both set +# to 0. This allows an CONCURRENT transaction to use its own private +# free-page-list, which is merged with the main database free-list when +# the transaction is committed. +# +# The following tests check that nFree/iTrunk are correctly restored if +# an CONCURRENT transaction is rolled back, and that savepoint rollbacks +# that occur within CONCURRENT transactions do not incorrectly restore +# these fields to their on-disk values. +# +reset_db +do_execsql_test 3.0 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x, y); + INSERT INTO t1 VALUES(randomblob(1500), randomblob(1500)); + DELETE FROM t1; +} {wal} + +do_execsql_test 3.1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(1, 2); + ROLLBACK; +} + +do_execsql_test 3.2 { PRAGMA integrity_check } {ok} +do_execsql_test 3.3 { PRAGMA freelist_count } {2} + +do_execsql_test 3.4.1 { + BEGIN CONCURRENT; + PRAGMA freelist_count; +} {2} +do_execsql_test 3.4.2 { + SAVEPOINT xyz; + INSERT INTO t1 VALUES(randomblob(1500), NULL); + PRAGMA freelist_count; +} {0} +do_execsql_test 3.4.3 { + ROLLBACK TO xyz; +} {} +do_execsql_test 3.4.4 { PRAGMA freelist_count } {0} +do_execsql_test 3.4.5 { COMMIT; PRAGMA freelist_count } {2} +do_execsql_test 3.4.6 { PRAGMA integrity_check } {ok} + +do_execsql_test 3.5.1 { + BEGIN CONCURRENT; + UPDATE t1 SET x=randomblob(10) WHERE y=555; + PRAGMA freelist_count; +} {0} +do_execsql_test 3.5.2 { + ROLLBACK; + PRAGMA freelist_count; +} {2} +do_execsql_test 3.5.3 { PRAGMA integrity_check } {ok} + +#------------------------------------------------------------------------- +# Test that nothing goes wrong if an CONCURRENT transaction allocates a +# page at the end of the file, frees it within the same transaction, and +# then has to move the same page to avoid a conflict on COMMIT. +# +do_multiclient_test tn { + do_test 4.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + CREATE TABLE t2(x); + } + } {wal} + + do_test 4.$tn.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(randomblob(1500)); + INSERT INTO t1 VALUES(randomblob(1500)); + DELETE FROM t1 WHERE rowid = 1; + } + + sql2 { + INSERT INTO t2 VALUES(randomblob(1500)); + INSERT INTO t2 VALUES(randomblob(1500)); + INSERT INTO t2 VALUES(randomblob(1500)); + INSERT INTO t2 VALUES(randomblob(1500)); + DELETE FROM t2 WHERE rowid IN (1, 2); + } + + sql1 COMMIT + } {} +} + +#------------------------------------------------------------------------- +# +do_multiclient_test tn { + do_test 5.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + CREATE TABLE t2(x); + INSERT INTO t1 VALUES(randomblob(1500)); + PRAGMA page_count; + } + } {wal 4} + + do_test 5.$tn.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t2 VALUES(randomblob(1500)); + PRAGMA page_count; + } + } {5} + + do_test 5.$tn.3 { + sql2 { + DELETE FROM t1; + PRAGMA freelist_count; + PRAGMA page_count; + } + } {1 4} + + do_test 5.$tn.4 { sql1 COMMIT } {} + do_test 5.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} +} + +#------------------------------------------------------------------------- +# +do_multiclient_test tn { + do_test 6.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); + INSERT INTO t1 VALUES(randomblob(1500)); + PRAGMA wal_checkpoint; + } + } {wal 0 5 5} + + do_test 6.$tn.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(randomblob(1500)); + INSERT INTO t1 VALUES(randomblob(1500)); + } + } {} + + do_test 6.$tn.3 { + sql2 { + BEGIN; + INSERT INTO t1 VALUES(randomblob(1500)); + INSERT INTO t1 VALUES(randomblob(1500)); + COMMIT; + } + } {} + + do_test 6.$tn.4 { + list [catch { sql1 COMMIT } msg] $msg + } {1 {database is locked}} + do_test 6.$tn.5 { sql3 { PRAGMA integrity_check } } {ok} + do_test 6.$tn.5 { sql3 { SELECT count(*) from t1 } } {3} +} + +#------------------------------------------------------------------------- +# Test that if a corrupt wal-index-header is encountered when attempting +# to commit a CONCURRENT transaction, the transaction is not committed +# (or rolled back) and that SQLITE_BUSY_SNAPSHOT is returned to the user. +# +catch { db close } +forcedelete test.db +testvfs tvfs +sqlite3 db test.db -vfs tvfs +do_execsql_test 7.1 { + PRAGMA journal_mode = wal; + BEGIN; + CREATE TABLE t1(a, b, PRIMARY KEY(a)); + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 VALUES(3, 4); + COMMIT; + BEGIN CONCURRENT; + INSERT INTO t1 VALUES(5, 6); + INSERT INTO t1 VALUES(7, 8); + SELECT * FROM t1; +} {wal 1 2 3 4 5 6 7 8} + +# Corrupt the wal-index header +incr_tvfs_hdr test.db 11 1 + +do_catchsql_test 7.2.1 { COMMIT } {1 {database is locked}} +do_test 7.2.2 { sqlite3_extended_errcode db } SQLITE_BUSY_SNAPSHOT + +do_execsql_test 7.3.1 { + SELECT * FROM t1; + ROLLBACK; +} {1 2 3 4 5 6 7 8} +do_execsql_test 7.3.2 { + SELECT * FROM t1; +} {1 2 3 4} + +#------------------------------------------------------------------------- +# Test that "PRAGMA integrity_check" works within a concurrent +# transaction. Within a concurrent transaction, "PRAGMA integrity_check" +# is unable to detect unused database pages, but can detect other types +# of corruption. +# +reset_db +do_execsql_test 8.1 { + PRAGMA journal_mode = wal; + CREATE TABLE kv(k INTEGER PRIMARY KEY, v UNIQUE); + INSERT INTO kv VALUES(NULL, randomblob(750)); + INSERT INTO kv SELECT NULL, randomblob(750) FROM kv; + INSERT INTO kv SELECT NULL, randomblob(750) FROM kv; + INSERT INTO kv SELECT NULL, randomblob(750) FROM kv; + INSERT INTO kv SELECT NULL, randomblob(750) FROM kv; + INSERT INTO kv SELECT NULL, randomblob(750) FROM kv; + DELETE FROM kv WHERE rowid%2; + PRAGMA freelist_count; - } {wal 33} ++} {wal 34} +do_execsql_test 8.2 { PRAGMA integrity_check } ok +do_execsql_test 8.3 { + BEGIN CONCURRENT; + PRAGMA integrity_check; +} {ok} +do_execsql_test 8.4 { + INSERT INTO kv VALUES(1100, 1100); + PRAGMA integrity_check; +} {ok} +do_execsql_test 8.5 { + COMMIT; + PRAGMA integrity_check; +} {ok} + +#------------------------------------------------------------------------- +# Test that concurrent transactions do not allow foreign-key constraints +# to be bypassed. +# +do_multiclient_test tn { + do_test 9.$tn.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE pp(i INTEGER PRIMARY KEY, j); + CREATE TABLE cc(a, b REFERENCES pp); + + WITH seq(i) AS (SELECT 1 UNION ALL SELECT i+1 FROM seq WHERE i<100) + INSERT INTO pp SELECT i, randomblob(1000) FROM seq; + + PRAGMA foreign_keys = 1; + } + } {wal} + + + do_test 9.$tn.2.1 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO cc VALUES(42, 42); + } + } {} + do_test 9.$tn.2.2 { + sql2 { DELETE FROM pp WHERE i=42 } + list [catch { sql1 COMMIT } msg] $msg + } {1 {database is locked}} + do_test 9.$tn.2.3 { + sql1 ROLLBACK + } {} + + do_test 9.$tn.3.1 { + sql1 { + PRAGMA foreign_keys = 0; + BEGIN CONCURRENT; + INSERT INTO cc VALUES(43, 43); + } + } {} + do_test 9.$tn.3.2 { + sql2 { DELETE FROM pp WHERE i=43 } + list [catch { sql1 COMMIT } msg] $msg + } {0 {}} + + do_test 9.$tn.4.1 { + sql1 { + PRAGMA foreign_keys = on; + BEGIN CONCURRENT; + INSERT INTO cc VALUES(44, 44); + } + } {} + do_test 9.$tn.4.2 { + sql2 { DELETE FROM pp WHERE i=1 } + list [catch { sql1 COMMIT } msg] $msg + } {0 {}} +} + +#------------------------------------------------------------------------- +# Test that even if a SELECT statement appears before all writes within +# a CONCURRENT transaction, the pages it reads are still considered when +# considering whether or not the transaction may be committed. +# +do_multiclient_test tn { + do_test 10.$tn.1.1 { + sql1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(a); + CREATE TABLE t2(b); + CREATE TABLE t3(c); + INSERT INTO t1 VALUES(1), (2), (3); + INSERT INTO t2 VALUES(1), (2), (3); + INSERT INTO t3 VALUES(1), (2), (3); + } + } {wal} + + do_test 10.$tn.1.2 { + sql1 { + BEGIN CONCURRENT; + SELECT * FROM t1; + INSERT INTO t2 VALUES(4); + } + } {1 2 3} + + do_test 10.$tn.1.3 { + sql2 { INSERT INTO t1 VALUES(4) } + list [catch {sql1 COMMIT} msg] $msg + } {1 {database is locked}} + sql1 ROLLBACK + + # In this case, because the "SELECT * FROM t1" is first stepped before + # the "BEGIN CONCURRENT", the pages it reads are not recorded by the + # pager object. And so the transaction can be committed. Technically + # this behaviour (the effect of an ongoing SELECT on a BEGIN CONCURRENT + # transacation) is undefined. + # + do_test 10.$tn.2.1 { + code1 { + set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy] + sqlite3_step $::stmt + } + } {SQLITE_ROW} + do_test 10.$tn.2.2 { + sql1 { + BEGIN CONCURRENT; + INSERT INTO t2 VALUES(4); + } + code1 { + set res [list] + lappend res [sqlite3_column_int $::stmt 0] + while {[sqlite3_step $::stmt]=="SQLITE_ROW"} { + lappend res [sqlite3_column_int $::stmt 0] + } + sqlite3_finalize $::stmt + set res + } + } {1 2 3 4} + do_test 10.$tn.2.3 { + sql2 { INSERT INTO t1 VALUES(5) } + sql1 COMMIT + } {} + + # More tests surrounding long-lived prepared statements and concurrent + # transactions. + do_test 10.$tn.3.1 { + sql1 { + BEGIN CONCURRENT; + SELECT * FROM t1; + COMMIT; + } + sql1 { + BEGIN CONCURRENT; + INSERT INTO t2 VALUES(5); + } + sql2 { + INSERT INTO t1 VALUES(5); + } + sql1 COMMIT + sql3 { + SELECT * FROM t2; + } + } {1 2 3 4 5} + do_test 10.$tn.3.2 { + sql1 { + BEGIN CONCURRENT; + SELECT * FROM t1; + ROLLBACK; + } + sql1 { + BEGIN CONCURRENT; + INSERT INTO t2 VALUES(6); + } + sql2 { + INSERT INTO t1 VALUES(6); + } + sql1 COMMIT + sql3 { SELECT * FROM t2 } + } {1 2 3 4 5 6} + do_test 10.$tn.3.3 { + sql1 { BEGIN CONCURRENT } + code1 { + set ::stmt [sqlite3_prepare db "SELECT * FROM t1" -1 dummy] + sqlite3_step $::stmt + } + sql1 { + INSERT INTO t2 VALUES(7); + SELECT * FROM t3; + ROLLBACK; + BEGIN CONCURRENT; + } + sql2 { INSERT INTO t3 VALUES(5) } + code1 { sqlite3_finalize $::stmt } + sql1 { + INSERT INTO t2 VALUES(8); + COMMIT; + } + } {} +} + + +finish_test -