/ Check-in [2b755def]
Login

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

Overview
Comment:Allocate page cache headers and page cache data buffers separately. The data buffer will be a power of two in size and this gives some malloc implementation additional optimization opportunitites. (CVS 4409)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 2b755defe51a565a2b6ace58381d6e91f6f17db8
User & Date: drh 2007-09-06 22:19:15
Context
2007-09-06
23:28
Base the name of the statement journal on the original database filename. Remember the statement journal name for the lifetime of the Pager so that the name pointer passed to xOpen persists as long as the file exists. (CVS 4410) check-in: 44d8d1e9 user: drh tags: trunk
22:19
Allocate page cache headers and page cache data buffers separately. The data buffer will be a power of two in size and this gives some malloc implementation additional optimization opportunitites. (CVS 4409) check-in: 2b755def user: drh tags: trunk
13:49
Updated comments on journal.c. No changes to code. (CVS 4408) check-in: 32984410 user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
898
899
900
901
902
903
904

905
906
907
908
909
910
911
912
...
965
966
967
968
969
970
971
972

973
974
975
976
977
978
979
....
1049
1050
1051
1052
1053
1054
1055
1056

1057
1058
1059
1060
1061
1062
1063
....
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
....
4344
4345
4346
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
....
4895
4896
4897
4898
4899
4900
4901
4902

4903
4904
4905
4906
4907
4908
4909
4910
4911
4912
4913
4914
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.422 2007/09/03 22:00:39 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
  int top;           /* First byte of the cell content area */

  pBt = pPage->pBt;
  assert( pBt!=0 );
  assert( pParent==0 || pParent->pBt==pBt );
  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );

  assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
  if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
    /* The parent page should never change unless the file is corrupt */
    return SQLITE_CORRUPT_BKPT;
  }
  if( pPage->isInit ) return SQLITE_OK;
  if( pPage->pParent==0 && pParent!=0 ){
    pPage->pParent = pParent;
................................................................................
static void zeroPage(MemPage *pPage, int flags){
  unsigned char *data = pPage->aData;
  BtShared *pBt = pPage->pBt;
  int hdr = pPage->hdrOffset;
  int first;

  assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
  assert( &data[pBt->pageSize] == (unsigned char*)pPage );

  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( sqlite3_mutex_held(pBt->mutex) );
  memset(&data[hdr], 0, pBt->usableSize - hdr);
  data[hdr] = flags;
  first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
  memset(&data[hdr+1], 0, 4);
  data[hdr+7] = 0;
................................................................................
** Release a MemPage.  This should be called once for each prior
** call to sqlite3BtreeGetPage.
*/
static void releasePage(MemPage *pPage){
  if( pPage ){
    assert( pPage->aData );
    assert( pPage->pBt );
    assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );

    assert( sqlite3_mutex_held(pPage->pBt->mutex) );
    sqlite3PagerUnref(pPage->pDbPage);
  }
}

/*
** This routine is called when the reference count for a page
................................................................................
*/
static void unlockBtreeIfUnused(BtShared *pBt){
  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
    if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
      if( pBt->pPage1->aData==0 ){
        MemPage *pPage = pBt->pPage1;
        pPage->aData = &((u8*)pPage)[-pBt->pageSize];
        pPage->pBt = pBt;
        pPage->pgno = 1;
      }
      releasePage(pBt->pPage1);
    }
    pBt->pPage1 = 0;
    pBt->inStmt = 0;
................................................................................
  assert( pNewParent!=0 );
  if( pgno==0 ) return SQLITE_OK;
  assert( pBt->pPager!=0 );
  pDbPage = sqlite3PagerLookup(pBt->pPager, pgno);
  if( pDbPage ){
    pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage);
    if( pThis->isInit ){
      assert( pThis->aData==(sqlite3PagerGetData(pDbPage)) );
      if( pThis->pParent!=pNewParent ){
        if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage);
        pThis->pParent = pNewParent;
        sqlite3PagerRef(pNewParent->pDbPage);
      }
      pThis->idxParent = idx;
    }
