SQLite4
Check-in [a4c634a7d6]
Not logged in

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

Overview
Comment:Add new file bt_lock.c. Currently contains stubs only.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: a4c634a7d64e09423e685c456887481d9f73bbcc
User & Date: dan 2013-10-18 17:07:33
Context
2013-10-19
05:50
Add missing bt_varint.c file. check-in: e5d33eb472 user: dan tags: trunk
2013-10-18
17:07
Add new file bt_lock.c. Currently contains stubs only. check-in: a4c634a7d6 user: dan tags: trunk
08:28
Trim overflow pages when the corresponding record is deleted from the database. check-in: 547b950db0 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to main.mk.

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
         random.o resolve.o rowset.o rtree.o select.o status.o \
         tokenize.o trigger.o \
         update.o util.o varint.o \
         vdbeapi.o vdbeaux.o vdbecodec.o vdbecursor.o \
         vdbemem.o vdbetrace.o \
         walker.o where.o utf.o

LIBOBJ += bt_unix.o bt_pager.o bt_main.o bt_varint.o kvbt.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/alter.c \
  $(TOP)/src/analyze.c \
  $(TOP)/src/attach.c \







|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
         random.o resolve.o rowset.o rtree.o select.o status.o \
         tokenize.o trigger.o \
         update.o util.o varint.o \
         vdbeapi.o vdbeaux.o vdbecodec.o vdbecursor.o \
         vdbemem.o vdbetrace.o \
         walker.o where.o utf.o

LIBOBJ += bt_unix.o bt_pager.o bt_main.o bt_varint.o kvbt.o bt_lock.o

# All of the source code files.
#
SRC = \
  $(TOP)/src/alter.c \
  $(TOP)/src/analyze.c \
  $(TOP)/src/attach.c \

Changes to src/btInt.h.

156
157
158
159
160
161
162





























163
164
165
166
167
168
169
int sqlite4BtVarintPut64(u8 *aData, i64 iVal);
int sqlite4BtVarintGet64(const u8 *aData, i64 *piVal);
int sqlite4BtVarintLen32(int);
int sqlite4BtVarintSize(u8 c);
/*
** End of bt_varint.c interface.
*************************************************************************/






























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








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







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
int sqlite4BtVarintPut64(u8 *aData, i64 iVal);
int sqlite4BtVarintGet64(const u8 *aData, i64 *piVal);
int sqlite4BtVarintLen32(int);
int sqlite4BtVarintSize(u8 c);
/*
** End of bt_varint.c interface.
*************************************************************************/

/*************************************************************************
** Interface to bt_lock.c functionality.
*/
#define BT_LOCK_NOLOCK  0x00      /* No lock held on database */
#define BT_LOCK_READER  0x01      /* Read lock held on database */
#define BT_LOCK_WRITER  0x02      /* Write lock held on database */

typedef struct BtLock BtLock;
struct BtLock {
  /* These three are set by the bt_pager module and thereafter used by 
  ** both bt_lock and bt_pager. */
  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. */
  /* todo... */
};

int sqlite4BtLockConnect(BtLock*, int (*xRecover)(BtLock*));
int sqlite4BtLockDisconnect(BtLock*, int(*xCkpt)(BtLock*), int(*xDel)(BtLock*));
int sqlite4BtLockCheckpoint(BtLock*, int(*xCkpt)(BtLock*));

int sqlite4BtLockBegin(BtLock*, int eLock);
int sqlite4BtLockEnd(BtLock*, int eLock);
/*
** End of bt_lock.c interface.
*************************************************************************/

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

Added src/bt_lock.c.









































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
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
/*
** 2013 October 18
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
*/

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

int sqlite4BtLockBegin(BtLock *p, int eLock){
  return SQLITE4_OK;
}

int sqlite4BtLockEnd(BtLock *p, int eLock){
  return SQLITE4_OK;
}


Changes to src/bt_main.c.

28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#ifndef MIN
# define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif

#define BT_EXPENSIVE_ASSERT 0
/* #define BT_STDERR_DEBUG 1 */

