/ Check-in [67df4446]
Login

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

Overview
Comment:Further refinements to the virtual method implementation of sqlite3PagerGet().
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | pager-get-method
Files: files | file ages | folders
SHA1: 67df44464847b43f8c0b186157e31cc66c1e5796
User & Date: drh 2016-12-13 15:53:22
Context
2016-12-13
18:34
In the pager, avoid checking for the illegal page number 0 except when creating a new page. Closed-Leaf check-in: dee20ba9 user: drh tags: pager-get-method
15:53
Further refinements to the virtual method implementation of sqlite3PagerGet(). check-in: 67df4446 user: drh tags: pager-get-method
14:32
Make the sqlite3PagerGet() interface into a virtual method, with different implementations based on the current state of the pager. This gives a small performance increase by avoiding unnecessary branches inside the various methods. check-in: df5bb90d user: drh tags: pager-get-method
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/pager.c.

1018
1019
1020
1021
1022
1023
1024
1025

1026

1027
1028
1029
1030
1031
1032
1033
1034

1035
1036
1037
1038
1039
1040

1041
1042
1043
1044
1045
1046
1047
....
5291
5292
5293
5294
5295
5296
5297
5298
5299
5300
5301







5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
....
5347
5348
5349
5350
5351
5352
5353
5354
5355
5356
5357
5358
5359
5360
5361
5362
5363
5364
5365
5366
5367
5368
5369

5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383
5384
5385
5386
5387
5388
5389
5390
....
5397
5398
5399
5400
5401
5402
5403

5404
5405
5406
5407
5408
5409
5410
....
5421
5422
5423
5424
5425
5426
5427

5428
5429
5430
5431
5432
5433
5434
5435
....
5446
5447
5448
5449
5450
5451
5452

5453
5454
5455
5456
5457
5458
5459
....
5516
5517
5518
5519
5520
5521
5522

5523
5524
5525
5526
5527
5528
5529

  return zRet;
}
#endif

/* Forward references to the various page getters */
static int getPageNormal(Pager*,Pgno,DbPage**,int);
static int getPageMMap(Pager*,Pgno,DbPage**,int);

static int getPageError(Pager*,Pgno,DbPage**,int);


/*
** Set the Pager.xGet method for the appropriate routine used to fetch
** content from the pager.
*/
static void setGetterMethod(Pager *pPager){
  if( pPager->errCode ){
    pPager->xGet = getPageError;

  }else if( USEFETCH(pPager)
#ifdef SQLITE_HAS_CODEC
   && pPager->xCodec==0
#endif
  ){
    pPager->xGet = getPageMMap;

  }else{
    pPager->xGet = getPageNormal;
  }
}

/*
** Return true if it is necessary to write page *pPg into the sub-journal.
................................................................................
static void pagerUnlockIfUnused(Pager *pPager){
  if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
    pagerUnlockAndRollback(pPager);
  }
}

/*
** Acquire a reference to page number pgno in pager pPager (a page
** reference has type DbPage*). If the requested reference is 
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned.
**







** If the requested page is already in the cache, it is returned. 
** Otherwise, a new page object is allocated and populated with data
** read from the database file. In some cases, the pcache module may
** choose not to allocate a new page object and may reuse an existing
** object with no outstanding references.
**
** The extra data appended to a page is always initialized to zeros the 
** first time a page is loaded into memory. If the page requested is 
** already in the cache when this function is called, then the extra
** data is left as it was when the page object was last used.
**
** If the database image is smaller than the requested page or if a 
** non-zero value is passed as the noContent parameter and the 
** requested page is not already stored in the cache, then no 
** actual disk read occurs. In this case the memory image of the 
** page is initialized to all zeros. 
**
** If noContent is true, it means that we do not care about the contents
** of the page. This occurs in two scenarios:
**
**   a) When reading a free-list leaf page from the database, and
**
**   b) When a savepoint is being rolled back and we need to load
**      a new page into the cache to be filled with the data read
**      from the savepoint journal.
**
** If noContent is true, then the data returned is zeroed instead of
** being read from the database. Additionally, the bits corresponding
** to pgno in Pager.pInJournal (bitvec of pages already written to the
** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open
** savepoints are set. This means if the page is made writable at any
** point in the future, using a call to sqlite3PagerWrite(), its contents
** will not be journaled. This saves IO.
**
** The acquisition might fail for several reasons.  In all cases,
................................................................................
static int getPageNormal(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
  int rc = SQLITE_OK;
  PgHdr *pPg = 0;
  u32 iFrame = 0;                 /* Frame to read from WAL file */
  const int noContent = (flags & PAGER_GET_NOCONTENT);
  sqlite3_pcache_page *pBase;

  if( pgno==0 ){
    return SQLITE_CORRUPT_BKPT;
  }
  assert( pPager->errCode==SQLITE_OK );
  assert( pPager->eState>=PAGER_READER );
  assert( assert_pager_state(pPager) );
  assert( pPager->hasHeldSharedLock==1 );


  pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
  if( pBase==0 ){

    rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
    if( pBase==0 ){
      pPg = *ppPage = 0;
      rc = SQLITE_NOMEM_BKPT;
      goto pager_acquire_err;
    }
  }
  pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
  assert( pPg==(*ppPage) );
  assert( pPg->pgno==pgno );
  assert( pPg->pPager==pPager || pPg->pPager==0 );

  if( pPg->pPager && !noContent ){
    /* In this case the pcache already contains an initialized copy of
    ** the page. Return without further ado.  */
    assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
    pPager->aStat[PAGER_STAT_HIT]++;
    return SQLITE_OK;

  }else{
................................................................................
    ** number greater than this, or the unused locking-page, is requested. */
    if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
      rc = SQLITE_CORRUPT_BKPT;
      goto pager_acquire_err;
    }

    assert( !isOpen(pPager->fd) || !MEMDB );

    if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
      if( pgno>pPager->mxPgno ){
        rc = SQLITE_FULL;
        goto pager_acquire_err;
      }
      if( noContent ){
        /* Failure to set the bits in the InJournal bit-vectors is benign.
................................................................................
        TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno);
        testcase( rc==SQLITE_NOMEM );
        sqlite3EndBenignMalloc();
      }
      memset(pPg->pData, 0, pPager->pageSize);
      IOTRACE(("ZERO %p %d\n", pPager, pgno));
    }else{

      if( pagerUseWal(pPager) /*&& bMmapOk==0*/ ){
        rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
        if( rc!=SQLITE_OK ) goto pager_acquire_err;
      }
      assert( pPg->pPager==pPager );
      pPager->aStat[PAGER_STAT_MISS]++;
      rc = readDbPage(pPg, iFrame);
      if( rc!=SQLITE_OK ){
................................................................................
    sqlite3PcacheDrop(pPg);
  }
  pagerUnlockIfUnused(pPager);
  *ppPage = 0;
  return rc;
}


