SQLite4
Check-in [1a0d07f113]
Not logged in

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

Overview
Comment:Add code to free pages and blocks.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1:1a0d07f113cf04a7ca902c80fc6fb239c1852167
User & Date: dan 2013-11-21 18:46:03
Context
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
2013-11-21
18:46
Add code to free pages and blocks. check-in: 1a0d07f113 user: dan tags: trunk
2013-11-20
19:23
Combine the bt database and shm headers. check-in: 1512aee6f6 user: dan tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btInt.h.

34
35
36
37
38
39
40











41
42
43
44
45
46
47
...
183
184
185
186
187
188
189

190
191
192



193
194
195
196
197
198
199
...
264
265
266
267
268
269
270

271
272
273
274
275
276
277
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif

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












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

................................................................................
int sqlite4BtLogSnapshotEndWrite(BtLog*);

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

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);


int sqlite4BtLogPagesize(BtLog*);
int sqlite4BtLogPagecount(BtLog*);
u32 sqlite4BtLogCookie(BtLog*);



int sqlite4BtLogSetCookie(BtLog*, u32 iCookie);

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

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

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


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

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







>
>
>
>
>
>
>
>
>
>
>







 







>



>
>
>







 







>







34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
...
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
...
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
#endif
#ifndef MAX
# define MAX(a,b) (((a)>(b))?(a):(b))
#endif

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

typedef struct BtDbHdr BtDbHdr;
struct BtDbHdr {
  u32 pgsz;                       /* Page size in bytes */
  u32 nPg;                        /* Size of database file in pages */
  u32 iRoot;                      /* B-tree root page */
  u32 iCookie;                    /* Current value of schema cookie */

  u32 iFreePg;                    /* First page in free-page list trunk */
  u32 iFreeBlk;                   /* First page in free-block list trunk */
};

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

................................................................................
int sqlite4BtLogSnapshotEndWrite(BtLog*);

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

int sqlite4BtLogFrameToIdx(u32 *aLog, u32 iFrame);

#if 0
int sqlite4BtLogPagesize(BtLog*);
int sqlite4BtLogPagecount(BtLog*);
u32 sqlite4BtLogCookie(BtLog*);
#endif
BtDbHdr *sqlite4BtLogDbhdr(BtLog*);

int sqlite4BtLogSetCookie(BtLog*, u32 iCookie);

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

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

Changes to src/bt_log.c.

825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
....
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
....
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
....
1837
1838
1839
1840
1841
1842
1843

1844
1845
1846
1847
1848
1849
1850
....
1857
1858
1859
1860
1861
1862
1863





1864
1865
1866
1867
1868
1869
1870
        return SQLITE4_OK;
      }
    }
  }

  memset(pHdr, 0, sizeof(BtDbHdr));
  pHdr->pgsz = BT_DEFAULT_PGSZ;
  pHdr->nPg = 1;
  pHdr->iRoot = 2;

  return rc;
}

