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

Overview
Comment:Continue adding logging to the btree module. Still does not work.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: ae62a34eb76721eb80c3fcaba399b8a92d749123
User & Date: dan 2013-10-21 20:04:48.842
Context
2013-10-22
16:20
More logging code. check-in: 94016fe5e8 user: dan tags: trunk
2013-10-21
20:04
Continue adding logging to the btree module. Still does not work. check-in: ae62a34eb7 user: dan tags: trunk
2013-10-19
20:24
Begin to add logging to btree database. check-in: ca59333dcd user: dan tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/btInt.h.
95
96
97
98
99
100
101






102
103
104
105
106
107
108
int sqlite4BtPagerRefcount(BtPager*);

/* 
** Read/write the schema cookie value.
*/
int sqlite4BtPagerSetCookie(BtPager*, u32 iVal);
int sqlite4BtPagerGetCookie(BtPager*, u32 *piVal);






/*
** End of bt_pager.c interface.
*************************************************************************/

/*************************************************************************
** File-system interface.
*/







>
>
>
>
>
>







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
int sqlite4BtPagerRefcount(BtPager*);

/* 
** Read/write the schema cookie value.
*/
int sqlite4BtPagerSetCookie(BtPager*, u32 iVal);
int sqlite4BtPagerGetCookie(BtPager*, u32 *piVal);

/*
** Return a pointer to a buffer containing the name of the pager log file.
*/
const char *sqlite4BtPagerWalFile(BtPager*);

/*
** End of bt_pager.c interface.
*************************************************************************/

/*************************************************************************
** File-system interface.
*/
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
** End of bt_varint.c interface.
*************************************************************************/

/*************************************************************************
** Interface to bt_log.c functionality.
*/
typedef struct BtLog BtLog;
int sqlite4BtLogOpen(BtPager*, BtLog**, int bRecover);
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*);







|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
** End of bt_varint.c interface.
*************************************************************************/

/*************************************************************************
** 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*);
211
212
213
214
215
216
217




218
219
220
221
222
223
224
225
226
227
228
int sqlite4BtLockReader(BtLock*, u32 *aLog, u32 *aLock);
int sqlite4BtLockReaderUnlock(BtLock*);
int sqlite4BtLockReaderMin(BtLock*, u32 *aLog, u32 *aLock, u32 *piMinFrame);

/* Obtain and release CHECKPOINTER lock */
int sqlite4BtLockCkpt(BtLock*);
int sqlite4BtLockCkptUnlock(BtLock*);




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


#ifdef NDEBUG
# define btErrorBkpt(x) x
#else
int btErrorBkpt(int rc);
#endif








>
>
>
>











217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
int sqlite4BtLockReader(BtLock*, u32 *aLog, u32 *aLock);
int sqlite4BtLockReaderUnlock(BtLock*);
int sqlite4BtLockReaderMin(BtLock*, u32 *aLog, u32 *aLock, u32 *piMinFrame);

/* Obtain and release CHECKPOINTER lock */
int sqlite4BtLockCkpt(BtLock*);
int sqlite4BtLockCkptUnlock(BtLock*);

/* 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

Changes to src/bt_lock.c.
14
15
16
17
18
19
20










21
22
23
24















25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include "btInt.h"

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












int sqlite4BtLockConnect(BtLock *p, int (*xRecover)(BtLock*)){
  return xRecover(p);
}
















int sqlite4BtLockDisconnect(
  BtLock *p,                      /* Locker handle */
  int (*xCkpt)(BtLock*),          /* Callback to checkpoint database */
  int (*xDel)(BtLock*)            /* Callback to delete wal+shm files */
){
  int rc;                         /* Return code */

  rc = xCkpt(p);
  if( rc==SQLITE4_OK ){
    rc = xDel(p);
  }

  return rc;
}

int sqlite4BtLockCheckpoint(BtLock *p, int (*xCkpt)(BtLock*)){
  return xCkpt(p);
}








>
>
>
>
>
>
>
>
>
>




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






