Fix the ".lint fkey-indexes" shell command so that it works with WITHOUT ROWID
[sqlite.git] / ext / lsm1 / lsm-test / lsmtest6.c
bloba61b738b8faf354a2ec688e141507585484b9753
2 #include "lsmtest.h"
4 typedef struct OomTest OomTest;
5 struct OomTest {
6 lsm_env *pEnv;
7 int iNext; /* Next value to pass to testMallocOom() */
8 int nFail; /* Number of OOM events injected */
9 int bEnable;
10 int rc; /* Test case error code */
13 static void testOomStart(OomTest *p){
14 memset(p, 0, sizeof(OomTest));
15 p->iNext = 1;
16 p->bEnable = 1;
17 p->nFail = 1;
18 p->pEnv = tdb_lsm_env();
21 static void xOomHook(OomTest *p){
22 p->nFail++;
25 static int testOomContinue(OomTest *p){
26 if( p->rc!=0 || (p->iNext>1 && p->nFail==0) ){
27 return 0;
29 p->nFail = 0;
30 testMallocOom(p->pEnv, p->iNext, 0, (void (*)(void*))xOomHook, (void *)p);
31 return 1;
34 static void testOomEnable(OomTest *p, int bEnable){
35 p->bEnable = bEnable;
36 testMallocOomEnable(p->pEnv, bEnable);
39 static void testOomNext(OomTest *p){
40 p->iNext++;
43 static int testOomHit(OomTest *p){
44 return (p->nFail>0);
47 static int testOomFinish(OomTest *p){
48 return p->rc;
51 static void testOomAssert(OomTest *p, int bVal){
52 if( bVal==0 ){
53 test_failed();
54 p->rc = 1;
59 ** Test that the error code matches the state of the OomTest object passed
60 ** as the first argument. Specifically, check that rc is LSM_NOMEM if an
61 ** OOM error has already been injected, or LSM_OK if not.
63 static void testOomAssertRc(OomTest *p, int rc){
64 testOomAssert(p, rc==LSM_OK || rc==LSM_NOMEM);
65 testOomAssert(p, testOomHit(p)==(rc==LSM_NOMEM) || p->bEnable==0 );
68 static void testOomOpen(
69 OomTest *pOom,
70 const char *zName,
71 lsm_db **ppDb,
72 int *pRc
74 if( *pRc==LSM_OK ){
75 int rc;
76 rc = lsm_new(tdb_lsm_env(), ppDb);
77 if( rc==LSM_OK ) rc = lsm_open(*ppDb, zName);
78 testOomAssertRc(pOom, rc);
79 *pRc = rc;
83 static void testOomFetch(
84 OomTest *pOom,
85 lsm_db *pDb,
86 void *pKey, int nKey,
87 void *pVal, int nVal,
88 int *pRc
90 testOomAssertRc(pOom, *pRc);
91 if( *pRc==LSM_OK ){
92 lsm_cursor *pCsr;
93 int rc;
95 rc = lsm_csr_open(pDb, &pCsr);
96 if( rc==LSM_OK ) rc = lsm_csr_seek(pCsr, pKey, nKey, 0);
97 testOomAssertRc(pOom, rc);
99 if( rc==LSM_OK ){
100 const void *p; int n;
101 testOomAssert(pOom, lsm_csr_valid(pCsr));
103 rc = lsm_csr_key(pCsr, &p, &n);
104 testOomAssertRc(pOom, rc);
105 testOomAssert(pOom, rc!=LSM_OK || (n==nKey && memcmp(pKey, p, nKey)==0) );
108 if( rc==LSM_OK ){
109 const void *p; int n;
110 testOomAssert(pOom, lsm_csr_valid(pCsr));
112 rc = lsm_csr_value(pCsr, &p, &n);
113 testOomAssertRc(pOom, rc);
114 testOomAssert(pOom, rc!=LSM_OK || (n==nVal && memcmp(pVal, p, nVal)==0) );
117 lsm_csr_close(pCsr);
118 *pRc = rc;
122 static void testOomWrite(
123 OomTest *pOom,
124 lsm_db *pDb,
125 void *pKey, int nKey,
126 void *pVal, int nVal,
127 int *pRc
129 testOomAssertRc(pOom, *pRc);
130 if( *pRc==LSM_OK ){
131 int rc;
133 rc = lsm_insert(pDb, pKey, nKey, pVal, nVal);
134 testOomAssertRc(pOom, rc);
136 *pRc = rc;
141 static void testOomFetchStr(
142 OomTest *pOom,
143 lsm_db *pDb,
144 const char *zKey,
145 const char *zVal,
146 int *pRc
148 int nKey = strlen(zKey);
149 int nVal = strlen(zVal);
150 testOomFetch(pOom, pDb, (void *)zKey, nKey, (void *)zVal, nVal, pRc);
153 static void testOomFetchData(
154 OomTest *pOom,
155 lsm_db *pDb,
156 Datasource *pData,
157 int iKey,
158 int *pRc
160 void *pKey; int nKey;
161 void *pVal; int nVal;
162 testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal);
163 testOomFetch(pOom, pDb, pKey, nKey, pVal, nVal, pRc);
166 static void testOomWriteStr(
167 OomTest *pOom,
168 lsm_db *pDb,
169 const char *zKey,
170 const char *zVal,
171 int *pRc
173 int nKey = strlen(zKey);
174 int nVal = strlen(zVal);
175 testOomWrite(pOom, pDb, (void *)zKey, nKey, (void *)zVal, nVal, pRc);
178 static void testOomWriteData(
179 OomTest *pOom,
180 lsm_db *pDb,
181 Datasource *pData,
182 int iKey,
183 int *pRc
185 void *pKey; int nKey;
186 void *pVal; int nVal;
187 testDatasourceEntry(pData, iKey, &pKey, &nKey, &pVal, &nVal);
188 testOomWrite(pOom, pDb, pKey, nKey, pVal, nVal, pRc);
191 static void testOomScan(
192 OomTest *pOom,
193 lsm_db *pDb,
194 int bReverse,
195 const void *pKey, int nKey,
196 int nScan,
197 int *pRc
199 if( *pRc==0 ){
200 int rc;
201 int iScan = 0;
202 lsm_cursor *pCsr;
203 int (*xAdvance)(lsm_cursor *) = 0;
206 rc = lsm_csr_open(pDb, &pCsr);
207 testOomAssertRc(pOom, rc);
209 if( rc==LSM_OK ){
210 if( bReverse ){
211 rc = lsm_csr_seek(pCsr, pKey, nKey, LSM_SEEK_LE);
212 xAdvance = lsm_csr_prev;
213 }else{
214 rc = lsm_csr_seek(pCsr, pKey, nKey, LSM_SEEK_GE);
215 xAdvance = lsm_csr_next;
218 testOomAssertRc(pOom, rc);
220 while( rc==LSM_OK && lsm_csr_valid(pCsr) && iScan<nScan ){
221 const void *p; int n;
223 rc = lsm_csr_key(pCsr, &p, &n);
224 testOomAssertRc(pOom, rc);
225 if( rc==LSM_OK ){
226 rc = lsm_csr_value(pCsr, &p, &n);
227 testOomAssertRc(pOom, rc);
229 if( rc==LSM_OK ){
230 rc = xAdvance(pCsr);
231 testOomAssertRc(pOom, rc);
233 iScan++;
236 lsm_csr_close(pCsr);
237 *pRc = rc;
241 #define LSMTEST6_TESTDB "testdb.lsm"
243 void testDeleteLsmdb(const char *zFile){
244 char *zLog = testMallocPrintf("%s-log", zFile);
245 char *zShm = testMallocPrintf("%s-shm", zFile);
246 unlink(zFile);
247 unlink(zLog);
248 unlink(zShm);
249 testFree(zLog);
250 testFree(zShm);
253 static void copy_file(const char *zFrom, const char *zTo, int isDatabase){
255 if( access(zFrom, F_OK) ){
256 unlink(zTo);
257 }else{
258 int fd1;
259 int fd2;
260 off_t sz;
261 off_t i;
262 struct stat buf;
263 u8 *aBuf;
265 fd1 = open(zFrom, O_RDONLY | _O_BINARY, 0644);
266 fd2 = open(zTo, O_RDWR | O_CREAT | _O_BINARY, 0644);
268 fstat(fd1, &buf);
269 sz = buf.st_size;
270 ftruncate(fd2, sz);
272 aBuf = testMalloc(4096);
273 for(i=0; i<sz; i+=4096){
274 int bLockPage = isDatabase && i == 0;
275 int nByte = MIN((bLockPage ? 4066 : 4096), sz - i);
276 memset(aBuf, 0, 4096);
277 read(fd1, aBuf, nByte);
278 write(fd2, aBuf, nByte);
279 if( bLockPage ){
280 lseek(fd1, 4096, SEEK_SET);
281 lseek(fd2, 4096, SEEK_SET);
284 testFree(aBuf);
286 close(fd1);
287 close(fd2);
291 void testCopyLsmdb(const char *zFrom, const char *zTo){
292 char *zLog1 = testMallocPrintf("%s-log", zFrom);
293 char *zLog2 = testMallocPrintf("%s-log", zTo);
294 char *zShm1 = testMallocPrintf("%s-shm", zFrom);
295 char *zShm2 = testMallocPrintf("%s-shm", zTo);
297 unlink(zShm2);
298 unlink(zLog2);
299 unlink(zTo);
300 copy_file(zFrom, zTo, 1);
301 copy_file(zLog1, zLog2, 0);
302 copy_file(zShm1, zShm2, 0);
304 testFree(zLog1); testFree(zLog2); testFree(zShm1); testFree(zShm2);
308 ** File zFile is the path to a database. This function makes backups
309 ** of the database file and its log as follows:
311 ** cp $(zFile) $(zFile)-save
312 ** cp $(zFile)-$(zAux) $(zFile)-save-$(zAux)
314 ** Function testRestoreDb() can be used to copy the files back in the
315 ** other direction.
317 void testSaveDb(const char *zFile, const char *zAux){
318 char *zLog = testMallocPrintf("%s-%s", zFile, zAux);
319 char *zFileSave = testMallocPrintf("%s-save", zFile);
320 char *zLogSave = testMallocPrintf("%s-%s-save", zFile, zAux);
322 unlink(zFileSave);
323 unlink(zLogSave);
324 copy_file(zFile, zFileSave, 1);
325 copy_file(zLog, zLogSave, 0);
327 testFree(zLog); testFree(zFileSave); testFree(zLogSave);
331 ** File zFile is the path to a database. This function restores
332 ** a backup of the database made by a previous call to testSaveDb().
333 ** Specifically, it does the equivalent of:
335 ** cp $(zFile)-save $(zFile)
336 ** cp $(zFile)-save-$(zAux) $(zFile)-$(zAux)
338 void testRestoreDb(const char *zFile, const char *zAux){
339 char *zLog = testMallocPrintf("%s-%s", zFile, zAux);
340 char *zFileSave = testMallocPrintf("%s-save", zFile);
341 char *zLogSave = testMallocPrintf("%s-%s-save", zFile, zAux);
343 copy_file(zFileSave, zFile, 1);
344 copy_file(zLogSave, zLog, 0);
346 testFree(zLog); testFree(zFileSave); testFree(zLogSave);
350 static int lsmWriteStr(lsm_db *pDb, const char *zKey, const char *zVal){
351 int nKey = strlen(zKey);
352 int nVal = strlen(zVal);
353 return lsm_insert(pDb, (void *)zKey, nKey, (void *)zVal, nVal);
356 static void setup_delete_db(void){
357 testDeleteLsmdb(LSMTEST6_TESTDB);
361 ** Create a small database. With the following content:
363 ** "one" -> "one"
364 ** "two" -> "four"
365 ** "three" -> "nine"
366 ** "four" -> "sixteen"
367 ** "five" -> "twentyfive"
368 ** "six" -> "thirtysix"
369 ** "seven" -> "fourtynine"
370 ** "eight" -> "sixtyfour"
372 static void setup_populate_db(void){
373 const char *azStr[] = {
374 "one", "one",
375 "two", "four",
376 "three", "nine",
377 "four", "sixteen",
378 "five", "twentyfive",
379 "six", "thirtysix",
380 "seven", "fourtynine",
381 "eight", "sixtyfour",
383 int rc;
384 int ii;
385 lsm_db *pDb;
387 testDeleteLsmdb(LSMTEST6_TESTDB);
389 rc = lsm_new(tdb_lsm_env(), &pDb);
390 if( rc==LSM_OK ) rc = lsm_open(pDb, LSMTEST6_TESTDB);
392 for(ii=0; rc==LSM_OK && ii<ArraySize(azStr); ii+=2){
393 rc = lsmWriteStr(pDb, azStr[ii], azStr[ii+1]);
395 lsm_close(pDb);
397 testSaveDb(LSMTEST6_TESTDB, "log");
398 assert( rc==LSM_OK );
401 static Datasource *getDatasource(void){
402 const DatasourceDefn defn = { TEST_DATASOURCE_RANDOM, 10, 15, 200, 250 };
403 return testDatasourceNew(&defn);
407 ** Set up a database file with the following properties:
409 ** * Page size is 1024 bytes.
410 ** * Block size is 64 KB.
411 ** * Contains 5000 key-value pairs starting at 0 from the
412 ** datasource returned getDatasource().
414 static void setup_populate_db2(void){
415 Datasource *pData;
416 int ii;
417 int rc;
418 int nBlocksize = 64*1024;
419 int nPagesize = 1024;
420 int nWritebuffer = 4*1024;
421 lsm_db *pDb;
423 testDeleteLsmdb(LSMTEST6_TESTDB);
424 rc = lsm_new(tdb_lsm_env(), &pDb);
425 if( rc==LSM_OK ) rc = lsm_open(pDb, LSMTEST6_TESTDB);
427 lsm_config(pDb, LSM_CONFIG_BLOCK_SIZE, &nBlocksize);
428 lsm_config(pDb, LSM_CONFIG_PAGE_SIZE, &nPagesize);
429 lsm_config(pDb, LSM_CONFIG_AUTOFLUSH, &nWritebuffer);
431 pData = getDatasource();
432 for(ii=0; rc==LSM_OK && ii<5000; ii++){
433 void *pKey; int nKey;
434 void *pVal; int nVal;
435 testDatasourceEntry(pData, ii, &pKey, &nKey, &pVal, &nVal);
436 lsm_insert(pDb, pKey, nKey, pVal, nVal);
438 testDatasourceFree(pData);
439 lsm_close(pDb);
441 testSaveDb(LSMTEST6_TESTDB, "log");
442 assert( rc==LSM_OK );
446 ** Test the results of OOM conditions in lsm_new().
448 static void simple_oom_1(OomTest *pOom){
449 int rc;
450 lsm_db *pDb;
452 rc = lsm_new(tdb_lsm_env(), &pDb);
453 testOomAssertRc(pOom, rc);
455 lsm_close(pDb);
459 ** Test the results of OOM conditions in lsm_open().
461 static void simple_oom_2(OomTest *pOom){
462 int rc;
463 lsm_db *pDb;
465 rc = lsm_new(tdb_lsm_env(), &pDb);
466 if( rc==LSM_OK ){
467 rc = lsm_open(pDb, "testdb.lsm");
469 testOomAssertRc(pOom, rc);
471 lsm_close(pDb);
475 ** Test the results of OOM conditions in simple fetch operations.
477 static void simple_oom_3(OomTest *pOom){
478 int rc = LSM_OK;
479 lsm_db *pDb;
481 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
483 testOomFetchStr(pOom, pDb, "four", "sixteen", &rc);
484 testOomFetchStr(pOom, pDb, "seven", "fourtynine", &rc);
485 testOomFetchStr(pOom, pDb, "one", "one", &rc);
486 testOomFetchStr(pOom, pDb, "eight", "sixtyfour", &rc);
488 lsm_close(pDb);
492 ** Test the results of OOM conditions in simple write operations.
494 static void simple_oom_4(OomTest *pOom){
495 int rc = LSM_OK;
496 lsm_db *pDb;
498 testDeleteLsmdb(LSMTEST6_TESTDB);
499 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
501 testOomWriteStr(pOom, pDb, "123", "onetwothree", &rc);
502 testOomWriteStr(pOom, pDb, "456", "fourfivesix", &rc);
503 testOomWriteStr(pOom, pDb, "789", "seveneightnine", &rc);
504 testOomWriteStr(pOom, pDb, "123", "teneleventwelve", &rc);
505 testOomWriteStr(pOom, pDb, "456", "fourteenfifteensixteen", &rc);
507 lsm_close(pDb);
510 static void simple_oom_5(OomTest *pOom){
511 Datasource *pData = getDatasource();
512 int rc = LSM_OK;
513 lsm_db *pDb;
515 testRestoreDb(LSMTEST6_TESTDB, "log");
516 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
518 testOomFetchData(pOom, pDb, pData, 3333, &rc);
519 testOomFetchData(pOom, pDb, pData, 0, &rc);
520 testOomFetchData(pOom, pDb, pData, 4999, &rc);
522 lsm_close(pDb);
523 testDatasourceFree(pData);
526 static void simple_oom_6(OomTest *pOom){
527 Datasource *pData = getDatasource();
528 int rc = LSM_OK;
529 lsm_db *pDb;
531 testRestoreDb(LSMTEST6_TESTDB, "log");
532 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
534 testOomWriteData(pOom, pDb, pData, 5000, &rc);
535 testOomWriteData(pOom, pDb, pData, 5001, &rc);
536 testOomWriteData(pOom, pDb, pData, 5002, &rc);
537 testOomFetchData(pOom, pDb, pData, 5001, &rc);
538 testOomFetchData(pOom, pDb, pData, 1234, &rc);
540 lsm_close(pDb);
541 testDatasourceFree(pData);
544 static void simple_oom_7(OomTest *pOom){
545 Datasource *pData = getDatasource();
546 int rc = LSM_OK;
547 lsm_db *pDb;
549 testRestoreDb(LSMTEST6_TESTDB, "log");
550 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
551 testOomScan(pOom, pDb, 0, "abc", 3, 20, &rc);
552 lsm_close(pDb);
553 testDatasourceFree(pData);
556 static void simple_oom_8(OomTest *pOom){
557 Datasource *pData = getDatasource();
558 int rc = LSM_OK;
559 lsm_db *pDb;
560 testRestoreDb(LSMTEST6_TESTDB, "log");
561 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb, &rc);
562 testOomScan(pOom, pDb, 1, "xyz", 3, 20, &rc);
563 lsm_close(pDb);
564 testDatasourceFree(pData);
568 ** This test case has two clients connected to a database. The first client
569 ** hits an OOM while writing to the database. Check that the second
570 ** connection is still able to query the db following the OOM.
572 static void simple_oom2_1(OomTest *pOom){
573 const int nRecord = 100; /* Number of records initially in db */
574 const int nIns = 10; /* Number of records inserted with OOM */
576 Datasource *pData = getDatasource();
577 int rc = LSM_OK;
578 lsm_db *pDb1;
579 lsm_db *pDb2;
580 int i;
582 testDeleteLsmdb(LSMTEST6_TESTDB);
584 /* Open the two connections. Initialize the in-memory tree so that it
585 ** contains 100 records. Do all this with OOM injection disabled. */
586 testOomEnable(pOom, 0);
587 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb1, &rc);
588 testOomOpen(pOom, LSMTEST6_TESTDB, &pDb2, &rc);
589 for(i=0; i<nRecord; i++){
590 testOomWriteData(pOom, pDb1, pData, i, &rc);
592 testOomEnable(pOom, 1);
593 assert( rc==0 );
595 /* Insert 10 more records using pDb1. Stop when an OOM is encountered. */
596 for(i=nRecord; i<nRecord+nIns; i++){
597 testOomWriteData(pOom, pDb1, pData, i, &rc);
598 if( rc ) break;
600 testOomAssertRc(pOom, rc);
602 /* Switch off OOM injection. Write a few rows using pDb2. Then check
603 ** that the database may be successfully queried. */
604 testOomEnable(pOom, 0);
605 rc = 0;
606 for(; i<nRecord+nIns && rc==0; i++){
607 testOomWriteData(pOom, pDb2, pData, i, &rc);
609 for(i=0; i<nRecord+nIns; i++) testOomFetchData(pOom, pDb2, pData, i, &rc);
610 testOomEnable(pOom, 1);
612 lsm_close(pDb1);
613 lsm_close(pDb2);
614 testDatasourceFree(pData);
618 static void do_test_oom1(const char *zPattern, int *pRc){
619 struct SimpleOom {
620 const char *zName;
621 void (*xSetup)(void);
622 void (*xFunc)(OomTest *);
623 } aSimple[] = {
624 { "oom1.lsm.1", setup_delete_db, simple_oom_1 },
625 { "oom1.lsm.2", setup_delete_db, simple_oom_2 },
626 { "oom1.lsm.3", setup_populate_db, simple_oom_3 },
627 { "oom1.lsm.4", setup_delete_db, simple_oom_4 },
628 { "oom1.lsm.5", setup_populate_db2, simple_oom_5 },
629 { "oom1.lsm.6", setup_populate_db2, simple_oom_6 },
630 { "oom1.lsm.7", setup_populate_db2, simple_oom_7 },
631 { "oom1.lsm.8", setup_populate_db2, simple_oom_8 },
633 { "oom2.lsm.1", setup_delete_db, simple_oom2_1 },
635 int i;
637 for(i=0; i<ArraySize(aSimple); i++){
638 if( *pRc==0 && testCaseBegin(pRc, zPattern, "%s", aSimple[i].zName) ){
639 OomTest t;
641 if( aSimple[i].xSetup ){
642 aSimple[i].xSetup();
645 for(testOomStart(&t); testOomContinue(&t); testOomNext(&t)){
646 aSimple[i].xFunc(&t);
649 printf("(%d injections).", t.iNext-2);
650 testCaseFinish( (*pRc = testOomFinish(&t)) );
651 testMallocOom(tdb_lsm_env(), 0, 0, 0, 0);
656 void test_oom(
657 const char *zPattern, /* Run test cases that match this pattern */
658 int *pRc /* IN/OUT: Error code */
660 do_test_oom1(zPattern, pRc);