SQLite4
Check-in [37983095fd]
Not logged in

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

Overview
Comment:Use the log to store the page-size, database size and user cookie value instead of writing these directly to the database header.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 37983095fdb2beb208dd4593c22f2c2e0017c615
User & Date: dan 2013-11-01 19:54:20
Context
2013-11-02
10:31
Modify the way read-locks are taken to avoid unnecessary SQLITE4_BUSY errors. check-in: 1d06636492 user: dan tags: trunk
2013-11-01
19:54
Use the log to store the page-size, database size and user cookie value instead of writing these directly to the database header. check-in: 37983095fd user: dan tags: trunk
2013-10-31
16:31
Fix some problems causing multi-threaded btree tests to fail. Some still remain. check-in: 67b28147ea user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btInt.h.

32
33
34
35
36
37
38












39
40
41
42
43
44
45
...
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
...
219
220
221
222
223
224
225

226
227
228
229
230
231

232
233
234
235
236
237
238
...
258
259
260
261
262
263
264




265




266

267
268

269
270
271
#ifndef MIN
# define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif














/*************************************************************************
** Interface to bt_pager.c functionality.
*/
typedef struct BtPage BtPage;
typedef struct BtPager BtPager;

................................................................................
** Interface to bt_log.c functionality.
*/
typedef struct BtLog BtLog;
int sqlite4BtLogOpen(BtPager*, int bRecover, BtLog**);
int sqlite4BtLogClose(BtLog*, int bCleanup);

int sqlite4BtLogRead(BtLog*, u32 pgno, u8 *aData);
int sqlite4BtLogWrite(BtLog*, u32 pgno, u8 *aData, int bCommit);

int sqlite4BtLogSnapshotOpen(BtLog*);
int sqlite4BtLogSnapshotClose(BtLog*);

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

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

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

#ifndef NDEBUG
void sqlite4BtDebugReadPage(u32 pgno, u8 *aData, int pgsz);
#else
# define sqlite4BtDebugReadPage(a,b,c)
#endif

/*
** End of bt_log.c interface.
*************************************************************************/

/*************************************************************************
** Interface to bt_lock.c functionality.
................................................................................
typedef struct BtReadSlot BtReadSlot;
struct BtLock {
  /* These three are set by the bt_pager module and thereafter used by 
  ** the bt_lock, bt_pager and bt_log modules. */
  sqlite4_env *pEnv;              /* SQLite environment */
  bt_env *pVfs;                   /* Bt environment */
  bt_file *pFd;                   /* Database file descriptor */


  /* These are used only by the bt_lock module. */
  BtShared *pShared;              /* Shared by all handles on this file */
  BtLock *pNext;                  /* Next connection using pShared */
  u32 mExclLock;                  /* Mask of exclusive locks held */
  u32 mSharedLock;                /* Mask of shared locks held */

};

struct BtReadSlot {
  u32 iFirst;
  u32 iLast;
};

................................................................................
/* Obtain pointers to shared-memory chunks */
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);

/*
** End of bt_lock.c interface.
*************************************************************************/










#ifdef NDEBUG

# define btErrorBkpt(x) x
#else

int btErrorBkpt(int rc);
#endif








>
>
>
>
>
>
>
>
>
>
>
>







 







|












|
|
|
|
<







 







>






>







 







>
>
>
>

>
>
>
>

>


>



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219

220
221
222
223
224
225
226
...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
#ifndef MIN
# define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif

/* By default pages are 1024 bytes in size. */
#define BT_DEFAULT_PGSZ 1024

/*
** Database header fields.
*/
typedef struct BtDbhdr BtDbhdr;
struct BtDbhdr {
  u32 pgsz;                       /* Page size in bytes */
  u32 nPg;                        /* Number of pages in database */
  u32 cookie;                     /* User cookie value (SQL schema cookie) */
};

/*************************************************************************
** Interface to bt_pager.c functionality.
*/
typedef struct BtPage BtPage;
typedef struct BtPager BtPager;

................................................................................
** Interface to bt_log.c functionality.
*/
typedef struct BtLog BtLog;
int sqlite4BtLogOpen(BtPager*, int bRecover, BtLog**);
int sqlite4BtLogClose(BtLog*, int bCleanup);

int sqlite4BtLogRead(BtLog*, u32 pgno, u8 *aData);
int sqlite4BtLogWrite(BtLog*, u32 pgno, u8 *aData, u32 nPg);

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);


/*
** End of bt_log.c interface.
*************************************************************************/

/*************************************************************************
** Interface to bt_lock.c functionality.
................................................................................
typedef struct BtReadSlot BtReadSlot;
struct BtLock {
  /* These three are set by the bt_pager module and thereafter used by 
  ** the bt_lock, bt_pager and bt_log modules. */
  sqlite4_env *pEnv;              /* SQLite environment */
  bt_env *pVfs;                   /* Bt environment */
  bt_file *pFd;                   /* Database file descriptor */
  int iDebugId;                   /* Sometimes useful when debugging */

  /* These are used only by the bt_lock module. */
  BtShared *pShared;              /* Shared by all handles on this file */
  BtLock *pNext;                  /* Next connection using pShared */
  u32 mExclLock;                  /* Mask of exclusive locks held */
  u32 mSharedLock;                /* Mask of shared locks held */

};

struct BtReadSlot {
  u32 iFirst;
  u32 iLast;
};

................................................................................
/* Obtain pointers to shared-memory chunks */
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);

/*
** End of bt_lock.c interface.
*************************************************************************/

/*************************************************************************
** Utility functions
*/
void sqlite4BtPutU32(u8 *a, u32 i);

/*
** End of utility interface.
*************************************************************************/

#ifdef NDEBUG
# define sqlite4BtDebugReadPage(a,b,c,d)
# define btErrorBkpt(x) x
#else
void sqlite4BtDebugReadPage(BtLock *pLock, u32 pgno, u8 *aData, int pgsz);
int btErrorBkpt(int rc);
#endif

Changes to src/bt_lock.c.

37
38
39
40
41
42
43





44
45
46

47
48
49
50
51
52
53
54
...
216
217
218
219
220
221
222

223
224
225
226
227
228
229
** Global data. All global variables used by code in this file are grouped
** into the following structure instance.
**
** pDatabase:
**   Linked list of all Database objects allocated within this process.
**   This list may not be traversed without holding the global mutex (see
**   functions enterGlobalMutex() and leaveGlobalMutex()).





*/
static struct SharedData {
  BtShared *pDatabase;            /* Linked list of all Database objects */

} gShared;

struct BtFile {
  BtFile *pNext;
  bt_file *pFd;
};

struct BtShared {
................................................................................
  int nName;
  BtShared *pShared;

  zName = sqlite4BtPagerFilename((BtPager*)p, BT_PAGERFILE_DATABASE);
  nName = strlen(zName);

  btLockMutexEnter(p->pEnv);

  for(pShared=gShared.pDatabase; pShared; pShared=pShared->pNext){
    if( pShared->nName==nName && 0==memcmp(zName, pShared->zName, nName) ){
      break;
    }
  }

