mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
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. FossilOrigin-Name: df5bb90d208e0633056389e97696d260e3830e8d
This commit is contained in:
220
src/pager.c
220
src/pager.c
@@ -693,6 +693,7 @@ struct Pager {
|
||||
int nRead; /* Database pages read */
|
||||
#endif
|
||||
void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
|
||||
int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
void *(*xCodec)(void*,void*,Pgno,int); /* Routine for en/decoding data */
|
||||
void (*xCodecSizeChng)(void*,int,int); /* Notify of page size changes */
|
||||
@@ -1019,6 +1020,29 @@ static char *print_pager_state(Pager *p){
|
||||
}
|
||||
#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.
|
||||
** A page needs to be written into the sub-journal if there exists one
|
||||
@@ -1833,6 +1857,7 @@ static void pager_unlock(Pager *pPager){
|
||||
}
|
||||
if( USEFETCH(pPager) ) sqlite3OsUnfetch(pPager->fd, 0, 0);
|
||||
pPager->errCode = SQLITE_OK;
|
||||
setGetterMethod(pPager);
|
||||
}
|
||||
|
||||
pPager->journalOff = 0;
|
||||
@@ -1870,6 +1895,7 @@ static int pager_error(Pager *pPager, int rc){
|
||||
if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){
|
||||
pPager->errCode = rc;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
setGetterMethod(pPager);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
@@ -3437,6 +3463,7 @@ static void pagerFixMaplimit(Pager *pPager){
|
||||
sqlite3_int64 sz;
|
||||
sz = pPager->szMmap;
|
||||
pPager->bUseFetch = (sz>0);
|
||||
setGetterMethod(pPager);
|
||||
sqlite3OsFileControlHint(pPager->fd, SQLITE_FCNTL_MMAP_SIZE, &sz);
|
||||
}
|
||||
#endif
|
||||
@@ -4854,6 +4881,7 @@ act_like_temp_file:
|
||||
/* pPager->xBusyHandler = 0; */
|
||||
/* pPager->pBusyHandlerArg = 0; */
|
||||
pPager->xReiniter = xReinit;
|
||||
setGetterMethod(pPager);
|
||||
/* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
|
||||
/* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */
|
||||
|
||||
@@ -5316,7 +5344,7 @@ static void pagerUnlockIfUnused(Pager *pPager){
|
||||
** Since Lookup() never goes to disk, it never has to deal with locks
|
||||
** or journal files.
|
||||
*/
|
||||
int sqlite3PagerGet(
|
||||
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 */
|
||||
@@ -5326,92 +5354,28 @@ int sqlite3PagerGet(
|
||||
PgHdr *pPg = 0;
|
||||
u32 iFrame = 0; /* Frame to read from WAL file */
|
||||
const int noContent = (flags & PAGER_GET_NOCONTENT);
|
||||
sqlite3_pcache_page *pBase;
|
||||
|
||||
/* It is acceptable to use a read-only (mmap) page for any page except
|
||||
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
|
||||
** flag was specified by the caller. And so long as the db is not a
|
||||
** temporary or in-memory database. */
|
||||
const int bMmapOk = (pgno>1 && USEFETCH(pPager)
|
||||
&& (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
&& pPager->xCodec==0
|
||||
#endif
|
||||
);
|
||||
|
||||
/* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
|
||||
** allows the compiler optimizer to reuse the results of the "pgno>1"
|
||||
** test in the previous statement, and avoid testing pgno==0 in the
|
||||
** common case where pgno is large. */
|
||||
if( pgno<=1 && pgno==0 ){
|
||||
if( pgno==0 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
assert( pPager->errCode==SQLITE_OK );
|
||||
assert( pPager->eState>=PAGER_READER );
|
||||
assert( assert_pager_state(pPager) );
|
||||
assert( noContent==0 || bMmapOk==0 );
|
||||
|
||||
assert( pPager->hasHeldSharedLock==1 );
|
||||
|
||||
/* If the pager is in the error state, return an error immediately.
|
||||
** Otherwise, request the page from the PCache layer. */
|
||||
if( pPager->errCode!=SQLITE_OK ){
|
||||
rc = pPager->errCode;
|
||||
}else{
|
||||
if( bMmapOk && pagerUseWal(pPager) ){
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
|
||||
if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
||||
}
|
||||
|
||||
if( bMmapOk && iFrame==0 ){
|
||||
void *pData = 0;
|
||||
|
||||
rc = sqlite3OsFetch(pPager->fd,
|
||||
(i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData
|
||||
);
|
||||
|
||||
if( rc==SQLITE_OK && pData ){
|
||||
if( pPager->eState>PAGER_READER || pPager->tempFile ){
|
||||
pPg = sqlite3PagerLookup(pPager, pgno);
|
||||
}
|
||||
if( pPg==0 ){
|
||||
rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
|
||||
}else{
|
||||
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
|
||||
}
|
||||
if( pPg ){
|
||||
assert( rc==SQLITE_OK );
|
||||
*ppPage = pPg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto pager_acquire_err;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
sqlite3_pcache_page *pBase;
|
||||
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!=0 );
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* Either the call to sqlite3PcacheFetch() returned an error or the
|
||||
** pager was already in the error-state when this function was called.
|
||||
** Set pPg to 0 and jump to the exception handler. */
|
||||
pPg = 0;
|
||||
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 );
|
||||
@@ -5461,7 +5425,7 @@ int sqlite3PagerGet(
|
||||
memset(pPg->pData, 0, pPager->pageSize);
|
||||
IOTRACE(("ZERO %p %d\n", pPager, pgno));
|
||||
}else{
|
||||
if( pagerUseWal(pPager) && bMmapOk==0 ){
|
||||
if( pagerUseWal(pPager) /*&& bMmapOk==0*/ ){
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
|
||||
if( rc!=SQLITE_OK ) goto pager_acquire_err;
|
||||
}
|
||||
@@ -5474,7 +5438,6 @@ int sqlite3PagerGet(
|
||||
}
|
||||
pager_set_pagehash(pPg);
|
||||
}
|
||||
|
||||
return SQLITE_OK;
|
||||
|
||||
pager_acquire_err:
|
||||
@@ -5483,11 +5446,105 @@ pager_acquire_err:
|
||||
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 */
|
||||
){
|
||||
int rc = SQLITE_OK;
|
||||
PgHdr *pPg = 0;
|
||||
u32 iFrame = 0; /* Frame to read from WAL file */
|
||||
|
||||
assert( USEFETCH(pPager) );
|
||||
#ifdef SQLITE_HAS_CODEC
|
||||
assert( pPager->xCodec==0 );
|
||||
#endif
|
||||
|
||||
/* It is acceptable to use a read-only (mmap) page for any page except
|
||||
** page 1 if there is no write-transaction open or the ACQUIRE_READONLY
|
||||
** flag was specified by the caller. And so long as the db is not a
|
||||
** temporary or in-memory database. */
|
||||
const int bMmapOk = (pgno>1
|
||||
&& (pPager->eState==PAGER_READER || (flags & PAGER_GET_READONLY))
|
||||
);
|
||||
|
||||
/* Optimization note: Adding the "pgno<=1" term before "pgno==0" here
|
||||
** allows the compiler optimizer to reuse the results of the "pgno>1"
|
||||
** test in the previous statement, and avoid testing pgno==0 in the
|
||||
** common case where pgno is large. */
|
||||
if( pgno<=1 && pgno==0 ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
}
|
||||
assert( pPager->eState>=PAGER_READER );
|
||||
assert( assert_pager_state(pPager) );
|
||||
assert( pPager->hasHeldSharedLock==1 );
|
||||
assert( pPager->errCode==SQLITE_OK );
|
||||
|
||||
if( bMmapOk && pagerUseWal(pPager) ){
|
||||
rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iFrame);
|
||||
if( rc!=SQLITE_OK ){
|
||||
*ppPage = 0;
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
if( bMmapOk && iFrame==0 ){
|
||||
void *pData = 0;
|
||||
rc = sqlite3OsFetch(pPager->fd,
|
||||
(i64)(pgno-1) * pPager->pageSize, pPager->pageSize, &pData
|
||||
);
|
||||
if( rc==SQLITE_OK && pData ){
|
||||
if( pPager->eState>PAGER_READER || pPager->tempFile ){
|
||||
pPg = sqlite3PagerLookup(pPager, pgno);
|
||||
}
|
||||
if( pPg==0 ){
|
||||
rc = pagerAcquireMapPage(pPager, pgno, pData, &pPg);
|
||||
}else{
|
||||
sqlite3OsUnfetch(pPager->fd, (i64)(pgno-1)*pPager->pageSize, pData);
|
||||
}
|
||||
if( pPg ){
|
||||
assert( rc==SQLITE_OK );
|
||||
*ppPage = pPg;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
}
|
||||
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 */
|
||||
){
|
||||
assert( pPager->errCode!=SQLITE_OK );
|
||||
*ppPage = 0;
|
||||
return pPager->errCode;
|
||||
}
|
||||
|
||||
|
||||
/* Dispatch all page fetch requests to the appropriate getter method.
|
||||
*/
|
||||
int sqlite3PagerGet(
|
||||
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 */
|
||||
){
|
||||
return pPager->xGet(pPager, pgno, ppPage, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
** Acquire a page if it is already in the in-memory cache. Do
|
||||
** not read the page from disk. Return a pointer to the page,
|
||||
@@ -6460,6 +6517,7 @@ int sqlite3PagerRollback(Pager *pPager){
|
||||
*/
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
setGetterMethod(pPager);
|
||||
return rc;
|
||||
}
|
||||
}else{
|
||||
@@ -6721,6 +6779,7 @@ int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint){
|
||||
){
|
||||
pPager->errCode = SQLITE_ABORT;
|
||||
pPager->eState = PAGER_ERROR;
|
||||
setGettterMethod(pPager);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -6793,6 +6852,7 @@ void sqlite3PagerSetCodec(
|
||||
pPager->xCodecSizeChng = xCodecSizeChng;
|
||||
pPager->xCodecFree = xCodecFree;
|
||||
pPager->pCodec = pCodec;
|
||||
setGetterMethod(pPager);
|
||||
pagerReportSize(pPager);
|
||||
}
|
||||
void *sqlite3PagerGetCodec(Pager *pPager){
|
||||
|
Reference in New Issue
Block a user