]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge latest wal2 changes, including support for handling of structured-exceptions...
authordan <Dan Kennedy>
Fri, 11 Aug 2023 19:36:24 +0000 (19:36 +0000)
committerdan <Dan Kennedy>
Fri, 11 Aug 2023 19:36:24 +0000 (19:36 +0000)
FossilOrigin-Name: 331f2f3e5db9b6139be984f1b959cd0d51563adaa68452aa2f42741c69bc6319

1  2 
manifest
manifest.uuid
src/pager.c
src/pager.h
src/sqlite.h.in
src/vdbeaux.c
src/wal.c
src/wal.h

diff --cc manifest
index 542901d465eb7c03f78c28a849c030a66ce157f6,5adfa1337d544ea2f882b1dfdb4c85c190d94699..ea8acb35c074360e2c4f4c8b35a921d5dbb82c17
+++ b/manifest
@@@ -1,11 -1,11 +1,11 @@@
- C Fix\sa\sproblem\swith\shandling\ssavepoints\sin\sBEGIN\sCONCURRENT\stransactions.
- D 2023-08-11T18:31:17.859
 -C Merge\slatest\strunk\schanges,\sincluding\ssupport\sfor\shandling\sof\sstructured-exceptions\sin\sMSVC\sbuilds,\swith\sthis\sbranch.
 -D 2023-08-11T19:33:54.234
++C Merge\slatest\swal2\schanges,\sincluding\ssupport\sfor\shandling\sof\sstructured-exceptions\sin\sMSVC\sbuilds,\swith\sthis\sbranch.
++D 2023-08-11T19:36:24.769
  F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
  F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
  F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
 -F Makefile.in e1282d59cf2dffef4a69ca4b0364129fdaefd2f1718bf789f6b5d27992261bad
 +F Makefile.in 1ac6badc8e625d87c76f64fad033ece76721c6d9341c685baeb17aea5f620acd
  F Makefile.linux-gcc f3842a0b1efbfbb74ac0ef60e56b301836d05b4d867d014f714fa750048f1ab6
- F Makefile.msc 97f9b14ef0418943c315db08f4961932f2fc839603c13ab646079840b0d83329
+ F Makefile.msc 0b57ab2867b1fdc90c4e35a6777a3b24b81006cc7ba947c8db13929b4edd3800
  F README.md c1c4218efcc4071a6e26db2b517fdbc1035696a29b370edd655faddbef02b224
  F VERSION c6366dc72582d3144ce87b013cc35fe48d62f6d07d5be0c9716ea33c862144aa
  F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
@@@ -635,28 -629,28 +635,28 @@@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b
  F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
  F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
  F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
 -F src/os_unix.c 2e8b12107f75d1bd16412f312b4c5d5103191807a37836d3b81beb26436ad81b
 +F src/os_unix.c c8003dfe6502315cf930160b6cf1b93f82211bed5ab9226509faa6324030c8ea
  F src/os_win.c 7038223a1cda0a47e2ab4db47f63bf1833fe53ba0542f0f283a062ea13894103
  F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
- F src/pager.c 71f3c8b45890d35156518ffeb71e422274557793cbedff80585b4e292feed234
- F src/pager.h e055e649d93f1e121ce50b30a3d01a5225e6d2c45d712c676c8477dec19beeb8
 -F src/pager.c e7af170f5799a5edd77fc267e81cb0292e1a1cb7b488b62c6e2fff78aedc2efb
 -F src/pager.h ca3b415a3f03359d5692b71ec0ba068a58ce447fac7312f89293280d95ee38da
 -F src/parse.y aeb7760d41cfa86465e3adba506500c021597049fd55f82a30e5b7045862c28c
++F src/pager.c fda6c42c41a7eb58a62fd786cdef7a0e836234553ab8e59e03ea10b4dc3e26a1
++F src/pager.h 55870bc2e0cabd29bcbaf650d342d3b970e316f440886f388052e34623ad1e29
 +F src/parse.y 92a9cc670816e1274a107d02ed8efec6028c23713c767f035479fde411c86f27
  F src/pcache.c 4cd4a0043167da9ba7e19b4d179a0e6354e7fe32c16f781ecf9bf0a5ff63b40b
  F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
 -F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
 +F src/pcache1.c 181baf62714c20e41c77655360bd4179661f519ecd952314cdeacf60460a1878
  F src/pragma.c 3a7c0f8e06b94ee34ded7468002b4a502fcdd23bb58c3677a185a4addae7604b
 -F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
 +F src/pragma.h 1f421360eed1a7721e8c521463df8519a7c8d0d5893ebd9dbfe0dba8de996f8c
  F src/prepare.c 80548297dc0e1fb3139cdebffb5a1bcac3dfac66d791012dd74838e70445072d
  F src/printf.c e3ba080e2f409f9bfcc8d34724e6fc160e9c718dc92d0548f6b71b8b6f860ce2
 -F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
 +F src/random.c 9bd018738ec450bf35d28050b4b33fa9a6eebf3aaefb1a1cff42dc14a7725673
  F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
  F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
 -F src/select.c 5f545a2c8702d4d3430bbb188cfec47d6c122d899061ef00cbe56af14591c574
 +F src/select.c 996dda45d2a1a0228005849702348c7fd598437afa169c2c110f2c2ee582b382
  F src/shell.c.in 694aaf751f00610381533d4a31c83d142cfc83ef91ef65e2aa6912ace7c39b40