  if( pShared==0 ){







>
>
>
>
>



>
|







 







>







37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
...
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
** Global data. All global variables used by code in this file are grouped
** into the following structure instance.
**
** pDatabase:
**   Linked list of all Database objects allocated within this process.
**   This list may not be traversed without holding the global mutex (see
**   functions enterGlobalMutex() and leaveGlobalMutex()).
**
** iDebugId:
**   Each new connection is assigned a "debug-id". This contributes 
**   nothing to the operation of the library, but sometimes makes it 
**   easier to debug various problems.
*/
static struct SharedData {
  BtShared *pDatabase;            /* Linked list of all Database objects */
  int iDebugId;                   /* Next free debugging id */
} gShared = {0, 0};

struct BtFile {
  BtFile *pNext;
  bt_file *pFd;
};

struct BtShared {
................................................................................
  int nName;
  BtShared *pShared;

  zName = sqlite4BtPagerFilename((BtPager*)p, BT_PAGERFILE_DATABASE);
  nName = strlen(zName);

  btLockMutexEnter(p->pEnv);
  p->iDebugId = gShared.iDebugId++;
  for(pShared=gShared.pDatabase; pShared; pShared=pShared->pNext){
    if( pShared->nName==nName && 0==memcmp(zName, pShared->zName, nName) ){
      break;
    }
  }

  if( pShared==0 ){

Changes to src/bt_log.c.

41
42
43
44
45
46
47



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62





63
64
65
66

67
68
69
70
71
72
73
..
75
76
77
78
79
80
81


82



83
84
85
86
87
88
89
...
163
164
165
166
167
168
169




170
171
172
173
174
175
176
...
210
211
212
213
214
215
216












217
218
219
220
221
222
223
224


225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248

249

250
251
252
253
254
255














256
257
258
259

260
261
262

263
264
265
266
267
268
269
270
271
272
273
274
275
...
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
...
580
581
582
583
584
585
586
587
588
589
590
591

592
593
594
595
596
597
598
...
628
629
630
631
632
633
634























635
636
637
638
639
640
641
642
643
644
645

646
647
648
649
650
651
652
...
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703












704
705
706
707
708
709
710
...
871
872
873
874
875
876
877


878
879
880
881
882
883
884
...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
















921
922
923
924
925
926
927
...
971
972
973
974
975
976
977
978

979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995

996
997
998
999

1000
1001
1002
1003
1004
1005
1006
....
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
....
1376
1377
1378
1379
1380
1381
1382


















1383
1384
1385
1386
1387
1388
1389
....
1396
1397
1398
1399
1400
1401
1402


1403

1404
1405

1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425




1426
1427
1428
1429
1430
1431
1432
....
1474
1475
1476
1477
1478
1479
1480
1481












































*/
struct BtWalHdr {
  u32 iMagic;                     /* Magic number (BT_WAL_MAGIC) */
  u32 iVersion;                   /* File format version */
  u32 iCnt;                       /* 0, 1 or 2 */
  u32 nSector;                    /* Sector size when header written */
  u32 nPgsz;                      /* Database page size in bytes */




  u32 iSalt1;                     /* Initial frame cksum-0 value */
  u32 iSalt2;                     /* Initial frame cksum-1 value */
  u32 iFirstFrame;                /* First frame of log (numbered from 1) */

  u32 aCksum[2];                  /* Checksum of all prior fields */
};

/*
** WAL Frame header. All fields are stored in big-endian order.
**
** ctrl:
**   The most-significant-bit (BT_FRAME_COMMIT) of this field is set for
**   a commit frame and clear for all others. The other 31 bits contain
**   the frame number of the next frame in the log. 





*/
struct BtFrameHdr {
  u32 pgno;                       /* Page number of this frame */
  u32 ctrl;                       /* Next frame pointer and commit bit */

  u32 aCksum[2];                  /* Frame checksum */
};

#define BT_FRAME_COMMIT 0x80000000

/*
** Shared memory header. Shared memory begins with two copies of
................................................................................
*/
struct BtShmHdr {
  u32 aLog[6];                    /* First/last frames for each log region */
  int nSector;                    /* Sector size assumed for WAL file */
  int iHashSide;                  /* Hash table side for region (c) of log */
  u32 aFrameCksum[2];             /* Checksum of previous frame */
  u32 iNextFrame;                 /* Location to write next log frame to */


  int padding;                    /* So that this structure is 8-byte aligned */



  u32 aCksum[2];                  /* Object checksum */
};

/*
** A single instance of this structure follows the two BtShmHdr structures 
** in shared memory.
**
................................................................................
** of the input value as a little-endian integer.
*/
#define BYTESWAP32(x) ( \
  (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8)  \
  + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
)





/*
** Generate or extend an 8 byte checksum based on the data in
** array aByte[] and the initial values of aIn[0] and aIn[1] (or
** initial values of 0 and 0 if aIn==NULL).
**
** The checksum is written back into aOut[] before returning.
**
................................................................................
    }while( aData<aEnd );
  }

  aOut[0] = s1;
  aOut[1] = s2;
}














static void btDebugTopology(char *zStr, u32 *aLog){
  fprintf(stderr, "%s: %d..%d  %d..%d  %d..%d\n", zStr,
      (int)aLog[0], (int)aLog[1], (int)aLog[2], 
      (int)aLog[3], (int)aLog[4], (int)aLog[5]
  );
  fflush(stderr);
}



#ifndef NDEBUG
static void btDebugCheckSnapshot(BtShmHdr *pHdr){
  u32 *aLog = pHdr->aLog;
  assert( pHdr->iNextFrame!=1 ||
      (aLog[0]==0 && aLog[1]==0 && aLog[2]==0 && aLog[3]==0)
  );
}
#else
#define btDebugCheckSnapshot(x,y)
#endif

static void btDebugCkptPage(u32 pgno, u8 *aData, int pgsz){
#if 0
  static nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d: Ckpt page %d (cksum=%08x%08x)\n", nCall++,
      (int)pgno, aCksum[0], aCksum[1]
  );
  fflush(stderr);
#endif
}


static void btDebugLogPage(u32 pgno, u32 iFrame, u8 *aData, int pgsz){

#if 0
  static nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d: Log page %d to frame %d (cksum=%08x%08x)\n", nCall++,
      (int)pgno, (int)iFrame, aCksum[0], aCksum[1]














  );
  fflush(stderr);
#endif
}


#ifndef NDEBUG
void sqlite4BtDebugReadPage(u32 pgno, u8 *aData, int pgsz){

#if 0
  static nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d: Read page %d (cksum=%08x%08x)\n", nCall++,
      (int)pgno, aCksum[0], aCksum[1]
  );
  fflush(stderr);
#endif
}
#endif

/*
................................................................................
    iOff = btLogFrameOffset(pLog, pgsz, iFrame);

    rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
    if( rc==SQLITE4_OK ){
      rc = btLogReadData(pLog, iOff+sizeof(BtFrameHdr), aBuf, pgsz);
    }
    if( rc==SQLITE4_OK ){
      btLogChecksum(1, (u8*)&fhdr, offsetof(BtFrameHdr,aCksum), aCksum, aCksum);
      btLogChecksum(1, aBuf, pgsz, aCksum, aCksum);
      if( aCksum[0]!=fhdr.aCksum[0] || aCksum[1]!=fhdr.aCksum[1] ) break;
    }
    if( rc==SQLITE4_OK ){
      rc = xFrame(pLog, pCtx, iFrame, &fhdr);
    }

    iFrame = (fhdr.ctrl & ~BT_FRAME_COMMIT);
  }

  return rc;
}

/*
** Locate the iHash'th hash table in shared memory. Return it.
................................................................................
      pLog->snapshot.aLog[3] = pLog->snapshot.aLog[5];
      pLog->snapshot.aLog[4] = iFrame;
      pLog->snapshot.aLog[5] = iFrame;
    }
  }

  btLogHashInsert(pLog, pHdr->pgno, iFrame);
  if( pHdr->ctrl & BT_FRAME_COMMIT ){
    FrameRecoverCtx *pFRC = (FrameRecoverCtx*)pCtx;
    pFRC->iLast = iFrame;
    pFRC->iNextFrame = (pHdr->ctrl & ~BT_FRAME_COMMIT);
    memcpy(pLog->snapshot.aFrameCksum, pHdr->aCksum, sizeof(pHdr->aCksum));

  }

#if 0
  fprintf(stderr, "recovered frame=%d pgno=%d\n", iFrame, pHdr->pgno);
  fflush(stderr);
#endif
  return 0;
................................................................................
      pLog->snapshot.iHashSide = (pLog->snapshot.iHashSide + 1) % 2;
    }
  }

  aLog[5] = iLast;
  return btLogHashRollback(pLog, btLogFrameHash(pLog, iLast), iLast);
}
























/*
** Run log recovery. In other words, read the log file from disk and 
** initialize the shared-memory accordingly.
*/
static int btLogRecover(BtLog *pLog){
  bt_env *pVfs = pLog->pLock->pVfs;
  i64 nByte = 0;                  /* Size of log file on disk */
  int rc;                         /* Return code */
  BtWalHdr *pHdr = 0;
  int iSlot = 0;


  /* Read a log file header from the start of the file. */
  rc = pVfs->xSize(pLog->pFd, &nByte);
  if( rc==SQLITE4_OK && nByte>0 ){
    BtWalHdr hdr1;
    rc = btLogReadHeader(pLog, 0, &hdr1);
    if( rc==SQLITE4_OK ){
................................................................................
    /* The following iterates through all readable frames in the log file.
    ** It populates pLog->snapshot.aLog[] with the log topology and the
    ** shared hash-tables with the pgno->frame mapping. The FrameRecoverCtx
    ** object is populated with the frame number and "next frame" pointer of
    ** the last commit-frame in the log (if any). Additionally, the
    ** pLog->snapshot.aFrameCksum[] variables are populated with the checksum
    ** beloging to the frame header of the last commit-frame in the log.  */
    FrameRecoverCtx ctx = {0, 0};
    rc = btLogTraverse(pLog, pHdr, btLogRecoverFrame, (void*)&ctx);

    if( rc==SQLITE4_OK ){
      if( ctx.iLast==0 ){
        /* No transactions recovered from the log file. */
        btLogZeroSnapshot(pLog);
      }else{
        /* One or more transactions were recovered from the log file. */
        BtShm *pShm = btLogShm(pLog);
        pShm->ckpt.iWalHdr = (iSlot<<2) + pHdr->iCnt;
        pShm->ckpt.iFirstRead = pHdr->iFirstFrame;
        pShm->ckpt.iFirstRecover = pHdr->iFirstFrame;
        rc = btLogRollbackRecovery(pLog, &ctx);
        pLog->snapshot.iNextFrame = ctx.iNextFrame;
      }
    }
  }













  return rc;
}

/*
** Open the log file for pager pPager. If successful, return the BtLog* 
** handle via output variable *ppLog. If parameter bRecover is true, then
................................................................................
      }
    }
    if( (iSafe>=iLo && iSafe<=iHi) ){
      bSeen = 1;
    }
  }



  if( rc==SQLITE4_OK ){
    bt_env *pVfs = pLog->pLock->pVfs;
    i64 iOff;
    assert( rc==SQLITE4_OK );
    iOff = btLogFrameOffset(pLog, pgsz, iFrame);
    rc = pVfs->xRead(pLog->pFd, iOff + sizeof(BtFrameHdr), aData, pgsz);

................................................................................
int sqlite4BtLogRead(BtLog *pLog, u32 pgno, u8 *aData){
  return btLogRead(pLog, pgno, aData, 0);
}

/*
** Write a frame to the log file.
*/
int sqlite4BtLogWrite(BtLog *pLog, u32 pgno, u8 *aData, int bCommit){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  int rc = SQLITE4_OK;
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  BtFrameHdr frame;               /* Header for new frame */
  u32 *a;                         /* Pointer to cksum of previous frame */
  i64 iOff;                       /* Offset of log file to write to */
  u32 iNextFrame;
  u32 *aLog = pLog->snapshot.aLog;

















  /* Handle a special case - if the log file is completely empty then
  ** this writer must write the first header into the WAL file. */
  if( btLogIsEmpty(pLog) ){
    BtWalHdr hdr;
    memset(&hdr, 0, sizeof(BtWalHdr));

................................................................................
  if( iNextFrame & 0x80000000 ){
    rc = SQLITE4_FULL;
  }else{

    /* Populate the frame header object. */
    memset(&frame, 0, sizeof(frame));
    frame.pgno = pgno;
    frame.ctrl = (bCommit ? BT_FRAME_COMMIT : 0) + iNextFrame;

    a = pLog->snapshot.aFrameCksum;
    btLogChecksum(1, (u8*)&frame, offsetof(BtFrameHdr,aCksum), a, frame.aCksum);
    btLogChecksum(1, aData, pgsz, frame.aCksum, frame.aCksum);

    btDebugLogPage(pgno, iFrame, aData, pgsz);

    /* Write the frame header to the log file. */
    rc = btLogWriteData(pLog, iOff, (u8*)&frame, sizeof(frame));
  }
  pLog->snapshot.iNextFrame = iNextFrame;

  /* Write the frame contents to the log file. */
  if( rc==SQLITE4_OK ){
    rc = btLogWriteData(pLog, iOff+sizeof(frame), aData, pgsz);
  }

  /* Update the wal index hash tables with the (pgno -> iFrame) record. */

  if( rc==SQLITE4_OK ){
    if( iFrame==1 ){
      pLog->snapshot.iHashSide = (pLog->snapshot.iHashSide+1) %2;
    }

    rc = btLogHashInsert(pLog, pgno, iFrame);
  }

  /* Update the private copy of the shm-header */
  btDebugCheckSnapshot(&pLog->snapshot);
  BtShmHdr hdr;
  memcpy(&hdr, &pLog->snapshot, sizeof(BtShmHdr));
................................................................................

    aLog[5] = iFrame;
    memcpy(pLog->snapshot.aFrameCksum, frame.aCksum, sizeof(frame.aCksum));
  }
  btDebugCheckSnapshot(&pLog->snapshot);

  /* If this is a COMMIT, also update the shared shm-header. */
  if( bCommit ){
    rc = btLogUpdateSharedHdr(pLog);
  }

  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];
}



















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

  /* Take the CHECKPOINTER lock. */
  rc = sqlite4BtLockCkpt(pLock);
................................................................................
    BtFrameHdr fhdr;              /* Frame header of frame iLast */
    u32 *aPgno = 0;               /* Array of page numbers to checkpoint */
    int nPgno;                    /* Number of entries in aPgno[] */
    int i;                        /* Used to loop through aPgno[] */
    u8 *aBuf;                     /* Buffer to load page data into */
    u32 iFirstRead;               /* First frame not checkpointed */



    /* 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 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);
      rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
      iFirstRead = (fhdr.ctrl & ~BT_FRAME_COMMIT);
    }

    /* Copy data from the log file to the database file. */
    for(i=0; rc==SQLITE4_OK && i<nPgno; i++){
      u32 pgno = aPgno[i];
      rc = btLogRead(pLog, pgno, aBuf, iLast);
      if( rc==SQLITE4_OK ){
        btDebugCkptPage(pgno, aBuf, pgsz);
        i64 iOff = (i64)pgsz * (pgno-1);




        rc = pVfs->xWrite(pFd, iOff, aBuf, pgsz);
      }else if( rc==SQLITE4_NOTFOUND ){
        rc = SQLITE4_OK;
      }
    }

    /* Update the first field of the checkpoint-header. This tells readers
................................................................................
    /* Free buffers and drop the checkpointer lock */
    sqlite4_free(pLock->pEnv, aBuf);
    sqlite4_free(pLock->pEnv, aPgno);
    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}




















































>
>
>











|
|
|
|
>
>
>
>
>



|
>







 







>
>
|
>
>
>







 







>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>








>
>













|
|









>
|
>
|
|


|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>


<
>
|
|
<
|
|
<







 







|







|







 







|


|

>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











>







 







<


|
<
<
<
<
|
|
|
|
|
|
|
|
|
|
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>







 







|








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
>

|


|











|
>




>







 







|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>

>
|
|
>










|







<

>
>
>
>







 








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
..
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
...
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310

311
312
313

314
315

316
317
318
319
320
321
322
...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
...
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
...
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
...
751
752
753
754
755
756
757

758
759
760




761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
...
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
...
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
....
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
....
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
....
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
....
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545

1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
....
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
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
*/
struct BtWalHdr {
  u32 iMagic;                     /* Magic number (BT_WAL_MAGIC) */
  u32 iVersion;                   /* File format version */
  u32 iCnt;                       /* 0, 1 or 2 */
  u32 nSector;                    /* Sector size when header written */
  u32 nPgsz;                      /* Database page size in bytes */
  u32 nPg;                        /* Database size in pages at last commit */

  u32 padding;

  u32 iSalt1;                     /* Initial frame cksum-0 value */
  u32 iSalt2;                     /* Initial frame cksum-1 value */
  u32 iFirstFrame;                /* First frame of log (numbered from 1) */

  u32 aCksum[2];                  /* Checksum of all prior fields */
};

/*
** WAL Frame header. All fields are stored in big-endian order.
**
** pgno:
**   Page number for the frame.
**
** iNext:
**   Next frame in logical log.
**   
** nPg:
**   For non-commit frames, zero. For commit frames, the size of the
**   database file in pages at the time of commit (always at least 1).
*/
struct BtFrameHdr {
  u32 pgno;                       /* Page number of this frame */
  u32 iNext;                      /* Next frame pointer */
  u32 nPg;                        /* For commit frames, size of db file */
  u32 aCksum[2];                  /* Frame checksum */
};

#define BT_FRAME_COMMIT 0x80000000

/*
** Shared memory header. Shared memory begins with two copies of
................................................................................
*/
struct BtShmHdr {
  u32 aLog[6];                    /* First/last frames for each log region */
  int nSector;                    /* Sector size assumed for WAL file */
  int iHashSide;                  /* Hash table side for region (c) of log */
  u32 aFrameCksum[2];             /* Checksum of previous frame */
  u32 iNextFrame;                 /* Location to write next log frame to */

  /* The three "db-header" values */
  u32 iCookie;                    /* User cookie value */
  int pgsz;                       /* Size of database pages in bytes */
  int nPg;                        /* Number of pages in db at last commit */

  u32 aCksum[2];                  /* Object checksum */
};

/*
** A single instance of this structure follows the two BtShmHdr structures 
** in shared memory.
**
................................................................................
** of the input value as a little-endian integer.
*/
#define BYTESWAP32(x) ( \
  (((x)&0x000000FF)<<24) + (((x)&0x0000FF00)<<8)  \
  + (((x)&0x00FF0000)>>8)  + (((x)&0xFF000000)>>24) \
)

/* True if this is a little-endian build */
static const int one = 1;
#define BTLOG_LITTLE_ENDIAN (*(u8 *)(&one))

/*
** Generate or extend an 8 byte checksum based on the data in
** array aByte[] and the initial values of aIn[0] and aIn[1] (or
** initial values of 0 and 0 if aIn==NULL).
**
** The checksum is written back into aOut[] before returning.
**
................................................................................
    }while( aData<aEnd );
  }

  aOut[0] = s1;
  aOut[1] = s2;
}

static void btLogChecksum32(
  int nativeCksum,                /* True for native byte-order, else false */
  u8 *a,                          /* Content to be checksummed */
  int nByte,                      /* Bytes of content in a[]. */
  const u32 *aIn,                 /* Initial checksum value input */
  u32 *aOut                       /* OUT: Final checksum value output */
){
  assert( (nByte&0x00000007)==4 && nByte>=8 );
  btLogChecksum(nativeCksum, a, 8, aIn, aOut);
  btLogChecksum(nativeCksum, &a[4], nByte-4, aOut, aOut);
}


