SQLite4
Check-in [e09d4c6aa8]
Not logged in

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

Overview
Comment:Add a BT_CONTROL_INFO option to query for database header values. Add a command line interface to lsmtest to access this and other options. "lsmtest bt <filename> <option> ....".
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:e09d4c6aa83d7b88c520c9ebcadd933044ea047b
User & Date: dan 2013-11-23 18:41:56
Context
2013-11-25
20:50
Begin adding code for blind-writes. check-in: fc9cdc6ca3 user: dan tags: trunk
2013-11-23
18:41
Add a BT_CONTROL_INFO option to query for database header values. Add a command line interface to lsmtest to access this and other options. "lsmtest bt <filename> <option> ....". check-in: e09d4c6aa8 user: dan tags: trunk
2013-11-22
18:06
Fix a couple of bugs to do with recovering the database header and recycling large overflow trees. check-in: 8341d438d3 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest.h.

143
144
145
146
147
148
149



150
151
152
153
154
155
156
void testFetchCompare(TestDb *, TestDb *, void *, int, int *);

void *testMalloc(int);
void *testMallocCopy(void *pCopy, int nByte);
void *testRealloc(void *, int);
void testFree(void *);




/* testio.c */
int testVfsConfigureDb(TestDb *pDb);

/* testfunc.c */
int do_show(int nArg, char **azArg);
int do_work(int nArg, char **azArg);








>
>
>







143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
void testFetchCompare(TestDb *, TestDb *, void *, int, int *);

void *testMalloc(int);
void *testMallocCopy(void *pCopy, int nByte);
void *testRealloc(void *, int);
void testFree(void *);

/* lsmtest_bt.c */
int do_bt(int nArg, char **azArg);

/* testio.c */
int testVfsConfigureDb(TestDb *pDb);

/* testfunc.c */
int do_show(int nArg, char **azArg);
int do_work(int nArg, char **azArg);

Changes to lsm-test/lsmtest_main.c.

1466
1467
1468
1469
1470
1471
1472

1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486


1487
1488
1489
1490
1491
1492
1493
....
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538

1539
1540
  /* no-op */
}
#endif

int main(int argc, char **argv){
  struct TestFunc {
    const char *zName;

    int (*xFunc)(int, char **);
  } aTest[] = {
    {"random",      do_random_tests},
    {"writespeed",  do_writer_test},
    {"io",          st_do_io},

    {"insert",      do_insert},
    {"replay",      do_replay},

    {"speed",       do_speed_tests},
    {"speed2",      do_speed_test2},
    {"show",        st_do_show},
    {"work",        st_do_work},
    {"test",        do_test},


    {0, 0}
  };
  int rc;                         /* Return Code */
  int iFunc;                      /* Index into aTest[] */

  int nLeakAlloc = 0;             /* Allocations leaked by lsm */
  int nLeakByte = 0;              /* Bytes leaked by lsm */
................................................................................
    testPrintError("Leaked %d bytes in %d allocations (%s)\n", 
        nLeakByte, nLeakAlloc, zReport
    );
    if( rc==0 ) rc = -1;
  }
  testMallocUninstall(tdb_lsm_env());

#if 1
  lsmtest_rusage_report();
#endif

  return rc;
}







>


|
|
|

|
|

|
|
|
|
|
>
>







 







|
|
<
>


1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
....
1532
1533
1534
1535
1536
1537
1538
1539
1540

1541
1542
1543
  /* no-op */
}
#endif

int main(int argc, char **argv){
  struct TestFunc {
    const char *zName;
    int bRusageReport;
    int (*xFunc)(int, char **);
  } aTest[] = {
    {"random",      1, do_random_tests},
    {"writespeed",  1, do_writer_test},
    {"io",          1, st_do_io},

    {"insert",      1, do_insert},
    {"replay",      1, do_replay},

    {"speed",       1, do_speed_tests},
    {"speed2",      1, do_speed_test2},
    {"show",        0, st_do_show},
    {"work",        1, st_do_work},
    {"test",        1, do_test},

    {"bt",          0, do_bt},
    {0, 0}
  };
  int rc;                         /* Return Code */
  int iFunc;                      /* Index into aTest[] */

  int nLeakAlloc = 0;             /* Allocations leaked by lsm */
  int nLeakByte = 0;              /* Bytes leaked by lsm */
................................................................................
    testPrintError("Leaked %d bytes in %d allocations (%s)\n", 
        nLeakByte, nLeakAlloc, zReport
    );
    if( rc==0 ) rc = -1;
  }
  testMallocUninstall(tdb_lsm_env());

  if( aTest[iFunc].bRusageReport ){
    lsmtest_rusage_report();

  }
  return rc;
}

