]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge latest trunk changes into this branch.
authordan <Dan Kennedy>
Sat, 13 Jan 2024 20:21:36 +0000 (20:21 +0000)
committerdan <Dan Kennedy>
Sat, 13 Jan 2024 20:21:36 +0000 (20:21 +0000)
FossilOrigin-Name: 68c2b8d0ebd1a180c4828cb4996c6c5fd67386c3b6304edf587c255f8d067c24

1  2 
manifest
manifest.uuid
src/wal.c
test/tester.tcl

diff --cc manifest
index fb47e2e122bfc5dc5a95fc01c8ed89c43ebab747,8c862e2c0c122ad920de679bd5770d394d88bffe..e75d7b02667d93642b77c39a624a164ce5980cc6
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\swal2\sbranch.
- D 2024-01-04T16:16:41.539
 -C Have\sthe\sshell\stool\sautomatically\senable\sSQLITE_CONFIG_DQS_DDL\swhen\sexecuting\sa\s".dump"\sscript\sagainst\san\sempty\sdb.
 -D 2024-01-12T11:44:49.861
++C Merge\slatest\strunk\schanges\sinto\sthis\sbranch.
++D 2024-01-13T20:21:36.767
  F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
  F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
  F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@@ -678,13 -676,13 +678,13 @@@ F src/auth.c 19b7ccacae3dfba23fc6f1d0af
  F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
  F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
  F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
 -F src/btree.c dee25e097b749275333b55d64a5ffc079249576f8e88a2ee476468cf67510f4b
 +F src/btree.c 737d43c75d9f15310663a9b0e4dff2d3f0e8f9b7ce4d5fa6e196c513eca34106
  F src/btree.h 03e3356f5208bcab8eed4e094240fdac4a7f9f5ddf5e91045ce589f67d47c240
  F src/btreeInt.h 3e2589726c4f105e653461814f65857465da68be1fac688de340c43b873f4062
- F src/build.c f640dbe723e35f29a91870ff8bd9fb015b1cbc49666014b1d422c012eb333de9
+ F src/build.c e7d9044592eeeea8e78d8ae53ca8d31fd6e92ca0d4f53e2f2e8ccf7352e0b04b
  F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
  F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 -F src/ctime.c 23331529e654be40ca97d171cbbffe9b3d4c71cc53b78fe5501230675952da8b
 +F src/ctime.c 047a0613c4c3ff65e05903d5b6931185b3df8f34b5178ad2f8d865ada4e9da44
  F src/date.c 3b8d02977d160e128469de38493b4085f7c5cf4073193459909a6af3cf6d7c91
  F src/dbpage.c 80e46e1df623ec40486da7a5086cb723b0275a6e2a7b01d9f9b5da0f04ba2782
  F src/dbstat.c 3b677254d512fcafd4d0b341bf267b38b235ccfddbef24f9154e19360fa22e43
@@@ -723,11 -721,11 +723,11 @@@ 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 1672c708df279fca1b6ba619cbb26a88baa7913b21dda95817290d76666a9688
+ F src/os_unix.c 5dc41030cd5dfd99b907976b1725a4ed695566405d33744e4824c3d6aff245a3
  F src/os_win.c 4a50a154aeebc66a1f8fb79c1ff6dd5fe3d005556533361e0d460d41cb6a45a8
  F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
 -F src/pager.c ff60e98138d2499082ac6230f01ac508aba545315debccfca2fd6042f5f10fcd
 -F src/pager.h 4b1140d691860de0be1347474c51fee07d5420bd7f802d38cbab8ea4ab9f538a
 +F src/pager.c ac0201bd6782fb760a433b1ebaddb0b8a5998f3efea6dad24954855db60a27bd
 +F src/pager.h fc0c95d27f2aefffcb39b4373b3557e89e1b4b9a36b183cc04d6f22002bf0ad2
  F src/parse.y 020d80386eb216ec9520549106353c517d2bbc89be28752ffdca649a9eaf56ec
  F src/pcache.c 040b165f30622a21b7a9a77c6f2e4877a32fb7f22d4c7f0d2a6fa6833a156a75
  F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
@@@ -821,8 -819,8 +821,8 @@@ F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1
  F src/vdbevtab.c 2143db7db0ceed69b21422581f434baffc507a08d831565193a7a02882a1b6a7
  F src/vtab.c 11948e105f56e84099ca17f1f434b1944539ea84de26d0d767eadfbc670ce1ea
  F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
- F src/wal.c 8542e921cd502a0f2a28dc544b61d9c473ceb1469d0d0cd7f6b97cfdcd504f51
 -F src/wal.c 887fc4ca3f020ebb2e376f222069570834ac63bf50111ef0cbf3ae417048ed89
 -F src/wal.h ba252daaa94f889f4b2c17c027e823d9be47ce39da1d3799886bbd51f0490452