static void btDebugTopology(char *zStr, u32 *aLog){
  fprintf(stderr, "%s: %d..%d  %d..%d  %d..%d\n", zStr,
      (int)aLog[0], (int)aLog[1], (int)aLog[2], 
      (int)aLog[3], (int)aLog[4], (int)aLog[5]
  );
  fflush(stderr);
}

#define BT_PAGE_DEBUG 0

#ifndef NDEBUG
static void btDebugCheckSnapshot(BtShmHdr *pHdr){
  u32 *aLog = pHdr->aLog;
  assert( pHdr->iNextFrame!=1 ||
      (aLog[0]==0 && aLog[1]==0 && aLog[2]==0 && aLog[3]==0)
  );
}
#else
#define btDebugCheckSnapshot(x,y)
#endif

static void btDebugCkptPage(u32 pgno, u8 *aData, int pgsz){
#if BT_PAGE_DEBUG
  static int nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d: Ckpt page %d (cksum=%08x%08x)\n", nCall++,
      (int)pgno, aCksum[0], aCksum[1]
  );
  fflush(stderr);
#endif
}

static void btDebugLogPage(
    BtLock *pLock, u32 pgno, u32 iFrame, u8 *aData, int pgsz, int bCommit
){
#if BT_PAGE_DEBUG
  static int nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d:%d: Log page %d to frame %d (cksum=%08x%08x)%s\n", 
      pLock->iDebugId, nCall++, (int)pgno, (int)iFrame, 
      aCksum[0], aCksum[1], (bCommit ? " commit" : "")
  );
  fflush(stderr);
