/ Check-in [72de0073]
Login

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

Overview
Comment:Change the semantics of xShmGet() such that it will never increase the size of shared memory. xShmSize() must be used to grow the size of shared memory. A shared memory segment size cannot be shrunk (except by dropping it).
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 72de00731245277b2209103ec0a76e3d4f56530e
User & Date: drh 2010-05-26 15:06:38
Context
2010-05-26
17:31
Make sure the wal-index mapping is always large enough to cover the entire active area of the wal-index. check-in: 42705bab user: drh tags: trunk
15:06
Change the semantics of xShmGet() such that it will never increase the size of shared memory. xShmSize() must be used to grow the size of shared memory. A shared memory segment size cannot be shrunk (except by dropping it). check-in: 72de0073 user: drh tags: trunk
2010-05-25
15:53
Updated header comments in wal.c. No functional code changes. check-in: 687632a6 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/os_unix.c.

3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
....
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657

3658
3659
3660


3661
3662
3663
3664
3665
3666
3667
....
3670
3671
3672
3673
3674
3675
3676
3677



3678
3679
3680
3681
3682
3683
3684
3685
3686
....
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708

3709
3710
3711

3712
3713
3714




3715
3716
3717
3718
3719
3720
3721
  }
  unixLeaveMutex();

  return SQLITE_OK;
}

/*
** Query and/or changes the size of the underlying storage for
** a shared-memory segment.  The reqSize parameter is the new size
** of the underlying storage, or -1 to do just a query.  The size
** of the underlying storage (after resizing if resizing occurs) is
** written into pNewSize.
**
** This routine does not (necessarily) change the size of the mapping 
** of the underlying storage into memory.  Use xShmGet() to change
** the mapping size.
**
** The reqSize parameter is the minimum size requested.  The implementation
** is free to expand the storage to some larger amount if it chooses.
*/
static int unixShmSize(
  sqlite3_file *fd,         /* The open database file holding SHM */
  int reqSize,              /* Requested size.  -1 for query only */
................................................................................
  unixShmNode *pShmNode = p->pShmNode;
  int rc = SQLITE_OK;
  struct stat sStat;

  assert( pShmNode==pDbFd->pInode->pShmNode );
  assert( pShmNode->pInode==pDbFd->pInode );

  /* On a query, this loop runs once.  When reqSize>=0, the loop potentially
  ** runs twice, except if the actual size is already greater than or equal
  ** to the requested size, reqSize is set to -1 on the first iteration and
  ** the loop only runs once.
  */
  while( 1 ){
    if( fstat(pShmNode->h, &sStat)==0 ){
      *pNewSize = (int)sStat.st_size;
      if( reqSize>=0 && reqSize<=(int)sStat.st_size ) break;
    }else{
      *pNewSize = 0;
      rc = SQLITE_IOERR;
      break;
    }
    if( reqSize<0 ) break;
    reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR;
    reqSize *= SQLITE_UNIX_SHM_INCR;
    rc = ftruncate(pShmNode->h, reqSize);
    reqSize = -1;
  }
  return rc;
}


/*
** Map the shared storage into memory.  The minimum size of the

** mapping should be reqMapSize if reqMapSize is positive.  If
** reqMapSize is zero or negative, the implementation can choose
** whatever mapping size is convenient.


**
** *ppBuf is made to point to the memory which is a mapping of the
** underlying storage.  A mutex is acquired to prevent other threads
** from running while *ppBuf is in use in order to prevent other threads
** remapping *ppBuf out from under this thread.  The unixShmRelease()
** call will release the mutex.  However, if the lock state is CHECKPOINT,
** the mutex is not acquired because CHECKPOINT will never remap the
................................................................................
**
** RECOVER needs to be atomic.  The same mutex that prevents *ppBuf from
** being remapped also prevents more than one thread from being in
** RECOVER at a time.  But, RECOVER sometimes wants to remap itself.
** To prevent RECOVER from losing its lock while remapping, the
** mutex is not released by unixShmRelease() when in RECOVER.
**
** *pNewMapSize is set to the size of the mapping.



**
** *ppBuf and *pNewMapSize might be NULL and zero if no space has
** yet been allocated to the underlying storage.
*/
static int unixShmGet(
  sqlite3_file *fd,        /* Database file holding shared memory */
  int reqMapSize,          /* Requested size of mapping. -1 means don't care */
  int *pNewMapSize,        /* Write new size of mapping here */
  void volatile **ppBuf    /* Write mapping buffer origin here */
................................................................................
    assert( sqlite3_mutex_notheld(pShmNode->mutex) );
    sqlite3_mutex_enter(pShmNode->mutexBuf);
    p->hasMutexBuf = 1;
  }
  sqlite3_mutex_enter(pShmNode->mutex);
  if( pShmNode->szMap==0 || reqMapSize>pShmNode->szMap ){
    int actualSize;
    if( unixShmSize(fd, -1, &actualSize)==SQLITE_OK
     && reqMapSize<actualSize
    ){
      reqMapSize = actualSize;
    }

    if( pShmNode->pMMapBuf ){
      munmap(pShmNode->pMMapBuf, pShmNode->szMap);
    }

    pShmNode->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED,
                           pShmNode->h, 0);
    pShmNode->szMap = pShmNode->pMMapBuf ? reqMapSize : 0;




  }
  *pNewMapSize = pShmNode->szMap;
  *ppBuf = pShmNode->pMMapBuf;
  sqlite3_mutex_leave(pShmNode->mutex);
  return rc;
}