++F src/wal.c 2586d8f3503ed7a59cb3becb380bd90a5ca218c2d331d3d33e3bed58d26aa1ae
 +F src/wal.h 97b8a9903387401377b59507e86b93a148ef1ad4e5ce0f23659a12dcdce56af2
  F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
  F src/where.c 217fe82a26c0fb6a3c7fd01865d821e752f9c01fb72f114af3f0b77ce234d1fb
  F src/whereInt.h 82a13766f13d1a53b05387c2e60726289ef26404bc7b9b1f7770204d97357fb8
@@@ -1332,11 -1330,11 +1332,11 @@@ F test/journal3.test 7c3cf23ffc77db0660
  F test/jrnlmode.test 9b5bc01dac22223cb60ec2d5f97acf568d73820794386de5634dcadbea9e1946
  F test/jrnlmode2.test 8759a1d4657c064637f8b079592651530db738419e1d649c6df7048cd724363d
  F test/jrnlmode3.test 556b447a05be0e0963f4311e95ab1632b11c9eaa
- F test/json/README.md 63e3e589e1df8fd3cc1588ba1faaff659214003f8b77a15af5c6452b35e30ee2
+ F test/json/README.md de59d5ba0bd2796d797115688630a6405bbf43a2891bad445ac6b9f38b83f236
  F test/json/json-generator.tcl dc0dd0f393800c98658fc4c47eaa6af29d4e17527380cd28656fb261bddc8a3f
- F test/json/json-q1-b.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7
  F test/json/json-q1.txt 65f9d1cdcc4cffa9823fb73ed936aae5658700cd001fde448f68bfb91c807307
- F test/json/json-speed-check.sh b060a9a6c696c0a807d8929400fa11bd7113edc58b0d66b9795f424f8d0db326 x
+ F test/json/json-speed-check.sh 912ee03e700a65c827ee0c7b4752c21ec5ef69cf7679d2f482ca817042bead52 x
 -F test/json/jsonb-q1.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7
++F test/json/jsonb-q1.txt 1e180fe6491efab307e318b22879e3a736ac9a96539bbde7911a13ee5b33abc7 w test/json/json-q1-b.txt
  F test/json101.test 70587d7d35ef9e2126364ba70f0c951f70827cfbd28649d779ff3df7e8f87547
  F test/json102.test 557a46e16df1aa9bdbc4076a71a45814ea0e7503d6621d87d42a8c04cbc2b0ef
  F test/json103.test 53df87f83a4e5fa0c0a56eb29ff6c94055c6eb919f33316d62161a8880112dbe
@@@ -1667,7 -1666,7 +1668,7 @@@ F test/temptable.test d2c9b87a54147161b
  F test/temptable2.test 76821347810ecc88203e6ef0dd6897b6036ac788e9dd3e6b04fd4d1631311a16
  F test/temptable3.test d11a0974e52b347e45ee54ef1923c91ed91e4637
  F test/temptrigger.test 38f0ca479b1822d3117069e014daabcaacefffcc
- F test/tester.tcl e6b9adcf4e56aa5c658501c10650646fee467b85d51a4ac80263d2cdae3993ba
 -F test/tester.tcl fe617b88c7eb08bdf983d2aaa31c20fbf439eee7b8e0d61ca636fcd0c305bbbf
++F test/tester.tcl 7977c8a32fcd09b5c51548a6db43e827f05ca1fd44598bb7986702bcfa1f0cd4
  F test/testrunner.tcl 8e2a5c7550b78d3283eee6103104ae2bcf56aa1df892dbd1608f27b93ebf4de8
  F test/testrunner_data.tcl 7ffd951527bbc614e723fd8d123b6834321878530696adecfdf6035100bac64e
  F test/thread001.test a0985c117eab62c0c65526e9fa5d1360dd1cac5b03bde223902763274ce21899
@@@ -2170,8 -2157,8 +2171,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 1f592dd32d165456d40a90a2757225e05cdb810518beee87f0700863dc73d2d0 fe952c12903ea2150880c8bb57cda2efc00ce9fa801568a68c619e0745f30567
- R 9d23944f15f13801b549dcf524d06ec2
- U drh
- Z 0665dd1dce8682bc2822e601446991ed
 -P b0eb6d3628c1f70399a22d9fd3b79a796bc343adfeba50515440db609565961a
 -R ab5a4296dc153e787688d4ab18626d94
++P 8fb42df89a47b716c824de8742b7e3bda1a5c0f9a85ce3f328d7aa94ab735497 f47a5f4e0ce078e6cc1183e6cbb3c4013af379b496efae94863a42e5c39928ed
++R 533592aca6769708484a27173b269b8a
+ U dan
 -Z 9795dc3f51dc5999429a02089c48c9c6
++Z 3755d176b3b83dd08bce5a725f4586c0
  # Remove this line to create a well-formed Fossil manifest.
diff --cc manifest.uuid
index 0dec249e019847dda41a1b00bfee4c12c90ec136,eaba41901e075c611fa0570555061f14110b5569..e9ab00451077127433abb004b32e939693090bc1
@@@ -1,1 -1,1 +1,1 @@@
- 8fb42df89a47b716c824de8742b7e3bda1a5c0f9a85ce3f328d7aa94ab735497
 -f47a5f4e0ce078e6cc1183e6cbb3c4013af379b496efae94863a42e5c39928ed
