]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge recent enhancements from trunk.
authordrh <drh@noemail.net>
Wed, 2 Dec 2015 20:53:14 +0000 (20:53 +0000)
committerdrh <drh@noemail.net>
Wed, 2 Dec 2015 20:53:14 +0000 (20:53 +0000)
FossilOrigin-Name: 9130661a786e4c158f15103be57467a5cc03875a

1  2 
manifest
manifest.uuid
src/build.c
src/pager.c
src/pager.h
src/test_config.c
src/vdbe.c
src/vdbeaux.c
src/wal.c
test/concurrent2.test

diff --cc manifest
index f1134c5e0b4c83acb0ba4de89fe43a906fcaad24,123da22f21a3ddf5dc704fdb995ae4247bb1f3fc..018ece16c9af8e3d451cf90fb91c450edc689a80
+++ 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/concurrent2.test f20913d376d993a85f28ef3323f09d4cba37206a
 +F test/concfault.test 500f17c3fcfe7705114422bcc6ddd3c740001a43
 +F test/concurrent.test 634b6a88f1942f5d68cc89d4d5efa2b11ba7913c
++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 05825a74e95449e7d1da7eb584108b5c04ce7f5c,c4252b60212948a0c55024a1a036b411ecb0e307..4e98908bfe1b6e5efa7b533e6567448f60073bb6
@@@ -1,1 -1,1 +1,1 @@@
- 41c8b8e39bc0483cfbc0b4bfcc8ef8b2737a70a9
 -4f2bcff94c672312805be1400050a7026f93a9d7
++9130661a786e4c158f15103be57467a5cc03875a
diff --cc src/build.c
Simple merge
diff --cc src/pager.c
Simple merge
diff --cc src/pager.h
Simple merge
Simple merge
diff --cc src/vdbe.c
Simple merge
diff --cc src/vdbeaux.c
Simple merge
diff --cc src/wal.c
index 1ac3b215c8eebf2575f4fd17ae40d6fa7188cb3a,144db27a303bcfb22796f4c649865cf87d314f8c..9adb565a7ff46ba4b8b58928614e5d6380234723
+++ b/src/wal.c
@@@ -2543,34 -2508,6 +2542,34 @@@ Pgno sqlite3WalDbsize(Wal *pWal)
    return 0;
  }
  
-     rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1, 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);
 +    if( rc==SQLITE_OK ){
 +      pWal->writeLock = 1;
 +    }
 +  }
 +
 +  return rc;
 +}
  
  /* 
  ** This function starts a write transaction on the WAL.
index aa2cd6221733aa3eec8d6850cdf091973de4df9c,0000000000000000000000000000000000000000..e0600ff9c3369e165e428c2c86164b3f97d83320
mode 100644,000000..100644
--- /dev/null
@@@ -1,553 -1,0 +1,552 @@@
- } {wal 33}
 +# 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 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