SQLite4
Check-in [15856cf080]
Not logged in

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

Overview
Comment:Add BT_CONTROL directives to configure the safety-level and auto-checkpoint parameter. Fix bugs.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 15856cf0805a11c02268e4c21d96ea38abf5d828
User & Date: dan 2013-11-09 17:32:02
Context
2013-11-11
20:06
Add support for multiple processes to bt. check-in: 1336c5d00a user: dan tags: trunk
2013-11-09
17:32
Add BT_CONTROL directives to configure the safety-level and auto-checkpoint parameter. Fix bugs. check-in: 15856cf080 user: dan tags: trunk
2013-11-08
20:18
Fix a robustness related problem with wrapped logs. check-in: 7dd7b942fd user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to lsm-test/lsmtest.h.

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

/* 
** Functions in wrapper2.cc (a C++ source file). wrapper2.cc contains the
** wrapper for Kyoto Cabinet. Kyoto cabinet has a C API, but
** the primary interface is the C++ API.
*/
int test_kc_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_kc_close(TestDb *);
int test_kc_write(TestDb *, void *, int , void *, int);
int test_kc_delete(TestDb *, void *, int);
int test_kc_delete_range(TestDb *, void *, int, void *, int);
int test_kc_fetch(TestDb *, void *, int, void **, int *);
int test_kc_scan(TestDb *, void *, int, void *, int, void *, int,
  void (*)(void *, void *, int , void *, int)
);

int test_mdb_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_mdb_close(TestDb *);
int test_mdb_write(TestDb *, void *, int , void *, int);
int test_mdb_delete(TestDb *, void *, int);
int test_mdb_fetch(TestDb *, void *, int, void **, int *);
int test_mdb_scan(TestDb *, void *, int, void *, int, void *, int,
  void (*)(void *, void *, int , void *, int)
);
................................................................................