Changes to main.mk.

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
             $(TOP)/lsm-test/lsmtest7.c $(TOP)/lsm-test/lsmtest8.c           \
             $(TOP)/lsm-test/lsmtest9.c                                      \
             $(TOP)/lsm-test/lsmtest_datasource.c \
             $(TOP)/lsm-test/lsmtest_func.c $(TOP)/lsm-test/lsmtest_io.c     \
             $(TOP)/lsm-test/lsmtest_main.c $(TOP)/lsm-test/lsmtest_mem.c    \
             $(TOP)/lsm-test/lsmtest_tdb.c $(TOP)/lsm-test/lsmtest_tdb3.c    \
             $(TOP)/lsm-test/lsmtest_tdb4.c \
             $(TOP)/lsm-test/lsmtest_util.c 

LSMTESTHDR = $(TOP)/lsm-test/lsmtest.h $(TOP)/lsm-test/lsmtest_tdb.h

# This is the default Makefile target.  The objects listed here
# are what get build when you type just "make" with no arguments.
#
all:	sqlite4.h libsqlite4.a sqlite4$(EXE)







|







303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
             $(TOP)/lsm-test/lsmtest7.c $(TOP)/lsm-test/lsmtest8.c           \
             $(TOP)/lsm-test/lsmtest9.c                                      \
             $(TOP)/lsm-test/lsmtest_datasource.c \
             $(TOP)/lsm-test/lsmtest_func.c $(TOP)/lsm-test/lsmtest_io.c     \
             $(TOP)/lsm-test/lsmtest_main.c $(TOP)/lsm-test/lsmtest_mem.c    \
             $(TOP)/lsm-test/lsmtest_tdb.c $(TOP)/lsm-test/lsmtest_tdb3.c    \
             $(TOP)/lsm-test/lsmtest_tdb4.c \
             $(TOP)/lsm-test/lsmtest_util.c $(TOP)/lsm-test/lsmtest_bt.c

LSMTESTHDR = $(TOP)/lsm-test/lsmtest.h $(TOP)/lsm-test/lsmtest_tdb.h

# This is the default Makefile target.  The objects listed here
# are what get build when you type just "make" with no arguments.
#
all:	sqlite4.h libsqlite4.a sqlite4$(EXE)

Changes to src/bt.h.

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
**
** BT_CONTROL_MULTIPROC:
**   The third argument is interpreted as a pointer to type (int).
**
** BT_CONTROL_LOGSIZECB:
**
** BT_CONTROL_CHECKPOINT:
**   









*/
#define BT_CONTROL_INFO       7706389
#define BT_CONTROL_SETVFS     7706390
#define BT_CONTROL_GETVFS     7706391
#define BT_CONTROL_SAFETY     7706392
#define BT_CONTROL_AUTOCKPT   7706393
#define BT_CONTROL_LOGSIZE    7706394
#define BT_CONTROL_MULTIPROC  7706395
#define BT_CONTROL_LOGSIZECB  7706396
#define BT_CONTROL_CHECKPOINT 7706397


int sqlite4BtControl(bt_db*, int op, void *pArg);

#define BT_SAFETY_OFF    0
#define BT_SAFETY_NORMAL 1
#define BT_SAFETY_FULL   2

................................................................................
typedef struct bt_info bt_info;
struct bt_info {
  int eType;
  unsigned int pgno;
  sqlite4_buffer output;
};

#define BT_INFO_PAGEDUMP 1
#define BT_INFO_FILENAME 2


typedef struct bt_logsizecb bt_logsizecb;
struct bt_logsizecb {
  void *pCtx;                     /* A copy of this is passed to xLogsize() */
  void (*xLogsize)(void*, int);   /* Callback function */
};








