]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Sync this branch with the latest trunk.
authordan <dan@noemail.net>
Sat, 1 Dec 2018 20:14:06 +0000 (20:14 +0000)
committerdan <dan@noemail.net>
Sat, 1 Dec 2018 20:14:06 +0000 (20:14 +0000)
FossilOrigin-Name: 7a44fa5a350a3f19b8e9f5196d22535788885f8c0e849572202bf64a055ddc2d

16 files changed:
1  2 
ext/misc/bgckpt.c
main.mk
manifest
manifest.uuid
src/btree.c
src/os_unix.c
src/pager.c
src/pager.h
src/pragma.c
src/test_tclsh.c
src/vdbe.c
src/wal.c
src/wal.h
test/permutations.test
test/tester.tcl
test/wal2simple.test

index ca8f3edf016882ffa944e3c7b4c64edb5ffcbed1,0000000000000000000000000000000000000000..5e6488dd117ca7719a0d955881e5454904cf6432
mode 100644,000000..100644
--- /dev/null
@@@ -1,238 -1,0 +1,244 @@@
- int Bgckpt_Init(Tcl_Interp *interp){
-   return TCL_OK;
- }
 +/*
 +** 2017-10-11
 +**
 +** 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.
 +**
 +******************************************************************************
 +**
 +*/
 +
 +#if !defined(SQLITE_TEST) || defined(SQLITE_OS_UNIX)
 +
 +#include "sqlite3.h"
 +#include <string.h>
 +#include <pthread.h>
 +
 +/*
 +** API declarations.
 +*/
 +typedef struct Checkpointer Checkpointer;
 +int sqlite3_bgckpt_create(const char *zFilename, Checkpointer **pp);
 +int sqlite3_bgckpt_checkpoint(Checkpointer *p, int bBlock);
 +void sqlite3_bgckpt_destroy(Checkpointer *p);
 +
 +
 +struct Checkpointer {
 +  sqlite3 *db;                    /* Database handle */
 +
 +  pthread_t thread;               /* Background thread */
 +  pthread_mutex_t mutex;
 +  pthread_cond_t cond;
 +
 +  int rc;                         /* Error from "PRAGMA wal_checkpoint" */
 +  int bCkpt;                      /* True if checkpoint requested */
 +  int bExit;                      /* True if exit requested */
 +};
 +
 +static void *bgckptThreadMain(void *pCtx){
 +  int rc = SQLITE_OK;
 +  Checkpointer *p = (Checkpointer*)pCtx;
 +
 +  while( rc==SQLITE_OK ){
 +    int bExit;
 +
 +    pthread_mutex_lock(&p->mutex);
 +    if( p->bCkpt==0 && p->bExit==0 ){
 +      pthread_cond_wait(&p->cond, &p->mutex);
 +    }
 +    p->bCkpt = 0;
 +    bExit = p->bExit;
 +    pthread_mutex_unlock(&p->mutex);
 +
 +    if( bExit ) break;
 +    rc = sqlite3_exec(p->db, "PRAGMA wal_checkpoint", 0, 0, 0);
 +    if( rc==SQLITE_BUSY ){
 +      rc = SQLITE_OK;
 +    }
 +  }
 +
 +  pthread_mutex_lock(&p->mutex);
 +  p->rc = rc;
 +  pthread_mutex_unlock(&p->mutex);
 +  return 0;
 +}
 +
 +void sqlite3_bgckpt_destroy(Checkpointer *p){
 +  if( p ){
 +    void *ret = 0;
 +
 +    /* Signal the background thread to exit */
 +    pthread_mutex_lock(&p->mutex);
 +    p->bExit = 1;
 +    pthread_cond_broadcast(&p->cond);
 +    pthread_mutex_unlock(&p->mutex);
 +
 +    pthread_join(p->thread, &ret);
 +    sqlite3_close(p->db);
 +    sqlite3_free(p);
 +  }
 +}
 +
 +
 +int sqlite3_bgckpt_create(const char *zFilename, Checkpointer **pp){
 +  Checkpointer *pNew = 0;
 +  int rc;
 +
 +  pNew = (Checkpointer*)sqlite3_malloc(sizeof(Checkpointer));
 +  if( pNew==0 ){
 +    rc = SQLITE_NOMEM;
 +  }else{
 +    memset(pNew, 0, sizeof(Checkpointer));
 +    rc = sqlite3_open(zFilename, &pNew->db);
 +  }
 +
 +  if( rc==SQLITE_OK ){
 +    pthread_mutex_init(&pNew->mutex, 0);
 +    pthread_cond_init(&pNew->cond, 0);
 +    pthread_create(&pNew->thread, 0, bgckptThreadMain, (void*)pNew);
 +  }
 +
 +  if( rc!=SQLITE_OK ){
 +    sqlite3_bgckpt_destroy(pNew);
 +    pNew = 0;
 +  }
 +  *pp = pNew;
 +  return rc;
 +}
 +
 +int sqlite3_bgckpt_checkpoint(Checkpointer *p, int bBlock){
 +  int rc;
 +  pthread_mutex_lock(&p->mutex);
 +  rc = p->rc;
 +  if( rc==SQLITE_OK ){
 +    p->bCkpt = 1;
 +    pthread_cond_broadcast(&p->cond);
 +  }
 +  pthread_mutex_unlock(&p->mutex);
 +  return rc;
 +}
 +
 +#ifdef SQLITE_TEST
 +
 +#if defined(INCLUDE_SQLITE_TCL_H)
 +#  include "sqlite_tcl.h"
 +#else
 +#  include "tcl.h"
 +#  ifndef SQLITE_TCLAPI
 +#    define SQLITE_TCLAPI
 +#  endif
 +#endif
 +
 +const char *sqlite3ErrName(int rc);
 +
 +static void SQLITE_TCLAPI bgckpt_del(void * clientData){
 +  Checkpointer *pCkpt = (Checkpointer*)clientData;
 +  sqlite3_bgckpt_destroy(pCkpt);
 +}
 +
 +/*
 +** Tclcmd: $ckpt SUBCMD ...
 +*/
 +static int SQLITE_TCLAPI bgckpt_obj_cmd(
 +  void * clientData,
 +  Tcl_Interp *interp,
 +  int objc,
 +  Tcl_Obj *CONST objv[]
 +){
 +  Checkpointer *pCkpt = (Checkpointer*)clientData;
 +  const char *aCmd[] = { "checkpoint", "destroy", 0 };
 +  int iCmd;
 +
 +  if( objc<2 ){
 +    Tcl_WrongNumArgs(interp, 1, objv, "SUBCMD ...");
 +    return TCL_ERROR;
 +  }
 +
 +  if( Tcl_GetIndexFromObj(interp, objv[1], aCmd, "sub-command", 0, &iCmd) ){
 +    return TCL_ERROR;
 +  }
 +
 +  switch( iCmd ){
 +    case 0: {
 +      int rc;
 +      int bBlock = 0;
 +
 +      if( objc>3 ){
 +        Tcl_WrongNumArgs(interp, 2, objv, "?BLOCKING?");
 +        return TCL_ERROR;
 +      }
 +      if( objc==3 && Tcl_GetBooleanFromObj(interp, objv[2], &bBlock) ){
 +        return TCL_ERROR;
 +      }
 +
 +      rc = sqlite3_bgckpt_checkpoint(pCkpt, bBlock);
 +      if( rc!=SQLITE_OK ){
 +        Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
 +        return TCL_ERROR;
 +      }
 +      break;
 +    }
 +
 +    case 1: {
 +      Tcl_DeleteCommand(interp, Tcl_GetString(objv[0]));
 +      break;
 +    }
 +  }
 +
 +  return TCL_OK;
 +}
 +
 +/*
 +** Tclcmd: bgckpt CMDNAME FILENAME
 +*/
 +static int SQLITE_TCLAPI bgckpt_cmd(
 +  void * clientData,
 +  Tcl_Interp *interp,
 +  int objc,
 +  Tcl_Obj *CONST objv[]
 +){
 +  const char *zCmd;
 +  const char *zFilename;
 +  int rc;
 +  Checkpointer *pCkpt;
 +
 +  if( objc!=3 ){
 +    Tcl_WrongNumArgs(interp, 1, objv, "CMDNAME FILENAME");
 +    return TCL_ERROR;
 +  }
 +  zCmd = Tcl_GetString(objv[1]);
 +  zFilename = Tcl_GetString(objv[2]);
 +
 +  rc = sqlite3_bgckpt_create(zFilename, &pCkpt);
 +  if( rc!=SQLITE_OK ){
 +    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
 +    return TCL_ERROR;
 +  }
 +
 +  Tcl_CreateObjCommand(interp, zCmd, bgckpt_obj_cmd, (void*)pCkpt, bgckpt_del);
 +  Tcl_SetObjResult(interp, objv[1]);
 +  return TCL_OK;
 +}
 +
 +int Bgckpt_Init(Tcl_Interp *interp){
 +  Tcl_CreateObjCommand(interp, "bgckpt", bgckpt_cmd, 0, 0);
 +  return TCL_OK;
 +}
 +#endif   /* SQLITE_TEST */
 +
 +#else
++#if defined(INCLUDE_SQLITE_TCL_H)
++#  include "sqlite_tcl.h"
++#else
++#  include "tcl.h"
++#  ifndef SQLITE_TCLAPI
++#    define SQLITE_TCLAPI
++#  endif
++#endif
++int Bgckpt_Init(Tcl_Interp *interp){ return TCL_OK; }
 +#endif
 +
diff --cc main.mk
Simple merge
diff --cc manifest
index 709f88d499c634fd67b1f50cbaa57d4b643c611b,85ce65b7a125f2649f50224ed6ef0bc710285c3b..1531e9dadf011c84d94dfcc8240d2227920f3191
+++ b/manifest
@@@ -1,10 -1,12 +1,12 @@@
- C Add\snew\sextension\s"bgckpt"\sin\sext/misc/bgckpt.c.\sFor\sexperimenting\swith\nrunning\swal2\smode\scheckpoints\sin\sa\sbackground\sthread.
- D 2017-10-10T20:11:10.809
- F Makefile.in 4bc36d913c2e3e2d326d588d72f618ac9788b2fd4b7efda61102611a6495c3ff
 -C Version\s3.26.0
 -D 2018-12-01T12:34:55.966
++C Sync\sthis\sbranch\swith\sthe\slatest\strunk.
++D 2018-12-01T20:14:06.719
+ F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
+ F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
+ F Makefile.in a050c8670ea0d7b37b2192306cbb50d392acd9902b84e9b56f3444d006f97a6c
  F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
- F Makefile.msc 6033b51b6aea702ea059f6ab2d47b1d3cef648695f787247dd4fb395fe60673f
- F README.md f5c87359573c4d255425e588a56554b50fdcc2afba4e017a2e02a43701456afd
- F VERSION f81232df28e2d3ff049feefad5fbd5489cc33697f6bd2ecf61af7f0dde3b83d0
+ F Makefile.msc 0d6831ff7951b302e888d86d4c469e2ec3c22f59eba4118b8c38d5a51d9e2d4f
+ F README.md 377233394b905d3b2e2b33741289e093bc93f2e7adbe00923b2c5958c9a9edee
+ F VERSION 654da1d4053fb09ffc33a3910e6d427182a7dcdc67e934fa83de2849ac83fccb
  F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
  F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
  F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@@ -255,24 -268,29 +268,30 @@@ F ext/lsm1/lsm_vtab.c 529255dc704289001
  F ext/lsm1/lsm_win32.c 0a4acbd7e8d136dd3a5753f0a9e7a9802263a9d96cef3278cf120bcaa724db7c
  F ext/lsm1/test/lsm1_common.tcl 5ed4bab07c93be2e4f300ebe46007ecf4b3e20bc5fbe1dedaf04a8774a6d8d82
  F ext/lsm1/test/lsm1_simple.test ca949efefa102f4644231dcd9291d8cda7699a4ce1006b26e0e3fcb72233f422
- F ext/misc/README.md 8e008c8d2b02e09096b31dfba033253ac27c6c06a18aa5826e299fa7601d90b2
- F ext/misc/amatch.c 6db4607cb17c54b853a2d7c7c36046d004853f65b9b733e6f019d543d5dfae87
+ F ext/lsm1/tool/mklsm1c.tcl f31561bbee5349f0a554d1ad7236ac1991fc09176626f529f6078e07335398b0
+ F ext/misc/README.md d6dd0fe1d8af77040216798a6a2b0c46c73054d2f0ea544fbbcdccf6f238c240
+ F ext/misc/amatch.c c0a6a807a553eaa220bf69fca0353cd1587a3bfb3d2224fa425e3e6efcacc98a
  F ext/misc/anycollseq.c 5ffdfde9829eeac52219136ad6aa7cd9a4edb3b15f4f2532de52f4a22525eddb
- F ext/misc/bgckpt.c 1e0178c1d9f44d44f45c731ebff45854194ca59a41d33f078f10a3a714bf4532
+ F ext/misc/appendvfs.c 3777f22ec1057dc4e5fd89f2fbddcc7a29fbeef1ad038c736c54411bb1967af7
++F ext/misc/bgckpt.c 49ae19aa03e6da393db5d17da256374d4c4d36889fdd89d6e4bc93aca2b752e6
+ F ext/misc/btreeinfo.c 4f0ebf278f46e68e6306c667917766cebc5550fd35d5de17847988e22892d4d2
  F ext/misc/carray.c ed96c218ea940b85c9a274c4d9c59fe9491c299147a38a8bba537687bd6c6005
- F ext/misc/closure.c 0d2a038df8fbae7f19de42e7c7d71f2e4dc88704
- F ext/misc/completion.c 52c3f01523e3e387eb321b4739a89d1fe47cbe6025aa1f2d8d3685e9e365df0f
- F ext/misc/compress.c 122faa92d25033d6c3f07c39231de074ab3d2e83
- F ext/misc/csv.c 1a009b93650732e22334edc92459c4630b9fa703397cbb3c8ca279921a36ca11
- F ext/misc/dbdump.c 3509fa6b8932d04e932d6b6b827b6a82ca362781b8e8f3c77336f416793e215e
- F ext/misc/eval.c f971962e92ebb8b0a4e6b62949463ee454d88fa2
- F ext/misc/fileio.c b1aa06c0f1dac277695d4529e5e976c65ab5678dcbb53a0304deaa8adc44b332
+ F ext/misc/closure.c 9f8fa11aa6c6e2f6d7296ffa88f103df4b46abd9602bcab3ea2f8fc24f334f63
+ F ext/misc/completion.c cec672d40604075bb341a7f11ac48393efdcd90a979269b8fe7977ea62d0547f
+ F ext/misc/compress.c dd4f8a6d0baccff3c694757db5b430f3bbd821d8686d1fc24df55cf9f035b189
+ F ext/misc/csv.c 88333dc9f7dcf6a8148406f10ae04261e24e3b4c721550ae33e9e71f1265c1f1
+ F ext/misc/dbdump.c 12389a10c410fadf1e68eeb382def92d5a7fa9ce7cce4fb86a736fa2bac1000a
+ F ext/misc/eval.c 6ea9b22a5fa0dd973b67ca4e53555be177bc0b7b263aadf1024429457c82c0e3
+ F ext/misc/explain.c d5c12962d79913ef774b297006872af1fccda388f61a11d37758f9179a09551f
+ F ext/misc/fileio.c e3153b04433897a18a3d17185845f286892e96fdf87f4301290d09c36ae1759f
  F ext/misc/fuzzer.c 7c64b8197bb77b7d64eff7cac7848870235d4c25
  F ext/misc/ieee754.c f190d0cc5182529acb15babd177781be1ac1718c
- F ext/misc/json1.c dbe086615b9546c156bf32b9378fc09383b58bd17513b866cfd24c1e15281984
- F ext/misc/memvfs.c e5225bc22e79dde6b28380f3a068ddf600683a33
+ F ext/misc/json1.c 3f017d2659e531d021d015ec5d69ea0b1c71f2e15bf9768b1e149fcdf6c3e0b1
+ F ext/misc/memstat.c 941928c6104d8ed569a6c47caa756dc78b8091f7a15f87d3004f3b1e576b10da
+ F ext/misc/memvfs.c ab36f49e02ebcdf85a1e08dc4d8599ea8f343e073ac9e0bca18a98b7e1ec9567
  F ext/misc/mmapwarm.c 70b618f2d0bde43fae288ad0b7498a629f2b6f61b50a27e06fae3cd23c83af29
  F ext/misc/nextchar.c 35c8b8baacb96d92abbb34a83a997b797075b342
+ F ext/misc/normalize.c b4290464f542bae7a97b43f15bd197949b833ffd668b7c313631bd5d4610212c
  F ext/misc/percentile.c 92699c8cd7d517ff610e6037e56506f8904dae2e
  F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4
  F ext/misc/remember.c add730f0f7e7436cd15ea3fd6a90fd83c3f706ab44169f7f048438b7d6baa69c
@@@ -383,7 -428,7 +429,7 @@@ F ext/userauth/userauth.c f81aa5a3ecacf
  F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
  F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
  F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
- F main.mk 8dcd78e5977c6e220ec0ab9a00aa2469ca38380d1553cb3eff18469a28e04654
 -F main.mk eeaa279fa6acdcfa6555058548075569a06f891fd67f5901b1e7700d18052fda