<




<







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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
58
59

60
61
62
63
64
65
66
#include "btInt.h"

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




/*
** Connect to the database as a read/write connection. If recovery
** is required (i.e. if this is the first connection to the db), invoke 
** the xRecover() method.
**
** Return SQLITE4_OK if successful, or an SQLite4 error code if an
** error occurs.
*/
int sqlite4BtLockConnect(BtLock *p, int (*xRecover)(BtLock*)){
  return xRecover(p);
}

/*
** Disconnect the read/write connection passed as the first argument
** from the database.
**
** If a checkpoint is required (i.e. if this is the last read/write
** connection to the database), this function invokes xCkpt() to
** request it. If the wal and shm files should be deleted from the
** file-system (i.e. if there are no read/write or read-only connections
** remaining), the xDel() callback is invoked as well.
**
** Return SQLITE4_OK if successful, or an SQLite4 error code if an
** error occurs. Even if an error occurs, the connection should be
** considered to be disconnected. The error code merely indicates
** that an error occurred while checkpointing or deleting the log file.
*/
int sqlite4BtLockDisconnect(
  BtLock *p,                      /* Locker handle */
  int (*xCkpt)(BtLock*),          /* Callback to checkpoint database */
  int (*xDel)(BtLock*)            /* Callback to delete wal+shm files */
){
  int rc;                         /* Return code */

  rc = xCkpt(p);
  if( rc==SQLITE4_OK ){
    rc = xDel(p);
  }

  return rc;
}

int sqlite4BtLockCheckpoint(BtLock *p, int (*xCkpt)(BtLock*)){
  return xCkpt(p);
}

51
52
53
54
55
56
57
58
59
60


61




/* Obtain a READER lock. 
**
** Argument aLog points to an array of 6 frame addresses. These are the 
** first and last frames in each of log regions A, B and C. Argument 
** aLock points to the array of read-lock slots in shared memory.
*/
int sqlite4BtLockReader(BtLock*, u32 *aLog, u32 *aLock){
}














|


>
>
|
>
>
>
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89

/* Obtain a READER lock. 
**
** Argument aLog points to an array of 6 frame addresses. These are the 
** first and last frames in each of log regions A, B and C. Argument 
** aLock points to the array of read-lock slots in shared memory.
*/
int sqlite4BtLockReader(BtLock *pLock, u32 *aLog, u32 *aLock){
}

int sqlite4BtLockShmMap(BtLock *pLock, int iChunk, int nByte, u8 **ppOut){
  return SQLITE4_OK;
}



Changes to src/bt_log.c.
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
};

/*
** WAL Frame header. All fields are stored in big-endian order.
*/
struct BtFrameHdr {
  u32 pgno;                       /* Page number of this frame */

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

/*
** Shared memory header. Shared memory begins with two copies of
** this structure. All fields are stored in machine byte-order.
*/
struct BtShmHdr {
  u32 aLog[6];                    /* First/last frames for each log region */



};

/*
** A single instance of this structure follows the two BtShmHdr structures 
** in shared memory.
*/
struct BtCkptHdr {
  u32 iFirstRead;                 /* First uncheckpointed frame */
  u32 iFirstRecover;              /* First recovery frame */
};


struct BtShm {
  BtShmHdr hdr1;
  BtShmHdr hdr2;
  BtCkptHdr ckpt;
  u32 aReadlock[BT_NREADER];

















































































































































































































































































































































































}









>









>
>
>











<





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



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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
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
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
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
};

/*
** WAL Frame header. All fields are stored in big-endian order.
*/
struct BtFrameHdr {
  u32 pgno;                       /* Page number of this frame */
  u32 ctrl;                       /* Next frame pointer and commit bit */
  u32 aCksum[2];                  /* Frame checksum */
};

