| Index: third_party/sqlite/src/src/pcache.c | 
| diff --git a/third_party/sqlite/src/src/pcache.c b/third_party/sqlite/src/src/pcache.c | 
| index 242f3071d90ae2fec8c16b9f97ea93c8bbd4aae3..191a9d00f41ded328642e71fa5da12138f38bc35 100644 | 
| --- a/third_party/sqlite/src/src/pcache.c | 
| +++ b/third_party/sqlite/src/src/pcache.c | 
| @@ -20,10 +20,11 @@ struct PCache { | 
| PgHdr *pDirty, *pDirtyTail;         /* List of dirty pages in LRU order */ | 
| PgHdr *pSynced;                     /* Last synced page in dirty page list */ | 
| int nRef;                           /* Number of referenced pages */ | 
| -  int nMax;                           /* Configured cache size */ | 
| +  int szCache;                        /* Configured cache size */ | 
| int szPage;                         /* Size of every page in this cache */ | 
| int szExtra;                        /* Size of extra space for each page */ | 
| -  int bPurgeable;                     /* True if pages are on backing store */ | 
| +  u8 bPurgeable;                      /* True if pages are on backing store */ | 
| +  u8 eCreate;                         /* eCreate value for for xFetch() */ | 
| int (*xStress)(void*,PgHdr*);       /* Call to try make a page clean */ | 
| void *pStress;                      /* Argument to xStress */ | 
| sqlite3_pcache *pCache;             /* Pluggable cache module */ | 
| @@ -44,81 +45,71 @@ struct PCache { | 
|  | 
| /********************************** Linked List Management ********************/ | 
|  | 
| -#if !defined(NDEBUG) && defined(SQLITE_ENABLE_EXPENSIVE_ASSERT) | 
| -/* | 
| -** Check that the pCache->pSynced variable is set correctly. If it | 
| -** is not, either fail an assert or return zero. Otherwise, return | 
| -** non-zero. This is only used in debugging builds, as follows: | 
| -** | 
| -**   expensive_assert( pcacheCheckSynced(pCache) ); | 
| -*/ | 
| -static int pcacheCheckSynced(PCache *pCache){ | 
| -  PgHdr *p; | 
| -  for(p=pCache->pDirtyTail; p!=pCache->pSynced; p=p->pDirtyPrev){ | 
| -    assert( p->nRef || (p->flags&PGHDR_NEED_SYNC) ); | 
| -  } | 
| -  return (p==0 || p->nRef || (p->flags&PGHDR_NEED_SYNC)==0); | 
| -} | 
| -#endif /* !NDEBUG && SQLITE_ENABLE_EXPENSIVE_ASSERT */ | 
| +/* Allowed values for second argument to pcacheManageDirtyList() */ | 
| +#define PCACHE_DIRTYLIST_REMOVE   1    /* Remove pPage from dirty list */ | 
| +#define PCACHE_DIRTYLIST_ADD      2    /* Add pPage to the dirty list */ | 
| +#define PCACHE_DIRTYLIST_FRONT    3    /* Move pPage to the front of the list */ | 
|  | 
| /* | 
| -** Remove page pPage from the list of dirty pages. | 
| +** Manage pPage's participation on the dirty list.  Bits of the addRemove | 
| +** argument determines what operation to do.  The 0x01 bit means first | 
| +** remove pPage from the dirty list.  The 0x02 means add pPage back to | 
| +** the dirty list.  Doing both moves pPage to the front of the dirty list. | 
| */ | 
| -static void pcacheRemoveFromDirtyList(PgHdr *pPage){ | 
| +static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ | 
| PCache *p = pPage->pCache; | 
|  | 
| -  assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); | 
| -  assert( pPage->pDirtyPrev || pPage==p->pDirty ); | 
| - | 
| -  /* Update the PCache1.pSynced variable if necessary. */ | 
| -  if( p->pSynced==pPage ){ | 
| -    PgHdr *pSynced = pPage->pDirtyPrev; | 
| -    while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ | 
| -      pSynced = pSynced->pDirtyPrev; | 
| +  if( addRemove & PCACHE_DIRTYLIST_REMOVE ){ | 
| +    assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); | 
| +    assert( pPage->pDirtyPrev || pPage==p->pDirty ); | 
| + | 
| +    /* Update the PCache1.pSynced variable if necessary. */ | 
| +    if( p->pSynced==pPage ){ | 
| +      PgHdr *pSynced = pPage->pDirtyPrev; | 
| +      while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ | 
| +        pSynced = pSynced->pDirtyPrev; | 
| +      } | 
| +      p->pSynced = pSynced; | 
| +    } | 
| + | 
| +    if( pPage->pDirtyNext ){ | 
| +      pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; | 
| +    }else{ | 
| +      assert( pPage==p->pDirtyTail ); | 
| +      p->pDirtyTail = pPage->pDirtyPrev; | 
| +    } | 
| +    if( pPage->pDirtyPrev ){ | 
| +      pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; | 
| +    }else{ | 
| +      assert( pPage==p->pDirty ); | 
| +      p->pDirty = pPage->pDirtyNext; | 
| +      if( p->pDirty==0 && p->bPurgeable ){ | 
| +        assert( p->eCreate==1 ); | 
| +        p->eCreate = 2; | 
| +      } | 
| +    } | 
| +    pPage->pDirtyNext = 0; | 
| +    pPage->pDirtyPrev = 0; | 
| +  } | 
| +  if( addRemove & PCACHE_DIRTYLIST_ADD ){ | 
| +    assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); | 
| + | 
| +    pPage->pDirtyNext = p->pDirty; | 
| +    if( pPage->pDirtyNext ){ | 
| +      assert( pPage->pDirtyNext->pDirtyPrev==0 ); | 
| +      pPage->pDirtyNext->pDirtyPrev = pPage; | 
| +    }else{ | 
| +      p->pDirtyTail = pPage; | 
| +      if( p->bPurgeable ){ | 
| +        assert( p->eCreate==2 ); | 
| +        p->eCreate = 1; | 
| +      } | 
| +    } | 
| +    p->pDirty = pPage; | 
| +    if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ | 
| +      p->pSynced = pPage; | 
| } | 
| -    p->pSynced = pSynced; | 
| -  } | 
| - | 
| -  if( pPage->pDirtyNext ){ | 
| -    pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; | 
| -  }else{ | 
| -    assert( pPage==p->pDirtyTail ); | 
| -    p->pDirtyTail = pPage->pDirtyPrev; | 
| -  } | 
| -  if( pPage->pDirtyPrev ){ | 
| -    pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; | 
| -  }else{ | 
| -    assert( pPage==p->pDirty ); | 
| -    p->pDirty = pPage->pDirtyNext; | 
| -  } | 
| -  pPage->pDirtyNext = 0; | 
| -  pPage->pDirtyPrev = 0; | 
| - | 
| -  expensive_assert( pcacheCheckSynced(p) ); | 
| -} | 
| - | 
| -/* | 
| -** Add page pPage to the head of the dirty list (PCache1.pDirty is set to | 
| -** pPage). | 
| -*/ | 
| -static void pcacheAddToDirtyList(PgHdr *pPage){ | 
| -  PCache *p = pPage->pCache; | 
| - | 
| -  assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); | 
| - | 
| -  pPage->pDirtyNext = p->pDirty; | 
| -  if( pPage->pDirtyNext ){ | 
| -    assert( pPage->pDirtyNext->pDirtyPrev==0 ); | 
| -    pPage->pDirtyNext->pDirtyPrev = pPage; | 
| -  } | 
| -  p->pDirty = pPage; | 
| -  if( !p->pDirtyTail ){ | 
| -    p->pDirtyTail = pPage; | 
| -  } | 
| -  if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ | 
| -    p->pSynced = pPage; | 
| } | 
| -  expensive_assert( pcacheCheckSynced(p) ); | 
| } | 
|  | 
| /* | 
| @@ -126,12 +117,22 @@ static void pcacheAddToDirtyList(PgHdr *pPage){ | 
| ** being used for an in-memory database, this function is a no-op. | 
| */ | 
| static void pcacheUnpin(PgHdr *p){ | 
| -  PCache *pCache = p->pCache; | 
| -  if( pCache->bPurgeable ){ | 
| +  if( p->pCache->bPurgeable ){ | 
| if( p->pgno==1 ){ | 
| -      pCache->pPage1 = 0; | 
| +      p->pCache->pPage1 = 0; | 
| } | 
| -    sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0); | 
| +    sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); | 
| +  } | 
| +} | 
| + | 
| +/* | 
| +** Compute the number of pages of cache requested. | 
| +*/ | 
| +static int numberOfCachePages(PCache *p){ | 
| +  if( p->szCache>=0 ){ | 
| +    return p->szCache; | 
| +  }else{ | 
| +    return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); | 
| } | 
| } | 
|  | 
| @@ -141,18 +142,18 @@ static void pcacheUnpin(PgHdr *p){ | 
| ** functions are threadsafe. | 
| */ | 
| int sqlite3PcacheInitialize(void){ | 
| -  if( sqlite3GlobalConfig.pcache.xInit==0 ){ | 
| +  if( sqlite3GlobalConfig.pcache2.xInit==0 ){ | 
| /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the | 
| ** built-in default page cache is used instead of the application defined | 
| ** page cache. */ | 
| sqlite3PCacheSetDefault(); | 
| } | 
| -  return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg); | 
| +  return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg); | 
| } | 
| void sqlite3PcacheShutdown(void){ | 
| -  if( sqlite3GlobalConfig.pcache.xShutdown ){ | 
| +  if( sqlite3GlobalConfig.pcache2.xShutdown ){ | 
| /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ | 
| -    sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg); | 
| +    sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg); | 
| } | 
| } | 
|  | 
| @@ -167,7 +168,7 @@ int sqlite3PcacheSize(void){ return sizeof(PCache); } | 
| ** The caller discovers how much space needs to be allocated by | 
| ** calling sqlite3PcacheSize(). | 
| */ | 
| -void sqlite3PcacheOpen( | 
| +int sqlite3PcacheOpen( | 
| int szPage,                  /* Size of every page */ | 
| int szExtra,                 /* Extra space associated with each page */ | 
| int bPurgeable,              /* True if pages are on backing store */ | 
| @@ -176,134 +177,211 @@ void sqlite3PcacheOpen( | 
| PCache *p                    /* Preallocated space for the PCache */ | 
| ){ | 
| memset(p, 0, sizeof(PCache)); | 
| -  p->szPage = szPage; | 
| +  p->szPage = 1; | 
| p->szExtra = szExtra; | 
| p->bPurgeable = bPurgeable; | 
| +  p->eCreate = 2; | 
| p->xStress = xStress; | 
| p->pStress = pStress; | 
| -  p->nMax = 100; | 
| +  p->szCache = 100; | 
| +  return sqlite3PcacheSetPageSize(p, szPage); | 
| } | 
|  | 
| /* | 
| ** Change the page size for PCache object. The caller must ensure that there | 
| ** are no outstanding page references when this function is called. | 
| */ | 
| -void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ | 
| +int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ | 
| assert( pCache->nRef==0 && pCache->pDirty==0 ); | 
| -  if( pCache->pCache ){ | 
| -    sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); | 
| -    pCache->pCache = 0; | 
| +  if( pCache->szPage ){ | 
| +    sqlite3_pcache *pNew; | 
| +    pNew = sqlite3GlobalConfig.pcache2.xCreate( | 
| +                szPage, pCache->szExtra + sizeof(PgHdr), pCache->bPurgeable | 
| +    ); | 
| +    if( pNew==0 ) return SQLITE_NOMEM; | 
| +    sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); | 
| +    if( pCache->pCache ){ | 
| +      sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); | 
| +    } | 
| +    pCache->pCache = pNew; | 
| pCache->pPage1 = 0; | 
| +    pCache->szPage = szPage; | 
| } | 
| -  pCache->szPage = szPage; | 
| +  return SQLITE_OK; | 
| } | 
|  | 
| /* | 
| ** Try to obtain a page from the cache. | 
| -*/ | 
| -int sqlite3PcacheFetch( | 
| +** | 
| +** This routine returns a pointer to an sqlite3_pcache_page object if | 
| +** such an object is already in cache, or if a new one is created. | 
| +** This routine returns a NULL pointer if the object was not in cache | 
| +** and could not be created. | 
| +** | 
| +** The createFlags should be 0 to check for existing pages and should | 
| +** be 3 (not 1, but 3) to try to create a new page. | 
| +** | 
| +** If the createFlag is 0, then NULL is always returned if the page | 
| +** is not already in the cache.  If createFlag is 1, then a new page | 
| +** is created only if that can be done without spilling dirty pages | 
| +** and without exceeding the cache size limit. | 
| +** | 
| +** The caller needs to invoke sqlite3PcacheFetchFinish() to properly | 
| +** initialize the sqlite3_pcache_page object and convert it into a | 
| +** PgHdr object.  The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish() | 
| +** routines are split this way for performance reasons. When separated | 
| +** they can both (usually) operate without having to push values to | 
| +** the stack on entry and pop them back off on exit, which saves a | 
| +** lot of pushing and popping. | 
| +*/ | 
| +sqlite3_pcache_page *sqlite3PcacheFetch( | 
| PCache *pCache,       /* Obtain the page from this cache */ | 
| Pgno pgno,            /* Page number to obtain */ | 
| -  int createFlag,       /* If true, create page if it does not exist already */ | 
| -  PgHdr **ppPage        /* Write the page here */ | 
| +  int createFlag        /* If true, create page if it does not exist already */ | 
| ){ | 
| -  PgHdr *pPage = 0; | 
| int eCreate; | 
|  | 
| assert( pCache!=0 ); | 
| -  assert( createFlag==1 || createFlag==0 ); | 
| +  assert( pCache->pCache!=0 ); | 
| +  assert( createFlag==3 || createFlag==0 ); | 
| assert( pgno>0 ); | 
|  | 
| -  /* If the pluggable cache (sqlite3_pcache*) has not been allocated, | 
| -  ** allocate it now. | 
| +  /* eCreate defines what to do if the page does not exist. | 
| +  **    0     Do not allocate a new page.  (createFlag==0) | 
| +  **    1     Allocate a new page if doing so is inexpensive. | 
| +  **          (createFlag==1 AND bPurgeable AND pDirty) | 
| +  **    2     Allocate a new page even it doing so is difficult. | 
| +  **          (createFlag==1 AND !(bPurgeable AND pDirty) | 
| */ | 
| -  if( !pCache->pCache && createFlag ){ | 
| -    sqlite3_pcache *p; | 
| -    int nByte; | 
| -    nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr); | 
| -    p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable); | 
| -    if( !p ){ | 
| -      return SQLITE_NOMEM; | 
| -    } | 
| -    sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax); | 
| -    pCache->pCache = p; | 
| -  } | 
| +  eCreate = createFlag & pCache->eCreate; | 
| +  assert( eCreate==0 || eCreate==1 || eCreate==2 ); | 
| +  assert( createFlag==0 || pCache->eCreate==eCreate ); | 
| +  assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); | 
| +  return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); | 
| +} | 
|  | 
| -  eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); | 
| -  if( pCache->pCache ){ | 
| -    pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate); | 
| -  } | 
| +/* | 
| +** If the sqlite3PcacheFetch() routine is unable to allocate a new | 
| +** page because new clean pages are available for reuse and the cache | 
| +** size limit has been reached, then this routine can be invoked to | 
| +** try harder to allocate a page.  This routine might invoke the stress | 
| +** callback to spill dirty pages to the journal.  It will then try to | 
| +** allocate the new page and will only fail to allocate a new page on | 
| +** an OOM error. | 
| +** | 
| +** This routine should be invoked only after sqlite3PcacheFetch() fails. | 
| +*/ | 
| +int sqlite3PcacheFetchStress( | 
| +  PCache *pCache,                 /* Obtain the page from this cache */ | 
| +  Pgno pgno,                      /* Page number to obtain */ | 
| +  sqlite3_pcache_page **ppPage    /* Write result here */ | 
| +){ | 
| +  PgHdr *pPg; | 
| +  if( pCache->eCreate==2 ) return 0; | 
|  | 
| -  if( !pPage && eCreate==1 ){ | 
| -    PgHdr *pPg; | 
| - | 
| -    /* Find a dirty page to write-out and recycle. First try to find a | 
| -    ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC | 
| -    ** cleared), but if that is not possible settle for any other | 
| -    ** unreferenced dirty page. | 
| -    */ | 
| -    expensive_assert( pcacheCheckSynced(pCache) ); | 
| -    for(pPg=pCache->pSynced; | 
| -        pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); | 
| -        pPg=pPg->pDirtyPrev | 
| -    ); | 
| -    pCache->pSynced = pPg; | 
| -    if( !pPg ){ | 
| -      for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); | 
| -    } | 
| -    if( pPg ){ | 
| -      int rc; | 
| -      rc = pCache->xStress(pCache->pStress, pPg); | 
| -      if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ | 
| -        return rc; | 
| -      } | 
| -    } | 
|  | 
| -    pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2); | 
| +  /* Find a dirty page to write-out and recycle. First try to find a | 
| +  ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC | 
| +  ** cleared), but if that is not possible settle for any other | 
| +  ** unreferenced dirty page. | 
| +  */ | 
| +  for(pPg=pCache->pSynced; | 
| +      pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); | 
| +      pPg=pPg->pDirtyPrev | 
| +  ); | 
| +  pCache->pSynced = pPg; | 
| +  if( !pPg ){ | 
| +    for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); | 
| +  } | 
| +  if( pPg ){ | 
| +    int rc; | 
| +#ifdef SQLITE_LOG_CACHE_SPILL | 
| +    sqlite3_log(SQLITE_FULL, | 
| +                "spill page %d making room for %d - cache used: %d/%d", | 
| +                pPg->pgno, pgno, | 
| +                sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), | 
| +                numberOfCachePages(pCache)); | 
| +#endif | 
| +    rc = pCache->xStress(pCache->pStress, pPg); | 
| +    if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ | 
| +      return rc; | 
| +    } | 
| } | 
| +  *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); | 
| +  return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK; | 
| +} | 
|  | 
| -  if( pPage ){ | 
| -    if( !pPage->pData ){ | 
| -      memset(pPage, 0, sizeof(PgHdr)); | 
| -      pPage->pData = (void *)&pPage[1]; | 
| -      pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage]; | 
| -      memset(pPage->pExtra, 0, pCache->szExtra); | 
| -      pPage->pCache = pCache; | 
| -      pPage->pgno = pgno; | 
| -    } | 
| -    assert( pPage->pCache==pCache ); | 
| -    assert( pPage->pgno==pgno ); | 
| -    assert( pPage->pData==(void *)&pPage[1] ); | 
| -    assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] ); | 
| +/* | 
| +** This is a helper routine for sqlite3PcacheFetchFinish() | 
| +** | 
| +** In the uncommon case where the page being fetched has not been | 
| +** initialized, this routine is invoked to do the initialization. | 
| +** This routine is broken out into a separate function since it | 
| +** requires extra stack manipulation that can be avoided in the common | 
| +** case. | 
| +*/ | 
| +static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( | 
| +  PCache *pCache,             /* Obtain the page from this cache */ | 
| +  Pgno pgno,                  /* Page number obtained */ | 
| +  sqlite3_pcache_page *pPage  /* Page obtained by prior PcacheFetch() call */ | 
| +){ | 
| +  PgHdr *pPgHdr; | 
| +  assert( pPage!=0 ); | 
| +  pPgHdr = (PgHdr*)pPage->pExtra; | 
| +  assert( pPgHdr->pPage==0 ); | 
| + memset(pPgHdr, 0, sizeof(PgHdr)); | 
| +  pPgHdr->pPage = pPage; | 
| +  pPgHdr->pData = pPage->pBuf; | 
| +  pPgHdr->pExtra = (void *)&pPgHdr[1]; | 
| +  memset(pPgHdr->pExtra, 0, pCache->szExtra); | 
| +  pPgHdr->pCache = pCache; | 
| +  pPgHdr->pgno = pgno; | 
| +  return sqlite3PcacheFetchFinish(pCache,pgno,pPage); | 
| +} | 
|  | 
| -    if( 0==pPage->nRef ){ | 
| -      pCache->nRef++; | 
| -    } | 
| -    pPage->nRef++; | 
| -    if( pgno==1 ){ | 
| -      pCache->pPage1 = pPage; | 
| -    } | 
| +/* | 
| +** This routine converts the sqlite3_pcache_page object returned by | 
| +** sqlite3PcacheFetch() into an initialized PgHdr object.  This routine | 
| +** must be called after sqlite3PcacheFetch() in order to get a usable | 
| +** result. | 
| +*/ | 
| +PgHdr *sqlite3PcacheFetchFinish( | 
| +  PCache *pCache,             /* Obtain the page from this cache */ | 
| +  Pgno pgno,                  /* Page number obtained */ | 
| +  sqlite3_pcache_page *pPage  /* Page obtained by prior PcacheFetch() call */ | 
| +){ | 
| +  PgHdr *pPgHdr; | 
| + | 
| +  if( pPage==0 ) return 0; | 
| +  pPgHdr = (PgHdr *)pPage->pExtra; | 
| + | 
| +  if( !pPgHdr->pPage ){ | 
| +    return pcacheFetchFinishWithInit(pCache, pgno, pPage); | 
| +  } | 
| +  if( 0==pPgHdr->nRef ){ | 
| +    pCache->nRef++; | 
| } | 
| -  *ppPage = pPage; | 
| -  return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK; | 
| +  pPgHdr->nRef++; | 
| +  if( pgno==1 ){ | 
| +    pCache->pPage1 = pPgHdr; | 
| +  } | 
| +  return pPgHdr; | 
| } | 
|  | 
| /* | 
| ** Decrement the reference count on a page. If the page is clean and the | 
| -** reference count drops to 0, then it is made elible for recycling. | 
| +** reference count drops to 0, then it is made eligible for recycling. | 
| */ | 
| -void sqlite3PcacheRelease(PgHdr *p){ | 
| +void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ | 
| assert( p->nRef>0 ); | 
| p->nRef--; | 
| if( p->nRef==0 ){ | 
| -    PCache *pCache = p->pCache; | 
| -    pCache->nRef--; | 
| +    p->pCache->nRef--; | 
| if( (p->flags&PGHDR_DIRTY)==0 ){ | 
| pcacheUnpin(p); | 
| -    }else{ | 
| +    }else if( p->pDirtyPrev!=0 ){ | 
| /* Move the page to the head of the dirty list. */ | 
| -      pcacheRemoveFromDirtyList(p); | 
| -      pcacheAddToDirtyList(p); | 
| +      pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); | 
| } | 
| } | 
| } | 
| @@ -322,17 +400,15 @@ void sqlite3PcacheRef(PgHdr *p){ | 
| ** page pointed to by p is invalid. | 
| */ | 
| void sqlite3PcacheDrop(PgHdr *p){ | 
| -  PCache *pCache; | 
| assert( p->nRef==1 ); | 
| if( p->flags&PGHDR_DIRTY ){ | 
| -    pcacheRemoveFromDirtyList(p); | 
| +    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); | 
| } | 
| -  pCache = p->pCache; | 
| -  pCache->nRef--; | 
| +  p->pCache->nRef--; | 
| if( p->pgno==1 ){ | 
| -    pCache->pPage1 = 0; | 
| +    p->pCache->pPage1 = 0; | 
| } | 
| -  sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1); | 
| +  sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1); | 
| } | 
|  | 
| /* | 
| @@ -344,7 +420,7 @@ void sqlite3PcacheMakeDirty(PgHdr *p){ | 
| assert( p->nRef>0 ); | 
| if( 0==(p->flags & PGHDR_DIRTY) ){ | 
| p->flags |= PGHDR_DIRTY; | 
| -    pcacheAddToDirtyList( p); | 
| +    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); | 
| } | 
| } | 
|  | 
| @@ -354,7 +430,7 @@ void sqlite3PcacheMakeDirty(PgHdr *p){ | 
| */ | 
| void sqlite3PcacheMakeClean(PgHdr *p){ | 
| if( (p->flags & PGHDR_DIRTY) ){ | 
| -    pcacheRemoveFromDirtyList(p); | 
| +    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); | 
| p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC); | 
| if( p->nRef==0 ){ | 
| pcacheUnpin(p); | 
| @@ -390,11 +466,10 @@ void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ | 
| PCache *pCache = p->pCache; | 
| assert( p->nRef>0 ); | 
| assert( newPgno>0 ); | 
| -  sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno); | 
| +  sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); | 
| p->pgno = newPgno; | 
| if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ | 
| -    pcacheRemoveFromDirtyList(p); | 
| -    pcacheAddToDirtyList(p); | 
| +    pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); | 
| } | 
| } | 
|  | 
| @@ -427,7 +502,7 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ | 
| memset(pCache->pPage1->pData, 0, pCache->szPage); | 
| pgno = 1; | 
| } | 
| -    sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1); | 
| +    sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); | 
| } | 
| } | 
|  | 
| @@ -435,9 +510,8 @@ void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ | 
| ** Close a cache. | 
| */ | 
| void sqlite3PcacheClose(PCache *pCache){ | 
| -  if( pCache->pCache ){ | 
| -    sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache); | 
| -  } | 
| +  assert( pCache->pCache!=0 ); | 
| +  sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); | 
| } | 
|  | 
| /* | 
| @@ -546,11 +620,8 @@ int sqlite3PcachePageRefcount(PgHdr *p){ | 
| ** Return the total number of pages in the cache. | 
| */ | 
| int sqlite3PcachePagecount(PCache *pCache){ | 
| -  int nPage = 0; | 
| -  if( pCache->pCache ){ | 
| -    nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache); | 
| -  } | 
| -  return nPage; | 
| +  assert( pCache->pCache!=0 ); | 
| +  return sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); | 
| } | 
|  | 
| #ifdef SQLITE_TEST | 
| @@ -558,7 +629,7 @@ int sqlite3PcachePagecount(PCache *pCache){ | 
| ** Get the suggested cache-size value. | 
| */ | 
| int sqlite3PcacheGetCachesize(PCache *pCache){ | 
| -  return pCache->nMax; | 
| +  return numberOfCachePages(pCache); | 
| } | 
| #endif | 
|  | 
| @@ -566,10 +637,18 @@ int sqlite3PcacheGetCachesize(PCache *pCache){ | 
| ** Set the suggested cache-size value. | 
| */ | 
| void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ | 
| -  pCache->nMax = mxPage; | 
| -  if( pCache->pCache ){ | 
| -    sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage); | 
| -  } | 
| +  assert( pCache->pCache!=0 ); | 
| +  pCache->szCache = mxPage; | 
| +  sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, | 
| +                                         numberOfCachePages(pCache)); | 
| +} | 
| + | 
| +/* | 
| +** Free up as much memory as possible from the page cache. | 
| +*/ | 
| +void sqlite3PcacheShrink(PCache *pCache){ | 
| +  assert( pCache->pCache!=0 ); | 
| +  sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); | 
| } | 
|  | 
| #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) | 
|  |