++F main.mk 1274d58d63cb050586fbe0e06b22dcc69972ccb16e57f39a033f37d294316d12
  F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
  F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
  F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
@@@ -395,29 -440,30 +441,30 @@@ F spec.template 86a4a43b99ebb3e75e6b9a7
  F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
  F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
  F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
- F src/alter.c cf7a8af45cb0ace672f47a1b29ab24092a9e8cd8d945a9974e3b5d925f548594
- F src/analyze.c 0d0ccf7520a201d8747ea2f02c92c26e26f801bc161f714f27b9f7630dde0421
- F src/attach.c 07b706e336fd3cedbd855e1f8266d10e82fecae07daf86717b5760cd7784c584
- F src/auth.c 6277d63837357549fe14e723490d6dc1a38768d71c795c5eb5c0f8a99f918f73
- F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b
+ F src/alter.c f886160da189e4e99093cd5a2aca625652cc9b027d5100b87f81c175d1056387
+ F src/analyze.c 3dc6b98cf007b005af89df165c966baaa48e8124f38c87b4d2b276fe7f0b9eb9
+ F src/attach.c 92b51739a885da8bd84bc9a05485f1e48148bce5c15432f059b45af98fff75cd
+ F src/auth.c 0fac71038875693a937e506bceb492c5f136dd7b1249fbd4ae70b4e8da14f9df
+ F src/backup.c 78d3cecfbe28230a3a9a1793e2ead609f469be43e8f486ca996006be551857ab
  F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
- F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
- F src/btree.c a559d64185c737daf64f361df40a51f35e1f9ba862ae3f4193bf5eadc549e6b2
- F src/btree.h 32ef5d3f25dc70ef1ee9cecf84a023c21378f06a57cd701d2e866e141b150f09
- F src/btreeInt.h 55b702efce17e5d1941865464227d3802cfc9c7c832fac81d4c94dced47a71fc
- F src/build.c e71e96a67daf3d1dd23188423e66cd6af38017e2ec73fead5d2b57da2d3c7e16
- F src/callback.c 28a8ede982fde4129b828350f78f2c01fe7d12c74d1a0a05d7108ab36f308688
+ F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
 -F src/btree.c ba7c7eef4461790f37c309936bfc5d0d6ba9b194b02d3c8ff1fd53b420ea6d3b
++F src/btree.c 3887a4f5513a831c445063ff1d81d9d6a50ad47522600dab0ef0656f4389fe5a
+ F src/btree.h febb2e817be499570b7a2e32a9bbb4b607a9234f6b84bb9ae84916d4806e96f2
+ F src/btreeInt.h 620ab4c7235f43572cf3ac2ac8723cbdf68073be4d29da24897c7b77dda5fd96
+ F src/build.c 127d33ad57b455a9339e9fabff41284c8b030cc6247ca7a2a6c0ad7abfc1ce85
+ F src/callback.c 789bd33d188146f66c0dd8306472a72d1c05f71924b24a91caf6bd45cf9aba73
  F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
- F src/ctime.c ff1be3eed7bdd75aaca61ca8dc848f7c9f850ef2fb9cb56f2734e922a098f9c0
- F src/date.c 48f743d88bbe88f848532d333cca84f26e52a4f217e86f86be7fc1b919c33d74
- F src/dbstat.c 7a4ba8518b6369ef3600c49cf9c918ad979acba610b2aebef1b656d649b96720
- F src/delete.c 21a5f1812fdb599e9f7afb9f650bdabab60a3afd51d7e94e539c982f647b0023
- F src/expr.c 4d2d0aafd945424f638ee03e11330f03288ccf616e025498f3c8602d01609a0a
+ F src/ctime.c 109e58d00f62e8e71ee1eb5944ac18b90171c928ab2e082e058056e1137cc20b
+ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
+ F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
+ F src/dbstat.c 3c8bd4e77f0244fd2bd7cc90acf116ad2f8e82d70e536637f35ac2bc99b726f9
+ F src/delete.c f7938125847e8ef485448db5fbad29acb2991381a02887dd854c1617315ab9fb
+ F src/expr.c 9aacc0b72348ba90010b672dcbbbe2fa56e1182043bc917a3a147b2bc57a5497
  F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
- F src/fkey.c 5ff2c895fe087756d8085dc1a9bc229b5670e2a65c3929dd87c71e43649af333
- F src/func.c b4d259af627e3cd9510cf08db37f0bcc88b1887c735169c74490c3739d5cf5c6
- F src/global.c ac3094f1dc59fbeb919aef7cc0cc827a8459d1fb1adb7972ef75bd9e0c10b75b
- F src/hash.c a12580e143f10301ed5166ea4964ae2853d3905a511d4e0c44497245c7ce1f7a
+ F src/fkey.c 972a4ba14296bef2303a0abbad1e3d82bc3c61f9e6ce4e8e9528bdee68748812
+ F src/func.c 7c288b4ce309b5a8b8473514b88e1f8e69a80134509a8c0db8e39c858e367e7f
+ F src/global.c 8291eee0782b83124de14ec0389ec9fd6ae1873358a6b0d9469fe17a46ad803b
+ F src/hash.c 931ec82d7e070654a8facb42549bbb3a25720171d73ba94c3d3160580d01ef1f
  F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4
  F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
  F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
@@@ -443,36 -490,35 +491,35 @@@ F src/os.c 8aeb0b0f40f8f5b0da03fe497066
  F src/os.h 48388821692e87da174ea198bf96b1b2d9d83be5dfc908f673ee21fafbe0d432
  F src/os_common.h b2f4707a603e36811d9b1a13278bffd757857b85
  F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
- F src/os_unix.c 1f9c3e771557edd248e1fcec0818739826b2a3121b609a6e3372b670236d085a
- F src/os_win.c 6892c3ff23b7886577e47f13d827ca220c0831bae3ce00eea8c258352692f8c6
 -F src/os_unix.c f6e91b8fd82af7afbfd073c4974ad6cdb8e62d9f65ceddb45167835a0567fdc0
++F src/os_unix.c 711480e9152f221098ec2b0d4ef94dc798f08af649c34f5cd4dc2bbf40c4f556
+ F src/os_win.c 85d9e532d0444ab6c16d7431490c2e279e282aa0917b0e988996b1ae0de5c5a0
  F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
- F src/pager.c 811c1a1eed536bf30e12e69b700fb8fef254b33e8753cd5b354371bb2cf1e74f
- F src/pager.h e11e516208a460bea1b95fe3da697642306e7f350d5f96d2c1d21231ee4d2bf2
- F src/parse.y 52ef3cecd0934e9da4a45b585883a03243ad615d338ad94f44501a05891dcdfa
- F src/pcache.c 4bada070456980c3c1f16d58ec2e64e389ad77b935e3d77e0c96e7bbd397289c
- F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170
- F src/pcache1.c 716975564c15eb6679e97f734cec1bfd6c16ac3d4010f05f1f8e509fc7d19880
- F src/pragma.c 49a04b2ec3199b7967c19b3239182d9c4e860726909ed80b7a3e21fe5bb9e6c4
- F src/pragma.h bb83728944b42f6d409c77f5838a8edbdb0fe83046c5496ffc9602b40340a324
- F src/prepare.c 9a141a1b02dca53beaa9771699d390aafcac01f5d1f1c0ae6e23ded8dcdb709a
- F src/printf.c 40aee47ae9be4bd3dbdc8968bd07fddc027be8edec8daddf24d3391d36698a1c
 -F src/pager.c 75e0f3cfa3962c714f519f8a3d1e67ecca1c91de0e010a036b988e40ce9e4c73
 -F src/pager.h 217921e81eb5fe455caa5cda96061959706bcdd29ddb57166198645ef7822ac3
++F src/pager.c dd88ccf7fa519d6e325a115af28cd528713ea4d33a510d6f202386df002f2e74
++F src/pager.h 3abf6d65199fd0680b26a047c6167a96a4d6ead7535e02522b79f0fb27a3edec
+ F src/parse.y 6840fe7c0b5eb4dd25ee5d075213bc8255ed4c0678d71bfb6744d0520d91c179
+ F src/pcache.c 696a01f1a6370c1b50a09c15972bc3bee3333f8fcd1f2da8e9a76b1b062c59ee
+ F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
+ F src/pcache1.c bf9fcea656dce1cd2cca6b77a1d1d3552050d55a31c98bf0d9f405930a83bc95
 -F src/pragma.c 4e056f042683b99c4ea0db395f68d051b1a95833ab40951c40d3ef7e1fee1354
++F src/pragma.c dbc7a8fa3e057be14ebb4f1c6b9fa78a09d77367df0dd13faf1980ff591dd373
+ F src/pragma.h fdd03d78a7497f74a3f652909f945328480089189526841ae829ce7313d98d13
+ F src/prepare.c f81f8d707e583192c28fea0b2e19385415b7d188123b23f49b038076408d7a69
+ F src/printf.c 0f1177cf1dd4d7827bf64d840768514ec76409abecaca9e8b577dbd065150381
  F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
- F src/resolve.c 4324a94573b1e29286f8121e4881db59eaedc014afeb274c8d3e07ed282e0e20
- F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
- F src/select.c 42aca61e739c405ddd8a1b702977a7743c7d52a94885f7c5596bd7e73e6bff18
- F src/shell.c cbf450e75665a185c546adc702ec5fd091306ae7a08bc88b1508ac9c11acc7fe
- F src/shell.c.in e03f7d473e10b65c25836a058a3e7a1665ffb1fe712949dcd6e38c790e4eafd0
- F src/sqlite.h.in ab4f8a29d1580dfaeb6891fa1b83cff8229ba0daa56994707ceaca71495d9ab7
+ F src/resolve.c 4cfc44def0f0690ceaab8f6481f5d76284d7f9509aab6e218a679b4836a54614
+ F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
+ F src/select.c 61e867a906f140b73baf4ce7a201ad6dcba30820969f5618ee40e9a0d32c6f5f
+ F src/shell.c.in 482e23a370cbe5b0d4c73a0f0f5fce34f7caa08a14a8d75e12f0225c4e14915c
+ F src/sqlite.h.in cce9feede1c1c03923c091b4bbbd081dd77aaf92024cc2cdbf65f712c2f668c3
  F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
- F src/sqlite3ext.h a1fd3aa82f967da436164e0728a7d6841651fd0c6e27b9044e0eb9f6c8462e47
- F src/sqliteInt.h c07bc88eca1f59ce73e1f486187d0df4effe67c4579e112dfdd91c159e5c0569
+ F src/sqlite3ext.h 960f1b86c3610fa23cb6a267572a97dcf286e77aa0dd3b9b23292ffaa1ea8683
+ F src/sqliteInt.h 1161f7579cdd6217737a66517ef27f4016426603eff492e9b31f45a7d7d4c61f
  F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
- F src/status.c 9737ed017279a9e0c5da748701c3c7bf1e8ae0dae459aad20dd64fcff97a7e35
+ F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
  F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
- F src/tclsqlite.c 37be19e8ea1b4ec1e632d86272ada77bbb93f20b033df258ad3ed63dc4d40fa4
- F src/test1.c 8ef15f7a357f85dfc41c6c748ce9c947b4f676e01bb5ae6a45bee4923dff8b51
+ F src/tclsqlite.c e72862a271348d779672b45a730c33fd0c535e630ff927e8ce4a0c908d1d28c6
+ F src/test1.c 5390e5afb31fed61f72d0be0cb1b322d198a6e03fc13ff245ae76d17b4dcf2e9
  F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
- F src/test3.c b8434949dfb8aff8dfa082c8b592109e77844c2135ed3c492113839b6956255b
+ F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
  F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
  F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
  F src/test6.c e8d839fbc552ce044bec8234561a2d5b8819b48e29548ad0ba400471697946a8
@@@ -512,39 -559,43 +560,43 @@@ F src/test_server.c a2615049954cbb9cfb4
  F src/test_sqllog.c 11e6ce7575f489155c604ac4b439f2ac1d3d5aef
  F src/test_superlock.c 4839644b9201da822f181c5bc406c0b2385f672e
  F src/test_syscall.c 1073306ba2e9bfc886771871a13d3de281ed3939
 -F src/test_tclsh.c 06317648b0d85a85fd823f7973b55535c59a3156c1ef59394fe511f932cfa78d
++F src/test_tclsh.c c225ebf05d8905e204580198ffaa09847256ac58af04af632fde3fd760475d90
  F src/test_tclvar.c 33ff42149494a39c5fbb0df3d25d6fafb2f668888e41c0688d07273dcb268dfc
  F src/test_thread.c 911d15fb14e19c0c542bdc8aabf981c2f10a4858
- F src/test_vfs.c f0186261a24de2671d080bcd8050732f0cb64f6e
+ F src/test_vfs.c 112f1f9271c33c211812e0e681830a84262dac065da58579ff49f9cefec97d4f
  F src/test_vfstrace.c bab9594adc976cbe696ff3970728830b4c5ed698
- F src/test_windirent.c 17f91f5f2aa1bb7328abb49414c363b5d2a9d3ff
- F src/test_windirent.h 5d67483a55442e31e1bde0f4a230e6e932ad5906
+ F src/test_windirent.c a895e2c068a06644eef91a7f0a32182445a893b9a0f33d0cdb4283dca2486ac1
+ F src/test_windirent.h 90dfbe95442c9762357fe128dc7ae3dc199d006de93eb33ba3972e0a90484215
+ F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
  F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
  F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
- F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5
- F src/treeview.c 2ee4a5dada213d5ab08a742af5c876cee6f1aaae65f10a61923f3fb63846afef
- F src/trigger.c 48e0f7ed6749ce4d50a695e09e20ce9cf84ecabf2691852c965a51e0b620eccc
- F src/update.c 5404be9e840717323a69209190cdbc9d0d34adaedaaf1d1a1069babf2c4171c0
+ F src/tokenize.c 9e781e1ca80eefe7b5d6a9e2cd5c678c847da55fd6f093781fad7950934d4c83
+ F src/treeview.c 7b12ac059de54c939b6eb0dbffc9410c29c80d2470cee5cbe07d5ff9ea2d9253
+ F src/trigger.c d3d78568f37fb2e6cdcc2d1e7b60156f15b0b600adec55b83c5d42f6cad250bd
+ F src/update.c 1816d56c1bca1ba4e0ef98cac2f49be62858e9df1dc08844c7067eb41cc44274
+ F src/upsert.c 0dd81b40206841814d46942a7337786932475f085716042d0cb2fc7791bf8ca4
  F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
- F src/util.c 5168013cfd937a695d23cce8c67cb07a3dda242d4cb812530ba1148b88e0f159
- F src/vacuum.c 90839322fd5f00df9617eb21b68beda9b6e2a2937576b0d65985e4aeb1c53739
- F src/vdbe.c c3c3fe95d98df8757570fb265236cfdf31536d1025bf37c232237d1e422ae39b
- F src/vdbe.h d50cadf12bcf9fb99117ef392ce1ea283aa429270481426b6e8b0280c101fd97
- F src/vdbeInt.h 1fe00770144c12c4913128f35262d11527ef3284561baaab59b947a41c08d0d9
- F src/vdbeapi.c 9c670ca0dcc1cd86373aa353b747b26fe531ca5cd4331690c611d1f03842e2a1
- F src/vdbeaux.c c423065d50cee24bc8cba57764f5e9869a1bb920c50907f5dd363ebd7c5aef82
- F src/vdbeblob.c 635a79b60340a6a14a622ea8dcb081f0a66b1ac3836870c587f232eec08c0286
- F src/vdbemem.c 5c1533bf756918b4e46b2ed2bb82c29c7c651e1e37bbd0a0d8731a68787598ff
- F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
- F src/vdbetrace.c 48e11ebe040c6b41d146abed2602e3d00d621d7ebe4eb29b0a0f1617fd3c2f6c
- F src/vtab.c 0e4885495172e1bdf54b12cce23b395ac74ef5729031f15e1bc1e3e6b360ed1a
+ F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
+ F src/vacuum.c 836cadc922de866c849e23a75f93d344cdc143d388339305d09a3fed27e8798d
 -F src/vdbe.c 005e691ea4c7d51e6c1a69d9389aeb34700884c85f51681817ddea3fdc2fc39b
++F src/vdbe.c ee46b31b88015e1ba5b38b9405cada3c8d264bc5e19764ecc751e63aeb2edf66
+ F src/vdbe.h 5081dcc497777efe5e9ebe7330d283a044a005e4bdda2e2e984f03bf89a0d907
+ F src/vdbeInt.h 437e6c6af679fdf157867eb83a8adc6cf5145d6774453c2214cfd0bd01d92980
+ F src/vdbeapi.c ecccfce6f614c33a95952efeec969d163e8349eac314ee2b7b163eda921b5eb0
+ F src/vdbeaux.c f547901b1aa9e2d81c63f06893f633648e434180666a827aacb547d7d6c8a601
+ F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
+ F src/vdbemem.c 7b3305bc4a5139f4536ac9b5f61da0f915e49d2e3fdfa87dfdfa9d7aba8bc1e9
+ F src/vdbesort.c 90aad5a92608f2dd771c96749beabdb562c9d881131a860a7a5bccf66dc3be7f
+ F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
+ F src/vtab.c 70188a745dc4e57d26e942681ff4b2912b7c8249ad5de3f60f0677b4337bcfaa
  F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