struct bt_db {
  sqlite4_env *pEnv;              /* SQLite environment */
  BtPager *pPager;                /* Underlying page-based database */
  bt_cursor *pAllCsr;             /* List of all open cursors */
};







<







28
29
30
31
32
33
34

35
36
37
38
39
40
41
#ifndef MIN
# define MIN(a,b) (((a)<(b))?(a):(b))
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif


/* #define BT_STDERR_DEBUG 1 */

struct bt_db {
  sqlite4_env *pEnv;              /* SQLite environment */
  BtPager *pPager;                /* Underlying page-based database */
  bt_cursor *pAllCsr;             /* List of all open cursors */
};

Changes to src/bt_pager.c.

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
...
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
...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
...
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
...
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
...
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
...
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
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
...
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
...
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
*/
struct BtDbhdr {
  u32 pgsz;                       /* Page size in bytes */
  u32 nPg;                        /* Number of pages in database */
  u32 cookie;                     /* User cookie value */
};


/*
** Pager object.
*/
struct BtPager {
  sqlite4_env *pEnv;              /* SQLite environment */
  bt_env *pVfs;                   /* Bt environment */
  int iTransactionLevel;          /* Current transaction level (see bt.h) */
  char *zFile;                    /* Database file name */
  int nFile;                      /* Length of string zFile in bytes */
  bt_file *pFd;                   /* Database file */
  BtPageHash hash;                /* Hash table */
  BtPage *pDirty;                 /* List of all dirty pages */
  BtDbhdr dbhdr;
  int nTotalRef;
};


................................................................................
  /* If required, increase the number of buckets in the hash table. */
  if( p->hash.nEntry>=p->hash.nHash/2 ){
    int i;
    int nNew = (p->hash.nHash ? p->hash.nHash*2 : 256);
    BtPage **aNew;
    BtPage **aOld = p->hash.aHash;

    aNew = (BtPage **)sqlite4_malloc(p->pEnv, nNew*sizeof(BtPage*));
    if( aNew==0 ) return btErrorBkpt(SQLITE4_NOMEM);
    memset(aNew, 0, nNew*sizeof(BtPage*));
    for(i=0; i<p->hash.nHash; i++){
      while( aOld[i] ){
        BtPage *pShift = aOld[i];
        aOld[i] = pShift->pNextHash;
        h = hashkey(nNew, pShift->pgno);
        pShift->pNextHash = aNew[h];
        aNew[h] = pShift;
      }
    }
    p->hash.aHash = aNew;
    p->hash.nHash = nNew;
    sqlite4_free(p->pEnv, aOld);
  }

  /* Add the new entry to the hash table. */
  assert( pPg->pNextHash==0 );
  h = hashkey(p->hash.nHash, pPg->pgno);
  pPg->pNextHash = p->hash.aHash[h];
  p->hash.aHash[h] = pPg;
................................................................................
}

/*
** Remove all entries from the hash-table. And free any allocations made
** by earlier calls to btHashAdd().
*/
static void btHashClear(BtPager *p){
  sqlite4_free(p->pEnv, p->hash.aHash);
  memset(&p->hash, 0, sizeof(BtPageHash));
}

#ifndef NDEBUG
static void btHashIterate(
  BtPager *p, 
  void (*xCall)(void*, BtPage*),
................................................................................
  int nByte;

  nByte = sizeof(BtPager) + nExtra;
  p = (BtPager*)sqlite4_malloc(pEnv, nByte);
  if( !p ) return btErrorBkpt(SQLITE4_NOMEM); 
  memset(p, 0, nByte);

  p->pEnv = pEnv;
  p->pVfs = sqlite4BtEnvDefault();
  *pp = p;
  return SQLITE4_OK;
}

static void btFreePage(BtPager *p, BtPage *pPg){
  if( pPg ){
    sqlite4_free(p->pEnv, pPg->aData);
    sqlite4_free(p->pEnv, pPg);
  }
}