|
>
>
>
>
>
>
>
>
>

|
|
|
|
|
|
|
|
|
>







 







|
|
>







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
**
** BT_CONTROL_MULTIPROC:
**   The third argument is interpreted as a pointer to type (int).
**
** BT_CONTROL_LOGSIZECB:
**
** BT_CONTROL_CHECKPOINT:
**
** BT_CONTROL_FAST_INSERT_OP:
**   The third argument is currently unused. This file-control causes the 
**   next call to sqlite4BtReplace() or sqlite4BtCsrOpen() to write to or
**   open a cursor on the "fast-insert" tree. Subsequent operations are
**   unaffected.
**  
**   In other words, an app that uses the fast-insert tree exclusively 
**   must execute this file-control before every call to CsrOpen() or 
**   Replace().
*/
#define BT_CONTROL_INFO           7706389
#define BT_CONTROL_SETVFS         7706390
#define BT_CONTROL_GETVFS         7706391
#define BT_CONTROL_SAFETY         7706392
#define BT_CONTROL_AUTOCKPT       7706393
#define BT_CONTROL_LOGSIZE        7706394
#define BT_CONTROL_MULTIPROC      7706395
#define BT_CONTROL_LOGSIZECB      7706396
#define BT_CONTROL_CHECKPOINT     7706397
#define BT_CONTROL_FAST_INSERT_OP 7706498

int sqlite4BtControl(bt_db*, int op, void *pArg);

#define BT_SAFETY_OFF    0
#define BT_SAFETY_NORMAL 1
#define BT_SAFETY_FULL   2

................................................................................
typedef struct bt_info bt_info;
struct bt_info {
  int eType;
  unsigned int pgno;
  sqlite4_buffer output;
};

#define BT_INFO_PAGEDUMP   1
#define BT_INFO_FILENAME   2
#define BT_INFO_HDRDUMP    3

typedef struct bt_logsizecb bt_logsizecb;
struct bt_logsizecb {
  void *pCtx;                     /* A copy of this is passed to xLogsize() */
  void (*xLogsize)(void*, int);   /* Callback function */
};

Changes to src/btInt.h.

134
135
136
137
138
139
140


141
142
143
144
145
146
147
...
275
276
277
278
279
280
281
282
283
284
285

286
287
288
289
290
291
292
void sqlite4BtPagerSetAutockpt(BtPager*, int*);

void sqlite4BtPagerLogsize(BtPager*, int*);
void sqlite4BtPagerMultiproc(BtPager *pPager, int *piVal);
void sqlite4BtPagerLogsizeCb(BtPager *pPager, bt_logsizecb*);
int sqlite4BtPagerCheckpoint(BtPager *pPager, bt_checkpoint*);



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

/*************************************************************************
** File-system interface.
*/
................................................................................
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);

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

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


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

#ifdef NDEBUG
# define sqlite4BtDebugReadPage(a,b,c,d)







>
>







 







|



>







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
...
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
void sqlite4BtPagerSetAutockpt(BtPager*, int*);

void sqlite4BtPagerLogsize(BtPager*, int*);
void sqlite4BtPagerMultiproc(BtPager *pPager, int *piVal);
void sqlite4BtPagerLogsizeCb(BtPager *pPager, bt_logsizecb*);
int sqlite4BtPagerCheckpoint(BtPager *pPager, bt_checkpoint*);

int sqlite4BtPagerHdrdump(BtPager *pPager, sqlite4_buffer *pBuf);

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

/*************************************************************************
** File-system interface.
*/
................................................................................
int sqlite4BtLockShmMap(BtLock*, int iChunk, int nByte, u8 **ppOut);

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

/*************************************************************************
** Utility functions.
*/
void sqlite4BtPutU32(u8 *a, u32 i);
u32 sqlite4BtGetU32(const u8 *a);
void sqlite4BtBufAppendf(sqlite4_buffer *pBuf, const char *zFormat, ...);

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

#ifdef NDEBUG
# define sqlite4BtDebugReadPage(a,b,c,d)

Changes to src/bt_main.c.