++68c2b8d0ebd1a180c4828cb4996c6c5fd67386c3b6304edf587c255f8d067c24
diff --cc src/wal.c
index f1fd2eda8e5b4ddafe10e51ceecf5b0c57ca12e2,fd2eabfd963c6b07ace6d9a6fde8824a26e36d4e..17d63439ad53903cb0b0c6dd88f3544ba1c53eab
+++ b/src/wal.c
@@@ -3579,9 -2983,13 +3610,9 @@@ static int walBeginShmUnreliable(Wal *p
  ** so it takes care to hold an exclusive lock on the corresponding
  ** WAL_READ_LOCK() while changing values.
  */
- static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
+ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
    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 */
  #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    int nBlockTmout = 0;
  #endif
    assert( pWal->apWiData[0]!=0 );
    pInfo = walCkptInfo(pWal);
    SEH_INJECT_FAULT;
 -  if( !useWal && AtomicLoad(&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));
 +  if( isWalMode2(pWal) ){
 +    /* This connection needs a "part" lock on the current wal file and, 
 +    ** unless pInfo->nBackfill is set to indicate that it has already been
 +    ** checkpointed, a "full" lock on the other wal file.  */
 +    int iWal = walidxGetFile(&pWal->hdr);
 +    int nBackfill = pInfo->nBackfill || walidxGetMxFrame(&pWal->hdr, !iWal)==0;
 +    int eLock = 1 + (iWal*2) + (nBackfill==iWal);
 +
 +    assert( nBackfill==0 || nBackfill==1 );
 +    assert( iWal==0 || iWal==1 );
 +    assert( iWal!=0 || nBackfill!=1 || eLock==WAL_LOCK_PART1 );
 +    assert( iWal!=0 || nBackfill!=0 || eLock==WAL_LOCK_PART1_FULL2 );
 +    assert( iWal!=1 || nBackfill!=1 || eLock==WAL_LOCK_PART2 );
 +    assert( iWal!=1 || nBackfill!=0 || eLock==WAL_LOCK_PART2_FULL1 );
 +
 +    rc = walLockShared(pWal, WAL_READ_LOCK(eLock));
 +    if( rc!=SQLITE_OK ){
 +      return (rc==SQLITE_BUSY ? WAL_RETRY : rc);
 +    }
      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;
 +    if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 +      walUnlockShared(pWal, WAL_READ_LOCK(eLock));
 +      return WAL_RETRY;
 +    }else{
 +      pWal->readLock = eLock;
 +    }
 +    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)
 +  #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 ){
 +        return rc;
        }
 -      pWal->readLock = 0;
 -      return SQLITE_OK;
 -    }else if( rc!=SQLITE_BUSY ){
 -      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); SEH_INJECT_FAULT;
 -    if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 -      assert( thisMark!=READMARK_NOT_USED );
 -      mxReadMark = thisMark;
 -      mxI = i;
 +  
 +    /* 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;
      }
 -  }
 -  if( (pWal->readOnly & WAL_SHM_RDONLY)==0
 -   && (mxReadMark<mxFrame || mxI==0)
 -  ){
 +  #endif
      for(i=1; i<WAL_NREADER; i++){
 -      rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
 -      if( rc==SQLITE_OK ){
 -        AtomicStore(pInfo->aReadMark+i,mxFrame);
 -        mxReadMark = mxFrame;
 +      u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
 +      if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 +        assert( thisMark!=READMARK_NOT_USED );
 +        mxReadMark = thisMark;
          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_CANTINIT;
 -  }
 -
 -  (void)walEnableBlockingMs(pWal, nBlockTmout);
 -  rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 -  walDisableBlocking(pWal);
 -  if( rc ){
 -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
 -    if( rc==SQLITE_BUSY_TIMEOUT ){
 -      *pCnt |= WAL_RETRY_BLOCKED_MASK;
 +    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);
 +        if( rc==SQLITE_OK ){
 +          AtomicStore(pInfo->aReadMark+i,mxFrame);
 +          mxReadMark = 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_CANTINIT;
      }
 -    assert( rc!=SQLITE_BUSY_TIMEOUT );
 +  
 +    (void)walEnableBlockingMs(pWal, nBlockTmout);
 +    rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 +    walDisableBlocking(pWal);
 +    if( rc ){
++#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
++      if( rc==SQLITE_BUSY_TIMEOUT ){
++        *pCnt |= WAL_RETRY_BLOCKED_MASK;
++      }
+ #else
 -    assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
 -    return (rc&0xFF)==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; SEH_INJECT_FAULT;
 -  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;
++      assert( rc!=SQLITE_BUSY_TIMEOUT );
+ #endif
 +      assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT);
 +      return (rc&0xFF)==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; SEH_INJECT_FAULT;
 +    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;
 +    }
    }
    return rc;
  }
diff --cc test/tester.tcl
Simple merge