/*
** Shared memory header. Shared memory begins with two copies of
** this structure. All fields are stored in machine byte-order.
*/
struct BtShmHdr {
  u32 aLog[6];                    /* First/last frames for each log region */
  int nSector;                    /* Sector size assumed for WAL file */
  u32 aFrameCksum[2];             /* Checksum of previous frame */
  u32 aCksum[2];                  /* Object checksum */
};

/*
** A single instance of this structure follows the two BtShmHdr structures 
** in shared memory.
*/
struct BtCkptHdr {
  u32 iFirstRead;                 /* First uncheckpointed frame */
  u32 iFirstRecover;              /* First recovery frame */
};


struct BtShm {
  BtShmHdr hdr1;
  BtShmHdr hdr2;
  BtCkptHdr ckpt;
  u32 aReadlock[BT_NREADER];
};

/* 
** Log handle used by bt_pager.c to access functionality implemented by
** this module. 
*/
struct BtLog {
  BtLock *pLock;                  /* Lock object associated with this log */
  bt_file *pFd;                   /* File handle open on WAL file */
  BtShmHdr snapshot;              /* Current snapshot of shm-header */

  int nShm;                       /* Size of apShm[] array */
  u8 **apShm;                     /* Array of mapped shared-memory blocks */
};

typedef u16 ht_slot;

/*
** Number of entries in each hash table bar the first. 
*/
#define HASHTABLE_NFRAME (BT_SHM_CHUNK_SIZE/(sizeof(u32) + 4*sizeof(ht_slot))) 

/* 
** Number of entries in first hash table. The first hash table is 
** smaller than the others as space for the BtShm structure is carved
** from the start of it. The two hash-slot arrays remain the same size, 
** but the number of entries in the page-number array is reduced to the
** value defined below.
*/
#define HASHTABLE_NFRAME_ONE (HASHTABLE_NFRAME - (sizeof(BtShm)/sizeof(u32)))

/*
** Number of slots in hash table.
*/
#define HASHTABLE_NSLOT (HASHTABLE_NFRAME * 2)

/*
** Used to calculate hash keys.
*/
#define HASHTABLE_KEY_MUL 383


