/ Check-in [c275c9d3]
Login
SQLite training in Houston TX on 2019-11-05 (details)
Part of the 2019 Tcl Conference

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

Overview
Comment:Experimental change to the pcache interface to allow page buffers to be allocated separately from their associated container structures.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental-pcache
Files: files | file ages | folders
SHA1: c275c9d323cb1dccb031b199d413ac3a0b244fea
User & Date: dan 2011-11-08 20:08:44
Context
2011-11-09
00:06
Update the API documentation for the new pcache2 interface. Change the order of parameters on the xCreate method of pcache2. check-in: 4da70956 user: drh tags: experimental-pcache
2011-11-08
20:08
Experimental change to the pcache interface to allow page buffers to be allocated separately from their associated container structures. check-in: c275c9d3 user: dan tags: experimental-pcache
2011-11-07
18:16
Make the unix VFS tolerant of read() calls that return less than the requested number of bytes. check-in: a210695a user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/global.c.

   143    143      SQLITE_THREADSAFE==1,      /* bFullMutex */
   144    144      SQLITE_USE_URI,            /* bOpenUri */
   145    145      0x7ffffffe,                /* mxStrlen */
   146    146      128,                       /* szLookaside */
   147    147      500,                       /* nLookaside */
   148    148      {0,0,0,0,0,0,0,0},         /* m */
   149    149      {0,0,0,0,0,0,0,0,0},       /* mutex */
   150         -   {0,0,0,0,0,0,0,0,0,0,0},   /* pcache */
          150  +   {0,0,0,0,0,0,0,0,0,0,0},   /* pcache2 */
   151    151      (void*)0,                  /* pHeap */
   152    152      0,                         /* nHeap */
   153    153      0, 0,                      /* mnHeap, mxHeap */
   154    154      (void*)0,                  /* pScratch */
   155    155      0,                         /* szScratch */
   156    156      0,                         /* nScratch */
   157    157      (void*)0,                  /* pPage */

Changes to src/main.c.

   361    361         sqlite3GlobalConfig.pPage = va_arg(ap, void*);
   362    362         sqlite3GlobalConfig.szPage = va_arg(ap, int);
   363    363         sqlite3GlobalConfig.nPage = va_arg(ap, int);
   364    364         break;
   365    365       }
   366    366   
   367    367       case SQLITE_CONFIG_PCACHE: {
   368         -      /* Specify an alternative page cache implementation */
   369         -      sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
          368  +      /* no-op */
   370    369         break;
   371    370       }
   372         -
   373    371       case SQLITE_CONFIG_GETPCACHE: {
   374         -      if( sqlite3GlobalConfig.pcache.xInit==0 ){
          372  +      /* now an error */
          373  +      rc = SQLITE_ERROR;
          374  +      break;
          375  +    }
          376  +
          377  +    case SQLITE_CONFIG_PCACHE2: {
          378  +      /* Specify an alternative page cache implementation */
          379  +      sqlite3GlobalConfig.pcache2 = *va_arg(ap, sqlite3_pcache_methods2*);
          380  +      break;
          381  +    }
          382  +    case SQLITE_CONFIG_GETPCACHE2: {
          383  +      if( sqlite3GlobalConfig.pcache2.xInit==0 ){
   375    384           sqlite3PCacheSetDefault();
   376    385         }
   377         -      *va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache;
          386  +      *va_arg(ap, sqlite3_pcache_methods2*) = sqlite3GlobalConfig.pcache2;
   378    387         break;
   379    388       }
   380    389   
   381    390   #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
   382    391       case SQLITE_CONFIG_HEAP: {
   383    392         /* Designate a buffer for heap memory space */
   384    393         sqlite3GlobalConfig.pHeap = va_arg(ap, void*);

Changes to src/pcache.c.

   127    127   */
   128    128   static void pcacheUnpin(PgHdr *p){
   129    129     PCache *pCache = p->pCache;
   130    130     if( pCache->bPurgeable ){
   131    131       if( p->pgno==1 ){
   132    132         pCache->pPage1 = 0;
   133    133       }
   134         -    sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 0);
          134  +    sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 0);
   135    135     }
   136    136   }
   137    137   
   138    138   /*************************************************** General Interfaces ******
   139    139   **
   140    140   ** Initialize and shutdown the page cache subsystem. Neither of these 
   141    141   ** functions are threadsafe.
   142    142   */
   143    143   int sqlite3PcacheInitialize(void){
   144         -  if( sqlite3GlobalConfig.pcache.xInit==0 ){
          144  +  if( sqlite3GlobalConfig.pcache2.xInit==0 ){
   145    145       /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the
   146    146       ** built-in default page cache is used instead of the application defined
   147    147       ** page cache. */
   148    148       sqlite3PCacheSetDefault();
   149    149     }
   150         -  return sqlite3GlobalConfig.pcache.xInit(sqlite3GlobalConfig.pcache.pArg);
          150  +  return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg);
   151    151   }
   152    152   void sqlite3PcacheShutdown(void){
   153         -  if( sqlite3GlobalConfig.pcache.xShutdown ){
          153  +  if( sqlite3GlobalConfig.pcache2.xShutdown ){
   154    154       /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */
   155         -    sqlite3GlobalConfig.pcache.xShutdown(sqlite3GlobalConfig.pcache.pArg);
          155  +    sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg);
   156    156     }
   157    157   }
   158    158   
   159    159   /*
   160    160   ** Return the size in bytes of a PCache object.
   161    161   */
   162    162   int sqlite3PcacheSize(void){ return sizeof(PCache); }