#endif
}

#ifndef NDEBUG
void sqlite4BtDebugReadPage(BtLock *pLock, u32 pgno, u8 *aData, int pgsz){
#if BT_PAGE_DEBUG
  static int nCall = 0;
  u32 aCksum[2];
  btLogChecksum(1, aData, pgsz, 0, aCksum);
  fprintf(stderr, "%d:%d: Read page %d (cksum=%08x%08x)\n", pLock->iDebugId,
      nCall++, (int)pgno, aCksum[0], aCksum[1]
  );
  fflush(stderr);
#endif
}
#endif

#ifndef NDEBUG

static void btDebugLogSearch(BtLock *pLock, u32 pgno, u32 iFrame){
#if BT_PAGE_DEBUG
  static int nCall = 0;

  fprintf(stderr, "%d:%d: Search log for page %d - frame %d\n", pLock->iDebugId,
      nCall++, (int)pgno, (int)iFrame

  );
  fflush(stderr);
#endif
}
#endif

/*
................................................................................
    iOff = btLogFrameOffset(pLog, pgsz, iFrame);

    rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
    if( rc==SQLITE4_OK ){
      rc = btLogReadData(pLog, iOff+sizeof(BtFrameHdr), aBuf, pgsz);
    }
    if( rc==SQLITE4_OK ){
      btLogChecksum32(1, (u8*)&fhdr, offsetof(BtFrameHdr,aCksum),aCksum,aCksum);
      btLogChecksum(1, aBuf, pgsz, aCksum, aCksum);
      if( aCksum[0]!=fhdr.aCksum[0] || aCksum[1]!=fhdr.aCksum[1] ) break;
    }
    if( rc==SQLITE4_OK ){
      rc = xFrame(pLog, pCtx, iFrame, &fhdr);
    }

    iFrame = fhdr.iNext;
  }

  return rc;
}

/*
** Locate the iHash'th hash table in shared memory. Return it.
................................................................................
      pLog->snapshot.aLog[3] = pLog->snapshot.aLog[5];
      pLog->snapshot.aLog[4] = iFrame;
      pLog->snapshot.aLog[5] = iFrame;
    }
  }

  btLogHashInsert(pLog, pHdr->pgno, iFrame);
  if( pHdr->nPg!=0 ){
    FrameRecoverCtx *pFRC = (FrameRecoverCtx*)pCtx;
    pFRC->iLast = iFrame;
    pFRC->iNextFrame = pHdr->iNext;
    memcpy(pLog->snapshot.aFrameCksum, pHdr->aCksum, sizeof(pHdr->aCksum));
    pLog->snapshot.nPg = pHdr->nPg;
  }

#if 0
  fprintf(stderr, "recovered frame=%d pgno=%d\n", iFrame, pHdr->pgno);
  fflush(stderr);
#endif
  return 0;
................................................................................
      pLog->snapshot.iHashSide = (pLog->snapshot.iHashSide + 1) % 2;
    }
  }

  aLog[5] = iLast;
  return btLogHashRollback(pLog, btLogFrameHash(pLog, iLast), iLast);
}

static int btLogReadDbhdr(BtLog *pLog, BtDbhdr *pHdr){
  BtLock *p = pLog->pLock;
  int rc;
  i64 nByte;

  rc = p->pVfs->xSize(p->pFd, &nByte);
  if( rc==SQLITE4_OK && nByte>0 ){
    rc = p->pVfs->xRead(p->pFd, 0, pHdr, sizeof(BtDbhdr));
    if( BTLOG_LITTLE_ENDIAN ){
      pHdr->cookie = BYTESWAP32(pHdr->cookie);
      pHdr->nPg = BYTESWAP32(pHdr->nPg);
      pHdr->pgsz = BYTESWAP32(pHdr->pgsz);
    }
    assert( pHdr->pgsz>0 );
  }else{
    memset(pHdr, 0, sizeof(BtDbhdr));
    pHdr->pgsz = BT_DEFAULT_PGSZ;
    pHdr->nPg = 1;
  }

  return rc;
}

/*
** Run log recovery. In other words, read the log file from disk and 
** initialize the shared-memory accordingly.
*/
static int btLogRecover(BtLog *pLog){
  bt_env *pVfs = pLog->pLock->pVfs;
  i64 nByte = 0;                  /* Size of log file on disk */
  int rc;                         /* Return code */
  BtWalHdr *pHdr = 0;
  int iSlot = 0;
  FrameRecoverCtx ctx = {0, 0};

  /* Read a log file header from the start of the file. */
  rc = pVfs->xSize(pLog->pFd, &nByte);
  if( rc==SQLITE4_OK && nByte>0 ){
    BtWalHdr hdr1;
    rc = btLogReadHeader(pLog, 0, &hdr1);
    if( rc==SQLITE4_OK ){
................................................................................
    /* The following iterates through all readable frames in the log file.
    ** It populates pLog->snapshot.aLog[] with the log topology and the
    ** shared hash-tables with the pgno->frame mapping. The FrameRecoverCtx
    ** object is populated with the frame number and "next frame" pointer of
    ** the last commit-frame in the log (if any). Additionally, the
    ** pLog->snapshot.aFrameCksum[] variables are populated with the checksum
    ** beloging to the frame header of the last commit-frame in the log.  */

    rc = btLogTraverse(pLog, pHdr, btLogRecoverFrame, (void*)&ctx);

    if( rc==SQLITE4_OK && ctx.iLast>0 ){




      /* One or more transactions were recovered from the log file. */
      BtShm *pShm = btLogShm(pLog);
      pShm->ckpt.iWalHdr = (iSlot<<2) + pHdr->iCnt;
      pShm->ckpt.iFirstRead = pHdr->iFirstFrame;
      pShm->ckpt.iFirstRecover = pHdr->iFirstFrame;
      rc = btLogRollbackRecovery(pLog, &ctx);
      pLog->snapshot.iNextFrame = ctx.iNextFrame;
    }
  }

  if( rc==SQLITE4_OK && ctx.iLast==0 ){
    /* No transactions were recovered from the log file. */
    BtDbhdr dbhdr;            /* Database header */
    btLogZeroSnapshot(pLog);

    /* Read the database file header to obtail values required
    ** by the snapshot.  */
    rc = btLogReadDbhdr(pLog, &dbhdr);
    pLog->snapshot.nPg = dbhdr.nPg;
    pLog->snapshot.pgsz = dbhdr.pgsz;
    pLog->snapshot.iCookie = dbhdr.cookie;
  }

  return rc;
}

/*
** Open the log file for pager pPager. If successful, return the BtLog* 
** handle via output variable *ppLog. If parameter bRecover is true, then
................................................................................
      }
    }
    if( (iSafe>=iLo && iSafe<=iHi) ){
      bSeen = 1;
    }
  }

  btDebugLogSearch(pLog->pLock, pgno, (rc==SQLITE4_OK ? iFrame : 0));

  if( rc==SQLITE4_OK ){
    bt_env *pVfs = pLog->pLock->pVfs;
    i64 iOff;
    assert( rc==SQLITE4_OK );
    iOff = btLogFrameOffset(pLog, pgsz, iFrame);
    rc = pVfs->xRead(pLog->pFd, iOff + sizeof(BtFrameHdr), aData, pgsz);

................................................................................
int sqlite4BtLogRead(BtLog *pLog, u32 pgno, u8 *aData){
  return btLogRead(pLog, pgno, aData, 0);
}

/*
** Write a frame to the log file.
*/
int sqlite4BtLogWrite(BtLog *pLog, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  int rc = SQLITE4_OK;
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  BtFrameHdr frame;               /* Header for new frame */
  u32 *a;                         /* Pointer to cksum of previous frame */
  i64 iOff;                       /* Offset of log file to write to */
  u32 iNextFrame;
  u32 *aLog = pLog->snapshot.aLog;

  /* If this is a commit frame and the size of the database has changed,
  ** ensure that the log file contains at least one copy of page 1 written
  ** since the last checkpoint. This is required as a future checkpoint
  ** will need to update the nPg field in the database header located on
  ** page 1. */
  if( nPg && nPg!=pLog->snapshot.nPg ){
    BtPager *pPager = (BtPager *)(pLog->pLock);
    BtPage *pOne = 0;
    rc = sqlite4BtPageGet(pPager, 1, &pOne);
    if( rc==SQLITE4_OK ){
      rc = sqlite4BtLogWrite(pLog, 1, sqlite4BtPageData(pOne), 0);
      sqlite4BtPageRelease(pOne);
    }
    if( rc!=SQLITE4_OK ) return rc;
  }

  /* Handle a special case - if the log file is completely empty then
  ** this writer must write the first header into the WAL file. */
  if( btLogIsEmpty(pLog) ){
    BtWalHdr hdr;
    memset(&hdr, 0, sizeof(BtWalHdr));

................................................................................
  if( iNextFrame & 0x80000000 ){
    rc = SQLITE4_FULL;
  }else{

    /* Populate the frame header object. */
    memset(&frame, 0, sizeof(frame));
    frame.pgno = pgno;
    frame.iNext = iNextFrame;
    frame.nPg = nPg;
    a = pLog->snapshot.aFrameCksum;
    btLogChecksum32(1, (u8*)&frame, offsetof(BtFrameHdr,aCksum),a,frame.aCksum);
    btLogChecksum(1, aData, pgsz, frame.aCksum, frame.aCksum);

    btDebugLogPage(pLog->pLock, pgno, iFrame, aData, pgsz, nPg);

    /* Write the frame header to the log file. */
    rc = btLogWriteData(pLog, iOff, (u8*)&frame, sizeof(frame));
  }
  pLog->snapshot.iNextFrame = iNextFrame;

  /* Write the frame contents to the log file. */
  if( rc==SQLITE4_OK ){
    rc = btLogWriteData(pLog, iOff+sizeof(frame), aData, pgsz);
  }

  /* Update the wal index hash tables with the (pgno -> iFrame) record. 
  ** If this is a commit frame, update the nPg field as well. */
  if( rc==SQLITE4_OK ){
    if( iFrame==1 ){
      pLog->snapshot.iHashSide = (pLog->snapshot.iHashSide+1) %2;
    }
    if( nPg ) pLog->snapshot.nPg = nPg;
    rc = btLogHashInsert(pLog, pgno, iFrame);
  }

  /* Update the private copy of the shm-header */
  btDebugCheckSnapshot(&pLog->snapshot);
  BtShmHdr hdr;
  memcpy(&hdr, &pLog->snapshot, sizeof(BtShmHdr));
................................................................................

    aLog[5] = iFrame;
    memcpy(pLog->snapshot.aFrameCksum, frame.aCksum, sizeof(frame.aCksum));
  }
  btDebugCheckSnapshot(&pLog->snapshot);

  /* If this is a COMMIT, also update the shared shm-header. */
  if( nPg ){
    rc = btLogUpdateSharedHdr(pLog);
  }

  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);
................................................................................
    BtFrameHdr fhdr;              /* Frame header of frame iLast */
    u32 *aPgno = 0;               /* Array of page numbers to checkpoint */
    int nPgno;                    /* Number of entries in aPgno[] */
    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);

    /* Allocate space to load log data into */
    if( rc==SQLITE4_OK ){
      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 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);
      rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
      iFirstRead = fhdr.iNext;
    }

    /* Copy data from the log file to the database file. */
    for(i=0; rc==SQLITE4_OK && i<nPgno; i++){
      u32 pgno = aPgno[i];
      rc = btLogRead(pLog, pgno, aBuf, iLast);
      if( rc==SQLITE4_OK ){

        i64 iOff = (i64)pgsz * (pgno-1);
        if( pgno==1 ){
          btLogUpdateDbhdr(pLog, aBuf);
        }
        btDebugCkptPage(pgno, aBuf, pgsz);
        rc = pVfs->xWrite(pFd, iOff, aBuf, pgsz);
      }else if( rc==SQLITE4_NOTFOUND ){
        rc = SQLITE4_OK;
      }
    }

    /* Update the first field of the checkpoint-header. This tells readers
................................................................................
    /* Free buffers and drop the checkpointer lock */
    sqlite4_free(pLock->pEnv, aBuf);
    sqlite4_free(pLock->pEnv, aPgno);
    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}