................................................................................
  /*
  ** Make copies of the content of pPage and its siblings into aOld[].
  ** The rest of this function will use data from the copies rather
  ** that the original pages since the original pages will be in the
  ** process of being overwritten.
  */
  for(i=0; i<nOld; i++){
    MemPage *p = apCopy[i] = (MemPage*)&aCopy[i][pBt->pageSize];

    p->aData = &((u8*)p)[-pBt->pageSize];
    memcpy(p->aData, apOld[i]->aData, pBt->pageSize + sizeof(MemPage));
    /* The memcpy() above changes the value of p->aData so we have to
    ** set it again. */
    p->aData = &((u8*)p)[-pBt->pageSize];
  }

  /*
  ** Load pointers to all cells on sibling pages and the divider cells
  ** into the local apCell[] array.  Make copies of the divider cells
  ** into space obtained form aSpace[] and remove the the divider Cells
  ** from pParent.







|







 







>
|







 







|
>







 







|
>







 







|







 







|







 







|
>
|
|
<
<
<







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
...
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
...
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
....
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
....
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
....
4347
4348
4349
4350
4351
4352
4353
4354
4355
4356
4357
4358
4359
4360
4361
....
4898
4899
4900
4901
4902
4903
4904
4905
4906
4907
4908



4909
4910
4911
4912
4913
4914
4915
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.423 2007/09/06 22:19:15 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
** Including a description of file format and an overview of operation.
*/
#include "btreeInt.h"