/* 
** Functions in wrapper3.c. This file contains the tdb wrapper for lsm.
** The wrapper for lsm is a bit more involved than the others, as it 
** includes code for a couple of different lsm configurations, and for
** various types of fault injection and robustness testing.
*/
int test_lsm_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_lomem_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_zip_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_small_open(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt2(const char *zFilename, int bClear, TestDb **ppDb);
int test_lsm_mt3(const char *zFilename, int bClear, TestDb **ppDb);

int tdb_lsm_configure(lsm_db *, const char *);

/* Functions in lsmtest_tdb4.c */
int test_bt_open(const char *zFilename, int bClear, TestDb **ppDb);


/* Functions in testutil.c. */
int  testPrngInit(void);
u32  testPrngValue(u32 iVal);
void testPrngArray(u32 iVal, u32 *aOut, int nOut);
void testPrngString(u32 iVal, char *aOut, int nOut);







|









|







 







|
|
|
|
|
|




|







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

/* 
** Functions in wrapper2.cc (a C++ source file). wrapper2.cc contains the
** wrapper for Kyoto Cabinet. Kyoto cabinet has a C API, but
** the primary interface is the C++ API.
*/
int test_kc_open(const char*, const char *zFilename, int bClear, TestDb **ppDb);
int test_kc_close(TestDb *);
int test_kc_write(TestDb *, void *, int , void *, int);
int test_kc_delete(TestDb *, void *, int);
int test_kc_delete_range(TestDb *, void *, int, void *, int);
int test_kc_fetch(TestDb *, void *, int, void **, int *);
int test_kc_scan(TestDb *, void *, int, void *, int, void *, int,
  void (*)(void *, void *, int , void *, int)
);

int test_mdb_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
int test_mdb_close(TestDb *);
int test_mdb_write(TestDb *, void *, int , void *, int);
int test_mdb_delete(TestDb *, void *, int);
int test_mdb_fetch(TestDb *, void *, int, void **, int *);
int test_mdb_scan(TestDb *, void *, int, void *, int, void *, int,
  void (*)(void *, void *, int , void *, int)
);
................................................................................

/* 
** Functions in wrapper3.c. This file contains the tdb wrapper for lsm.
** The wrapper for lsm is a bit more involved than the others, as it 
** includes code for a couple of different lsm configurations, and for
** various types of fault injection and robustness testing.
*/
int test_lsm_open(const char*, const char *zFile, int bClear, TestDb **ppDb);
int test_lsm_lomem_open(const char*, const char*, int bClear, TestDb **ppDb);
int test_lsm_zip_open(const char*, const char*, int bClear, TestDb **ppDb);
int test_lsm_small_open(const char*, const char*, int bClear, TestDb **ppDb);
int test_lsm_mt2(const char*, const char *zFile, int bClear, TestDb **ppDb);
int test_lsm_mt3(const char*, const char *zFile, int bClear, TestDb **ppDb);

int tdb_lsm_configure(lsm_db *, const char *);

/* Functions in lsmtest_tdb4.c */
int test_bt_open(const char*, const char *zFilename, int bClear, TestDb **ppDb);


/* Functions in testutil.c. */
int  testPrngInit(void);
u32  testPrngValue(u32 iVal);
void testPrngArray(u32 iVal, u32 *aOut, int nOut);
void testPrngString(u32 iVal, char *aOut, int nOut);

Changes to lsm-test/lsmtest_datasource.c.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  void **ppKey, int *pnKey,
  void **ppVal, int *pnVal
){
  assert( (ppKey==0)==(pnKey==0) );
  assert( (ppVal==0)==(pnVal==0) );

  if( ppKey ){
    int nKey;
    switch( p->eType ){
      case TEST_DATASOURCE_RANDOM: {
        int nRange = (1 + p->nMaxKey - p->nMinKey);
        nKey = (int)( testPrngValue((u32)iData) % nRange ) + p->nMinKey; 
        testPrngString((u32)iData, p->aKey, nKey);
        break;
      }







|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
  void **ppKey, int *pnKey,
  void **ppVal, int *pnVal
){
  assert( (ppKey==0)==(pnKey==0) );
  assert( (ppVal==0)==(pnVal==0) );

  if( ppKey ){
    int nKey = 0;
    switch( p->eType ){
      case TEST_DATASOURCE_RANDOM: {
        int nRange = (1 + p->nMaxKey - p->nMinKey);
        nKey = (int)( testPrngValue((u32)iData) % nRange ) + p->nMinKey; 
        testPrngString((u32)iData, p->aKey, nKey);
        break;
      }

Changes to lsm-test/lsmtest_main.c.

10
11
12
13
14
15
16
17



18
19
20
21
22
23
24
...
636
637
638
639
640
641
642

643
644
645

646
647
648
649
650
651
652
...
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
....
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


void test_failed(){ return; }




#define testSetError(rc) testSetErrorFunc(rc, pRc, __FILE__, __LINE__)
static void testSetErrorFunc(int rc, int *pRc, const char *zFile, int iLine){
  if( rc ){
    *pRc = rc;
    printf("FAILED (%s:%d) rc=%d ", zFile, iLine, rc);
    test_failed();
................................................................................
    }
    if( aOpt[iSel].eVal>=0 ){
      aParam[aOpt[iSel].eVal] = atoi(azArg[i+1]);
    }else{
      int j;
      zSystem = azArg[i+1];
      bLsm = 0;

      for(j=0; zSystem[j]; j++){
        if( zSystem[j]=='=' ) bLsm = 1;
      }

    }
  }
  
  printf("#");
  for(i=0; i<ArraySize(aOpt); i++){
    if( aOpt[i].zOpt ){
      if( aOpt[i].eVal>=0 ){
................................................................................

#if 0
  pLog = fopen("/tmp/speed.log", "w");
  tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog);
#endif

  for(i=0; i<aParam[ST_REPEAT] && rc==0; i++){
    int msWrite, msFetch, msScan;
    int iFetch;
    int nWrite = aParam[ST_WRITE];

    if( bReadonly ){
      msWrite = 0;
    }else{
      testTimeInit();
................................................................................
static int st_do_io(int a, char **b)        { return do_io(a, b); }

#ifdef __linux__
#include <sys/time.h>
#include <sys/resource.h>

static void lsmtest_rusage_report(void){
  int res;
  struct rusage r;
  memset(&r, 0, sizeof(r));

  res = getrusage(RUSAGE_SELF, &r);
  assert( res==0 );

  printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", 
      (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock
  );
}
#else
static void lsmtest_rusage_report(void){
  /* no-op */







|
>
>
>







 







>



>







 







|







 







<



|
<
<







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
...
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
....
1434
1435
1436
1437
1438
1439
1440

1441
1442
1443
1444


1445
1446
1447
1448
1449
1450
1451
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


void test_failed(){ 
  assert( 0 );
  return; 
}

#define testSetError(rc) testSetErrorFunc(rc, pRc, __FILE__, __LINE__)
static void testSetErrorFunc(int rc, int *pRc, const char *zFile, int iLine){
  if( rc ){
    *pRc = rc;
    printf("FAILED (%s:%d) rc=%d ", zFile, iLine, rc);
    test_failed();
................................................................................
    }
    if( aOpt[iSel].eVal>=0 ){
      aParam[aOpt[iSel].eVal] = atoi(azArg[i+1]);
    }else{
      int j;
      zSystem = azArg[i+1];
      bLsm = 0;
#if 0
      for(j=0; zSystem[j]; j++){
        if( zSystem[j]=='=' ) bLsm = 1;
      }
#endif
    }
  }
  
  printf("#");
  for(i=0; i<ArraySize(aOpt); i++){
    if( aOpt[i].zOpt ){
      if( aOpt[i].eVal>=0 ){
................................................................................

#if 0
  pLog = fopen("/tmp/speed.log", "w");
  tdb_lsm_write_hook(pDb, do_speed_write_hook2, (void *)pLog);
#endif

  for(i=0; i<aParam[ST_REPEAT] && rc==0; i++){
    int msWrite, msFetch;
    int iFetch;
    int nWrite = aParam[ST_WRITE];

    if( bReadonly ){
      msWrite = 0;
    }else{
      testTimeInit();
................................................................................
static int st_do_io(int a, char **b)        { return do_io(a, b); }

#ifdef __linux__
#include <sys/time.h>
#include <sys/resource.h>

static void lsmtest_rusage_report(void){

  struct rusage r;
  memset(&r, 0, sizeof(r));

  getrusage(RUSAGE_SELF, &r);


  printf("# getrusage: { ru_maxrss %d ru_oublock %d ru_inblock %d }\n", 
      (int)r.ru_maxrss, (int)r.ru_oublock, (int)r.ru_inblock
  );
}
#else
static void lsmtest_rusage_report(void){
  /* no-op */

Changes to lsm-test/lsmtest_tdb.c.

172
173
174
175
176
177
178
179





180
181
182
183
184
185
186
...
270
271
272
273
274
275
276


277



278
279
280
281
282
283
284
...
343
344
345
346
347
348
349


350



351
352
353
354
355
356
357
...
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
...
597
598
599
600
601
602
603


604



605
606
607
608
609
610
611
...
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
...
713
714
715
716
717
718
719
720
721
722

723








724
725

726
727
728
729
730
731
732
733
    }
  }

  leveldb_iter_destroy(iter);
  return 0;
}

static int test_leveldb_open(const char *zFilename, int bClear, TestDb **ppDb){





  static const DatabaseMethods LeveldbMethods = {
    test_leveldb_close,
    test_leveldb_write,
    test_leveldb_delete,
    0,
    test_leveldb_fetch,
    test_leveldb_scan,
................................................................................
  void (*xCallback)(void *, void *, int , void *, int)
){
  return test_kc_scan(
      pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback
  );
}



static int kc_open(const char *zFilename, int bClear, TestDb **ppDb){



  static const DatabaseMethods KcdbMethods = {
    kc_close,
    kc_write,
    kc_delete,
    kc_delete_range,
    kc_fetch,
    kc_scan,
................................................................................
  void (*xCallback)(void *, void *, int , void *, int)
){
  return test_mdb_scan(
      pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback
  );
}



static int mdb_open(const char *zFilename, int bClear, TestDb **ppDb){



  static const DatabaseMethods KcdbMethods = {
    mdb_close,
    mdb_write,
    mdb_delete,
    0,
    mdb_fetch,
    mdb_scan,
................................................................................
    error_transaction_function,
    error_transaction_function
  };

  int rc;
  TestDb *pTestDb = 0;

  rc = test_mdb_open(zFilename, bClear, &pTestDb);
  if( rc!=0 ){
    *ppDb = 0;
    return rc;
  }
  pTestDb->pMethods = &KcdbMethods;
  *ppDb = pTestDb;
  return 0;
................................................................................
    if( rc!=0 ) return rc;
  }

  pDb->nOpenTrans = iLevel;
  return 0;
}



static int sql_open(const char *zFilename, int bClear, TestDb **ppDb){



  static const DatabaseMethods SqlMethods = {
    sql_close,
    sql_write,
    sql_delete,
    sql_delete_range,
    sql_fetch,
    sql_scan,
................................................................................

/*************************************************************************
** Begin exported functions.
*/
static struct Lib {
  const char *zName;
  const char *zDefaultDb;
  int (*xOpen)(const char *zFilename, int bClear, TestDb **ppDb);
} aLib[] = {
  { "bt",           "testdb.bt",        test_bt_open },
  { "sqlite3",      "testdb.sqlite",    sql_open },
  { "lsm_small",    "testdb.lsm_small", test_lsm_small_open },
  { "lsm_lomem",    "testdb.lsm_lomem", test_lsm_lomem_open },
#ifdef HAVE_ZLIB
  { "lsm_zip",      "testdb.lsm_zip",   test_lsm_zip_open },
................................................................................
};

const char *tdb_system_name(int i){
  if( i<0 || i>=ArraySize(aLib) ) return 0;
  return aLib[i].zName;
}

int tdb_open(const char *zLibrary, const char *zDb, int bClear, TestDb **ppDb){
  int i;
  int rc = 1;










  for(i=0; i<ArraySize(aLib); i++){
    if( 0==(strcmp(zLibrary, aLib[i].zName)) ){

      rc = aLib[i].xOpen((zDb ? zDb : aLib[i].zDefaultDb), bClear, ppDb);
      if( rc==0 ){
        (*ppDb)->zLibrary = aLib[i].zName;
      }
      break;
    }
  }








|
>
>
>
>
>







 







>
>
|
>
>
>







 







>
>
|
>
>
>







 







|







 







>
>
|
>
>
>







 







|







 







|


>

>
>
>
>
>
>
>
>

<
>
|







172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
...
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
...
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
...
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
...
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
...
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753

754
755
756
757
758
759
760
761
762
    }
  }

  leveldb_iter_destroy(iter);
  return 0;
}

static int test_leveldb_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  static const DatabaseMethods LeveldbMethods = {
    test_leveldb_close,
    test_leveldb_write,
    test_leveldb_delete,
    0,
    test_leveldb_fetch,
    test_leveldb_scan,
................................................................................
  void (*xCallback)(void *, void *, int , void *, int)
){
  return test_kc_scan(
      pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback
  );
}

static int kc_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  static const DatabaseMethods KcdbMethods = {
    kc_close,
    kc_write,
    kc_delete,
    kc_delete_range,
    kc_fetch,
    kc_scan,
................................................................................
  void (*xCallback)(void *, void *, int , void *, int)
){
  return test_mdb_scan(
      pTestDb, pCtx, bReverse, pFirst, nFirst, pLast, nLast, xCallback
  );
}

static int mdb_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  static const DatabaseMethods KcdbMethods = {
    mdb_close,
    mdb_write,
    mdb_delete,
    0,
    mdb_fetch,
    mdb_scan,
................................................................................
    error_transaction_function,
    error_transaction_function
  };

  int rc;
  TestDb *pTestDb = 0;

  rc = test_mdb_open(zSpec, zFilename, bClear, &pTestDb);
  if( rc!=0 ){
    *ppDb = 0;
    return rc;
  }
  pTestDb->pMethods = &KcdbMethods;
  *ppDb = pTestDb;
  return 0;
................................................................................
    if( rc!=0 ) return rc;
  }

  pDb->nOpenTrans = iLevel;
  return 0;
}

static int sql_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  static const DatabaseMethods SqlMethods = {
    sql_close,
    sql_write,
    sql_delete,
    sql_delete_range,
    sql_fetch,
    sql_scan,
................................................................................

/*************************************************************************
** Begin exported functions.
*/
static struct Lib {
  const char *zName;
  const char *zDefaultDb;
  int (*xOpen)(const char *, const char *zFilename, int bClear, TestDb **ppDb);
} aLib[] = {
  { "bt",           "testdb.bt",        test_bt_open },
  { "sqlite3",      "testdb.sqlite",    sql_open },
  { "lsm_small",    "testdb.lsm_small", test_lsm_small_open },
  { "lsm_lomem",    "testdb.lsm_lomem", test_lsm_lomem_open },
#ifdef HAVE_ZLIB
  { "lsm_zip",      "testdb.lsm_zip",   test_lsm_zip_open },
................................................................................
};

const char *tdb_system_name(int i){
  if( i<0 || i>=ArraySize(aLib) ) return 0;
  return aLib[i].zName;
}

int tdb_open(const char *zLib, const char *zDb, int bClear, TestDb **ppDb){
  int i;
  int rc = 1;
  const char *zSpec = 0;

  int nLib = 0;
  while( zLib[nLib] && zLib[nLib]!=' ' ){
    nLib++;
  }
  zSpec = &zLib[nLib];
  while( *zSpec==' ' ) zSpec++;
  if( *zSpec=='\0' ) zSpec = 0;

  for(i=0; i<ArraySize(aLib); i++){

    if( strlen(aLib[i].zName)==nLib && 0==memcmp(zLib, aLib[i].zName, nLib) ){
      rc = aLib[i].xOpen(zSpec, (zDb ? zDb : aLib[i].zDefaultDb), bClear, ppDb);
      if( rc==0 ){
        (*ppDb)->zLibrary = aLib[i].zName;
      }
      break;
    }
  }

Changes to lsm-test/lsmtest_tdb2.cc.

211
212
213
214
215
216
217


218



219
220
221
222
223
224
225
  struct MdbDb {
    TestDb base;
    MDB_env *env;
    MDB_dbi dbi;
  };
}



int test_mdb_open(const char *zFilename, int bClear, TestDb **ppDb){



  MDB_txn *txn;
  MdbDb *pMdb;
  int rc;

  if( bClear ){
    char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename);
    system(zCmd);







>
>
|
>
>
>







211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  struct MdbDb {
    TestDb base;
    MDB_env *env;
    MDB_dbi dbi;
  };
}

int test_mdb_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  MDB_txn *txn;
  MdbDb *pMdb;
  int rc;

  if( bClear ){
    char *zCmd = sqlite3_mprintf("rm -rf %s\n", zFilename);
    system(zCmd);

Changes to lsm-test/lsmtest_tdb3.c.

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
1007
1008
1009
1010
1011

1012
1013
1014
1015
1016
1017
1018
....
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
....
1318
1319
1320
1321
1322
1323
1324


1325



1326
1327
1328
1329


1330



1331
1332
1333
1334
1335
1336
1337
    }
  }

  *ppDb = (TestDb *)pDb;
  return rc;
}



int test_lsm_open(const char *zFilename, int bClear, TestDb **ppDb){



  return testLsmOpen("", zFilename, bClear, ppDb);
}

int test_lsm_small_open(

  const char *zFile, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = "page_size=256 block_size=64 mmap=1024";
  return testLsmOpen(zCfg, zFile, bClear, ppDb);
}

int test_lsm_lomem_open(

  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
    /* "max_freelist=4 autocheckpoint=32" */
  const char *zCfg = 
    "page_size=256 block_size=64 autoflush=16 "
................................................................................
    "autocheckpoint=32"
    "mmap=0 "
  ;
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}

int test_lsm_zip_open(

  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = 
    "page_size=256 block_size=64 autoflush=16 "
    "autocheckpoint=32 compression=1 mmap=0 "
................................................................................
static void *worker_main(void *pArg){
  LsmWorker *p = (LsmWorker *)pArg;
  lsm_db *pWorker;                /* Connection to access db through */

  pthread_mutex_lock(&p->worker_mutex);
  while( (pWorker = p->pWorker) ){
    int rc = LSM_OK;
    int nCkpt = -1;

    /* Do some work. If an error occurs, exit. */

    pthread_mutex_unlock(&p->worker_mutex);
    if( p->eType==LSMTEST_THREAD_CKPT ){
      int nKB = 0;
      rc = lsm_info(pWorker, LSM_INFO_CHECKPOINT_SIZE, &nKB);
................................................................................
      break;
  }

  return rc;
}




int test_lsm_mt2(const char *zFilename, int bClear, TestDb **ppDb){



  const char *zCfg = "mt_mode=2";
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}



int test_lsm_mt3(const char *zFilename, int bClear, TestDb **ppDb){



  const char *zCfg = "mt_mode=4";
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}

#else
static void mt_shutdown(LsmDb *pDb) { 
  unused_parameter(pDb); 







>
>
|
>
>
>




>









>







 







>







 







<







 







>
>
|
>
>
>




>
>
|
>
>
>







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
1007
1008
1009
1010
1011
....
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
....
1132
1133
1134
1135
1136
1137
1138

1139
1140
1141
1142
1143
1144
1145
....
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
    }
  }

  *ppDb = (TestDb *)pDb;
  return rc;
}

int test_lsm_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  return testLsmOpen("", zFilename, bClear, ppDb);
}

int test_lsm_small_open(
  const char *zSpec, 
  const char *zFile, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = "page_size=256 block_size=64 mmap=1024";
  return testLsmOpen(zCfg, zFile, bClear, ppDb);
}

int test_lsm_lomem_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
    /* "max_freelist=4 autocheckpoint=32" */
  const char *zCfg = 
    "page_size=256 block_size=64 autoflush=16 "
................................................................................
    "autocheckpoint=32"
    "mmap=0 "
  ;
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}

int test_lsm_zip_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = 
    "page_size=256 block_size=64 autoflush=16 "
    "autocheckpoint=32 compression=1 mmap=0 "
................................................................................
static void *worker_main(void *pArg){
  LsmWorker *p = (LsmWorker *)pArg;
  lsm_db *pWorker;                /* Connection to access db through */

  pthread_mutex_lock(&p->worker_mutex);
  while( (pWorker = p->pWorker) ){
    int rc = LSM_OK;


    /* Do some work. If an error occurs, exit. */

    pthread_mutex_unlock(&p->worker_mutex);
    if( p->eType==LSMTEST_THREAD_CKPT ){
      int nKB = 0;
      rc = lsm_info(pWorker, LSM_INFO_CHECKPOINT_SIZE, &nKB);
................................................................................
      break;
  }

  return rc;
}


int test_lsm_mt2(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = "mt_mode=2";
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}

int test_lsm_mt3(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){
  const char *zCfg = "mt_mode=4";
  return testLsmOpen(zCfg, zFilename, bClear, ppDb);
}

#else
static void mt_shutdown(LsmDb *pDb) { 
  unused_parameter(pDb); 

Changes to lsm-test/lsmtest_tdb4.c.

516
517
518
519
520
521
522

































































































523



524
525
526
527
528
529
530
...
568
569
570
571
572
573
574


575

576
577
578
579
580
581
582

static int bt_rollback(TestDb *pTestDb, int iLvl){
  BtDb *p = (BtDb*)pTestDb;
  int rc = sqlite4BtRollback(p->pBt, iLvl);
  return rc;
}


































































































int test_bt_open(const char *zFilename, int bClear, TestDb **ppDb){




  static const DatabaseMethods SqlMethods = {
    bt_close,
    bt_write,
    bt_delete,
    bt_delete_range,
    bt_fetch,
................................................................................
    p->env.xShmMap = btVfsShmMap;
    p->env.xShmBarrier = btVfsShmBarrier;
    p->env.xShmUnmap = btVfsShmUnmap;

    sqlite4BtControl(pBt, BT_CONTROL_GETVFS, (void*)&p->pVfs);
    sqlite4BtControl(pBt, BT_CONTROL_SETVFS, (void*)&p->env);



    rc = sqlite4BtOpen(pBt, zFilename);

  }

  if( rc!=SQLITE4_OK && p ){
    bt_close(&p->base);
  }

  *ppDb = &p->base;







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







 







>
>
|
>







516
517
518
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
545
546
547
548
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
577
578
579
580
581
582
583
584
585
586
587
588
589
590
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
618
619
620
621
622
623
624
625
626
627
628
629
630
...
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685

static int bt_rollback(TestDb *pTestDb, int iLvl){
  BtDb *p = (BtDb*)pTestDb;
  int rc = sqlite4BtRollback(p->pBt, iLvl);
  return rc;
}

static int testParseOption(
  const char **pzIn,              /* IN/OUT: pointer to next option */
  const char **pzOpt,             /* OUT: nul-terminated option name */
  const char **pzArg,             /* OUT: nul-terminated option argument */
  char *pSpace                    /* Temporary space for output params */
){
  const char *p = *pzIn;
  const char *pStart;
  int n;

  char *pOut = pSpace;

  while( *p==' ' ) p++;
  pStart = p;
  while( *p && *p!='=' ) p++;
  if( *p==0 ) return 1;

  n = (p - pStart);
  memcpy(pOut, pStart, n);
  *pzOpt = pOut;
  pOut += n;
  *pOut++ = '\0';

  p++;
  pStart = p;
  while( *p && *p!=' ' ) p++;
  n = (p - pStart);

  memcpy(pOut, pStart, n);
  *pzArg = pOut;
  pOut += n;
  *pOut++ = '\0';

  *pzIn = p;
  return 0;
}

static int testParseInt(const char *z, int *piVal){
  int i = 0;
  const char *p = z;

  while( *p>='0' && *p<='9' ){
    i = i*10 + (*p - '0');
    p++;
  }
  if( *p=='K' || *p=='k' ){
    i = i * 1000;
    p++;
  }else if( *p=='M' || *p=='m' ){
    i = i * 1000000;
    p++;
  }

  if( *p ) return SQLITE4_ERROR;
  *piVal = i;
  return SQLITE4_OK;
}

static int testBtConfigure(bt_db *db, const char *zCfg){
  int rc = SQLITE4_OK;

  if( zCfg ){
    struct CfgParam {
      const char *zParam;
      int eParam;
    } aParam[] = {
      { "safety",         BT_CONTROL_SAFETY },
      { "autockpt",       BT_CONTROL_AUTOCKPT }
    };
    const char *z = zCfg;
    int n = strlen(z);
    char *aSpace;
    const char *zOpt;
    const char *zArg;

    aSpace = (char*)testMalloc(n+2);
    while( 0==testParseOption(&z, &zOpt, &zArg, aSpace) ){
      int i;
      int iVal;
      rc = testArgSelect(aParam, "param", zOpt, &i);
      if( rc!=SQLITE4_OK ) break;

      rc = testParseInt(zArg, &iVal);
      if( rc!=SQLITE4_OK ) break;

      rc = sqlite4BtControl(db, aParam[i].eParam, (void*)&iVal);
      if( rc!=SQLITE4_OK ) break;
    }
    testFree(aSpace);
  }

  return rc;
}


int test_bt_open(
  const char *zSpec, 
  const char *zFilename, 
  int bClear, 
  TestDb **ppDb
){

  static const DatabaseMethods SqlMethods = {
    bt_close,
    bt_write,
    bt_delete,
    bt_delete_range,
    bt_fetch,
................................................................................
    p->env.xShmMap = btVfsShmMap;
    p->env.xShmBarrier = btVfsShmBarrier;
    p->env.xShmUnmap = btVfsShmUnmap;

    sqlite4BtControl(pBt, BT_CONTROL_GETVFS, (void*)&p->pVfs);
    sqlite4BtControl(pBt, BT_CONTROL_SETVFS, (void*)&p->env);

    rc = testBtConfigure(pBt, zSpec);
    if( rc==SQLITE4_OK ){
      rc = sqlite4BtOpen(pBt, zFilename);
    }
  }

  if( rc!=SQLITE4_OK && p ){
    bt_close(&p->base);
  }

  *ppDb = &p->base;

Changes to src/bt.h.

146
147
148
149
150
151
152


153
154
155
156
157

158
159




160
161
162
163
164
165
166
**   to the current bt_env object.
**
** BT_CONTROL_SAFETY:
**   The third argument is interpreted as a pointer to type (int). If
**   the value stored in the (int) location is 0, 1 or 2, then the current
**   b-tree safety level is set to 0, 1 or 2, respectively. Otherwise, the
**   integer value is set to the current safety level.


*/
#define BT_CONTROL_INFO   7706389
#define BT_CONTROL_SETVFS 7706390
#define BT_CONTROL_GETVFS 7706391
#define BT_CONTROL_SAFETY 7706392


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





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







>
>

|
|
|
|
>


>
>
>
>







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
**   to the current bt_env object.
**
** BT_CONTROL_SAFETY:
**   The third argument is interpreted as a pointer to type (int). If
**   the value stored in the (int) location is 0, 1 or 2, then the current
**   b-tree safety level is set to 0, 1 or 2, respectively. Otherwise, the
**   integer value is set to the current safety level.
**
** BT_CONTROL_AUTOCKPT:
*/
#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

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

Changes to src/btInt.h.

127
128
129
130
131
132
133

134
135
136
137
138
139
140
...
213
214
215
216
217
218
219












220
221
222
223
224
225
226
227
228
229
230
231
232
#define BT_PAGERFILE_SHM      2
const char *sqlite4BtPagerFilename(BtPager*, int ePagerfile);

bt_env *sqlite4BtPagerGetEnv(BtPager*);
void sqlite4BtPagerSetEnv(BtPager*, bt_env*);

void sqlite4BtPagerSetSafety(BtPager*, int*);


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

/*************************************************************************
** File-system interface.
................................................................................
  /* 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;
};








>







 







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





<







127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
...
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
#define BT_PAGERFILE_SHM      2
const char *sqlite4BtPagerFilename(BtPager*, int ePagerfile);

bt_env *sqlite4BtPagerGetEnv(BtPager*);
void sqlite4BtPagerSetEnv(BtPager*, bt_env*);

void sqlite4BtPagerSetSafety(BtPager*, int*);
void sqlite4BtPagerSetAutockpt(BtPager*, int*);

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

/*************************************************************************
** File-system interface.
................................................................................
  /* 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 */

  /* Global configuration settings:
  **
  ** nAutoCkpt:
  **   If a transaction is committed and there are this many frames in the
  **   log file, automatically run a checkpoint operation.
  **
  ** iSafetyLevel:
  **   Current safety level. 0==off, 1==normal, 2=full.
  */
  int iSafetyLevel;               /* 0==OFF, 1==NORMAL, 2==FULL */
  int nAutoCkpt;                  /* Auto-checkpoint when log is this large */

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

Changes to src/bt_log.c.

272
273
274
275
276
277
278










279
280
281
282
283
284
285
...
480
481
482
483
484
485
486

487
488
489
490
491
492
493
....
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
....
1228
1229
1230
1231
1232
1233
1234
1235



1236
1237
1238
1239
1240
1241
1242
....
1277
1278
1279
1280
1281
1282
1283

1284

1285
1286
1287
1288
1289
1290
1291
....
1731
1732
1733
1734
1735
1736
1737






1738
1739
1740
1741
1742
1743
1744

#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)
#endif

#ifndef NDEBUG
static void btDebugCheckHash(BtLock *pLock){
................................................................................
static i64 btLogFrameOffset(BtLog *pLog, int pgsz, u32 iFrame){
  return 
      (i64)pLog->snapshot.nSector*2 
    + (i64)(iFrame-1) * (i64)(pgsz + sizeof(BtFrameHdr));
}

static int btLogSyncFile(BtLog *pLog, bt_file *pFd){

  bt_env *pVfs = pLog->pLock->pVfs;
  return pVfs->xSync(pFd);
}

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);
................................................................................
  iNextFrame = pLog->snapshot.iNextFrame + 1;
  if( iFrame!=1 && iFrame==aLog[5]+1
   && aLog[0]==0 && aLog[2]==0 
   && aLog[4]!=0 && aLog[4]>pLog->nWrapLog 
  ){
    /* Case 2) It is possible to wrap the log around */
    iNextFrame = 1;
  }else if( (iNextFrame+nPad)>=aLog[0] && iNextFrame<aLog[1] ){

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

    if( btLogFrameHash(pLog, iNextFrame)!=btLogFrameHash(pLog, iFrame) ){
      rc = btLogZeroHash(pLog, btLogFrameHash(pLog, iNextFrame));
................................................................................
/*
** 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;

  int nPad = 1;




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

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

    if( rc==SQLITE4_OK ) rc = btLogSyncFile(pLog, pLog->pFd);

    if( rc==SQLITE4_OK ) rc = btLogUpdateSharedHdr(pLog);
  }

  return rc;
}

/*
................................................................................
    ** ascending order.  */
    if( rc==SQLITE4_OK ){
      rc = btLogGatherPgno(pLog, nFrameBuffer, &aPgno, &nPgno, &iLast);
    }

    if( rc==SQLITE4_OK && nPgno>0 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);






      rc = btLogReadData(pLog, iOff, (u8*)&fhdr, sizeof(BtFrameHdr));
      iFirstRead = fhdr.iNext;

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







>
>
>
>
>
>
>
>
>
>







 







>







 







|







 







|
>
>
>







 







>
|
>







 







>
>
>
>
>
>







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
...
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
....
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
....
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
....
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
....
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766

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

  /* Check that the three log regions do not overlap */
  assert( aLog[0]==0 || aLog[2]==0 || aLog[3]<aLog[0] || aLog[2]>aLog[1] );
  assert( aLog[0]==0 || aLog[4]==0 || aLog[5]<aLog[0] || aLog[4]>aLog[1] );
  assert( aLog[2]==0 || aLog[4]==0 || aLog[5]<aLog[2] || aLog[4]>aLog[3] );

  /* Check that the "next frame" does not overlap with any region */
  assert( aLog[0]==0 || pHdr->iNextFrame<aLog[0] ||  pHdr->iNextFrame>aLog[1] );
  assert( aLog[2]==0 || pHdr->iNextFrame<aLog[2] ||  pHdr->iNextFrame>aLog[3] );
  assert( aLog[4]==0 || pHdr->iNextFrame<aLog[4] ||  pHdr->iNextFrame>aLog[5] );
}
#else
#define btDebugCheckSnapshot(x)
#endif

#ifndef NDEBUG
static void btDebugCheckHash(BtLock *pLock){
................................................................................
static i64 btLogFrameOffset(BtLog *pLog, int pgsz, u32 iFrame){
  return 
      (i64)pLog->snapshot.nSector*2 
    + (i64)(iFrame-1) * (i64)(pgsz + sizeof(BtFrameHdr));
}

static int btLogSyncFile(BtLog *pLog, bt_file *pFd){
  if( pLog->pLock->iSafetyLevel==BT_SAFETY_OFF ) return SQLITE4_OK;
  bt_env *pVfs = pLog->pLock->pVfs;
  return pVfs->xSync(pFd);
}

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);
................................................................................
  iNextFrame = pLog->snapshot.iNextFrame + 1;
  if( iFrame!=1 && iFrame==aLog[5]+1
   && aLog[0]==0 && aLog[2]==0 
   && aLog[4]!=0 && aLog[4]>pLog->nWrapLog 
  ){
    /* Case 2) It is possible to wrap the log around */
    iNextFrame = 1;
  }else if( (iNextFrame+nPad)>=aLog[0] && iNextFrame<=aLog[1] ){

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

    if( btLogFrameHash(pLog, iNextFrame)!=btLogFrameHash(pLog, iFrame) ){
      rc = btLogZeroHash(pLog, btLogFrameHash(pLog, iNextFrame));
................................................................................
/*
** 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;

  int nPad = 0;
  if( pLog->pLock->iSafetyLevel==BT_SAFETY_FULL ){
    nPad = (pLog->snapshot.nSector + pgsz-1) / pgsz;
  }

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

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

  return rc;
}

/*
................................................................................
    ** ascending order.  */
    if( rc==SQLITE4_OK ){
      rc = btLogGatherPgno(pLog, nFrameBuffer, &aPgno, &nPgno, &iLast);
    }

    if( rc==SQLITE4_OK && nPgno>0 ){
      i64 iOff = btLogFrameOffset(pLog, pgsz, iLast);

      /* Ensure the log has been synced to disk */
      if( rc==SQLITE4_OK ){
        rc = btLogSyncFile(pLog, pLog->pLock->pFd);
      }

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

Changes to src/bt_main.c.

2392
2393
2394
2395
2396
2397
2398






2399
2400
2401
2402
2403
    }

    case BT_CONTROL_SAFETY: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerSetSafety(db->pPager, pInt);
      break;
    }






  }

  return rc;
}








>
>
>
>
>
>





2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
    }

    case BT_CONTROL_SAFETY: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerSetSafety(db->pPager, pInt);
      break;
    }

    case BT_CONTROL_AUTOCKPT: {
      int *pInt = (int*)pArg;
      sqlite4BtPagerSetAutockpt(db->pPager, pInt);
      break;
    }
  }

  return rc;
}

Changes to src/bt_pager.c.

17
18
19
20
21
22
23


24
25
26
27
28
29
30
..
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
...
217
218
219
220
221
222
223

224
225
226
227
228
229
230
231
...
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
...
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
...
922
923
924
925
926
927
928
929
930








931
932
933
934
935
936
937
938
939
#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
................................................................................
/*
** 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.
**
** iSafetyLevel:
**   Current safety level. 0==off, 1==normal, 2=full.
*/
struct BtPager {
  BtLock btl;                     /* Variables shared with bt_lock module */
  BtLog *pLog;                    /* Logging module */
  int iTransactionLevel;          /* Current transaction level (see bt.h) */
  int iSafetyLevel;               /* Current safety level */
  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 */
};

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

  p->nAutoCkpt = BT_DEFAULT_AUTOCKPT;
  *pp = p;
  return SQLITE4_OK;
}

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

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

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

  return rc;
}

/*
................................................................................
      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;
}

static int btLoadPageData(BtPager *p, BtPage *pPg){
................................................................................
void sqlite4BtPagerSetEnv(BtPager *p, bt_env *pVfs){
  p->btl.pVfs = pVfs;
}

void sqlite4BtPagerSetSafety(BtPager *pPager, int *piVal){
  int iVal = *piVal;
  if( iVal>=0 && iVal<=2 ){
    pPager->iSafetyLevel = iVal;
  }








  *piVal = pPager->iSafetyLevel;
}

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








>
>







 







<
<
<
<
<
<
<





<





<







 







>
|







 







|







 







|







 







|

>
>
>
>
>
>
>
>
|








17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
..
74
75
76
77
78
79
80







81
82
83
84
85

86
87
88
89
90

91
92
93
94
95
96
97
...
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
...
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
...
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
...
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
#include <assert.h>
#include <stdio.h>


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

#define BT_DEFAULT_SAFETY BT_SAFETY_NORMAL

typedef struct BtPageHash BtPageHash;

typedef struct BtSavepoint BtSavepoint;
typedef struct BtSavepage BtSavepage;

/*
** Hash table for page references currently in memory. Manipulated using
................................................................................
/*
** Candidate values for BtPage.flags
*/
#define BT_PAGE_DIRTY 0x0001      /* Set for pages in BtPager.pDirty list */

/*
** Pager object.







*/
struct BtPager {
  BtLock btl;                     /* Variables shared with bt_lock module */
  BtLog *pLog;                    /* Logging 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 */
  int nTotalRef;                  /* Total number of outstanding page refs */

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

................................................................................
  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();
  p->btl.iSafetyLevel = BT_DEFAULT_SAFETY;
  p->btl.nAutoCkpt = BT_DEFAULT_AUTOCKPT;
  *pp = p;
  return SQLITE4_OK;
}

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

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

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

  return rc;
}

/*
................................................................................
      int nPg = ((pNext==0) ? p->nPg : 0);
      rc = sqlite4BtLogWrite(p->pLog, pPg->pgno, pPg->aData, nPg);
    }
  }
  p->pDirty = 0;
  sqlite4BtLogSnapshotEndWrite(p->pLog);

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

  return rc;
}

static int btLoadPageData(BtPager *p, BtPage *pPg){
................................................................................
void sqlite4BtPagerSetEnv(BtPager *p, bt_env *pVfs){
  p->btl.pVfs = pVfs;
}

void sqlite4BtPagerSetSafety(BtPager *pPager, int *piVal){
  int iVal = *piVal;
  if( iVal>=0 && iVal<=2 ){
    pPager->btl.iSafetyLevel = iVal;
  }
  *piVal = pPager->btl.iSafetyLevel;
}

void sqlite4BtPagerSetAutockpt(BtPager *pPager, int *piVal){
  int iVal = *piVal;
  if( iVal>=0 ){
    pPager->btl.nAutoCkpt = iVal;
  }
  *piVal = pPager->btl.nAutoCkpt;
}

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