/*
** Return the database page size in bytes.
*/
int sqlite4BtLogPagesize(BtLog *pLog){
  return pLog->snapshot.pgsz;
}

/*
** Return the number of pages in the database at last commit.
*/
int sqlite4BtLogPagecount(BtLog *pLog){
  return (pLog->snapshot.nPg==1 ? 2 : pLog->snapshot.nPg);
}

/*
** Return the current value of the user cookie.
*/
u32 sqlite4BtLogCookie(BtLog *pLog){
  return pLog->snapshot.iCookie;
}


/*
** Set the value of the user cookie.
*/
int sqlite4BtLogSetCookie(BtLog *pLog, u32 iCookie){
  BtPager *pPager = (BtPager *)(pLog->pLock);
  BtPage *pOne = 0;
  int rc;

  rc = sqlite4BtPageGet(pPager, 1, &pOne);
  if( rc==SQLITE4_OK ){
    rc = sqlite4BtPageWrite(pOne);
  }
  if( rc==SQLITE4_OK ){
    pLog->snapshot.iCookie = iCookie;
    btLogUpdateDbhdr(pLog, sqlite4BtPageData(pOne));
  }
  sqlite4BtPageRelease(pOne);

  return rc;
}


Changes to src/bt_main.c.

102
103
104
105
106
107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
  a[1] = (u8)((i>>0) & 0xFF);
}