- F src/wal.c c025455c9d6cf48ca55bd894be4a37a160565de9c845510a048fb9113761b2f4
- F src/wal.h b6063e6be1b03389372f3f32240e99b8ab92c32cdd05aa0e31b30a21e4e41654
- F src/walker.c 3ccfa8637f95355bff61144e01a615b8ef26f79c312880848da73f03367da1e6
- F src/where.c 049522adcf5426f1a8c3ed07be15e1ffa3266afd34e8e7bee64b63e2fbfad0b5
- F src/whereInt.h 82c04c5075308abbac59180c8bad5ecb45b07453981f60a53f3c7dee21e1e971
- F src/wherecode.c e8c2ece5843ea56e6c90277d421f2d628f3f7b7c976642369cc519f008e1d2b1
- F src/whereexpr.c afcac9cccfc0fdaccbdda94034a398947b6dc47dbf821c1b496261722832a6a4
 -F src/wal.c 3f4f653daf234fe713edbcbca3fec2350417d159d28801feabc702a22c4e213f
 -F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
++F src/wal.c e5b19ec1ce95882a2ae610d2657b441b079734427757c1a4d069f5f57bb7e9a2
++F src/wal.h fc6113057f2950fc14631176b748293e216fb385ea8df665e6d259e37f8f7d21
+ F src/walker.c fb94aadc9099ff9c6506d0a8b88d51266005bcaa265403f3d7caf732a562eb66
+ F src/where.c 3818e8a736a05d2cb194e64399af707e367fbcc5c251d785804d02eaf121288e
+ F src/whereInt.h f125f29fca80890768e0b2caa14f95db74b2dacd3a122a168f97aa7b64d6968f
+ F src/wherecode.c c45f03aefc2266b990df0fc4d7acc4e27f56f881f4fc0fc355b7cbc4d7189da5
+ F src/whereexpr.c 491f0894ad9903750cdecb7894437a0cabdffdd88f574d2b1c9ac85d14fe4b9c
+ F src/window.c 6550e2850ebced51100ef83d49b00a1cf03f81a482dafedafb0320df647ed8fc
  F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
  F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
  F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
@@@ -1099,15 -1179,16 +1180,16 @@@ F test/parser1.test 6ccdf5e459a5dc4673d
  F test/pcache.test c8acbedd3b6fd0f9a7ca887a83b11d24a007972b
  F test/pcache2.test af7f3deb1a819f77a6d0d81534e97d1cf62cd442
  F test/percentile.test 4243af26b8f3f4555abe166f723715a1f74c77ff
- F test/permutations.test 5fe80f417441ed6a69acd77f5190305dbf255cc2be214f8e36df4715c4f63f08
- F test/pragma.test c31b5e98998c160a4c85b1e04f590655c67f2daa7f73854640cd120610e3ac15
 -F test/permutations.test cf0b4e498db1d0143c19641d4420df7cc27fab2c95ed0abd2c7c5753beab25b8
++F test/permutations.test 57c9f829d72c91c89ae08febe205b73ec22751f476b4dafeafb614dfd263d74c
+ F test/pg_common.tcl 301ac19c1a52fd55166d26db929b3b89165c634d52b5f8ad76ea8cb06960db30
+ F test/pragma.test c267bf02742c823a191960895b3d52933cebd7beee26757d1ed694f213fcd867
  F test/pragma2.test e5d5c176360c321344249354c0c16aec46214c9f
- F test/pragma3.test 14c12bc5352b1e100e0b6b44f371053a81ccf8ed
- F test/pragma4.test 3046501bee2f652dc2a4f9c87781e2741361d6864439c8381aba6c3b774b335c
- F test/pragma5.test fd517f42ee847e126afbbbd9fd0fb9e5a4a61a962496a350adb8a22583fbdc37
+ F test/pragma3.test 8300aa9c63cff1027006ca34bf413a148abbd6dcd471fa9a1ded322fe18c0df9
+ F test/pragma4.test 52d8186f9e8d09b87189432cdd401dfa66d0b32445e837fa19046c8ae7621b0e
+ F test/pragma5.test 824ce6ced5d6b7ec71abe37fc6005ff836fe39d638273dc5192b39864b9ee983
  F test/pragmafault.test 275edaf3161771d37de60e5c2b412627ac94cef11739236bec12ed1258b240f8
- F test/printf.test b3ff34e73d59124140eaf89f7672e21bc2ca5fcc
- F test/printf2.test 9e6db85f81c63f2367c34a9d7db384088bd374ad
+ F test/printf.test a3e559bc9d922e7fe44e9d05c6965fee34fe3bc28300a4248c6a063425246ffd
+ F test/printf2.test 30b5dd0b4b992dc5626496846ecce17ff592cacbcb11c3e589f3ac4d7e129dae
  F test/progress.test ebab27f670bd0d4eb9d20d49cef96e68141d92fb
  F test/ptrchng.test ef1aa72d6cf35a2bbd0869a649b744e9d84977fc
  F test/pushdown.test 5e72c51c5e33253ed639ccee1e01ce62d62b6eee5ca893cd82334e4ee7b1d7fc
@@@ -1148,10 -1230,10 +1231,10 @@@ F test/savepoint.test 69c56b891ce0ff28f
  F test/savepoint2.test 9b8543940572a2f01a18298c3135ad0c9f4f67d7
  F test/savepoint4.test c8f8159ade6d2acd9128be61e1230f1c1edc6cc0
  F test/savepoint5.test 0735db177e0ebbaedc39812c8d065075d563c4fd
 -F test/savepoint6.test f41279c5e137139fa5c21485773332c7adb98cd7
 +F test/savepoint6.test 48a645a7bb3a59a6fcf06a7364cfe5b655c336760de39068f7c241b0fc80d963
  F test/savepoint7.test cde525ea3075283eb950cdcdefe23ead4f700daa
  F test/savepointfault.test f044eac64b59f09746c7020ee261734de82bf9b2
- F test/scanstatus.test 5253c219e331318a437f436268e0e82345700285
+ F test/scanstatus.test d14842d0a2757ee059bcffa365746453d60952ba1077980c9a348a9fefbd232a
  F test/schema.test 8f7999be894260f151adf15c2c7540f1c6d6a481
  F test/schema2.test 906408621ea881fdb496d878b1822572a34e32c5
  F test/schema3.test 1bc1008e1f8cb5654b248c55f27249366eb7ed38
@@@ -1243,28 -1334,29 +1335,29 @@@ F test/subselect.test 0966aa8e720224dbd
  F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a
  F test/subtype1.test 7fe09496352f97053af1437150751be2d0a0cae8
  F test/superlock.test ec94f0556b6488d97f71c79f9061ae08d9ab8f12
- F test/swarmvtab.test 05c4ca7b6ab0cc6f4c335a510347f99d741fa71366004699cf7dfa3cff4e2d17
- F test/swarmvtab2.test 038ef9bcad6fd2fb9e395196080cf23e223ddb1219015049a61540c161bc577d
- F test/swarmvtabfault.test 73563eefe3073c6fb3bb14475fb4ef5d4f2e3a67a02947ee0ca08980ea3dd7fe
- F test/symlink.test c9ebe7330d228249e447038276bfc8a7b22f4849
- F test/sync.test 2f84bdbc2b2df1fcb0220575b4b9f8cea94b7529
- F test/sync2.test 6be8ed007fa063b147773c1982b5bdba97a32badc536bdc6077eff5cf8710ece
+ F test/swarmvtab.test 9a3fd5ab3e9b3c976ad1b3d7646aab725114f2ac26b59395d0778b33bab6cdaf
+ F test/swarmvtab2.test c948cb2fdfc5b01d85e8f6d6504854202dc1a0782ab2a0ed61538f27cbd0aa5c
+ F test/swarmvtab3.test 247aa38b6ebd2b99db2075847ae47e789ac34f1c2ab5c720dfcffd990004c544
+ F test/swarmvtabfault.test 8a67a9f27c61073a47990829e92bc0c64420a807cb642b15a25f6c788210ed95
+ F test/symlink.test 0d816670325536b8973ec08d32b45136baddb80bd45fd178e0ce7a9e8153f3e7
+ F test/sync.test 89539f4973c010eda5638407e71ca7fddbcd8e0594f4c9980229f804d4333092
+ F test/sync2.test 8f9f7d4f6d5be8ca8941a8dadcc4299e558cb6a1ff653a9469146c7a76ef2039
  F test/syscall.test a39d9a36f852ae6e4800f861bc2f2e83f68bbc2112d9399931ecfadeabd2d69d
  F test/sysfault.test c9f2b0d8d677558f74de750c75e12a5454719d04
- F test/tabfunc01.test c47171c36b3d411df2bd49719dcaa5d034f8d277477fd41d253940723b969a51
- F test/table.test b708f3e5fa2542fa51dfab21fc07b36ea445cb2f
- F test/tableapi.test 2674633fa95d80da917571ebdd759a14d9819126
+ F test/tabfunc01.test 54300134f76db817685194d2f0e63e3fbf7380b45e0d426e00a9aee752497cfb
+ F test/table.test eb3463b7add9f16a5bb836badf118cf391b809d09fdccd1f79684600d07ec132
+ F test/tableapi.test ecbcc29c4ab62c1912c3717c48ea5c5e59f7d64e4a91034e6148bd2b82f177f4
  F test/tableopts.test dba698ba97251017b7c80d738c198d39ab747930
- F test/tclsqlite.test c3d7ac9449634b9f17fd048a3c0212e88a7448be810a9c5bd051acc1ffa00d2f
- F test/tempdb.test bd92eba8f20e16a9136e434e20b280794de3cdb6
- F test/tempdb2.test 27e41ed540b2f9b056c2e77e9bddc1b875358507
+ F test/tclsqlite.test dca8aa30d84175e7d8c8fc43d3ffa11fa56e23fbdac2679d03833a0f326edf34
+ F test/tempdb.test 4cdaa23ddd8acb4d79cbb1b68ccdfd09b0537aaba909ca69a876157c2a2cbd08
+ F test/tempdb2.test 2479226e4cb96f4c663eccd2d12c077cf6bda29ca5cc69a8a58a06127105dd62
  F test/tempfault.test 0c0d349c9a99bf5f374655742577f8712c647900
  F test/temptable.test d2c9b87a54147161bcd1822e30c1d1cd891e5b30
- F test/temptable2.test cd396beb41117a5302fff61767c35fa4270a0d5e
+ F test/temptable2.test d2940417496e2b9548e01d09990763fbe88c316504033256d51493e1f1a5ce6a
  F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
  F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
- F test/tester.tcl 71d30287dd22aae0eb9b07e62574336ae6a57e7a50e5dab320a604cfcca4b173
- F test/thread001.test 9f22fd3525a307ff42a326b6bc7b0465be1745a5
 -F test/tester.tcl fa5656391e3b477508abe12b3b81f019b2e71397399ab38a2f32d8d7f3bf8e56
++F test/tester.tcl f93080be43af7d7687e5817c62b4097e06ea2ed0f1af3ba773675a9d2f90e971
+ F test/thread001.test b61a29dd87cf669f5f6ac96124a7c97d71b0c80d9012746072055877055cf9ef
  F test/thread002.test e630504f8a06c00bf8bbe68528774dd96aeb2e58
  F test/thread003.test ee4c9efc3b86a6a2767516a37bd64251272560a7
  F test/thread004.test f51dfc3936184aaf73ee85f315224baad272a87f
@@@ -1455,10 -1548,15 +1549,15 @@@ F test/unionvtabfault.test e8759f3d14fb
  F test/unique.test 93f8b2ef5ea51b9495f8d6493429b1fd0f465264
  F test/unique2.test 3674e9f2a3f1fbbfd4772ac74b7a97090d0f77d2
  F test/unixexcl.test d936ba2b06794018e136418addd59a2354eeae97
- F test/unordered.test ca7adce0419e4ca0c50f039885e76ed2c531eda8
- F test/update.test 6c68446b8a0a33d522a7c72b320934596a2d7d32
- F test/update2.test fffc92e72ae568fe048588762e650cd8ccbd8c8b6e4fe9099231766bfe4b51de
+ F test/unordered.test ffeea7747d5ba962a8009a20b7e53d68cbae05b063604c68702c5998eb50c981
+ F test/update.test 1148de8d913e9817717990603aadeca07aab9ddbb10a30f167cbfd8d3a3ccb60
+ F test/update2.test 5e67667e1c54017d964e626db765cf8bedcf87483c184f4c575bdb8c1dd2313e
+ F test/upsert1.test 994bde41800bb77dbe32fcd2e1f6c4b49cc9f2c6cd345731c774dff02b51c110
+ F test/upsert2.test 9c3cdbb1a890227f6504ce4b0e3de68f4cdfa16bb21d8641208a9239896c5a09
+ F test/upsert3.test 88d7d590a1948a9cb6eac1b54b0642f67a9f35a1fc0f19b200e97d5d39e3179c
+ F test/upsert4.test 25d2a1da92f149331ae0c51ca6e3eee78189577585eab92de149900d62994fa5
+ F test/upsertfault.test f21ca47740841fdb4d61acfa7b17646d773e67724fe8c185b71c018db8a94b35
 -F test/uri.test 3481026f00ade6dfe8adb7acb6e1e47b04369568
 +F test/uri.test a2becabcb9fe25d08d1ae49c0788f4a75dda97bfe4c8641c2d04e224faf7a6e2
  F test/uri2.test 9d3ba7a53ee167572d53a298ee4a5d38ec4a8fb7
  F test/userauth01.test e740a2697a7b40d7c5003a7d7edaee16acd349a9
  F test/utf16align.test 54cd35a27c005a9b6e7815d887718780b6a462ae
@@@ -1490,12 -1588,10 +1589,12 @@@ F test/vtabH.test 3cf9aa1c1c4381b3b3ac3
  F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f
  F test/vtabJ.test d7b73675708cf63cfcb9d443bb451fc01a028347275b7311e51f9fdf3ca6757f
  F test/vtab_alter.test 736e66fb5ec7b4fee58229aa3ada2f27ec58bc58c00edae4836890c3784c6783
- F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8
+ F test/vtab_err.test dcc8b7b9cb67522b3fe7a272c73856829dae4ab7fdb30399aea1b6981bda2b65
  F test/vtab_shared.test 5253bff2355a9a3f014c15337da7e177ab0ef8ad
  F test/wal.test 613efec03e517e1775d86b993a54877d2e29a477
- F test/wal2.test 6ac39b94a284ebac6efb6be93b0cdfe73ee6083f129555e3144d8a615e9900ef
+ F test/wal2.test 155b9efa999bdb38ce1cd729b9a4fcdbffd6b88be27f039bad1d2929d287d918
 +F test/wal2rewrite.test 6ca6f631ffcf871240beab5f02608913fd075c6d0d31310b026c8383c65c9f9c
- F test/wal2simple.test 8719413446ca97ca88507c5b79f139b631faa5b4e177b7424008b26fc153da30
++F test/wal2simple.test 8c9dfb8f1bca01a0deb57f7074cdb83865c2292e89b13f7a51a1c160dca3f5f4
  F test/wal3.test 2a93004bc0fb2b5c29888964024695bade278ab2
  F test/wal4.test 4744e155cd6299c6bd99d3eab1c82f77db9cdb3c
  F test/wal5.test 9c11da7aeccd83a46d79a556ad11a18d3cb15aa9
@@@ -1659,7 -1779,10 +1782,7 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P d218d815f89cb1368fdb5e3f774b7adaaf02560a367ba0f3e54987e08dd6241a
- R 7d8085c250f90df872ed6ab275626e25
 -P 23684cb841ac2cb0d69e5470253bd96feb733762a7553b952a08470834fe85fa
 -R dcc394af10af6094607f56a6086fb748
 -T +bgcolor * #d0c0ff
 -T +sym-release *
 -T +sym-version-3.26.0 *
 -U drh
 -Z 560a5decbb241d53eb97e72267b6e6e8
++P 63955442304052f5adddd05ccaeebe87ddc5e25af695702793518f015b4f0a87 bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9
++R 434605294285e3da7c3ee68026e7ade7
 +U dan
- Z bca32e00ecb94225e888df6bb3b83e85
++Z 6329688d1e7210750fc075a6d45758a7
diff --cc manifest.uuid
index 91bc8d5baea226e73f532c4a529a24aaeef75806,fd08abb29b2e8dcc2683226ac91954e2287d0b78..bb767038c1a75ccc32b3ab24322959b0e5a0c23f
@@@ -1,1 -1,1 +1,1 @@@
- 63955442304052f5adddd05ccaeebe87ddc5e25af695702793518f015b4f0a87
 -bf8c1b2b7a5960c282e543b9c293686dccff272512d08865f4600fb58238b4f9
++7a44fa5a350a3f19b8e9f5196d22535788885f8c0e849572202bf64a055ddc2d
diff --cc src/btree.c
Simple merge
diff --cc src/os_unix.c
index 19ac47263f304e5125826571344fa369a46095f7,f20763e5b0bc96dbdb7947ec6f919081d946c845..82a19bf7e493393a0ccda7fc4229f3ec21950b8e
@@@ -4160,10 -4287,11 +4287,11 @@@ static int unixShmSystemLock
  
    /* Access to the unixShmNode object is serialized by the caller */
    pShmNode = pFile->pInode->pShmNode;
