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 #include "sqlite3.h" 25 #include <string.h> 26 #include <assert.h> 27 28 /* 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. 32 */ 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 agressively */ 40 }; 41 static testpcacheGlobalType testpcacheGlobal; 42 43 /* 44 ** Initializer. 45 ** 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 51 ** memory leak. 52 */ 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; 59 } 60 61 /* 62 ** Destructor 63 ** 64 ** Verify that this is only called after initialization. 65 ** Free the memory allocated by the initializer. 66 */ 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; 73 } 74 75 /* 76 ** Number of pages in a cache. 77 ** 78 ** The number of pages is a hard upper bound in this test module. 79 ** If more pages are requested, sqlite3PcacheFetch() returns NULL. 80 ** 81 ** If testing with in-memory temp tables, provide a larger pcache. 82 ** Some of the test cases need this. 83 */ 84 #if defined(SQLITE_TEMP_STORE) && SQLITE_TEMP_STORE>=2 85 # define TESTPCACHE_NPAGE 499 86 #else 87 # define TESTPCACHE_NPAGE 217 88 #endif 89 #define TESTPCACHE_RESERVE 17 90 91 /* 92 ** Magic numbers used to determine validity of the page cache. 93 */ 94 #define TESTPCACHE_VALID 0x364585fd 95 #define TESTPCACHE_CLEAR 0xd42670d4 96 97 /* 98 ** Private implementation of a page cache. 99 */ 100 typedef struct testpcache testpcache; 101 struct testpcache { 102 int 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 */ 114 }; 115 116 /* 117 ** Get a random number using the PRNG in the given page cache. 118 */ 119 static unsigned testpcacheRandom(testpcache *p){ 120 unsigned x = 0; 121 int i; 122 for(i=0; i<4; i++){ 123 p->iRand = (p->iRand*69069 + 5); 124 x = (x<<8) | ((p->iRand>>16)&0xff); 125 } 126 return x; 127 } 128 129 130 /* 131 ** Allocate a new page cache instance. 132 */ 133 static sqlite3_pcache *testpcacheCreate( 134 int szPage, 135 int szExtra, 136 int bPurgeable 137 ){ 138 int nMem; 139 char *x; 140 testpcache *p; 141 int i; 142 assert( testpcacheGlobal.pDummy!=0 ); 143 szPage = (szPage+7)&~7; 144 nMem = sizeof(testpcache) + TESTPCACHE_NPAGE*(szPage+szExtra); 145 p = sqlite3_malloc( nMem ); 146 if( p==0 ) return 0; 147 x = (char*)&p[1]; 148 p->szPage = szPage; 149 p->szExtra = szExtra; 150 p->nFree = TESTPCACHE_NPAGE; 151 p->nPinned = 0; 152 p->iRand = testpcacheGlobal.prngSeed; 153 p->bPurgeable = bPurgeable; 154 p->iMagic = TESTPCACHE_VALID; 155 for(i=0; i<TESTPCACHE_NPAGE; i++, x += (szPage+szExtra)){ 156 p->a[i].key = 0; 157 p->a[i].isPinned = 0; 158 p->a[i].page.pBuf = (void*)x; 159 p->a[i].page.pExtra = (void*)&x[szPage]; 160 } 161 testpcacheGlobal.nInstance++; 162 return (sqlite3_pcache*)p; 163 } 164 165 /* 166 ** Set the cache size 167 */ 168 static void testpcacheCachesize(sqlite3_pcache *pCache, int newSize){ 169 testpcache *p = (testpcache*)pCache; 170 assert( p->iMagic==TESTPCACHE_VALID ); 171 assert( testpcacheGlobal.pDummy!=0 ); 172 assert( testpcacheGlobal.nInstance>0 ); 173 } 174 175 /* 176 ** Return the number of pages in the cache that are being used. 177 ** This includes both pinned and unpinned pages. 178 */ 179 static int testpcachePagecount(sqlite3_pcache *pCache){ 180 testpcache *p = (testpcache*)pCache; 181 assert( p->iMagic==TESTPCACHE_VALID ); 182 assert( testpcacheGlobal.pDummy!=0 ); 183 assert( testpcacheGlobal.nInstance>0 ); 184 return TESTPCACHE_NPAGE - p->nFree; 185 } 186 187 /* 188 ** Fetch a page. 189 */ 190 static sqlite3_pcache_page *testpcacheFetch( 191 sqlite3_pcache *pCache, 192 unsigned key, 193 int createFlag 194 ){ 195 testpcache *p = (testpcache*)pCache; 196 int i, j; 197 assert( p->iMagic==TESTPCACHE_VALID ); 198 assert( testpcacheGlobal.pDummy!=0 ); 199 assert( testpcacheGlobal.nInstance>0 ); 200 201 /* See if the page is already in cache. Return immediately if it is */ 202 for(i=0; i<TESTPCACHE_NPAGE; i++){ 203 if( p->a[i].key==key ){ 204 if( !p->a[i].isPinned ){ 205 p->nPinned++; 206 assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); 207 p->a[i].isPinned = 1; 208 } 209 return &p->a[i].page; 210 } 211 } 212 213 /* If createFlag is 0, never allocate a new page */ 214 if( createFlag==0 ){ 215 return 0; 216 } 217 218 /* If no pages are available, always fail */ 219 if( p->nPinned==TESTPCACHE_NPAGE ){ 220 return 0; 221 } 222 223 /* Do not allocate the last TESTPCACHE_RESERVE pages unless createFlag is 2 */ 224 if( p->nPinned>=TESTPCACHE_NPAGE-TESTPCACHE_RESERVE && createFlag<2 ){ 225 return 0; 226 } 227 228 /* Do not allocate if highStress is enabled and createFlag is not 2. 229 ** 230 ** The highStress setting causes pagerStress() to be called much more 231 ** often, which exercises the pager logic more intensely. 232 */ 233 if( testpcacheGlobal.highStress && createFlag<2 ){ 234 return 0; 235 } 236 237 /* Find a free page to allocate if there are any free pages. 238 ** Withhold TESTPCACHE_RESERVE free pages until createFlag is 2. 239 */ 240 if( p->nFree>TESTPCACHE_RESERVE || (createFlag==2 && p->nFree>0) ){ 241 j = testpcacheRandom(p) % TESTPCACHE_NPAGE; 242 for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){ 243 if( p->a[j].key==0 ){ 244 p->a[j].key = key; 245 p->a[j].isPinned = 1; 246 memset(p->a[j].page.pBuf, 0, p->szPage); 247 memset(p->a[j].page.pExtra, 0, p->szExtra); 248 p->nPinned++; 249 p->nFree--; 250 assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); 251 return &p->a[j].page; 252 } 253 } 254 255 /* The prior loop always finds a freepage to allocate */ 256 assert( 0 ); 257 } 258 259 /* If this cache is not purgeable then we have to fail. 260 */ 261 if( p->bPurgeable==0 ){ 262 return 0; 263 } 264 265 /* If there are no free pages, recycle a page. The page to 266 ** recycle is selected at random from all unpinned pages. 267 */ 268 j = testpcacheRandom(p) % TESTPCACHE_NPAGE; 269 for(i=0; i<TESTPCACHE_NPAGE; i++, j = (j+1)%TESTPCACHE_NPAGE){ 270 if( p->a[j].key>0 && p->a[j].isPinned==0 ){ 271 p->a[j].key = key; 272 p->a[j].isPinned = 1; 273 memset(p->a[j].page.pBuf, 0, p->szPage); 274 memset(p->a[j].page.pExtra, 0, p->szExtra); 275 p->nPinned++; 276 assert( p->nPinned <= TESTPCACHE_NPAGE - p->nFree ); 277 return &p->a[j].page; 278 } 279 } 280 281 /* The previous loop always finds a page to recycle. */ 282 assert(0); 283 return 0; 284 } 285 286 /* 287 ** Unpin a page. 288 */ 289 static void testpcacheUnpin( 290 sqlite3_pcache *pCache, 291 sqlite3_pcache_page *pOldPage, 292 int discard 293 ){ 294 testpcache *p = (testpcache*)pCache; 295 int i; 296 assert( p->iMagic==TESTPCACHE_VALID ); 297 assert( testpcacheGlobal.pDummy!=0 ); 298 assert( testpcacheGlobal.nInstance>0 ); 299 300 /* Randomly discard pages as they are unpinned according to the 301 ** discardChance setting. If discardChance is 0, the random discard 302 ** never happens. If discardChance is 100, it always happens. 303 */ 304 if( p->bPurgeable 305 && (100-testpcacheGlobal.discardChance) <= (testpcacheRandom(p)%100) 306 ){ 307 discard = 1; 308 } 309 310 for(i=0; i<TESTPCACHE_NPAGE; i++){ 311 if( &p->a[i].page==pOldPage ){ 312 /* The pOldPage pointer always points to a pinned page */ 313 assert( p->a[i].isPinned ); 314 p->a[i].isPinned = 0; 315 p->nPinned--; 316 assert( p->nPinned>=0 ); 317 if( discard ){ 318 p->a[i].key = 0; 319 p->nFree++; 320 assert( p->nFree<=TESTPCACHE_NPAGE ); 321 } 322 return; 323 } 324 } 325 326 /* The pOldPage pointer always points to a valid page */ 327 assert( 0 ); 328 } 329 330 331 /* 332 ** Rekey a single page. 333 */ 334 static void testpcacheRekey( 335 sqlite3_pcache *pCache, 336 sqlite3_pcache_page *pOldPage, 337 unsigned oldKey, 338 unsigned newKey 339 ){ 340 testpcache *p = (testpcache*)pCache; 341 int i; 342 assert( p->iMagic==TESTPCACHE_VALID ); 343 assert( testpcacheGlobal.pDummy!=0 ); 344 assert( testpcacheGlobal.nInstance>0 ); 345 346 /* If there already exists another page at newKey, verify that 347 ** the other page is unpinned and discard it. 348 */ 349 for(i=0; i<TESTPCACHE_NPAGE; i++){ 350 if( p->a[i].key==newKey ){ 351 /* The new key is never a page that is already pinned */ 352 assert( p->a[i].isPinned==0 ); 353 p->a[i].key = 0; 354 p->nFree++; 355 assert( p->nFree<=TESTPCACHE_NPAGE ); 356 break; 357 } 358 } 359 360 /* Find the page to be rekeyed and rekey it. 361 */ 362 for(i=0; i<TESTPCACHE_NPAGE; i++){ 363 if( p->a[i].key==oldKey ){ 364 /* The oldKey and pOldPage parameters match */ 365 assert( &p->a[i].page==pOldPage ); 366 /* Page to be rekeyed must be pinned */ 367 assert( p->a[i].isPinned ); 368 p->a[i].key = newKey; 369 return; 370 } 371 } 372 373 /* Rekey is always given a valid page to work with */ 374 assert( 0 ); 375 } 376 377 378 /* 379 ** Truncate the page cache. Every page with a key of iLimit or larger 380 ** is discarded. 381 */ 382 static void testpcacheTruncate(sqlite3_pcache *pCache, unsigned iLimit){ 383 testpcache *p = (testpcache*)pCache; 384 unsigned int i; 385 assert( p->iMagic==TESTPCACHE_VALID ); 386 assert( testpcacheGlobal.pDummy!=0 ); 387 assert( testpcacheGlobal.nInstance>0 ); 388 for(i=0; i<TESTPCACHE_NPAGE; i++){ 389 if( p->a[i].key>=iLimit ){ 390 p->a[i].key = 0; 391 if( p->a[i].isPinned ){ 392 p->nPinned--; 393 assert( p->nPinned>=0 ); 394 } 395 p->nFree++; 396 assert( p->nFree<=TESTPCACHE_NPAGE ); 397 } 398 } 399 } 400 401 /* 402 ** Destroy a page cache. 403 */ 404 static void testpcacheDestroy(sqlite3_pcache *pCache){ 405 testpcache *p = (testpcache*)pCache; 406 assert( p->iMagic==TESTPCACHE_VALID ); 407 assert( testpcacheGlobal.pDummy!=0 ); 408 assert( testpcacheGlobal.nInstance>0 ); 409 p->iMagic = TESTPCACHE_CLEAR; 410 sqlite3_free(p); 411 testpcacheGlobal.nInstance--; 412 } 413 414 415 /* 416 ** Invoke this routine to register or unregister the testing pager cache 417 ** implemented by this file. 418 ** 419 ** Install the test pager cache if installFlag is 1 and uninstall it if 420 ** installFlag is 0. 421 ** 422 ** When installing, discardChance is a number between 0 and 100 that 423 ** indicates the probability of discarding a page when unpinning the 424 ** page. 0 means never discard (unless the discard flag is set). 425 ** 100 means always discard. 426 */ 427 void installTestPCache( 428 int installFlag, /* True to install. False to uninstall. */ 429 unsigned discardChance, /* 0-100. Chance to discard on unpin */ 430 unsigned prngSeed, /* Seed for the PRNG */ 431 unsigned highStress /* Call xStress agressively */ 432 ){ 433 static const sqlite3_pcache_methods2 testPcache = { 434 1, 435 (void*)&testpcacheGlobal, 436 testpcacheInit, 437 testpcacheShutdown, 438 testpcacheCreate, 439 testpcacheCachesize, 440 testpcachePagecount, 441 testpcacheFetch, 442 testpcacheUnpin, 443 testpcacheRekey, 444 testpcacheTruncate, 445 testpcacheDestroy, 446 }; 447 static sqlite3_pcache_methods2 defaultPcache; 448 static int isInstalled = 0; 449 450 assert( testpcacheGlobal.nInstance==0 ); 451 assert( testpcacheGlobal.pDummy==0 ); 452 assert( discardChance<=100 ); 453 testpcacheGlobal.discardChance = discardChance; 454 testpcacheGlobal.prngSeed = prngSeed ^ (prngSeed<<16); 455 testpcacheGlobal.highStress = highStress; 456 if( installFlag!=isInstalled ){ 457 if( installFlag ){ 458 sqlite3_config(SQLITE_CONFIG_GETPCACHE2, &defaultPcache); 459 assert( defaultPcache.xCreate!=testpcacheCreate ); 460 sqlite3_config(SQLITE_CONFIG_PCACHE2, &testPcache); 461 }else{ 462 assert( defaultPcache.xCreate!=0 ); 463 sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultPcache); 464 } 465 isInstalled = installFlag; 466 } 467 } 468