................................................................................
   187    187   /*
   188    188   ** Change the page size for PCache object. The caller must ensure that there
   189    189   ** are no outstanding page references when this function is called.
   190    190   */
   191    191   void sqlite3PcacheSetPageSize(PCache *pCache, int szPage){
   192    192     assert( pCache->nRef==0 && pCache->pDirty==0 );
   193    193     if( pCache->pCache ){
   194         -    sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
          194  +    sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
   195    195       pCache->pCache = 0;
   196    196       pCache->pPage1 = 0;
   197    197     }
   198    198     pCache->szPage = szPage;
   199    199   }
   200    200   
   201    201   /*
................................................................................
   203    203   */
   204    204   int sqlite3PcacheFetch(
   205    205     PCache *pCache,       /* Obtain the page from this cache */
   206    206     Pgno pgno,            /* Page number to obtain */
   207    207     int createFlag,       /* If true, create page if it does not exist already */
   208    208     PgHdr **ppPage        /* Write the page here */
   209    209   ){
   210         -  PgHdr *pPage = 0;
          210  +  sqlite3_pcache_page *pPage = 0;
          211  +  PgHdr *pPgHdr = 0;
   211    212     int eCreate;
   212    213   
   213    214     assert( pCache!=0 );
   214    215     assert( createFlag==1 || createFlag==0 );
   215    216     assert( pgno>0 );
   216    217   
   217    218     /* If the pluggable cache (sqlite3_pcache*) has not been allocated,
   218    219     ** allocate it now.
   219    220     */
   220    221     if( !pCache->pCache && createFlag ){
   221    222       sqlite3_pcache *p;
   222         -    int nByte;
   223         -    nByte = pCache->szPage + pCache->szExtra + sizeof(PgHdr);
   224         -    p = sqlite3GlobalConfig.pcache.xCreate(nByte, pCache->bPurgeable);
          223  +    p = sqlite3GlobalConfig.pcache2.xCreate(
          224  +        pCache->szExtra + sizeof(PgHdr), pCache->szPage, pCache->bPurgeable
          225  +    );
   225    226       if( !p ){
   226    227         return SQLITE_NOMEM;
   227    228       }
   228         -    sqlite3GlobalConfig.pcache.xCachesize(p, pCache->nMax);
          229  +    sqlite3GlobalConfig.pcache2.xCachesize(p, pCache->nMax);
   229    230       pCache->pCache = p;
   230    231     }
   231    232   
   232    233     eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty));
   233    234     if( pCache->pCache ){
   234         -    pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate);
          235  +    pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate);
   235    236     }
   236    237   
   237    238     if( !pPage && eCreate==1 ){
   238    239       PgHdr *pPg;
   239    240   
   240    241       /* Find a dirty page to write-out and recycle. First try to find a 
   241    242       ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC
................................................................................
   262    263   #endif
   263    264         rc = pCache->xStress(pCache->pStress, pPg);
   264    265         if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){
   265    266           return rc;
   266    267         }
   267    268       }
   268    269   
   269         -    pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, 2);
          270  +    pPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2);
   270    271     }
   271    272   
   272    273     if( pPage ){
   273         -    if( !pPage->pData ){
   274         -      memset(pPage, 0, sizeof(PgHdr));
   275         -      pPage->pData = (void *)&pPage[1];
   276         -      pPage->pExtra = (void*)&((char *)pPage->pData)[pCache->szPage];
   277         -      memset(pPage->pExtra, 0, pCache->szExtra);
   278         -      pPage->pCache = pCache;
   279         -      pPage->pgno = pgno;
          274  +    pPgHdr = (PgHdr *)pPage->pExtra;
          275  +
          276  +    if( !pPgHdr->pPage ){
          277  +      memset(pPgHdr, 0, sizeof(PgHdr));
          278  +      pPgHdr->pPage = pPage;
          279  +      pPgHdr->pData = pPage->pBuf;
          280  +      pPgHdr->pExtra = (void *)&pPgHdr[1];
          281  +      memset(pPgHdr->pExtra, 0, pCache->szExtra);
          282  +      pPgHdr->pCache = pCache;
          283  +      pPgHdr->pgno = pgno;
   280    284       }
   281         -    assert( pPage->pCache==pCache );
   282         -    assert( pPage->pgno==pgno );
   283         -    assert( pPage->pData==(void *)&pPage[1] );
   284         -    assert( pPage->pExtra==(void *)&((char *)&pPage[1])[pCache->szPage] );
          285  +    assert( pPgHdr->pCache==pCache );
          286  +    assert( pPgHdr->pgno==pgno );
          287  +    assert( pPgHdr->pData==pPage->pBuf );
          288  +    assert( pPgHdr->pExtra==(void *)&pPgHdr[1] );
   285    289   
   286         -    if( 0==pPage->nRef ){
          290  +    if( 0==pPgHdr->nRef ){
   287    291         pCache->nRef++;
   288    292       }
   289         -    pPage->nRef++;
          293  +    pPgHdr->nRef++;
   290    294       if( pgno==1 ){
   291         -      pCache->pPage1 = pPage;
          295  +      pCache->pPage1 = pPgHdr;
   292    296       }
   293    297     }
   294         -  *ppPage = pPage;
   295         -  return (pPage==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
          298  +  *ppPage = pPgHdr;
          299  +  return (pPgHdr==0 && eCreate) ? SQLITE_NOMEM : SQLITE_OK;
   296    300   }
   297    301   
   298    302   /*
   299    303   ** Decrement the reference count on a page. If the page is clean and the
   300    304   ** reference count drops to 0, then it is made elible for recycling.
   301    305   */
   302    306   void sqlite3PcacheRelease(PgHdr *p){
................................................................................
   335    339       pcacheRemoveFromDirtyList(p);
   336    340     }
   337    341     pCache = p->pCache;
   338    342     pCache->nRef--;
   339    343     if( p->pgno==1 ){
   340    344       pCache->pPage1 = 0;
   341    345     }
   342         -  sqlite3GlobalConfig.pcache.xUnpin(pCache->pCache, p, 1);
          346  +  sqlite3GlobalConfig.pcache2.xUnpin(pCache->pCache, p->pPage, 1);
   343    347   }
   344    348   
   345    349   /*
   346    350   ** Make sure the page is marked as dirty. If it isn't dirty already,
   347    351   ** make it so.
   348    352   */
   349    353   void sqlite3PcacheMakeDirty(PgHdr *p){
................................................................................
   393    397   /*
   394    398   ** Change the page number of page p to newPgno. 
   395    399   */
   396    400   void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){
   397    401     PCache *pCache = p->pCache;
   398    402     assert( p->nRef>0 );
   399    403     assert( newPgno>0 );
   400         -  sqlite3GlobalConfig.pcache.xRekey(pCache->pCache, p, p->pgno, newPgno);
          404  +  sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno);
   401    405     p->pgno = newPgno;
   402    406     if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){
   403    407       pcacheRemoveFromDirtyList(p);
   404    408       pcacheAddToDirtyList(p);
   405    409     }
   406    410   }
   407    411   
