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