/ Check-in [16f1e6ec]
Login

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

Overview
Comment:Add an alternative application-defined pcache implementation and add test cases to permutations.test to invoke it. Added the SQLITE_CONFIG_GETPCACHE method to sqlite3_config(). (CVS 5920)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 16f1e6ec2ad92f68c0079a0c2b5ca08a3b4af816
User & Date: drh 2008-11-19 01:20:26
Context
2008-11-19
09:05
Changes to avoid "unused parameter" compiler warnings. (CVS 5921) check-in: 88134322 user: danielk1977 tags: trunk
01:20
Add an alternative application-defined pcache implementation and add test cases to permutations.test to invoke it. Added the SQLITE_CONFIG_GETPCACHE method to sqlite3_config(). (CVS 5920) check-in: 16f1e6ec user: drh tags: trunk
2008-11-18
23:25
Fix to the lemon parser template when YYSTACKSIZE is 0 (dynamically allocated stack space). (CVS 5919) check-in: 00ccc596 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to Makefile.in.

   372    372     $(TOP)/src/test_func.c \
   373    373     $(TOP)/src/test_hexio.c \
   374    374     $(TOP)/src/test_malloc.c \
   375    375     $(TOP)/src/test_md5.c \
   376    376     $(TOP)/src/test_mutex.c \
   377    377     $(TOP)/src/test_onefile.c \
   378    378     $(TOP)/src/test_osinst.c \
          379  +  $(TOP)/src/test_pcache.c \
   379    380     $(TOP)/src/test_schema.c \
   380    381     $(TOP)/src/test_server.c \
   381    382     $(TOP)/src/test_tclvar.c \
   382    383     $(TOP)/src/test_thread.c
   383    384   
   384    385   # Header files used by all library source files.
   385    386   #

Changes to main.mk.

   224    224     $(TOP)/src/test_func.c \
   225    225     $(TOP)/src/test_hexio.c \
   226    226     $(TOP)/src/test_malloc.c \
   227    227     $(TOP)/src/test_md5.c \
   228    228     $(TOP)/src/test_mutex.c \
   229    229     $(TOP)/src/test_onefile.c \
   230    230     $(TOP)/src/test_osinst.c \
          231  +  $(TOP)/src/test_pcache.c \
   231    232     $(TOP)/src/test_schema.c \
   232    233     $(TOP)/src/test_server.c \
   233    234     $(TOP)/src/test_tclvar.c \
   234    235     $(TOP)/src/test_thread.c \
   235    236     $(TOP)/src/test_wsd.c \
   236    237   
   237    238   #TESTSRC += $(TOP)/ext/fts2/fts2_tokenizer.c

Changes to src/main.c.

    10     10   **
    11     11   *************************************************************************
    12     12   ** Main file for the SQLite library.  The routines in this file
    13     13   ** implement the programmer interface to the library.  Routines in
    14     14   ** other files are for internal use by SQLite and should not be
    15     15   ** accessed by users of the library.
    16     16   **
    17         -** $Id: main.c,v 1.512 2008/11/13 14:28:29 danielk1977 Exp $
           17  +** $Id: main.c,v 1.513 2008/11/19 01:20:26 drh Exp $
    18     18   */
    19     19   #include "sqliteInt.h"
    20     20   #include <ctype.h>
    21     21   
    22     22   #ifdef SQLITE_ENABLE_FTS3
    23     23   # include "fts3.h"
    24     24   #endif