................................................................................
  int top;           /* First byte of the cell content area */

  pBt = pPage->pBt;
  assert( pBt!=0 );
  assert( pParent==0 || pParent->pBt==pBt );
  assert( sqlite3_mutex_held(pBt->mutex) );
  assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
  assert( pPage == sqlite3PagerGetExtra(pPage->pDbPage) );
  assert( pPage->aData == sqlite3PagerGetData(pPage->pDbPage) );
  if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
    /* The parent page should never change unless the file is corrupt */
    return SQLITE_CORRUPT_BKPT;
  }
  if( pPage->isInit ) return SQLITE_OK;
  if( pPage->pParent==0 && pParent!=0 ){
    pPage->pParent = pParent;
................................................................................
static void zeroPage(MemPage *pPage, int flags){
  unsigned char *data = pPage->aData;
  BtShared *pBt = pPage->pBt;
  int hdr = pPage->hdrOffset;
  int first;

  assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
  assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
  assert( sqlite3PagerGetData(pPage->pDbPage) == data );
  assert( sqlite3PagerIswriteable(pPage->pDbPage) );
  assert( sqlite3_mutex_held(pBt->mutex) );
  memset(&data[hdr], 0, pBt->usableSize - hdr);
  data[hdr] = flags;
  first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
  memset(&data[hdr+1], 0, 4);
  data[hdr+7] = 0;
................................................................................
** Release a MemPage.  This should be called once for each prior
** call to sqlite3BtreeGetPage.
*/
static void releasePage(MemPage *pPage){
  if( pPage ){
    assert( pPage->aData );
    assert( pPage->pBt );
    assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
    assert( sqlite3PagerGetData(pPage->pDbPage)==pPage->aData );
    assert( sqlite3_mutex_held(pPage->pBt->mutex) );
    sqlite3PagerUnref(pPage->pDbPage);
  }
}

/*
** This routine is called when the reference count for a page
................................................................................
*/
static void unlockBtreeIfUnused(BtShared *pBt){
  assert( sqlite3_mutex_held(pBt->mutex) );
  if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
    if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
      if( pBt->pPage1->aData==0 ){
        MemPage *pPage = pBt->pPage1;
        pPage->aData = sqlite3PagerGetData(pPage->pDbPage);
        pPage->pBt = pBt;
        pPage->pgno = 1;
      }
      releasePage(pBt->pPage1);
    }
    pBt->pPage1 = 0;
    pBt->inStmt = 0;
................................................................................
  assert( pNewParent!=0 );
  if( pgno==0 ) return SQLITE_OK;
  assert( pBt->pPager!=0 );
  pDbPage = sqlite3PagerLookup(pBt->pPager, pgno);
  if( pDbPage ){
    pThis = (MemPage *)sqlite3PagerGetExtra(pDbPage);
    if( pThis->isInit ){
      assert( pThis->aData==sqlite3PagerGetData(pDbPage) );
      if( pThis->pParent!=pNewParent ){
        if( pThis->pParent ) sqlite3PagerUnref(pThis->pParent->pDbPage);
        pThis->pParent = pNewParent;
        sqlite3PagerRef(pNewParent->pDbPage);
      }
      pThis->idxParent = idx;
    }
................................................................................
  /*
  ** Make copies of the content of pPage and its siblings into aOld[].
  ** The rest of this function will use data from the copies rather
  ** that the original pages since the original pages will be in the
  ** process of being overwritten.
  */
  for(i=0; i<nOld; i++){
    MemPage *p = apCopy[i] = (MemPage*)aCopy[i];
    memcpy(p, apOld[i], sizeof(MemPage));
    p->aData = (void*)&p[1];
    memcpy(p->aData, apOld[i]->aData, pBt->pageSize);



  }

  /*
  ** Load pointers to all cells on sibling pages and the divider cells
  ** into the local apCell[] array.  Make copies of the divider cells
  ** into space obtained form aSpace[] and remove the the divider Cells
  ** from pParent.

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
...
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
....
1241
1242
1243
1244
1245
1246
1247

1248
1249
1250
1251
1252
1253
1254
....
2491
2492
2493
2494
2495
2496
2497

2498
2499
2500
2501
2502
2503
2504
....
3154
3155
3156
3157
3158
3159
3160

3161
3162
3163
3164
3165
3166
3167
....
3390
3391
3392
3393
3394
3395
3396

3397
3398
3399
3400
3401
3402
3403
....
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416







3417
3418
3419
3420
3421
3422
3423
3424
3425

3426
3427
3428
3429
3430
3431
3432
....
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014



4015

4016
4017
4018
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
....
4064
4065
4066
4067
4068
4069
4070
4071
4072

4073

4074
4075
4076
4077
4078
4079
4080
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.385 2007/09/03 15:19:35 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  u8 alwaysRollback;             /* Disable DontRollback() for this page */
  u8 needRead;                   /* Read content if PagerWrite() is called */
  short int nRef;                /* Number of users of this page */
  PgHdr *pDirty, *pPrevDirty;    /* Dirty pages */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  PagerLruLink gfree;            /* Global list of nRef==0 pages */
#endif
  u32 notUsed;                   /* Buffer space */
#ifdef SQLITE_CHECK_PAGES
  u32 pageHash;
#endif
  /* pPager->pageSize bytes of page data follow this header */
  /* Pager.nExtra bytes of local data follow the page data */
};

/*
** For an in-memory only database, some extra information is recorded about
** each page so that changes can be rolled back.  (Journal files are not
** used for in-memory databases.)  The following information is added to
** the end of every EXTRA block for in-memory databases.
................................................................................
# define CODEC2(P,D,N,X) ((char*)D)
#endif

/*
** Convert a pointer to a PgHdr into a pointer to its data
** and back again.
*/
#define PGHDR_TO_DATA(P)  ((void*)(&(P)[1]))
#define DATA_TO_PGHDR(D)  (&((PgHdr*)(D))[-1])
#define PGHDR_TO_EXTRA(G,P) ((void*)&((char*)(&(G)[1]))[(P)->pageSize])
#define PGHDR_TO_HIST(P,PGR)  \
            ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->pageSize+(PGR)->nExtra])

