/ Check-in [cb68e9d0]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add the nBackfillAttempted field in formerly unused space in WalCkptInfo and use that field to close the race condition on opening a snapshot.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | snapshot-get
Files: files | file ages | folders
SHA1: cb68e9d0738fc7db7316947b4d2aab91aae819f2
User & Date: drh 2015-12-10 02:15:03
Context
2015-12-10
03:16
Fix spacing typo in comment. No changes to code. check-in: 3a18526f user: mistachkin tags: snapshot-get
02:15
Add the nBackfillAttempted field in formerly unused space in WalCkptInfo and use that field to close the race condition on opening a snapshot. check-in: cb68e9d0 user: drh tags: snapshot-get
2015-12-09
20:05
Update sqlite3_snapshot_open() to reduce the chances of reading a corrupt snapshot created by a checkpointer process exiting unexpectedly. check-in: 7315f7cb user: dan tags: snapshot-get
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/wal.c.

   268    268   ** returns SQLITE_CANTOPEN.
   269    269   */
   270    270   #define WAL_MAX_VERSION      3007000
   271    271   #define WALINDEX_MAX_VERSION 3007000
   272    272   
   273    273   /*
   274    274   ** Indices of various locking bytes.   WAL_NREADER is the number
   275         -** of available reader locks and should be at least 3.
          275  +** of available reader locks and should be at least 3.  The default
          276  +** is SQLITE_SHM_NLOCK==8 and  WAL_NREADER==5.
   276    277   */
   277    278   #define WAL_WRITE_LOCK         0
   278    279   #define WAL_ALL_BUT_WRITE      1
   279    280   #define WAL_CKPT_LOCK          1
   280    281   #define WAL_RECOVER_LOCK       2
   281    282   #define WAL_READ_LOCK(I)       (3+(I))
   282    283   #define WAL_NREADER            (SQLITE_SHM_NLOCK-3)