/*
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int i;
  if( p->pFd ){
    p->pVfs->xClose(p->pFd);
  }

  for(i=0; i<p->hash.nHash; i++){
    BtPage *pPg;
    BtPage *pNext;
    for(pPg=p->hash.aHash[i]; pPg; pPg=pNext){
      pNext = pPg->pNextHash;
      btFreePage(p, pPg);
    }
  }

  btHashClear(p);
  sqlite4_free(p->pEnv, p->zFile);
  sqlite4_free(p->pEnv, p);
  return SQLITE4_OK;
}

/*
** Return a pointer to the nExtra bytes of space allocated by PagerNew().
*/
void *sqlite4BtPagerExtra(BtPager *p){
................................................................................
** caller using BtPagerClose()).
**
** If successful, SQLITE4_OK is returned. Otherwise, an SQLite error code.
*/
int sqlite4BtPagerOpen(BtPager *p, const char *zFilename){
  int rc;                         /* Return code */
  int flags = 0;                  /* Flags to pass to xOpen() */
  sqlite4_env *pEnv = p->pEnv;
  bt_env *pVfs = p->pVfs;

  assert( p->pFd==0 && p->zFile==0 );

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

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

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

  rc = p->pVfs->xSize(p->pFd, &nByte);
  if( rc==SQLITE4_OK && nByte>0 ){
    rc = p->pVfs->xRead(p->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 ){
................................................................................

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      i64 iOff = (i64)p->dbhdr.pgsz * (i64)(pPg->pgno-1);
      rc = p->pVfs->xWrite(p->pFd, iOff, pPg->aData, p->dbhdr.pgsz);
    }
  }
  p->pDirty = 0;

  if( rc==SQLITE4_OK ){
    rc = p->pVfs->xWrite(p->pFd, 0, (void*)&p->dbhdr, sizeof(BtDbhdr));
  }

  return rc;
}

static int btLoadPageData(BtPager *p, BtPage *pPg){
  i64 iOff = (i64)p->dbhdr.pgsz * (i64)(pPg->pgno-1);
  int rc = p->pVfs->xRead(p->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->pEnv, sizeof(BtPage));
  aData = (u8*)sqlite4_malloc(p->pEnv, p->dbhdr.pgsz);

  if( pRet && aData ){
    memset(pRet, 0, sizeof(BtPage));
    pRet->aData = aData;
    pRet->pPager = p;
    rc = SQLITE4_OK;
  }else{
    sqlite4_free(p->pEnv, pRet);
    sqlite4_free(p->pEnv, aData);
    rc = btErrorBkpt(SQLITE4_NOMEM);
    pRet = 0;
  }

  *ppPg = pRet;
  return rc;
}
................................................................................
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=2 );

  /* Load the old db header from disk */
  rc = p->pVfs->xRead(p->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;
................................................................................

/*
** Transactions. These methods are more or less the same as their 
** counterparts in bt.h.
*/
int sqlite4BtPagerBegin(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;
  assert( p->pFd );

  if( p->iTransactionLevel<iLevel ){
    /* Open a read transaction if one is not already open */
    if( p->iTransactionLevel==0 ){
      rc = btOpenReadTransaction(p);
    }

................................................................................

  return rc;
}

int sqlite4BtPagerCommit(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->pFd );
  if( p->iTransactionLevel>=iLevel ){
    if( p->iTransactionLevel>=2 && iLevel<2 ){
      /* Commit the main write transaction. */
      rc = btCommitTransaction(p);
    }
    p->iTransactionLevel = iLevel;
  }
  return rc;
}

int sqlite4BtPagerRollback(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->pFd );
  if( p->iTransactionLevel>=iLevel ){
    assert( iLevel<=1 );          /* TODO: Fix this! */
    if( p->iTransactionLevel>=2 ){
      rc = btRollbackTransaction(p);
    }
    p->iTransactionLevel = iLevel;
  }
................................................................................
  return rc;
}

int sqlite4BtPagerRevert(BtPager *p, int iLevel){
  int rc;
  assert( 0 );                    /* TODO: Fix this */

  assert( p->pFd );
  rc = sqlite4BtPagerRollback(p, iLevel);
  if( rc==SQLITE4_OK && iLevel>=2 && p->iTransactionLevel==iLevel ){
    /* Rollback (but do not close) transaction iLevel */
  }
  return rc;
}