/*
** The argument to this macro must be of type u32. On a little-endian
** architecture, it returns the u32 value that results from interpreting
** the 4 bytes as a big-endian value. On a big-endian architecture, it
** returns the value that would be produced by intepreting the 4 bytes
** 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.
**
** nByte must be a positive multiple of 8.
*/
static void btLogChecksum(
  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 */
){
  u32 s1, s2;
  u32 *aData = (u32 *)a;
  u32 *aEnd = (u32 *)&a[nByte];

  if( aIn ){
    s1 = aIn[0];
    s2 = aIn[1];
  }else{
    s1 = s2 = 0;
  }

  assert( nByte>=8 );
  assert( (nByte&0x00000007)==0 );

  if( nativeCksum ){
    do {
      s1 += *aData++ + s2;
      s2 += *aData++ + s1;
    }while( aData<aEnd );
  }else{
    do {
      s1 += BYTESWAP32(aData[0]) + s2;
      s2 += BYTESWAP32(aData[1]) + s1;
      aData += 2;
    }while( aData<aEnd );
  }

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

/*
** Open the log file for pager pPager. If successful, return the BtLog* 
** handle via output variable *ppLog. If parameter bRecover is true, then
** also run database recovery before returning. In this case, the caller
** has already obtained the required locks.
*/
int sqlite4BtLogOpen(BtPager *pPager, int bRecover, BtLog **ppLog){
  BtLock *pLock = (BtLock*)pPager;
  bt_env *pVfs = pLock->pVfs;
  sqlite4_env *pEnv = pLock->pEnv;
  const char *zWal;               /* Name of log file to open */
  BtLog *pLog;                    /* Log handle to return */

  pLog = sqlite4_malloc(pEnv, sizeof(BtLog));
  if( pLog==0 ){
    rc = SQLITE4_NOMEM;
    goto open_out;
  }
  memset(pLog, 0, sizeof(BtLog));
  pLog->pPager = pPager;

  zWal = sqlite4BtPagerWalFile(BtPager*);
  rc = pVfs->xOpen(pEnv, pVfs, zWal, 0, &pLog->pFd);

  if( rc==SQLITE4_OK && bRecover ){
    /* TODO: Run recovery... */
  }

 open_out:
  if( rc!=SQLITE4_OK ){
    sqlite4_free(pEnv, pLog);
    pLog = 0;
  }
  *ppLog = pLog;
  return rc;
}

/*
** Close the log file handle BtLog*. 
*/
int sqlite4BtLogClose(BtLog *pLog){
  sqlite4_env *pEnv = pLock->pEnv;
  bt_env *pVfs = pLock->pVfs;

  pVfs->xClose(pLock->pFd);
  sqlite4_free(pEnv, pLog);

  return SQLITE4_OK;
}

/*
** Attempt to read data for page pgno from the log file. If successful,
** the data is written into buffer aData[] (which must be at least as
** large as a database page). In this case SQLITE4_OK is returned.
**
** If the log does not contain any version of page pgno, SQLITE4_NOTFOUND
** is returned and the contents of buffer aData[] are not modified.
**
** If any other error occurs, an SQLite4 error code is returned. The final
** state of buffer aData[] is undefined in this case.
*/
int sqlite4BtLogRead(BtLog *pLog, u32 pgno, u8 *aData){
}

/*
** Return true if log is completely empty (as it is if a file zero bytes
** in size has been opened or created).
*/
static int btLogIsEmpty(BtLog *pLog){
  return (pLog->snapshot.aLog[4]==0);
}

static int btLogWriteData(BtLog *pLog, i64 iOff, u8 *aData, int nData){
  bt_env *pVfs = pLog->pLock->pVfs;
  return pVfs->xWrite(pLog->pFd, iOff, aData, nData);
}

static int btLogWriteHeader(BtLog *pLog, int iHdr, BtWalHdr *pHdr){
  int rc;                         /* Return code */
  i64 iOff;                       /* File offset to write to */
  assert( iHdr==0 || iHdr==1 );

  /* Calculate a checksum for the header */
  btLogChecksum(1, pHdr, offsetof(BtWalHdr, aCksum), 0, pHdr->aCksum);

  /* Write the object to disk */
  iOff = iHdr * pLog->snapshot.nSector;
  rc = btLogWriteData(pLog, iOff, pHdr, sizeof(BtWalHdr));

  return rc;
}

static int btLogMapShm(BtLock *pLog, int iChunk){
  int rc = SQLITE4_OK;

  if( pLog->nShm<=iChunk ){
    u8 **apNew;
    int nNew = iChunk+1;

    apNew = (u8**)sqlite4_malloc(pLog->pEnv, sizeof(u8*)*nNew);
    if( apNew==0 ) return btErrorBkpt(SQLITE4_NOMEM);
    memcpy(apNew, pLog->apShm, sizeof(u8*)*pLog->nShm);
    memset(&apNew[pLog->nShm], 0, (nNew-pLog->nShm) * sizeof(u8*));
    pLog->nShm = nNew;
    pLog->apShm = apNew;
  }

  if( pLog->apShm[iChunk]==0 ){
    u8 **pp = &pLog->apShm[iChunk];
    rc = sqlite4BtLockShmMap(pLog->pLock, iChunk, BT_SHM_CHUNK_SIZE, pp);
  }

  return rc;
}

/*
** Locate the iHash'th hash table in shared memory. Return it.
*/
static int btLogFindHash(
  BtLog *pLog,                    /* Log handle */
  int iHash,                      /* Hash table (numbered from 0) to find */
  volatile ht_slot **paHash,      /* OUT: Pointer to hash slots */
  volatile u32 **paPgno,          /* OUT: Pointer to page number array */
  u32 *piZero                     /* OUT: Frame associated with *paPgno[0] */
){
}

static int btLogFrameHash(BtLock *pLog, u32 iFrame){
}

/*
** Return a hash key for page number pgno.
*/
static int btLogHashKey(BtLog *pLog, u32 pgno){
  assert( pgno>=1 );
  return ((pgno * HASHTABLE_KEY_MUL) % HASHTABLE_NSLOT);
}
static int btLogHashNext(BtLog *pLog, int iSlot){
  return ((iSlot + 1) % HASHTABLE_NSLOT);
}

/*
** Add an entry mapping database page pgno to log frame iFrame to the
** the shared hash table. Return SQLITE4_OK if successful, or an SQLite4
** error code if an error occurs.
*/
static int btLogHashInsert(BtLog *pLog, u32 pgno, u32 iFrame){
  int rc = SQLITE4_OK;

  assert( iFrame>=1 && pgno>=1 );
  assert( iFrame==(pLog->snapshot.aLog[5]+1) );

  /* Find the required hash table */
  iHash = btLogFrameHash(pLog, iFrame);
  rc = btLogFindHash(pLog, iHash, &aHash, &aPgno, &iZero);

  /* Update the hash table */
  if( rc==SQLITE4_OK ){
    int nCollide = HASHTABLE_NSLOT*2;
    int iKey = btLogHashKey(pLog, pgno);
    aPgno[iFrame-iZero] = pgno;

    for(iSlot=btLogHashKey(pLog,pgno); ; iSlot=btLogHashNext(pLog, iSlot)){
      if( aHash[iSlot]==0 ){
        aHash[iSlot] = (iFrame-iZero+1);
        break;
      }
      if( (nCollide--)==0 ) return btErrorBkpt(SQLITE4_CORRUPT);
    }
  }

  return rc;
}

static int btLogUpdateSharedHdr(BtLog *pLog){
  bt_env *pVfs = pLog->pLock->pVfs;
  BtShmHdr *p = &pLog->snapshot;
  BtShm *pShm = btLogShm(pLog);

  /* Calculate a checksum for the private snapshot object. */
  btLogChecksum(1, p, offset(BtShmHdr, aCksum), 0, p->aCksum);

  /* Update the shared object. */
  pVfs->xShmBarrier(pLog->pFd);
  memcpy(&pShm->hdr1, p, sizeof(BtShmHdr));
  pVfs->xShmBarrier(pLog->pFd);
  memcpy(&pShm->hdr2, p, sizeof(BtShmHdr));

  return SQLITE4_OK;
}

/*
** Write a frame to the log file.
*/
int sqlite4BtLogWrite(BtLog *pLog, u32 pgno, u8 *aData, int bCommit){
  int rc = SQLITE4_OK;
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  BtFrameHdr frame;               /* Header for new frame */
  u32 *aFrameCksum;               /* Pointer to cksum of previous frame */

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

    hdr.iMagic = BT_WAL_MAGIC;
    hdr.iVersion = BT_WAL_VERSION;
    hdr.nSector = pLog->snapshot.nSector;
    hdr.iSalt1 = 22;
    hdr.iSalt2 = 23;
    hdr.iFirstFrame = 1;

    rc = btLogWriteHeader(pLog, 0, &hdr);
    if( rc!=SQLITE4_OK ) return rc;
  }

  /* Figure out where exactly to write the new data */
  iFrame = pLog->snapshot.aLog[5] + 1;
  iOff = btLogFrameOffset(pLog, iFrame);

  /* Populate the frame header object. */
  memset(&frame, 0, sizeof(frame));
  frame.pgno = pgno;
  frame.ctrl = (bCommit ? 0x80000000 : 0x00000000);
  aFrameCksum = pLog->snapshot.aFrameCksum;
  btLogChecksum(1, &frame, offsetof(aCksum), aFrameCksum, frame.aCksum);
  btLogChecksum(1, aData, pgsz, frame.aCksum, frame.aCksum);

  /* Write the header and page record to the log file. */
  rc = btLogWriteData(pLog, iOff, &frame, sizeof(frame));
  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 ){
    rc = btLogHashInsert(pLog, pgno, iFrame);
  }

  /* Update the private copy of the shm-header */
  if( rc==SQLITE4_OK ){
    pLog->snapshot.aLog[5] = iFrame;
    if( btLogIsEmpty(pLog) ){
      assert( iFrame==1 );
      pLog->snapshot.aLog[4] = iFrame;
    }
    memcpy(pLog->snapshot.aFrameCksum, frame.aCksum, sizeof(frame.aCksum));
  }

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

  return rc;
}