................................................................................
   310    310       }
   311    311   
   312    312       case SQLITE_CONFIG_PCACHE: {
   313    313         /* Specify an alternative malloc implementation */
   314    314         sqlite3GlobalConfig.pcache = *va_arg(ap, sqlite3_pcache_methods*);
   315    315         break;
   316    316       }
          317  +
          318  +    case SQLITE_CONFIG_GETPCACHE: {
          319  +      if( sqlite3GlobalConfig.pcache.xInit==0 ){
          320  +        sqlite3PCacheSetDefault();
          321  +      }
          322  +      *va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache;
          323  +      break;
          324  +    }
   317    325   
   318    326   #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
   319    327       case SQLITE_CONFIG_HEAP: {
   320    328         /* Designate a buffer for heap memory space */
   321    329         sqlite3GlobalConfig.pHeap = va_arg(ap, void*);
   322    330         sqlite3GlobalConfig.nHeap = va_arg(ap, int);
   323    331         sqlite3GlobalConfig.mnReq = va_arg(ap, int);

Changes to src/sqlite.h.in.

    26     26   ** on how SQLite interfaces are suppose to operate.
    27     27   **
    28     28   ** The name of this file under configuration management is "sqlite.h.in".
    29     29   ** The makefile makes some minor changes to this file (such as inserting
    30     30   ** the version number) and changes its name to "sqlite3.h" as
    31     31   ** part of the build process.
    32     32   **
    33         -** @(#) $Id: sqlite.h.in,v 1.414 2008/11/18 19:18:09 drh Exp $
           33  +** @(#) $Id: sqlite.h.in,v 1.415 2008/11/19 01:20:26 drh Exp $
    34     34   */
    35     35   #ifndef _SQLITE3_H_
    36     36   #define _SQLITE3_H_
    37     37   #include <stdarg.h>     /* Needed for the definition of va_list */
    38     38   
    39     39   /*
    40     40   ** Make sure we can call this stuff from C++.
................................................................................
  6679   6679   **
  6680   6680   ** The cache is not required to perform any reference counting. A single 
  6681   6681   ** call to xUnpin() unpins the page regardless of the number of prior calls 
  6682   6682   ** to xFetch().
  6683   6683   **
  6684   6684   ** The xRekey() method is used to change the key value associated with the
  6685   6685   ** page passed as the second argument from oldKey to newKey. If the cache
  6686         -** contains an entry associated with oldKey, it should be discarded. Any
  6687         -** cache entry associated with oldKey is guaranteed not to be pinned.
         6686  +** previously contains an entry associated with newKey, it should be
         6687  +** discarded. Any prior cache entry associated with newKey is guaranteed not
         6688  +** to be pinned.
  6688   6689   **
  6689   6690   ** When SQLite calls the xTruncate() method, the cache must discard all
  6690   6691   ** existing cache entries with page numbers (keys) greater than or equal
  6691   6692   ** to the value of the iLimit parameter passed to xTruncate(). If any
  6692   6693   ** of these pages are pinned, they are implicitly unpinned, meaning that
  6693   6694   ** they can be safely discarded.
  6694   6695   **

Changes to src/test_malloc.c.

     9      9   **    May you share freely, never taking more than you give.
    10     10   **
    11     11   *************************************************************************
    12     12   **
    13     13   ** This file contains code used to implement test interfaces to the
    14     14   ** memory allocation subsystem.
    15     15   **
    16         -** $Id: test_malloc.c,v 1.50 2008/11/10 18:05:36 shane Exp $
           16  +** $Id: test_malloc.c,v 1.51 2008/11/19 01:20:26 drh Exp $
    17     17   */
    18     18   #include "sqliteInt.h"
    19     19   #include "tcl.h"
    20     20   #include <stdlib.h>
    21     21   #include <string.h>
    22     22   #include <assert.h>
    23     23   
................................................................................
   940    940     }
   941    941     pResult = Tcl_NewObj();
   942    942     Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(rc));
   943    943     Tcl_ListObjAppendElement(0, pResult, Tcl_NewIntObj(N));
   944    944     Tcl_SetObjResult(interp, pResult);
   945    945     return TCL_OK;
   946    946   }
          947  +
          948  +/*
          949  +** Usage:    sqlite3_config_alt_pcache INSTALL_FLAG DISCARD_CHANCE PRNG_SEED
          950  +**
          951  +** Set up the alternative test page cache.  Install if INSTALL_FLAG is
          952  +** true and uninstall (reverting to the default page cache) if INSTALL_FLAG
          953  +** is false.  DISCARD_CHANGE is an integer between 0 and 100 inclusive
          954  +** which determines the chance of discarding a page when unpinned.  100
          955  +** is certainty.  0 is never.  PRNG_SEED is the pseudo-random number generator
          956  +** seed.
          957  +*/
          958  +static int test_alt_pcache(
          959  +  void * clientData,
          960  +  Tcl_Interp *interp,
          961  +  int objc,
          962  +  Tcl_Obj *CONST objv[]
          963  +){
          964  +  int installFlag;
          965  +  int discardChance;
          966  +  int prngSeed;
          967  +  extern void installTestPCache(int,unsigned,unsigned);
          968  +  if( objc!=4 ){
          969  +    Tcl_WrongNumArgs(interp, 1, objv, "INSTALLFLAG DISCARDCHANCE PRNGSEEED");
          970  +    return TCL_ERROR;
          971  +  }
          972  +  if( Tcl_GetIntFromObj(interp, objv[1], &installFlag) ) return TCL_ERROR;
          973  +  if( Tcl_GetIntFromObj(interp, objv[2], &discardChance) ) return TCL_ERROR;
          974  +  if( Tcl_GetIntFromObj(interp, objv[3], &prngSeed) ) return TCL_ERROR;
          975  +  if( discardChance<0 || discardChance>100 ){
          976  +    Tcl_AppendResult(interp, "discard-chance should be between 0 and 100",
          977  +                     (char*)0);
          978  +    return TCL_ERROR;
          979  +  }
          980  +  installTestPCache(installFlag, (unsigned)discardChance, (unsigned)prngSeed);
          981  +  return TCL_OK;
          982  +}
   947    983   
   948    984   /*
   949    985   ** Usage:    sqlite3_config_memstatus BOOLEAN
   950    986   **
   951    987   ** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS.
   952    988   */
   953    989   static int test_config_memstatus(
................................................................................
  1308   1344        { "sqlite3_memdebug_fail",      test_memdebug_fail            ,0 },
  1309   1345        { "sqlite3_memdebug_pending",   test_memdebug_pending         ,0 },
  1310   1346        { "sqlite3_memdebug_settitle",  test_memdebug_settitle        ,0 },
  1311   1347        { "sqlite3_memdebug_malloc_count", test_memdebug_malloc_count ,0 },
  1312   1348        { "sqlite3_memdebug_log",       test_memdebug_log             ,0 },
  1313   1349        { "sqlite3_config_scratch",     test_config_scratch           ,0 },
  1314   1350        { "sqlite3_config_pagecache",   test_config_pagecache         ,0 },
         1351  +     { "sqlite3_config_alt_pcache",  test_alt_pcache               ,0 },
  1315   1352        { "sqlite3_status",             test_status                   ,0 },
  1316   1353        { "sqlite3_db_status",          test_db_status                ,0 },
  1317   1354        { "install_malloc_faultsim",    test_install_malloc_faultsim  ,0 },
  1318   1355        { "sqlite3_config_heap",        test_config_heap              ,0 },
  1319   1356        { "sqlite3_config_memstatus",   test_config_memstatus         ,0 },
  1320   1357        { "sqlite3_config_lookaside",   test_config_lookaside         ,0 },
  1321   1358        { "sqlite3_config_error",       test_config_error             ,0 },
  1322   1359        { "sqlite3_db_config_lookaside",test_db_config_lookaside      ,0 },
  1323   1360        { "sqlite3_dump_memsys3",       test_dump_memsys3             ,3 },
  1324         -     { "sqlite3_dump_memsys5",       test_dump_memsys3             ,5 }
         1361  +     { "sqlite3_dump_memsys5",       test_dump_memsys3             ,5 },
  1325   1362     };
  1326   1363     int i;
  1327   1364     for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){
  1328   1365       ClientData c = (ClientData)aObjCmd[i].clientData;
  1329   1366       Tcl_CreateObjCommand(interp, aObjCmd[i].zName, aObjCmd[i].xProc, c, 0);
  1330   1367     }
  1331   1368     return TCL_OK;
  1332   1369   }
  1333   1370   #endif