................................................................................
   430    434           sqlite3PcacheMakeClean(p);
   431    435         }
   432    436       }
   433    437       if( pgno==0 && pCache->pPage1 ){
   434    438         memset(pCache->pPage1->pData, 0, pCache->szPage);
   435    439         pgno = 1;
   436    440       }
   437         -    sqlite3GlobalConfig.pcache.xTruncate(pCache->pCache, pgno+1);
          441  +    sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1);
   438    442     }
   439    443   }
   440    444   
   441    445   /*
   442    446   ** Close a cache.
   443    447   */
   444    448   void sqlite3PcacheClose(PCache *pCache){
   445    449     if( pCache->pCache ){
   446         -    sqlite3GlobalConfig.pcache.xDestroy(pCache->pCache);
          450  +    sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache);
   447    451     }
   448    452   }
   449    453   
   450    454   /* 
   451    455   ** Discard the contents of the cache.
   452    456   */
   453    457   void sqlite3PcacheClear(PCache *pCache){
................................................................................
   551    555   
   552    556   /* 
   553    557   ** Return the total number of pages in the cache.
   554    558   */
   555    559   int sqlite3PcachePagecount(PCache *pCache){
   556    560     int nPage = 0;
   557    561     if( pCache->pCache ){
   558         -    nPage = sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache);
          562  +    nPage = sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache);
   559    563     }
   560    564     return nPage;
   561    565   }
   562    566   
   563    567   #ifdef SQLITE_TEST
   564    568   /*
   565    569   ** Get the suggested cache-size value.
................................................................................
   571    575   
   572    576   /*
   573    577   ** Set the suggested cache-size value.
   574    578   */
   575    579   void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){
   576    580     pCache->nMax = mxPage;
   577    581     if( pCache->pCache ){
   578         -    sqlite3GlobalConfig.pcache.xCachesize(pCache->pCache, mxPage);
          582  +    sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, mxPage);
   579    583     }
   580    584   }
   581    585   
   582    586   #if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG)
   583    587   /*
   584    588   ** For all dirty pages currently in the cache, invoke the specified
   585    589   ** callback. This is only used if the SQLITE_CHECK_PAGES macro is

Changes to src/pcache.h.

    19     19   typedef struct PCache PCache;
    20     20   
    21     21   /*
    22     22   ** Every page in the cache is controlled by an instance of the following
    23     23   ** structure.
    24     24   */
    25     25   struct PgHdr {
    26         -  void *pData;                   /* Content of this page */
           26  +  sqlite3_pcache_page *pPage;    /* Pcache object page handle */
           27  +  void *pData;                   /* Page data */
    27     28     void *pExtra;                  /* Extra content */
    28     29     PgHdr *pDirty;                 /* Transient list of dirty pages */
    29     30     Pgno pgno;                     /* Page number for this page */
    30     31     Pager *pPager;                 /* The pager this page is part of */
    31     32   #ifdef SQLITE_CHECK_PAGES
    32     33     u32 pageHash;                  /* Hash of page content */
    33     34   #endif

Changes to src/pcache1.c.

    68     68     /* Cache configuration parameters. Page size (szPage) and the purgeable
    69     69     ** flag (bPurgeable) are set when the cache is created. nMax may be 
    70     70     ** modified at any time by a call to the pcache1CacheSize() method.
    71     71     ** The PGroup mutex must be held when accessing nMax.
    72     72     */
    73     73     PGroup *pGroup;                     /* PGroup this cache belongs to */
    74     74     int szPage;                         /* Size of allocated pages in bytes */
           75  +  int szExtra;                        /* Size of extra space in bytes */
    75     76     int bPurgeable;                     /* True if cache is purgeable */
    76     77     unsigned int nMin;                  /* Minimum number of pages reserved */
    77     78     unsigned int nMax;                  /* Configured "cache_size" value */
    78     79     unsigned int n90pct;                /* nMax*9/10 */
    79     80   
    80     81     /* Hash table of all pages. The following variables may only be accessed
    81     82     ** when the accessor is holding the PGroup mutex.
................................................................................
    86     87     PgHdr1 **apHash;                    /* Hash table for fast lookup by key */
    87     88   
    88     89     unsigned int iMaxKey;               /* Largest key seen since xTruncate() */
    89     90   };
    90     91   
    91     92   /*
    92     93   ** Each cache entry is represented by an instance of the following 
    93         -** structure. A buffer of PgHdr1.pCache->szPage bytes is allocated 
    94         -** directly before this structure in memory (see the PGHDR1_TO_PAGE() 
    95         -** macro below).
           94  +** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of
           95  +** PgHdr1.pCache->szPage bytes is allocated directly before this structure 
           96  +** in memory.
    96     97   */
    97     98   struct PgHdr1 {
           99  +  sqlite3_pcache_page page;
    98    100     unsigned int iKey;             /* Key value (page number) */
    99    101     PgHdr1 *pNext;                 /* Next in hash table chain */
   100    102     PCache1 *pCache;               /* Cache that currently owns this page */
   101    103     PgHdr1 *pLruNext;              /* Next in LRU list of unpinned pages */
   102    104     PgHdr1 *pLruPrev;              /* Previous in LRU list of unpinned pages */
   103    105   };
   104    106   
................................................................................
   140    142   /*
   141    143   ** All code in this file should access the global structure above via the
   142    144   ** alias "pcache1". This ensures that the WSD emulation is used when
   143    145   ** compiling for systems that do not support real WSD.
   144    146   */
   145    147   #define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g))
   146    148   
   147         -/*
   148         -** When a PgHdr1 structure is allocated, the associated PCache1.szPage
   149         -** bytes of data are located directly before it in memory (i.e. the total
   150         -** size of the allocation is sizeof(PgHdr1)+PCache1.szPage byte). The
   151         -** PGHDR1_TO_PAGE() macro takes a pointer to a PgHdr1 structure as
   152         -** an argument and returns a pointer to the associated block of szPage
   153         -** bytes. The PAGE_TO_PGHDR1() macro does the opposite: its argument is
   154         -** a pointer to a block of szPage bytes of data and the return value is
   155         -** a pointer to the associated PgHdr1 structure.
   156         -**
   157         -**   assert( PGHDR1_TO_PAGE(PAGE_TO_PGHDR1(pCache, X))==X );
   158         -*/
   159         -#define PGHDR1_TO_PAGE(p)    (void*)(((char*)p) - p->pCache->szPage)
   160         -#define PAGE_TO_PGHDR1(c, p) (PgHdr1*)(((char*)p) + c->szPage)
   161         -
   162    149   /*
   163    150   ** Macros to enter and leave the PCache LRU mutex.
   164    151   */
   165    152   #define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex)
   166    153   #define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex)
   167    154   
   168    155   /******************************************************************************/