int sqlite4BtLogSnapshotOpen(BtLog *pLog){
  return SQLITE4_OK;
}

int sqlite4BtLogSnapshotClose(BtLog *pLog){
  return SQLITE4_OK;
}

int sqlite4BtLogSnapshotWritable(BtLog *pLog){
  return 1;
}


Changes to src/bt_pager.c.
232
233
234
235
236
237
238






















239
240
241
242
243
244
245

/*
** Return a pointer to the nExtra bytes of space allocated by PagerNew().
*/
void *sqlite4BtPagerExtra(BtPager *p){
  return (void*)&p[1];
}























/*
** Attach a database file to a pager object.
**
** This function may only be called once for each BtPager object. If it
** fails, the BtPager is rendered unusable (and must be closed by the
** caller using BtPagerClose()).







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







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

/*
** Return a pointer to the nExtra bytes of space allocated by PagerNew().
*/
void *sqlite4BtPagerExtra(BtPager *p){
  return (void*)&p[1];
}

/*
** Open the logging module and run recovery on the database. This is 
** called during connection by the bt_lock module.
*/
static int btRecover(BtLock *pLock){
  BtPager *p = (BtPager*)pLock;
  int rc;
  rc = sqlite4BtLogOpen(p, 1, &p->pLog);
  return rc;
}

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

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