27
28
29
30
31
32
33

34
35
36
37
38
39
40
...
210
211
212
213
214
215
216

217
218
219
220
221
222
223
...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
...
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
....
2409
2410
2411
2412
2413
2414
2415

2416
2417
2418
2419
2420
2421


2422
2423
2424
2425
2426
2427
2428
....
2446
2447
2448
2449
2450
2451
2452

2453
2454
2455
2456
2457
2458
2459
....
2481
2482
2483
2484
2485
2486
2487















2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518










2519
2520
2521
2522
2523
2524
2525
....
2576
2577
2578
2579
2580
2581
2582





2583
2584
2585
2586
2587

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

};

typedef struct BtOvfl BtOvfl;
struct BtOvfl {
  int nKey;
  int nVal;
  sqlite4_buffer buf;
................................................................................
  }else{
    btCsrSetup(db, pCsr);
    pCsr->pNextCsr = db->pAllCsr;
    db->pAllCsr = pCsr;
  }

  btCheckPageRefs(db);

  return rc;
}

static void btCsrReleaseAll(bt_cursor *pCsr){
  int i;
  for(i=0; i<pCsr->nPg; i++){
    sqlite4BtPageRelease(pCsr->apPage[i]);
................................................................................

  return pgno;
}

/*
**************************************************************************/

static void btBufPrintf(sqlite4_buffer *pBuf, const char *zFormat, ...){
  char *zAppend;
  va_list ap;

  va_start(ap, zFormat);
  zAppend = sqlite4_vmprintf(0, zFormat, ap);
  va_end(ap);

................................................................................
** Append a human-readable interpretation of the b-tree page in aData/nData
** to buffer pBuf.
*/
static void btPageToAscii(u32 pgno, u8 *aData, int nData, sqlite4_buffer *pBuf){
  int i;
  int nCell = (int)btCellCount(aData, nData);

  btBufPrintf(pBuf, "Page %d: ", pgno);
  btBufPrintf(pBuf, "nCell=%d ", nCell);
  btBufPrintf(pBuf, "iFree=%d ", (int)btFreeOffset(aData, nData));
  btBufPrintf(pBuf, "flags=%d ", (int)btFlags(aData));
  if( btFlags(aData) & BT_PGFLAGS_INTERNAL ){
    btBufPrintf(pBuf, "rchild=%d ", (int)btGetU32(&aData[1]));
  }
  btBufPrintf(pBuf, "cell-offsets=(");
  for(i=0; i<nCell; i++){
    u8 *ptr = btCellPtrFind(aData, nData, i);
    btBufPrintf(pBuf, "%s%d", i==0?"":" ", (int)btGetU16(ptr));
  }
  btBufPrintf(pBuf, ")\n");

  for(i=0; i<nCell; i++){
    int nKey;
    int j;
    u8 *pCell = btCellFind(aData, nData, i);
    btBufPrintf(pBuf, "  Key %d: ", i);
    pCell += sqlite4BtVarintGet32(pCell, &nKey);
    for(j=0; j<nKey; j++){
      btBufPrintf(pBuf, "%02X", (int)pCell[j]);
    }
    if( btFlags(aData) & BT_PGFLAGS_INTERNAL ){
      btBufPrintf(pBuf, "  child=%d ", (int)btGetU32(&pCell[j]));
    }
    btBufPrintf(pBuf, "\n");
  }
}

int sqlite4BtDebugPage(sqlite4_buffer *pBuf, u32 pgno, char *aData, int nData){
  btPageToAscii(pgno, (u8*)aData, nData, pBuf);
  return SQLITE4_OK;
}
................................................................................
** Insert a new key/value pair or replace an existing one.
*/
int sqlite4BtReplace(bt_db *db, const void *pK, int nK, const void *pV, int nV){
  int rc = SQLITE4_OK;
  bt_cursor csr;

  rc = btSaveAllCursor(db, 0);

  if( rc!=SQLITE4_OK ) return rc;
  sqlite4BtDebugKV((BtLock*)db->pPager, "replace", (u8*)pK, nK, (u8*)pV, nV);

  btCheckPageRefs(db);
  btCsrSetup(db, &csr);
  rc = btCsrSeek(&csr, pK, nK, BT_SEEK_GE, BT_CSRSEEK_UPDATE);


  if( rc==SQLITE4_OK ){
    /* The cursor currently points to an entry with key pK/nK. This call
    ** should therefore replace that entry. So delete it and then re-seek
    ** the cursor.  */
    rc = sqlite4BtDelete(&csr);

    if( rc==SQLITE4_OK && nV>=0 ){
................................................................................
    if( kv.eType==KV_CELL ){
      sqlite4_free(db->pEnv, (void*)kv.pV);
    }
  }
  btCsrReset(&csr, 1);

  btCheckPageRefs(db);

  return rc;
}


/*
** Delete the entry that the cursor currently points to.
*/
................................................................................
int sqlite4BtSetCookie(bt_db *db, unsigned int iVal){
  return sqlite4BtPagerSetCookie(db->pPager, iVal);
}

int sqlite4BtGetCookie(bt_db *db, unsigned int *piVal){
  return sqlite4BtPagerGetCookie(db->pPager, piVal);
}
















static int btControlInfo(bt_db *db, bt_info *pInfo){
  int rc = SQLITE4_OK;

  switch( pInfo->eType ){
    case BT_INFO_PAGEDUMP: {
      int iTrans = sqlite4BtTransactionLevel(db);
      if( iTrans==0 ) rc = sqlite4BtBegin(db, 1);
      if( rc==SQLITE4_OK ){
        BtPage *pPg = 0;
        rc = sqlite4BtPageGet(db->pPager, pInfo->pgno, &pPg);
        if( rc==SQLITE4_OK ){
          u8 *aData;
          int nData;
          aData = sqlite4BtPageData(pPg);
          nData = sqlite4BtPagerPagesize(db->pPager);
          btPageToAscii(pInfo->pgno, aData, nData, &pInfo->output);
          sqlite4_buffer_append(&pInfo->output, "", 1);
          sqlite4BtPageRelease(pPg);
        }
        if( iTrans==0 ) rc = sqlite4BtCommit(db, 0);
      }
      break;
    }

    case BT_INFO_FILENAME: {
      const char *zFile;
      zFile = sqlite4BtPagerFilename(db->pPager, BT_PAGERFILE_DATABASE);
      rc = sqlite4_buffer_set(&pInfo->output, zFile, strlen(zFile)+1);
      break;
    }











    default: {
      rc = SQLITE4_ERROR;
      break;
    }
  }
  return rc;
................................................................................
    }

    case BT_CONTROL_CHECKPOINT: {
      bt_checkpoint *p = (bt_checkpoint*)pArg;
      rc = sqlite4BtPagerCheckpoint(db->pPager, p);
      break;
    }





  }

  return rc;
}








>







 







>







 







|







 







|
|
|
|

|

|


|

|





|


|


|

|







 







>
|
|
<
|
|
|
>
>







 







>







 







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






|
|












|










>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>





27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
...
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
...
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
....
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420

2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
....
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
....
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
....
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622

/* #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 */
  int bFastInsertOp;              /* Set by CONTROL_FAST_INSERT_OP */
};