................................................................................
   284    271   }
   285    272   #endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */
   286    273   
   287    274   /*
   288    275   ** Allocate a new page object initially associated with cache pCache.
   289    276   */
   290    277   static PgHdr1 *pcache1AllocPage(PCache1 *pCache){
   291         -  int nByte = sizeof(PgHdr1) + pCache->szPage;
   292    278     PgHdr1 *p = 0;
   293    279     void *pPg;
   294    280   
   295    281     /* The group mutex must be released before pcache1Alloc() is called. This
   296    282     ** is because it may call sqlite3_release_memory(), which assumes that 
   297    283     ** this mutex is not held. */
   298    284     assert( sqlite3_mutex_held(pCache->pGroup->mutex) );
   299    285     pcache1LeaveMutex(pCache->pGroup);
   300         -  pPg = pcache1Alloc(nByte);
          286  +#ifdef SQLITE_PCACHE_SEPARATE_HEADER
          287  +  pPg = pcache1Alloc(pCache->szPage);
          288  +  p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra);
          289  +  if( !pPg || !p ){
          290  +    pcache1Free(pPg);
          291  +    sqlite3_free(p);
          292  +    pPg = 0;
          293  +  }
          294  +#else
          295  +  pPg = pcache1Alloc(sizeof(PgHdr1) + pCache->szPage + pCache->szExtra);
          296  +  p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage];
          297  +#endif
   301    298     pcache1EnterMutex(pCache->pGroup);
   302    299   
   303    300     if( pPg ){
   304         -    p = PAGE_TO_PGHDR1(pCache, pPg);
          301  +    p->page.pBuf = pPg;
          302  +    p->page.pExtra = &p[1];
   305    303       if( pCache->bPurgeable ){
   306    304         pCache->pGroup->nCurrentPage++;
   307    305       }
          306  +    return p;
   308    307     }
   309         -  return p;
          308  +  return 0;
   310    309   }
   311    310   
   312    311   /*
   313    312   ** Free a page object allocated by pcache1AllocPage().
   314    313   **
   315    314   ** The pointer is allowed to be NULL, which is prudent.  But it turns out
   316    315   ** that the current implementation happens to never call this routine
   317    316   ** with a NULL pointer, so we mark the NULL test with ALWAYS().
   318    317   */
   319    318   static void pcache1FreePage(PgHdr1 *p){
   320    319     if( ALWAYS(p) ){
   321    320       PCache1 *pCache = p->pCache;
   322    321       assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) );
   323         -    pcache1Free(PGHDR1_TO_PAGE(p));
          322  +    pcache1Free(p->page.pBuf);
          323  +#ifdef SQLITE_PCACHE_SEPARATE_HEADER
          324  +    sqlite3_free(p);
          325  +#endif
   324    326       if( pCache->bPurgeable ){
   325    327         pCache->pGroup->nCurrentPage--;
   326    328       }
   327    329     }
   328    330   }
   329    331   
   330    332   /*
................................................................................
   357    359   **
   358    360   ** Or, the heap is used for all page cache memory put the heap is
   359    361   ** under memory pressure, then again it is desirable to avoid
   360    362   ** allocating a new page cache entry in order to avoid stressing
   361    363   ** the heap even further.
   362    364   */
   363    365   static int pcache1UnderMemoryPressure(PCache1 *pCache){
   364         -  if( pcache1.nSlot && pCache->szPage<=pcache1.szSlot ){
          366  +  if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
   365    367       return pcache1.bUnderPressure;
   366    368     }else{
   367    369       return sqlite3HeapNearlyFull();
   368    370     }
   369    371   }
   370    372   
   371    373   /******************************************************************************/