|
|
|
|
|
|
<
<
<







 







<
<
<
<
<



|





<
<
<








|
>
|
<
|
>
>







 







|
>
>
>

|







 







|
|
<
<

>
|


>



>
>
>
>







3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610



3611
3612
3613
3614
3615
3616
3617
....
3622
3623
3624
3625
3626
3627
3628





3629
3630
3631
3632
3633
3634
3635
3636
3637



3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648

3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
....
3661
3662
3663
3664
3665
3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
....
3691
3692
3693
3694
3695
3696
3697
3698
3699


3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
  }
  unixLeaveMutex();

  return SQLITE_OK;
}

/*
** Changes the size of the underlying storage for  a shared-memory segment.
**
** The reqSize parameter is the new requested size of the shared memory.
** This implementation is free to increase the shared memory size to
** any amount greater than or equal to reqSize.  If the shared memory is
** already as big or bigger as reqSize, this routine is a no-op.



**
** The reqSize parameter is the minimum size requested.  The implementation
** is free to expand the storage to some larger amount if it chooses.
*/
static int unixShmSize(
  sqlite3_file *fd,         /* The open database file holding SHM */
  int reqSize,              /* Requested size.  -1 for query only */
................................................................................
  unixShmNode *pShmNode = p->pShmNode;
  int rc = SQLITE_OK;
  struct stat sStat;

  assert( pShmNode==pDbFd->pInode->pShmNode );
  assert( pShmNode->pInode==pDbFd->pInode );






  while( 1 ){
    if( fstat(pShmNode->h, &sStat)==0 ){
      *pNewSize = (int)sStat.st_size;
      if( reqSize<=(int)sStat.st_size ) break;
    }else{
      *pNewSize = 0;
      rc = SQLITE_IOERR;
      break;
    }



    rc = ftruncate(pShmNode->h, reqSize);
    reqSize = -1;
  }
  return rc;
}


/*
** Map the shared storage into memory. 
**
** If reqMapSize is positive, then an attempt is made to make the

** mapping at least reqMapSize bytes in size.  However, the mapping
** will never be larger than the size of the underlying shared memory
** as set by prior calls to xShmSize().  
**
** *ppBuf is made to point to the memory which is a mapping of the
** underlying storage.  A mutex is acquired to prevent other threads
** from running while *ppBuf is in use in order to prevent other threads
** remapping *ppBuf out from under this thread.  The unixShmRelease()
** call will release the mutex.  However, if the lock state is CHECKPOINT,
** the mutex is not acquired because CHECKPOINT will never remap the
................................................................................
**
** RECOVER needs to be atomic.  The same mutex that prevents *ppBuf from
** being remapped also prevents more than one thread from being in
** RECOVER at a time.  But, RECOVER sometimes wants to remap itself.
** To prevent RECOVER from losing its lock while remapping, the
** mutex is not released by unixShmRelease() when in RECOVER.
**
** *pNewMapSize is set to the size of the mapping.  Usually *pNewMapSize
** will be reqMapSize or larger, though it could be smaller if the
** underlying shared memory has never been enlarged to reqMapSize bytes
** by prior calls to xShmSize().
**
** *ppBuf might be NULL and zero if no space has
** yet been allocated to the underlying storage.
*/
static int unixShmGet(
  sqlite3_file *fd,        /* Database file holding shared memory */
  int reqMapSize,          /* Requested size of mapping. -1 means don't care */
  int *pNewMapSize,        /* Write new size of mapping here */
  void volatile **ppBuf    /* Write mapping buffer origin here */
................................................................................
    assert( sqlite3_mutex_notheld(pShmNode->mutex) );
    sqlite3_mutex_enter(pShmNode->mutexBuf);
    p->hasMutexBuf = 1;
  }
  sqlite3_mutex_enter(pShmNode->mutex);
  if( pShmNode->szMap==0 || reqMapSize>pShmNode->szMap ){
    int actualSize;
    if( unixShmSize(fd, -1, &actualSize)!=SQLITE_OK ){
      actualSize = 0;


    }
    reqMapSize = actualSize;
    if( pShmNode->pMMapBuf || reqMapSize<=0 ){
      munmap(pShmNode->pMMapBuf, pShmNode->szMap);
    }
    if( reqMapSize>0 ){
      pShmNode->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED,
                             pShmNode->h, 0);
      pShmNode->szMap = pShmNode->pMMapBuf ? reqMapSize : 0;
    }else{
      pShmNode->pMMapBuf = 0;
      pShmNode->szMap = 0;
    }
  }
  *pNewMapSize = pShmNode->szMap;
  *ppBuf = pShmNode->pMMapBuf;
  sqlite3_mutex_leave(pShmNode->mutex);
  return rc;
}