typedef struct BtOvfl BtOvfl;
struct BtOvfl {
  int nKey;
  int nVal;
  sqlite4_buffer buf;
................................................................................
  }else{
    btCsrSetup(db, pCsr);
    pCsr->pNextCsr = db->pAllCsr;
    db->pAllCsr = pCsr;
  }

  btCheckPageRefs(db);
  db->bFastInsertOp = 0;
  return rc;
}

static void btCsrReleaseAll(bt_cursor *pCsr){
  int i;
  for(i=0; i<pCsr->nPg; i++){
    sqlite4BtPageRelease(pCsr->apPage[i]);
................................................................................

  return pgno;
}

/*
**************************************************************************/

void sqlite4BtBufAppendf(sqlite4_buffer *pBuf, const char *zFormat, ...){
  char *zAppend;
  va_list ap;

  va_start(ap, zFormat);
  zAppend = sqlite4_vmprintf(0, zFormat, ap);
  va_end(ap);

................................................................................
** Append a human-readable interpretation of the b-tree page in aData/nData
** to buffer pBuf.
*/
static void btPageToAscii(u32 pgno, u8 *aData, int nData, sqlite4_buffer *pBuf){
  int i;
  int nCell = (int)btCellCount(aData, nData);

  sqlite4BtBufAppendf(pBuf, "Page %d: ", pgno);
  sqlite4BtBufAppendf(pBuf, "nCell=%d ", nCell);
  sqlite4BtBufAppendf(pBuf, "iFree=%d ", (int)btFreeOffset(aData, nData));
  sqlite4BtBufAppendf(pBuf, "flags=%d ", (int)btFlags(aData));
  if( btFlags(aData) & BT_PGFLAGS_INTERNAL ){
    sqlite4BtBufAppendf(pBuf, "rchild=%d ", (int)btGetU32(&aData[1]));
  }
  sqlite4BtBufAppendf(pBuf, "cell-offsets=(");
  for(i=0; i<nCell; i++){
    u8 *ptr = btCellPtrFind(aData, nData, i);
    sqlite4BtBufAppendf(pBuf, "%s%d", i==0?"":" ", (int)btGetU16(ptr));
  }
  sqlite4BtBufAppendf(pBuf, ")\n");

  for(i=0; i<nCell; i++){
    int nKey;
    int j;
    u8 *pCell = btCellFind(aData, nData, i);
    sqlite4BtBufAppendf(pBuf, "  Key %d: ", i);
    pCell += sqlite4BtVarintGet32(pCell, &nKey);
    for(j=0; j<nKey; j++){
      sqlite4BtBufAppendf(pBuf, "%02X", (int)pCell[j]);
    }
    if( btFlags(aData) & BT_PGFLAGS_INTERNAL ){
      sqlite4BtBufAppendf(pBuf, "  child=%d ", (int)btGetU32(&pCell[j]));
    }
    sqlite4BtBufAppendf(pBuf, "\n");
  }
}

int sqlite4BtDebugPage(sqlite4_buffer *pBuf, u32 pgno, char *aData, int nData){
  btPageToAscii(pgno, (u8*)aData, nData, pBuf);
  return SQLITE4_OK;
}
................................................................................
** Insert a new key/value pair or replace an existing one.
*/
int sqlite4BtReplace(bt_db *db, const void *pK, int nK, const void *pV, int nV){
  int rc = SQLITE4_OK;
  bt_cursor csr;

  rc = btSaveAllCursor(db, 0);
  assert( rc!=SQLITE4_NOTFOUND && rc!=SQLITE4_INEXACT );
  if( rc==SQLITE4_OK ){
    sqlite4BtDebugKV((BtLock*)db->pPager, "replace", (u8*)pK, nK, (u8*)pV, nV);

    btCheckPageRefs(db);
    btCsrSetup(db, &csr);
    rc = btCsrSeek(&csr, pK, nK, BT_SEEK_GE, BT_CSRSEEK_UPDATE);
  }

  if( rc==SQLITE4_OK ){
    /* The cursor currently points to an entry with key pK/nK. This call
    ** should therefore replace that entry. So delete it and then re-seek
    ** the cursor.  */
    rc = sqlite4BtDelete(&csr);

    if( rc==SQLITE4_OK && nV>=0 ){
................................................................................
    if( kv.eType==KV_CELL ){
      sqlite4_free(db->pEnv, (void*)kv.pV);
    }
  }
  btCsrReset(&csr, 1);

  btCheckPageRefs(db);
  db->bFastInsertOp = 0;
  return rc;
}


/*
** Delete the entry that the cursor currently points to.
*/
................................................................................
int sqlite4BtSetCookie(bt_db *db, unsigned int iVal){
  return sqlite4BtPagerSetCookie(db->pPager, iVal);
}

int sqlite4BtGetCookie(bt_db *db, unsigned int *piVal){
  return sqlite4BtPagerGetCookie(db->pPager, piVal);
}

static int btControlTransaction(bt_db *db, int *piCtx){
  int rc = SQLITE4_OK;
  int iTrans = sqlite4BtTransactionLevel(db);

  if( iTrans==0 ){
    rc = sqlite4BtBegin(db, 1);
  }
  *piCtx = iTrans;
  return rc;
}

static void btControlTransactionDone(bt_db *db, int iCtx){
  if( iCtx==0 ) sqlite4BtCommit(db, 0);
}

static int btControlInfo(bt_db *db, bt_info *pInfo){
  int rc = SQLITE4_OK;

  switch( pInfo->eType ){
    case BT_INFO_PAGEDUMP: {
      int iCtx;                   /* ControlTransaction() context */
      rc = btControlTransaction(db, &iCtx);
      if( rc==SQLITE4_OK ){
        BtPage *pPg = 0;
        rc = sqlite4BtPageGet(db->pPager, pInfo->pgno, &pPg);
        if( rc==SQLITE4_OK ){
          u8 *aData;
          int nData;
          aData = sqlite4BtPageData(pPg);
          nData = sqlite4BtPagerPagesize(db->pPager);
          btPageToAscii(pInfo->pgno, aData, nData, &pInfo->output);
          sqlite4_buffer_append(&pInfo->output, "", 1);
          sqlite4BtPageRelease(pPg);
        }
        btControlTransactionDone(db, iCtx);
      }
      break;
    }

    case BT_INFO_FILENAME: {
      const char *zFile;
      zFile = sqlite4BtPagerFilename(db->pPager, BT_PAGERFILE_DATABASE);
      rc = sqlite4_buffer_set(&pInfo->output, zFile, strlen(zFile)+1);
      break;
    }

    case BT_INFO_HDRDUMP: {
      int iCtx;                   /* ControlTransaction() context */
      rc = btControlTransaction(db, &iCtx);
      if( rc==SQLITE4_OK ){
        rc = sqlite4BtPagerHdrdump(db->pPager, &pInfo->output);
        btControlTransactionDone(db, iCtx);
      }
      break;
    }

    default: {
      rc = SQLITE4_ERROR;
      break;
    }
  }
  return rc;
................................................................................
    }

    case BT_CONTROL_CHECKPOINT: {
      bt_checkpoint *p = (bt_checkpoint*)pArg;
      rc = sqlite4BtPagerCheckpoint(db->pPager, p);
      break;
    }

    case BT_CONTROL_FAST_INSERT_OP: {
      db->bFastInsertOp = 1;
      break;
    }
  }

  return rc;
}

Changes to src/bt_pager.c.

1072
1073
1074
1075
1076
1077
1078























1079
1080
1081
1082
1083
1084
1085
1086
1087
  pPager->xLogsize = p->xLogsize;
  pPager->pLogsizeCtx = p->pCtx;
}