................................................................................
   288    289   typedef struct WalCkptInfo WalCkptInfo;
   289    290   
   290    291   
   291    292   /*
   292    293   ** The following object holds a copy of the wal-index header content.
   293    294   **
   294    295   ** The actual header in the wal-index consists of two copies of this
   295         -** object.
          296  +** object followed by one instance of the WalCkptInfo object.
          297  +** For all versions of SQLite through 3.10.0 and probably beyond,
          298  +** the locking bytes (WalCkptInfo.aLock) start at offset 120 and
          299  +** the total header size is 136 bytes.
   296    300   **
   297    301   ** The szPage value can be any power of 2 between 512 and 32768, inclusive.
   298    302   ** Or it can be 1 to represent a 65536-byte page.  The latter case was
   299    303   ** added in 3.7.1 when support for 64K pages was added.  
   300    304   */
   301    305   struct WalIndexHdr {
   302    306     u32 iVersion;                   /* Wal-index version */
................................................................................
   320    324   ** nBackfill is the number of frames in the WAL that have been written
   321    325   ** back into the database. (We call the act of moving content from WAL to
   322    326   ** database "backfilling".)  The nBackfill number is never greater than
   323    327   ** WalIndexHdr.mxFrame.  nBackfill can only be increased by threads
   324    328   ** holding the WAL_CKPT_LOCK lock (which includes a recovery thread).
   325    329   ** However, a WAL_WRITE_LOCK thread can move the value of nBackfill from
   326    330   ** mxFrame back to zero when the WAL is reset.
          331  +**
          332  +** nBackfillAttempted is the largest value of nBackfill that a checkpoint
          333  +** has attempted to achieve.  Normally nBackfill==nBackfillAtempted, however
          334  +** the nBackfillAttempted is set before any backfilling is done and the
          335  +** nBackfill is only set afte rall backfilling completes.  So if a checkpoint
          336  +** crashes, nBackfillAttempted might be larger than nBackfill.  The
          337  +** WalIndexHdr.mxFrame must never be less than nBackfillAttempted.
          338  +**
          339  +** The aLock[] field is a set of bytes used for locking.  These bytes should
          340  +** never be read or written.
   327    341   **
   328    342   ** There is one entry in aReadMark[] for each reader lock.  If a reader
   329    343   ** holds read-lock K, then the value in aReadMark[K] is no greater than
   330    344   ** the mxFrame for that reader.  The value READMARK_NOT_USED (0xffffffff)
   331    345   ** for any aReadMark[] means that entry is unused.  aReadMark[0] is 
   332    346   ** a special case; its value is never used and it exists as a place-holder
   333    347   ** to avoid having to offset aReadMark[] indexs by one.  Readers holding
................................................................................
   360    374   **
   361    375   ** We assume that 32-bit loads are atomic and so no locks are needed in
   362    376   ** order to read from any aReadMark[] entries.
   363    377   */
   364    378   struct WalCkptInfo {
   365    379     u32 nBackfill;                  /* Number of WAL frames backfilled into DB */
   366    380     u32 aReadMark[WAL_NREADER];     /* Reader marks */
          381  +  u8 aLock[SQLITE_SHM_NLOCK];     /* Reserved space for locks */
          382  +  u32 nBackfillAttempted;         /* WAL frames perhaps written, or maybe not */
          383  +  u32 notUsed0;                   /* Available for future enhancements */
   367    384   };
   368    385   #define READMARK_NOT_USED  0xffffffff
   369    386   
   370    387   
   371    388   /* A block of WALINDEX_LOCK_RESERVED bytes beginning at
   372    389   ** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
   373    390   ** only support mandatory file-locks, we do not read or write data
   374    391   ** from the region of the file on which locks are applied.
   375    392   */
   376         -#define WALINDEX_LOCK_OFFSET   (sizeof(WalIndexHdr)*2 + sizeof(WalCkptInfo))
   377         -#define WALINDEX_LOCK_RESERVED 16
   378         -#define WALINDEX_HDR_SIZE      (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)
          393  +#define WALINDEX_LOCK_OFFSET (sizeof(WalIndexHdr)*2+offsetof(WalCkptInfo,aLock))
          394  +#define WALINDEX_HDR_SIZE    (sizeof(WalIndexHdr)*2+sizeof(WalCkptInfo))
   379    395   
   380    396   /* Size of header before each frame in wal */
   381    397   #define WAL_FRAME_HDRSIZE 24
   382    398   
   383    399   /* Size of write ahead log header, including checksum. */
   384    400   /* #define WAL_HDRSIZE 24 */
   385    401   #define WAL_HDRSIZE 32
................................................................................
   431    447     u32 minFrame;              /* Ignore wal frames before this one */
   432    448     const char *zWalName;      /* Name of WAL file */
   433    449     u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
   434    450   #ifdef SQLITE_DEBUG
   435    451     u8 lockError;              /* True if a locking error has occurred */
   436    452   #endif
   437    453   #ifdef SQLITE_ENABLE_SNAPSHOT
   438         -  WalIndexHdr *pSnapshot;
          454  +  WalIndexHdr *pSnapshot;    /* Start transaction here if not NULL */
   439    455   #endif
   440    456   };
   441    457   
   442    458   /*
   443    459   ** Candidate values for Wal.exclusiveMode.
   444    460   */
   445    461   #define WAL_NORMAL_MODE     0