/*
** Write the value passed as the second argument to the buffer passed
** as the first. Formatted as an unsigned 32-bit big-endian integer.
*/
static void btPutU32(u8 *a, u32 i){
  a[0] = (u8)((i>>24) & 0xFF);
  a[1] = (u8)((i>>16) & 0xFF);
  a[2] = (u8)((i>>8) & 0xFF);
  a[3] = (u8)((i>>0) & 0xFF);
}


/*
** Allocate a new database handle.
*/
int sqlite4BtNew(sqlite4_env *pEnv, int nExtra, bt_db **ppDb){
  bt_db *db = 0;                  /* New database object */
  BtPager *pPager = 0;            /* Pager object for this database */







|





>







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  a[1] = (u8)((i>>0) & 0xFF);
}

/*
** Write the value passed as the second argument to the buffer passed
** as the first. Formatted as an unsigned 32-bit big-endian integer.
*/
void sqlite4BtPutU32(u8 *a, u32 i){
  a[0] = (u8)((i>>24) & 0xFF);
  a[1] = (u8)((i>>16) & 0xFF);
  a[2] = (u8)((i>>8) & 0xFF);
  a[3] = (u8)((i>>0) & 0xFF);
}
#define btPutU32(x,y) sqlite4BtPutU32(x,y)

/*
** Allocate a new database handle.
*/
int sqlite4BtNew(sqlite4_env *pEnv, int nExtra, bt_db **ppDb){
  bt_db *db = 0;                  /* New database object */
  BtPager *pPager = 0;            /* Pager object for this database */

Changes to src/bt_pager.c.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
..
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
...
100
101
102
103
104
105
106
107
108
109


110
111
112
113
114
115
116
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479


480
481
482
483
484
485
486
...
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
...
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
...
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
...
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
...
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
...
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
...
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
...
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
...
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925

#include "btInt.h"

#include <string.h>
#include <assert.h>
#include <stdio.h>

/* By default pages are 1024 bytes in size. */
#define BT_DEFAULT_PGSZ 1024

/* By default auto-checkpoint is 1000 */
#define BT_DEFAULT_AUTOCKPT 1000

typedef struct BtPageHash BtPageHash;
typedef struct BtDbhdr BtDbhdr;

typedef struct BtSavepoint BtSavepoint;
typedef struct BtSavepage BtSavepage;

/*
** Hash table for page references currently in memory. Manipulated using
** the following functions:
................................................................................
};

/*
** Candidate values for BtPage.flags
*/
#define BT_PAGE_DIRTY 0x0001      /* Set for pages in BtPager.pDirty list */

/*
** Database header.
*/
struct BtDbhdr {
  u32 pgsz;                       /* Page size in bytes */
  u32 nPg;                        /* Number of pages in database */
  u32 cookie;                     /* User cookie value (SQL schema cookie) */
};

/*
** Pager object.
**
** nAutoCkpt:
**   If a transaction is committed and there are this many frames in the
**   log file, automatically run a checkpoint operation.
*/
................................................................................
  char *zFile;                    /* Database file name */
  int nFile;                      /* Length of string zFile in bytes */
  BtPageHash hash;                /* Hash table */
  BtPage *pDirty;                 /* List of all dirty pages */
  int nTotalRef;                  /* Total number of outstanding page refs */
  int nAutoCkpt;                  /* Auto-checkpoint when log is this large */
  int bDoAutoCkpt;                /* Do auto-checkpoint after next unlock */
  BtDbhdr dbhdr;
  BtSavepoint *aSavepoint;        /* Savepoint array */
  int nSavepoint;                 /* Number of entries in aSavepoint array */


};


/**************************************************************************
** Interface to BtPageHash object.
*/

................................................................................
    if( pSavepage==0 ){
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }

    if( rc==SQLITE4_OK && (1 || (pPg->flags & BT_PAGE_DIRTY)) ){
      pSavepage->aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->dbhdr.pgsz);
      if( pSavepage->aData==0 ){
        sqlite4_free(p->btl.pEnv, pSavepage);
        rc = btErrorBkpt(SQLITE4_NOMEM);
      }else{
        memcpy(pSavepage->aData, pPg->aData, p->dbhdr.pgsz);
      }
    }

    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;

................................................................................
}

/*
** Open a read-transaction.
*/
static int btOpenReadTransaction(BtPager *p){
  int rc;
  i64 nByte;                      /* Size of db file in bytes */

  assert( p->iTransactionLevel==0 );
  assert( p->btl.pFd );

  /* TODO: This should be subsumed into log recovery etc. */
  rc = p->btl.pVfs->xSize(p->btl.pFd, &nByte);
  if( rc==SQLITE4_OK && nByte>0 ){
    rc = p->btl.pVfs->xRead(p->btl.pFd, 0, &p->dbhdr, sizeof(p->dbhdr));
  }else{
    memset(&p->dbhdr, 0, sizeof(p->dbhdr));
    p->dbhdr.pgsz = BT_DEFAULT_PGSZ;
    p->dbhdr.nPg = 2;
  }

  if( rc==SQLITE4_OK ){
    rc = sqlite4BtLogSnapshotOpen(p->pLog);
  }

  if( rc==SQLITE4_OK ){
    /* If the read transaction was successfully opened, the transaction 
    ** level is now 1.  */
    p->iTransactionLevel = 1;


  }
  return rc;
}

static int btOpenWriteTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==1 );
................................................................................
  btCloseSavepoints(p, 2);

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int bCommit = (pNext==0);
      rc = sqlite4BtLogWrite(p->pLog, pPg->pgno, pPg->aData, bCommit);
    }
  }
  p->pDirty = 0;
  sqlite4BtLogSnapshotEndWrite(p->pLog);

  /* TODO: Fix this. */
  if( rc==SQLITE4_OK ){
    rc = p->btl.pVfs->xWrite(p->btl.pFd, 0, (void*)&p->dbhdr, sizeof(BtDbhdr));
  }

  if( p->nAutoCkpt && sqlite4BtLogSize(p->pLog)>=p->nAutoCkpt ){
    p->bDoAutoCkpt = 1;
  }

  return rc;
}