int sqlite4BtPagerCheckpoint(BtPager *pPager, bt_checkpoint *pCkpt){
  int rc;
  rc = sqlite4BtLogCheckpoint(pPager->pLog, pCkpt->nFrameBuffer);























  return rc;
}

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








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









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
1107
1108
1109
1110
  pPager->xLogsize = p->xLogsize;
  pPager->pLogsizeCtx = p->pCtx;
}

int sqlite4BtPagerCheckpoint(BtPager *pPager, bt_checkpoint *pCkpt){
  int rc;
  rc = sqlite4BtLogCheckpoint(pPager->pLog, pCkpt->nFrameBuffer);
  return rc;
}

/*
** It is guaranteed that at least a read-transaction is open when
** this function is called. It appends a text representation of the
** current database header (BtDbHdr) object to the buffer passed as
** the second argument.
**
** An SQLite4 error code is returned if an error (i.e. OOM) occurs,
** or SQLITE4_OK otherwise.
*/
int sqlite4BtPagerHdrdump(BtPager *pPager, sqlite4_buffer *pBuf){
  BtDbHdr *pHdr = pPager->pHdr;
  int rc = SQLITE4_OK;

  sqlite4BtBufAppendf(pBuf, 
      "pgsz=%d nPg=%d iRoot=%d"
      " iCookie=%d iFreePg=%d iFreeBlk=%d",
      pHdr->pgsz, pHdr->nPg, pHdr->iRoot,
      pHdr->iCookie, pHdr->iFreePg, pHdr->iFreeBlk
  );

  return rc;
}

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