Added src/test_pcache.c.

            1  +/*
            2  +** 2008 November 18
            3  +**
            4  +** The author disclaims copyright to this source code.  In place of
            5  +** a legal notice, here is a blessing:
            6  +**
            7  +**    May you do good and not evil.
            8  +**    May you find forgiveness for yourself and forgive others.
            9  +**    May you share freely, never taking more than you give.
           10  +**
           11  +*************************************************************************
           12  +** 
           13  +** This file contains code used for testing the SQLite system.
           14  +** None of the code in this file goes into a deliverable build.
           15  +** 
           16  +** This file contains an application-defined pager cache
           17  +** implementation that can be plugged in in place of the
           18  +** default pcache.  This alternative pager cache will throw
           19  +** some errors that the default cache does not.
           20  +**
           21  +** This pagecache implementation is designed for simplicity
           22  +** not speed.  
           23  +**
           24  +** $Id: test_pcache.c,v 1.1 2008/11/19 01:20:26 drh Exp $
           25  +*/
           26  +#include "sqlite3.h"
           27  +#include <string.h>
           28  +#include <assert.h>
           29  +
           30  +/*
           31  +** Global data used by this test implementation.  There is no
           32  +** mutexing, which means this page cache will not work in a
           33  +** multi-threaded test.
           34  +*/
           35  +typedef struct testpcacheGlobalType testpcacheGlobalType;
           36  +struct testpcacheGlobalType {
           37  +  void *pDummy;             /* Dummy allocation to simulate failures */
           38  +  int nInstance;            /* Number of current instances */
           39  +  unsigned discardChance;   /* Chance of discarding on an unpin */
           40  +  unsigned prngSeed;        /* Seed for the PRNG */
           41  +};
           42  +static testpcacheGlobalType testpcacheGlobal;
           43  +
           44  +/*
           45  +** Initializer.
           46  +**
           47  +** Verify that the initializer is only called when the system is
           48  +** uninitialized.  Allocate some memory and report SQLITE_NOMEM if
           49  +** the allocation fails.  This provides a means to test the recovery
           50  +** from a failed initialization attempt.  It also verifies that the
           51  +** the destructor always gets call - otherwise there would be a
           52  +** memory leak.
           53  +*/
           54  +static int testpcacheInit(void *pArg){
           55  +  assert( pArg==(void*)&testpcacheGlobal );
           56  +  assert( testpcacheGlobal.pDummy==0 );
           57  +  assert( testpcacheGlobal.nInstance==0 );
           58  +  testpcacheGlobal.pDummy = sqlite3_malloc(10);
           59  +  return testpcacheGlobal.pDummy==0 ? SQLITE_NOMEM : SQLITE_OK;
           60  +}
           61  +
           62  +/*
           63  +** Destructor
           64  +**
           65  +** Verify that this is only called after initialization.
           66  +** Free the memory allocated by the initializer.
           67  +*/
           68  +static void testpcacheShutdown(void *pArg){
           69  +  assert( pArg==(void*)&testpcacheGlobal );
           70  +  assert( testpcacheGlobal.pDummy!=0 );
           71  +  assert( testpcacheGlobal.nInstance==0 );
           72  +  sqlite3_free( testpcacheGlobal.pDummy );
           73  +  testpcacheGlobal.pDummy = 0;
           74  +}
           75  +
           76  +/*
           77  +** Number of pages in a cache
           78  +*/
           79  +#define TESTPCACHE_NPAGE    217
           80  +#define TESTPCACHE_RESERVE   17
           81  +
           82  +/*
           83  +** Magic numbers used to determine validity of the page cache.
           84  +*/
           85  +#define TESTPCACHE_VALID  0x364585fd
           86  +#define TESTPCACHE_CLEAR  0xd42670d4
           87  +
           88  +/*
           89  +** Private implementation of a page cache.
           90  +*/
           91  +typedef struct testpcache testpcache;
           92  +struct testpcache {
           93  +  int szPage;               /* Size of each page.  Multiple of 8. */
           94  +  int bPurgeable;           /* True if the page cache is purgeable */
           95  +  int nFree;                /* Number of unused slots in a[] */
           96  +  int nPinned;              /* Number of pinned slots in a[] */
           97  +  unsigned iRand;           /* State of the PRNG */
           98  +  unsigned iMagic;          /* Magic number for sanity checking */
           99  +  struct testpcachePage {
          100  +    unsigned key;              /* The key for this page. 0 means unallocated */
          101  +    int isPinned;              /* True if the page is pinned */
          102  +    void *pData;               /* Data for this page */
          103  +  } a[TESTPCACHE_NPAGE];    /* All pages in the cache */
          104  +};
          105  +
          106  +/*
          107  +** Get a random number using the PRNG in the given page cache.
          108  +*/
          109  +static unsigned testpcacheRandom(testpcache *p){
          110  +  unsigned x = 0;
          111  +  int i;
          112  +  for(i=0; i<4; i++){
          113  +    p->iRand = (p->iRand*69069 + 5);
          114  +    x = (x<<8) | ((p->iRand>>16)&0xff);
          115  +  }
          116  +  return x;
          117  +}
          118  +
          119  +
          120  +/*
          121  +** Allocate a new page cache instance.
          122  +*/
          123  +static sqlite3_pcache *testpcacheCreate(int szPage, int bPurgeable){
          124  +  int nMem;
          125  +  char *x;
          126  +  testpcache *p;
          127  +  int i;
          128  +  assert( testpcacheGlobal.pDummy!=0 );
          129  +  szPage = (szPage+7)&~7;
          130  +  nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*szPage;
          131  +  p = sqlite3_malloc( nMem );
          132  +  if( p==0 ) return 0;
          133  +  x = (char*)&p[1];
          134  +  p->szPage = szPage;
          135  +  p->nFree = TESTPCACHE_NPAGE;
          136  +  p->nPinned = 0;
          137  +  p->iRand = testpcacheGlobal.prngSeed;
          138  +  p->bPurgeable = bPurgeable;
          139  +  p->iMagic = TESTPCACHE_VALID;
          140  +  for(i=0; i<TESTPCACHE_NPAGE; i++, x += szPage){
          141  +    p->a[i].key = 0;
          142  +    p->a[i].isPinned = 0;
          143  +    p->a[i].pData = (void*)x;
          144  +  }
          145  +  testpcacheGlobal.nInstance++;
          146  +  return (sqlite3_pcache*)p;
          147  +}
          148  +
          149  +/*
          150  +** Set the cache size
          151  +*/
          152  +static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){
          153  +  testpcache *p = (testpcache*)pCache;
          154  +  assert( p->iMagic==TESTPCACHE_VALID );
          155  +  assert( newSize>=1 );
          156  +  assert( testpcacheGlobal.pDummy!=0 );
          157  +  assert( testpcacheGlobal.nInstance>0 );
          158  +}
          159  +
          160  +/*
          161  +** Return the number of pages in the cache that are being used.
          162  +** This includes both pinned and unpinned pages.
          163  +*/
          164  +static int testpcachePagecount(sqlite3_pcache *pCache){
          165  +  testpcache *p = (testpcache*)pCache;
          166  +  assert( p->iMagic==TESTPCACHE_VALID );
          167  +  assert( testpcacheGlobal.pDummy!=0 );
          168  +  assert( testpcacheGlobal.nInstance>0 );
          169  +  return TESTPCACHE_NPAGE - p->nFree;
          170  +}
          171  +
          172  +/*
          173  +** Fetch a page.
          174  +*/
          175  +static void *testpcacheFetch(
          176  +  sqlite3_pcache *pCache,
          177  +  unsigned key,
          178  +  int createFlag
          179  +){
          180  +  testpcache *p = (testpcache*)pCache;
          181  +  int i, j;
          182  +  assert( p->iMagic==TESTPCACHE_VALID );
          183  +  assert( testpcacheGlobal.pDummy!=0 );
          184  +  assert( testpcacheGlobal.nInstance>0 );
          185  +
          186  +  /* See if the page is already in cache.  Return immediately if it is */
          187  +  for(i=0; i<TESTPCACHE_NPAGE; i++){
          188  +    if( p->a[i].key==key ){
          189  +      if( !p->a[i].isPinned ){
          190  +        p->nPinned++;
          191  +        assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
          192  +        p->a[i].isPinned = 1;
          193  +      }
          194  +      return p->a[i].pData;
          195  +    }
          196  +  }
          197  +
          198  +  /* If createFlag is 0, never allocate a new page */
          199  +  if( createFlag==0 ){
          200  +    return 0;
          201  +  }
          202  +
          203  +  /* If no pages are available, always fail */
          204  +  if( p->nPinned==TESTPCACHE_NPAGE ){
          205  +    return 0;
          206  +  }
          207  +
          208  +  /* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */
          209  +  if( p->nPinned>=TESTPCACHE_NPAGE-TESTPCACHE_RESERVE && createFlag<2 ){
          210  +    return 0;
          211  +  }
          212  +
          213  +  /* Find a free page to allocate if there are any free pages.
          214  +  ** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2.
          215  +  */
          216  +  if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){
          217  +    j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
          218  +    for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
          219  +      if( p->a[j].key==0 ){
          220  +        p->a[j].key = key;
          221  +        p->a[j].isPinned = 1;
          222  +        memset(p->a[j].pData, 0, p->szPage);
          223  +        p->nPinned++;
          224  +        p->nFree--;
          225  +        assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
          226  +        return p->a[j].pData;
          227  +      }
          228  +    }
          229  +
          230  +    /* The prior loop always finds a freepage to allocate */
          231  +    assert( 0 );
          232  +  }
          233  +
          234  +  /* If this cache is not purgeable then we have to fail.
          235  +  */
          236  +  if( p->bPurgeable==0 ){
          237  +    return 0;
          238  +  }
          239  +
          240  +  /* If there are no free pages, recycle a page.  The page to
          241  +  ** recycle is selected at random from all unpinned pages.
          242  +  */
          243  +  j = testpcacheRandom(p) % TESTPCACHE_NPAGE;
          244  +  for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){
          245  +    if( p->a[j].key>0 && p->a[j].isPinned==0 ){
          246  +      p->a[j].key = key;
          247  +      p->a[j].isPinned = 1;
          248  +      memset(p->a[j].pData, 0, p->szPage);
          249  +      p->nPinned++;
          250  +      assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree );
          251  +      return p->a[j].pData;
          252  +    }
          253  +  }
          254  +
          255  +  /* The previous loop always finds a page to recycle. */
          256  +  assert(0);
          257  +  return 0;
          258  +}
          259  +
          260  +/*
          261  +** Unpin a page.
          262  +*/
          263  +static void testpcacheUnpin(
          264  +  sqlite3_pcache *pCache,
          265  +  void *pOldPage,
          266  +  int discard
          267  +){
          268  +  testpcache *p = (testpcache*)pCache;
          269  +  int i;
          270  +  assert( p->iMagic==TESTPCACHE_VALID );
          271  +  assert( testpcacheGlobal.pDummy!=0 );
          272  +  assert( testpcacheGlobal.nInstance>0 );
          273  +
          274  +  /* Randomly discard pages as they are unpinned according to the
          275  +  ** discardChance setting.  If discardChance is 0, the random discard
          276  +  ** never happens.  If discardChance is 100, it always happens.
          277  +  */
          278  +  if( p->bPurgeable
          279  +  && (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100)
          280  +  ){
          281  +    discard = 1;
          282  +  }
          283  +
          284  +  for(i=0; i<TESTPCACHE_NPAGE; i++){
          285  +    if( p->a[i].pData==pOldPage ){
          286  +      /* The pOldPage pointer always points to a pinned page */
          287  +      assert( p->a[i].isPinned );
          288  +      p->a[i].isPinned = 0;
          289  +      p->nPinned--;
          290  +      assert( p->nPinned>=0 );
          291  +      if( discard ){
          292  +        p->a[i].key = 0;
          293  +        p->nFree++;
          294  +        assert( p->nFree<=TESTPCACHE_NPAGE );
          295  +      }
          296  +      return;
          297  +    }
          298  +  }
          299  +
          300  +  /* The pOldPage pointer always points to a valid page */
          301  +  assert( 0 );
          302  +}
          303  +
          304  +
          305  +/*
          306  +** Rekey a single page.
          307  +*/
          308  +static void testpcacheRekey(
          309  +  sqlite3_pcache *pCache,
          310  +  void *pOldPage,
          311  +  unsigned oldKey,
          312  +  unsigned newKey
          313  +){
          314  +  testpcache *p = (testpcache*)pCache;
          315  +  int i;
          316  +  assert( p->iMagic==TESTPCACHE_VALID );
          317  +  assert( testpcacheGlobal.pDummy!=0 );
          318  +  assert( testpcacheGlobal.nInstance>0 );
          319  +
          320  +  /* If there already exists another page at newKey, verify that
          321  +  ** the other page is unpinned and discard it.
          322  +  */
          323  +  for(i=0; i<TESTPCACHE_NPAGE; i++){
          324  +    if( p->a[i].key==newKey ){
          325  +      /* The new key is never a page that is already pinned */
          326  +      assert( p->a[i].isPinned==0 );
          327  +      p->a[i].key = 0;
          328  +      p->nFree++;
          329  +      assert( p->nFree<=TESTPCACHE_NPAGE );
          330  +      break;
          331  +    }
          332  +  }
          333  +
          334  +  /* Find the page to be rekeyed and rekey it.
          335  +  */
          336  +  for(i=0; i<TESTPCACHE_NPAGE; i++){
          337  +    if( p->a[i].key==oldKey ){
          338  +      /* The oldKey and pOldPage parameters match */
          339  +      assert( p->a[i].pData==pOldPage );
          340  +      /* Page to be rekeyed must be pinned */
          341  +      assert( p->a[i].isPinned );
          342  +      p->a[i].key = newKey;
          343  +      return;
          344  +    }
          345  +  }
          346  +
          347  +  /* Rekey is always given a valid page to work with */
          348  +  assert( 0 );
          349  +}
          350  +
          351  +
          352  +/*
          353  +** Truncate the page cache.  Every page with a key of iLimit or larger
          354  +** is discarded.
          355  +*/
          356  +static void testpcacheTruncate(sqlite3_pcache *pCache, unsigned iLimit){
          357  +  testpcache *p = (testpcache*)pCache;
          358  +  unsigned int i;
          359  +  assert( p->iMagic==TESTPCACHE_VALID );
          360  +  assert( testpcacheGlobal.pDummy!=0 );
          361  +  assert( testpcacheGlobal.nInstance>0 );
          362  +  for(i=0; i<TESTPCACHE_NPAGE; i++){
          363  +    if( p->a[i].key>=iLimit ){
          364  +      p->a[i].key = 0;
          365  +      if( p->a[i].isPinned ){
          366  +        p->nPinned--;
          367  +        assert( p->nPinned>=0 );
          368  +      }
          369  +      p->nFree++;
          370  +      assert( p->nFree<=TESTPCACHE_NPAGE );
          371  +    }
          372  +  }
          373  +}
          374  +
          375  +/*
          376  +** Destroy a page cache.
          377  +*/
          378  +static void testpcacheDestroy(sqlite3_pcache *pCache){
          379  +  testpcache *p = (testpcache*)pCache;
          380  +  assert( p->iMagic==TESTPCACHE_VALID );
          381  +  assert( testpcacheGlobal.pDummy!=0 );
          382  +  assert( testpcacheGlobal.nInstance>0 );
          383  +  p->iMagic = TESTPCACHE_CLEAR;
          384  +  sqlite3_free(p);
          385  +  testpcacheGlobal.nInstance--;
          386  +}
          387  +
          388  +
          389  +/*
          390  +** Invoke this routine to register or unregister the testing pager cache
          391  +** implemented by this file.
          392  +**
          393  +** Install the test pager cache if installFlag is 1 and uninstall it if
          394  +** installFlag is 0.
          395  +**
          396  +** When installing, discardChance is a number between 0 and 100 that
          397  +** indicates the probability of discarding a page when unpinning the
          398  +** page.  0 means never discard (unless the discard flag is set).
          399  +** 100 means always discard.
          400  +*/
          401  +void installTestPCache(
          402  +  int installFlag,            /* True to install.  False to uninstall. */
          403  +  unsigned discardChance,     /* 0-100.  Chance to discard on unpin */
          404  +  unsigned prngSeed           /* Seed for the PRNG */
          405  +){
          406  +  static const sqlite3_pcache_methods testPcache = {
          407  +    (void*)&testpcacheGlobal,
          408  +    testpcacheInit,
          409  +    testpcacheShutdown,
          410  +    testpcacheCreate,
          411  +    testpcacheCachesize,
          412  +    testpcachePagecount,
          413  +    testpcacheFetch,
          414  +    testpcacheUnpin,
          415  +    testpcacheRekey,
          416  +    testpcacheTruncate,
          417  +    testpcacheDestroy,
          418  +  };
          419  +  static sqlite3_pcache_methods defaultPcache;
          420  +  static int isInstalled = 0;
          421  +
          422  +  assert( testpcacheGlobal.nInstance==0 );
          423  +  assert( testpcacheGlobal.pDummy==0 );
          424  +  assert( discardChance<=100 );
          425  +  testpcacheGlobal.discardChance = discardChance;
          426  +  testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16);
          427  +  if( installFlag!=isInstalled ){
          428  +    if( installFlag ){
          429  +      sqlite3_config(SQLITE_CONFIG_GETPCACHE, &defaultPcache);
          430  +      assert( defaultPcache.xCreate!=testpcacheCreate );
          431  +      sqlite3_config(SQLITE_CONFIG_PCACHE, &testPcache);
          432  +    }else{
          433  +      assert( defaultPcache.xCreate!=0 );
          434  +      sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultPcache);
          435  +    }
          436  +    isInstalled = installFlag;
          437  +  }
          438  +}