static int btLogUpdateDbhdr(BtLog *pLog, u8 *aData){
  BtDbHdrCksum hdr;
................................................................................
/*
** If parameter iSafe is non-zero, then this function is being called as
** part of a checkpoint operation. In this case, if there exists a version
** of page pgno within the log at some point past frame iSafe, return
** SQLITE4_NOTFOUND.
*/
int btLogRead(BtLog *pLog, u32 pgno, u8 *aData, u32 iSafe){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  int rc = SQLITE4_NOTFOUND;
  u32 iFrame = 0;
  int i;

  u32 *aLog = pLog->snapshot.aLog;
  int iSafeIdx = sqlite4BtLogFrameToIdx(aLog, iSafe);

................................................................................
  if( rc==SQLITE4_OK ){
    memset(aHash, 0, sizeof(ht_slot)*HASHTABLE_NSLOT);
  }
  return rc;
}

static int btLogWriteFrame(BtLog *pLog, int nPad, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = sqlite4BtPagerPagesize((BtPager*)(pLog->pLock));
  u32 *aLog = pLog->snapshot.aLog;
  int rc = SQLITE4_OK;            /* Return code */
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  u32 iNextFrame;                 /* Frame to write following this one */
  i64 iOff;                       /* Offset of log file to write to */
  BtFrameHdr frame;               /* Header for new frame */

................................................................................
  return rc;
}

/*
** 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.dbhdr.nPg ){
    BtPager *pPager = (BtPager *)(pLog->pLock);
    BtPage *pOne = 0;
    rc = sqlite4BtPageGet(pPager, 1, &pOne);
    if( rc==SQLITE4_OK ){
      rc = sqlite4BtLogWrite(pLog, 1, sqlite4BtPageData(pOne), 0);
      sqlite4BtPageRelease(pOne);
    }
................................................................................
    sqlite4_free(pLock->pEnv, aBuf);
    sqlite4_free(pLock->pEnv, aPgno);
    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}


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

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

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







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







|







 







|







 







|







 







|












|







 







>







 







>
>
>
>
>







825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
....
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
....
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
....
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
....
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
....
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
        return SQLITE4_OK;
      }
    }
  }

  memset(pHdr, 0, sizeof(BtDbHdr));
  pHdr->pgsz = BT_DEFAULT_PGSZ;
  pHdr->nPg = 2;
  pHdr->iRoot = 2;

  return rc;
}

static int btLogUpdateDbhdr(BtLog *pLog, u8 *aData){
  BtDbHdrCksum hdr;
................................................................................
/*
** If parameter iSafe is non-zero, then this function is being called as
** part of a checkpoint operation. In this case, if there exists a version
** of page pgno within the log at some point past frame iSafe, return
** SQLITE4_NOTFOUND.
*/
int btLogRead(BtLog *pLog, u32 pgno, u8 *aData, u32 iSafe){
  const int pgsz = pLog->snapshot.dbhdr.pgsz;
  int rc = SQLITE4_NOTFOUND;
  u32 iFrame = 0;
  int i;

  u32 *aLog = pLog->snapshot.aLog;
  int iSafeIdx = sqlite4BtLogFrameToIdx(aLog, iSafe);

................................................................................
  if( rc==SQLITE4_OK ){
    memset(aHash, 0, sizeof(ht_slot)*HASHTABLE_NSLOT);
  }
  return rc;
}

static int btLogWriteFrame(BtLog *pLog, int nPad, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = pLog->snapshot.dbhdr.pgsz;
  u32 *aLog = pLog->snapshot.aLog;
  int rc = SQLITE4_OK;            /* Return code */
  u32 iFrame;                     /* Write this frame (numbered from 1) */
  u32 iNextFrame;                 /* Frame to write following this one */
  i64 iOff;                       /* Offset of log file to write to */
  BtFrameHdr frame;               /* Header for new frame */

................................................................................
  return rc;
}

/*
** Write a frame to the log file.
*/
int sqlite4BtLogWrite(BtLog *pLog, u32 pgno, u8 *aData, u32 nPg){
  const int pgsz = pLog->snapshot.dbhdr.pgsz;
  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.dbhdr.nPg */ ){
    BtPager *pPager = (BtPager *)(pLog->pLock);
    BtPage *pOne = 0;
    rc = sqlite4BtPageGet(pPager, 1, &pOne);
    if( rc==SQLITE4_OK ){
      rc = sqlite4BtLogWrite(pLog, 1, sqlite4BtPageData(pOne), 0);
      sqlite4BtPageRelease(pOne);
    }
................................................................................
    sqlite4_free(pLock->pEnv, aBuf);
    sqlite4_free(pLock->pEnv, aPgno);
    sqlite4BtLockCkptUnlock(pLog->pLock);
  }
  return rc;
}

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

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

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

BtDbHdr *sqlite4BtLogDbhdr(BtLog *pLog){
  return &pLog->snapshot.dbhdr;
}


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

Changes to src/bt_main.c.

80
81
82
83
84
85
86
87
88
89

90
91
92
93
94
95
96
# define btCheckPageRefs(x) 
#endif

/*
** Interpret the first 4 bytes of the buffer indicated by the first 
** parameter as a 32-bit unsigned big-endian integer.
*/
static u32 btGetU32(const u8 *a){
  return ((u32)a[0] << 24) + ((u32)a[1] << 16) + ((u32)a[2] << 8) + ((u32)a[3]);
}


