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

Overview
Comment:Fix a robustness related problem with wrapped logs.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 7dd7b942fd42218c643d76afbcf27d39daf89d89
User & Date: dan 2013-11-08 20:18:11.928
Context
2013-11-09
17:32
Add BT_CONTROL directives to configure the safety-level and auto-checkpoint parameter. Fix bugs. check-in: 15856cf080 user: dan tags: trunk
2013-11-08
20:18
Fix a robustness related problem with wrapped logs. check-in: 7dd7b942fd user: dan tags: trunk
17:50
Add missing calls to xSync(). Fix a problem with recovering wrapped logs. check-in: 93af0d7d05 user: dan tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btInt.h.
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
int sqlite4BtLogSnapshotOpen(BtLog*);
int sqlite4BtLogSnapshotClose(BtLog*);

int sqlite4BtLogSnapshotWrite(BtLog*);
int sqlite4BtLogSnapshotEndWrite(BtLog*);

int sqlite4BtLogSize(BtLog*);
int sqlite4BtLogCheckpoint(BtLog*);

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

int sqlite4BtLogPagesize(BtLog*);
int sqlite4BtLogPagecount(BtLog*);
u32 sqlite4BtLogCookie(BtLog*);
int sqlite4BtLogSetCookie(BtLog*, u32 iCookie);







|







186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
int sqlite4BtLogSnapshotOpen(BtLog*);
int sqlite4BtLogSnapshotClose(BtLog*);

int sqlite4BtLogSnapshotWrite(BtLog*);
int sqlite4BtLogSnapshotEndWrite(BtLog*);

int sqlite4BtLogSize(BtLog*);
int sqlite4BtLogCheckpoint(BtLog*, int);

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

int sqlite4BtLogPagesize(BtLog*);
int sqlite4BtLogPagecount(BtLog*);
u32 sqlite4BtLogCookie(BtLog*);
int sqlite4BtLogSetCookie(BtLog*, u32 iCookie);
Changes to src/bt_log.c.
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  return rc;
}

/*
** Remove everything following frame iFrame from the iHash'th hash table.
*/
static int btLogHashRollback(BtLog *pLog, int iHash, u32 iFrame){
  const int nPgno = (iHash==0 ? HASHTABLE_NFRAME_ONE : HASHTABLE_NFRAME);
  ht_slot *aHash;                 /* Hash slots */
  u32 *aPgno;                     /* Page array for updated hash table */
  u32 iZero;                      /* Zero-offset of updated hash table */
  int iSide = pLog->snapshot.iHashSide;
  int rc;

  rc = btLogFindHash(pLog, iSide, iHash, &aHash, &aPgno, &iZero);







<







665
666
667
668
669
670
671

672
673
674
675
676
677
678
  return rc;
}

/*
** Remove everything following frame iFrame from the iHash'th hash table.
*/
static int btLogHashRollback(BtLog *pLog, int iHash, u32 iFrame){

  ht_slot *aHash;                 /* Hash slots */
  u32 *aPgno;                     /* Page array for updated hash table */
  u32 iZero;                      /* Zero-offset of updated hash table */
  int iSide = pLog->snapshot.iHashSide;
  int rc;

  rc = btLogFindHash(pLog, iSide, iHash, &aHash, &aPgno, &iZero);
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
  rc = btLogFindHash(pLog, iSide, iHash, &aHash, &aPgno, &iZero);
  if( rc==SQLITE4_OK ){
    memset(aHash, 0, sizeof(ht_slot)*HASHTABLE_NSLOT);
  }
  return rc;
}

static int btLogWriteFrame(BtLog *pLog, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  u32 *aLog = pLog->snapshot.aLog;
  int rc;                         /* Return code */
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  u32 iNextFrame;                 /* Frame to write following this one */
  i64 iOff;                       /* Offset of log file to write to */
  BtFrameHdr frame;               /* Header for new frame */

  /* Figure out the offset to write the current frame to. */
  iFrame = pLog->snapshot.iNextFrame;







|


|







1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
  rc = btLogFindHash(pLog, iSide, iHash, &aHash, &aPgno, &iZero);
  if( rc==SQLITE4_OK ){
    memset(aHash, 0, sizeof(ht_slot)*HASHTABLE_NSLOT);
  }
  return rc;
}