Changes to test/permutations.test.

     5      5   #
     6      6   #    May you do good and not evil.
     7      7   #    May you find forgiveness for yourself and forgive others.
     8      8   #    May you share freely, never taking more than you give.
     9      9   #
    10     10   #***********************************************************************
    11     11   #
    12         -# $Id: permutations.test,v 1.38 2008/11/13 16:21:50 danielk1977 Exp $
           12  +# $Id: permutations.test,v 1.39 2008/11/19 01:20:26 drh Exp $
    13     13   
    14     14   set testdir [file dirname $argv0]
    15     15   source $testdir/tester.tcl
    16     16   
    17     17   # Argument processing.
    18     18   #
    19     19   #puts "PERM-DEBUG: argv=$argv"
................................................................................
   600    600     sqlite3_simulate_device -char safe_append
   601    601   } -shutdown {
   602    602     rename sqlite3 {}
   603    603     rename sqlite3_shutdown sqlite3
   604    604   } -include [lsort [concat shared_err.test $ALLTESTS]] \
   605    605     -exclude async3.test
   606    606   
          607  +# The set of tests to run on the alternative-pcache
          608  +set perm-alt-pcache-testset {
          609  +  async.test
          610  +  attach.test
          611  +  delete.test delete2.test
          612  +  index.test
          613  +  insert.test insert2.test
          614  +  join.test join2.test
          615  +  rollback.test
          616  +  select1.test select2.test
          617  +  trans.test
          618  +  update.test
          619  +}
          620  +
          621  +run_tests "pcache0" -description {
          622  +  Alternative pcache implementation without random discard
          623  +} -initialize {
          624  +  catch {db close}
          625  +  sqlite3_reset_auto_extension
          626  +  sqlite3_shutdown
          627  +  sqlite3_config_alt_pcache 1 0 1
          628  +  sqlite3_initialize
          629  +  autoinstall_test_functions
          630  +} -shutdown {
          631  +  catch {db close}
          632  +  sqlite3_reset_auto_extension
          633  +  sqlite3_shutdown
          634  +  sqlite3_config_alt_pcache 0 0 0
          635  +  sqlite3_config_lookaside 100 500
          636  +  install_malloc_faultsim 1 
          637  +  sqlite3_initialize
          638  +} -include ${perm-alt-pcache-testset}
          639  +
          640  +run_tests "pcache10" -description {
          641  +  Alternative pcache implementation without 10% random discard
          642  +} -initialize {
          643  +  catch {db close}
          644  +  sqlite3_reset_auto_extension
          645  +  sqlite3_shutdown
          646  +  sqlite3_config_alt_pcache 1 50 1
          647  +  sqlite3_initialize
          648  +  autoinstall_test_functions
          649  +} -shutdown {
          650  +  catch {db close}
          651  +  sqlite3_reset_auto_extension
          652  +  sqlite3_shutdown
          653  +  sqlite3_config_alt_pcache 0 0 0
          654  +  sqlite3_initialize
          655  +} -include ${perm-alt-pcache-testset}
          656  +
          657  +run_tests "pcache50" -description {
          658  +  Alternative pcache implementation without 50% random discard
          659  +} -initialize {
          660  +  catch {db close}
          661  +  sqlite3_reset_auto_extension
          662  +  sqlite3_shutdown
          663  +  sqlite3_config_alt_pcache 1 50 1
          664  +  sqlite3_initialize
          665  +  autoinstall_test_functions
          666  +} -shutdown {
          667  +  catch {db close}
          668  +  sqlite3_reset_auto_extension
          669  +  sqlite3_shutdown
          670  +  sqlite3_config_alt_pcache 0 0 0
          671  +  sqlite3_initialize
          672  +} -include ${perm-alt-pcache-testset}
          673  +
          674  +run_tests "pcache90" -description {
          675  +  Alternative pcache implementation without 90% random discard
          676  +} -initialize {
          677  +  catch {db close}
          678  +  sqlite3_reset_auto_extension
          679  +  sqlite3_shutdown
          680  +  sqlite3_config_alt_pcache 1 50 1
          681  +  sqlite3_initialize
          682  +  autoinstall_test_functions
          683  +} -shutdown {
          684  +  catch {db close}
          685  +  sqlite3_reset_auto_extension
          686  +  sqlite3_shutdown
          687  +  sqlite3_config_alt_pcache 0 0 0
          688  +  sqlite3_initialize
          689  +} -include ${perm-alt-pcache-testset}
          690  +
          691  +run_tests "pcache100" -description {
          692  +  Alternative pcache implementation that always discards when unpinning
          693  +} -initialize {
          694  +  catch {db close}
          695  +  sqlite3_reset_auto_extension
          696  +  sqlite3_shutdown
          697  +  sqlite3_config_alt_pcache 1 100 1
          698  +  sqlite3_initialize
          699  +  autoinstall_test_functions
          700  +} -shutdown {
          701  +  catch {db close}
          702  +  sqlite3_reset_auto_extension
          703  +  sqlite3_shutdown
          704  +  sqlite3_config_alt_pcache 0 0 0
          705  +  sqlite3_initialize
          706  +} -include ${perm-alt-pcache-testset}
   607    707   
   608    708   # End of tests
   609    709   #############################################################################
   610    710   
   611    711   if {$::perm::testmode eq "targets"} { puts "" ; exit }
   612    712   
   613    713   # Restore the [sqlite3] command.