Changes to src/os_win.c.

1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769




1770
1771
1772


1773
1774
1775
1776
1777
1778
1779
  }
  winShmLeaveMutex();

  return SQLITE_OK;
}

/*
** Query and/or changes the size of the underlying storage for
** a shared-memory segment.  The reqSize parameter is the new size
** of the underlying storage, or -1 to do just a query.  The size
** of the underlying storage (after resizing if resizing occurs) is
** written into pNewSize.
**
** This routine does not (necessarily) change the size of the mapping 
** of the underlying storage into memory.  Use xShmGet() to change
** the mapping size.




**
** The reqSize parameter is the minimum size requested.  The implementation
** is free to expand the storage to some larger amount if it chooses.


*/
static int winShmSize(
  sqlite3_file *fd,         /* Database holding the shared memory */
  int reqSize,              /* Requested size.  -1 for query only */
  int *pNewSize             /* Write new size here */
){
  winFile *pDbFd = (winFile*)fd;







|
<
<
<
<

<
<
<
>
>
>
>

<
<
>
>







1754
1755
1756
1757
1758
1759
1760
1761




1762



1763
1764
1765
1766
1767


1768
1769
1770
1771
1772
1773
1774
1775
1776
  }
  winShmLeaveMutex();

  return SQLITE_OK;
}