/*
** A open page cache is an instance of the following structure.
**
** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
** or SQLITE_FULL. Once one of the first three errors occurs, it persists
** and is returned as the result of every major pager API call.  The
................................................................................
  PgHdr *pPg, *pNext;
  if( pPager->errCode ) return;
  for(pPg=pPager->pAll; pPg; pPg=pNext){
    IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
    PAGER_INCR(sqlite3_pager_pgfree_count);
    pNext = pPg->pNextAll;
    lruListRemove(pPg);

    sqlite3_free(pPg);
  }
  assert(pPager->lru.pFirst==0);
  assert(pPager->lru.pFirstSynced==0);
  assert(pPager->lru.pLast==0);
  pPager->pStmt = 0;
  pPager->pAll = 0;
................................................................................
      ppPg = &pPg->pNextAll;
    }else{
      *ppPg = pPg->pNextAll;
      IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
      PAGER_INCR(sqlite3_pager_pgfree_count);
      unlinkPage(pPg);
      makeClean(pPg);

      sqlite3_free(pPg);
      pPager->nPage--;
    }
  }
}

/*
................................................................................
      nReleased += (
          sizeof(*pPg) + pPager->pageSize
          + sizeof(u32) + pPager->nExtra
          + MEMDB*sizeof(PgHistory) 
      );
      IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
      PAGER_INCR(sqlite3_pager_pgfree_count);

      sqlite3_free(pPg);
      pPager->nPage--;
    }else{
      /* An error occured whilst writing to the database file or 
      ** journal in pager_recycle(). The error is not returned to the 
      ** caller of this function. Instead, set the Pager.errCode variable.
      ** The error will be returned to the user (or users, in the case 
................................................................................
**     (4)  Either there is an available PgHdr that does not need
**          to be synced to disk or else disk syncing is currently
**          allowed.
*/
static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
  int rc = SQLITE_OK;
  PgHdr *pPg;


  /* Create a new PgHdr if any of the four conditions defined 
  ** above are met: */
  if( pPager->nPage<pPager->mxPage
   || pPager->lru.pFirst==0 
   || MEMDB
   || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
................................................................................
         pPager->nHash<256 ? 256 : pPager->nHash*2);
      if( pPager->nHash==0 ){
        rc = SQLITE_NOMEM;
        goto pager_allocate_out;
      }
    }
    pagerLeave(pPager);
    pPg = sqlite3_malloc( sizeof(*pPg) + pPager->pageSize
                            + sizeof(u32) + pPager->nExtra
                            + MEMDB*sizeof(PgHistory) );







    pagerEnter(pPager);
    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, sizeof(*pPg));
    if( MEMDB ){
      memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
    }

    pPg->pPager = pPager;
    pPg->pNextAll = pPager->pAll;
    pPager->pAll = pPg;
    pPager->nPage++;
  }else{
    /* Recycle an existing page with a zero ref-count. */
    rc = pager_recycle(pPager, &pPg);
................................................................................
  
    /* The transaction journal now exists and we have a RESERVED or an
    ** EXCLUSIVE lock on the main database file.  Write the current page to
    ** the transaction journal if it is not there already.
    */
    if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){
      if( (int)pPg->pgno <= pPager->origDbSize ){
        int szPg;
        if( MEMDB ){
          PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
          PAGERTRACE3("JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
          assert( pHist->pOrig==0 );
          pHist->pOrig = sqlite3_malloc( pPager->pageSize );
          if( pHist->pOrig ){
            memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
          }
        }else{
          u32 cksum, saved;
          char *pData2, *pEnd;

          /* We should never write to the journal file the page that
          ** contains the database locks.  The following assert verifies
          ** that we do not. */
          assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
          pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
          cksum = pager_cksum(pPager, (u8*)pData2);
          pEnd = pData2 + pPager->pageSize;
          pData2 -= 4;
          saved = *(u32*)pEnd;
          put32bits(pEnd, cksum);
          szPg = pPager->pageSize+8;
          put32bits(pData2, pPg->pgno);



          rc = sqlite3OsWrite(pPager->jfd, pData2, szPg, pPager->journalOff);

          IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno,
                   pPager->journalOff, szPg));
          PAGER_INCR(sqlite3_pager_writej_count);
          pPager->journalOff += szPg;
          PAGERTRACE5("JOURNAL %d page %d needSync=%d hash(%08x)\n",
               PAGERID(pPager), pPg->pgno, pPg->needSync, pager_pagehash(pPg));
          *(u32*)pEnd = saved;

          /* An error has occured writing to the journal file. The 
          ** transaction will be rolled back by the layer above.
          */
          if( rc!=SQLITE_OK ){
            return rc;
          }
................................................................................
        if( pHist->pStmt ){
          memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
        }
        PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
        page_add_to_stmt_list(pPg);
      }else{
        i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
        char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7)-4;
        put32bits(pData2, pPg->pgno);

        rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize+4, offset);

        PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
        if( rc!=SQLITE_OK ){
          return rc;
        }
        pPager->stmtNRec++;
        assert( pPager->aInStmt!=0 );
        pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);