static int btLogWriteFrame(BtLog *pLog, int nPad, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  u32 *aLog = pLog->snapshot.aLog;
  int rc = SQLITE4_OK;            /* Return code */
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  u32 iNextFrame;                 /* Frame to write following this one */
  i64 iOff;                       /* Offset of log file to write to */
  BtFrameHdr frame;               /* Header for new frame */

  /* Figure out the offset to write the current frame to. */
  iFrame = pLog->snapshot.iNextFrame;
1143
1144
1145
1146
1147
1148
1149

1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
  iNextFrame = pLog->snapshot.iNextFrame + 1;
  if( iFrame!=1 && iFrame==aLog[5]+1
   && aLog[0]==0 && aLog[2]==0 
   && aLog[4]!=0 && aLog[4]>pLog->nWrapLog 
  ){
    /* Case 2) It is possible to wrap the log around */
    iNextFrame = 1;

  }else if( iNextFrame==aLog[0] ){
    /* Case 3) It is necessary to jump over some existing log. */
    iNextFrame = aLog[1]+1;
    assert( iNextFrame!=1 );

    if( btLogFrameHash(pLog, iNextFrame)!=btLogFrameHash(pLog, iFrame) ){
      rc = btLogZeroHash(pLog, btLogFrameHash(pLog, iNextFrame));
    }
  }








>
|

|







1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
  iNextFrame = pLog->snapshot.iNextFrame + 1;
  if( iFrame!=1 && iFrame==aLog[5]+1
   && aLog[0]==0 && aLog[2]==0 
   && aLog[4]!=0 && aLog[4]>pLog->nWrapLog 
  ){
    /* Case 2) It is possible to wrap the log around */
    iNextFrame = 1;
  }else if( (iNextFrame+nPad)>=aLog[0] && iNextFrame<aLog[1] ){

    /* Case 3) It is necessary to jump over some existing log. */
    iNextFrame = aLog[1]+nPad+1;
    assert( iNextFrame!=1 );

    if( btLogFrameHash(pLog, iNextFrame)!=btLogFrameHash(pLog, iFrame) ){
      rc = btLogZeroHash(pLog, btLogFrameHash(pLog, iNextFrame));
    }
  }

1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289

    pLog->snapshot.aFrameCksum[0] = hdr.iSalt1;
    pLog->snapshot.aFrameCksum[1] = hdr.iSalt2;
    pLog->snapshot.iNextFrame = 1;
  }
  btDebugCheckSnapshot(&pLog->snapshot);

  rc = btLogWriteFrame(pLog, pgno, aData, nPg);

  /* If this is a COMMIT, sync the log and update the shared shm-header. */
  if( nPg ){
    int i;
    for(i=0; i<nPad && rc==SQLITE4_OK; i++){
      rc = btLogWriteFrame(pLog, pgno, aData, nPg);
    }
    if( rc==SQLITE4_OK ) rc = btLogSyncFile(pLog, pLog->pFd);
    if( rc==SQLITE4_OK ) rc = btLogUpdateSharedHdr(pLog);
  }

  return rc;
}







|





|







1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289

    pLog->snapshot.aFrameCksum[0] = hdr.iSalt1;
    pLog->snapshot.aFrameCksum[1] = hdr.iSalt2;
    pLog->snapshot.iNextFrame = 1;
  }
  btDebugCheckSnapshot(&pLog->snapshot);

  rc = btLogWriteFrame(pLog, nPad, pgno, aData, nPg);

  /* If this is a COMMIT, sync the log and update the shared shm-header. */
  if( nPg ){
    int i;
    for(i=0; i<nPad && rc==SQLITE4_OK; i++){
      rc = btLogWriteFrame(pLog, nPad, pgno, aData, nPg);
    }
    if( rc==SQLITE4_OK ) rc = btLogSyncFile(pLog, pLog->pFd);
    if( rc==SQLITE4_OK ) rc = btLogUpdateSharedHdr(pLog);
  }

  return rc;
}
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565