/*
** Increase the size of the underlying storage for a shared-memory segment.




**



** The reqSize parameter is the new requested minimum size of the underlying
** shared memory.  This routine may choose to make the shared memory larger
** than this value (for example to round the shared memory size up to an
** operating-system dependent page size.)
**


** This routine will only grow the size of shared memory.  A request for
** a smaller size is a no-op.
*/
static int winShmSize(
  sqlite3_file *fd,         /* Database holding the shared memory */
  int reqSize,              /* Requested size.  -1 for query only */
  int *pNewSize             /* Write new size here */
){
  winFile *pDbFd = (winFile*)fd;

Changes to src/wal.c.

240
241
242
243
244
245
246

247
248
249
250
251
252
253
...
558
559
560
561
562
563
564
565
566


567
568
569
570
571
572
573


574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594

595
596
597
598
599
600
601
602
603

604
605
606
607
608

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
...
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
...
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
...
979
980
981
982
983
984
985

986
987
988
989
990
991
992
....
1305
1306
1307
1308
1309
1310
1311
1312
1313

1314
1315
1316
1317
1318
1319

1320
1321
1322
1323
1324
1325
1326
....
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
....
1624
1625
1626
1627
1628
1629
1630
1631

1632
1633
1634
1635
1636
1637
1638
/* A block of WALINDEX_LOCK_RESERVED bytes beginning at
** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
** only support mandatory file-locks, we do not read or write data
** from the region of the file on which locks are applied.
*/
#define WALINDEX_LOCK_OFFSET   (sizeof(WalIndexHdr)*2)
#define WALINDEX_LOCK_RESERVED 8


/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24

/* Size of write ahead log header */
#define WAL_HDRSIZE 24

................................................................................
/*
** Release our reference to the wal-index memory map, if we are holding
** it.
*/
static void walIndexUnmap(Wal *pWal){
  if( pWal->pWiData ){
    sqlite3OsShmRelease(pWal->pDbFd);
    pWal->pWiData = 0;
  }


}

/*
** Map the wal-index file into memory if it isn't already. 
**
** The reqSize parameter is the minimum required size of the mapping.
** A value of -1 means "don't care".


*/
static int walIndexMap(Wal *pWal, int reqSize){
  int rc = SQLITE_OK;
  if( pWal->pWiData==0 || reqSize>pWal->szWIndex ){
    walIndexUnmap(pWal);
    rc = sqlite3OsShmGet(pWal->pDbFd, reqSize, &pWal->szWIndex,
                             (void volatile**)(char volatile*)&pWal->pWiData);
    if( rc==SQLITE_OK && pWal->pWiData==0 ){
      /* Make sure pWal->pWiData is not NULL while we are holding the
      ** lock on the mapping. */
      assert( pWal->szWIndex==0 );
      pWal->pWiData = &pWal->iCallback;
    }
    if( rc!=SQLITE_OK ){
      walIndexUnmap(pWal);
    }
  }
  return rc;
}

/*

** Remap the wal-index so that the mapping covers the full size
** of the underlying file.
**
** If enlargeTo is non-negative, then increase the size of the underlying
** storage to be at least as big as enlargeTo before remapping.
*/
static int walIndexRemap(Wal *pWal, int enlargeTo){
  int rc;
  int sz;

  rc = sqlite3OsShmSize(pWal->pDbFd, enlargeTo, &sz);
  if( rc==SQLITE_OK && sz>pWal->szWIndex ){
    walIndexUnmap(pWal);
    rc = walIndexMap(pWal, sz);
  }

  return rc;
}

/*
** Increment by which to increase the wal-index file size.
*/
#define WALINDEX_MMAP_INCREMENT (64*1024)


/*
** Compute a hash on a page number.  The resulting hash value must land
** between 0 and (HASHTABLE_NSLOT-1).
*/
static int walHash(u32 iPage){
  assert( iPage>0 );
................................................................................
*/
static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
  int rc;                         /* Return code */
  int nMapping;                   /* Required mapping size in bytes */
  
  /* Make sure the wal-index is mapped. Enlarge the mapping if required. */
  nMapping = walMappingSize(iFrame);
  rc = walIndexMap(pWal, -1);
  while( rc==SQLITE_OK && nMapping>pWal->szWIndex ){
    int nByte = pWal->szWIndex + WALINDEX_MMAP_INCREMENT;
    rc = walIndexRemap(pWal, nByte);
  }

  /* Assuming the wal-index file was successfully mapped, find the hash 
  ** table and section of of the page number array that pertain to frame 
  ** iFrame of the WAL. Then populate the page number array and the hash 
  ** table entry.
  */
................................................................................
    }

    sqlite3_free(aFrame);
  }

finished:
  if( rc==SQLITE_OK && pWal->hdr.mxFrame==0 ){
    rc = walIndexRemap(pWal, WALINDEX_MMAP_INCREMENT);
  }
  if( rc==SQLITE_OK ){
    pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
    pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
    walIndexWriteHdr(pWal);
  }
  return rc;