|







 







<



|
|







 







|
<
|

|







 







>







 







>







 







>







 







>







 







<
|

>
>
>
>
>
>
>









>







 







<









|
|







|
|
|
|
|
<
>
>
>
|
>
|


<


<







 







|
|
>
|
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
...
267
268
269
270
271
272
273

274
275
276
277
278
279
280
281
282
283
284
285
...
308
309
310
311
312
313
314
315

316
317
318
319
320
321
322
323
324
325
....
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
....
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
....
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
....
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404
3405
....
3409
3410
3411
3412
3413
3414
3415

3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
....
3992
3993
3994
3995
3996
3997
3998

3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
4009
4010
4011
4012
4013
4014
4015
4016
4017
4018
4019
4020
4021

4022
4023
4024
4025
4026
4027
4028
4029

4030
4031

4032
4033
4034
4035
4036
4037
4038
....
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.386 2007/09/06 22:19:15 drh Exp $
*/
#ifndef SQLITE_OMIT_DISKIO
#include "sqliteInt.h"
#include <assert.h>
#include <string.h>

/*
................................................................................
  u8 alwaysRollback;             /* Disable DontRollback() for this page */
  u8 needRead;                   /* Read content if PagerWrite() is called */
  short int nRef;                /* Number of users of this page */
  PgHdr *pDirty, *pPrevDirty;    /* Dirty pages */
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  PagerLruLink gfree;            /* Global list of nRef==0 pages */
#endif

#ifdef SQLITE_CHECK_PAGES
  u32 pageHash;
#endif
  void *pData;                   /* Page data */
  /* Pager.nExtra bytes of local data appended to this header */
};

/*
** For an in-memory only database, some extra information is recorded about
** each page so that changes can be rolled back.  (Journal files are not
** used for in-memory databases.)  The following information is added to
** the end of every EXTRA block for in-memory databases.
................................................................................
# define CODEC2(P,D,N,X) ((char*)D)
#endif

/*
** Convert a pointer to a PgHdr into a pointer to its data
** and back again.
*/
#define PGHDR_TO_DATA(P)    ((P)->pData)

#define PGHDR_TO_EXTRA(G,P) ((void*)&((G)[1]))
#define PGHDR_TO_HIST(P,PGR)  \
            ((PgHistory*)&((char*)(&(P)[1]))[(PGR)->nExtra])

