diff options
author | drh <drh@noemail.net> | 2008-11-19 01:20:26 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2008-11-19 01:20:26 +0000 |
commit | b232c23297248eb0753cf036c99f4bece9df992a (patch) | |
tree | 1131a6c766216f9a071bac0119ed202a6f377332 | |
parent | 4c6517848fac8fa107bbd746171f37e57380be3f (diff) | |
download | sqlite-b232c23297248eb0753cf036c99f4bece9df992a.tar.gz sqlite-b232c23297248eb0753cf036c99f4bece9df992a.zip |
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)
FossilOrigin-Name: 16f1e6ec2ad92f68c0079a0c2b5ca08a3b4af816
-rw-r--r-- | Makefile.in | 1 | ||||
-rw-r--r-- | main.mk | 1 | ||||
-rw-r--r-- | manifest | 23 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/main.c | 10 | ||||
-rw-r--r-- | src/sqlite.h.in | 7 | ||||
-rw-r--r-- | src/test_malloc.c | 41 | ||||
-rw-r--r-- | src/test_pcache.c | 438 | ||||
-rw-r--r-- | test/permutations.test | 102 |
9 files changed, 606 insertions, 19 deletions
diff --git a/Makefile.in b/Makefile.in index 63152856c..427680ee6 100644 --- a/Makefile.in +++ b/Makefile.in @@ -376,6 +376,7 @@ TESTSRC = \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ + $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ @@ -228,6 +228,7 @@ TESTSRC = \ $(TOP)/src/test_mutex.c \ $(TOP)/src/test_onefile.c \ $(TOP)/src/test_osinst.c \ + $(TOP)/src/test_pcache.c \ $(TOP)/src/test_schema.c \ $(TOP)/src/test_server.c \ $(TOP)/src/test_tclvar.c \ @@ -1,7 +1,7 @@ -C Fix\sto\sthe\slemon\sparser\stemplate\swhen\sYYSTACKSIZE\sis\s0\s(dynamically\nallocated\sstack\sspace).\s(CVS\s5919) -D 2008-11-18T23:25:55 +C Add\san\salternative\sapplication-defined\spcache\simplementation\sand\sadd\stest\ncases\sto\spermutations.test\sto\sinvoke\sit.\s\sAdded\sthe\sSQLITE_CONFIG_GETPCACHE\nmethod\sto\ssqlite3_config().\s(CVS\s5920) +D 2008-11-19T01:20:26 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 -F Makefile.in 6cbc7db84c23804c368bc7ffe51367412212d7b2 +F Makefile.in 0aa7bbe3be6acc4045706e3bb3fd0b8f38f4a3b5 F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 F Makefile.vxwSH4 d53b4be86491060d498b22148951b6d765884cab F README b974cdc3f9f12b87e851b04e75996d720ebf81ac @@ -80,7 +80,7 @@ F ext/rtree/tkt3363.test 6662237ea75bb431cd5d262dfc9535e1023315fc F ext/rtree/viewrtree.tcl 09526398dae87a5a87c5aac2b3854dbaf8376869 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh 09fe5815427dc7d0abb188bbcdf0e34896577210 -F main.mk 2da751f09754965bcbc8b3fde56c8c71e67ebdb5 +F main.mk 87a73e91a3d0827dc796a249260866335e38a36e F mkdll.sh 7d09b23c05d56532e9d44a50868eb4b12ff4f74a F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 4123480947681d9b434a5e7b1ee08135abe409ac @@ -119,7 +119,7 @@ F src/insert.c 47d2c27396724d5050a5f7d6a54ce44ec163e473 F src/journal.c cffd2cd214e58c0e99c3ff632b3bee6c7cbb260e F src/legacy.c aac57bd984e666059011ea01ec4383892a253be3 F src/loadext.c 3872457afdf25bb174fd383cb4e3e0d2a9e60552 -F src/main.c fd93666b883dbe976f8fb9a5b87784bde2eca43d +F src/main.c 3b99ff818efa18a772b4fceb8bc72081aa9b1517 F src/malloc.c 00532787dc7f0dbf4e2487aa823946e1d0524ef1 F src/mem0.c f2f84062d1f35814d6535c9f9e33de3bfb3b132c F src/mem1.c 2091081d1c6bcd4516738f37cd84d42e814cf9a2 @@ -152,7 +152,7 @@ F src/random.c a87afbd598aa877e23ac676ee92fd8ee5c786a51 F src/resolve.c 4af5391d2b4c1d6c583a6805ac6660181de4545b F src/select.c 18c6d96f4f8c6e43cb35201a1245ff02be8c9378 F src/shell.c 650d1a87408682280d0e9d014d0d328c59c84b38 -F src/sqlite.h.in 4d05b9195e9489dc62857d9dd3334b0139715101 +F src/sqlite.h.in e9a0aa2502dfe01bf166956051528f28871474c3 F src/sqlite3ext.h 1db7d63ab5de4b3e6b83dd03d1a4e64fef6d2a17 F src/sqliteInt.h d48bb0ecc2d1c25d47ea144560d7a8e6864fbabe F src/sqliteLimit.h f435e728c6b620ef7312814d660a81f9356eb5c8 @@ -176,11 +176,12 @@ F src/test_devsym.c 802d10e65b4217208cb47059b84adf46318bcdf4 F src/test_func.c a55c4d5479ff2eb5c0a22d4d88e9528ab59c953b F src/test_hexio.c 2f1122aa3f012fa0142ee3c36ce5c902a70cd12f F src/test_loadext.c 97dc8800e46a46ed002c2968572656f37e9c0dd9 -F src/test_malloc.c e2f6e6774308f531a3d12475dd2a5211dd31b7eb +F src/test_malloc.c 5127337c9fb4c851a7f604c0170e0e5ca1fbfe33 F src/test_md5.c 28209a4e2068711b5443c33104fe41f21d160071 F src/test_mutex.c 66c4ab4e0396a440ddb17cd9b58a05305144f05d F src/test_onefile.c 243157b10275251c5dc2d6619aee2ff9ae22379c F src/test_osinst.c ae29e9c09485622a157849508302dd9ffe44f21f +F src/test_pcache.c 0008968cc36558c8253585e5d321eccba44edb80 F src/test_schema.c 4b4bf7bb329326458c491b0e6facd4c8c4c5b479 F src/test_server.c f0a403b5f699c09bd2b1236b6f69830fd6221f6b F src/test_tclvar.c 9e42fa59d3d2f064b7ab8628e7ab2dc8a9fe93d4 @@ -461,7 +462,7 @@ F test/pageropt.test 3ee6578891baaca967f0bd349e4abfa736229e1a F test/pagesize.test 0d9ff3fedfce6e5ffe8fa7aca9b6d3433a2e843b F test/pcache.test 515b4c26e9f57660357dfff5b6b697acac1abc5f F test/pcache2.test 46efd980a89f737847b99327bda19e08fe11e402 -F test/permutations.test 6f2952820e43568d7b869d54f4c7140bc695a5f0 +F test/permutations.test 5308a94878efc81a8e8ce133926dfb2c53d19133 F test/pragma.test 165372b62391d233715cde82d99f34d306f9257f F test/pragma2.test 5364893491b9231dd170e3459bfc2e2342658b47 F test/printf.test 262a5acd3158f788e9bdf7f18d718f3af32ff6ef @@ -658,7 +659,7 @@ F tool/speedtest16.c c8a9c793df96db7e4933f0852abb7a03d48f2e81 F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e -P 2990b5b8e7bd8f91af24e5a606666077855ae283 -R d14041d00b154aef89232b6b6d2d34f2 +P 00ccc5967f8912961029a3513445c5e2ac713560 +R 073d299d61cca110cab93939ee191b26 U drh -Z 887cd197809b3ccb7d5ef687ab5d5c82 +Z 897c512f710f8b2c13194a810c1ac26d diff --git a/manifest.uuid b/manifest.uuid index 3e7e91a2f..798e4184a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -00ccc5967f8912961029a3513445c5e2ac713560
\ No newline at end of file +16f1e6ec2ad92f68c0079a0c2b5ca08a3b4af816
\ No newline at end of file diff --git a/src/main.c b/src/main.c index 3662697fe..16f89b785 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.512 2008/11/13 14:28:29 danielk1977 Exp $ +** $Id: main.c,v 1.513 2008/11/19 01:20:26 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -315,6 +315,14 @@ int sqlite3_config(int op, ...){ break; } + case SQLITE_CONFIG_GETPCACHE: { + if( sqlite3GlobalConfig.pcache.xInit==0 ){ + sqlite3PCacheSetDefault(); + } + *va_arg(ap, sqlite3_pcache_methods*) = sqlite3GlobalConfig.pcache; + break; + } + #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) case SQLITE_CONFIG_HEAP: { /* Designate a buffer for heap memory space */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 39070f737..8f1c73459 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.414 2008/11/18 19:18:09 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.415 2008/11/19 01:20:26 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -6683,8 +6683,9 @@ typedef struct sqlite3_pcache sqlite3_pcache; ** ** The xRekey() method is used to change the key value associated with the ** page passed as the second argument from oldKey to newKey. If the cache -** contains an entry associated with oldKey, it should be discarded. Any -** cache entry associated with oldKey is guaranteed not to be pinned. +** previously contains an entry associated with newKey, it should be +** discarded. Any prior cache entry associated with newKey is guaranteed not +** to be pinned. ** ** When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal diff --git a/src/test_malloc.c b/src/test_malloc.c index bcf18d2a0..2009f5428 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -13,7 +13,7 @@ ** This file contains code used to implement test interfaces to the ** memory allocation subsystem. ** -** $Id: test_malloc.c,v 1.50 2008/11/10 18:05:36 shane Exp $ +** $Id: test_malloc.c,v 1.51 2008/11/19 01:20:26 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -946,6 +946,42 @@ static int test_config_pagecache( } /* +** Usage: sqlite3_config_alt_pcache INSTALL_FLAG DISCARD_CHANCE PRNG_SEED +** +** Set up the alternative test page cache. Install if INSTALL_FLAG is +** true and uninstall (reverting to the default page cache) if INSTALL_FLAG +** is false. DISCARD_CHANGE is an integer between 0 and 100 inclusive +** which determines the chance of discarding a page when unpinned. 100 +** is certainty. 0 is never. PRNG_SEED is the pseudo-random number generator +** seed. +*/ +static int test_alt_pcache( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int installFlag; + int discardChance; + int prngSeed; + extern void installTestPCache(int,unsigned,unsigned); + if( objc!=4 ){ + Tcl_WrongNumArgs(interp, 1, objv, "INSTALLFLAG DISCARDCHANCE PRNGSEEED"); + return TCL_ERROR; + } + if( Tcl_GetIntFromObj(interp, objv[1], &installFlag) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[2], &discardChance) ) return TCL_ERROR; + if( Tcl_GetIntFromObj(interp, objv[3], &prngSeed) ) return TCL_ERROR; + if( discardChance<0 || discardChance>100 ){ + Tcl_AppendResult(interp, "discard-chance should be between 0 and 100", + (char*)0); + return TCL_ERROR; + } + installTestPCache(installFlag, (unsigned)discardChance, (unsigned)prngSeed); + return TCL_OK; +} + +/* ** Usage: sqlite3_config_memstatus BOOLEAN ** ** Enable or disable memory status reporting using SQLITE_CONFIG_MEMSTATUS. @@ -1312,6 +1348,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_memdebug_log", test_memdebug_log ,0 }, { "sqlite3_config_scratch", test_config_scratch ,0 }, { "sqlite3_config_pagecache", test_config_pagecache ,0 }, + { "sqlite3_config_alt_pcache", test_alt_pcache ,0 }, { "sqlite3_status", test_status ,0 }, { "sqlite3_db_status", test_db_status ,0 }, { "install_malloc_faultsim", test_install_malloc_faultsim ,0 }, @@ -1321,7 +1358,7 @@ int Sqlitetest_malloc_Init(Tcl_Interp *interp){ { "sqlite3_config_error", test_config_error ,0 }, { "sqlite3_db_config_lookaside",test_db_config_lookaside ,0 }, { "sqlite3_dump_memsys3", test_dump_memsys3 ,3 }, - { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 } + { "sqlite3_dump_memsys5", test_dump_memsys3 ,5 }, }; int i; for(i=0; i<sizeof(aObjCmd)/sizeof(aObjCmd[0]); i++){ diff --git a/src/test_pcache.c b/src/test_pcache.c new file mode 100644 index 000000000..58f5b3981 --- /dev/null +++ b/src/test_pcache.c @@ -0,0 +1,438 @@ +/* +** 2008 November 18 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains code used for testing the SQLite system. +** None of the code in this file goes into a deliverable build. +** +** This file contains an application-defined pager cache +** implementation that can be plugged in in place of the +** default pcache. This alternative pager cache will throw +** some errors that the default cache does not. +** +** This pagecache implementation is designed for simplicity +** not speed. +** +** $Id: test_pcache.c,v 1.1 2008/11/19 01:20:26 drh Exp $ +*/ +#include "sqlite3.h" +#include <string.h> +#include <assert.h> + +/* +** Global data used by this test implementation. There is no +** mutexing, which means this page cache will not work in a +** multi-threaded test. +*/ +typedef struct testpcacheGlobalType testpcacheGlobalType; +struct testpcacheGlobalType { + void *pDummy; /* Dummy allocation to simulate failures */ + int nInstance; /* Number of current instances */ + unsigned discardChance; /* Chance of discarding on an unpin */ + unsigned prngSeed; /* Seed for the PRNG */ +}; +static testpcacheGlobalType testpcacheGlobal; + +/* +** Initializer. +** +** Verify that the initializer is only called when the system is +** uninitialized. Allocate some memory and report SQLITE_NOMEM if +** the allocation fails. This provides a means to test the recovery +** from a failed initialization attempt. It also verifies that the +** the destructor always gets call - otherwise there would be a +** memory leak. +*/ +static int testpcacheInit(void *pArg){ + assert( pArg==(void*)&testpcacheGlobal ); + assert( testpcacheGlobal.pDummy==0 ); + assert( testpcacheGlobal.nInstance==0 ); + testpcacheGlobal.pDummy = sqlite3_malloc(10); + return testpcacheGlobal.pDummy==0 ? SQLITE_NOMEM : SQLITE_OK; +} + +/* +** Destructor +** +** Verify that this is only called after initialization. +** Free the memory allocated by the initializer. +*/ +static void testpcacheShutdown(void *pArg){ + assert( pArg==(void*)&testpcacheGlobal ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance==0 ); + sqlite3_free( testpcacheGlobal.pDummy ); + testpcacheGlobal.pDummy = 0; +} + +/* +** Number of pages in a cache +*/ +#define TESTPCACHE_NPAGE 217 +#define TESTPCACHE_RESERVE 17 + +/* +** Magic numbers used to determine validity of the page cache. +*/ +#define TESTPCACHE_VALID 0x364585fd +#define TESTPCACHE_CLEAR 0xd42670d4 + +/* +** Private implementation of a page cache. +*/ +typedef struct testpcache testpcache; +struct testpcache { + int szPage; /* Size of each page. Multiple of 8. */ + int bPurgeable; /* True if the page cache is purgeable */ + int nFree; /* Number of unused slots in a[] */ + int nPinned; /* Number of pinned slots in a[] */ + unsigned iRand; /* State of the PRNG */ + unsigned iMagic; /* Magic number for sanity checking */ + struct testpcachePage { + unsigned key; /* The key for this page. 0 means unallocated */ + int isPinned; /* True if the page is pinned */ + void *pData; /* Data for this page */ + } a[TESTPCACHE_NPAGE]; /* All pages in the cache */ +}; + +/* +** Get a random number using the PRNG in the given page cache. +*/ +static unsigned testpcacheRandom(testpcache *p){ + unsigned x = 0; + int i; + for(i=0; i<4; i++){ + p->iRand = (p->iRand*69069 + 5); + x = (x<<8) | ((p->iRand>>16)&0xff); + } + return x; +} + + +/* +** Allocate a new page cache instance. +*/ +static sqlite3_pcache *testpcacheCreate(int szPage, int bPurgeable){ + int nMem; + char *x; + testpcache *p; + int i; + assert( testpcacheGlobal.pDummy!=0 ); + szPage = (szPage+7)&~7; + nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*szPage; + p = sqlite3_malloc( nMem ); + if( p==0 ) return 0; + x = (char*)&p[1]; + p->szPage = szPage; + p->nFree = TESTPCACHE_NPAGE; + p->nPinned = 0; + p->iRand = testpcacheGlobal.prngSeed; + p->bPurgeable = bPurgeable; + p->iMagic = TESTPCACHE_VALID; + for(i=0; i<TESTPCACHE_NPAGE; i++, x += szPage){ + p->a[i].key = 0; + p->a[i].isPinned = 0; + p->a[i].pData = (void*)x; + } + testpcacheGlobal.nInstance++; + return (sqlite3_pcache*)p; +} + +/* +** Set the cache size +*/ +static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){ + testpcache *p = (testpcache*)pCache; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( newSize>=1 ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); +} + +/* +** Return the number of pages in the cache that are being used. +** This includes both pinned and unpinned pages. +*/ +static int testpcachePagecount(sqlite3_pcache *pCache){ + testpcache *p = (testpcache*)pCache; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + return TESTPCACHE_NPAGE - p->nFree; +} + +/* +** Fetch a page. +*/ +static void *testpcacheFetch( + sqlite3_pcache *pCache, + unsigned key, + int createFlag +){ + testpcache *p = (testpcache*)pCache; + int i, j; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + + /* See if the page is already in cache. Return immediately if it is */ + for(i=0; i<TESTPCACHE_NPAGE; i++){ + if( p->a[i].key==key ){ + if( !p->a[i].isPinned ){ + p->nPinned++; + assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); + p->a[i].isPinned = 1; + } + return p->a[i].pData; + } + } + + /* If createFlag is 0, never allocate a new page */ + if( createFlag==0 ){ + return 0; + } + + /* If no pages are available, always fail */ + if( p->nPinned==TESTPCACHE_NPAGE ){ + return 0; + } + + /* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */ + if( p->nPinned>=TESTPCACHE_NPAGE-TESTPCACHE_RESERVE && createFlag<2 ){ + return 0; + } + + /* Find a free page to allocate if there are any free pages. + ** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2. + */ + if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){ + j = testpcacheRandom(p) % TESTPCACHE_NPAGE; + for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){ + if( p->a[j].key==0 ){ + p->a[j].key = key; + p->a[j].isPinned = 1; + memset(p->a[j].pData, 0, p->szPage); + p->nPinned++; + p->nFree--; + assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); + return p->a[j].pData; + } + } + + /* The prior loop always finds a freepage to allocate */ + assert( 0 ); + } + + /* If this cache is not purgeable then we have to fail. + */ + if( p->bPurgeable==0 ){ + return 0; + } + + /* If there are no free pages, recycle a page. The page to + ** recycle is selected at random from all unpinned pages. + */ + j = testpcacheRandom(p) % TESTPCACHE_NPAGE; + for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){ + if( p->a[j].key>0 && p->a[j].isPinned==0 ){ + p->a[j].key = key; + p->a[j].isPinned = 1; + memset(p->a[j].pData, 0, p->szPage); + p->nPinned++; + assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); + return p->a[j].pData; + } + } + + /* The previous loop always finds a page to recycle. */ + assert(0); + return 0; +} + +/* +** Unpin a page. +*/ +static void testpcacheUnpin( + sqlite3_pcache *pCache, + void *pOldPage, + int discard +){ + testpcache *p = (testpcache*)pCache; + int i; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + + /* Randomly discard pages as they are unpinned according to the + ** discardChance setting. If discardChance is 0, the random discard + ** never happens. If discardChance is 100, it always happens. + */ + if( p->bPurgeable + && (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100) + ){ + discard = 1; + } + + for(i=0; i<TESTPCACHE_NPAGE; i++){ + if( p->a[i].pData==pOldPage ){ + /* The pOldPage pointer always points to a pinned page */ + assert( p->a[i].isPinned ); + p->a[i].isPinned = 0; + p->nPinned--; + assert( p->nPinned>=0 ); + if( discard ){ + p->a[i].key = 0; + p->nFree++; + assert( p->nFree<=TESTPCACHE_NPAGE ); + } + return; + } + } + + /* The pOldPage pointer always points to a valid page */ + assert( 0 ); +} + + +/* +** Rekey a single page. +*/ +static void testpcacheRekey( + sqlite3_pcache *pCache, + void *pOldPage, + unsigned oldKey, + unsigned newKey +){ + testpcache *p = (testpcache*)pCache; + int i; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + + /* If there already exists another page at newKey, verify that + ** the other page is unpinned and discard it. + */ + for(i=0; i<TESTPCACHE_NPAGE; i++){ + if( p->a[i].key==newKey ){ + /* The new key is never a page that is already pinned */ + assert( p->a[i].isPinned==0 ); + p->a[i].key = 0; + p->nFree++; + assert( p->nFree<=TESTPCACHE_NPAGE ); + break; + } + } + + /* Find the page to be rekeyed and rekey it. + */ + for(i=0; i<TESTPCACHE_NPAGE; i++){ + if( p->a[i].key==oldKey ){ + /* The oldKey and pOldPage parameters match */ + assert( p->a[i].pData==pOldPage ); + /* Page to be rekeyed must be pinned */ + assert( p->a[i].isPinned ); + p->a[i].key = newKey; + return; + } + } + + /* Rekey is always given a valid page to work with */ + assert( 0 ); +} + + +/* +** Truncate the page cache. Every page with a key of iLimit or larger +** is discarded. +*/ +static void testpcacheTruncate(sqlite3_pcache *pCache, unsigned iLimit){ + testpcache *p = (testpcache*)pCache; + unsigned int i; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + for(i=0; i<TESTPCACHE_NPAGE; i++){ + if( p->a[i].key>=iLimit ){ + p->a[i].key = 0; + if( p->a[i].isPinned ){ + p->nPinned--; + assert( p->nPinned>=0 ); + } + p->nFree++; + assert( p->nFree<=TESTPCACHE_NPAGE ); + } + } +} + +/* +** Destroy a page cache. +*/ +static void testpcacheDestroy(sqlite3_pcache *pCache){ + testpcache *p = (testpcache*)pCache; + assert( p->iMagic==TESTPCACHE_VALID ); + assert( testpcacheGlobal.pDummy!=0 ); + assert( testpcacheGlobal.nInstance>0 ); + p->iMagic = TESTPCACHE_CLEAR; + sqlite3_free(p); + testpcacheGlobal.nInstance--; +} + + +/* +** Invoke this routine to register or unregister the testing pager cache +** implemented by this file. +** +** Install the test pager cache if installFlag is 1 and uninstall it if +** installFlag is 0. +** +** When installing, discardChance is a number between 0 and 100 that +** indicates the probability of discarding a page when unpinning the +** page. 0 means never discard (unless the discard flag is set). +** 100 means always discard. +*/ +void installTestPCache( + int installFlag, /* True to install. False to uninstall. */ + unsigned discardChance, /* 0-100. Chance to discard on unpin */ + unsigned prngSeed /* Seed for the PRNG */ +){ + static const sqlite3_pcache_methods testPcache = { + (void*)&testpcacheGlobal, + testpcacheInit, + testpcacheShutdown, + testpcacheCreate, + testpcacheCachesize, + testpcachePagecount, + testpcacheFetch, + testpcacheUnpin, + testpcacheRekey, + testpcacheTruncate, + testpcacheDestroy, + }; + static sqlite3_pcache_methods defaultPcache; + static int isInstalled = 0; + + assert( testpcacheGlobal.nInstance==0 ); + assert( testpcacheGlobal.pDummy==0 ); + assert( discardChance<=100 ); + testpcacheGlobal.discardChance = discardChance; + testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16); + if( installFlag!=isInstalled ){ + if( installFlag ){ + sqlite3_config(SQLITE_CONFIG_GETPCACHE, &defaultPcache); + assert( defaultPcache.xCreate!=testpcacheCreate ); + sqlite3_config(SQLITE_CONFIG_PCACHE, &testPcache); + }else{ + assert( defaultPcache.xCreate!=0 ); + sqlite3_config(SQLITE_CONFIG_PCACHE, &defaultPcache); + } + isInstalled = installFlag; + } +} diff --git a/test/permutations.test b/test/permutations.test index bec84e993..c4cfaa9ea 100644 --- a/test/permutations.test +++ b/test/permutations.test @@ -9,7 +9,7 @@ # #*********************************************************************** # -# $Id: permutations.test,v 1.38 2008/11/13 16:21:50 danielk1977 Exp $ +# $Id: permutations.test,v 1.39 2008/11/19 01:20:26 drh Exp $ set testdir [file dirname $argv0] source $testdir/tester.tcl @@ -604,6 +604,106 @@ run_tests "safe_append" -description { } -include [lsort [concat shared_err.test $ALLTESTS]] \ -exclude async3.test +# The set of tests to run on the alternative-pcache +set perm-alt-pcache-testset { + async.test + attach.test + delete.test delete2.test + index.test + insert.test insert2.test + join.test join2.test + rollback.test + select1.test select2.test + trans.test + update.test +} + +run_tests "pcache0" -description { + Alternative pcache implementation without random discard +} -initialize { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 1 0 1 + sqlite3_initialize + autoinstall_test_functions +} -shutdown { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 0 0 0 + sqlite3_config_lookaside 100 500 + install_malloc_faultsim 1 + sqlite3_initialize +} -include ${perm-alt-pcache-testset} + +run_tests "pcache10" -description { + Alternative pcache implementation without 10% random discard +} -initialize { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 1 50 1 + sqlite3_initialize + autoinstall_test_functions +} -shutdown { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 0 0 0 + sqlite3_initialize +} -include ${perm-alt-pcache-testset} + +run_tests "pcache50" -description { + Alternative pcache implementation without 50% random discard +} -initialize { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 1 50 1 + sqlite3_initialize + autoinstall_test_functions +} -shutdown { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 0 0 0 + sqlite3_initialize +} -include ${perm-alt-pcache-testset} + +run_tests "pcache90" -description { + Alternative pcache implementation without 90% random discard +} -initialize { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 1 50 1 + sqlite3_initialize + autoinstall_test_functions +} -shutdown { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 0 0 0 + sqlite3_initialize +} -include ${perm-alt-pcache-testset} + +run_tests "pcache100" -description { + Alternative pcache implementation that always discards when unpinning +} -initialize { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 1 100 1 + sqlite3_initialize + autoinstall_test_functions +} -shutdown { + catch {db close} + sqlite3_reset_auto_extension + sqlite3_shutdown + sqlite3_config_alt_pcache 0 0 0 + sqlite3_initialize +} -include ${perm-alt-pcache-testset} # End of tests ############################################################################# |