-   assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 );
+   assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
+   assert( pShmNode->nRef>0 || unixMutexHeld() );
  
    /* Shared locks never span more than one byte */
 -  assert( n==1 || lockType!=F_RDLCK );
 +  /* assert( n==1 || lockType!=F_RDLCK ); */
  
    /* Locks are within range */
    assert( n>=1 && n<=SQLITE_SHM_NLOCK );
@@@ -4609,9 -4794,9 +4794,9 @@@ static int unixShmLock
         || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
         || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
         || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
 -  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
 +  /* assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); */
-   assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 );
-   assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 );
+   assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
+   assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
  
    mask = (1<<(ofst+n)) - (1<<ofst);
    assert( n>1 || mask==(1<<ofst) );
diff --cc src/pager.c
index a50aa3eaa04c8c6c9ff0d704521dc7627facb7cc,e252a4b7a150a1d8536e9ebaa5ed694a77250a91..1dbbd4c6319f9671befb0d7a37b2c358a31a0803
@@@ -810,19 -811,44 +811,30 @@@ static const unsigned char aJournalMagi
  */
  #define PAGER_MAX_PGNO 2147483647
  
 -/*
 -** The argument to this macro is a file descriptor (type sqlite3_file*).
 -** Return 0 if it is not open, or non-zero (but not 1) if it is.
 -**
 -** This is so that expressions can be written as:
 -**
 -**   if( isOpen(pPager->jfd) ){ ...
 -**
 -** instead of
 -**
 -**   if( pPager->jfd->pMethods ){ ...
 -*/
 -#define isOpen(pFd) ((pFd)->pMethods!=0)
 -
+ #ifdef SQLITE_DIRECT_OVERFLOW_READ
  /*
- ** Return true if this pager uses a write-ahead log to read page pgno.
- ** Return false if the pager reads pgno directly from the database.
+ ** Return true if page pgno can be read directly from the database file
+ ** by the b-tree layer. This is the case if:
+ **
+ **   * the database file is open,
+ **   * there are no dirty pages in the cache, and
+ **   * the desired page is not currently in the wal file.
  */
- #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_DIRECT_OVERFLOW_READ)
- int sqlite3PagerUseWal(Pager *pPager, Pgno pgno){
-   u32 iRead = 0;
-   int rc;
-   if( pPager->pWal==0 ) return 0;
-   rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
-   return rc || iRead;
+ int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
+   if( pPager->fd->pMethods==0 ) return 0;
+   if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
+ #ifndef SQLITE_OMIT_WAL
+   if( pPager->pWal ){
+     u32 iRead = 0;
+     int rc;
+     rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
+     return (rc==SQLITE_OK && iRead==0);
+   }
+ #endif
+   return 1;
  }
  #endif
  #ifndef SQLITE_OMIT_WAL
  # define pagerUseWal(x) ((x)->pWal!=0)
  #else