1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584

1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597

1598
1599


1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627





1628
1629
1630
1631
1632
1633
1634
    u32 iFirst = aLog[i*2];
    u32 iLast = aLog[i*2+1];
    if( iFirst ){
      if( iFrame>=iFirst && iFrame<=iLast ){
        iRet += (iFrame - iFirst);
        return iRet;
      }else{
        iRet += (iLast - iFirst);
      }
    }
  }
  if( i==3 ) return -1;
  return iRet;
}

/*
** Parameters iFirst and iLast are frame numbers for frames that are part 
** of the current log. This function scans the wal-index from iFirst to
** iLast (inclusive) and records the set of page numbers that occur once.
** This set is sorted in ascending order and returned via the output 
** variables *paPgno and *pnPgno.
*/
static int btLogGatherPgno(
  BtLog *pLog,                    /* Log module handle */

  u32 **paPgno,                   /* OUT: s4_malloc'd array of sorted pgno */
  int *pnPgno,                    /* OUT: Number of entries in *paPgno */
  u32 *piLastFrame                /* OUT: Last frame checkpointed */
){
  BtShm *pShm = btLogShm(pLog);
  BtLock *pLock = pLog->pLock;
  u32 *aLog = pLog->snapshot.aLog;/* Log file topology */
  u32 i;
  u32 *aPgno;                     /* Returned array */
  int nPgno;                      /* Elements in aPgno[] */
  u32 *aSpace;                    /* Temporary space used by merge-sort */
  int nMax;
  int rc = SQLITE4_OK;
  int iRegion;
  int bLocked;
  u32 iSafe;                      /* Last frame in log it is safe to gather */

  int iSafeIdx = -1;
  int iFirstIdx = -1;

  int iIdx = 0;

  *paPgno = 0;
  *pnPgno = 0;
  *piLastFrame = 0;

  rc = sqlite4BtLockReaderQuery(pLock, aLog, pShm->aReadlock, &iSafe, &bLocked);
  if( rc!=SQLITE4_OK || bLocked ) return rc;
  btDebugLogSafepoint(pLock, iSafe);
  btDebugTopology(
      pLock, "checkpointer", pLog->snapshot.iHashSide, pLog->snapshot.aLog
  );


  iSafeIdx = sqlite4BtLogFrameToIdx(aLog, iSafe);
  iFirstIdx = sqlite4BtLogFrameToIdx(aLog, pShm->ckpt.iFirstRecover);



  /* Determine an upper limit on the number of distinct page numbers. This
  ** limit is used to allocate space for the returned array.  */
  nMax = 0;
  for(iRegion=0; iRegion<3; iRegion++){
    if( aLog[iRegion*2] ){
      nMax += 1 + aLog[iRegion*2+1] - aLog[iRegion*2+0];
    }
  }

  /* Allocate space to collect all page numbers. */
  aPgno = (u32*)sqlite4_malloc(pLog->pLock->pEnv, sizeof(u32)*nMax*2);
  if( aPgno==0 ) rc = btErrorBkpt(SQLITE4_NOMEM);
  aSpace = &aPgno[nMax];
  nPgno = 0;

  /* Copy the required page numbers into the allocated array */
  for(iRegion=0; iRegion<3; iRegion++){
    u32 iFirst = aLog[iRegion*2];
    u32 iLast = aLog[iRegion*2+1];
    if( iFirst ){

      for(i=iFirst; rc==SQLITE4_OK && i<=iLast; i++, iIdx++){
        int iHash = btLogFrameHash(pLog, i);
        u32 *aPage;
        ht_slot *aHash;
        u32 iZero;






        if( (iFirstIdx>=0 && iIdx<iFirstIdx) 
         || (iSafeIdx>=0 && iIdx>iSafeIdx) 
        ){
          continue;
        }
        *piLastFrame = i;








|
















>



















>













>

|
>
>



|
<
<
<
<
<



















>
>
>
>
>







1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608





1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
    u32 iFirst = aLog[i*2];
    u32 iLast = aLog[i*2+1];
    if( iFirst ){
      if( iFrame>=iFirst && iFrame<=iLast ){
        iRet += (iFrame - iFirst);
        return iRet;
      }else{
        iRet += (iLast - iFirst) + 1;
      }
    }
  }
  if( i==3 ) return -1;
  return iRet;
}

/*
** Parameters iFirst and iLast are frame numbers for frames that are part 
** of the current log. This function scans the wal-index from iFirst to
** iLast (inclusive) and records the set of page numbers that occur once.
** This set is sorted in ascending order and returned via the output 
** variables *paPgno and *pnPgno.
*/
static int btLogGatherPgno(
  BtLog *pLog,                    /* Log module handle */
  int nFrameBuffer,
  u32 **paPgno,                   /* OUT: s4_malloc'd array of sorted pgno */
  int *pnPgno,                    /* OUT: Number of entries in *paPgno */
  u32 *piLastFrame                /* OUT: Last frame checkpointed */
){
  BtShm *pShm = btLogShm(pLog);
  BtLock *pLock = pLog->pLock;
  u32 *aLog = pLog->snapshot.aLog;/* Log file topology */
  u32 i;
  u32 *aPgno;                     /* Returned array */
  int nPgno;                      /* Elements in aPgno[] */
  u32 *aSpace;                    /* Temporary space used by merge-sort */
  int nMax;
  int rc = SQLITE4_OK;
  int iRegion;
  int bLocked;
  u32 iSafe;                      /* Last frame in log it is safe to gather */

  int iSafeIdx = -1;
  int iFirstIdx = -1;
  int iBufIdx;
  int iIdx = 0;

  *paPgno = 0;
  *pnPgno = 0;
  *piLastFrame = 0;

  rc = sqlite4BtLockReaderQuery(pLock, aLog, pShm->aReadlock, &iSafe, &bLocked);
  if( rc!=SQLITE4_OK || bLocked ) return rc;
  btDebugLogSafepoint(pLock, iSafe);
  btDebugTopology(
      pLock, "checkpointer", pLog->snapshot.iHashSide, pLog->snapshot.aLog
  );

  iFirstIdx = sqlite4BtLogFrameToIdx(aLog, pShm->ckpt.iFirstRecover);
  iSafeIdx = sqlite4BtLogFrameToIdx(aLog, iSafe);
  iBufIdx = sqlite4BtLogFrameToIdx(aLog, pLog->snapshot.aLog[5]) - nFrameBuffer;
  if( iSafeIdx<0 || iBufIdx<iSafeIdx ) iSafeIdx = iBufIdx;
  if( iSafeIdx<0 || (iFirstIdx>=0 && iSafeIdx<iFirstIdx) ) return rc;

  /* Determine an upper limit on the number of distinct page numbers. This
  ** limit is used to allocate space for the returned array.  */
  nMax = iSafeIdx - iFirstIdx +1;






  /* Allocate space to collect all page numbers. */
  aPgno = (u32*)sqlite4_malloc(pLog->pLock->pEnv, sizeof(u32)*nMax*2);
  if( aPgno==0 ) rc = btErrorBkpt(SQLITE4_NOMEM);
  aSpace = &aPgno[nMax];
  nPgno = 0;

  /* Copy the required page numbers into the allocated array */
  for(iRegion=0; iRegion<3; iRegion++){
    u32 iFirst = aLog[iRegion*2];
    u32 iLast = aLog[iRegion*2+1];
    if( iFirst ){

      for(i=iFirst; rc==SQLITE4_OK && i<=iLast; i++, iIdx++){
        int iHash = btLogFrameHash(pLog, i);
        u32 *aPage;
        ht_slot *aHash;
        u32 iZero;

        /* Ensure that the checkpoint does not read any frames from the 
        ** log that occur earlier than iFirstRecover. This is not just 
        ** an optimization - there is a chance that such frames may be 
        ** overwritten by a writer running concurrently with this 
        ** checkpoint.  */
        if( (iFirstIdx>=0 && iIdx<iFirstIdx) 
         || (iSafeIdx>=0 && iIdx>iSafeIdx) 
        ){
          continue;
        }
        *piLastFrame = i;

1656
1657
1658
1659
1660
1661
1662
1663

1664

1665
1666





1667


1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
    *pnPgno = 0;
  }

  return rc;
}

/*
** Return the number of frames in the log file according to the current

** snapshot.

*/
int sqlite4BtLogSize(BtLog *pLog){





  return (int)pLog->snapshot.aLog[5] - (int)pLog->snapshot.aLog[4];


}

static int btLogUpdateDbhdr(BtLog *pLog, u8 *aData){
  BtDbhdr dbhdr;

  dbhdr.cookie = pLog->snapshot.iCookie;
  dbhdr.nPg = pLog->snapshot.nPg;
  dbhdr.pgsz = pLog->snapshot.pgsz;

  if( BTLOG_LITTLE_ENDIAN ){
    dbhdr.cookie = BYTESWAP32(dbhdr.cookie);
    dbhdr.nPg = BYTESWAP32(dbhdr.nPg);
    dbhdr.pgsz = BYTESWAP32(dbhdr.pgsz);
    assert( dbhdr.pgsz>0 );
  }
  memcpy(aData, &dbhdr, sizeof(BtDbhdr));

  return SQLITE4_OK;
}

int sqlite4BtLogCheckpoint(BtLog *pLog){
  BtLock *pLock = pLog->pLock;
  int rc;

  /* Take the CHECKPOINTER lock. */
  rc = sqlite4BtLockCkpt(pLock);
  if( rc==SQLITE4_OK ){
    const int pgsz = sqlite4BtPagerPagesize((BtPager*)pLock);







|
>
|
>


>
>
>
>
>
|
>
>




















|







1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
    *pnPgno = 0;
  }

  return rc;
}

/*
** Return the number of frames in the log file that have not yet been
** copied into the database file, according to the current snapshot.
**
** todo: adjust result for iFirstRead/iFirstRecover.
*/
int sqlite4BtLogSize(BtLog *pLog){
  return 
      (int)pLog->snapshot.aLog[1] - (int)pLog->snapshot.aLog[0]
    + (pLog->snapshot.aLog[0]!=0)
    + (int)pLog->snapshot.aLog[3] - (int)pLog->snapshot.aLog[2]
    + (pLog->snapshot.aLog[3]!=0)
    + (int)pLog->snapshot.aLog[5] - (int)pLog->snapshot.aLog[4]
    + (pLog->snapshot.aLog[5]!=0)
  ;
}

static int btLogUpdateDbhdr(BtLog *pLog, u8 *aData){
  BtDbhdr dbhdr;

  dbhdr.cookie = pLog->snapshot.iCookie;
  dbhdr.nPg = pLog->snapshot.nPg;
  dbhdr.pgsz = pLog->snapshot.pgsz;

  if( BTLOG_LITTLE_ENDIAN ){
    dbhdr.cookie = BYTESWAP32(dbhdr.cookie);
    dbhdr.nPg = BYTESWAP32(dbhdr.nPg);
    dbhdr.pgsz = BYTESWAP32(dbhdr.pgsz);
    assert( dbhdr.pgsz>0 );
  }
  memcpy(aData, &dbhdr, sizeof(BtDbhdr));

  return SQLITE4_OK;
}

int sqlite4BtLogCheckpoint(BtLog *pLog, int nFrameBuffer){
  BtLock *pLock = pLog->pLock;
  int rc;

  /* Take the CHECKPOINTER lock. */
  rc = sqlite4BtLockCkpt(pLock);
  if( rc==SQLITE4_OK ){
    const int pgsz = sqlite4BtPagerPagesize((BtPager*)pLock);
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
    int i;                        /* Used to loop through aPgno[] */
    u8 *aBuf;                     /* Buffer to load page data into */
    u32 iFirstRead;               /* First frame not checkpointed */

    rc = btLogSnapshot(pLog, &pLog->snapshot);

    if( rc==SQLITE4_OK ){
      /* Ensure that the checkpoint does not read any frames from the log
      ** that occur earlier than iFirstRecover. This is not just an 
      ** optimization - there is a chance that such frames may be 
      ** overwritten by a writer running concurrently with this checkpoint. */
#if 0
      pShm = btLogShm(pLog);
      btLogSnapshotTrim(pLog->snapshot.aLog, pShm->ckpt.iFirstRecover);
#endif

      /* Allocate space to load log data into */
      aBuf = sqlite4_malloc(pLock->pEnv, pgsz);
      if( aBuf==0 ) rc = btErrorBkpt(SQLITE4_NOMEM);
    }
    
    /* Figure out the set of page numbers stored in the part of the log 
    ** file being checkpointed. Remove any duplicates and sort them in 
    ** ascending order.  */
    if( rc==SQLITE4_OK ){
      rc = btLogGatherPgno(pLog, &aPgno, &nPgno, &iLast);
    }

    if( rc==SQLITE4_OK && nPgno>0 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);
      rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
      iFirstRead = fhdr.iNext;








<
<
<
<
<
<
<
<
<









|







1717
1718
1719
1720
1721
1722
1723









1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
    int i;                        /* Used to loop through aPgno[] */
    u8 *aBuf;                     /* Buffer to load page data into */
    u32 iFirstRead;               /* First frame not checkpointed */

    rc = btLogSnapshot(pLog, &pLog->snapshot);

    if( rc==SQLITE4_OK ){









      /* Allocate space to load log data into */
      aBuf = sqlite4_malloc(pLock->pEnv, pgsz);
      if( aBuf==0 ) rc = btErrorBkpt(SQLITE4_NOMEM);
    }
    
    /* Figure out the set of page numbers stored in the part of the log 
    ** file being checkpointed. Remove any duplicates and sort them in 
    ** ascending order.  */
    if( rc==SQLITE4_OK ){
      rc = btLogGatherPgno(pLog, nFrameBuffer, &aPgno, &nPgno, &iLast);
    }

    if( rc==SQLITE4_OK && nPgno>0 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);
      rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
      iFirstRead = fhdr.iNext;

Changes to src/bt_pager.c.
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    }
  }
  btHashClear(p);
}

static int btCheckpoint(BtLock *pLock){
  BtPager *p = (BtPager*)pLock;
  return sqlite4BtLogCheckpoint(p->pLog);
}

static int btCleanup(BtLock *pLock){
  BtPager *p = (BtPager*)pLock;
  int rc = sqlite4BtLogClose(p->pLog, 1);
  p->pLog = 0;
  return rc;







|







248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
    }
  }
  btHashClear(p);
}

static int btCheckpoint(BtLock *pLock){
  BtPager *p = (BtPager*)pLock;
  return sqlite4BtLogCheckpoint(p->pLog, 0);
}

static int btCleanup(BtLock *pLock){
  BtPager *p = (BtPager*)pLock;
  int rc = sqlite4BtLogClose(p->pLog, 1);
  p->pLog = 0;
  return rc;
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  rc = sqlite4BtLogSnapshotClose(p->pLog);

  /* Purge the page cache. */
  assert( p->pDirty==0 );
  btPurgeCache(p);

  if( rc==SQLITE4_OK && p->bDoAutoCkpt ){
    sqlite4BtLogCheckpoint(p->pLog);
  }
  p->bDoAutoCkpt = 0;

  return rc;
}

/*







|







478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
  rc = sqlite4BtLogSnapshotClose(p->pLog);

  /* Purge the page cache. */
  assert( p->pDirty==0 );
  btPurgeCache(p);

  if( rc==SQLITE4_OK && p->bDoAutoCkpt ){
    sqlite4BtLogCheckpoint(p->pLog, (p->nAutoCkpt / 2));
  }
  p->bDoAutoCkpt = 0;

  return rc;
}

/*