- F src/sqlite.h.in bc5d3d1776a3681ab552590c60589e0e93cbdfb66c7ed1615d649a180a62805f
 -F src/sqlite.h.in 73a366c1c45d5ac9888cfe81c458826a44498531d106cfb4f328193ab5f6f17d
++F src/sqlite.h.in bc319c39cb72ef72c4f1d1d3b7662bce1fb8ba961913e0c145472f2d9fc11a3b
  F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
  F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
 -F src/sqliteInt.h 025ed58a41968ef80d64cdc194caa8dd207b0256b147253d762fdac7a62408f9
 +F src/sqliteInt.h d639aff687cfaa64f273a0f62eebb81bad875fdb9e8ff02d1a0235a8c1957e96
  F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
  F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
  F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
@@@ -716,16 -710,16 +716,16 @@@ F src/threads.c 4ae07fa022a3dc7c5beb373
  F src/tokenize.c 23d9f4539880b40226254ad9072f4ecf12eb1902e62aea47aac29928afafcfd5
  F src/treeview.c 1d52fbc4e97161e65858d36e3424ea6e3fc045dd8a679c82b4b9593dc30de3bd
  F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0
 -F src/update.c 0bb9171afaa4d0b100ad946873bccda7aef90ffe083ef5c63668fce08c4df9da
 +F src/update.c eafa1d6e32de4749986cbebc32ffb094c4401318bb713e5d2a18fe3e5a93ae8d
  F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
  F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
- F src/util.c b3532a95ad56db67b3acd3955e688e4cb80ebec6fd1f459a8eb51cceedd6de69
+ F src/util.c a40062117e705eb3339201842717a022092816b92479eead6397cde28af32ff9
 -F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
 -F src/vdbe.c 11f1a00443991bd12bd0c97050b2614f7fd3e82b9cc0ffdb13d14d424d733e77
 +F src/vacuum.c b1dd6d73869229b6e08bac910ac011dc9da42e3120ec2b7241accc5a752bd419
 +F src/vdbe.c 3ef180bab789aa99aec753fd26ecb8f3d56c9f6870997cafd8c5fc2b7c989a43
  F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
  F src/vdbeInt.h 949669dfd8a41550d27dcb905b494f2ccde9a2e6c1b0b04daa1227e2e74c2b2c
  F src/vdbeapi.c f37822f215740ede2a8fcae99bc13f2cc3a72dd0e1d22b81b9298c5ca67dbc38
- F src/vdbeaux.c e4ff54897dca508b3c3726973a1cb08f09d81cbe13c0c36012d9c2572402327c
 -F src/vdbeaux.c e3aa5c46827cd95e0fc4d0f302fa3e901ab5f07258fdbb42709eeef40f63018d
++F src/vdbeaux.c 2c87c99975ac23e777e9c270d979eca38f2a021b1f14f061c83faab5b24d5576
  F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce
  F src/vdbemem.c 317b9f48708139db6239ade40c7980b4bc8233168383690d588dad6d8437f722
  F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015
@@@ -733,8 -727,8 +733,8 @@@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1
  F src/vdbevtab.c 57fa8f56478e5b5cb558cb425e7878515e0a105c54f96f1d1bbf4b9433529254
  F src/vtab.c 1ecf8c3745d29275688d583e12822fa984d421e0286b5ef50c137bc3bf6d7a64
  F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
- F src/wal.c 6e1705125b58635ce9ed534f4d26942e4a5e8b5a33219c88f436a9e4691026d4
- F src/wal.h dd2cd9880f308a1bda0a72f36a29c3d4388d47db45f321ebe936a378ac845e32
 -F src/wal.c 1ea71da11648befe8be082d0ef6adad144435a827d7ae1593da1bf375482c21d
 -F src/wal.h a3e64f463acf027f041c2081841e15e8c81c56d0daa45e308df423d9d66fd5a7
++F src/wal.c 8408687b696e4bb36d4312d2b71b6467c064681315c6c9f05cd823c3839cf676
++F src/wal.h dcb0533caecf286be1c87b683e1282f3ca29fa5c00eb798e9226ce377a687cf4
  F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
  F src/where.c b8917792f1e0dbfa28fb29e6cd3d560060d69667be0ba4c491cbc772363264f5
  F src/whereInt.h c7d19902863beadec1d04e66aca39c0bcd60b74f05f0eaa7422c7005dfc5d51a