................................................................................
  return p->iTransactionLevel;
}

/*
** Query for the database page size. Requires an open read transaction.
*/
int sqlite4BtPagerPagesize(BtPager *p){
  assert( p->iTransactionLevel>=1 && p->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->pFd );
  return 2;
}

/*
** Request a reference to page pgno of the database.
*/
int sqlite4BtPageGet(BtPager *p, u32 pgno, BtPage **ppPg){







<




|
<



<







 







|













|







 







|







 







|
|






|
|








|
|












|
|







 







|
|

|




|


|











|

|

|







 







|





|







|








|
|







|
|







 







|







 







|







 







|













|







 







|







 







|








|







58
59
60
61
62
63
64

65
66
67
68
69

70
71
72

73
74
75
76
77
78
79
..
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
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
...
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
...
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
...
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
...
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
...
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
...
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
...
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
*/
struct BtDbhdr {
  u32 pgsz;                       /* Page size in bytes */
  u32 nPg;                        /* Number of pages in database */
  u32 cookie;                     /* User cookie value */
};


/*
** Pager object.
*/
struct BtPager {
  BtLock btl;                     /* Variables shared with bt_lock module */

  int iTransactionLevel;          /* Current transaction level (see bt.h) */
  char *zFile;                    /* Database file name */
  int nFile;                      /* Length of string zFile in bytes */

  BtPageHash hash;                /* Hash table */
  BtPage *pDirty;                 /* List of all dirty pages */
  BtDbhdr dbhdr;
  int nTotalRef;
};


................................................................................
  /* If required, increase the number of buckets in the hash table. */
  if( p->hash.nEntry>=p->hash.nHash/2 ){
    int i;
    int nNew = (p->hash.nHash ? p->hash.nHash*2 : 256);
    BtPage **aNew;
    BtPage **aOld = p->hash.aHash;

    aNew = (BtPage **)sqlite4_malloc(p->btl.pEnv, nNew*sizeof(BtPage*));
    if( aNew==0 ) return btErrorBkpt(SQLITE4_NOMEM);
    memset(aNew, 0, nNew*sizeof(BtPage*));
    for(i=0; i<p->hash.nHash; i++){
      while( aOld[i] ){
        BtPage *pShift = aOld[i];
        aOld[i] = pShift->pNextHash;
        h = hashkey(nNew, pShift->pgno);
        pShift->pNextHash = aNew[h];
        aNew[h] = pShift;
      }
    }
    p->hash.aHash = aNew;
    p->hash.nHash = nNew;
    sqlite4_free(p->btl.pEnv, aOld);
  }

  /* Add the new entry to the hash table. */
  assert( pPg->pNextHash==0 );
  h = hashkey(p->hash.nHash, pPg->pgno);
  pPg->pNextHash = p->hash.aHash[h];
  p->hash.aHash[h] = pPg;
................................................................................
}

/*
** Remove all entries from the hash-table. And free any allocations made
** by earlier calls to btHashAdd().
*/
static void btHashClear(BtPager *p){
  sqlite4_free(p->btl.pEnv, p->hash.aHash);
  memset(&p->hash, 0, sizeof(BtPageHash));
}

#ifndef NDEBUG
static void btHashIterate(
  BtPager *p, 
  void (*xCall)(void*, BtPage*),
................................................................................
  int nByte;

  nByte = sizeof(BtPager) + nExtra;
  p = (BtPager*)sqlite4_malloc(pEnv, nByte);
  if( !p ) return btErrorBkpt(SQLITE4_NOMEM); 
  memset(p, 0, nByte);

  p->btl.pEnv = pEnv;
  p->btl.pVfs = sqlite4BtEnvDefault();
  *pp = p;
  return SQLITE4_OK;
}

static void btFreePage(BtPager *p, BtPage *pPg){
  if( pPg ){
    sqlite4_free(p->btl.pEnv, pPg->aData);
    sqlite4_free(p->btl.pEnv, pPg);
  }
}