................................................................................
  if( !pRet ){
    return SQLITE_NOMEM;
  }

  pRet->pVfs = pVfs;
  pRet->pWalFd = (sqlite3_file *)&pRet[1];
  pRet->pDbFd = pDbFd;

  sqlite3_randomness(8, &pRet->hdr.aSalt);
  pRet->zWalName = zWal = pVfs->szOsFile + (char*)pRet->pWalFd;
  sqlite3_snprintf(nWal, zWal, "%s-wal", zDbName);
  rc = sqlite3OsShmOpen(pDbFd);

  /* Open file handle on the write-ahead log file. */
  if( rc==SQLITE_OK ){
................................................................................
** is read successfully and the checksum verified, return zero.
*/
int walIndexTryHdr(Wal *pWal, int *pChanged){
  u32 aCksum[2];               /* Checksum on the header content */
  WalIndexHdr h1, h2;          /* Two copies of the header content */
  WalIndexHdr *aHdr;           /* Header in shared memory */

  assert( pWal->pWiData );
  if( pWal->szWIndex==0 ){

    /* The wal-index is of size 0 bytes. This is handled in the same way
    ** as an invalid header. The caller will run recovery to construct
    ** a valid wal-index file before accessing the database.
    */
    return 1;
  }


  /* Read the header. The caller may or may not have an exclusive 
  ** (WRITE, PENDING, CHECKPOINT or RECOVER) lock on the wal-index
  ** file, meaning it is possible that an inconsistent snapshot is read
  ** from the file. If this happens, return non-zero.
  **
  ** There are two copies of the header at the beginning of the wal-index.
................................................................................
*/
static int walIndexReadHdr(Wal *pWal, int *pChanged){
  int rc;                         /* Return code */
  int lockState;                  /* pWal->lockState before running recovery */

  assert( pWal->lockState>=SQLITE_SHM_READ );
  assert( pChanged );
  rc = walIndexMap(pWal, -1);
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* First attempt to read the wal-index header. This may fail for one
  ** of two reasons: (a) the wal-index does not yet exist or has been
  ** corrupted and needs to be constructed by running recovery, or (b)
................................................................................
    rc = walSetLock(pWal, SQLITE_SHM_WRITE);

    /* If this connection is not reading the most recent database snapshot,
    ** it is not possible to write to the database. In this case release
    ** the write locks and return SQLITE_BUSY.
    */
    if( rc==SQLITE_OK ){
      rc = walIndexMap(pWal, sizeof(WalIndexHdr));

      if( rc==SQLITE_OK
       && memcmp(&pWal->hdr, (void*)pWal->pWiData, sizeof(WalIndexHdr))
      ){
        rc = SQLITE_BUSY;
      }
      walIndexUnmap(pWal);
      if( rc!=SQLITE_OK ){







>







 







<

>
>





|
|
>
>







<
<
<
<
<
<








>









>





>


<
<
<
<
<
<







 







|

<
|







 







|







 







>







 







<
|
>
|
<
<
<


>







 







|







 







|
>







240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
...
559
560
561
562
563
564
565

566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584






585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611






612
613
614
615
616
617
618
...
729
730
731
732
733
734
735
736
737

738
739
740
741
742
743
744
745
...
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
...
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
....
1300
1301
1302
1303
1304
1305
1306

1307
1308
1309



1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
....
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
....
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
/* A block of WALINDEX_LOCK_RESERVED bytes beginning at
** WALINDEX_LOCK_OFFSET is reserved for locks. Since some systems
** only support mandatory file-locks, we do not read or write data
** from the region of the file on which locks are applied.
*/
#define WALINDEX_LOCK_OFFSET   (sizeof(WalIndexHdr)*2)
#define WALINDEX_LOCK_RESERVED 8
#define WALINDEX_HDR_SIZE      (WALINDEX_LOCK_OFFSET+WALINDEX_LOCK_RESERVED)

/* Size of header before each frame in wal */
#define WAL_FRAME_HDRSIZE 24

/* Size of write ahead log header */
#define WAL_HDRSIZE 24

................................................................................
/*
** Release our reference to the wal-index memory map, if we are holding
** it.
*/
static void walIndexUnmap(Wal *pWal){
  if( pWal->pWiData ){
    sqlite3OsShmRelease(pWal->pDbFd);

  }
  pWal->pWiData = 0;
  pWal->szWIndex = -1;
}

/*
** Map the wal-index file into memory if it isn't already. 
**
** The reqSize parameter is the requested size of the mapping.  The
** mapping will be at least this big if the underlying storage is
** that big.  But the mapping will never grow larger than the underlying
** storage.  Use the walIndexRemap() to enlarget the storage space.
*/
static int walIndexMap(Wal *pWal, int reqSize){
  int rc = SQLITE_OK;
  if( pWal->pWiData==0 || reqSize>pWal->szWIndex ){
    walIndexUnmap(pWal);
    rc = sqlite3OsShmGet(pWal->pDbFd, reqSize, &pWal->szWIndex,
                             (void volatile**)(char volatile*)&pWal->pWiData);






    if( rc!=SQLITE_OK ){
      walIndexUnmap(pWal);
    }
  }
  return rc;
}

/*
** Enlarge the wal-index to be at least enlargeTo bytes in size and
** Remap the wal-index so that the mapping covers the full size
** of the underlying file.
**
** If enlargeTo is non-negative, then increase the size of the underlying
** storage to be at least as big as enlargeTo before remapping.
*/
static int walIndexRemap(Wal *pWal, int enlargeTo){
  int rc;
  int sz;
  assert( pWal->lockState>=SQLITE_SHM_WRITE );
  rc = sqlite3OsShmSize(pWal->pDbFd, enlargeTo, &sz);
  if( rc==SQLITE_OK && sz>pWal->szWIndex ){
    walIndexUnmap(pWal);
    rc = walIndexMap(pWal, sz);
  }
  assert( pWal->szWIndex>=enlargeTo || rc!=SQLITE_OK );
  return rc;
}







/*
** Compute a hash on a page number.  The resulting hash value must land
** between 0 and (HASHTABLE_NSLOT-1).
*/
static int walHash(u32 iPage){
  assert( iPage>0 );
................................................................................
*/
static int walIndexAppend(Wal *pWal, u32 iFrame, u32 iPage){
  int rc;                         /* Return code */
  int nMapping;                   /* Required mapping size in bytes */
  
  /* Make sure the wal-index is mapped. Enlarge the mapping if required. */
  nMapping = walMappingSize(iFrame);
  rc = walIndexMap(pWal, nMapping);
  while( rc==SQLITE_OK && nMapping>pWal->szWIndex ){

    rc = walIndexRemap(pWal, nMapping);
  }

  /* Assuming the wal-index file was successfully mapped, find the hash 
  ** table and section of of the page number array that pertain to frame 
  ** iFrame of the WAL. Then populate the page number array and the hash 
  ** table entry.
  */
................................................................................
    }

    sqlite3_free(aFrame);
  }

finished:
  if( rc==SQLITE_OK && pWal->hdr.mxFrame==0 ){
    rc = walIndexRemap(pWal, walMappingSize(1));
  }
  if( rc==SQLITE_OK ){
    pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
    pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
    walIndexWriteHdr(pWal);
  }
  return rc;
................................................................................
  if( !pRet ){
    return SQLITE_NOMEM;
  }

  pRet->pVfs = pVfs;
  pRet->pWalFd = (sqlite3_file *)&pRet[1];
  pRet->pDbFd = pDbFd;
  pRet->szWIndex = -1;
  sqlite3_randomness(8, &pRet->hdr.aSalt);
  pRet->zWalName = zWal = pVfs->szOsFile + (char*)pRet->pWalFd;
  sqlite3_snprintf(nWal, zWal, "%s-wal", zDbName);
  rc = sqlite3OsShmOpen(pDbFd);

  /* Open file handle on the write-ahead log file. */
  if( rc==SQLITE_OK ){
................................................................................
** is read successfully and the checksum verified, return zero.
*/
int walIndexTryHdr(Wal *pWal, int *pChanged){
  u32 aCksum[2];               /* Checksum on the header content */
  WalIndexHdr h1, h2;          /* Two copies of the header content */
  WalIndexHdr *aHdr;           /* Header in shared memory */


  if( pWal->szWIndex < WALINDEX_HDR_SIZE ){
    /* The wal-index is not large enough to hold the header, then assume
    ** header is invalid. */



    return 1;
  }
  assert( pWal->pWiData );

  /* Read the header. The caller may or may not have an exclusive 
  ** (WRITE, PENDING, CHECKPOINT or RECOVER) lock on the wal-index
  ** file, meaning it is possible that an inconsistent snapshot is read
  ** from the file. If this happens, return non-zero.
  **
  ** There are two copies of the header at the beginning of the wal-index.
................................................................................
*/
static int walIndexReadHdr(Wal *pWal, int *pChanged){
  int rc;                         /* Return code */
  int lockState;                  /* pWal->lockState before running recovery */

  assert( pWal->lockState>=SQLITE_SHM_READ );
  assert( pChanged );
  rc = walIndexMap(pWal, walMappingSize(1));
  if( rc!=SQLITE_OK ){
    return rc;
  }

  /* First attempt to read the wal-index header. This may fail for one
  ** of two reasons: (a) the wal-index does not yet exist or has been
  ** corrupted and needs to be constructed by running recovery, or (b)
................................................................................
    rc = walSetLock(pWal, SQLITE_SHM_WRITE);

    /* If this connection is not reading the most recent database snapshot,
    ** it is not possible to write to the database. In this case release
    ** the write locks and return SQLITE_BUSY.
    */
    if( rc==SQLITE_OK ){
      rc = walIndexMap(pWal, walMappingSize(1));
      assert( pWal->szWIndex>=WALINDEX_HDR_SIZE || rc!=SQLITE_OK );
      if( rc==SQLITE_OK
       && memcmp(&pWal->hdr, (void*)pWal->pWiData, sizeof(WalIndexHdr))
      ){
        rc = SQLITE_BUSY;
      }
      walIndexUnmap(pWal);
      if( rc!=SQLITE_OK ){