@@@ -1892,7 -1871,8 +1892,8 @@@ F test/walprotocol.test 1b3f922125e3417
  F test/walprotocol2.test 7e4bedd5ee83607e2928ac438bf7332a396b980d3e02aa0746509ce11ad1f13c
  F test/walro.test cb438d05ba0d191f10b688e39c4f0cd5b71569a1d1f4440e5bdf3c6880e08c20
  F test/walro2.test 33955a6fd874dd9724005e17f77fef89d334b3171454a1256fe4941a96766cdc
 -F test/walrofault.test c70cb6e308c443867701856cce92ad8288cd99488fa52afab77cca6cfd51af68
 +F test/walrofault.test 5a25f91c16a68bae65edec7cdef4495e5c6494c8408743fe9b29045fa6665cd0
+ F test/walseh1.test 82da37763b0d87942dccd191e58321532ce3d44b87ef36e04ff9ce13f382bbae
  F test/walsetlk.test 34c901443b31ab720afc463f5b236c86ca5c4134402573dce91aa0761de8db5a
  F test/walshared.test 42e3808582504878af237ea02c42ca793e8a0efaa19df7df26ac573370dbc7a3
  F test/walslow.test c05c68d4dc2700a982f89133ce103a1a84cc285f
@@@ -2087,9 -2065,8 +2088,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P 859932ebce830c757cf4e889f2efc54807bd4d7947cf5d64a5d9e570bf75ed80
- Q +4a5c0439ae3ce6c98cea26f7e96c5bce543e76b9ad2a3a275601d6e5060899c8
- R 59a166515a93559c326932ec5fd38f12
 -P fe1bf30ca0c529c3c68f2531e28aca5659aef5d15c2e3a6925ecd35a5098386b 8a6b0c24937e855b710f97b4aea973eff53e6d43e1182842731547aa4b37db2a
 -R cb51b81316a6337126aa844ebf9c925d
++P ca422d00d7843ee72a1ae20753ba512896d6f6d2b1218523323946bf66f3c758 00bfae9ffafd71dfd0bbdbdc174e4ed7d7a850b385416f83be36a4f4a55d1873
++R f50941a07ea5ece4d4d7d1785830ae41
  U dan
- Z 76630078e009601b5939323665b2f41d
 -Z cdba8e6176109f758d02fe30f64aa30c
++Z 8a22f21379a7b432c426975e82b8bfd5
  # Remove this line to create a well-formed Fossil manifest.
diff --cc manifest.uuid
index 0f91512f5b0b1418f9e53fb3bc920471021da626,54923593009056fc2984a2b6e74e319bb75e132e..6ca4459d36395440b535b713f4ae6ea68a998662
@@@ -1,1 -1,1 +1,1 @@@
- ca422d00d7843ee72a1ae20753ba512896d6f6d2b1218523323946bf66f3c758
 -00bfae9ffafd71dfd0bbdbdc174e4ed7d7a850b385416f83be36a4f4a55d1873