................................................................................
  ** data was loaded successfully. If SQLITE4_NOTFOUND, the required page
  ** is not present in the log and should be loaded from the database
  ** file. Any other error code is returned to the caller.  */
  rc = sqlite4BtLogRead(p->pLog, pPg->pgno, pPg->aData);

  /* If necessary, load data from the database file. */
  if( rc==SQLITE4_NOTFOUND ){
    i64 iOff = (i64)p->dbhdr.pgsz * (i64)(pPg->pgno-1);
    rc = p->btl.pVfs->xRead(p->btl.pFd, iOff, pPg->aData, p->dbhdr.pgsz);
  }

  return rc;
}

static int btAllocatePage(BtPager *p, BtPage **ppPg){
  int rc;                         /* Return code */
  BtPage *pRet;
  u8 *aData;

  pRet = (BtPage*)sqlite4_malloc(p->btl.pEnv, sizeof(BtPage));
  aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->dbhdr.pgsz);

  if( pRet && aData ){
    memset(pRet, 0, sizeof(BtPage));
    pRet->aData = aData;
    pRet->pPager = p;
    rc = SQLITE4_OK;
  }else{
................................................................................
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=2 );
  btCloseSavepoints(p, 2);

  /* Load the old db header from disk */
  rc = p->btl.pVfs->xRead(p->btl.pFd, 0, &p->dbhdr, sizeof(p->dbhdr));

  /* Loop through all dirty pages in memory. Discard those with nRef==0.
  ** Reload data from disk for any others.  */
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( pPg->nRef==0 ){
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->dbhdr.nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  p->pDirty = 0;

  return rc;
}
................................................................................
        pNext = pSavepage->pNext;

        /* Detach the BtSavepage from its BtPage object */
        assert( pSavepage==pPg->pSavepage );
        pPg->pSavepage = pSavepage->pNextSavepage;

        /* Restore the page data */
        memcpy(pPg->aData, pSavepage->aData, p->dbhdr.pgsz);

        /* Free associated memory allocations */
        assert( pSavepage->aData ); /* temp */
        sqlite4_free(p->btl.pEnv, pSavepage->aData);
        sqlite4_free(p->btl.pEnv, pSavepage);
      }

................................................................................
        rc = btRollbackSavepoints(p, iLevel);
      }
    }

    p->iTransactionLevel = iLevel;
    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc2 = rc;
    }
  }

  return rc;
}

int sqlite4BtPagerRevert(BtPager *p, int iLevel){
................................................................................
}

/*
** Query for the database page size. Requires an open read transaction.
*/
int sqlite4BtPagerPagesize(BtPager *p){
  /* assert( p->iTransactionLevel>=1 && p->btl.pFd ); */
  return (int)p->dbhdr.pgsz;
}

/* 
** Query for the root page number. Requires an open read transaction.
*/
u32 sqlite4BtPagerRootpgno(BtPager *p){
  assert( p->iTransactionLevel>=1 && p->btl.pFd );
................................................................................
  pRet = btHashSearch(p, pgno);

  /* If the page is not in the cache, load it from disk */
  if( pRet==0 ){
    rc = btAllocatePage(p, &pRet);
    if( rc==SQLITE4_OK ){
      pRet->pgno = pgno;
      if( pgno<=p->dbhdr.nPg ){
        rc = btLoadPageData(p, pRet);
      }else{
        memset(pRet->aData, 0, p->dbhdr.pgsz);
      }

      if( rc==SQLITE4_OK ){
        rc = btHashAdd(p, pRet);
      }
      if( rc!=SQLITE4_OK ){
        btFreePage(p, pRet);
        pRet = 0;
      }
    }
    sqlite4BtDebugReadPage(pgno, pRet->aData, p->dbhdr.pgsz);
  }

  assert( (pRet!=0)==(rc==SQLITE4_OK) );
  if( rc==SQLITE4_OK ){
    p->nTotalRef++;
    pRet->nRef++;
  }
................................................................................

/*
** Allocate a new database page and return a writable reference to it.
*/
int sqlite4BtPageAllocate(BtPager *p, BtPage **ppPg){
  BtPage *pPg = 0;
  int rc;
  u32 pgno = p->dbhdr.nPg+1;

  rc = sqlite4BtPageGet(p, pgno, &pPg);
  if( rc==SQLITE4_OK ){
    rc = sqlite4BtPageWrite(pPg);
    if( rc!=SQLITE4_OK ){
      sqlite4BtPageRelease(pPg);
      pPg = 0;
    }else{
      p->dbhdr.nPg = pgno;
    }
  }

#ifdef BT_STDERR_DEBUG
  fprintf(stderr, "allocated page %d\n", pgno);
#endif

................................................................................
}

/* 
** Read the schema cookie value. Requires an open read-transaction.
*/
int sqlite4BtPagerSetCookie(BtPager *p, u32 iVal){
  assert( p->iTransactionLevel>=2 );
  p->dbhdr.cookie = iVal;
  return SQLITE4_OK;
}

/* 
** Set the schema cookie value. Requires an open write-transaction.
*/
int sqlite4BtPagerGetCookie(BtPager *p, u32 *piVal){
  assert( p->iTransactionLevel>=1 );
  *piVal = p->dbhdr.cookie;
  return SQLITE4_OK;
}

const char *sqlite4BtPagerFilename(BtPager *p, int ePagerfile){
  const char *zTail;

  switch( ePagerfile ){







<
<





<







 







<
<
<
<
<
<
<
<
<







 







<


>
>







 







|




|







 







<




<
<
<
<
<
<
<
<
<
<
<
|
<





>
>







 







|
|





<
<
<
<
<







 







|
|











|







 







<
<
<









|







 







|







 







|







 







|







 







|


|










|







 







|








|







 







|
<







|







13
14
15
16
17
18
19


20
21
22
23
24

25
26
27
28
29
30
31
..
70
71
72
73
74
75
76









77
78
79
80
81
82
83
..
88
89
90
91
92
93
94

95
96
97
98
99
100
101
102
103
104
105
...
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
...
439
440
441
442
443
444
445

446
447
448
449











450

451
452
453
454
455
456
457
458
459
460
461
462
463
464
...
497
498
499
500
501
502
503
504
505
506
507
508
509
510





511
512
513
514
515
516
517
...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
...
564
565
566
567
568
569
570



571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
...
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
...
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
...
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
...
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
...
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
...
872
873
874
875
876
877
878
879

880
881
882
883
884
885
886
887
888
889
890
891
892
893
894

#include "btInt.h"

#include <string.h>
#include <assert.h>
#include <stdio.h>




/* By default auto-checkpoint is 1000 */
#define BT_DEFAULT_AUTOCKPT 1000

typedef struct BtPageHash BtPageHash;


typedef struct BtSavepoint BtSavepoint;
typedef struct BtSavepage BtSavepage;

/*
** Hash table for page references currently in memory. Manipulated using
** the following functions:
................................................................................
};

/*
** Candidate values for BtPage.flags
*/
#define BT_PAGE_DIRTY 0x0001      /* Set for pages in BtPager.pDirty list */










/*
** Pager object.
**
** nAutoCkpt:
**   If a transaction is committed and there are this many frames in the
**   log file, automatically run a checkpoint operation.
*/
................................................................................
  char *zFile;                    /* Database file name */
  int nFile;                      /* Length of string zFile in bytes */
  BtPageHash hash;                /* Hash table */
  BtPage *pDirty;                 /* List of all dirty pages */
  int nTotalRef;                  /* Total number of outstanding page refs */
  int nAutoCkpt;                  /* Auto-checkpoint when log is this large */
  int bDoAutoCkpt;                /* Do auto-checkpoint after next unlock */

  BtSavepoint *aSavepoint;        /* Savepoint array */
  int nSavepoint;                 /* Number of entries in aSavepoint array */
  int pgsz;                       /* Page size in bytes */
  int nPg;                        /* Number of pages currently in db file */
};


/**************************************************************************
** Interface to BtPageHash object.
*/

................................................................................
    if( pSavepage==0 ){
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }

    if( rc==SQLITE4_OK && (1 || (pPg->flags & BT_PAGE_DIRTY)) ){
      pSavepage->aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->pgsz);
      if( pSavepage->aData==0 ){
        sqlite4_free(p->btl.pEnv, pSavepage);
        rc = btErrorBkpt(SQLITE4_NOMEM);
      }else{
        memcpy(pSavepage->aData, pPg->aData, p->pgsz);
      }
    }

    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;

................................................................................
}

