4 ** The author disclaims copyright to this source code. In place of
5 ** a legal notice, here is a blessing:
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.
11 *************************************************************************
13 ** This file contains code used for testing the SQLite system.
14 ** None of the code in this file goes into a deliverable build.
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.
21 ** This pagecache implementation is designed for simplicity
29 ** Global data used by this test implementation. There is no
30 ** mutexing, which means this page cache will not work in a
31 ** multi-threaded test.
33 typedef struct testpcacheGlobalType testpcacheGlobalType
;
34 struct testpcacheGlobalType
{
35 void *pDummy
; /* Dummy allocation to simulate failures */
36 int nInstance
; /* Number of current instances */
37 unsigned discardChance
; /* Chance of discarding on an unpin (0-100) */
38 unsigned prngSeed
; /* Seed for the PRNG */
39 unsigned highStress
; /* Call xStress aggressively */
41 static testpcacheGlobalType testpcacheGlobal
;
46 ** Verify that the initializer is only called when the system is
47 ** uninitialized. Allocate some memory and report SQLITE_NOMEM if
48 ** the allocation fails. This provides a means to test the recovery
49 ** from a failed initialization attempt. It also verifies that the
50 ** the destructor always gets call - otherwise there would be a
53 static int testpcacheInit(void *pArg
){
54 assert( pArg
==(void*)&testpcacheGlobal
);
55 assert( testpcacheGlobal
.pDummy
==0 );
56 assert( testpcacheGlobal
.nInstance
==0 );
57 testpcacheGlobal
.pDummy
= sqlite3_malloc(10);
58 return testpcacheGlobal
.pDummy
==0 ? SQLITE_NOMEM
: SQLITE_OK
;
64 ** Verify that this is only called after initialization.
65 ** Free the memory allocated by the initializer.
67 static void testpcacheShutdown(void *pArg
){
68 assert( pArg
==(void*)&testpcacheGlobal
);
69 assert( testpcacheGlobal
.pDummy
!=0 );
70 assert( testpcacheGlobal
.nInstance
==0 );
71 sqlite3_free( testpcacheGlobal
.pDummy
);
72 testpcacheGlobal
.pDummy
= 0;
76 ** Number of pages in a cache.
78 ** The number of pages is a hard upper bound in this test module.
79 ** If more pages are requested, sqlite3PcacheFetch() returns NULL.
81 ** If testing with in-memory temp tables, provide a larger pcache.
82 ** Some of the test cases need this.
84 #if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2
85 # define TESTPCACHE_NPAGE 499
87 # define TESTPCACHE_NPAGE 217
89 #define TESTPCACHE_RESERVE 17
92 ** Magic numbers used to determine validity of the page cache.
94 #define TESTPCACHE_VALID 0x364585fd
95 #define TESTPCACHE_CLEAR 0xd42670d4
98 ** Private implementation of a page cache.
100 typedef struct testpcache testpcache
;
102 sqlite3_int64 szPage
; /* Size of each page. Multiple of 8. */
103 int szExtra
; /* Size of extra data that accompanies each page */
104 int bPurgeable
; /* True if the page cache is purgeable */
105 int nFree
; /* Number of unused slots in a[] */
106 int nPinned
; /* Number of pinned slots in a[] */
107 unsigned iRand
; /* State of the PRNG */
108 unsigned iMagic
; /* Magic number for sanity checking */
109 struct testpcachePage
{
110 sqlite3_pcache_page page
; /* Base class */
111 unsigned key
; /* The key for this page. 0 means unallocated */
112 int isPinned
; /* True if the page is pinned */
113 } a
[TESTPCACHE_NPAGE
]; /* All pages in the cache */
117 ** Get a random number using the PRNG in the given page cache.
119 static unsigned testpcacheRandom(testpcache
*p
){
123 p
->iRand
= (p
->iRand
*69069 + 5);
124 x
= (x
<<8) | ((p
->iRand
>>16)&0xff);
131 ** Allocate a new page cache instance.
133 static sqlite3_pcache
*testpcacheCreate(
142 assert( testpcacheGlobal
.pDummy
!=0 );
143 szPage
= (szPage
+7)&~7;
144 szExtra
= (szPage
+7)&~7;
145 nMem
= sizeof(testpcache
) + TESTPCACHE_NPAGE
*(szPage
+szExtra
);
146 p
= sqlite3_malloc( nMem
);
150 p
->szExtra
= szExtra
;
151 p
->nFree
= TESTPCACHE_NPAGE
;
153 p
->iRand
= testpcacheGlobal
.prngSeed
;
154 p
->bPurgeable
= bPurgeable
;
155 p
->iMagic
= TESTPCACHE_VALID
;
156 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++, x
+= (szPage
+szExtra
)){
158 p
->a
[i
].isPinned
= 0;
159 p
->a
[i
].page
.pBuf
= (void*)x
;
160 p
->a
[i
].page
.pExtra
= (void*)&x
[szPage
];
162 testpcacheGlobal
.nInstance
++;
163 return (sqlite3_pcache
*)p
;
167 ** Set the cache size
169 static void testpcacheCachesize(sqlite3_pcache
*pCache
, int newSize
){
170 testpcache
*p
= (testpcache
*)pCache
;
171 assert( p
->iMagic
==TESTPCACHE_VALID
);
172 assert( testpcacheGlobal
.pDummy
!=0 );
173 assert( testpcacheGlobal
.nInstance
>0 );
177 ** Return the number of pages in the cache that are being used.
178 ** This includes both pinned and unpinned pages.
180 static int testpcachePagecount(sqlite3_pcache
*pCache
){
181 testpcache
*p
= (testpcache
*)pCache
;
182 assert( p
->iMagic
==TESTPCACHE_VALID
);
183 assert( testpcacheGlobal
.pDummy
!=0 );
184 assert( testpcacheGlobal
.nInstance
>0 );
185 return TESTPCACHE_NPAGE
- p
->nFree
;
191 static sqlite3_pcache_page
*testpcacheFetch(
192 sqlite3_pcache
*pCache
,
196 testpcache
*p
= (testpcache
*)pCache
;
198 assert( p
->iMagic
==TESTPCACHE_VALID
);
199 assert( testpcacheGlobal
.pDummy
!=0 );
200 assert( testpcacheGlobal
.nInstance
>0 );
202 /* See if the page is already in cache. Return immediately if it is */
203 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++){
204 if( p
->a
[i
].key
==key
){
205 if( !p
->a
[i
].isPinned
){
207 assert( p
->nPinned
<= TESTPCACHE_NPAGE
- p
->nFree
);
208 p
->a
[i
].isPinned
= 1;
210 return &p
->a
[i
].page
;
214 /* If createFlag is 0, never allocate a new page */
219 /* If no pages are available, always fail */
220 if( p
->nPinned
==TESTPCACHE_NPAGE
){
224 /* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */
225 if( p
->nPinned
>=TESTPCACHE_NPAGE
-TESTPCACHE_RESERVE
&& createFlag
<2 ){
229 /* Do not allocate if highStress is enabled and createFlag is not 2.
231 ** The highStress setting causes pagerStress() to be called much more
232 ** often, which exercises the pager logic more intensely.
234 if( testpcacheGlobal
.highStress
&& createFlag
<2 ){
238 /* Find a free page to allocate if there are any free pages.
239 ** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2.
241 if( p
->nFree
>TESTPCACHE_RESERVE
|| (createFlag
==2 && p
->nFree
>0) ){
242 j
= testpcacheRandom(p
) % TESTPCACHE_NPAGE
;
243 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++, j
= (j
+1)%TESTPCACHE_NPAGE
){
244 if( p
->a
[j
].key
==0 ){
246 p
->a
[j
].isPinned
= 1;
247 memset(p
->a
[j
].page
.pBuf
, 0, p
->szPage
);
248 memset(p
->a
[j
].page
.pExtra
, 0, p
->szExtra
);
251 assert( p
->nPinned
<= TESTPCACHE_NPAGE
- p
->nFree
);
252 return &p
->a
[j
].page
;
256 /* The prior loop always finds a freepage to allocate */
260 /* If this cache is not purgeable then we have to fail.
262 if( p
->bPurgeable
==0 ){
266 /* If there are no free pages, recycle a page. The page to
267 ** recycle is selected at random from all unpinned pages.
269 j
= testpcacheRandom(p
) % TESTPCACHE_NPAGE
;
270 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++, j
= (j
+1)%TESTPCACHE_NPAGE
){
271 if( p
->a
[j
].key
>0 && p
->a
[j
].isPinned
==0 ){
273 p
->a
[j
].isPinned
= 1;
274 memset(p
->a
[j
].page
.pBuf
, 0, p
->szPage
);
275 memset(p
->a
[j
].page
.pExtra
, 0, p
->szExtra
);
277 assert( p
->nPinned
<= TESTPCACHE_NPAGE
- p
->nFree
);
278 return &p
->a
[j
].page
;
282 /* The previous loop always finds a page to recycle. */
290 static void testpcacheUnpin(
291 sqlite3_pcache
*pCache
,
292 sqlite3_pcache_page
*pOldPage
,
295 testpcache
*p
= (testpcache
*)pCache
;
297 assert( p
->iMagic
==TESTPCACHE_VALID
);
298 assert( testpcacheGlobal
.pDummy
!=0 );
299 assert( testpcacheGlobal
.nInstance
>0 );
301 /* Randomly discard pages as they are unpinned according to the
302 ** discardChance setting. If discardChance is 0, the random discard
303 ** never happens. If discardChance is 100, it always happens.
306 && (100-testpcacheGlobal
.discardChance
) <= (testpcacheRandom(p
)%100)
311 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++){
312 if( &p
->a
[i
].page
==pOldPage
){
313 /* The pOldPage pointer always points to a pinned page */
314 assert( p
->a
[i
].isPinned
);
315 p
->a
[i
].isPinned
= 0;
317 assert( p
->nPinned
>=0 );
321 assert( p
->nFree
<=TESTPCACHE_NPAGE
);
327 /* The pOldPage pointer always points to a valid page */
333 ** Rekey a single page.
335 static void testpcacheRekey(
336 sqlite3_pcache
*pCache
,
337 sqlite3_pcache_page
*pOldPage
,
341 testpcache
*p
= (testpcache
*)pCache
;
343 assert( p
->iMagic
==TESTPCACHE_VALID
);
344 assert( testpcacheGlobal
.pDummy
!=0 );
345 assert( testpcacheGlobal
.nInstance
>0 );
347 /* If there already exists another page at newKey, verify that
348 ** the other page is unpinned and discard it.
350 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++){
351 if( p
->a
[i
].key
==newKey
){
352 /* The new key is never a page that is already pinned */
353 assert( p
->a
[i
].isPinned
==0 );
356 assert( p
->nFree
<=TESTPCACHE_NPAGE
);
361 /* Find the page to be rekeyed and rekey it.
363 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++){
364 if( p
->a
[i
].key
==oldKey
){
365 /* The oldKey and pOldPage parameters match */
366 assert( &p
->a
[i
].page
==pOldPage
);
367 /* Page to be rekeyed must be pinned */
368 assert( p
->a
[i
].isPinned
);
369 p
->a
[i
].key
= newKey
;
374 /* Rekey is always given a valid page to work with */
380 ** Truncate the page cache. Every page with a key of iLimit or larger
383 static void testpcacheTruncate(sqlite3_pcache
*pCache
, unsigned iLimit
){
384 testpcache
*p
= (testpcache
*)pCache
;
386 assert( p
->iMagic
==TESTPCACHE_VALID
);
387 assert( testpcacheGlobal
.pDummy
!=0 );
388 assert( testpcacheGlobal
.nInstance
>0 );
389 for(i
=0; i
<TESTPCACHE_NPAGE
; i
++){
390 if( p
->a
[i
].key
>=iLimit
){
392 if( p
->a
[i
].isPinned
){
394 assert( p
->nPinned
>=0 );
397 assert( p
->nFree
<=TESTPCACHE_NPAGE
);
403 ** Destroy a page cache.
405 static void testpcacheDestroy(sqlite3_pcache
*pCache
){
406 testpcache
*p
= (testpcache
*)pCache
;
407 assert( p
->iMagic
==TESTPCACHE_VALID
);
408 assert( testpcacheGlobal
.pDummy
!=0 );
409 assert( testpcacheGlobal
.nInstance
>0 );
410 p
->iMagic
= TESTPCACHE_CLEAR
;
412 testpcacheGlobal
.nInstance
--;
417 ** Invoke this routine to register or unregister the testing pager cache
418 ** implemented by this file.
420 ** Install the test pager cache if installFlag is 1 and uninstall it if
423 ** When installing, discardChance is a number between 0 and 100 that
424 ** indicates the probability of discarding a page when unpinning the
425 ** page. 0 means never discard (unless the discard flag is set).
426 ** 100 means always discard.
428 void installTestPCache(
429 int installFlag
, /* True to install. False to uninstall. */
430 unsigned discardChance
, /* 0-100. Chance to discard on unpin */
431 unsigned prngSeed
, /* Seed for the PRNG */
432 unsigned highStress
/* Call xStress aggressively */
434 static const sqlite3_pcache_methods2 testPcache
= {
436 (void*)&testpcacheGlobal
,
448 static sqlite3_pcache_methods2 defaultPcache
;
449 static int isInstalled
= 0;
451 assert( testpcacheGlobal
.nInstance
==0 );
452 assert( testpcacheGlobal
.pDummy
==0 );
453 assert( discardChance
<=100 );
454 testpcacheGlobal
.discardChance
= discardChance
;
455 testpcacheGlobal
.prngSeed
= prngSeed
^ (prngSeed
<<16);
456 testpcacheGlobal
.highStress
= highStress
;
457 if( installFlag
!=isInstalled
){
459 sqlite3_config(SQLITE_CONFIG_GETPCACHE2
, &defaultPcache
);
460 assert( defaultPcache
.xCreate
!=testpcacheCreate
);
461 sqlite3_config(SQLITE_CONFIG_PCACHE2
, &testPcache
);
463 assert( defaultPcache
.xCreate
!=0 );
464 sqlite3_config(SQLITE_CONFIG_PCACHE2
, &defaultPcache
);
466 isInstalled
= installFlag
;