++331f2f3e5db9b6139be984f1b959cd0d51563adaa68452aa2f42741c69bc6319
diff --cc src/pager.c
Simple merge
diff --cc src/pager.h
Simple merge
diff --cc src/sqlite.h.in
Simple merge
diff --cc src/vdbeaux.c
index c97b6be4d182de32766c21ed11b173dca7faec52,225c8d12c93923f8d538eb8572e72e154920a6cf..fa4b6c0d9a4e701ca04a77af779e06ce84e3a305
@@@ -3386,10 -3368,11 +3386,11 @@@ int sqlite3VdbeHalt(Vdbe *p)
            ** is required. */
            rc = vdbeCommit(db, p);
          }
 -        if( rc==SQLITE_BUSY && p->readOnly ){
 +        if( (rc & 0xFF)==SQLITE_BUSY && p->readOnly ){
            sqlite3VdbeLeave(p);
 -          return SQLITE_BUSY;
 +          return rc;
          }else if( rc!=SQLITE_OK ){
+           sqlite3SystemError(db, rc);
            p->rc = rc;
            sqlite3RollbackAll(db, SQLITE_OK);
            p->nChange = 0;
diff --cc src/wal.c
index 0640d00aeaa87b31384bb7e5585f8ad8d05cb459,b96a0c6be549d8885bcb458b5c0fab7663a28972..8bafab4c6960cf44306dc6e382f7f54f1a913efc
+++ b/src/wal.c
@@@ -791,8 -789,13 +791,14 @@@ struct Wal 
    const char *zWalName;      /* Name of WAL file */
    const char *zWalName2;     /* Name of second WAL file */
    u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
 +  FastPrng sPrng;            /* Random number generator */
+ #ifdef SQLITE_USE_SEH
+   u32 lockMask;              /* Mask of locks held */
+   void *pFree;               /* Pointer to sqlite3_free() if exception thrown */
+   int iSysErrno;             /* System error code following exception */
+ #endif
  #ifdef SQLITE_DEBUG
+   int nSehTry;               /* Number of nested SEH_TRY{} blocks */
    u8 lockError;              /* True if a locking error has occurred */
  #endif
  #ifdef SQLITE_ENABLE_SNAPSHOT
@@@ -3677,30 -3893,11 +3907,20 @@@ int sqlite3WalSnapshotRecover(Wal *pWal
  }
  #endif /* SQLITE_ENABLE_SNAPSHOT */
  
 +/*
 +** Return the current last frame in the wal-index - mxFrame for *-wal, 
 +** or mxFrame2 for *-wal2. If the last frame is current in wal2, return
 +** mxFrame2 without clearing the 0x80000000 bit.
 +*/
 +static u32 walGetPriorFrame(WalIndexHdr *pHdr){
 +  return (walidxGetFile(pHdr) ? pHdr->mxFrame2 : pHdr->mxFrame);
 +}
 +
  /*
- ** Begin a read transaction on the database.
- **
- ** This routine used to be called sqlite3OpenSnapshot() and with good reason:
- ** it takes a snapshot of the state of the WAL and wal-index for the current
- ** instant in time.  The current thread will continue to use this snapshot.
- ** Other threads might append new content to the WAL and wal-index but
- ** that extra content is ignored by the current thread.
- **
- ** If the database contents have changes since the previous read
- ** transaction, then *pChanged is set to 1 before returning.  The
- ** Pager layer will use this to know that its cache is stale and
- ** needs to be flushed.
+ ** This function does the work of sqlite3WalBeginReadTransaction() (see 
+ ** below). That function simply calls this one inside an SEH_TRY{...} block.
  */
int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
static int walBeginReadTransaction(Wal *pWal, int *pChanged){
    int rc;                         /* Return code */
    int cnt = 0;                    /* Number of TryBeginRead attempts */
  #ifdef SQLITE_ENABLE_SNAPSHOT
@@@ -4108,246 -4312,41 +4378,251 @@@ int sqlite3WalBeginWriteTransaction(Wa
      return SQLITE_OK;
    }
  #endif
-     if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
 +  
 +  rc = walWriteLock(pWal);
 +  if( rc==SQLITE_OK ){
 +    /* If another connection has written to the database file since the
 +    ** time the read transaction on this connection was started, then
 +    ** the write is disallowed. Release the WRITER lock and return
 +    ** SQLITE_BUSY_SNAPSHOT in this case.  */
-       rc = SQLITE_BUSY_SNAPSHOT;
++    SEH_TRY {
++      if( memcmp(&pWal->hdr, (void*)walIndexHdr(pWal),sizeof(WalIndexHdr))!=0 ){
++        rc = SQLITE_BUSY_SNAPSHOT;
++      }
++    }
++    SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
++    if( rc!=SQLITE_OK ){
 +      walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
 +      pWal->writeLock = 0;
 +    }
 +  }
 +  return rc;
 +}
 +
 +/*
 +** This function is called by a writer that has a read-lock on aReadmark[0]
 +** (pWal->readLock==0). This function relinquishes that lock and takes a
 +** lock on a different aReadmark[] slot. 
 +**
 +** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
 +*/
 +static int walUpgradeReadlock(Wal *pWal){
 +  int cnt;
 +  int rc;
 +  assert( pWal->writeLock && pWal->readLock==0 );
 +  assert( isWalMode2(pWal)==0 );
 +  walUnlockShared(pWal, WAL_READ_LOCK(0));
 +  pWal->readLock = -1;
 +  cnt = 0;
 +  do{
 +    int notUsed;
 +    rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
 +  }while( rc==WAL_RETRY );
 +  assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
 +  testcase( (rc&0xff)==SQLITE_IOERR );
 +  testcase( rc==SQLITE_PROTOCOL );
 +  testcase( rc==SQLITE_OK );
 +  return rc;
 +}
  
 -  /* Cannot start a write transaction without first holding a read
 -  ** transaction. */
 -  assert( pWal->readLock!=WAL_LOCK_NONE );
 -  assert( pWal->writeLock==0 && pWal->iReCksum==0 );
  
 -  if( pWal->readOnly ){
 -    return SQLITE_READONLY;
 -  }
 +#ifndef SQLITE_OMIT_CONCURRENT
  
 -  /* Only one writer allowed at a time.  Get the write lock.  Return
 -  ** SQLITE_BUSY if unable.
 -  */
 -  rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
 -  if( rc ){
 -    return rc;
 +/*
 +** A concurrent transaction has conflicted with external frame iExternal.
 +** Transform this value to the one required by SQLITE_COMMIT_CONFLICT_FRAME -
 +** the frame offset within its wal file, with the 0x80000000 bit set for
 +** wal2, clear for the default wal file.
 +*/
 +static u32 walConflictFrame(Wal *pWal, u32 iExternal){
 +  u32 iRet = iExternal;
 +  if( isWalMode2(pWal) ){
 +    int bFile = walExternalDecode(iExternal, &iRet);
 +    iRet = (iRet | (bFile ? 0x80000000 : 0));
    }
 -  pWal->writeLock = 1;
 +  return iRet;
 +}
 +
 +
 +
 +/* 
 +** This function is only ever called when committing a "BEGIN CONCURRENT"
 +** transaction. It may be assumed that no frames have been written to
 +** the wal file. The second parameter is a pointer to the in-memory 
 +** representation of page 1 of the database (which may or may not be
 +** dirty). The third is a bitvec with a bit set for each page in the
 +** database file that was read by the current concurrent transaction.
 +**
 +** This function performs three tasks:
 +**
 +**   1) It obtains the WRITER lock on the wal file,
 +**
 +**   2) It checks that there are no conflicts between the current
 +**      transaction and any transactions committed to the wal file since
 +**      it was opened, and
 +**
 +**   3) It ejects any non-dirty pages from the page-cache that have been
 +**      written by another client since the CONCURRENT transaction was started
 +**      (so as to avoid ending up with an inconsistent cache after the
 +**      current transaction is committed).
 +**
 +** If no error occurs and the caller may proceed with committing the 
 +** transaction, SQLITE_OK is returned. SQLITE_BUSY is returned if the WRITER
 +** lock cannot be obtained. Or, if the WRITER lock can be obtained but there
 +** are conflicts with a committed transaction, SQLITE_BUSY_SNAPSHOT. Finally,
 +** if an error (i.e. an OOM condition or IO error), an SQLite error code
 +** is returned.
 +*/
 +int sqlite3WalLockForCommit(
 +  Wal *pWal, 
 +  PgHdr *pPg1, 
 +  Bitvec *pAllRead, 
 +  u32 *aConflict
 +){
 +  int rc = walWriteLock(pWal);
  
 -  /* If another connection has written to the database file since the
 -  ** time the read transaction on this connection was started, then
 -  ** the write is disallowed.
 +  /* If the database has been modified since this transaction was started,
 +  ** check if it is still possible to commit. The transaction can be 
 +  ** committed if:
 +  **
 +  **   a) None of the pages in pList have been modified since the 
 +  **      transaction opened, and
 +  **
 +  **   b) The database schema cookie has not been modified since the
 +  **      transaction was started.
    */
 -  SEH_TRY {
 -    if( memcmp(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr))!=0 ){
 +  if( rc==SQLITE_OK ){
 +    WalIndexHdr head;
 +
 +    if( walIndexLoadHdr(pWal, &head) ){
 +      /* This branch is taken if the wal-index header is corrupted. This 
 +      ** occurs if some other writer has crashed while committing a 
 +      ** transaction to this database since the current concurrent transaction
 +      ** was opened.  */
        rc = SQLITE_BUSY_SNAPSHOT;
 -    }
 -  } 
 -  SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
 +    }else if( memcmp(&pWal->hdr, (void*)&head, sizeof(WalIndexHdr))!=0 ){
 +      int bWal2 = isWalMode2(pWal);
 +      int iHash;
 +      int nLoop = 1+(bWal2 && walidxGetFile(&head)!=walidxGetFile(&pWal->hdr));
 +      int iLoop;
 +
 +      if( pPg1==0 ){
 +        /* If pPg1==0, then the current transaction modified the database
 +        ** schema. This means it conflicts with all other transactions. */
 +        u32 bFile = walidxGetFile(&pWal->hdr);
 +        u32 iFrame = walidxGetMxFrame(&head, bFile) | (bFile << 31);
 +        aConflict[SQLITE_COMMIT_CONFLICT_PGNO] = 1;
 +        aConflict[SQLITE_COMMIT_CONFLICT_FRAME] = iFrame;
 +        rc = SQLITE_BUSY_SNAPSHOT;
 +      }
  
 -  if( rc!=SQLITE_OK ){
 -    walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
 -    pWal->writeLock = 0;
 +      assert( nLoop==1 || nLoop==2 );
 +      for(iLoop=0; rc==SQLITE_OK && iLoop<nLoop; iLoop++){
 +        u32 iFirst;               /* First (external) wal frame to check */
 +        u32 iLastHash;            /* Last hash to check this loop */
 +        u32 mxFrame;              /* Last (external) wal frame to check */
 +
 +        if( bWal2==0 ){
 +          assert( iLoop==0 );
 +          /* Special case for wal mode. If this concurrent transaction was
 +          ** opened after the entire wal file had been checkpointed, and
 +          ** another connection has since wrapped the wal file, then we wish to
 +          ** iterate through every frame in the new wal file - not just those
 +          ** that follow the current value of pWal->hdr.mxFrame (which will be
 +          ** set to the size of the old, now overwritten, wal file). This
 +          ** doesn't come up in wal2 mode, as in wal2 mode the client always
 +          ** has a PART lock on one of the wal files, preventing it from being
 +          ** checkpointed or overwritten. */
 +          iFirst = pWal->hdr.mxFrame+1;
 +          if( memcmp(pWal->hdr.aSalt, (u32*)head.aSalt, sizeof(u32)*2) ){
 +            assert( pWal->readLock==0 );
 +            iFirst = 1;
 +          }
 +          mxFrame = head.mxFrame;
 +        }else{
 +          int iA = walidxGetFile(&pWal->hdr);
 +          if( iLoop==0 ){
 +            iFirst = walExternalEncode(iA, 1+walidxGetMxFrame(&pWal->hdr, iA));
 +            mxFrame = walExternalEncode(iA, walidxGetMxFrame(&head, iA));
 +          }else{
 +            iFirst = walExternalEncode(!iA, 1);
 +            mxFrame = walExternalEncode(!iA, walidxGetMxFrame(&head, !iA));
 +          }
 +        }
 +        iLastHash = walFramePage(mxFrame);
 +
 +        for(iHash=walFramePage(iFirst); iHash<=iLastHash; iHash += (1+bWal2)){
 +          WalHashLoc sLoc;
 +
 +          rc = walHashGet(pWal, iHash, &sLoc);
 +          if( rc==SQLITE_OK ){
 +            u32 i, iMin, iMax;
 +            assert( mxFrame>=sLoc.iZero );
 +            iMin = (sLoc.iZero >= iFirst) ? 1 : (iFirst - sLoc.iZero);
 +            iMax = (iHash==0) ? HASHTABLE_NPAGE_ONE : HASHTABLE_NPAGE;
 +            if( iMax>(mxFrame-sLoc.iZero) ) iMax = (mxFrame-sLoc.iZero);
 +            for(i=iMin; rc==SQLITE_OK && i<=iMax; i++){
 +              PgHdr *pPg;
 +              if( sLoc.aPgno[i-1]==1 ){
 +                /* Check that the schema cookie has not been modified. If
 +                ** it has not, the commit can proceed. */
 +                u8 aNew[4];
 +                u8 *aOld = &((u8*)pPg1->pData)[40];
 +                int sz;
 +                i64 iOff;
 +                u32 iFrame = sLoc.iZero + i;
 +                int iWal = 0;
 +                if( bWal2 ){
 +                  iWal = walExternalDecode(iFrame, &iFrame);
 +                }
 +                sz = head.szPage;
 +                sz = (sz&0xfe00) + ((sz&0x0001)<<16);
 +                iOff = walFrameOffset(iFrame, sz) + WAL_FRAME_HDRSIZE + 40;
 +                rc = sqlite3OsRead(pWal->apWalFd[iWal],aNew,sizeof(aNew),iOff);
 +                if( rc==SQLITE_OK && memcmp(aOld, aNew, sizeof(aNew)) ){
 +                  u32 iFrame = walConflictFrame(pWal, sLoc.iZero+i);
 +                  aConflict[SQLITE_COMMIT_CONFLICT_PGNO] = 1;
 +                  aConflict[SQLITE_COMMIT_CONFLICT_FRAME] = iFrame;
 +                  rc = SQLITE_BUSY_SNAPSHOT;
 +                }
 +              }else if( sqlite3BitvecTestNotNull(pAllRead, sLoc.aPgno[i-1]) ){
 +                u32 iFrame = walConflictFrame(pWal, sLoc.iZero+i);
 +                aConflict[SQLITE_COMMIT_CONFLICT_PGNO] = sLoc.aPgno[i-1];
 +                aConflict[SQLITE_COMMIT_CONFLICT_FRAME] = iFrame;
 +                rc = SQLITE_BUSY_SNAPSHOT;
 +              }else
 +              if( (pPg = sqlite3PagerLookup(pPg1->pPager, sLoc.aPgno[i-1])) ){
 +                /* Page aPgno[i], which is present in the pager cache, has been
 +                ** modified since the current CONCURRENT transaction was
 +                ** started.  However it was not read by the current
 +                ** transaction, so is not a conflict. There are two
 +                ** possibilities: (a) the page was allocated at the of the file
 +                ** by the current transaction or (b) was present in the cache
 +                ** at the start of the transaction.
 +                **
 +                ** For case (a), do nothing. This page will be moved within the
 +                ** database file by the commit code to avoid the conflict. The
 +                ** call to PagerUnref() is to release the reference grabbed by
 +                ** the sqlite3PagerLookup() above.  
 +                **
 +                ** In case (b), drop the page from the cache - otherwise
 +                ** following the snapshot upgrade the cache would be
 +                ** inconsistent with the database as stored on disk. */
 +                if( sqlite3PagerIswriteable(pPg) ){
 +                  sqlite3PagerUnref(pPg);
 +                }else{
 +                  sqlite3PcacheDrop(pPg);
 +                }
 +              }
 +            }
 +          }
 +          if( rc!=SQLITE_OK ) break;
 +        }
 +      }
 +    }
    }
  
 +  pWal->nPriorFrame = walGetPriorFrame(&pWal->hdr);
    return rc;
  }
  
@@@ -4426,59 -4386,38 +4701,62 @@@ int sqlite3WalUndo
  
      assert( isWalMode2(pWal) || iWal==0 );
  
-     /* Restore the clients cache of the wal-index header to the state it
-     ** was in before the client began writing to the database.
-     */
-     memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
-     iNew = walidxGetMxFrame(&pWal->hdr, walidxGetFile(&pWal->hdr));
-     /* BEGIN CONCURRENT transactions are different, as the header just
-     ** memcpy()d into pWal->hdr may not be the same as the current header 
-     ** when the transaction was started. Instead, pWal->hdr now contains
-     ** the header written by the most recent successful COMMIT. Because
-     ** Wal.writeLock is set, if this is a BEGIN CONCURRENT transaction,
-     ** the rollback must be taking place because an error occurred during
-     ** a COMMIT.
-     **
-     ** The code below is still valid. All frames between (iNew+1) and iMax 
-     ** must have been written by this transaction before the error occurred.
-     ** The exception is in wal2 mode - if the current wal file at the time
-     ** of the last COMMIT is not wal file iWal, then the error must have
-     ** occurred in WalLockForCommit(), before any pages were written
-     ** to the database file. In this case return early.  */
+     SEH_TRY {
+       /* Restore the clients cache of the wal-index header to the state it
+       ** was in before the client began writing to the database.
+       */
+       memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
 -      assert( walidxGetFile(&pWal->hdr)==iWal );
+       iNew = walidxGetMxFrame(&pWal->hdr, walidxGetFile(&pWal->hdr));
 -
++  
++      /* BEGIN CONCURRENT transactions are different, as the header just
++      ** memcpy()d into pWal->hdr may not be the same as the current header 
++      ** when the transaction was started. Instead, pWal->hdr now contains
++      ** the header written by the most recent successful COMMIT. Because
++      ** Wal.writeLock is set, if this is a BEGIN CONCURRENT transaction,
++      ** the rollback must be taking place because an error occurred during
++      ** a COMMIT.
++      **
++      ** The code below is still valid. All frames between (iNew+1) and iMax 
++      ** must have been written by this transaction before the error occurred.
++      ** The exception is in wal2 mode - if the current wal file at the time
++      ** of the last COMMIT is not wal file iWal, then the error must have
++      ** occurred in WalLockForCommit(), before any pages were written
++      ** to the database file. In this case return early.  */
 +#ifndef SQLITE_OMIT_CONCURRENT
-     if( bConcurrent ){
-       pWal->hdr.aCksum[0]++;
-     }
-     if( walidxGetFile(&pWal->hdr)!=iWal ){
-       assert( bConcurrent && isWalMode2(pWal) );
-       return SQLITE_OK;
-     }
++      if( bConcurrent ){
++        pWal->hdr.aCksum[0]++;
++      }
++      if( walidxGetFile(&pWal->hdr)!=iWal ){
++        assert( bConcurrent && isWalMode2(pWal) );
++        return SQLITE_OK;
++      }
 +#endif
-     assert( walidxGetFile(&pWal->hdr)==iWal );
-     for(iFrame=iNew+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++){
-       /* This call cannot fail. Unless the page for which the page number
-       ** is passed as the second argument is (a) in the cache and
-       ** (b) has an outstanding reference, then xUndo is either a no-op
-       ** (if (a) is false) or simply expels the page from the cache (if (b)
-       ** is false).
-       **
-       ** If the upper layer is doing a rollback, it is guaranteed that there
-       ** are no outstanding references to any page other than page 1. And
-       ** page 1 is never written to the log until the transaction is
-       ** committed. As a result, the call to xUndo may not fail.
-       */
-       Pgno pgno;
-       if( isWalMode2(pWal) ){
-         pgno = walFramePgno2(pWal, iWal, iFrame);
-       }else{
-         pgno = walFramePgno(pWal, iFrame);
++      assert( walidxGetFile(&pWal->hdr)==iWal );
++  
+       for(iFrame=iNew+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++){
+         /* This call cannot fail. Unless the page for which the page number
+         ** is passed as the second argument is (a) in the cache and
+         ** (b) has an outstanding reference, then xUndo is either a no-op
+         ** (if (a) is false) or simply expels the page from the cache (if (b)
+         ** is false).
+         **
+         ** If the upper layer is doing a rollback, it is guaranteed that there
+         ** are no outstanding references to any page other than page 1. And
+         ** page 1 is never written to the log until the transaction is
+         ** committed. As a result, the call to xUndo may not fail.
+         */
+         Pgno pgno;
+         if( isWalMode2(pWal) ){
+           pgno = walFramePgno2(pWal, iWal, iFrame);
+         }else{
+           pgno = walFramePgno(pWal, iFrame);
+         }
+         assert( pgno!=1 );
+         rc = xUndo(pUndoCtx, pgno);
        }
-       assert( pgno!=1 );
-       rc = xUndo(pUndoCtx, pgno);
+       if( iMax!=iNew ) walCleanupHash(pWal);
      }
-     if( iMax!=iNew ) walCleanupHash(pWal);
+     SEH_EXCEPT( rc = SQLITE_IOERR_IN_PAGE; )
    }
    return rc;
  }
@@@ -5099,39 -5060,45 +5404,43 @@@ int sqlite3WalCheckpoint
  
  
    /* Read the wal-index header. */
-   if( rc==SQLITE_OK ){
-     walDisableBlocking(pWal);
-     rc = walIndexReadHdr(pWal, &isChanged);
-     (void)walEnableBlocking(pWal);
-     if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
-       sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
-     }
-   }
-   /* Copy data from the log to the database file. */
-   if( rc==SQLITE_OK ){
-     if( (walPagesize(pWal)!=nBuf) 
-      && ((pWal->hdr.mxFrame2 & 0x7FFFFFFF) || pWal->hdr.mxFrame)
-     ){
-       rc = SQLITE_CORRUPT_BKPT;
-     }else{
-       rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags, zBuf);
+   SEH_TRY {
+     if( rc==SQLITE_OK ){
+       walDisableBlocking(pWal);
+       rc = walIndexReadHdr(pWal, &isChanged);
+       (void)walEnableBlocking(pWal);
+       if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
+         sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
+       }
      }
-     /* If no error occurred, set the output variables. */
-     if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
-       if( pnLog ){
-         *pnLog = walidxGetMxFrame(&pWal->hdr,0)+walidxGetMxFrame(&pWal->hdr,1);
+   
+     /* Copy data from the log to the database file. */
+     if( rc==SQLITE_OK ){
 -      int iCkpt = walidxGetFile(&pWal->hdr);
 -  
+       if( (walPagesize(pWal)!=nBuf) 
+        && ((pWal->hdr.mxFrame2 & 0x7FFFFFFF) || pWal->hdr.mxFrame)
+       ){
+         rc = SQLITE_CORRUPT_BKPT;
+       }else{
+         rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
        }
-       if( pnCkpt ){
-         if( isWalMode2(pWal) ){
-           if( (int)(walCkptInfo(pWal)->nBackfill) ){
-             *pnCkpt = walidxGetMxFrame(&pWal->hdr, !walidxGetFile(&pWal->hdr));
+   
+       /* If no error occurred, set the output variables. */
+       if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
+         if( pnLog ){
+           WalIndexHdr *pHdr = &pWal->hdr;
+           *pnLog = walidxGetMxFrame(pHdr, 0) + walidxGetMxFrame(pHdr, 1);
+         }
+         SEH_INJECT_FAULT;
+         if( pnCkpt ){
+           if( isWalMode2(pWal) ){
+             if( (int)(walCkptInfo(pWal)->nBackfill) ){
 -              *pnCkpt = walidxGetMxFrame(&pWal->hdr, iCkpt);
++              *pnCkpt = walidxGetMxFrame(&pWal->hdr,!walidxGetFile(&pWal->hdr));
+             }else{
+               *pnCkpt = 0;
+             }
            }else{
-             *pnCkpt = 0;
+             *pnCkpt = walCkptInfo(pWal)->nBackfill;
            }
-         }else{
-           *pnCkpt = walCkptInfo(pWal)->nBackfill;
          }
        }
      }
diff --cc src/wal.h
index 6989142d916a91f0428606f1dfa74b8f69370aad,9a047d053655227ac726c174910178296eb63839..9abf0a6cfda01bd3ee10deacb4f0f64d21c99264
+++ b/src/wal.h
@@@ -164,11 -155,9 +164,15 @@@ int sqlite3WalWriteLock(Wal *pWal, int 
  void sqlite3WalDb(Wal *pWal, sqlite3 *db);
  #endif
  
+ #ifdef SQLITE_USE_SEH
+ int sqlite3WalSystemErrno(Wal*);
+ #endif
 +/* sqlite3_wal_info() data */
 +int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
 +
 +/* sqlite3_wal_info() data */
 +int sqlite3WalInfo(Wal *pWal, u32 *pnPrior, u32 *pnFrame);
 +
  #endif /* ifndef SQLITE_OMIT_WAL */
  #endif /* SQLITE_WAL_H */