................................................................................
  1197   1213   
  1198   1214       /* Reset the checkpoint-header. This is safe because this thread is 
  1199   1215       ** currently holding locks that exclude all other readers, writers and
  1200   1216       ** checkpointers.
  1201   1217       */
  1202   1218       pInfo = walCkptInfo(pWal);
  1203   1219       pInfo->nBackfill = 0;
         1220  +    pInfo->nBackfillAttempted = 0;
  1204   1221       pInfo->aReadMark[0] = 0;
  1205   1222       for(i=1; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
  1206   1223       if( pWal->hdr.mxFrame ) pInfo->aReadMark[1] = pWal->hdr.mxFrame;
  1207   1224   
  1208   1225       /* If more than one frame was recovered from the log file, report an
  1209   1226       ** event via sqlite3_log(). This is to help with identifying performance
  1210   1227       ** problems caused by applications routinely shutting down without
................................................................................
  1268   1285   
  1269   1286     assert( zWalName && zWalName[0] );
  1270   1287     assert( pDbFd );
  1271   1288   
  1272   1289     /* In the amalgamation, the os_unix.c and os_win.c source files come before
  1273   1290     ** this source file.  Verify that the #defines of the locking byte offsets
  1274   1291     ** in os_unix.c and os_win.c agree with the WALINDEX_LOCK_OFFSET value.
         1292  +  ** For that matter, if the lock offset ever changes from its initial design
         1293  +  ** value of 120, we need to know that so there is an assert() to check it.
  1275   1294     */
         1295  +  assert( 120==WALINDEX_LOCK_OFFSET );
         1296  +  assert( 136==WALINDEX_HDR_SIZE );
  1276   1297   #ifdef WIN_SHM_BASE
  1277   1298     assert( WIN_SHM_BASE==WALINDEX_LOCK_OFFSET );
  1278   1299   #endif
  1279   1300   #ifdef UNIX_SHM_BASE
  1280   1301     assert( UNIX_SHM_BASE==WALINDEX_LOCK_OFFSET );
  1281   1302   #endif
  1282   1303   
................................................................................
  1654   1675     u32 *aSalt = pWal->hdr.aSalt;   /* Big-endian salt values */
  1655   1676     pWal->nCkpt++;
  1656   1677     pWal->hdr.mxFrame = 0;
  1657   1678     sqlite3Put4byte((u8*)&aSalt[0], 1 + sqlite3Get4byte((u8*)&aSalt[0]));
  1658   1679     memcpy(&pWal->hdr.aSalt[1], &salt1, 4);
  1659   1680     walIndexWriteHdr(pWal);
  1660   1681     pInfo->nBackfill = 0;
         1682  +  pInfo->nBackfillAttempted = 0;
  1661   1683     pInfo->aReadMark[1] = 0;
  1662   1684     for(i=2; i<WAL_NREADER; i++) pInfo->aReadMark[i] = READMARK_NOT_USED;
  1663   1685     assert( pInfo->aReadMark[0]==0 );
  1664   1686   }
  1665   1687   
  1666   1688   /*
  1667   1689   ** Copy as much content as we can from the WAL back into the database file
................................................................................
  1731   1753   
  1732   1754       /* Compute in mxSafeFrame the index of the last frame of the WAL that is
  1733   1755       ** safe to write into the database.  Frames beyond mxSafeFrame might
  1734   1756       ** overwrite database pages that are in use by active readers and thus
  1735   1757       ** cannot be backfilled from the WAL.
  1736   1758       */
  1737   1759       mxSafeFrame = pWal->hdr.mxFrame;
         1760  +    pInfo->nBackfillAttempted = mxSafeFrame;
  1738   1761       mxPage = pWal->hdr.nPage;
  1739   1762       for(i=1; i<WAL_NREADER; i++){
  1740   1763         /* Thread-sanitizer reports that the following is an unsafe read,
  1741   1764         ** as some other thread may be in the process of updating the value
  1742   1765         ** of the aReadMark[] slot. The assumption here is that if that is
  1743   1766         ** happening, the other client may only be increasing the value,
  1744   1767         ** not decreasing it. So assuming either that either the "old" or
................................................................................
  2267   2290       u32 thisMark = pInfo->aReadMark[i];
  2268   2291       if( mxReadMark<=thisMark && thisMark<=mxFrame ){
  2269   2292         assert( thisMark!=READMARK_NOT_USED );
  2270   2293         mxReadMark = thisMark;
  2271   2294         mxI = i;
  2272   2295       }
  2273   2296     }
  2274         -  /* There was once an "if" here. The extra "{" is to preserve indentation. */
  2275         -  {
  2276         -    if( (pWal->readOnly & WAL_SHM_RDONLY)==0
  2277         -     && (mxReadMark<mxFrame || mxI==0)
         2297  +  if( (pWal->readOnly & WAL_SHM_RDONLY)==0
         2298  +   && (mxReadMark<mxFrame || mxI==0)
  2278   2299   #ifdef SQLITE_ENABLE_SNAPSHOT
  2279         -     && pWal->pSnapshot==0
         2300  +   && pWal->pSnapshot==0
  2280   2301   #endif
  2281         -    ){
  2282         -      for(i=1; i<WAL_NREADER; i++){
  2283         -        rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
  2284         -        if( rc==SQLITE_OK ){
  2285         -          mxReadMark = pInfo->aReadMark[i] = mxFrame;
  2286         -          mxI = i;
  2287         -          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
  2288         -          break;
  2289         -        }else if( rc!=SQLITE_BUSY ){
  2290         -          return rc;
  2291         -        }
         2302  +  ){
         2303  +    for(i=1; i<WAL_NREADER; i++){
         2304  +      rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
         2305  +      if( rc==SQLITE_OK ){
         2306  +        mxReadMark = pInfo->aReadMark[i] = mxFrame;
         2307  +        mxI = i;
         2308  +        walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
         2309  +        break;
         2310  +      }else if( rc!=SQLITE_BUSY ){
         2311  +        return rc;
  2292   2312         }
  2293   2313       }
  2294         -    if( mxI==0 ){
         2314  +  }
         2315  +  if( mxI==0 ){
  2295   2316   #ifdef SQLITE_ENABLE_SNAPSHOT
  2296         -      if( pWal->pSnapshot ) return SQLITE_BUSY_SNAPSHOT;
         2317  +    if( pWal->pSnapshot ) return SQLITE_BUSY_SNAPSHOT;
  2297   2318   #endif
  2298         -      assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
  2299         -      return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
  2300         -    }
  2301         -
  2302         -    rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
  2303         -    if( rc ){
  2304         -      return rc==SQLITE_BUSY ? WAL_RETRY : rc;
  2305         -    }
  2306         -    /* Now that the read-lock has been obtained, check that neither the
  2307         -    ** value in the aReadMark[] array or the contents of the wal-index
  2308         -    ** header have changed.
  2309         -    **
  2310         -    ** It is necessary to check that the wal-index header did not change
  2311         -    ** between the time it was read and when the shared-lock was obtained
  2312         -    ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
  2313         -    ** that the log file may have been wrapped by a writer, or that frames
  2314         -    ** that occur later in the log than pWal->hdr.mxFrame may have been
  2315         -    ** copied into the database by a checkpointer. If either of these things
  2316         -    ** happened, then reading the database with the current value of
  2317         -    ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
  2318         -    ** instead.
  2319         -    **
  2320         -    ** Before checking that the live wal-index header has not changed
  2321         -    ** since it was read, set Wal.minFrame to the first frame in the wal
  2322         -    ** file that has not yet been checkpointed. This client will not need
  2323         -    ** to read any frames earlier than minFrame from the wal file - they
  2324         -    ** can be safely read directly from the database file.
  2325         -    **
  2326         -    ** Because a ShmBarrier() call is made between taking the copy of 
  2327         -    ** nBackfill and checking that the wal-header in shared-memory still
  2328         -    ** matches the one cached in pWal->hdr, it is guaranteed that the 
  2329         -    ** checkpointer that set nBackfill was not working with a wal-index
  2330         -    ** header newer than that cached in pWal->hdr. If it were, that could
  2331         -    ** cause a problem. The checkpointer could omit to checkpoint
  2332         -    ** a version of page X that lies before pWal->minFrame (call that version
  2333         -    ** A) on the basis that there is a newer version (version B) of the same
  2334         -    ** page later in the wal file. But if version B happens to like past
  2335         -    ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
  2336         -    ** that it can read version A from the database file. However, since
  2337         -    ** we can guarantee that the checkpointer that set nBackfill could not
  2338         -    ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
  2339         -    */
  2340         -    pWal->minFrame = pInfo->nBackfill+1;
  2341         -    walShmBarrier(pWal);
  2342         -    if( pInfo->aReadMark[mxI]!=mxReadMark
  2343         -     || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
  2344         -    ){
  2345         -      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
  2346         -      return WAL_RETRY;
  2347         -    }else{
  2348         -      assert( mxReadMark<=pWal->hdr.mxFrame );
  2349         -      pWal->readLock = (i16)mxI;
  2350         -    }
         2319  +    assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
         2320  +    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTLOCK;
         2321  +  }
         2322  +
         2323  +  rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
         2324  +  if( rc ){
         2325  +    return rc==SQLITE_BUSY ? WAL_RETRY : rc;
         2326  +  }
         2327  +  /* Now that the read-lock has been obtained, check that neither the
         2328  +  ** value in the aReadMark[] array or the contents of the wal-index
         2329  +  ** header have changed.
         2330  +  **
         2331  +  ** It is necessary to check that the wal-index header did not change
         2332  +  ** between the time it was read and when the shared-lock was obtained
         2333  +  ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
         2334  +  ** that the log file may have been wrapped by a writer, or that frames
         2335  +  ** that occur later in the log than pWal->hdr.mxFrame may have been
         2336  +  ** copied into the database by a checkpointer. If either of these things
         2337  +  ** happened, then reading the database with the current value of
         2338  +  ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
         2339  +  ** instead.
         2340  +  **
         2341  +  ** Before checking that the live wal-index header has not changed
         2342  +  ** since it was read, set Wal.minFrame to the first frame in the wal
         2343  +  ** file that has not yet been checkpointed. This client will not need
         2344  +  ** to read any frames earlier than minFrame from the wal file - they
         2345  +  ** can be safely read directly from the database file.
         2346  +  **
         2347  +  ** Because a ShmBarrier() call is made between taking the copy of 
         2348  +  ** nBackfill and checking that the wal-header in shared-memory still
         2349  +  ** matches the one cached in pWal->hdr, it is guaranteed that the 
         2350  +  ** checkpointer that set nBackfill was not working with a wal-index
         2351  +  ** header newer than that cached in pWal->hdr. If it were, that could
         2352  +  ** cause a problem. The checkpointer could omit to checkpoint
         2353  +  ** a version of page X that lies before pWal->minFrame (call that version
         2354  +  ** A) on the basis that there is a newer version (version B) of the same
         2355  +  ** page later in the wal file. But if version B happens to like past
         2356  +  ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
         2357  +  ** that it can read version A from the database file. However, since
         2358  +  ** we can guarantee that the checkpointer that set nBackfill could not
         2359  +  ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
         2360  +  */
         2361  +  pWal->minFrame = pInfo->nBackfill+1;
         2362  +  walShmBarrier(pWal);
         2363  +  if( pInfo->aReadMark[mxI]!=mxReadMark
         2364  +   || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
         2365  +  ){
         2366  +    walUnlockShared(pWal, WAL_READ_LOCK(mxI));
         2367  +    return WAL_RETRY;
         2368  +  }else{
         2369  +    assert( mxReadMark<=pWal->hdr.mxFrame );
         2370  +    pWal->readLock = (i16)mxI;
  2351   2371     }
  2352   2372     return rc;
  2353   2373   }
  2354   2374   
  2355   2375   /*
  2356   2376   ** Begin a read transaction on the database.
  2357   2377   **
................................................................................
  2369   2389   int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
  2370   2390     int rc;                         /* Return code */
  2371   2391     int cnt = 0;                    /* Number of TryBeginRead attempts */
  2372   2392   
  2373   2393   #ifdef SQLITE_ENABLE_SNAPSHOT
  2374   2394     int bChanged = 0;
  2375   2395     WalIndexHdr *pSnapshot = pWal->pSnapshot;
  2376         -  if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))){
         2396  +  if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
  2377   2397       bChanged = 1;
  2378   2398     }
  2379   2399   #endif
  2380   2400   
  2381   2401     do{
  2382   2402       rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
  2383   2403     }while( rc==WAL_RETRY );