/*
** Close a pager database handle.
*/
int sqlite4BtPagerClose(BtPager *p){
  int i;
  if( p->btl.pFd ){
    p->btl.pVfs->xClose(p->btl.pFd);
  }

  for(i=0; i<p->hash.nHash; i++){
    BtPage *pPg;
    BtPage *pNext;
    for(pPg=p->hash.aHash[i]; pPg; pPg=pNext){
      pNext = pPg->pNextHash;
      btFreePage(p, pPg);
    }
  }

  btHashClear(p);
  sqlite4_free(p->btl.pEnv, p->zFile);
  sqlite4_free(p->btl.pEnv, p);
  return SQLITE4_OK;
}

/*
** Return a pointer to the nExtra bytes of space allocated by PagerNew().
*/
void *sqlite4BtPagerExtra(BtPager *p){
................................................................................
** caller using BtPagerClose()).
**
** If successful, SQLITE4_OK is returned. Otherwise, an SQLite error code.
*/
int sqlite4BtPagerOpen(BtPager *p, const char *zFilename){
  int rc;                         /* Return code */
  int flags = 0;                  /* Flags to pass to xOpen() */
  sqlite4_env *pEnv = p->btl.pEnv;
  bt_env *pVfs = p->btl.pVfs;

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

  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){
  int rc;
  i64 nByte;                      /* Size of db file in bytes */

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

  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 ){
................................................................................

  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( rc==SQLITE4_OK ){
      i64 iOff = (i64)p->dbhdr.pgsz * (i64)(pPg->pgno-1);
      rc = p->btl.pVfs->xWrite(p->btl.pFd, iOff, pPg->aData, p->dbhdr.pgsz);
    }
  }
  p->pDirty = 0;

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

  return rc;
}

static int btLoadPageData(BtPager *p, BtPage *pPg){
  i64 iOff = (i64)p->dbhdr.pgsz * (i64)(pPg->pgno-1);
  int 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{
    sqlite4_free(p->btl.pEnv, pRet);
    sqlite4_free(p->btl.pEnv, aData);
    rc = btErrorBkpt(SQLITE4_NOMEM);
    pRet = 0;
  }

  *ppPg = pRet;
  return rc;
}
................................................................................
  int rc = SQLITE4_OK;
  BtPage *pPg;
  BtPage *pNext;

  assert( p->iTransactionLevel>=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;
................................................................................

/*
** Transactions. These methods are more or less the same as their 
** counterparts in bt.h.
*/
int sqlite4BtPagerBegin(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;
  assert( p->btl.pFd );

  if( p->iTransactionLevel<iLevel ){
    /* Open a read transaction if one is not already open */
    if( p->iTransactionLevel==0 ){
      rc = btOpenReadTransaction(p);
    }

................................................................................

  return rc;
}

int sqlite4BtPagerCommit(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->btl.pFd );
  if( p->iTransactionLevel>=iLevel ){
    if( p->iTransactionLevel>=2 && iLevel<2 ){
      /* Commit the main write transaction. */
      rc = btCommitTransaction(p);
    }
    p->iTransactionLevel = iLevel;
  }
  return rc;
}

int sqlite4BtPagerRollback(BtPager *p, int iLevel){
  int rc = SQLITE4_OK;

  assert( p->btl.pFd );
  if( p->iTransactionLevel>=iLevel ){
    assert( iLevel<=1 );          /* TODO: Fix this! */
    if( p->iTransactionLevel>=2 ){
      rc = btRollbackTransaction(p);
    }
    p->iTransactionLevel = iLevel;
  }
................................................................................
  return rc;
}

int sqlite4BtPagerRevert(BtPager *p, int iLevel){
  int rc;
  assert( 0 );                    /* TODO: Fix this */

  assert( p->btl.pFd );
  rc = sqlite4BtPagerRollback(p, iLevel);
  if( rc==SQLITE4_OK && iLevel>=2 && p->iTransactionLevel==iLevel ){
    /* Rollback (but do not close) transaction iLevel */
  }
  return rc;
}

................................................................................
  return p->iTransactionLevel;
}

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

/*
** Request a reference to page pgno of the database.
*/
int sqlite4BtPageGet(BtPager *p, u32 pgno, BtPage **ppPg){