/*
** Attach a database file to a pager object.
**
** This function may only be called once for each BtPager object. If it
** fails, the BtPager is rendered unusable (and must be closed by the
** caller using BtPagerClose()).
256
257
258
259
260
261
262









263
264
265
266
267
268
269
270

  rc = pVfs->xFullpath(pEnv, pVfs, zFilename, &p->zFile);
  if( rc==SQLITE4_OK ){
    p->nFile = strlen(p->zFile);
    rc = pVfs->xOpen(pEnv, pVfs, zFilename, flags, &p->btl.pFd);
  }










  assert( (rc==SQLITE4_OK)==(p->btl.pFd!=0) );
  return rc;
}

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







>
>
>
>
>
>
>
>
>
|







278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301

  rc = pVfs->xFullpath(pEnv, pVfs, zFilename, &p->zFile);
  if( rc==SQLITE4_OK ){
    p->nFile = strlen(p->zFile);
    rc = pVfs->xOpen(pEnv, pVfs, zFilename, flags, &p->btl.pFd);
  }

  /* If the database file was successfully opened, connect to the
  ** database and open the database logger. */
  if( rc==SQLITE4_OK ){
    rc = sqlite4BtLockConnect((BtLock*)p, btRecover);
    if( rc==SQLITE4_OK && p->pLog==0 ){
      rc = sqlite4BtLogOpen(pPager, 0, &p->pLog);
    }
  }

  assert( (rc==SQLITE4_OK)==(p->btl.pFd!=0 && p->pLog!=0) );
  return rc;
}

/*
** Open a read-transaction.
*/
static int btOpenReadTransaction(BtPager *p){
602
603
604
605
606
607
608






609
610
611
612
613
614
615
** 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;
}







#ifndef NDEBUG
int sqlite4BtPagerRefcount(BtPager *p){
  return p->nTotalRef;
}
#endif








>
>
>
>
>
>







633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
** 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 *sqlite4BtPagerWalFile(BtPager *p){
  const char *zTail = "-wal";
  memcpy(&p->zFile[p->nFile], zTail, strlen(zTail)+1);
  return p->zFile;
}

#ifndef NDEBUG
int sqlite4BtPagerRefcount(BtPager *p){
  return p->nTotalRef;
}
#endif