diff --cc src/pager.h
index 399fcd4de33667058d8c682b9a399519acb67ba2,de96dc551537e8addc2b4f49136f93f0bfcc3a8d..d363fef4c36cda5e3d0b89c11213e9c8feed0484
@@@ -194,11 -177,8 +194,8 @@@ int sqlite3PagerSharedLock(Pager *pPage
    int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
    int sqlite3PagerWalSupported(Pager *pPager);
    int sqlite3PagerWalCallback(Pager *pPager);
 -  int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
 +  int sqlite3PagerOpenWal(Pager *pPager, int, int *pisOpen);
    int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
- # ifdef SQLITE_DIRECT_OVERFLOW_READ
-   int sqlite3PagerUseWal(Pager *pPager, Pgno);
- # endif
  # ifdef SQLITE_ENABLE_SNAPSHOT
    int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
    int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
diff --cc src/pragma.c
Simple merge
index 0000000000000000000000000000000000000000,ff0ac5742f1859c3281315c55062a6b414b624bb..98d34fce103c5433d2a32b5c03e037c27d798a9b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,203 +1,206 @@@
+ /*
+ ** 2017-10-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 contains extensions to the the "tclsqlite.c" module used for
+ ** testing.  Basically, all of the other "test_*.c" modules are linked
+ ** into the enhanced tclsh used for testing (and named "testfixture" or
+ ** "testfixture.exe") using logic encoded by this file.
+ **
+ ** The code in this file used to be found in tclsqlite3.c, contained within
+ ** #if SQLITE_TEST ... #endif.  It is factored out into this separate module
+ ** in an effort to keep the tclsqlite.c file pure.
+ */
+ #include "sqlite3.h"
+ #if defined(INCLUDE_SQLITE_TCL_H)
+ # include "sqlite_tcl.h"
+ #else
+ # include "tcl.h"
+ # ifndef SQLITE_TCLAPI
+ #  define SQLITE_TCLAPI
+ # endif
+ #endif
+ /* Needed for the setrlimit() system call on unix */
+ #if defined(unix)
+ #include <sys/resource.h>
+ #endif
+ /* Forward declaration */
+ static int SQLITE_TCLAPI load_testfixture_extensions(
+   ClientData cd,
+   Tcl_Interp *interp,
+   int objc,
+   Tcl_Obj *CONST objv[]
+ );
+ /*
+ ** This routine is the primary export of this file.
+ **
+ ** Configure the interpreter passed as the first argument to have access
+ ** to the commands and linked variables that make up:
+ **
+ **   * the [sqlite3] extension itself,
+ **
+ **   * If SQLITE_TCLMD5 or SQLITE_TEST is defined, the Md5 commands, and
+ **
+ **   * If SQLITE_TEST is set, the various test interfaces used by the Tcl
+ **     test suite.
+ */
+ const char *sqlite3TestInit(Tcl_Interp *interp){
+   extern int Sqlite3_Init(Tcl_Interp*);
+   extern int Sqliteconfig_Init(Tcl_Interp*);
+   extern int Sqlitetest1_Init(Tcl_Interp*);
+   extern int Sqlitetest2_Init(Tcl_Interp*);
+   extern int Sqlitetest3_Init(Tcl_Interp*);
+   extern int Sqlitetest4_Init(Tcl_Interp*);
+   extern int Sqlitetest5_Init(Tcl_Interp*);
+   extern int Sqlitetest6_Init(Tcl_Interp*);
+   extern int Sqlitetest7_Init(Tcl_Interp*);
+   extern int Sqlitetest8_Init(Tcl_Interp*);
+   extern int Sqlitetest9_Init(Tcl_Interp*);
+   extern int Sqlitetestasync_Init(Tcl_Interp*);
+   extern int Sqlitetest_autoext_Init(Tcl_Interp*);
+   extern int Sqlitetest_blob_Init(Tcl_Interp*);
+   extern int Sqlitetest_demovfs_Init(Tcl_Interp *);
+   extern int Sqlitetest_func_Init(Tcl_Interp*);
+   extern int Sqlitetest_hexio_Init(Tcl_Interp*);
+   extern int Sqlitetest_init_Init(Tcl_Interp*);
+   extern int Sqlitetest_malloc_Init(Tcl_Interp*);
+   extern int Sqlitetest_mutex_Init(Tcl_Interp*);
+   extern int Sqlitetestschema_Init(Tcl_Interp*);
+   extern int Sqlitetestsse_Init(Tcl_Interp*);
+   extern int Sqlitetesttclvar_Init(Tcl_Interp*);
+   extern int Sqlitetestfs_Init(Tcl_Interp*);
+   extern int SqlitetestThread_Init(Tcl_Interp*);
+   extern int SqlitetestOnefile_Init();
+   extern int SqlitetestOsinst_Init(Tcl_Interp*);
+   extern int Sqlitetestbackup_Init(Tcl_Interp*);
+   extern int Sqlitetestintarray_Init(Tcl_Interp*);
+   extern int Sqlitetestvfs_Init(Tcl_Interp *);
+   extern int Sqlitetestrtree_Init(Tcl_Interp*);
+   extern int Sqlitequota_Init(Tcl_Interp*);
+   extern int Sqlitemultiplex_Init(Tcl_Interp*);
+   extern int SqliteSuperlock_Init(Tcl_Interp*);
+   extern int SqlitetestSyscall_Init(Tcl_Interp*);
+ #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+   extern int TestSession_Init(Tcl_Interp*);
+ #endif
+   extern int Md5_Init(Tcl_Interp*);
+   extern int Fts5tcl_Init(Tcl_Interp *);
+   extern int SqliteRbu_Init(Tcl_Interp*);
+   extern int Sqlitetesttcl_Init(Tcl_Interp*);
++  extern int Bgckpt_Init(Tcl_Interp*);
+ #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
+   extern int Sqlitetestfts3_Init(Tcl_Interp *interp);
+ #endif
+ #ifdef SQLITE_ENABLE_ZIPVFS
+   extern int Zipvfs_Init(Tcl_Interp*);
+ #endif
+   extern int TestExpert_Init(Tcl_Interp*);
+   extern int Sqlitetest_window_Init(Tcl_Interp *);
+   Tcl_CmdInfo cmdInfo;
+   /* Since the primary use case for this binary is testing of SQLite,
+   ** be sure to generate core files if we crash */
+ #if defined(unix)
+   { struct rlimit x;
+     getrlimit(RLIMIT_CORE, &x);
+     x.rlim_cur = x.rlim_max;
+     setrlimit(RLIMIT_CORE, &x);
+   }
+ #endif /* unix */
+   if( Tcl_GetCommandInfo(interp, "sqlite3", &cmdInfo)==0 ){
+     Sqlite3_Init(interp);
+   }
+ #ifdef SQLITE_ENABLE_ZIPVFS
+   Zipvfs_Init(interp);
+ #endif
+   Md5_Init(interp);
+   Sqliteconfig_Init(interp);
+   Sqlitetest1_Init(interp);
+   Sqlitetest2_Init(interp);
+   Sqlitetest3_Init(interp);
+   Sqlitetest4_Init(interp);
+   Sqlitetest5_Init(interp);
+   Sqlitetest6_Init(interp);
+   Sqlitetest7_Init(interp);
+   Sqlitetest8_Init(interp);
+   Sqlitetest9_Init(interp);
+   Sqlitetestasync_Init(interp);
+   Sqlitetest_autoext_Init(interp);
+   Sqlitetest_blob_Init(interp);
+   Sqlitetest_demovfs_Init(interp);
+   Sqlitetest_func_Init(interp);
+   Sqlitetest_hexio_Init(interp);
+   Sqlitetest_init_Init(interp);
+   Sqlitetest_malloc_Init(interp);
+   Sqlitetest_mutex_Init(interp);
+   Sqlitetestschema_Init(interp);
+   Sqlitetesttclvar_Init(interp);
+   Sqlitetestfs_Init(interp);
+   SqlitetestThread_Init(interp);
+   SqlitetestOnefile_Init();
+   SqlitetestOsinst_Init(interp);
+   Sqlitetestbackup_Init(interp);
+   Sqlitetestintarray_Init(interp);
+   Sqlitetestvfs_Init(interp);
+   Sqlitetestrtree_Init(interp);
+   Sqlitequota_Init(interp);
+   Sqlitemultiplex_Init(interp);
+   SqliteSuperlock_Init(interp);
+   SqlitetestSyscall_Init(interp);
+ #if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
+   TestSession_Init(interp);
+ #endif
+   Fts5tcl_Init(interp);
+   SqliteRbu_Init(interp);
+   Sqlitetesttcl_Init(interp);
++  Bgckpt_Init(interp);
++
+ #if defined(SQLITE_ENABLE_FTS3) || defined(SQLITE_ENABLE_FTS4)
+   Sqlitetestfts3_Init(interp);
+ #endif
+   TestExpert_Init(interp);
+   Sqlitetest_window_Init(interp);
+   Tcl_CreateObjCommand(
+       interp, "load_testfixture_extensions", load_testfixture_extensions,0,0
+   );
+   return 0;
+ }
+ /* tclcmd:   load_testfixture_extensions
+ */
+ static int SQLITE_TCLAPI load_testfixture_extensions(
+   ClientData cd,
+   Tcl_Interp *interp,
+   int objc,
+   Tcl_Obj *CONST objv[]
+ ){
+   Tcl_Interp *slave;
+   if( objc!=2 ){
+     Tcl_WrongNumArgs(interp, 1, objv, "SLAVE");
+     return TCL_ERROR;
+   }
+   slave = Tcl_GetSlave(interp, Tcl_GetString(objv[1]));
+   if( !slave ){
+     return TCL_ERROR;
+   }
+   (void)sqlite3TestInit(slave);
+   return TCL_OK;
+ }
diff --cc src/vdbe.c
Simple merge
diff --cc src/wal.c
index 836e876c5cc9dbf732cd1304d30090d82a85238d,4088bf2ec851aeecf4cbb50a625bbecb27ed9832..42001bf2d2bd541c70ef886ac272ddd5fe386958
+++ b/src/wal.c
@@@ -414,25 -258,47 +418,46 @@@ int sqlite3WalTrace = 0
  # define WALTRACE(X)
  #endif
  
+ /*
+ ** WAL mode depends on atomic aligned 32-bit loads and stores in a few
+ ** places.  The following macros try to make this explicit.
+ */
+ #if GCC_VESRION>=5004000
+ # define AtomicLoad(PTR)       __atomic_load_n((PTR),__ATOMIC_RELAXED)
+ # define AtomicStore(PTR,VAL)  __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
+ #else
+ # define AtomicLoad(PTR)       (*(PTR))
+ # define AtomicStore(PTR,VAL)  (*(PTR) = (VAL))
+ #endif
  /*
 -** The maximum (and only) versions of the wal and wal-index formats
 -** that may be interpreted by this version of SQLite.
 -**
 -** If a client begins recovering a WAL file and finds that (a) the checksum
 -** values in the wal-header are correct and (b) the version field is not
 -** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
 -**
 -** Similarly, if a client successfully reads a wal-index header (i.e. the 
 -** checksum test is successful) and finds that the version field is not
 -** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
 -** returns SQLITE_CANTOPEN.
 +** Both the wal-file and the wal-index contain version fields 
 +** indicating the current version of the system. If a client
 +** reads the header of a wal file (as part of recovery), or the
 +** wal-index (as part of opening a read transaction) and (a) the
 +** header checksum is correct but (b) the version field is not
 +** recognized, the operation fails with SQLITE_CANTOPEN.
 +**
 +** Currently, clients support both version-1 ("journal_mode=wal") and
 +** version-2 ("journal_mode=wal2"). Legacy clients may support version-1
 +** only.
  */
 -#define WAL_MAX_VERSION      3007000
 -#define WALINDEX_MAX_VERSION 3007000
 +#define WAL_VERSION1 3007000      /* For "journal_mode=wal" */
 +#define WAL_VERSION2 3021000      /* For "journal_mode=wal2" */
  
  /*
- ** Indices of various locking bytes.   WAL_NREADER is the number
+ ** Index numbers for various locking bytes.   WAL_NREADER is the number
  ** of available reader locks and should be at least 3.  The default
  ** is SQLITE_SHM_NLOCK==8 and  WAL_NREADER==5.
+ **
+ ** Technically, the various VFSes are free to implement these locks however
+ ** they see fit.  However, compatibility is encouraged so that VFSes can
+ ** interoperate.  The standard implemention used on both unix and windows
+ ** is for the index number to indicate a byte offset into the
+ ** WalCkptInfo.aLock[] array in the wal-index header.  In other words, all
+ ** locks are on the shm file.  The WALINDEX_LOCK_OFFSET constant (which
+ ** should be 120) is the location in the shm file for the first locking
+ ** byte.
  */
  #define WAL_WRITE_LOCK         0
  #define WAL_ALL_BUT_WRITE      1
@@@ -1280,25 -998,26 +1328,25 @@@ static void walCleanupHash(Wal *pWal)
    ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed
    ** that the page said hash-table and array reside on is already mapped.
    */
 -  assert( pWal->nWiData>walFramePage(pWal->hdr.mxFrame) );
 -  assert( pWal->apWiData[walFramePage(pWal->hdr.mxFrame)] );
 -  walHashGet(pWal, walFramePage(pWal->hdr.mxFrame), &sLoc);
 +  assert( pWal->nWiData>walFramePage(iExternal) );
 +  assert( pWal->apWiData[walFramePage(iExternal)] );
-   walHashGet(pWal, walFramePage(iExternal), &aHash, &aPgno, &iZero);
++  walHashGet(pWal, walFramePage(iExternal), &sLoc);
  
    /* Zero all hash-table entries that correspond to frame numbers greater
    ** than pWal->hdr.mxFrame.
    */
-   iLimit = iExternal - iZero;
 -  iLimit = pWal->hdr.mxFrame - sLoc.iZero;
++  iLimit = iExternal - sLoc.iZero;
    assert( iLimit>0 );
    for(i=0; i<HASHTABLE_NSLOT; i++){
-     if( aHash[i]>iLimit ){
-       aHash[i] = 0;
+     if( sLoc.aHash[i]>iLimit ){
+       sLoc.aHash[i] = 0;
      }
    }
    
    /* Zero the entries in the aPgno array that correspond to frames with
 -  ** frame numbers greater than pWal->hdr.mxFrame. 
 -  */
 +  ** frame numbers greater than pWal->hdr.mxFrame.  */
-   nByte = (int)((char *)aHash - (char *)&aPgno[iLimit+1]);
-   memset((void *)&aPgno[iLimit+1], 0, nByte);
+   nByte = (int)((char *)sLoc.aHash - (char *)&sLoc.aPgno[iLimit+1]);
+   memset((void *)&sLoc.aPgno[iLimit+1], 0, nByte);
  
  #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
    /* Verify that the every entry in the mapping region is still reachable
  ** Set an entry in the wal-index that will map database page number
  ** pPage into WAL frame iFrame.
  */
 -static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
 +static int walIndexAppend(Wal *pWal, int iWal, u32 iFrame, u32 iPage){
    int rc;                         /* Return code */
-   u32 iZero = 0;                  /* One less than frame number of aPgno[1] */
-   volatile u32 *aPgno = 0;        /* Page number array */
-   volatile ht_slot *aHash = 0;    /* Hash table */
+   WalHashLoc sLoc;                /* Wal-index hash table location */
 +  u32 iExternal;
 +  
 +  if( isWalMode2(pWal) ){
 +    iExternal = walExternalEncode(iWal, iFrame);
 +  }else{
 +    assert( iWal==0 );
 +    iExternal = iFrame;
 +  }
  
-   rc = walHashGet(pWal, walFramePage(iExternal), &aHash, &aPgno, &iZero);
 -  rc = walHashGet(pWal, walFramePage(iFrame), &sLoc);
++  rc = walHashGet(pWal, walFramePage(iExternal), &sLoc);
  
    /* Assuming the wal-index file was successfully mapped, populate the
    ** page number array and hash table entry.
      int idx;                      /* Value to write to hash-table slot */
      int nCollide;                 /* Number of hash collisions */
  
-     idx = iExternal - iZero;
 -    idx = iFrame - sLoc.iZero;
++    idx = iExternal - sLoc.iZero;
      assert( idx <= HASHTABLE_NSLOT/2 + 1 );
      
      /* If this is the first entry to be added to this hash-table, zero the
@@@ -1562,12 -1133,9 +1611,11 @@@ static int walTruncateWal2(Wal *pWal)
  */
  static int walIndexRecover(Wal *pWal){
    int rc;                         /* Return Code */
 -  i64 nSize;                      /* Size of log file */
 -  u32 aFrameCksum[2] = {0, 0};
    int iLock;                      /* Lock offset to lock for checkpoint */
-   int nLock;                      /* Number of locks to hold */
 +  u32 nCkpt1 = 0xFFFFFFFF;
 +  u32 nCkpt2 = 0xFFFFFFFF;
 +  int bZero = 0;
 +  WalIndexHdr hdr;
  
    /* Obtain an exclusive lock on all byte in the locking range not already
    ** locked by the caller. The caller is guaranteed to have locked the
    if( rc ){
      return rc;
    }
    WALTRACE(("WAL%p: recovery begin...\n", pWal));
  
 -  memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
 -
 -  rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
 -  if( rc!=SQLITE_OK ){
 -    goto recovery_error;
 -  }
 -
 -  if( nSize>WAL_HDRSIZE ){
 -    u8 aBuf[WAL_HDRSIZE];         /* Buffer to load WAL header into */
 -    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
 -    int szFrame;                  /* Number of bytes in buffer aFrame[] */
 -    u8 *aData;                    /* Pointer to data part of aFrame buffer */
 -    int iFrame;                   /* Index of last frame read */
 -    i64 iOffset;                  /* Next offset to read from log file */
 -    int szPage;                   /* Page size according to the log */
 -    u32 magic;                    /* Magic value read from WAL header */
 -    u32 version;                  /* Magic value read from WAL header */
 -    int isValid;                  /* True if this frame is valid */
 -
 -    /* Read in the WAL header. */
 -    rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
 -    if( rc!=SQLITE_OK ){
 -      goto recovery_error;
 -    }
 -
 -    /* If the database page size is not a power of two, or is greater than
 -    ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid 
 -    ** data. Similarly, if the 'magic' value is invalid, ignore the whole
 -    ** WAL file.
 -    */
 -    magic = sqlite3Get4byte(&aBuf[0]);
 -    szPage = sqlite3Get4byte(&aBuf[8]);
 -    if( (magic&0xFFFFFFFE)!=WAL_MAGIC 
 -     || szPage&(szPage-1) 
 -     || szPage>SQLITE_MAX_PAGE_SIZE 
 -     || szPage<512 
 -    ){
 -      goto finished;
 -    }
 -    pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
 -    pWal->szPage = szPage;
 -    pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
 -    memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
 -
 -    /* Verify that the WAL header checksum is correct */
 -    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
 -        aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
 -    );
 -    if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
 -     || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
 -    ){
 -      goto finished;
 +  /* Recover the *-wal file. If a valid version-1 header is recovered
 +  ** from it, do not open the *-wal2 file. Even if it exists.
 +  **
 +  ** Otherwise, if the *-wal2 file exists or if the "wal2" flag was 
 +  ** specified when sqlite3WalOpen() was called, open and recover
 +  ** the *-wal2 file. Except, if the *-wal file was zero bytes in size,
 +  ** truncate the *-wal2 to zero bytes in size.
 +  **
 +  ** After this block has run, if the *-wal2 file is open the system
 +  ** starts up in VERSION2 mode. In this case pWal->hdr contains the 
 +  ** wal-index header considering only *-wal2. Stack variable hdr
 +  ** contains the wal-index header considering only *-wal. The hash 
 +  ** tables are populated for both.  
 +  **
 +  ** Or, if the *-wal2 file is not open, start up in VERSION1 mode.
 +  ** pWal->hdr is already populated.
 +  */
 +  rc = walIndexRecoverOne(pWal, 0, &nCkpt1, &bZero);
 +  assert( pWal->hdr.iVersion==0 
 +      || pWal->hdr.iVersion==WAL_VERSION1 
 +      || pWal->hdr.iVersion==WAL_VERSION2 
 +  );
 +  if( rc==SQLITE_OK && bZero ){
 +    rc = walTruncateWal2(pWal);
 +  }
 +  if( rc==SQLITE_OK && pWal->hdr.iVersion!=WAL_VERSION1 ){
 +    int bOpen = 1;
 +    sqlite3_vfs *pVfs = pWal->pVfs;
 +    if( pWal->hdr.iVersion==0 && pWal->bWal2==0 ){
 +      rc = sqlite3OsAccess(pVfs, pWal->zWalName2, SQLITE_ACCESS_EXISTS, &bOpen);
      }
 -
 -    /* Verify that the version number on the WAL format is one that
 -    ** are able to understand */
 -    version = sqlite3Get4byte(&aBuf[4]);
 -    if( version!=WAL_MAX_VERSION ){
 -      rc = SQLITE_CANTOPEN_BKPT;
 -      goto finished;
 +    if( rc==SQLITE_OK && bOpen ){
 +      rc = walOpenWal2(pWal);
 +      if( rc==SQLITE_OK ){
 +        hdr = pWal->hdr;
 +        rc = walIndexRecoverOne(pWal, 1, &nCkpt2, 0);
 +      }
      }
 +  }
  
 -    /* Malloc a buffer to read frames into. */
 -    szFrame = szPage + WAL_FRAME_HDRSIZE;
 -    aFrame = (u8 *)sqlite3_malloc64(szFrame);
 -    if( !aFrame ){
 -      rc = SQLITE_NOMEM_BKPT;
 -      goto recovery_error;
 -    }
 -    aData = &aFrame[WAL_FRAME_HDRSIZE];
 +  if( rc==SQLITE_OK ){
 +    volatile WalCkptInfo *pInfo;
  
 -    /* Read all frames from the log file. */
 -    iFrame = 0;
 -    for(iOffset=WAL_HDRSIZE; (iOffset+szFrame)<=nSize; iOffset+=szFrame){
 -      u32 pgno;                   /* Database page number for frame */
 -      u32 nTruncate;              /* dbsize field from frame header */
 +    if( isOpen(pWal->apWalFd[1]) ){
 +      /* The case where *-wal2 may follow *-wal */
 +      if( nCkpt2<=0x0F && nCkpt2==nCkpt1+1 ){
 +        if( sqlite3Get4byte((u8*)(&pWal->hdr.aSalt[0]))==hdr.aFrameCksum[0]
 +            && sqlite3Get4byte((u8*)(&pWal->hdr.aSalt[1]))==hdr.aFrameCksum[1]
 +          ){
 +          walidxSetFile(&pWal->hdr, 1);
 +          walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +          walidxSetMxFrame(&pWal->hdr, 0, hdr.mxFrame);
 +        }else{
 +          pWal->hdr = hdr;
 +        }
 +      }else
  
 -      /* Read and decode the next log frame. */
 -      iFrame++;
 -      rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
 -      if( rc!=SQLITE_OK ) break;
 -      isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
 -      if( !isValid ) break;
 -      rc = walIndexAppend(pWal, iFrame, pgno);
 -      if( rc!=SQLITE_OK ) break;
 +      /* When *-wal may follow *-wal2 */
 +      if( (nCkpt2==0x0F && nCkpt1==0) || (nCkpt2<0x0F && nCkpt2==nCkpt1-1) ){
 +        if( sqlite3Get4byte((u8*)(&hdr.aSalt[0]))==pWal->hdr.aFrameCksum[0]
 +         && sqlite3Get4byte((u8*)(&hdr.aSalt[1]))==pWal->hdr.aFrameCksum[1]
 +        ){
 +          SWAP(WalIndexHdr, pWal->hdr, hdr);
 +          walidxSetMxFrame(&pWal->hdr, 1, hdr.mxFrame);
 +        }else{
 +          walidxSetFile(&pWal->hdr, 1);
 +          walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +          walidxSetMxFrame(&pWal->hdr, 0, 0);
 +        }
 +      }else
  
 -      /* If nTruncate is non-zero, this is a commit record. */
 -      if( nTruncate ){
 -        pWal->hdr.mxFrame = iFrame;
 -        pWal->hdr.nPage = nTruncate;
 -        pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
 -        testcase( szPage<=32768 );
 -        testcase( szPage>=65536 );
 -        aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
 -        aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
 +      /* Fallback */
 +      if( nCkpt1<=nCkpt2 ){
 +        pWal->hdr = hdr;
 +      }else{
 +        walidxSetFile(&pWal->hdr, 1);
 +        walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +        walidxSetMxFrame(&pWal->hdr, 0, 0);
        }
 +      pWal->hdr.iVersion = WAL_VERSION2;
 +    }else{
 +      pWal->hdr.iVersion = WAL_VERSION1;
      }
  
 -    sqlite3_free(aFrame);
 -  }
 -
 -finished:
 -  if( rc==SQLITE_OK ){
 -    volatile WalCkptInfo *pInfo;
 -    int i;
 -    pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
 -    pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
      walIndexWriteHdr(pWal);
  
      /* Reset the checkpoint-header. This is safe because this thread is 
      }
    }
  
 -recovery_error:
    WALTRACE(("WAL%p: recovery %s\n", pWal, rc ? "failed" : "ok"));
-   walUnlockExclusive(pWal, iLock, nLock);
+   walUnlockExclusive(pWal, iLock, WAL_READ_LOCK(0)-iLock);
+   walUnlockExclusive(pWal, WAL_READ_LOCK(1), WAL_NREADER-1);
    return rc;
  }
  
  /*
 -** Close an open wal-index.
 +** Close an open wal-index and wal files.
  */
  static void walIndexClose(Wal *pWal, int isDelete){
-   if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE ){
+   if( pWal->exclusiveMode==WAL_HEAPMEMORY_MODE || pWal->bShmUnreliable ){
      int i;
      for(i=0; i<pWal->nWiData; i++){
        sqlite3_free((void *)pWal->apWiData[i]);
        pWal->apWiData[i] = 0;
      }
-   }else{
+   }
+   if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
      sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
    }
 +  sqlite3OsClose(pWal->apWalFd[0]);
 +  sqlite3OsClose(pWal->apWalFd[1]);
  }
  
  /* 
@@@ -2021,28 -1599,25 +2077,35 @@@ static void walIteratorFree(WalIterato
  
  /*
  ** Construct a WalInterator object that can be used to loop over all 
- ** pages in the WAL in ascending order. The caller must hold the checkpoint
- ** lock.
 -** pages in the WAL following frame nBackfill in ascending order. Frames
++** pages in wal file iWal following frame nBackfill in ascending order. Frames
+ ** nBackfill or earlier may be included - excluding them is an optimization
+ ** only. The caller must hold the checkpoint lock.
  **
 -** On success, make *pp point to the newly allocated WalInterator object
 -** return SQLITE_OK. Otherwise, return an error code. If this routine
 -** returns an error, the value of *pp is undefined.
 +** On success, make *pp point to the newly allocated WalIterator object
 +** and return SQLITE_OK. Otherwise, return an error code. If this routine
 +** returns an error, the final value of *pp is undefined.
  **
  ** The calling routine should invoke walIteratorFree() to destroy the
  ** WalIterator object when it has finished with it.
  */
- static int walIteratorInit(Wal *pWal, int iWal, WalIterator **pp){
 -static int walIteratorInit(Wal *pWal, u32 nBackfill, WalIterator **pp){
++static int walIteratorInit(
++  Wal *pWal, 
++  int iWal, 
++  u32 nBackfill, 
++  WalIterator **pp
++){
    WalIterator *p;                 /* Return value */
    int nSegment;                   /* Number of segments to merge */
    u32 iLast;                      /* Last frame in log */
    int nByte;                      /* Number of bytes to allocate */
    int i;                          /* Iterator variable */
 +  int iLastSeg;                   /* Last hash table to iterate though */
    ht_slot *aTmp;                  /* Temp space used by merge-sort */
    int rc = SQLITE_OK;             /* Return Code */
 +  int iMode = isWalMode2(pWal) ? 2 : 1;
 +
 +  assert( isWalMode2(pWal) || iWal==0 );
++  assert( 0==isWalMode2(pWal) || nBackfill==0 );
  
    /* This routine only runs while holding the checkpoint lock. And
    ** it only runs if there is actually content in the log (mxFrame>0).
      rc = SQLITE_NOMEM_BKPT;
    }
  
-   for(i=iWal; rc==SQLITE_OK && i<=iLastSeg; i+=iMode){
-     volatile ht_slot *aHash;
-     u32 iExtZero;
-     volatile u32 *aPgno;
 -  for(i=walFramePage(nBackfill+1); rc==SQLITE_OK && i<nSegment; i++){
++  i = iMode==2 ? iWal : walFramePage(nBackfill+1);
++  for(; rc==SQLITE_OK && i<=iLastSeg; i+=iMode){
+     WalHashLoc sLoc;
  
-     rc = walHashGet(pWal, i, &aHash, &aPgno, &iExtZero);
+     rc = walHashGet(pWal, i, &sLoc);
      if( rc==SQLITE_OK ){
        int j;                      /* Counter variable */
        int nEntry;                 /* Number of entries in this segment */
        ht_slot *aIndex;            /* Sorted index for this segment */
-         walExternalDecode(iExtZero+1, &iZero);
 +      u32 iZero;
 +
 +      if( iMode==2 ){
-         iZero = iExtZero;
++        walExternalDecode(sLoc.iZero+1, &iZero);
 +        iZero--;
 +        assert( iZero==0 || i>=2 );
 +      }else{
++        iZero = sLoc.iZero;
 +      }
  
-       aPgno++;
+       sLoc.aPgno++;
 -      if( (i+1)==nSegment ){
 -        nEntry = (int)(iLast - sLoc.iZero);
 +      if( i==iLastSeg ){
 +        nEntry = (int)(iLast - iZero);
        }else{
-         nEntry = (int)((u32*)aHash - (u32*)aPgno);
+         nEntry = (int)((u32*)sLoc.aHash - (u32*)sLoc.aPgno);
        }
 -      aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[sLoc.iZero];
 -      sLoc.iZero++;
 +      aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[iZero];
 +      iZero++;
    
        for(j=0; j<nEntry; j++){
          aIndex[j] = (ht_slot)j;
        }
-       walMergesort((u32 *)aPgno, aTmp, aIndex, &nEntry);
 -      walMergesort((u32 *)sLoc.aPgno, aTmp, aIndex, &nEntry);
 -      p->aSegment[i].iZero = sLoc.iZero;
 -      p->aSegment[i].nEntry = nEntry;
 -      p->aSegment[i].aIndex = aIndex;
 -      p->aSegment[i].aPgno = (u32 *)sLoc.aPgno;
++      walMergesort((u32*)sLoc.aPgno, aTmp, aIndex, &nEntry);
 +      p->aSegment[i/iMode].iZero = iZero;
 +      p->aSegment[i/iMode].nEntry = nEntry;
 +      p->aSegment[i/iMode].aIndex = aIndex;
-       p->aSegment[i/iMode].aPgno = (u32 *)aPgno;
++      p->aSegment[i/iMode].aPgno = (u32*)sLoc.aPgno;
      }
    }
    sqlite3_free(aTmp);
@@@ -2245,80 -1800,66 +2308,81 @@@ static int walCheckpoint
    testcase( szPage<=32768 );
    testcase( szPage>=65536 );
    pInfo = walCkptInfo(pWal);
 -  if( pInfo->nBackfill<pWal->hdr.mxFrame ){
 +  if( (bWal2==1 && pInfo->nBackfill==0 && mxSafeFrame) 
-    || (bWal2==0 && pInfo->nBackfill<mxSafeFrame) 
++   || (bWal2==0 && pInfo->nBackfill<mxSafeFrame)
 +  ){
 +    sqlite3_file *pWalFd = pWal->apWalFd[iCkpt];
 +    mxPage = pWal->hdr.nPage;
 +
 +    /* If this is a wal2 system, check for a reader holding a lock 
 +    ** preventing this checkpoint operation. If one is found, return
 +    ** early.  */
 +    if( bWal2 ){
 +      rc = walLockExclusive(pWal, WAL_READ_LOCK(1 + iCkpt*2), 1);
 +      if( rc!=SQLITE_OK ) return rc;
 +    }
  
-     /* Allocate the iterator */
-     rc = walIteratorInit(pWal, iCkpt, &pIter);
-     if( rc!=SQLITE_OK ){
-       return rc;
-     }
-     assert( pIter );
      /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
      ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
      assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
  
 -    /* Compute in mxSafeFrame the index of the last frame of the WAL that is
 -    ** safe to write into the database.  Frames beyond mxSafeFrame might
 -    ** overwrite database pages that are in use by active readers and thus
 -    ** cannot be backfilled from the WAL.
 +    /* If this is a wal system (not wal2), compute in mxSafeFrame the index 
 +    ** of the last frame of the WAL that is safe to write into the database.
 +    ** Frames beyond mxSafeFrame might overwrite database pages that are in 
 +    ** use by active readers and thus cannot be backfilled from the WAL.
      */
 -    mxSafeFrame = pWal->hdr.mxFrame;
 -    mxPage = pWal->hdr.nPage;
 -    for(i=1; i<WAL_NREADER; i++){
 -      /* Thread-sanitizer reports that the following is an unsafe read,
 -      ** as some other thread may be in the process of updating the value
 -      ** of the aReadMark[] slot. The assumption here is that if that is
 -      ** happening, the other client may only be increasing the value,
 -      ** not decreasing it. So assuming either that either the "old" or
 -      ** "new" version of the value is read, and not some arbitrary value
 -      ** that would never be written by a real client, things are still 
 -      ** safe.  */
 -      u32 y = pInfo->aReadMark[i];
 -      if( mxSafeFrame>y ){
 -        assert( y<=pWal->hdr.mxFrame );
 -        rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
 -        if( rc==SQLITE_OK ){
 -          pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
 -          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 -        }else if( rc==SQLITE_BUSY ){
 -          mxSafeFrame = y;
 -          xBusy = 0;
 -        }else{
 -          goto walcheckpoint_out;
 +    if( bWal2==0 ){
++      mxSafeFrame = pWal->hdr.mxFrame;
++      mxPage = pWal->hdr.nPage;
 +      for(i=1; i<WAL_NREADER; i++){
 +        /* Thread-sanitizer reports that the following is an unsafe read,
 +        ** as some other thread may be in the process of updating the value
 +        ** of the aReadMark[] slot. The assumption here is that if that is
 +        ** happening, the other client may only be increasing the value,
 +        ** not decreasing it. So assuming either that either the "old" or
 +        ** "new" version of the value is read, and not some arbitrary value
 +        ** that would never be written by a real client, things are still 
 +        ** safe.  */
 +        u32 y = pInfo->aReadMark[i];
 +        if( mxSafeFrame>y ){
 +          assert( y<=pWal->hdr.mxFrame );
 +          rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(i), 1);
 +          if( rc==SQLITE_OK ){
 +            pInfo->aReadMark[i] = (i==1 ? mxSafeFrame : READMARK_NOT_USED);
 +            walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +          }else if( rc==SQLITE_BUSY ){
 +            mxSafeFrame = y;
 +            xBusy = 0;
 +          }else{
 +            goto walcheckpoint_out;
 +          }
          }
        }
      }
  
-     if( bWal2 || (pInfo->nBackfill<mxSafeFrame
+     /* Allocate the iterator */
 -    if( pInfo->nBackfill<mxSafeFrame ){
 -      rc = walIteratorInit(pWal, pInfo->nBackfill, &pIter);
++    if( bWal2 || pInfo->nBackfill<mxSafeFrame ){
++      assert( bWal2==0 || pInfo->nBackfill==0 );
++      rc = walIteratorInit(pWal, iCkpt, pInfo->nBackfill, &pIter);
+       assert( rc==SQLITE_OK || pIter==0 );
+     }
+     if( pIter
       && (rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_READ_LOCK(0),1))==SQLITE_OK
-     )){
-       i64 nSize;                    /* Current size of database file */
+     ){
        u32 nBackfill = pInfo->nBackfill;
  
 +      assert( bWal2==0 || nBackfill==0 );
        pInfo->nBackfillAttempted = mxSafeFrame;
  
 -      /* Sync the WAL to disk */
 -      rc = sqlite3OsSync(pWal->pWalFd, CKPT_SYNC_FLAGS(sync_flags));
 +      /* Sync the wal file being checkpointed to disk */
 +      rc = sqlite3OsSync(pWalFd, CKPT_SYNC_FLAGS(sync_flags));
  
        /* If the database may grow as a result of this checkpoint, hint
 -      ** about the eventual size of the db file to the VFS layer.
 -      */
 +      ** about the eventual size of the db file to the VFS layer.  */
        if( rc==SQLITE_OK ){
          i64 nReq = ((i64)mxPage * szPage);
+         i64 nSize;                    /* Current size of database file */
          rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
          if( rc==SQLITE_OK && nSize<nReq ){
            sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
          if( rc!=SQLITE_OK ) break;
        }
  
-       /* Truncate the db file, sync the wal file and set the WalCkptInfo
-       ** flag to indicate that it has been checkpointed. */
 -      /* If work was actually accomplished... */
 -      if( rc==SQLITE_OK ){
 -        if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
++      /* If work was actually accomplished, truncate the db file, sync the wal
++      ** file and set WalCkptInfo.nBackfill to indicate so. */
 +      if( rc==SQLITE_OK && (bWal2 || mxSafeFrame==walIndexHdr(pWal)->mxFrame) ){
 +        if( !bWal2 ){
            i64 szDb = pWal->hdr.nPage*(i64)szPage;
            testcase( IS_BIG_INT(szDb) );
            rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
@@@ -2649,20 -2196,196 +2739,198 @@@ static int walIndexReadHdr(Wal *pWal, i
    ** sure the wal-index was not constructed with some future format that
    ** this version of SQLite cannot understand.
    */
 -  if( badHdr==0 && pWal->hdr.iVersion!=WALINDEX_MAX_VERSION ){
 +  if( badHdr==0 
 +   && pWal->hdr.iVersion!=WAL_VERSION1 && pWal->hdr.iVersion!=WAL_VERSION2
 +  ){
      rc = SQLITE_CANTOPEN_BKPT;
    }
+   if( pWal->bShmUnreliable ){
+     if( rc!=SQLITE_OK ){
+       walIndexClose(pWal, 0);
+       pWal->bShmUnreliable = 0;
+       assert( pWal->nWiData>0 && pWal->apWiData[0]==0 );
+       /* walIndexRecover() might have returned SHORT_READ if a concurrent
+       ** writer truncated the WAL out from under it.  If that happens, it
+       ** indicates that a writer has fixed the SHM file for us, so retry */
+       if( rc==SQLITE_IOERR_SHORT_READ ) rc = WAL_RETRY;
+     }
+     pWal->exclusiveMode = WAL_NORMAL_MODE;
+   }
  
    return rc;
  }
  
  /*
- ** This is the value that walTryBeginRead returns when it needs to
- ** be retried.
+ ** Open a transaction in a connection where the shared-memory is read-only
+ ** and where we cannot verify that there is a separate write-capable connection
+ ** on hand to keep the shared-memory up-to-date with the WAL file.
+ **
+ ** This can happen, for example, when the shared-memory is implemented by
+ ** memory-mapping a *-shm file, where a prior writer has shut down and
+ ** left the *-shm file on disk, and now the present connection is trying
+ ** to use that database but lacks write permission on the *-shm file.
+ ** Other scenarios are also possible, depending on the VFS implementation.
+ **
+ ** Precondition:
+ **
+ **    The *-wal file has been read and an appropriate wal-index has been
+ **    constructed in pWal->apWiData[] using heap memory instead of shared
+ **    memory. 
+ **
+ ** If this function returns SQLITE_OK, then the read transaction has
+ ** been successfully opened. In this case output variable (*pChanged) 
+ ** is set to true before returning if the caller should discard the
+ ** contents of the page cache before proceeding. Or, if it returns 
+ ** WAL_RETRY, then the heap memory wal-index has been discarded and 
+ ** the caller should retry opening the read transaction from the 
+ ** beginning (including attempting to map the *-shm file). 
+ **
+ ** If an error occurs, an SQLite error code is returned.
  */
- #define WAL_RETRY  (-1)
+ static int walBeginShmUnreliable(Wal *pWal, int *pChanged){
+   i64 szWal;                      /* Size of wal file on disk in bytes */
+   i64 iOffset;                    /* Current offset when reading wal file */
+   u8 aBuf[WAL_HDRSIZE];           /* Buffer to load WAL header into */
+   u8 *aFrame = 0;                 /* Malloc'd buffer to load entire frame */
+   int szFrame;                    /* Number of bytes in buffer aFrame[] */
+   u8 *aData;                      /* Pointer to data part of aFrame buffer */
+   volatile void *pDummy;          /* Dummy argument for xShmMap */
+   int rc;                         /* Return code */
+   u32 aSaveCksum[2];              /* Saved copy of pWal->hdr.aFrameCksum */
+   assert( pWal->bShmUnreliable );
+   assert( pWal->readOnly & WAL_SHM_RDONLY );
+   assert( pWal->nWiData>0 && pWal->apWiData[0] );
+   /* Take WAL_READ_LOCK(0). This has the effect of preventing any
+   ** writers from running a checkpoint, but does not stop them
+   ** from running recovery.  */
+   rc = walLockShared(pWal, WAL_READ_LOCK(0));
+   if( rc!=SQLITE_OK ){
+     if( rc==SQLITE_BUSY ) rc = WAL_RETRY;
+     goto begin_unreliable_shm_out;
+   }
+   pWal->readLock = 0;
+   /* Check to see if a separate writer has attached to the shared-memory area,
+   ** thus making the shared-memory "reliable" again.  Do this by invoking
+   ** the xShmMap() routine of the VFS and looking to see if the return
+   ** is SQLITE_READONLY instead of SQLITE_READONLY_CANTINIT.
+   **
+   ** If the shared-memory is now "reliable" return WAL_RETRY, which will
+   ** cause the heap-memory WAL-index to be discarded and the actual
+   ** shared memory to be used in its place.
+   **
+   ** This step is important because, even though this connection is holding
+   ** the WAL_READ_LOCK(0) which prevents a checkpoint, a writer might
+   ** have already checkpointed the WAL file and, while the current
+   ** is active, wrap the WAL and start overwriting frames that this
+   ** process wants to use.
+   **
+   ** Once sqlite3OsShmMap() has been called for an sqlite3_file and has
+   ** returned any SQLITE_READONLY value, it must return only SQLITE_READONLY
+   ** or SQLITE_READONLY_CANTINIT or some error for all subsequent invocations,
+   ** even if some external agent does a "chmod" to make the shared-memory
+   ** writable by us, until sqlite3OsShmUnmap() has been called.
+   ** This is a requirement on the VFS implementation.
+    */
+   rc = sqlite3OsShmMap(pWal->pDbFd, 0, WALINDEX_PGSZ, 0, &pDummy);
+   assert( rc!=SQLITE_OK ); /* SQLITE_OK not possible for read-only connection */
+   if( rc!=SQLITE_READONLY_CANTINIT ){
+     rc = (rc==SQLITE_READONLY ? WAL_RETRY : rc);
+     goto begin_unreliable_shm_out;
+   }
+   /* We reach this point only if the real shared-memory is still unreliable.
+   ** Assume the in-memory WAL-index substitute is correct and load it
+   ** into pWal->hdr.
+   */
+   memcpy(&pWal->hdr, (void*)walIndexHdr(pWal), sizeof(WalIndexHdr));
+   /* Make sure some writer hasn't come in and changed the WAL file out
+   ** from under us, then disconnected, while we were not looking.
+   */
 -  rc = sqlite3OsFileSize(pWal->pWalFd, &szWal);
++  rc = sqlite3OsFileSize(pWal->apWalFd[0], &szWal);
+   if( rc!=SQLITE_OK ){
+     goto begin_unreliable_shm_out;
+   }
+   if( szWal<WAL_HDRSIZE ){
+     /* If the wal file is too small to contain a wal-header and the
+     ** wal-index header has mxFrame==0, then it must be safe to proceed
+     ** reading the database file only. However, the page cache cannot
+     ** be trusted, as a read/write connection may have connected, written
+     ** the db, run a checkpoint, truncated the wal file and disconnected
+     ** since this client's last read transaction.  */
+     *pChanged = 1;
+     rc = (pWal->hdr.mxFrame==0 ? SQLITE_OK : WAL_RETRY);
+     goto begin_unreliable_shm_out;
+   }
+   /* Check the salt keys at the start of the wal file still match. */
 -  rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
++  rc = sqlite3OsRead(pWal->apWalFd[0], aBuf, WAL_HDRSIZE, 0);
+   if( rc!=SQLITE_OK ){
+     goto begin_unreliable_shm_out;
+   }
+   if( memcmp(&pWal->hdr.aSalt, &aBuf[16], 8) ){
+     /* Some writer has wrapped the WAL file while we were not looking.
+     ** Return WAL_RETRY which will cause the in-memory WAL-index to be
+     ** rebuilt. */
+     rc = WAL_RETRY;
+     goto begin_unreliable_shm_out;
+   }
+   /* Allocate a buffer to read frames into */
+   szFrame = pWal->hdr.szPage + WAL_FRAME_HDRSIZE;
+   aFrame = (u8 *)sqlite3_malloc64(szFrame);
+   if( aFrame==0 ){
+     rc = SQLITE_NOMEM_BKPT;
+     goto begin_unreliable_shm_out;
+   }
+   aData = &aFrame[WAL_FRAME_HDRSIZE];
+   /* Check to see if a complete transaction has been appended to the
+   ** wal file since the heap-memory wal-index was created. If so, the
+   ** heap-memory wal-index is discarded and WAL_RETRY returned to
+   ** the caller.  */
+   aSaveCksum[0] = pWal->hdr.aFrameCksum[0];
+   aSaveCksum[1] = pWal->hdr.aFrameCksum[1];
+   for(iOffset=walFrameOffset(pWal->hdr.mxFrame+1, pWal->hdr.szPage); 
+       iOffset+szFrame<=szWal; 
+       iOffset+=szFrame
+   ){
+     u32 pgno;                   /* Database page number for frame */
+     u32 nTruncate;              /* dbsize field from frame header */
+     /* Read and decode the next log frame. */
 -    rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
++    rc = sqlite3OsRead(pWal->apWalFd[0], aFrame, szFrame, iOffset);
+     if( rc!=SQLITE_OK ) break;
+     if( !walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame) ) break;
+     /* If nTruncate is non-zero, then a complete transaction has been
+     ** appended to this wal file. Set rc to WAL_RETRY and break out of
+     ** the loop.  */
+     if( nTruncate ){
+       rc = WAL_RETRY;
+       break;
+     }
+   }
+   pWal->hdr.aFrameCksum[0] = aSaveCksum[0];
+   pWal->hdr.aFrameCksum[1] = aSaveCksum[1];
+  begin_unreliable_shm_out:
+   sqlite3_free(aFrame);
+   if( rc!=SQLITE_OK ){
+     int i;
+     for(i=0; i<pWal->nWiData; i++){
+       sqlite3_free((void*)pWal->apWiData[i]);
+       pWal->apWiData[i] = 0;
+     }
+     pWal->bShmUnreliable = 0;
+     sqlite3WalEndReadTransaction(pWal);
+     *pChanged = 1;
+   }
+   return rc;
+ }
  
  /*
  ** Attempt to start a read transaction.  This might fail due to a race or
  */
  static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
    volatile WalCkptInfo *pInfo;    /* Checkpoint information in wal-index */
 -  u32 mxReadMark;                 /* Largest aReadMark[] value */
 -  int mxI;                        /* Index of largest aReadMark[] value */
 -  int i;                          /* Loop counter */
    int rc = SQLITE_OK;             /* Return code  */
 -  u32 mxFrame;                    /* Wal frame to lock to */
  
 -  assert( pWal->readLock<0 );     /* Not currently locked */
 +  assert( pWal->readLock==WAL_LOCK_NONE );     /* Not currently locked */
  
+   /* useWal may only be set for read/write connections */
+   assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );
    /* Take steps to avoid spinning forever if there is a protocol error.
    **
    ** Circumstances that cause a RETRY should only last for the briefest
      if( rc!=SQLITE_OK ){
        return rc;
      }
+     else if( pWal->bShmUnreliable ){
+       return walBeginShmUnreliable(pWal, pChanged);
+     }
    }
  
+   assert( pWal->nWiData>0 );
+   assert( pWal->apWiData[0]!=0 );
    pInfo = walCkptInfo(pWal);
 -  if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
 -#ifdef SQLITE_ENABLE_SNAPSHOT
 -   && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
 -#endif
 -  ){
 -    /* The WAL has been completely backfilled (or it is empty).
 -    ** and can be safely ignored.
 -    */
 -    rc = walLockShared(pWal, WAL_READ_LOCK(0));
 -    walShmBarrier(pWal);
 -    if( rc==SQLITE_OK ){
 -      if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 -        /* It is not safe to allow the reader to continue here if frames
 -        ** may have been appended to the log before READ_LOCK(0) was obtained.
 -        ** When holding READ_LOCK(0), the reader ignores the entire log file,
 -        ** which implies that the database file contains a trustworthy
 -        ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
 -        ** happening, this is usually correct.
 -        **
 -        ** However, if frames have been appended to the log (or if the log 
 -        ** is wrapped and written for that matter) before the READ_LOCK(0)
 -        ** is obtained, that is not necessarily true. A checkpointer may
 -        ** have started to backfill the appended frames but crashed before
 -        ** it finished. Leaving a corrupt image in the database file.
 -        */
 -        walUnlockShared(pWal, WAL_READ_LOCK(0));
 -        return WAL_RETRY;
 -      }
 -      pWal->readLock = 0;
 -      return SQLITE_OK;
 -    }else if( rc!=SQLITE_BUSY ){
 +  if( isWalMode2(pWal) ){
 +    int eLock = 1 + (walidxGetFile(&pWal->hdr)*2);
 +    if( pInfo->nBackfill==0 ){
 +      eLock += walidxGetMxFrame(&pWal->hdr, !walidxGetFile(&pWal->hdr))>0;
 +    }
 +    rc = walLockReader(pWal, eLock, 1);
 +    if( rc!=SQLITE_OK ){
        return rc;
      }
 -  }
  
 -  /* If we get this far, it means that the reader will want to use
 -  ** the WAL to get at content from recent commits.  The job now is
 -  ** to select one of the aReadMark[] entries that is closest to
 -  ** but not exceeding pWal->hdr.mxFrame and lock that entry.
 -  */
 -  mxReadMark = 0;
 -  mxI = 0;
 -  mxFrame = pWal->hdr.mxFrame;
 -#ifdef SQLITE_ENABLE_SNAPSHOT
 -  if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
 -    mxFrame = pWal->pSnapshot->mxFrame;
 -  }
 -#endif
 -  for(i=1; i<WAL_NREADER; i++){
 -    u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
 -    if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 -      assert( thisMark!=READMARK_NOT_USED );
 -      mxReadMark = thisMark;
 -      mxI = i;
 +    walShmBarrier(pWal);
 +    if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 +      walLockReader(pWal, eLock, 0);
 +      return WAL_RETRY;
 +    }else{
 +      pWal->readLock = eLock;
      }
 -  }
 -  if( (pWal->readOnly & WAL_SHM_RDONLY)==0
 -   && (mxReadMark<mxFrame || mxI==0)
 -  ){
 -    for(i=1; i<WAL_NREADER; i++){
 -      rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +    assert( pWal->minFrame==0 && walFramePage(pWal->minFrame)==0 );
 +  }else{
 +    u32 mxReadMark;               /* Largest aReadMark[] value */
 +    int mxI;                      /* Index of largest aReadMark[] value */
 +    int i;                        /* Loop counter */
 +    u32 mxFrame;                  /* Wal frame to lock to */
-     if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame 
- #ifdef SQLITE_ENABLE_SNAPSHOT
-      && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0
-        || 0==memcmp(&pWal->hdr, pWal->pSnapshot, sizeof(WalIndexHdr)))
- #endif
-       ){
++    if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
++  #ifdef SQLITE_ENABLE_SNAPSHOT
++     && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
++  #endif
++    ){
 +      /* The WAL has been completely backfilled (or it is empty).
 +      ** and can be safely ignored.
 +      */
 +      rc = walLockShared(pWal, WAL_READ_LOCK(0));
 +      walShmBarrier(pWal);
        if( rc==SQLITE_OK ){
-         if( memcmp((void*)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 -        mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
 -        mxI = i;
 -        walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 -        break;
++        if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
 +          /* It is not safe to allow the reader to continue here if frames
 +          ** may have been appended to the log before READ_LOCK(0) was obtained.
 +          ** When holding READ_LOCK(0), the reader ignores the entire log file,
 +          ** which implies that the database file contains a trustworthy
 +          ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
 +          ** happening, this is usually correct.
 +          **
 +          ** However, if frames have been appended to the log (or if the log 
 +          ** is wrapped and written for that matter) before the READ_LOCK(0)
 +          ** is obtained, that is not necessarily true. A checkpointer may
 +          ** have started to backfill the appended frames but crashed before
 +          ** it finished. Leaving a corrupt image in the database file.
 +          */
 +          walUnlockShared(pWal, WAL_READ_LOCK(0));
 +          return WAL_RETRY;
 +        }
 +        pWal->readLock = 0;
 +        return SQLITE_OK;
        }else if( rc!=SQLITE_BUSY ){
          return rc;
        }
      }
 -  }
 -  if( mxI==0 ){
 -    assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
 -    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
 -  }
--
 -  rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 -  if( rc ){
 -    return rc==SQLITE_BUSY ? WAL_RETRY : rc;
 -  }
 -  /* Now that the read-lock has been obtained, check that neither the
 -  ** value in the aReadMark[] array or the contents of the wal-index
 -  ** header have changed.
 -  **
 -  ** It is necessary to check that the wal-index header did not change
 -  ** between the time it was read and when the shared-lock was obtained
 -  ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
 -  ** that the log file may have been wrapped by a writer, or that frames
 -  ** that occur later in the log than pWal->hdr.mxFrame may have been
 -  ** copied into the database by a checkpointer. If either of these things
 -  ** happened, then reading the database with the current value of
 -  ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
 -  ** instead.
 -  **
 -  ** Before checking that the live wal-index header has not changed
 -  ** since it was read, set Wal.minFrame to the first frame in the wal
 -  ** file that has not yet been checkpointed. This client will not need
 -  ** to read any frames earlier than minFrame from the wal file - they
 -  ** can be safely read directly from the database file.
 -  **
 -  ** Because a ShmBarrier() call is made between taking the copy of 
 -  ** nBackfill and checking that the wal-header in shared-memory still
 -  ** matches the one cached in pWal->hdr, it is guaranteed that the 
 -  ** checkpointer that set nBackfill was not working with a wal-index
 -  ** header newer than that cached in pWal->hdr. If it were, that could
 -  ** cause a problem. The checkpointer could omit to checkpoint
 -  ** a version of page X that lies before pWal->minFrame (call that version
 -  ** A) on the basis that there is a newer version (version B) of the same
 -  ** page later in the wal file. But if version B happens to like past
 -  ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
 -  ** that it can read version A from the database file. However, since
 -  ** we can guarantee that the checkpointer that set nBackfill could not
 -  ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
 -  */
 -  pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
 -  walShmBarrier(pWal);
 -  if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
 -   || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
 -  ){
 -    walUnlockShared(pWal, WAL_READ_LOCK(mxI));
 -    return WAL_RETRY;
 -  }else{
 -    assert( mxReadMark<=pWal->hdr.mxFrame );
 -    pWal->readLock = (i16)mxI;
++  
 +    /* If we get this far, it means that the reader will want to use
 +    ** the WAL to get at content from recent commits.  The job now is
 +    ** to select one of the aReadMark[] entries that is closest to
 +    ** but not exceeding pWal->hdr.mxFrame and lock that entry.
 +    */
 +    mxReadMark = 0;
 +    mxI = 0;
 +    mxFrame = pWal->hdr.mxFrame;
- #ifdef SQLITE_ENABLE_SNAPSHOT
++  #ifdef SQLITE_ENABLE_SNAPSHOT
 +    if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
 +      mxFrame = pWal->pSnapshot->mxFrame;
 +    }
- #endif
++  #endif
 +    for(i=1; i<WAL_NREADER; i++){
-       u32 thisMark = pInfo->aReadMark[i];
++      u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
 +      if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 +        assert( thisMark!=READMARK_NOT_USED );
 +        mxReadMark = thisMark;
 +        mxI = i;
 +      }
 +    }
 +    if( (pWal->readOnly & WAL_SHM_RDONLY)==0
-         && (mxReadMark<mxFrame || mxI==0)
-       ){
++     && (mxReadMark<mxFrame || mxI==0)
++    ){
 +      for(i=1; i<WAL_NREADER; i++){
 +        rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +        if( rc==SQLITE_OK ){
-           mxReadMark = pInfo->aReadMark[i] = mxFrame;
++          mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
 +          mxI = i;
 +          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +          break;
 +        }else if( rc!=SQLITE_BUSY ){
 +          return rc;
 +        }
 +      }
 +    }
 +    if( mxI==0 ){
 +      assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
-       return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
++      return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
 +    }
++  
 +    rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 +    if( rc ){
 +      return rc==SQLITE_BUSY ? WAL_RETRY : rc;
 +    }
 +    /* Now that the read-lock has been obtained, check that neither the
 +    ** value in the aReadMark[] array or the contents of the wal-index
 +    ** header have changed.
 +    **
 +    ** It is necessary to check that the wal-index header did not change
 +    ** between the time it was read and when the shared-lock was obtained
 +    ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
 +    ** that the log file may have been wrapped by a writer, or that frames
 +    ** that occur later in the log than pWal->hdr.mxFrame may have been
 +    ** copied into the database by a checkpointer. If either of these things
 +    ** happened, then reading the database with the current value of
 +    ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
 +    ** instead.
 +    **
 +    ** Before checking that the live wal-index header has not changed
 +    ** since it was read, set Wal.minFrame to the first frame in the wal
 +    ** file that has not yet been checkpointed. This client will not need
 +    ** to read any frames earlier than minFrame from the wal file - they
 +    ** can be safely read directly from the database file.
 +    **
 +    ** Because a ShmBarrier() call is made between taking the copy of 
 +    ** nBackfill and checking that the wal-header in shared-memory still
 +    ** matches the one cached in pWal->hdr, it is guaranteed that the 
 +    ** checkpointer that set nBackfill was not working with a wal-index
 +    ** header newer than that cached in pWal->hdr. If it were, that could
 +    ** cause a problem. The checkpointer could omit to checkpoint
 +    ** a version of page X that lies before pWal->minFrame (call that version
 +    ** A) on the basis that there is a newer version (version B) of the same
 +    ** page later in the wal file. But if version B happens to like past
 +    ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
 +    ** that it can read version A from the database file. However, since
 +    ** we can guarantee that the checkpointer that set nBackfill could not
 +    ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
 +    */
-     pWal->minFrame = pInfo->nBackfill+1;
++    pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
 +    walShmBarrier(pWal);
-     if( pInfo->aReadMark[mxI]!=mxReadMark
-         || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
-       ){
++    if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
++     || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
++    ){
 +      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
 +      return WAL_RETRY;
 +    }else{
 +      assert( mxReadMark<=pWal->hdr.mxFrame );
 +      pWal->readLock = (i16)mxI;
 +    }
    }
    return rc;
  }
@@@ -2987,7 -2697,7 +3262,7 @@@ int sqlite3WalSnapshotRecover(Wal *pWal
  
            if( iDbOff+szPage<=szDb ){
              iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
--            rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
++            rc = sqlite3OsRead(pWal->apWalFd[0], pBuf1, szPage, iWalOff);
  
              if( rc==SQLITE_OK ){
                rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
@@@ -3132,89 -2835,6 +3408,90 @@@ void sqlite3WalEndReadTransaction(Wal *
    }
  }
  
-   volatile ht_slot *aHash;        /* Pointer to hash table */
-   volatile u32 *aPgno;            /* Pointer to array of page numbers */
-   u32 iZero;                      /* Frame number corresponding to aPgno[0] */
 +/* Search hash table iHash for an entry matching page number
 +** pgno. Each call to this function searches a single hash table
 +** (each hash table indexes up to HASHTABLE_NPAGE frames).
 +**
 +** This code might run concurrently to the code in walIndexAppend()
 +** that adds entries to the wal-index (and possibly to this hash 
 +** table). This means the value just read from the hash 
 +** slot (aHash[iKey]) may have been added before or after the 
 +** current read transaction was opened. Values added after the
 +** read transaction was opened may have been written incorrectly -
 +** i.e. these slots may contain garbage data. However, we assume
 +** that any slots written before the current read transaction was
 +** opened remain unmodified.
 +**
 +** For the reasons above, the if(...) condition featured in the inner
 +** loop of the following block is more stringent that would be required 
 +** if we had exclusive access to the hash-table:
 +**
 +**   (aPgno[iFrame]==pgno): 
 +**     This condition filters out normal hash-table collisions.
 +**
 +**   (iFrame<=iLast): 
 +**     This condition filters out entries that were added to the hash
 +**     table after the current read-transaction had started.
 +*/
 +static int walSearchHash(
 +  Wal *pWal, 
 +  u32 iLast,
 +  int iHash, 
 +  Pgno pgno, 
 +  u32 *piRead
 +){
-   rc = walHashGet(pWal, iHash, &aHash, &aPgno, &iZero);
++  WalHashLoc sLoc;                /* Hash table location */
 +  int iKey;                       /* Hash slot index */
 +  int nCollide;                   /* Number of hash collisions remaining */
 +  int rc;                         /* Error code */
 +
-   for(iKey=walHash(pgno); aHash[iKey]; iKey=walNextHash(iKey)){
-     u32 iFrame = aHash[iKey] + iZero;
-     if( iFrame<=iLast && iFrame>=pWal->minFrame && aPgno[aHash[iKey]]==pgno ){
++  rc = walHashGet(pWal, iHash, &sLoc);
 +  if( rc!=SQLITE_OK ){
 +    return rc;
 +  }
 +  nCollide = HASHTABLE_NSLOT;
++  for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
++    u32 iFrame = sLoc.aHash[iKey] + sLoc.iZero;
++    if( iFrame<=iLast 
++     && iFrame>=pWal->minFrame 
++     && sLoc.aPgno[sLoc.aHash[iKey]]==pgno 
++    ){
 +      assert( iFrame>*piRead || CORRUPT_DB );
 +      *piRead = iFrame;
 +    }
 +    if( (nCollide--)==0 ){
 +      return SQLITE_CORRUPT_BKPT;
 +    }
 +  }
 +
 +  return SQLITE_OK;
 +}
 +
 +static int walSearchWal(
 +  Wal *pWal, 
 +  int iWal, 
 +  Pgno pgno, 
 +  u32 *piRead
 +){
 +  int rc = SQLITE_OK;
 +  int bWal2 = isWalMode2(pWal);
 +  u32 iLast = walidxGetMxFrame(&pWal->hdr, iWal);
 +  if( iLast ){
 +    int iHash;
 +    int iMinHash = walFramePage(pWal->minFrame);
 +    u32 iExternal = bWal2 ? walExternalEncode(iWal, iLast) : iLast;
 +    assert( bWal2==0 || pWal->minFrame==0 );
 +    for(iHash=walFramePage(iExternal); 
 +        iHash>=iMinHash && *piRead==0; 
 +        iHash-=(1+bWal2)
 +    ){
 +      rc = walSearchHash(pWal, iExternal, iHash, pgno, piRead);
 +      if( rc!=SQLITE_OK ) break;
 +    }
 +  }
 +  return rc;
 +}
 +
  /*
  ** Search the wal file for page pgno. If found, set *piRead to the frame that
  ** contains the page. Otherwise, if pgno is not in the wal file, set *piRead
@@@ -3228,48 -2848,76 +3505,53 @@@ int sqlite3WalFindFrame
    Pgno pgno,                      /* Database page number to read data for */
    u32 *piRead                     /* OUT: Frame number (or zero) */
  ){
 +  int bWal2 = isWalMode2(pWal);
 +  int iApp = walidxGetFile(&pWal->hdr);
 +  int rc = SQLITE_OK;
    u32 iRead = 0;                  /* If !=0, WAL frame to return data from */
 -  u32 iLast = pWal->hdr.mxFrame;  /* Last page in WAL for this reader */
 -  int iHash;                      /* Used to loop through N hash tables */
 -  int iMinHash;
  
    /* This routine is only be called from within a read transaction. */
 -  assert( pWal->readLock>=0 || pWal->lockError );
 +  assert( pWal->readLock!=WAL_LOCK_NONE );
  
 -  /* If the "last page" field of the wal-index header snapshot is 0, then
 -  ** no data will be read from the wal under any circumstances. Return early
 -  ** in this case as an optimization.  Likewise, if pWal->readLock==0, 
 -  ** then the WAL is ignored by the reader so return early, as if the 
 -  ** WAL were empty.
 -  */
 -  if( iLast==0 || (pWal->readLock==0 && pWal->bShmUnreliable==0) ){
 +  /* If this is a wal2 system, the client must have a partial-wal lock 
 +  ** on wal file iApp. Or if it is a wal system, iApp==0 must be true.  */
 +  assert( bWal2==0 || iApp==1
 +       || pWal->readLock==WAL_LOCK_PART1 || pWal->readLock==WAL_LOCK_PART1_FULL2
 +  );
 +  assert( bWal2==0 || iApp==0
 +       || pWal->readLock==WAL_LOCK_PART2 || pWal->readLock==WAL_LOCK_PART2_FULL1
 +  );
 +  assert( bWal2 || iApp==0 );
 +
-   /* Search the wal file that the client holds a partial lock on first */
++  /* Return early if read-lock 0 is held. */
++  if( (pWal->readLock==0 && pWal->bShmUnreliable==0) ){
+     *piRead = 0;
+     return SQLITE_OK;
+   }
  
 -  /* Search the hash table or tables for an entry matching page number
 -  ** pgno. Each iteration of the following for() loop searches one
 -  ** hash table (each hash table indexes up to HASHTABLE_NPAGE frames).
 -  **
 -  ** This code might run concurrently to the code in walIndexAppend()
 -  ** that adds entries to the wal-index (and possibly to this hash 
 -  ** table). This means the value just read from the hash 
 -  ** slot (aHash[iKey]) may have been added before or after the 
 -  ** current read transaction was opened. Values added after the
 -  ** read transaction was opened may have been written incorrectly -
 -  ** i.e. these slots may contain garbage data. However, we assume
 -  ** that any slots written before the current read transaction was
 -  ** opened remain unmodified.
 -  **
 -  ** For the reasons above, the if(...) condition featured in the inner
 -  ** loop of the following block is more stringent that would be required 
 -  ** if we had exclusive access to the hash-table:
 -  **
 -  **   (aPgno[iFrame]==pgno): 
 -  **     This condition filters out normal hash-table collisions.
 -  **
 -  **   (iFrame<=iLast): 
 -  **     This condition filters out entries that were added to the hash
 -  **     table after the current read-transaction had started.
 -  */
 -  iMinHash = walFramePage(pWal->minFrame);
 -  for(iHash=walFramePage(iLast); iHash>=iMinHash; iHash--){
 -    WalHashLoc sLoc;              /* Hash table location */
 -    int iKey;                     /* Hash slot index */
 -    int nCollide;                 /* Number of hash collisions remaining */
 -    int rc;                       /* Error code */
 -
 -    rc = walHashGet(pWal, iHash, &sLoc);
 -    if( rc!=SQLITE_OK ){
 -      return rc;
 -    }
 -    nCollide = HASHTABLE_NSLOT;
 -    for(iKey=walHash(pgno); sLoc.aHash[iKey]; iKey=walNextHash(iKey)){
 -      u32 iFrame = sLoc.aHash[iKey] + sLoc.iZero;
 -      if( iFrame<=iLast && iFrame>=pWal->minFrame
 -       && sLoc.aPgno[sLoc.aHash[iKey]]==pgno ){
 -        assert( iFrame>iRead || CORRUPT_DB );
 -        iRead = iFrame;
 -      }
 -      if( (nCollide--)==0 ){
 -        return SQLITE_CORRUPT_BKPT;
 -      }
 -    }
 -    if( iRead ) break;
++  /* Search the wal file that the client holds a partial lock on first */
 +  rc = walSearchWal(pWal, iApp, pgno, &iRead);
 +
 +  /* If the requested page was not found, no error has occured, and 
 +  ** the client holds a full-wal lock on the other wal file, search it
 +  ** too.  */
 +  if( rc==SQLITE_OK && bWal2 && iRead==0 && (
 +        pWal->readLock==WAL_LOCK_PART1_FULL2 
 +     || pWal->readLock==WAL_LOCK_PART2_FULL1
 +  )){
 +    rc = walSearchWal(pWal, !iApp, pgno, &iRead);
    }
  
 +#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
 +  if( iRead ){ 
 +    u32 iFrame;
 +    int iWal = walExternalDecode(iRead, &iFrame);
 +    WALTRACE(("WAL%p: page %d @ frame %d wal %d\n",pWal,(int)pgno,iFrame,iWal));
 +  }else{
 +    WALTRACE(("WAL%p: page %d not found\n", pWal, (int)pgno));
 +  }
 +#endif
 +
  #ifdef SQLITE_ENABLE_EXPENSIVE_ASSERT
    /* If expensive assert() statements are available, do a linear search
    ** of the wal-index file content. Make sure the results agree with the
@@@ -3715,10 -3262,7 +3997,8 @@@ static int walRewriteChecksums(Wal *pWa
    u8 aFrame[WAL_FRAME_HDRSIZE];   /* Buffer to assemble frame-headers in */
    u32 iRead;                      /* Next frame to read from wal file */
    i64 iCksumOff;
 +  sqlite3_file *pWalFd = pWal->apWalFd[walidxGetFile(&pWal->hdr)];
  
-   assert( isWalMode2(pWal)==0 );
    aBuf = sqlite3_malloc(szPage + WAL_FRAME_HDRSIZE);
    if( aBuf==0 ) return SQLITE_NOMEM_BKPT;
  
@@@ -4200,33 -3698,24 +4480,33 @@@ int sqlite3WalExclusiveMode(Wal *pWal, 
    ** locks are taken in this case). Nor should the pager attempt to
    ** upgrade to exclusive-mode following such an error.
    */
 -  assert( pWal->readLock>=0 || pWal->lockError );
 -  assert( pWal->readLock>=0 || (op<=0 && pWal->exclusiveMode==0) );
 +  assert( pWal->readLock!=WAL_LOCK_NONE || pWal->lockError );
 +  assert( pWal->readLock!=WAL_LOCK_NONE || (op<=0 && pWal->exclusiveMode==0) );
  
    if( op==0 ){
 -    if( pWal->exclusiveMode!=WAL_NORMAL_MODE ){
 +    if( pWal->exclusiveMode ){
        pWal->exclusiveMode = WAL_NORMAL_MODE;
 -      if( walLockShared(pWal, WAL_READ_LOCK(pWal->readLock))!=SQLITE_OK ){
 +      if( isWalMode2(pWal) ){
 +        rc = walLockReader(pWal, pWal->readLock, 1);
 +      }else{
 +        rc = walLockShared(pWal, WAL_READ_LOCK(pWal->readLock));
 +      }
 +      if( rc!=SQLITE_OK ){
          pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
        }
-       rc = pWal->exclusiveMode==0;
+       rc = pWal->exclusiveMode==WAL_NORMAL_MODE;
      }else{
        /* Already in locking_mode=NORMAL */
        rc = 0;
      }
    }else if( op>0 ){
-     assert( pWal->exclusiveMode==0 );
+     assert( pWal->exclusiveMode==WAL_NORMAL_MODE );
      assert( pWal->readLock>=0 );
 -    walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
 +    if( isWalMode2(pWal) ){
 +      walLockReader(pWal, pWal->readLock, 0);
 +    }else{
 +      walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
 +    }
      pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
      rc = 1;
    }else{
diff --cc src/wal.h
Simple merge
Simple merge
diff --cc test/tester.tcl
Simple merge
index 2498c5500fc4db73d83f3fed238eaf0a19256f47,0000000000000000000000000000000000000000..becbb232fc5e2db11c5ba78ad744ca04b2741f6b
mode 100644,000000..100644
--- /dev/null
@@@ -1,291 -1,0 +1,292 @@@
 +# 2017 September 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=WAL2" mode.
 +#
 +
 +set testdir [file dirname $argv0]
 +source $testdir/tester.tcl
 +source $testdir/lock_common.tcl
 +source $testdir/malloc_common.tcl
 +source $testdir/wal_common.tcl
 +
 +set testprefix wal2simple
 +ifcapable !wal {finish_test ; return }
 +
 +#-------------------------------------------------------------------------
 +# The following tests verify that a client can switch in and out of wal
 +# and wal2 mode. But that it is not possible to change directly from wal
 +# to wal2, or from wal2 to wal mode.
 +#
 +do_execsql_test 1.1.0 {
 +  PRAGMA journal_mode = wal2
 +} {wal2}
 +execsql { SELECT * FROM sqlite_master} 
 +do_execsql_test 1.x {
 +  PRAGMA journal_mode;
 +  PRAGMA main.journal_mode;
 +} {wal2 wal2}
 +db close
 +do_test 1.1.1 { file size test.db } {1024}
 +do_test 1.1.2 { hexio_read test.db 18 2 } 0303
 +
 +sqlite3 db test.db
 +do_execsql_test 1.2.0 {
 +  SELECT * FROM sqlite_master;
 +  PRAGMA journal_mode = delete;
 +} {delete}
 +db close
 +do_test 1.2.1 { file size test.db } {1024}
 +do_test 1.2.2 { hexio_read test.db 18 2 } 0101
 +
 +sqlite3 db test.db
 +do_execsql_test 1.3.0 {
 +  SELECT * FROM sqlite_master;
 +  PRAGMA journal_mode = wal;
 +} {wal}
 +db close
 +do_test 1.3.1 { file size test.db } {1024}
 +do_test 1.3.2 { hexio_read test.db 18 2 } 0202
 +
 +sqlite3 db test.db
 +do_catchsql_test 1.4.0 {
 +  PRAGMA journal_mode = wal2;
 +} {1 {cannot change from wal to wal2 mode}}
 +do_execsql_test 1.4.1 {
 +  PRAGMA journal_mode = wal;
 +  PRAGMA journal_mode = delete;
 +  PRAGMA journal_mode = wal2;
 +  PRAGMA journal_mode = wal2;
 +} {wal delete wal2 wal2}
 +do_catchsql_test 1.4.2 {
 +  PRAGMA journal_mode = wal;
 +} {1 {cannot change from wal2 to wal mode}}
 +db close
 +do_test 1.4.3 { hexio_read test.db 18 2 } 0303
 +
 +#-------------------------------------------------------------------------
 +# Test that recovery in wal2 mode works.
 +#
 +forcedelete test.db test.db-wal test.db-wal2
 +reset_db
 +do_execsql_test 2.0 {
 +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b);
 +  PRAGMA journal_mode = wal2;
 +  PRAGMA journal_size_limit = 5000;
 +} {wal2 5000}
 +
 +proc wal_hook {DB nm nFrame} { $DB eval { PRAGMA wal_checkpoint } }
 +db wal_hook {wal_hook db}
 +
 +for {set i 1} {$i <= 200} {incr i} {
 +  execsql { INSERT INTO t1 VALUES(NULL, randomblob(100)) }
 +  set res [db eval { SELECT sum(a), md5sum(b) FROM t1 }]
 +
 +  do_test 2.1.$i {
 +    foreach f [glob -nocomplain test.db2*] { forcedelete $f }
 +    forcecopy test.db      test.db2
 +    forcecopy test.db-wal  test.db2-wal
 +    forcecopy test.db-wal2 test.db2-wal2
 +
 +    sqlite3 db2 test.db2
 +    db2 eval { SELECT sum(a), md5sum(b) FROM t1 }
 +  } $res
 +
 +  db2 close
 +}
 +
 +#-------------------------------------------------------------------------
 +
 +reset_db
 +do_execsql_test 3.0 {
 +  CREATE TABLE t1(x BLOB, y INTEGER PRIMARY KEY);
 +  CREATE INDEX i1 ON t1(x);
 +  PRAGMA cache_size = 5;
 +  PRAGMA journal_mode = wal2;
 +} {wal2}
 +
++breakpoint
 +do_test 3.1 {
 +  execsql BEGIN
 +  for {set i 1} {$i < 1000} {incr i} {
 +    execsql { INSERT INTO t1 VALUES(randomblob(800), $i) }
 +  }
 +  execsql COMMIT
 +} {}
 +
 +do_execsql_test 3.2 {
 +  PRAGMA integrity_check;
 +} {ok}
 +
 +#-------------------------------------------------------------------------
 +catch { db close }
 +foreach f [glob -nocomplain test.db*] { forcedelete $f }
 +reset_db
 +do_execsql_test 4.0 {
 +  CREATE TABLE t1(x, y);
 +  PRAGMA journal_mode = wal2;
 +} {wal2}
 +
 +do_execsql_test 4.1 {
 +  SELECT * FROM t1;
 +} {}
 +
 +do_execsql_test 4.2 {
 +  INSERT INTO t1 VALUES(1, 2);
 +} {}
 +
 +do_execsql_test 4.3 {
 +  SELECT * FROM t1;
 +} {1 2}
 +
 +do_test 4.4 {
 +  sqlite3 db2 test.db
 +  execsql { SELECT * FROM t1 } db2
 +} {1 2}
 +
 +do_test 4.5 {
 +  lsort [glob test.db*]
 +} {test.db test.db-shm test.db-wal test.db-wal2}
 +
 +do_test 4.6 {
 +  db close
 +  db2 close
 +  sqlite3 db test.db
 +  execsql { SELECT * FROM t1 }
 +} {1 2}
 +
 +do_execsql_test 4.7 {
 +  PRAGMA journal_size_limit = 4000;
 +  INSERT INTO t1 VALUES(3, 4);
 +  INSERT INTO t1 VALUES(5, 6);
 +  INSERT INTO t1 VALUES(7, 8);
 +  INSERT INTO t1 VALUES(9, 10);
 +  INSERT INTO t1 VALUES(11, 12);
 +  INSERT INTO t1 VALUES(13, 14);
 +  INSERT INTO t1 VALUES(15, 16);
 +  INSERT INTO t1 VALUES(17, 18);
 +  SELECT * FROM t1;
 +} {4000 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18}
 +
 +do_test 4.8 {
 +  sqlite3 db2 test.db
 +  execsql { SELECT * FROM t1 } db2
 +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18}
 +
 +do_test 4.9 {
 +  db close
 +  db2 close
 +  lsort [glob test.db*]
 +} {test.db}
 +
 +#-------------------------------------------------------------------------
 +reset_db
 +do_execsql_test 5.0 {
 +  CREATE TABLE t1(a INTEGER PRIMARY KEY, b, c);
 +  CREATE INDEX i1 ON t1(b, c);
 +  PRAGMA journal_mode = wal2;
 +  PRAGMA journal_size_limit = 4000;
 +} {wal2 4000}
 +
 +proc wal_hook {DB nm nFrame} {
 +  $DB eval { PRAGMA wal_checkpoint }
 +}
 +db wal_hook [list wal_hook db]
 +
 +
 +foreach js {4000 8000 12000} {
 +  foreach NROW [list 100 200 300 400 500 600 1000] {
 +    do_test 5.$js.$NROW.1 {
 +      db eval "DELETE FROM t1"
 +      db eval "PRAGMA journal_size_limit = $js"
 +      set nTotal 0
 +      for {set i 0} {$i < $NROW} {incr i} {
 +        db eval { INSERT INTO t1 VALUES($i, $i, randomblob(abs(random()%50))) }
 +        incr nTotal $i
 +      }
 +      set {} {}
 +    } {}
 +
 +    do_test 5.$js.$NROW.2 {
 +      sqlite3 db2 test.db
 +      db2 eval { 
 +        PRAGMA integrity_check;
 +        SELECT count(*), sum(b) FROM t1;
 +      }
 +    } [list ok $NROW $nTotal]
 +
 +    db2 close
 +  }
 +}
 +
 +
 +#-------------------------------------------------------------------------
 +reset_db
 +do_execsql_test 6.0 {
 +  CREATE TABLE tx(x);
 +  PRAGMA journal_mode = wal2;
 +  PRAGMA journal_size_limit = 3500;
 +} {wal2 3500}
 +
 +do_test 6.1 {
 +  for {set i 0} {$i < 10} {incr i} {
 +    execsql "CREATE TABLE t$i (x);"
 +  }
 +} {}
 +
 +puts "[file size test.db-wal] [file size test.db-wal2]"
 +
 +do_test 6.2.1 {
 +  foreach f [glob -nocomplain test.db2*] { forcedelete $f }
 +  forcecopy test.db-wal2 test.db2-wal2
 +  sqlite3 db2 test.db2
 +  db2 eval { SELECT * FROM sqlite_master }
 +} {}
 +do_test 6.2.2 {
 +  db2 eval {
 +    PRAGMA journal_mode = wal2;
 +    SELECT * FROM sqlite_master;
 +  }
 +} {wal2}
 +
 +do_test 6.3.1 {
 +  db2 close
 +  foreach f [glob -nocomplain test.db2*] { forcedelete $f }
 +  forcecopy test.db-wal2 test.db2-wal2
 +  forcecopy test.db test.db2
 +  sqlite3 db2 test.db2
 +  db2 eval { SELECT * FROM sqlite_master }
 +} {table tx tx 2 {CREATE TABLE tx(x)}}
 +do_test 6.3.2 {
 +  db2 eval {
 +    PRAGMA journal_mode = wal2;
 +    SELECT * FROM sqlite_master;
 +  }
 +} {wal2 table tx tx 2 {CREATE TABLE tx(x)}}
 +
 +do_test 6.4.1 {
 +  db2 close
 +  foreach f [glob -nocomplain test.db2*] { forcedelete $f }
 +  forcecopy test.db-wal2 test.db2-wal2
 +  forcecopy test.db-wal test.db2-wal
 +  sqlite3 db2 test.db2
 +  db2 eval { SELECT * FROM sqlite_master }
 +} {}
 +do_test 6.4.2 {
 +  db2 eval {
 +    PRAGMA journal_mode = wal2;
 +    SELECT * FROM sqlite_master;
 +  }
 +} {wal2}
 +db2 close
 +
 +finish_test