................................................................................
   548    550   }
   549    551   
   550    552   /*
   551    553   ** Implementation of the sqlite3_pcache.xCreate method.
   552    554   **
   553    555   ** Allocate a new cache.
   554    556   */
   555         -static sqlite3_pcache *pcache1Create(int szPage, int bPurgeable){
          557  +static sqlite3_pcache *pcache1Create(int szExtra, int szPage, int bPurgeable){
   556    558     PCache1 *pCache;      /* The newly created page cache */
   557    559     PGroup *pGroup;       /* The group the new page cache will belong to */
   558    560     int sz;               /* Bytes of memory required to allocate the new cache */
   559    561   
   560    562     /*
   561    563     ** The seperateCache variable is true if each PCache has its own private
   562    564     ** PGroup.  In other words, separateCache is true for mode (1) where no
................................................................................
   583    585         pGroup = (PGroup*)&pCache[1];
   584    586         pGroup->mxPinned = 10;
   585    587       }else{
   586    588         pGroup = &pcache1.grp;
   587    589       }
   588    590       pCache->pGroup = pGroup;
   589    591       pCache->szPage = szPage;
          592  +    pCache->szExtra = szExtra;
   590    593       pCache->bPurgeable = (bPurgeable ? 1 : 0);
   591    594       if( bPurgeable ){
   592    595         pCache->nMin = 10;
   593    596         pcache1EnterMutex(pGroup);
   594    597         pGroup->nMinPage += pCache->nMin;
   595    598         pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage;
   596    599         pcache1LeaveMutex(pGroup);
................................................................................
   680    683   **
   681    684   **      then attempt to recycle a page from the LRU list. If it is the right
   682    685   **      size, return the recycled buffer. Otherwise, free the buffer and
   683    686   **      proceed to step 5. 
   684    687   **
   685    688   **   5. Otherwise, allocate and return a new page buffer.
   686    689   */
   687         -static void *pcache1Fetch(sqlite3_pcache *p, unsigned int iKey, int createFlag){
          690  +static sqlite3_pcache_page *pcache1Fetch(
          691  +  sqlite3_pcache *p, 
          692  +  unsigned int iKey, 
          693  +  int createFlag
          694  +){
   688    695     int nPinned;
   689    696     PCache1 *pCache = (PCache1 *)p;
   690    697     PGroup *pGroup;
   691    698     PgHdr1 *pPage = 0;
   692    699   
   693    700     assert( pCache->bPurgeable || createFlag!=1 );
   694    701     assert( pCache->bPurgeable || pCache->nMin==0 );
................................................................................
   715    722     ** optimization:  The common case is to exit the module before reaching
   716    723     ** this point.
   717    724     */
   718    725   #ifdef SQLITE_MUTEX_OMIT
   719    726     pGroup = pCache->pGroup;
   720    727   #endif
   721    728   
   722         -
   723    729     /* Step 3: Abort if createFlag is 1 but the cache is nearly full */
   724    730     nPinned = pCache->nPage - pCache->nRecyclable;
   725    731     assert( nPinned>=0 );
   726    732     assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage );
   727    733     assert( pCache->n90pct == pCache->nMax*9/10 );
   728    734     if( createFlag==1 && (
   729    735           nPinned>=pGroup->mxPinned
................................................................................
   743    749         || pGroup->nCurrentPage>=pGroup->nMaxPage
   744    750         || pcache1UnderMemoryPressure(pCache)
   745    751     )){
   746    752       PCache1 *pOtherCache;
   747    753       pPage = pGroup->pLruTail;
   748    754       pcache1RemoveFromHash(pPage);
   749    755       pcache1PinPage(pPage);
   750         -    if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage ){
          756  +    if( (pOtherCache = pPage->pCache)->szPage!=pCache->szPage 
          757  +     || pOtherCache->szExtra!=pCache->szExtra 
          758  +    ){
   751    759         pcache1FreePage(pPage);
   752    760         pPage = 0;
   753    761       }else{
   754         -      pGroup->nCurrentPage -= 
   755         -               (pOtherCache->bPurgeable - pCache->bPurgeable);
          762  +      pGroup->nCurrentPage -= (pOtherCache->bPurgeable - pCache->bPurgeable);
   756    763       }
   757    764     }
   758    765   
   759    766     /* Step 5. If a usable page buffer has still not been found, 
   760    767     ** attempt to allocate a new one. 
   761    768     */
   762    769     if( !pPage ){
................................................................................
   769    776       unsigned int h = iKey % pCache->nHash;
   770    777       pCache->nPage++;
   771    778       pPage->iKey = iKey;
   772    779       pPage->pNext = pCache->apHash[h];
   773    780       pPage->pCache = pCache;
   774    781       pPage->pLruPrev = 0;
   775    782       pPage->pLruNext = 0;
   776         -    *(void **)(PGHDR1_TO_PAGE(pPage)) = 0;
          783  +    *(void **)pPage->page.pExtra = 0;
   777    784       pCache->apHash[h] = pPage;
   778    785     }
   779    786   
   780    787   fetch_out:
   781    788     if( pPage && iKey>pCache->iMaxKey ){
   782    789       pCache->iMaxKey = iKey;
   783    790     }
   784    791     pcache1LeaveMutex(pGroup);
   785         -  return (pPage ? PGHDR1_TO_PAGE(pPage) : 0);
          792  +  return &pPage->page;
   786    793   }
   787    794   
   788    795   
   789    796   /*
   790    797   ** Implementation of the sqlite3_pcache.xUnpin method.
   791    798   **
   792    799   ** Mark a page as unpinned (eligible for asynchronous recycling).
   793    800   */
   794         -static void pcache1Unpin(sqlite3_pcache *p, void *pPg, int reuseUnlikely){
          801  +static void pcache1Unpin(
          802  +  sqlite3_pcache *p, 
          803  +  sqlite3_pcache_page *pPg, 
          804  +  int reuseUnlikely
          805  +){
   795    806     PCache1 *pCache = (PCache1 *)p;
   796         -  PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
          807  +  PgHdr1 *pPage = (PgHdr1 *)pPg;
   797    808     PGroup *pGroup = pCache->pGroup;
   798    809    
   799    810     assert( pPage->pCache==pCache );
   800    811     pcache1EnterMutex(pGroup);
   801    812   
   802    813     /* It is an error to call this function if the page is already 
   803    814     ** part of the PGroup LRU list.
................................................................................
   825    836   }
   826    837   
   827    838   /*
   828    839   ** Implementation of the sqlite3_pcache.xRekey method. 
   829    840   */
   830    841   static void pcache1Rekey(
   831    842     sqlite3_pcache *p,
   832         -  void *pPg,
          843  +  sqlite3_pcache_page *pPg,
   833    844     unsigned int iOld,
   834    845     unsigned int iNew
   835    846   ){
   836    847     PCache1 *pCache = (PCache1 *)p;
   837         -  PgHdr1 *pPage = PAGE_TO_PGHDR1(pCache, pPg);
          848  +  PgHdr1 *pPage = (PgHdr1 *)pPg;
   838    849     PgHdr1 **pp;
   839    850     unsigned int h; 
   840    851     assert( pPage->iKey==iOld );
   841    852     assert( pPage->pCache==pCache );
   842    853   
   843    854     pcache1EnterMutex(pCache->pGroup);
   844    855   
................................................................................
   899    910   
   900    911   /*
   901    912   ** This function is called during initialization (sqlite3_initialize()) to
   902    913   ** install the default pluggable cache module, assuming the user has not
   903    914   ** already provided an alternative.
   904    915   */
   905    916   void sqlite3PCacheSetDefault(void){
   906         -  static const sqlite3_pcache_methods defaultMethods = {
          917  +  static const sqlite3_pcache_methods2 defaultMethods = {
   907    918       0,                       /* pArg */
   908    919       pcache1Init,             /* xInit */
   909    920       pcache1Shutdown,         /* xShutdown */
   910    921       pcache1Create,           /* xCreate */
   911    922       pcache1Cachesize,        /* xCachesize */
   912    923       pcache1Pagecount,        /* xPagecount */
   913    924       pcache1Fetch,            /* xFetch */
   914    925       pcache1Unpin,            /* xUnpin */
   915    926       pcache1Rekey,            /* xRekey */
   916    927       pcache1Truncate,         /* xTruncate */
   917    928       pcache1Destroy           /* xDestroy */
   918    929     };
   919         -  sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultMethods);
          930  +  sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods);
   920    931   }
   921    932   
   922    933   #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
   923    934   /*
   924    935   ** This function is called to free superfluous dynamically allocated memory
   925    936   ** held by the pager system. Memory in use by any SQLite pager allocated
   926    937   ** by the current thread may be sqlite3_free()ed.
................................................................................
   933    944     int nFree = 0;
   934    945     assert( sqlite3_mutex_notheld(pcache1.grp.mutex) );
   935    946     assert( sqlite3_mutex_notheld(pcache1.mutex) );
   936    947     if( pcache1.pStart==0 ){
   937    948       PgHdr1 *p;
   938    949       pcache1EnterMutex(&pcache1.grp);
   939    950       while( (nReq<0 || nFree<nReq) && ((p=pcache1.grp.pLruTail)!=0) ){
   940         -      nFree += pcache1MemSize(PGHDR1_TO_PAGE(p));
          951  +      nFree += pcache1MemSize(p->page.pBuf);
          952  +#ifdef SQLITE_PCACHE_SEPARATE_HEADER
          953  +      nFree += sqlite3MemSize(p);
          954  +#endif
   941    955         pcache1PinPage(p);
   942    956         pcache1RemoveFromHash(p);
   943    957         pcache1FreePage(p);
   944    958       }
   945    959       pcache1LeaveMutex(&pcache1.grp);
   946    960     }
   947    961     return nFree;

Changes to src/sqlite.h.in.

  1493   1493   #define SQLITE_CONFIG_GETMUTEX     11  /* sqlite3_mutex_methods* */
  1494   1494   /* previously SQLITE_CONFIG_CHUNKALLOC 12 which is now unused. */ 
  1495   1495   #define SQLITE_CONFIG_LOOKASIDE    13  /* int int */
  1496   1496   #define SQLITE_CONFIG_PCACHE       14  /* sqlite3_pcache_methods* */
  1497   1497   #define SQLITE_CONFIG_GETPCACHE    15  /* sqlite3_pcache_methods* */
  1498   1498   #define SQLITE_CONFIG_LOG          16  /* xFunc, void* */
  1499   1499   #define SQLITE_CONFIG_URI          17  /* int */
         1500  +#define SQLITE_CONFIG_PCACHE2      18  /* sqlite3_pcache_methods2* */
         1501  +#define SQLITE_CONFIG_GETPCACHE2   19  /* sqlite3_pcache_methods2* */
  1500   1502   
  1501   1503   /*
  1502   1504   ** CAPI3REF: Database Connection Configuration Options
  1503   1505   **
  1504   1506   ** These constants are the available integer configuration options that
  1505   1507   ** can be passed as the second argument to the [sqlite3_db_config()] interface.
  1506   1508   **
................................................................................
  6092   6094     int (*xPagecount)(sqlite3_pcache*);
  6093   6095     void *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
  6094   6096     void (*xUnpin)(sqlite3_pcache*, void*, int discard);
  6095   6097     void (*xRekey)(sqlite3_pcache*, void*, unsigned oldKey, unsigned newKey);
  6096   6098     void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
  6097   6099     void (*xDestroy)(sqlite3_pcache*);
  6098   6100   };
         6101  +
         6102  +typedef struct sqlite3_pcache_methods2 sqlite3_pcache_methods2;
         6103  +typedef struct sqlite3_pcache_page sqlite3_pcache_page;
         6104  +struct sqlite3_pcache_page {
         6105  +  void *pBuf;
         6106  +  void *pExtra;
         6107  +};
         6108  +struct sqlite3_pcache_methods2 {
         6109  +  void *pArg;
         6110  +  int (*xInit)(void*);
         6111  +  void (*xShutdown)(void*);
         6112  +  sqlite3_pcache *(*xCreate)(int szExtra, int szPage, int bPurgeable);
         6113  +  void (*xCachesize)(sqlite3_pcache*, int nCachesize);
         6114  +  int (*xPagecount)(sqlite3_pcache*);
         6115  +  sqlite3_pcache_page *(*xFetch)(sqlite3_pcache*, unsigned key, int createFlag);
         6116  +  void (*xUnpin)(sqlite3_pcache*, sqlite3_pcache_page*, int discard);
         6117  +  void (*xRekey)(sqlite3_pcache*, sqlite3_pcache_page*, 
         6118  +      unsigned oldKey, unsigned newKey);
         6119  +  void (*xTruncate)(sqlite3_pcache*, unsigned iLimit);
         6120  +  void (*xDestroy)(sqlite3_pcache*);
         6121  +};
  6099   6122   
  6100   6123   /*
  6101   6124   ** CAPI3REF: Online Backup Object
  6102   6125   **
  6103   6126   ** The sqlite3_backup object records state information about an ongoing
  6104   6127   ** online backup operation.  ^The sqlite3_backup object is created by
  6105   6128   ** a call to [sqlite3_backup_init()] and is destroyed by a call to

Changes to src/sqliteInt.h.

  2455   2455     int bFullMutex;                   /* True to enable full mutexing */
  2456   2456     int bOpenUri;                     /* True to interpret filenames as URIs */
  2457   2457     int mxStrlen;                     /* Maximum string length */
  2458   2458     int szLookaside;                  /* Default lookaside buffer size */
  2459   2459     int nLookaside;                   /* Default lookaside buffer count */
  2460   2460     sqlite3_mem_methods m;            /* Low-level memory allocation interface */
  2461   2461     sqlite3_mutex_methods mutex;      /* Low-level mutex interface */
  2462         -  sqlite3_pcache_methods pcache;    /* Low-level page-cache interface */
         2462  +  sqlite3_pcache_methods2 pcache2;  /* Low-level page-cache interface */
  2463   2463     void *pHeap;                      /* Heap storage space */
  2464   2464     int nHeap;                        /* Size of pHeap[] */
  2465   2465     int mnReq, mxReq;                 /* Min and max heap requests sizes */
  2466   2466     void *pScratch;                   /* Scratch memory */
  2467   2467     int szScratch;                    /* Size of each scratch buffer */
  2468   2468     int nScratch;                     /* Number of scratch buffers */
  2469   2469     void *pPage;                      /* Page cache memory */

Changes to src/test_init.c.

    26     26   */
    27     27   
    28     28   #include "sqliteInt.h"
    29     29   #include <string.h>
    30     30   #include <tcl.h>
    31     31   
    32     32   static struct Wrapped {
    33         -  sqlite3_pcache_methods pcache;
    34         -  sqlite3_mem_methods    mem;
    35         -  sqlite3_mutex_methods  mutex;
           33  +  sqlite3_pcache_methods2 pcache;
           34  +  sqlite3_mem_methods     mem;
           35  +  sqlite3_mutex_methods   mutex;
    36     36   
    37     37     int mem_init;                /* True if mem subsystem is initalized */
    38     38     int mem_fail;                /* True to fail mem subsystem inialization */
    39     39     int mutex_init;              /* True if mutex subsystem is initalized */
    40     40     int mutex_fail;              /* True to fail mutex subsystem inialization */
    41     41     int pcache_init;             /* True if pcache subsystem is initalized */
    42     42     int pcache_fail;             /* True to fail pcache subsystem inialization */
................................................................................
   119    119     return rc;
   120    120   }
   121    121   static void wrPCacheShutdown(void *pArg){
   122    122     wrapped.pcache.xShutdown(wrapped.pcache.pArg);
   123    123     wrapped.pcache_init = 0;
   124    124   }
   125    125   
   126         -static sqlite3_pcache *wrPCacheCreate(int a, int b){
   127         -  return wrapped.pcache.xCreate(a, b);
          126  +static sqlite3_pcache *wrPCacheCreate(int a, int b, int c){
          127  +  return wrapped.pcache.xCreate(a, b, c);
   128    128   }  
   129    129   static void wrPCacheCachesize(sqlite3_pcache *p, int n){
   130    130     wrapped.pcache.xCachesize(p, n);
   131    131   }  
   132    132   static int wrPCachePagecount(sqlite3_pcache *p){
   133    133     return wrapped.pcache.xPagecount(p);
   134    134   }  
   135         -static void *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){
          135  +static sqlite3_pcache_page *wrPCacheFetch(sqlite3_pcache *p, unsigned a, int b){
   136    136     return wrapped.pcache.xFetch(p, a, b);
   137    137   }  
   138         -static void wrPCacheUnpin(sqlite3_pcache *p, void *a, int b){
          138  +static void wrPCacheUnpin(sqlite3_pcache *p, sqlite3_pcache_page *a, int b){
   139    139     wrapped.pcache.xUnpin(p, a, b);
   140    140   }  
   141         -static void wrPCacheRekey(sqlite3_pcache *p, void *a, unsigned b, unsigned c){
          141  +static void wrPCacheRekey(
          142  +  sqlite3_pcache *p, 
          143  +  sqlite3_pcache_page *a, 
          144  +  unsigned b, 
          145  +  unsigned c
          146  +){
   142    147     wrapped.pcache.xRekey(p, a, b, c);
   143    148   }  
   144    149   static void wrPCacheTruncate(sqlite3_pcache *p, unsigned a){
   145    150     wrapped.pcache.xTruncate(p, a);
   146    151   }  
   147    152   static void wrPCacheDestroy(sqlite3_pcache *p){
   148    153     wrapped.pcache.xDestroy(p);
................................................................................
   150    155   
   151    156   static void installInitWrappers(void){
   152    157     sqlite3_mutex_methods mutexmethods = {
   153    158       wrMutexInit,  wrMutexEnd,   wrMutexAlloc,
   154    159       wrMutexFree,  wrMutexEnter, wrMutexTry,
   155    160       wrMutexLeave, wrMutexHeld,  wrMutexNotheld
   156    161     };
   157         -  sqlite3_pcache_methods pcachemethods = {
          162  +  sqlite3_pcache_methods2 pcachemethods = {
   158    163       0,
   159    164       wrPCacheInit,      wrPCacheShutdown,  wrPCacheCreate, 
   160    165       wrPCacheCachesize, wrPCachePagecount, wrPCacheFetch,
   161    166       wrPCacheUnpin,     wrPCacheRekey,     wrPCacheTruncate,  
   162    167       wrPCacheDestroy
   163    168     };
   164    169     sqlite3_mem_methods memmethods = {
................................................................................
   169    174     };
   170    175   
   171    176     memset(&wrapped, 0, sizeof(wrapped));
   172    177   
   173    178     sqlite3_shutdown();
   174    179     sqlite3_config(SQLITE_CONFIG_GETMUTEX, &wrapped.mutex);
   175    180     sqlite3_config(SQLITE_CONFIG_GETMALLOC, &wrapped.mem);
   176         -  sqlite3_config(SQLITE_CONFIG_GETPCACHE, &wrapped.pcache);
          181  +  sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &wrapped.pcache);
   177    182     sqlite3_config(SQLITE_CONFIG_MUTEX, &mutexmethods);
   178    183     sqlite3_config(SQLITE_CONFIG_MALLOC, &memmethods);
   179         -  sqlite3_config(SQLITE_CONFIG_PCACHE, &pcachemethods);
          184  +  sqlite3_config(SQLITE_CONFIG_PCACHE2, &pcachemethods);
   180    185   }
   181    186   
   182    187   static int init_wrapper_install(
   183    188     ClientData clientData, /* Unused */
   184    189     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   185    190     int objc,              /* Number of arguments */
   186    191     Tcl_Obj *CONST objv[]  /* Command arguments */
................................................................................
   214    219       return TCL_ERROR;
   215    220     }
   216    221   
   217    222     memset(&wrapped, 0, sizeof(&wrapped));
   218    223     sqlite3_shutdown();
   219    224     sqlite3_config(SQLITE_CONFIG_MUTEX, &wrapped.mutex);
   220    225     sqlite3_config(SQLITE_CONFIG_MALLOC, &wrapped.mem);
   221         -  sqlite3_config(SQLITE_CONFIG_PCACHE, &wrapped.pcache);
          226  +  sqlite3_config(SQLITE_CONFIG_PCACHE2, &wrapped.pcache);
   222    227     return TCL_OK;
   223    228   }
   224    229   
   225    230   static int init_wrapper_clear(
   226    231     ClientData clientData, /* Unused */
   227    232     Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
   228    233     int objc,              /* Number of arguments */

Changes to src/test_pcache.c.

    96     96   
    97     97   /*
    98     98   ** Private implementation of a page cache.
    99     99   */
   100    100   typedef struct testpcache testpcache;
   101    101   struct testpcache {
   102    102     int szPage;               /* Size of each page.  Multiple of 8. */
          103  +  int szExtra;              /* Size of extra data that accompanies each page */
   103    104     int bPurgeable;           /* True if the page cache is purgeable */
   104    105     int nFree;                /* Number of unused slots in a[] */
   105    106     int nPinned;              /* Number of pinned slots in a[] */
   106    107     unsigned iRand;           /* State of the PRNG */
   107    108     unsigned iMagic;          /* Magic number for sanity checking */
   108    109     struct testpcachePage {
          110  +    sqlite3_pcache_page page;  /* Base class */
   109    111       unsigned key;              /* The key for this page. 0 means unallocated */
   110    112       int isPinned;              /* True if the page is pinned */
   111         -    void *pData;               /* Data for this page */
   112    113     } a[TESTPCACHE_NPAGE];    /* All pages in the cache */
   113    114   };
   114    115   
   115    116   /*
   116    117   ** Get a random number using the PRNG in the given page cache.
   117    118   */
   118    119   static unsigned testpcacheRandom(testpcache *p){
................................................................................
   125    126     return x;
   126    127   }
   127    128   
   128    129   
   129    130   /*
   130    131   ** Allocate a new page cache instance.
   131    132   */
   132         -static sqlite3_pcache *testpcacheCreate(int szPage, int bPurgeable){
          133  +static sqlite3_pcache *testpcacheCreate(
          134  +  int szExtra, 
          135  +  int szPage, 
          136  +  int bPurgeable
          137  +){
   133    138     int nMem;
   134    139     char *x;
   135    140     testpcache *p;
   136    141     int i;
   137    142     assert( testpcacheGlobal.pDummy!=0 );
   138    143     szPage = (szPage+7)&~7;
   139         -  nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*szPage;
          144  +  nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*(szPage+szExtra);
   140    145     p = sqlite3_malloc( nMem );
   141    146     if( p==0 ) return 0;
   142    147     x = (char*)&p[1];
   143    148     p->szPage = szPage;
          149  +  p->szExtra = szExtra;
   144    150     p->nFree = TESTPCACHE_NPAGE;
   145    151     p->nPinned = 0;
   146    152     p->iRand = testpcacheGlobal.prngSeed;
   147    153     p->bPurgeable = bPurgeable;
   148    154     p->iMagic = TESTPCACHE_VALID;
   149         -  for(i=0; i<TESTPCACHE_NPAGE; i++, x += szPage){
          155  +  for(i=0; i<TESTPCACHE_NPAGE; i++, x += (szPage+szExtra)){
   150    156       p->a[i].key = 0;
   151    157       p->a[i].isPinned = 0;
   152         -    p->a[i].pData = (void*)x;
          158  +    p->a[i].page.pBuf = (void*)x;
          159  +    p->a[i].page.pExtra = (void*)&x[szPage];
   153    160     }
   154    161     testpcacheGlobal.nInstance++;
   155    162     return (sqlite3_pcache*)p;
   156    163   }
   157    164   
   158    165   /*
   159    166   ** Set the cache size
................................................................................
   177    184     assert( testpcacheGlobal.nInstance>0 );
   178    185     return TESTPCACHE_NPAGE - p->nFree;
   179    186   }
   180    187   
   181    188   /*
   182    189   ** Fetch a page.
   183    190   */
   184         -static void *testpcacheFetch(
          191  +static sqlite3_pcache_page *testpcacheFetch(
   185    192     sqlite3_pcache *pCache,
   186    193     unsigned key,
   187    194     int createFlag
   188    195   ){
   189    196     testpcache *p = (testpcache*)pCache;
   190    197     int i, j;
   191    198     assert( p->iMagic==TESTPCACHE_VALID );
................................................................................
   196    203     for(i=0; i<TESTPCACHE_NPAGE; i++){
   197    204       if( p->a[i].key==key ){
   198    205         if( !p->a[i].isPinned ){
   199    206           p->nPinned++;
   200    207           assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
   201    208           p->a[i].isPinned = 1;
   202    209         }
   203         -      return p->a[i].pData;
          210  +      return &p->a[i].page;
   204    211       }
   205    212     }
   206    213   
   207    214     /* If createFlag is 0, never allocate a new page */
   208    215     if( createFlag==0 ){
   209    216       return 0;
   210    217     }
................................................................................
   233    240     */
   234    241     if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){
   235    242       j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
   236    243       for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
   237    244         if( p->a[j].key==0 ){
   238    245           p->a[j].key = key;
   239    246           p->a[j].isPinned = 1;
   240         -        memset(p->a[j].pData, 0, p->szPage);
          247  +        memset(p->a[j].page.pBuf, 0, p->szPage);
          248  +        memset(p->a[j].page.pExtra, 0, p->szExtra);
   241    249           p->nPinned++;
   242    250           p->nFree--;
   243    251           assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
   244         -        return p->a[j].pData;
          252  +        return &p->a[j].page;
   245    253         }
   246    254       }
   247    255   
   248    256       /* The prior loop always finds a freepage to allocate */
   249    257       assert( 0 );
   250    258     }
   251    259   
................................................................................
   259    267     ** recycle is selected at random from all unpinned pages.
   260    268     */
   261    269     j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
   262    270     for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
   263    271       if( p->a[j].key>0 && p->a[j].isPinned==0 ){
   264    272         p->a[j].key = key;
   265    273         p->a[j].isPinned = 1;
   266         -      memset(p->a[j].pData, 0, p->szPage);
          274  +      memset(p->a[j].page.pBuf, 0, p->szPage);
          275  +      memset(p->a[j].page.pExtra, 0, p->szExtra);
   267    276         p->nPinned++;
   268    277         assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
   269         -      return p->a[j].pData;
          278  +      return &p->a[j].page;
   270    279       }
   271    280     }
   272    281   
   273    282     /* The previous loop always finds a page to recycle. */
   274    283     assert(0);
   275    284     return 0;
   276    285   }
   277    286   
   278    287   /*
   279    288   ** Unpin a page.
   280    289   */
   281    290   static void testpcacheUnpin(
   282    291     sqlite3_pcache *pCache,
   283         -  void *pOldPage,
          292  +  sqlite3_pcache_page *pOldPage,
   284    293     int discard
   285    294   ){
   286    295     testpcache *p = (testpcache*)pCache;
   287    296     int i;
   288    297     assert( p->iMagic==TESTPCACHE_VALID );
   289    298     assert( testpcacheGlobal.pDummy!=0 );
   290    299     assert( testpcacheGlobal.nInstance>0 );
................................................................................
   296    305     if( p->bPurgeable
   297    306     && (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100)
   298    307     ){
   299    308       discard = 1;
   300    309     }
   301    310   
   302    311     for(i=0; i<TESTPCACHE_NPAGE; i++){
   303         -    if( p->a[i].pData==pOldPage ){
          312  +    if( &p->a[i].page==pOldPage ){
   304    313         /* The pOldPage pointer always points to a pinned page */
   305    314         assert( p->a[i].isPinned );
   306    315         p->a[i].isPinned = 0;
   307    316         p->nPinned--;
   308    317         assert( p->nPinned>=0 );
   309    318         if( discard ){
   310    319           p->a[i].key = 0;
................................................................................
   321    330   
   322    331   
   323    332   /*
   324    333   ** Rekey a single page.
   325    334   */
   326    335   static void testpcacheRekey(
   327    336     sqlite3_pcache *pCache,
   328         -  void *pOldPage,
          337  +  sqlite3_pcache_page *pOldPage,
   329    338     unsigned oldKey,
   330    339     unsigned newKey
   331    340   ){
   332    341     testpcache *p = (testpcache*)pCache;
   333    342     int i;
   334    343     assert( p->iMagic==TESTPCACHE_VALID );
   335    344     assert( testpcacheGlobal.pDummy!=0 );
................................................................................
   350    359     }
   351    360   
   352    361     /* Find the page to be rekeyed and rekey it.
   353    362     */
   354    363     for(i=0; i<TESTPCACHE_NPAGE; i++){
   355    364       if( p->a[i].key==oldKey ){
   356    365         /* The oldKey and pOldPage parameters match */
   357         -      assert( p->a[i].pData==pOldPage );
          366  +      assert( &p->a[i].page==pOldPage );
   358    367         /* Page to be rekeyed must be pinned */
   359    368         assert( p->a[i].isPinned );
   360    369         p->a[i].key = newKey;
   361    370         return;
   362    371       }
   363    372     }
   364    373   
................................................................................
   418    427   */
   419    428   void installTestPCache(
   420    429     int installFlag,            /* True to install.  False to uninstall. */
   421    430     unsigned discardChance,     /* 0-100.  Chance to discard on unpin */
   422    431     unsigned prngSeed,          /* Seed for the PRNG */
   423    432     unsigned highStress         /* Call xStress agressively */
   424    433   ){
   425         -  static const sqlite3_pcache_methods testPcache = {
          434  +  static const sqlite3_pcache_methods2 testPcache = {
   426    435       (void*)&testpcacheGlobal,
   427    436       testpcacheInit,
   428    437       testpcacheShutdown,
   429    438       testpcacheCreate,
   430    439       testpcacheCachesize,
   431    440       testpcachePagecount,
   432    441       testpcacheFetch,
   433    442       testpcacheUnpin,
   434    443       testpcacheRekey,
   435    444       testpcacheTruncate,
   436    445       testpcacheDestroy,
   437    446     };
   438         -  static sqlite3_pcache_methods defaultPcache;
          447  +  static sqlite3_pcache_methods2 defaultPcache;
   439    448     static int isInstalled = 0;
   440    449   
   441    450     assert( testpcacheGlobal.nInstance==0 );
   442    451     assert( testpcacheGlobal.pDummy==0 );
   443    452     assert( discardChance<=100 );
   444    453     testpcacheGlobal.discardChance = discardChance;
   445    454     testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16);
   446    455     testpcacheGlobal.highStress = highStress;
   447    456     if( installFlag!=isInstalled ){
   448    457       if( installFlag ){
   449         -      sqlite3_config(SQLITE_CONFIG_GETPCACHE, &defaultPcache);
          458  +      sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &defaultPcache);
   450    459         assert( defaultPcache.xCreate!=testpcacheCreate );
   451         -      sqlite3_config(SQLITE_CONFIG_PCACHE, &testPcache);
          460  +      sqlite3_config(SQLITE_CONFIG_PCACHE2, &testPcache);
   452    461       }else{
   453    462         assert( defaultPcache.xCreate!=0 );
   454         -      sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultPcache);
          463  +      sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultPcache);
   455    464       }
   456    465       isInstalled = installFlag;
   457    466     }
   458    467   }

Changes to test/memsubsys1.test.

    64     64     sqlite3_status SQLITE_STATUS_PAGECACHE_SIZE 1
    65     65     sqlite3_status SQLITE_STATUS_SCRATCH_USED 1
    66     66     sqlite3_status SQLITE_STATUS_SCRATCH_OVERFLOW 1
    67     67     sqlite3_status SQLITE_STATUS_SCRATCH_SIZE 1
    68     68     sqlite3_status SQLITE_STATUS_PARSER_STACK 1
    69     69   }
    70     70   
    71         -set xtra_size 256
           71  +set xtra_size 272
    72     72   
    73     73   # Test 1:  Both PAGECACHE and SCRATCH are shut down.
    74     74   #
    75     75   db close
    76     76   sqlite3_shutdown
    77     77   sqlite3_config_lookaside 0 0
    78     78   sqlite3_initialize