/*
** Interpret the first 2 bytes of the buffer indicated by the first 
** parameter as a 16-bit unsigned big-endian integer.
*/
static u16 btGetU16(const u8 *a){
  return ((u32)a[0] << 8) + (u32)a[1];







|


>







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# define btCheckPageRefs(x) 
#endif

/*
** Interpret the first 4 bytes of the buffer indicated by the first 
** parameter as a 32-bit unsigned big-endian integer.
*/
u32 sqlite4BtGetU32(const u8 *a){
  return ((u32)a[0] << 24) + ((u32)a[1] << 16) + ((u32)a[2] << 8) + ((u32)a[3]);
}
#define btGetU32(x) sqlite4BtGetU32(x)

/*
** Interpret the first 2 bytes of the buffer indicated by the first 
** parameter as a 16-bit unsigned big-endian integer.
*/
static u16 btGetU16(const u8 *a){
  return ((u32)a[0] << 8) + (u32)a[1];

Changes to src/bt_pager.c.

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
...
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
...
491
492
493
494
495
496
497

498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
...
516
517
518
519
520
521
522



523
524
525
526
527
528
529
...
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
...
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
...
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
...
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
...
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
...
818
819
820
821
822
823
824
825
826


















































































827
828
829
830
831


832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
...
857
858
859
860
861
862
863
864








865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
  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 */

  void *pLogsizeCtx;              /* A copy of this is passed to xLogsize() */
  void (*xLogsize)(void*, int);   /* Log-size Callback function */
};


/**************************************************************************
** Interface to BtPageHash object.
................................................................................
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }

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

    /* Link the new BtSavepage structure into the pPg->pSavepage list */
    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;
................................................................................
        pNext = pSavepg->pNext;

        assert( pSavepg==pPg->pSavepage );
        assert( pSavepg->iSavepoint==pSavepoint->iLevel );

        /* If bRollback is set, restore the page data */
        if( bRollback ){
          memcpy(pPg->aData, pSavepg->aData, p->pgsz);
        }else{
          int iNextSaved = (
              pSavepg->pNextSavepage ? pSavepg->pNextSavepage->iSavepoint : 2
          );
          if( iLevel>iNextSaved ){
            assert( iLevel>=3 );
            assert( p->aSavepoint[iLevel-3].iLevel==iLevel ); 
................................................................................
** Open a read-transaction.
*/
static int btOpenReadTransaction(BtPager *p){
  int rc;

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


  rc = sqlite4BtLogSnapshotOpen(p->pLog);

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

static int btOpenWriteTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==1 );
................................................................................
  rc = sqlite4BtLogSnapshotWrite(p->pLog);
  return rc;
}

static int btCloseReadTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==0 );



  rc = sqlite4BtLogSnapshotClose(p->pLog);

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

  if( rc==SQLITE4_OK && p->bDoAutoCkpt ){
................................................................................
  btCloseSavepoints(p, 2, 0);

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

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

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

  return rc;
}

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

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

  if( pRet && aData ){
    memset(pRet, 0, sizeof(BtPage));
    pRet->aData = aData;
    pRet->pPager = p;
    rc = SQLITE4_OK;
  }else{
................................................................................
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( pPg->nRef==0 ){
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  p->pDirty = 0;

  return rc;
}
................................................................................
}

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

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

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

      if( rc==SQLITE4_OK ){
        rc = btHashAdd(p, pRet);
      }

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

  assert( (pRet!=0)==(rc==SQLITE4_OK) );
  if( rc==SQLITE4_OK ){
    p->nTotalRef++;
................................................................................
  if( (pPg->flags & BT_PAGE_DIRTY)==0 ){
    pPg->flags |= BT_PAGE_DIRTY;
    pPg->pNextDirty = pPg->pPager->pDirty;
    pPg->pPager->pDirty = pPg;
  }
  return rc;
}

/*


















































































** Decrement the refcount on page pPg. Also, indicate that page pPg is
** no longer in use.
*/
int sqlite4BtPageTrim(BtPage *pPg){
  /* assert( !"todo" ); */


  sqlite4BtPageRelease(pPg);
  return SQLITE4_OK;
}

/*
** Page number pgno is no longer in use.
*/
int sqlite4BtPageTrimPgno(BtPager *pPager, u32 pgno){
  return SQLITE4_OK;
}

int sqlite4BtPageRelease(BtPage *pPg){
  if( pPg ){
    assert( pPg->nRef>=1 );
    pPg->nRef--;
    pPg->pPager->nTotalRef--;
................................................................................

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









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

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

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

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

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

  switch( ePagerfile ){







|
<
<







 







|




|







 







|







 







>







|
<







 







>
>
>







 







|







 







|
|











|







 







|







 







|







 







|


|










|







 









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




<
>
>

|






|







 







|
>
>
>
>
>
>
>
>








|







 







|







89
90
91
92
93
94
95
96


97
98
99
100
101
102
103
...
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
...
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
...
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504

505
506
507
508
509
510
511
...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
...
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
...
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
...
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
...
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
...
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
...
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
...
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
...
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
  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 */
  BtDbHdr *pHdr;                  /* Header object for current read snapshot */


  void *pLogsizeCtx;              /* A copy of this is passed to xLogsize() */
  void (*xLogsize)(void*, int);   /* Log-size Callback function */
};


/**************************************************************************
** Interface to BtPageHash object.
................................................................................
      rc = btErrorBkpt(SQLITE4_NOMEM);
    }else{
      memset(pSavepage, 0, sizeof(BtSavepage));
    }

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

    /* Link the new BtSavepage structure into the pPg->pSavepage list */
    if( rc==SQLITE4_OK ){
      pSavepage->pPg = pPg;
      pSavepage->iSavepoint = iLevel;
................................................................................
        pNext = pSavepg->pNext;

        assert( pSavepg==pPg->pSavepage );
        assert( pSavepg->iSavepoint==pSavepoint->iLevel );

        /* If bRollback is set, restore the page data */
        if( bRollback ){
          memcpy(pPg->aData, pSavepg->aData, p->pHdr->pgsz);
        }else{
          int iNextSaved = (
              pSavepg->pNextSavepage ? pSavepg->pNextSavepage->iSavepoint : 2
          );
          if( iLevel>iNextSaved ){
            assert( iLevel>=3 );
            assert( p->aSavepoint[iLevel-3].iLevel==iLevel ); 
................................................................................
** Open a read-transaction.
*/
static int btOpenReadTransaction(BtPager *p){
  int rc;

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

  rc = sqlite4BtLogSnapshotOpen(p->pLog);

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

  }
  return rc;
}

static int btOpenWriteTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==1 );
................................................................................
  rc = sqlite4BtLogSnapshotWrite(p->pLog);
  return rc;
}

static int btCloseReadTransaction(BtPager *p){
  int rc;
  assert( p->iTransactionLevel==0 );

  assert( p->pHdr );
  p->pHdr = 0;
  rc = sqlite4BtLogSnapshotClose(p->pLog);

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

  if( rc==SQLITE4_OK && p->bDoAutoCkpt ){
................................................................................
  btCloseSavepoints(p, 2, 0);

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

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

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

  if( pRet && aData ){
    memset(pRet, 0, sizeof(BtPage));
    pRet->aData = aData;
    pRet->pPager = p;
    rc = SQLITE4_OK;
  }else{
................................................................................
  for(pPg=p->pDirty; pPg; pPg=pNext){
    pNext = pPg->pNextDirty;
    pPg->flags &= ~(BT_PAGE_DIRTY);
    pPg->pNextDirty = 0;
    if( pPg->nRef==0 ){
      btHashRemove(p, pPg);
      btFreePage(p, pPg);
    }else if( rc==SQLITE4_OK && (pPg->pgno<=p->pHdr->nPg) ){
      rc = btLoadPageData(p, pPg);
    }
  }
  p->pDirty = 0;

  return rc;
}
................................................................................
}

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

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

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

      if( rc==SQLITE4_OK ){
        rc = btHashAdd(p, pRet);
      }

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

  assert( (pRet!=0)==(rc==SQLITE4_OK) );
  if( rc==SQLITE4_OK ){
    p->nTotalRef++;
................................................................................
  if( (pPg->flags & BT_PAGE_DIRTY)==0 ){
    pPg->flags |= BT_PAGE_DIRTY;
    pPg->pNextDirty = pPg->pPager->pDirty;
    pPg->pPager->pDirty = pPg;
  }
  return rc;
}

/*
** Add page pgno to the free-page list. If argument pPg is not NULL, then
** it is a reference to page pgno.
*/
static int btFreelistAdd(BtPager *p, BtPage *pPg, u32 pgno){
  BtDbHdr *pHdr = sqlite4BtLogDbhdr(p->pLog);
  int rc = SQLITE4_OK;
  int bDone = 0;

  /* Check if there is space on the first free-list trunk page. If so,
  ** add the new entry to it. Set variable bDone to indicate that the
  ** page has already been added to the free-list. */
  if( pHdr->iFreePg ){
    BtPage *pTrunk;
    rc = sqlite4BtPageGet(p, pHdr->iFreePg, &pTrunk);
    if( rc==SQLITE4_OK ){
      const int nMax = ((pHdr->pgsz - 8) / 4);
      u8 *aData = pTrunk->aData;
      int nFree = (int)sqlite4BtGetU32(aData);

      if( nFree<nMax ){
        sqlite4BtPutU32(&pTrunk->aData[8 + nFree*4], pgno);
        sqlite4BtPutU32(pTrunk->aData, nFree+1);
        bDone = 1;
      }

      sqlite4BtPageRelease(pTrunk);
    } 
  }

  /* If no error has occurred but the page number has not yet been added 
  ** to the free-list, this page becomes the first trunk in the list.  */
  if( rc==SQLITE4_OK && bDone==0 ){
    BtPage *pRelease = 0;
    if( pPg==0 ){
      rc = sqlite4BtPageGet(p, pgno, &pRelease);
    }
    if( rc==SQLITE4_OK ){
      BtPage *pTrunk = pPg ? pPg : pRelease;
      rc = sqlite4BtPageWrite(pTrunk);
      if( rc==SQLITE4_OK ){
        sqlite4BtPutU32(&pTrunk->aData[4], 0);
        sqlite4BtPutU32(&pTrunk->aData[0], pHdr->iFreePg);
        pHdr->iFreePg = pgno;
      }
    }
    sqlite4BtPageRelease(pRelease);
  }

  return rc;
}

static int btFreelistAlloc(BtPager *p, u32 *pPgno){
  BtDbHdr *pHdr = sqlite4BtLogDbhdr(p->pLog);
  int rc = SQLITE4_OK;

  assert( *pPgno==0 );
  if( pHdr->iFreePg ){
    BtPage *pTrunk = 0;
    rc = sqlite4BtPageGet(p, pHdr->iFreePg, &pTrunk);
    if( rc==SQLITE4_OK ){
      rc = sqlite4BtPageWrite(pTrunk);
    }
    if( rc==SQLITE4_OK ){
      u8 *aData = pTrunk->aData;
      u32 nFree = sqlite4BtGetU32(aData);
      if( nFree==0 ){
        u32 iNext = sqlite4BtGetU32(&aData[4]);
        *pPgno = pHdr->iFreePg;
        pHdr->iFreePg = iNext;
      }else{
        *pPgno = sqlite4BtGetU32(&aData[8 + 4*(nFree-1)]);
        sqlite4BtPutU32(aData, nFree-1);
      }
    }

    sqlite4BtPageRelease(pTrunk);
  }

  return rc;
}

/*
** Decrement the refcount on page pPg. Also, indicate that page pPg is
** no longer in use.
*/
int sqlite4BtPageTrim(BtPage *pPg){

  int rc;                         /* Return code */
  rc = btFreelistAdd(pPg->pPager, pPg, pPg->pgno);
  sqlite4BtPageRelease(pPg);
  return rc;
}

/*
** Page number pgno is no longer in use.
*/
int sqlite4BtPageTrimPgno(BtPager *pPager, u32 pgno){
  return btFreelistAdd(pPager, 0, pgno);
}

int sqlite4BtPageRelease(BtPage *pPg){
  if( pPg ){
    assert( pPg->nRef>=1 );
    pPg->nRef--;
    pPg->pPager->nTotalRef--;
................................................................................

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

  /* Find the page number of the new page. There are two ways a page may
  ** be allocated - from the free-list or by appending it to the end of
  ** the database file. */ 
  rc = btFreelistAlloc(p, &pgno);
  if( rc==SQLITE4_OK && pgno==0 ){
    pgno = p->pHdr->nPg+1;
  }

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

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

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

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

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

  switch( ePagerfile ){