................................................................................
  2384   2404     testcase( (rc&0xff)==SQLITE_BUSY );
  2385   2405     testcase( (rc&0xff)==SQLITE_IOERR );
  2386   2406     testcase( rc==SQLITE_PROTOCOL );
  2387   2407     testcase( rc==SQLITE_OK );
  2388   2408   
  2389   2409   #ifdef SQLITE_ENABLE_SNAPSHOT
  2390   2410     if( rc==SQLITE_OK ){
  2391         -    if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr)) ){
         2411  +    if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
  2392   2412         /* At this point the client has a lock on an aReadMark[] slot holding
  2393         -      ** a value equal to or smaller than pSnapshot->mxFrame. This client
  2394         -      ** did not populate the aReadMark[] slot. pWal->hdr is populated with
  2395         -      ** the wal-index header for the snapshot currently at the head of the
  2396         -      ** wal file, which is different from pSnapshot.
         2413  +      ** a value equal to or smaller than pSnapshot->mxFrame.  Verify that
         2414  +      ** pSnapshot is still valid before continuing.  Reasons why pSnapshot
         2415  +      ** might no longer be valid:
         2416  +      **
         2417  +      **    (1)  The WAL file has been reset since the snapshot was taken.
         2418  +      **         In this case, the salt will have changed.
  2397   2419         **
  2398         -      ** The presence of the aReadMark[] slot entry makes it very likely 
  2399         -      ** that either there is currently another read-transaction open on
  2400         -      ** pSnapshot, or that there has been one more recently than the last
  2401         -      ** checkpoint of any frames greater than pSnapshot->mxFrame was 
  2402         -      ** started. There is an exception though: client 1 may have called
  2403         -      ** walTryBeginRead and started to open snapshot pSnapshot, setting
  2404         -      ** the aReadMark[] slot to do so. At the same time, client 2 may 
  2405         -      ** have committed a new snapshot to disk and started a checkpoint.
  2406         -      ** In this circumstance client 1 does not end up reading pSnapshot,
  2407         -      ** but may leave the aReadMark[] slot populated.
  2408         -      **
  2409         -      ** The race condition above is difficult to detect. One approach would
  2410         -      ** be to check the aReadMark[] slot for another client. But this is
  2411         -      ** prone to false-positives from other snapshot clients. And there
  2412         -      ** is no equivalent to xCheckReservedLock() for wal locks. Another
  2413         -      ** approach would be to take the checkpointer lock and check that
  2414         -      ** fewer than pSnapshot->mxFrame frames have been checkpointed. But
  2415         -      ** that does not account for checkpointer processes that failed after
  2416         -      ** checkpointing frames but before updating WalCkptInfo.nBackfill.
  2417         -      ** And it would mean that this function would block on checkpointers
  2418         -      ** and vice versa.
  2419         -      **
  2420         -      ** TODO: For now, this race condition is ignored.
         2420  +      **    (2)  A checkpoint as been attempted that wrote frames past
         2421  +      **         pSnapshot->mxFrame into the database file.  Note that the
         2422  +      **         checkpoint need not have completed for this to cause problems.
  2421   2423         */
  2422   2424         volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
  2423   2425   
  2424   2426         assert( pWal->readLock>0 );
  2425   2427         assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
  2426   2428   
  2427   2429         /* Check that the wal file has not been wrapped. Assuming it has not,
  2428   2430         ** overwrite pWal->hdr with *pSnapshot and set *pChanged as appropriate
  2429   2431         ** for opening the snapshot. Or, if the wal file has been wrapped
  2430   2432         ** since pSnapshot was written, return SQLITE_BUSY_SNAPSHOT. */
  2431         -      if( pSnapshot->aSalt[0]==pWal->hdr.aSalt[0]
  2432         -       && pSnapshot->aSalt[1]==pWal->hdr.aSalt[1]
         2433  +      if( memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))==0
         2434  +       && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
  2433   2435         ){
  2434   2436           memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
  2435   2437           *pChanged = bChanged;
  2436   2438         }else{
  2437   2439           rc = SQLITE_BUSY_SNAPSHOT;
  2438   2440         }
  2439   2441