/* The page getter for when memory-mapped I/O is enabled */
static int getPageMMap(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
................................................................................
    if( rc!=SQLITE_OK ){
      *ppPage = 0;
      return rc;
    }
  }
  return getPageNormal(pPager, pgno, ppPage, flags);
}


/* The page getter method for when the pager is an error state */
static int getPageError(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */







|
>
|
>








>






>







 







|
|


>
>
>
>
>
>
>











|
|




|
|







|
|







 







|
|
<













>



<









|







 







>







 







>
|







 







>







 







>







1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
....
5295
5296
5297
5298
5299
5300
5301
5302
5303
5304
5305
5306
5307
5308
5309
5310
5311
5312
5313
5314
5315
5316
5317
5318
5319
5320
5321
5322
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
5346
5347
....
5358
5359
5360
5361
5362
5363
5364
5365
5366

5367
5368
5369
5370
5371
5372
5373
5374
5375
5376
5377
5378
5379
5380
5381
5382
5383

5384
5385
5386
5387
5388
5389
5390
5391
5392
5393
5394
5395
5396
5397
5398
5399
5400
....
5407
5408
5409
5410
5411
5412
5413
5414
5415
5416
5417
5418
5419
5420
5421
....
5432
5433
5434
5435
5436
5437
5438
5439
5440
5441
5442
5443
5444
5445
5446
5447
....
5458
5459
5460
5461
5462
5463
5464
5465
5466
5467
5468
5469
5470
5471
5472
....
5529
5530
5531
5532
5533
5534
5535
5536
5537
5538
5539
5540
5541
5542
5543

  return zRet;
}
#endif

/* Forward references to the various page getters */
static int getPageNormal(Pager*,Pgno,DbPage**,int);
static int getPageError(Pager*,Pgno,DbPage**,int);
#if SQLITE_MAX_MMAP_SIZE>0
static int getPageMMap(Pager*,Pgno,DbPage**,int);
#endif

/*
** Set the Pager.xGet method for the appropriate routine used to fetch
** content from the pager.
*/
static void setGetterMethod(Pager *pPager){
  if( pPager->errCode ){
    pPager->xGet = getPageError;
#if SQLITE_MAX_MMAP_SIZE>0
  }else if( USEFETCH(pPager)
#ifdef SQLITE_HAS_CODEC
   && pPager->xCodec==0
#endif
  ){
    pPager->xGet = getPageMMap;
#endif /* SQLITE_MAX_MMAP_SIZE>0 */
  }else{
    pPager->xGet = getPageNormal;
  }
}