/*
** A open page cache is an instance of the following structure.
**
** Pager.errCode may be set to SQLITE_IOERR, SQLITE_CORRUPT, or
** or SQLITE_FULL. Once one of the first three errors occurs, it persists
** and is returned as the result of every major pager API call.  The
................................................................................
  PgHdr *pPg, *pNext;
  if( pPager->errCode ) return;
  for(pPg=pPager->pAll; pPg; pPg=pNext){
    IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
    PAGER_INCR(sqlite3_pager_pgfree_count);
    pNext = pPg->pNextAll;
    lruListRemove(pPg);
    sqlite3_free(pPg->pData);
    sqlite3_free(pPg);
  }
  assert(pPager->lru.pFirst==0);
  assert(pPager->lru.pFirstSynced==0);
  assert(pPager->lru.pLast==0);
  pPager->pStmt = 0;
  pPager->pAll = 0;
................................................................................
      ppPg = &pPg->pNextAll;
    }else{
      *ppPg = pPg->pNextAll;
      IOTRACE(("PGFREE %p %d\n", pPager, pPg->pgno));
      PAGER_INCR(sqlite3_pager_pgfree_count);
      unlinkPage(pPg);
      makeClean(pPg);
      sqlite3_free(pPg->pData);
      sqlite3_free(pPg);
      pPager->nPage--;
    }
  }
}

/*
................................................................................
      nReleased += (
          sizeof(*pPg) + pPager->pageSize
          + sizeof(u32) + pPager->nExtra
          + MEMDB*sizeof(PgHistory) 
      );
      IOTRACE(("PGFREE %p %d *\n", pPager, pPg->pgno));
      PAGER_INCR(sqlite3_pager_pgfree_count);
      sqlite3_free(pPg->pData);
      sqlite3_free(pPg);
      pPager->nPage--;
    }else{
      /* An error occured whilst writing to the database file or 
      ** journal in pager_recycle(). The error is not returned to the 
      ** caller of this function. Instead, set the Pager.errCode variable.
      ** The error will be returned to the user (or users, in the case 
................................................................................
**     (4)  Either there is an available PgHdr that does not need
**          to be synced to disk or else disk syncing is currently
**          allowed.
*/
static int pagerAllocatePage(Pager *pPager, PgHdr **ppPg){
  int rc = SQLITE_OK;
  PgHdr *pPg;
  void *pData;

  /* Create a new PgHdr if any of the four conditions defined 
  ** above are met: */
  if( pPager->nPage<pPager->mxPage
   || pPager->lru.pFirst==0 
   || MEMDB
   || (pPager->lru.pFirstSynced==0 && pPager->doNotSync)
................................................................................
         pPager->nHash<256 ? 256 : pPager->nHash*2);
      if( pPager->nHash==0 ){
        rc = SQLITE_NOMEM;
        goto pager_allocate_out;
      }
    }
    pagerLeave(pPager);

    pPg = sqlite3_malloc( sizeof(*pPg) + sizeof(u32) + pPager->nExtra
                            + MEMDB*sizeof(PgHistory) );
    if( pPg ){
      pData = sqlite3_malloc( pPager->pageSize );
      if( pData==0 ){
        sqlite3_free(pPg);
        pPg = 0;
      }
    }
    pagerEnter(pPager);
    if( pPg==0 ){
      rc = SQLITE_NOMEM;
      goto pager_allocate_out;
    }
    memset(pPg, 0, sizeof(*pPg));
    if( MEMDB ){
      memset(PGHDR_TO_HIST(pPg, pPager), 0, sizeof(PgHistory));
    }
    pPg->pData = pData;
    pPg->pPager = pPager;
    pPg->pNextAll = pPager->pAll;
    pPager->pAll = pPg;
    pPager->nPage++;
  }else{
    /* Recycle an existing page with a zero ref-count. */
    rc = pager_recycle(pPager, &pPg);
................................................................................
  
    /* The transaction journal now exists and we have a RESERVED or an
    ** EXCLUSIVE lock on the main database file.  Write the current page to
    ** the transaction journal if it is not there already.
    */
    if( !pPg->inJournal && (pPager->useJournal || MEMDB) ){
      if( (int)pPg->pgno <= pPager->origDbSize ){

        if( MEMDB ){
          PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
          PAGERTRACE3("JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
          assert( pHist->pOrig==0 );
          pHist->pOrig = sqlite3_malloc( pPager->pageSize );
          if( pHist->pOrig ){
            memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
          }
        }else{
          u32 cksum;
          char *pData2;

          /* We should never write to the journal file the page that
          ** contains the database locks.  The following assert verifies
          ** that we do not. */
          assert( pPg->pgno!=PAGER_MJ_PGNO(pPager) );
          pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
          cksum = pager_cksum(pPager, (u8*)pData2);
          rc = write32bits(pPager->jfd, pPager->journalOff, pPg->pgno);
          if( rc==SQLITE_OK ){
            rc = sqlite3OsWrite(pPager->jfd, pData2, pPager->pageSize,
                                pPager->journalOff + 4);
            pPager->journalOff += pPager->pageSize+4;

          }
          if( rc==SQLITE_OK ){
            rc = write32bits(pPager->jfd, pPager->journalOff, cksum);
            pPager->journalOff += 4;
          }
          IOTRACE(("JOUT %p %d %lld %d\n", pPager, pPg->pgno, 
                   pPager->journalOff, szPg));
          PAGER_INCR(sqlite3_pager_writej_count);

          PAGERTRACE5("JOURNAL %d page %d needSync=%d hash(%08x)\n",
               PAGERID(pPager), pPg->pgno, pPg->needSync, pager_pagehash(pPg));


          /* An error has occured writing to the journal file. The 
          ** transaction will be rolled back by the layer above.
          */
          if( rc!=SQLITE_OK ){
            return rc;
          }
................................................................................
        if( pHist->pStmt ){
          memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
        }
        PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
        page_add_to_stmt_list(pPg);
      }else{
        i64 offset = pPager->stmtNRec*(4+pPager->pageSize);
        char *pData2 = CODEC2(pPager, pData, pPg->pgno, 7);
        rc = write32bits(pPager->stfd, offset, pPg->pgno);
        if( rc==SQLITE_OK ){
          rc = sqlite3OsWrite(pPager->stfd, pData2, pPager->pageSize, offset+4);
        }
        PAGERTRACE3("STMT-JOURNAL %d page %d\n", PAGERID(pPager), pPg->pgno);
        if( rc!=SQLITE_OK ){
          return rc;
        }
        pPager->stmtNRec++;
        assert( pPager->aInStmt!=0 );
        pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);

Changes to test/btree.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
778
779
780
781
782
783
784

785
786
787
788
789
790
791
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree.test,v 1.40 2007/06/25 08:16:58 danielk1977 Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable default_autovacuum {
  finish_test
................................................................................
  lindex [btree_pager_stats $::b1] 1
} {1}
do_test btree-10.2 {
  set ::c1 [btree_cursor $::b1 2 1]
  lindex [btree_pager_stats $::b1] 1
} {2}
do_test btree-10.3 {

  for {set i 1} {$i<=30} {incr i} {
    set key [format %03d $i]
    set data "*** $key *** $key *** $key *** $key ***"
    btree_insert $::c1 $key $data
  }
  select_keys $::c1
} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030}







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is btree database backend
#
# $Id: btree.test,v 1.41 2007/09/06 22:19:15 drh Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl

ifcapable default_autovacuum {
  finish_test
................................................................................
  lindex [btree_pager_stats $::b1] 1
} {1}
do_test btree-10.2 {
  set ::c1 [btree_cursor $::b1 2 1]
  lindex [btree_pager_stats $::b1] 1
} {2}
do_test btree-10.3 {
btree_breakpoint
  for {set i 1} {$i<=30} {incr i} {
    set key [format %03d $i]
    set data "*** $key *** $key *** $key *** $key ***"
    btree_insert $::c1 $key $data
  }
  select_keys $::c1
} {001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030}