/*
** Open a read-transaction.
*/
static int btOpenReadTransaction(BtPager *p){
  int rc;


  assert( p->iTransactionLevel==0 );
  assert( p->btl.pFd );












  rc = sqlite4BtLogSnapshotOpen(p->pLog);


  if( rc==SQLITE4_OK ){
    /* If the read transaction was successfully opened, the transaction 
    ** level is now 1.  */
    p->iTransactionLevel = 1;
    p->pgsz = sqlite4BtLogPagesize(p->pLog);
    p->nPg = sqlite4BtLogPagecount(p->pLog);
  }
  return rc;
}

static int btOpenWriteTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==1 );
................................................................................
  btCloseSavepoints(p, 2);

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      int nPg = ((pNext==0) ? p->nPg : 0);
      rc = sqlite4BtLogWrite(p->pLog, pPg->pgno, pPg->aData, nPg);
    }
  }
  p->pDirty = 0;
  sqlite4BtLogSnapshotEndWrite(p->pLog);






  if( p->nAutoCkpt && sqlite4BtLogSize(p->pLog)>=p->nAutoCkpt ){
    p->bDoAutoCkpt = 1;
  }

  return rc;
}

................................................................................
  ** data was loaded successfully. If SQLITE4_NOTFOUND, the required page
  ** is not present in the log and should be loaded from the database
  ** file. Any other error code is returned to the caller.  */
  rc = sqlite4BtLogRead(p->pLog, pPg->pgno, pPg->aData);

  /* If necessary, load data from the database file. */
  if( rc==SQLITE4_NOTFOUND ){
    i64 iOff = (i64)p->pgsz * (i64)(pPg->pgno-1);
    rc = p->btl.pVfs->xRead(p->btl.pFd, iOff, pPg->aData, p->pgsz);
  }

  return rc;
}

static int btAllocatePage(BtPager *p, BtPage **ppPg){
  int rc;                         /* Return code */
  BtPage *pRet;
  u8 *aData;

  pRet = (BtPage*)sqlite4_malloc(p->btl.pEnv, sizeof(BtPage));
  aData = (u8*)sqlite4_malloc(p->btl.pEnv, p->pgsz);

  if( pRet && aData ){
    memset(pRet, 0, sizeof(BtPage));
    pRet->aData = aData;
    pRet->pPager = p;
    rc = SQLITE4_OK;
  }else{
................................................................................
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=2 );
  btCloseSavepoints(p, 2);




  /* Loop through all dirty pages in memory. Discard those with nRef==0.
  ** Reload data from disk for any others.  */
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( pPg->nRef==0 ){
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  p->pDirty = 0;

  return rc;
}
................................................................................
        pNext = pSavepage->pNext;

        /* Detach the BtSavepage from its BtPage object */
        assert( pSavepage==pPg->pSavepage );
        pPg->pSavepage = pSavepage->pNextSavepage;

        /* Restore the page data */
        memcpy(pPg->aData, pSavepage->aData, p->pgsz);

        /* Free associated memory allocations */
        assert( pSavepage->aData ); /* temp */
        sqlite4_free(p->btl.pEnv, pSavepage->aData);
        sqlite4_free(p->btl.pEnv, pSavepage);
      }

................................................................................
        rc = btRollbackSavepoints(p, iLevel);
      }
    }

    p->iTransactionLevel = iLevel;
    if( iLevel==0 ){
      int rc2 = btCloseReadTransaction(p);
      if( rc==SQLITE4_OK ) rc = rc2;
    }
  }

  return rc;
}

int sqlite4BtPagerRevert(BtPager *p, int iLevel){
................................................................................
}

/*
** Query for the database page size. Requires an open read transaction.
*/
int sqlite4BtPagerPagesize(BtPager *p){
  /* assert( p->iTransactionLevel>=1 && p->btl.pFd ); */
  return (int)p->pgsz;
}

/* 
** Query for the root page number. Requires an open read transaction.
*/
u32 sqlite4BtPagerRootpgno(BtPager *p){
  assert( p->iTransactionLevel>=1 && p->btl.pFd );
................................................................................
  pRet = btHashSearch(p, pgno);

  /* If the page is not in the cache, load it from disk */
  if( pRet==0 ){
    rc = btAllocatePage(p, &pRet);
    if( rc==SQLITE4_OK ){
      pRet->pgno = pgno;
      if( pgno<=p->nPg ){
        rc = btLoadPageData(p, pRet);
      }else{
        memset(pRet->aData, 0, p->pgsz);
      }

      if( rc==SQLITE4_OK ){
        rc = btHashAdd(p, pRet);
      }
      if( rc!=SQLITE4_OK ){
        btFreePage(p, pRet);
        pRet = 0;
      }
    }
    sqlite4BtDebugReadPage(&p->btl, pgno, pRet->aData, p->pgsz);
  }

  assert( (pRet!=0)==(rc==SQLITE4_OK) );
  if( rc==SQLITE4_OK ){
    p->nTotalRef++;
    pRet->nRef++;
  }
................................................................................

/*
** Allocate a new database page and return a writable reference to it.
*/
int sqlite4BtPageAllocate(BtPager *p, BtPage **ppPg){
  BtPage *pPg = 0;
  int rc;
  u32 pgno = p->nPg+1;

  rc = sqlite4BtPageGet(p, pgno, &pPg);
  if( rc==SQLITE4_OK ){
    rc = sqlite4BtPageWrite(pPg);
    if( rc!=SQLITE4_OK ){
      sqlite4BtPageRelease(pPg);
      pPg = 0;
    }else{
      p->nPg = pgno;
    }
  }

#ifdef BT_STDERR_DEBUG
  fprintf(stderr, "allocated page %d\n", pgno);
#endif

................................................................................
}

/* 
** Read the schema cookie value. Requires an open read-transaction.
*/
int sqlite4BtPagerSetCookie(BtPager *p, u32 iVal){
  assert( p->iTransactionLevel>=2 );
  return sqlite4BtLogSetCookie(p->pLog, iVal);

}

/* 
** Set the schema cookie value. Requires an open write-transaction.
*/
int sqlite4BtPagerGetCookie(BtPager *p, u32 *piVal){
  assert( p->iTransactionLevel>=1 );
  *piVal = sqlite4BtLogCookie(p->pLog);
  return SQLITE4_OK;
}

const char *sqlite4BtPagerFilename(BtPager *p, int ePagerfile){
  const char *zTail;

  switch( ePagerfile ){

Changes to test/test_thread0.c.

9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the the SQLite library in a multithreaded environment.
*/
#include "sqliteInt.h"
#include "tcl.h"
#if SQLITE4_OS_UNIX && SQLITE4_THREADSAFE
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <ctype.h>

/*







|







9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Code for testing the the SQLite library in a multithreaded environment.
*/
#include "sqliteInt.h"
#include "tcl.h"
#if 0 && SQLITE4_OS_UNIX && SQLITE4_THREADSAFE
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sched.h>
#include <ctype.h>

/*

Changes to test/tester.tcl.

771
772
773
774
775
776
777


778
779
780
781
782
783
784
    puts "in your TCL build."
    puts "******************************************************************"
  }
  if {$::cmdlinearg(binarylog)} {
    vfslog finalize binarylog
  }
  kvwrap uninstall



  set nOut  [test_mm_stat out]
  set nUnit [test_mm_stat units]
  if {$nOut!=0 || $nUnit!=0} {
    puts "Unfreed memory: $nOut bytes in $nUnit allocations"
  } else {
    puts "All memory allocations freed - no leaks"







>
>







771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
    puts "in your TCL build."
    puts "******************************************************************"
  }
  if {$::cmdlinearg(binarylog)} {
    vfslog finalize binarylog
  }
  kvwrap uninstall

  sqlite4_shutdown

  set nOut  [test_mm_stat out]
  set nUnit [test_mm_stat units]
  if {$nOut!=0 || $nUnit!=0} {
    puts "Unfreed memory: $nOut bytes in $nUnit allocations"
  } else {
    puts "All memory allocations freed - no leaks"