/*
** Return true if it is necessary to write page *pPg into the sub-journal.
................................................................................
static void pagerUnlockIfUnused(Pager *pPager){
  if( pPager->nMmapOut==0 && (sqlite3PcacheRefCount(pPager->pPCache)==0) ){
    pagerUnlockAndRollback(pPager);
  }
}

/*
** The page getter methods each try to acquire a reference to a
** page with page number pgno. If the requested reference is 
** successfully obtained, it is copied to *ppPage and SQLITE_OK returned.
**
** There are different implementations of the getter method depending
** on the current state of the pager.
**
**     getPageNormal()         --  The normal getter
**     getPageError()          --  Used if the pager is in an error state
**     getPageMmap()           --  Used if memory-mapped I/O is enabled
**
** If the requested page is already in the cache, it is returned. 
** Otherwise, a new page object is allocated and populated with data
** read from the database file. In some cases, the pcache module may
** choose not to allocate a new page object and may reuse an existing
** object with no outstanding references.
**
** The extra data appended to a page is always initialized to zeros the 
** first time a page is loaded into memory. If the page requested is 
** already in the cache when this function is called, then the extra
** data is left as it was when the page object was last used.
**
** If the database image is smaller than the requested page or if 
** the flags parameter contains the PAGER_GET_NOCONTENT bit and the 
** requested page is not already stored in the cache, then no 
** actual disk read occurs. In this case the memory image of the 
** page is initialized to all zeros. 
**
** If PAGER_GET_NOCONTENT is true, it means that we do not care about
** the contents of the page. This occurs in two scenarios:
**
**   a) When reading a free-list leaf page from the database, and
**
**   b) When a savepoint is being rolled back and we need to load
**      a new page into the cache to be filled with the data read
**      from the savepoint journal.
**
** If PAGER_GET_NOCONTENT is true, then the data returned is zeroed instead
** of being read from the database. Additionally, the bits corresponding
** to pgno in Pager.pInJournal (bitvec of pages already written to the
** journal file) and the PagerSavepoint.pInSavepoint bitvecs of any open
** savepoints are set. This means if the page is made writable at any
** point in the future, using a call to sqlite3PagerWrite(), its contents
** will not be journaled. This saves IO.
**
** The acquisition might fail for several reasons.  In all cases,
................................................................................
static int getPageNormal(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
  int rc = SQLITE_OK;
  PgHdr *pPg;
  u8 noContent;                   /* True if PAGER_GET_NOCONTENT is set */

  sqlite3_pcache_page *pBase;

  if( pgno==0 ){
    return SQLITE_CORRUPT_BKPT;
  }
  assert( pPager->errCode==SQLITE_OK );
  assert( pPager->eState>=PAGER_READER );
  assert( assert_pager_state(pPager) );
  assert( pPager->hasHeldSharedLock==1 );


  pBase = sqlite3PcacheFetch(pPager->pPCache, pgno, 3);
  if( pBase==0 ){
    pPg = 0;
    rc = sqlite3PcacheFetchStress(pPager->pPCache, pgno, &pBase);
    if( rc!=SQLITE_OK ) goto pager_acquire_err;
    if( pBase==0 ){

      rc = SQLITE_NOMEM_BKPT;
      goto pager_acquire_err;
    }
  }
  pPg = *ppPage = sqlite3PcacheFetchFinish(pPager->pPCache, pgno, pBase);
  assert( pPg==(*ppPage) );
  assert( pPg->pgno==pgno );
  assert( pPg->pPager==pPager || pPg->pPager==0 );

  if( pPg->pPager ){
    /* In this case the pcache already contains an initialized copy of
    ** the page. Return without further ado.  */
    assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) );
    pPager->aStat[PAGER_STAT_HIT]++;
    return SQLITE_OK;

  }else{
................................................................................
    ** number greater than this, or the unused locking-page, is requested. */
    if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){
      rc = SQLITE_CORRUPT_BKPT;
      goto pager_acquire_err;
    }

    assert( !isOpen(pPager->fd) || !MEMDB );
    noContent = (flags & PAGER_GET_NOCONTENT)!=0;
    if( !isOpen(pPager->fd) || pPager->dbSize<pgno || noContent ){
      if( pgno>pPager->mxPgno ){
        rc = SQLITE_FULL;
        goto pager_acquire_err;
      }
      if( noContent ){
        /* Failure to set the bits in the InJournal bit-vectors is benign.
................................................................................
        TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno);
        testcase( rc==SQLITE_NOMEM );
        sqlite3EndBenignMalloc();
      }
      memset(pPg->pData, 0, pPager->pageSize);
      IOTRACE(("ZERO %p %d\n", pPager, pgno));
    }else{
      u32 iFrame = 0;                 /* Frame to read from WAL file */
      if( pagerUseWal(pPager) ){
        rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
        if( rc!=SQLITE_OK ) goto pager_acquire_err;
      }
      assert( pPg->pPager==pPager );
      pPager->aStat[PAGER_STAT_MISS]++;
      rc = readDbPage(pPg, iFrame);
      if( rc!=SQLITE_OK ){
................................................................................
    sqlite3PcacheDrop(pPg);
  }
  pagerUnlockIfUnused(pPager);
  *ppPage = 0;
  return rc;
}

#if SQLITE_MAX_MMAP_SIZE>0
/* The page getter for when memory-mapped I/O is enabled */
static int getPageMMap(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */
){
................................................................................
    if( rc!=SQLITE_OK ){
      *ppPage = 0;
      return rc;
    }
  }
  return getPageNormal(pPager, pgno, ppPage, flags);
}
#endif /* SQLITE_MAX_MMAP_SIZE>0 */

/* The page getter method for when the pager is an error state */
static int getPageError(
  Pager *pPager,      /* The pager open on the database file */
  Pgno pgno,          /* Page number to fetch */
  DbPage **ppPage,    /* Write a pointer to the page here */
  int flags           /* PAGER_GET_XXX flags */