Fix the ".lint fkey-indexes" shell command so that it works with WITHOUT ROWID
[sqlite.git] / ext / lsm1 / lsm-test / lsmtest_tdb4.c
blobc45b0529abda26230bda8dc93dc32b5e3121c133
2 /*
3 ** This file contains the TestDb bt wrapper.
4 */
6 #include "lsmtest_tdb.h"
7 #include "lsmtest.h"
8 #include <unistd.h>
9 #include "bt.h"
11 #include <pthread.h>
13 typedef struct BtDb BtDb;
14 typedef struct BtFile BtFile;
16 /* Background checkpointer interface (see implementations below). */
17 typedef struct bt_ckpter bt_ckpter;
18 static int bgc_attach(BtDb *pDb, const char*);
19 static int bgc_detach(BtDb *pDb);
22 ** Each database or log file opened by a database handle is wrapped by
23 ** an object of the following type.
25 struct BtFile {
26 BtDb *pBt; /* Database handle that opened this file */
27 bt_env *pVfs; /* Underlying VFS */
28 bt_file *pFile; /* File handle belonging to underlying VFS */
29 int nSectorSize; /* Size of sectors in bytes */
30 int nSector; /* Allocated size of nSector array */
31 u8 **apSector; /* Original sector data */
35 ** nCrashSync:
36 ** If this value is non-zero, then a "crash-test" is running. If
37 ** nCrashSync==1, then the crash is simulated during the very next
38 ** call to the xSync() VFS method (on either the db or log file).
39 ** If nCrashSync==2, the following call to xSync(), and so on.
41 ** bCrash:
42 ** After a crash is simulated, this variable is set. Any subsequent
43 ** attempts to write to a file or modify the file system in any way
44 ** fail once this is set. All the caller can do is close the connection.
46 ** bFastInsert:
47 ** If this variable is set to true, then a BT_CONTROL_FAST_INSERT_OP
48 ** control is issued before each callto BtReplace() or BtCsrOpen().
50 struct BtDb {
51 TestDb base; /* Base class */
52 bt_db *pBt; /* bt database handle */
53 sqlite4_env *pEnv; /* SQLite environment (for malloc/free) */
54 bt_env *pVfs; /* Underlying VFS */
55 int bFastInsert; /* True to use fast-insert */
57 /* Space for bt_fetch() results */
58 u8 *aBuffer; /* Space to store results */
59 int nBuffer; /* Allocated size of aBuffer[] in bytes */
60 int nRef;
62 /* Background checkpointer used by mt connections */
63 bt_ckpter *pCkpter;
65 /* Stuff used for crash test simulation */
66 BtFile *apFile[2]; /* Database and log files used by pBt */
67 bt_env env; /* Private VFS for this object */
68 int nCrashSync; /* Number of syncs until crash (see above) */
69 int bCrash; /* True once a crash has been simulated */
72 static int btVfsFullpath(
73 sqlite4_env *pEnv,
74 bt_env *pVfs,
75 const char *z,
76 char **pzOut
78 BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
79 if( pBt->bCrash ) return SQLITE4_IOERR;
80 return pBt->pVfs->xFullpath(pEnv, pBt->pVfs, z, pzOut);
83 static int btVfsOpen(
84 sqlite4_env *pEnv,
85 bt_env *pVfs,
86 const char *zFile,
87 int flags, bt_file **ppFile
89 BtFile *p;
90 BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
91 int rc;
93 if( pBt->bCrash ) return SQLITE4_IOERR;
95 p = (BtFile*)testMalloc(sizeof(BtFile));
96 if( !p ) return SQLITE4_NOMEM;
97 if( flags & BT_OPEN_DATABASE ){
98 pBt->apFile[0] = p;
99 }else if( flags & BT_OPEN_LOG ){
100 pBt->apFile[1] = p;
102 if( (flags & BT_OPEN_SHARED)==0 ){
103 p->pBt = pBt;
105 p->pVfs = pBt->pVfs;
107 rc = pBt->pVfs->xOpen(pEnv, pVfs, zFile, flags, &p->pFile);
108 if( rc!=SQLITE4_OK ){
109 testFree(p);
110 p = 0;
111 }else{
112 pBt->nRef++;
115 *ppFile = (bt_file*)p;
116 return rc;
119 static int btVfsSize(bt_file *pFile, sqlite4_int64 *piRes){
120 BtFile *p = (BtFile*)pFile;
121 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
122 return p->pVfs->xSize(p->pFile, piRes);
125 static int btVfsRead(bt_file *pFile, sqlite4_int64 iOff, void *pBuf, int nBuf){
126 BtFile *p = (BtFile*)pFile;
127 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
128 return p->pVfs->xRead(p->pFile, iOff, pBuf, nBuf);
131 static int btFlushSectors(BtFile *p, int iFile){
132 sqlite4_int64 iSz;
133 int rc;
134 int i;
135 u8 *aTmp = 0;
137 rc = p->pBt->pVfs->xSize(p->pFile, &iSz);
138 for(i=0; rc==SQLITE4_OK && i<p->nSector; i++){
139 if( p->pBt->bCrash && p->apSector[i] ){
141 /* The system is simulating a crash. There are three choices for
142 ** this sector:
144 ** 1) Leave it as it is (simulating a successful write),
145 ** 2) Restore the original data (simulating a lost write),
146 ** 3) Populate the disk sector with garbage data.
148 sqlite4_int64 iSOff = p->nSectorSize*i;
149 int nWrite = MIN(p->nSectorSize, iSz - iSOff);
151 if( nWrite ){
152 u8 *aWrite = 0;
153 int iOpt = (testPrngValue(i) % 3) + 1;
154 if( iOpt==1 ){
155 aWrite = p->apSector[i];
156 }else if( iOpt==3 ){
157 if( aTmp==0 ) aTmp = testMalloc(p->nSectorSize);
158 aWrite = aTmp;
159 testPrngArray(i*13, (u32*)aWrite, nWrite/sizeof(u32));
162 #if 0
163 fprintf(stderr, "handle sector %d of %s with %s\n", i,
164 iFile==0 ? "db" : "log",
165 iOpt==1 ? "rollback" : iOpt==2 ? "write" : "omit"
167 fflush(stderr);
168 #endif
170 if( aWrite ){
171 rc = p->pBt->pVfs->xWrite(p->pFile, iSOff, aWrite, nWrite);
175 testFree(p->apSector[i]);
176 p->apSector[i] = 0;
179 testFree(aTmp);
180 return rc;
183 static int btSaveSectors(BtFile *p, sqlite4_int64 iOff, int nBuf){
184 int rc;
185 sqlite4_int64 iSz; /* Size of file on disk */
186 int iFirst; /* First sector affected */
187 int iSector; /* Current sector */
188 int iLast; /* Last sector affected */
190 if( p->nSectorSize==0 ){
191 p->nSectorSize = p->pBt->pVfs->xSectorSize(p->pFile);
192 if( p->nSectorSize<512 ) p->nSectorSize = 512;
194 iLast = (iOff+nBuf-1) / p->nSectorSize;
195 iFirst = iOff / p->nSectorSize;
197 rc = p->pBt->pVfs->xSize(p->pFile, &iSz);
198 for(iSector=iFirst; rc==SQLITE4_OK && iSector<=iLast; iSector++){
199 int nRead;
200 sqlite4_int64 iSOff = iSector * p->nSectorSize;
201 u8 *aBuf = testMalloc(p->nSectorSize);
202 nRead = MIN(p->nSectorSize, (iSz - iSOff));
203 if( nRead>0 ){
204 rc = p->pBt->pVfs->xRead(p->pFile, iSOff, aBuf, nRead);
207 while( rc==SQLITE4_OK && iSector>=p->nSector ){
208 int nNew = p->nSector + 32;
209 u8 **apNew = (u8**)testMalloc(nNew * sizeof(u8*));
210 memcpy(apNew, p->apSector, p->nSector*sizeof(u8*));
211 testFree(p->apSector);
212 p->apSector = apNew;
213 p->nSector = nNew;
216 p->apSector[iSector] = aBuf;
219 return rc;
222 static int btVfsWrite(bt_file *pFile, sqlite4_int64 iOff, void *pBuf, int nBuf){
223 BtFile *p = (BtFile*)pFile;
224 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
225 if( p->pBt && p->pBt->nCrashSync ){
226 btSaveSectors(p, iOff, nBuf);
228 return p->pVfs->xWrite(p->pFile, iOff, pBuf, nBuf);
231 static int btVfsTruncate(bt_file *pFile, sqlite4_int64 iOff){
232 BtFile *p = (BtFile*)pFile;
233 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
234 return p->pVfs->xTruncate(p->pFile, iOff);
237 static int btVfsSync(bt_file *pFile){
238 int rc = SQLITE4_OK;
239 BtFile *p = (BtFile*)pFile;
240 BtDb *pBt = p->pBt;
242 if( pBt ){
243 if( pBt->bCrash ) return SQLITE4_IOERR;
244 if( pBt->nCrashSync ){
245 pBt->nCrashSync--;
246 pBt->bCrash = (pBt->nCrashSync==0);
247 if( pBt->bCrash ){
248 btFlushSectors(pBt->apFile[0], 0);
249 btFlushSectors(pBt->apFile[1], 1);
250 rc = SQLITE4_IOERR;
251 }else{
252 btFlushSectors(p, 0);
257 if( rc==SQLITE4_OK ){
258 rc = p->pVfs->xSync(p->pFile);
260 return rc;
263 static int btVfsSectorSize(bt_file *pFile){
264 BtFile *p = (BtFile*)pFile;
265 return p->pVfs->xSectorSize(p->pFile);
268 static void btDeref(BtDb *p){
269 p->nRef--;
270 assert( p->nRef>=0 );
271 if( p->nRef<=0 ) testFree(p);
274 static int btVfsClose(bt_file *pFile){
275 BtFile *p = (BtFile*)pFile;
276 BtDb *pBt = p->pBt;
277 int rc;
278 if( pBt ){
279 btFlushSectors(p, 0);
280 if( p==pBt->apFile[0] ) pBt->apFile[0] = 0;
281 if( p==pBt->apFile[1] ) pBt->apFile[1] = 0;
283 testFree(p->apSector);
284 rc = p->pVfs->xClose(p->pFile);
285 #if 0
286 btDeref(p->pBt);
287 #endif
288 testFree(p);
289 return rc;
292 static int btVfsUnlink(sqlite4_env *pEnv, bt_env *pVfs, const char *zFile){
293 BtDb *pBt = (BtDb*)pVfs->pVfsCtx;
294 if( pBt->bCrash ) return SQLITE4_IOERR;
295 return pBt->pVfs->xUnlink(pEnv, pBt->pVfs, zFile);
298 static int btVfsLock(bt_file *pFile, int iLock, int eType){
299 BtFile *p = (BtFile*)pFile;
300 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
301 return p->pVfs->xLock(p->pFile, iLock, eType);
304 static int btVfsTestLock(bt_file *pFile, int iLock, int nLock, int eType){
305 BtFile *p = (BtFile*)pFile;
306 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
307 return p->pVfs->xTestLock(p->pFile, iLock, nLock, eType);
310 static int btVfsShmMap(bt_file *pFile, int iChunk, int sz, void **ppOut){
311 BtFile *p = (BtFile*)pFile;
312 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
313 return p->pVfs->xShmMap(p->pFile, iChunk, sz, ppOut);
316 static void btVfsShmBarrier(bt_file *pFile){
317 BtFile *p = (BtFile*)pFile;
318 return p->pVfs->xShmBarrier(p->pFile);
321 static int btVfsShmUnmap(bt_file *pFile, int bDelete){
322 BtFile *p = (BtFile*)pFile;
323 if( p->pBt && p->pBt->bCrash ) return SQLITE4_IOERR;
324 return p->pVfs->xShmUnmap(p->pFile, bDelete);
327 static int bt_close(TestDb *pTestDb){
328 BtDb *p = (BtDb*)pTestDb;
329 int rc = sqlite4BtClose(p->pBt);
330 free(p->aBuffer);
331 if( p->apFile[0] ) p->apFile[0]->pBt = 0;
332 if( p->apFile[1] ) p->apFile[1]->pBt = 0;
333 bgc_detach(p);
334 testFree(p);
335 return rc;
338 static int btMinTransaction(BtDb *p, int iMin, int *piLevel){
339 int iLevel;
340 int rc = SQLITE4_OK;
342 iLevel = sqlite4BtTransactionLevel(p->pBt);
343 if( iLevel<iMin ){
344 rc = sqlite4BtBegin(p->pBt, iMin);
345 *piLevel = iLevel;
346 }else{
347 *piLevel = -1;
350 return rc;
352 static int btRestoreTransaction(BtDb *p, int iLevel, int rcin){
353 int rc = rcin;
354 if( iLevel>=0 ){
355 if( rc==SQLITE4_OK ){
356 rc = sqlite4BtCommit(p->pBt, iLevel);
357 }else{
358 sqlite4BtRollback(p->pBt, iLevel);
360 assert( iLevel==sqlite4BtTransactionLevel(p->pBt) );
362 return rc;
365 static int bt_write(TestDb *pTestDb, void *pK, int nK, void *pV, int nV){
366 BtDb *p = (BtDb*)pTestDb;
367 int iLevel;
368 int rc;
370 rc = btMinTransaction(p, 2, &iLevel);
371 if( rc==SQLITE4_OK ){
372 if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0);
373 rc = sqlite4BtReplace(p->pBt, pK, nK, pV, nV);
374 rc = btRestoreTransaction(p, iLevel, rc);
376 return rc;
379 static int bt_delete(TestDb *pTestDb, void *pK, int nK){
380 return bt_write(pTestDb, pK, nK, 0, -1);
383 static int bt_delete_range(
384 TestDb *pTestDb,
385 void *pKey1, int nKey1,
386 void *pKey2, int nKey2
388 BtDb *p = (BtDb*)pTestDb;
389 bt_cursor *pCsr = 0;
390 int rc = SQLITE4_OK;
391 int iLevel;
393 rc = btMinTransaction(p, 2, &iLevel);
394 if( rc==SQLITE4_OK ){
395 if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0);
396 rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr);
398 while( rc==SQLITE4_OK ){
399 const void *pK;
400 int n;
401 int nCmp;
402 int res;
404 rc = sqlite4BtCsrSeek(pCsr, pKey1, nKey1, BT_SEEK_GE);
405 if( rc==SQLITE4_INEXACT ) rc = SQLITE4_OK;
406 if( rc!=SQLITE4_OK ) break;
408 rc = sqlite4BtCsrKey(pCsr, &pK, &n);
409 if( rc!=SQLITE4_OK ) break;
411 nCmp = MIN(n, nKey1);
412 res = memcmp(pKey1, pK, nCmp);
413 assert( res<0 || (res==0 && nKey1<=n) );
414 if( res==0 && nKey1==n ){
415 rc = sqlite4BtCsrNext(pCsr);
416 if( rc!=SQLITE4_OK ) break;
417 rc = sqlite4BtCsrKey(pCsr, &pK, &n);
418 if( rc!=SQLITE4_OK ) break;
421 nCmp = MIN(n, nKey2);
422 res = memcmp(pKey2, pK, nCmp);
423 if( res<0 || (res==0 && nKey2<=n) ) break;
425 rc = sqlite4BtDelete(pCsr);
427 if( rc==SQLITE4_NOTFOUND ) rc = SQLITE4_OK;
429 sqlite4BtCsrClose(pCsr);
431 rc = btRestoreTransaction(p, iLevel, rc);
432 return rc;
435 static int bt_fetch(
436 TestDb *pTestDb,
437 void *pK, int nK,
438 void **ppVal, int *pnVal
440 BtDb *p = (BtDb*)pTestDb;
441 bt_cursor *pCsr = 0;
442 int iLevel;
443 int rc = SQLITE4_OK;
445 iLevel = sqlite4BtTransactionLevel(p->pBt);
446 if( iLevel==0 ){
447 rc = sqlite4BtBegin(p->pBt, 1);
448 if( rc!=SQLITE4_OK ) return rc;
451 if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0);
452 rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr);
453 if( rc==SQLITE4_OK ){
454 rc = sqlite4BtCsrSeek(pCsr, pK, nK, BT_SEEK_EQ);
455 if( rc==SQLITE4_OK ){
456 const void *pV = 0;
457 int nV = 0;
458 rc = sqlite4BtCsrData(pCsr, 0, -1, &pV, &nV);
459 if( rc==SQLITE4_OK ){
460 if( nV>p->nBuffer ){
461 free(p->aBuffer);
462 p->aBuffer = (u8*)malloc(nV*2);
463 p->nBuffer = nV*2;
465 memcpy(p->aBuffer, pV, nV);
466 *pnVal = nV;
467 *ppVal = (void*)(p->aBuffer);
470 }else if( rc==SQLITE4_INEXACT || rc==SQLITE4_NOTFOUND ){
471 *ppVal = 0;
472 *pnVal = -1;
473 rc = SQLITE4_OK;
475 sqlite4BtCsrClose(pCsr);
478 if( iLevel==0 ) sqlite4BtCommit(p->pBt, 0);
479 return rc;
482 static int bt_scan(
483 TestDb *pTestDb,
484 void *pCtx,
485 int bReverse,
486 void *pFirst, int nFirst,
487 void *pLast, int nLast,
488 void (*xCallback)(void *, void *, int , void *, int)
490 BtDb *p = (BtDb*)pTestDb;
491 bt_cursor *pCsr = 0;
492 int rc;
493 int iLevel;
495 rc = btMinTransaction(p, 1, &iLevel);
497 if( rc==SQLITE4_OK ){
498 if( p->bFastInsert ) sqlite4BtControl(p->pBt, BT_CONTROL_FAST_INSERT_OP, 0);
499 rc = sqlite4BtCsrOpen(p->pBt, 0, &pCsr);
501 if( rc==SQLITE4_OK ){
502 if( bReverse ){
503 if( pLast ){
504 rc = sqlite4BtCsrSeek(pCsr, pLast, nLast, BT_SEEK_LE);
505 }else{
506 rc = sqlite4BtCsrLast(pCsr);
508 }else{
509 rc = sqlite4BtCsrSeek(pCsr, pFirst, nFirst, BT_SEEK_GE);
511 if( rc==SQLITE4_INEXACT ) rc = SQLITE4_OK;
513 while( rc==SQLITE4_OK ){
514 const void *pK = 0; int nK = 0;
515 const void *pV = 0; int nV = 0;
517 rc = sqlite4BtCsrKey(pCsr, &pK, &nK);
518 if( rc==SQLITE4_OK ){
519 rc = sqlite4BtCsrData(pCsr, 0, -1, &pV, &nV);
522 if( rc!=SQLITE4_OK ) break;
523 if( bReverse ){
524 if( pFirst ){
525 int res;
526 int nCmp = MIN(nK, nFirst);
527 res = memcmp(pFirst, pK, nCmp);
528 if( res>0 || (res==0 && nK<nFirst) ) break;
530 }else{
531 if( pLast ){
532 int res;
533 int nCmp = MIN(nK, nLast);
534 res = memcmp(pLast, pK, nCmp);
535 if( res<0 || (res==0 && nK>nLast) ) break;
539 xCallback(pCtx, (void*)pK, nK, (void*)pV, nV);
540 if( bReverse ){
541 rc = sqlite4BtCsrPrev(pCsr);
542 }else{
543 rc = sqlite4BtCsrNext(pCsr);
546 if( rc==SQLITE4_NOTFOUND ) rc = SQLITE4_OK;
548 sqlite4BtCsrClose(pCsr);
551 rc = btRestoreTransaction(p, iLevel, rc);
552 return rc;
555 static int bt_begin(TestDb *pTestDb, int iLvl){
556 BtDb *p = (BtDb*)pTestDb;
557 int rc = sqlite4BtBegin(p->pBt, iLvl);
558 return rc;
561 static int bt_commit(TestDb *pTestDb, int iLvl){
562 BtDb *p = (BtDb*)pTestDb;
563 int rc = sqlite4BtCommit(p->pBt, iLvl);
564 return rc;
567 static int bt_rollback(TestDb *pTestDb, int iLvl){
568 BtDb *p = (BtDb*)pTestDb;
569 int rc = sqlite4BtRollback(p->pBt, iLvl);
570 return rc;
573 static int testParseOption(
574 const char **pzIn, /* IN/OUT: pointer to next option */
575 const char **pzOpt, /* OUT: nul-terminated option name */
576 const char **pzArg, /* OUT: nul-terminated option argument */
577 char *pSpace /* Temporary space for output params */
579 const char *p = *pzIn;
580 const char *pStart;
581 int n;
583 char *pOut = pSpace;
585 while( *p==' ' ) p++;
586 pStart = p;
587 while( *p && *p!='=' ) p++;
588 if( *p==0 ) return 1;
590 n = (p - pStart);
591 memcpy(pOut, pStart, n);
592 *pzOpt = pOut;
593 pOut += n;
594 *pOut++ = '\0';
596 p++;
597 pStart = p;
598 while( *p && *p!=' ' ) p++;
599 n = (p - pStart);
601 memcpy(pOut, pStart, n);
602 *pzArg = pOut;
603 pOut += n;
604 *pOut++ = '\0';
606 *pzIn = p;
607 return 0;
610 static int testParseInt(const char *z, int *piVal){
611 int i = 0;
612 const char *p = z;
614 while( *p>='0' && *p<='9' ){
615 i = i*10 + (*p - '0');
616 p++;
618 if( *p=='K' || *p=='k' ){
619 i = i * 1024;
620 p++;
621 }else if( *p=='M' || *p=='m' ){
622 i = i * 1024 * 1024;
623 p++;
626 if( *p ) return SQLITE4_ERROR;
627 *piVal = i;
628 return SQLITE4_OK;
631 static int testBtConfigure(BtDb *pDb, const char *zCfg, int *pbMt){
632 int rc = SQLITE4_OK;
634 if( zCfg ){
635 struct CfgParam {
636 const char *zParam;
637 int eParam;
638 } aParam[] = {
639 { "safety", BT_CONTROL_SAFETY },
640 { "autockpt", BT_CONTROL_AUTOCKPT },
641 { "multiproc", BT_CONTROL_MULTIPROC },
642 { "blksz", BT_CONTROL_BLKSZ },
643 { "pagesz", BT_CONTROL_PAGESZ },
644 { "mt", -1 },
645 { "fastinsert", -2 },
646 { 0, 0 }
648 const char *z = zCfg;
649 int n = strlen(z);
650 char *aSpace;
651 const char *zOpt;
652 const char *zArg;
654 aSpace = (char*)testMalloc(n+2);
655 while( rc==SQLITE4_OK && 0==testParseOption(&z, &zOpt, &zArg, aSpace) ){
656 int i;
657 int iVal;
658 rc = testArgSelect(aParam, "param", zOpt, &i);
659 if( rc!=SQLITE4_OK ) break;
661 rc = testParseInt(zArg, &iVal);
662 if( rc!=SQLITE4_OK ) break;
664 switch( aParam[i].eParam ){
665 case -1:
666 *pbMt = iVal;
667 break;
668 case -2:
669 pDb->bFastInsert = 1;
670 break;
671 default:
672 rc = sqlite4BtControl(pDb->pBt, aParam[i].eParam, (void*)&iVal);
673 break;
676 testFree(aSpace);
679 return rc;
683 int test_bt_open(
684 const char *zSpec,
685 const char *zFilename,
686 int bClear,
687 TestDb **ppDb
690 static const DatabaseMethods SqlMethods = {
691 bt_close,
692 bt_write,
693 bt_delete,
694 bt_delete_range,
695 bt_fetch,
696 bt_scan,
697 bt_begin,
698 bt_commit,
699 bt_rollback
701 BtDb *p = 0;
702 bt_db *pBt = 0;
703 int rc;
704 sqlite4_env *pEnv = sqlite4_env_default();
706 if( bClear && zFilename && zFilename[0] ){
707 char *zLog = sqlite3_mprintf("%s-wal", zFilename);
708 unlink(zFilename);
709 unlink(zLog);
710 sqlite3_free(zLog);
713 rc = sqlite4BtNew(pEnv, 0, &pBt);
714 if( rc==SQLITE4_OK ){
715 int mt = 0; /* True for multi-threaded connection */
717 p = (BtDb*)testMalloc(sizeof(BtDb));
718 p->base.pMethods = &SqlMethods;
719 p->pBt = pBt;
720 p->pEnv = pEnv;
721 p->nRef = 1;
723 p->env.pVfsCtx = (void*)p;
724 p->env.xFullpath = btVfsFullpath;
725 p->env.xOpen = btVfsOpen;
726 p->env.xSize = btVfsSize;
727 p->env.xRead = btVfsRead;
728 p->env.xWrite = btVfsWrite;
729 p->env.xTruncate = btVfsTruncate;
730 p->env.xSync = btVfsSync;
731 p->env.xSectorSize = btVfsSectorSize;
732 p->env.xClose = btVfsClose;
733 p->env.xUnlink = btVfsUnlink;
734 p->env.xLock = btVfsLock;
735 p->env.xTestLock = btVfsTestLock;
736 p->env.xShmMap = btVfsShmMap;
737 p->env.xShmBarrier = btVfsShmBarrier;
738 p->env.xShmUnmap = btVfsShmUnmap;
740 sqlite4BtControl(pBt, BT_CONTROL_GETVFS, (void*)&p->pVfs);
741 sqlite4BtControl(pBt, BT_CONTROL_SETVFS, (void*)&p->env);
743 rc = testBtConfigure(p, zSpec, &mt);
744 if( rc==SQLITE4_OK ){
745 rc = sqlite4BtOpen(pBt, zFilename);
748 if( rc==SQLITE4_OK && mt ){
749 int nAuto = 0;
750 rc = bgc_attach(p, zSpec);
751 sqlite4BtControl(pBt, BT_CONTROL_AUTOCKPT, (void*)&nAuto);
755 if( rc!=SQLITE4_OK && p ){
756 bt_close(&p->base);
759 *ppDb = &p->base;
760 return rc;
763 int test_fbt_open(
764 const char *zSpec,
765 const char *zFilename,
766 int bClear,
767 TestDb **ppDb
769 return test_bt_open("fast=1", zFilename, bClear, ppDb);
772 int test_fbts_open(
773 const char *zSpec,
774 const char *zFilename,
775 int bClear,
776 TestDb **ppDb
778 return test_bt_open("fast=1 blksz=32K pagesz=512", zFilename, bClear, ppDb);
782 void tdb_bt_prepare_sync_crash(TestDb *pTestDb, int iSync){
783 BtDb *p = (BtDb*)pTestDb;
784 assert( pTestDb->pMethods->xClose==bt_close );
785 assert( p->bCrash==0 );
786 p->nCrashSync = iSync;
789 bt_db *tdb_bt(TestDb *pDb){
790 if( pDb->pMethods->xClose==bt_close ){
791 return ((BtDb *)pDb)->pBt;
793 return 0;
796 /*************************************************************************
797 ** Beginning of code for background checkpointer.
800 struct bt_ckpter {
801 sqlite4_buffer file; /* File name */
802 sqlite4_buffer spec; /* Options */
803 int nLogsize; /* Minimum log size to checkpoint */
804 int nRef; /* Number of clients */
806 int bDoWork; /* Set by client threads */
807 pthread_t ckpter_thread; /* Checkpointer thread */
808 pthread_cond_t ckpter_cond; /* Condition var the ckpter waits on */
809 pthread_mutex_t ckpter_mutex; /* Mutex used with ckpter_cond */
811 bt_ckpter *pNext; /* Next object in list at gBgc.pCkpter */
814 static struct GlobalBackgroundCheckpointer {
815 bt_ckpter *pCkpter; /* Linked list of checkpointers */
816 } gBgc;
818 static void *bgc_main(void *pArg){
819 BtDb *pDb = 0;
820 int rc;
821 int mt;
822 bt_ckpter *pCkpter = (bt_ckpter*)pArg;
824 rc = test_bt_open("", (char*)pCkpter->file.p, 0, (TestDb**)&pDb);
825 assert( rc==SQLITE4_OK );
826 rc = testBtConfigure(pDb, (char*)pCkpter->spec.p, &mt);
828 while( pCkpter->nRef>0 ){
829 bt_db *db = pDb->pBt;
830 int nLog = 0;
832 sqlite4BtBegin(db, 1);
833 sqlite4BtCommit(db, 0);
834 sqlite4BtControl(db, BT_CONTROL_LOGSIZE, (void*)&nLog);
836 if( nLog>=pCkpter->nLogsize ){
837 int rc;
838 bt_checkpoint ckpt;
839 memset(&ckpt, 0, sizeof(bt_checkpoint));
840 ckpt.nFrameBuffer = nLog/2;
841 rc = sqlite4BtControl(db, BT_CONTROL_CHECKPOINT, (void*)&ckpt);
842 assert( rc==SQLITE4_OK );
843 sqlite4BtControl(db, BT_CONTROL_LOGSIZE, (void*)&nLog);
846 /* The thread will wake up when it is signaled either because another
847 ** thread has created some work for this one or because the connection
848 ** is being closed. */
849 pthread_mutex_lock(&pCkpter->ckpter_mutex);
850 if( pCkpter->bDoWork==0 ){
851 pthread_cond_wait(&pCkpter->ckpter_cond, &pCkpter->ckpter_mutex);
853 pCkpter->bDoWork = 0;
854 pthread_mutex_unlock(&pCkpter->ckpter_mutex);
857 if( pDb ) bt_close((TestDb*)pDb);
858 return 0;
861 static void bgc_logsize_cb(void *pCtx, int nLogsize){
862 bt_ckpter *p = (bt_ckpter*)pCtx;
863 if( nLogsize>=p->nLogsize ){
864 pthread_mutex_lock(&p->ckpter_mutex);
865 p->bDoWork = 1;
866 pthread_cond_signal(&p->ckpter_cond);
867 pthread_mutex_unlock(&p->ckpter_mutex);
871 static int bgc_attach(BtDb *pDb, const char *zSpec){
872 int rc;
873 int n;
874 bt_info info;
875 bt_ckpter *pCkpter;
877 /* Figure out the full path to the database opened by handle pDb. */
878 info.eType = BT_INFO_FILENAME;
879 info.pgno = 0;
880 sqlite4_buffer_init(&info.output, 0);
881 rc = sqlite4BtControl(pDb->pBt, BT_CONTROL_INFO, (void*)&info);
882 if( rc!=SQLITE4_OK ) return rc;
884 sqlite4_mutex_enter(sqlite4_mutex_alloc(pDb->pEnv, SQLITE4_MUTEX_STATIC_KV));
886 /* Search for an existing bt_ckpter object. */
887 n = info.output.n;
888 for(pCkpter=gBgc.pCkpter; pCkpter; pCkpter=pCkpter->pNext){
889 if( n==pCkpter->file.n && 0==memcmp(info.output.p, pCkpter->file.p, n) ){
890 break;
894 /* Failed to find a suitable checkpointer. Create a new one. */
895 if( pCkpter==0 ){
896 bt_logsizecb cb;
898 pCkpter = testMalloc(sizeof(bt_ckpter));
899 memcpy(&pCkpter->file, &info.output, sizeof(sqlite4_buffer));
900 info.output.p = 0;
901 pCkpter->pNext = gBgc.pCkpter;
902 pCkpter->nLogsize = 1000;
903 gBgc.pCkpter = pCkpter;
904 pCkpter->nRef = 1;
906 sqlite4_buffer_init(&pCkpter->spec, 0);
907 rc = sqlite4_buffer_set(&pCkpter->spec, zSpec, strlen(zSpec)+1);
908 assert( rc==SQLITE4_OK );
910 /* Kick off the checkpointer thread. */
911 if( rc==0 ) rc = pthread_cond_init(&pCkpter->ckpter_cond, 0);
912 if( rc==0 ) rc = pthread_mutex_init(&pCkpter->ckpter_mutex, 0);
913 if( rc==0 ){
914 rc = pthread_create(&pCkpter->ckpter_thread, 0, bgc_main, (void*)pCkpter);
916 assert( rc==0 ); /* todo: Fix this */
918 /* Set up the logsize callback for the client thread */
919 cb.pCtx = (void*)pCkpter;
920 cb.xLogsize = bgc_logsize_cb;
921 sqlite4BtControl(pDb->pBt, BT_CONTROL_LOGSIZECB, (void*)&cb);
922 }else{
923 pCkpter->nRef++;
926 /* Assuming a checkpointer was encountered or effected, attach the
927 ** connection to it. */
928 if( pCkpter ){
929 pDb->pCkpter = pCkpter;
932 sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb->pEnv, SQLITE4_MUTEX_STATIC_KV));
933 sqlite4_buffer_clear(&info.output);
934 return rc;
937 static int bgc_detach(BtDb *pDb){
938 int rc = SQLITE4_OK;
939 bt_ckpter *pCkpter = pDb->pCkpter;
940 if( pCkpter ){
941 int bShutdown = 0; /* True if this is the last reference */
943 sqlite4_mutex_enter(sqlite4_mutex_alloc(pDb->pEnv,SQLITE4_MUTEX_STATIC_KV));
944 pCkpter->nRef--;
945 if( pCkpter->nRef==0 ){
946 bt_ckpter **pp;
948 *pp = pCkpter->pNext;
949 for(pp=&gBgc.pCkpter; *pp!=pCkpter; pp=&((*pp)->pNext));
950 bShutdown = 1;
952 sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb->pEnv,SQLITE4_MUTEX_STATIC_KV));
954 if( bShutdown ){
955 void *pDummy;
957 /* Signal the checkpointer thread. */
958 pthread_mutex_lock(&pCkpter->ckpter_mutex);
959 pCkpter->bDoWork = 1;
960 pthread_cond_signal(&pCkpter->ckpter_cond);
961 pthread_mutex_unlock(&pCkpter->ckpter_mutex);
963 /* Join the checkpointer thread. */
964 pthread_join(pCkpter->ckpter_thread, &pDummy);
965 pthread_cond_destroy(&pCkpter->ckpter_cond);
966 pthread_mutex_destroy(&pCkpter->ckpter_mutex);
968 sqlite4_buffer_clear(&pCkpter->file);
969 sqlite4_buffer_clear(&pCkpter->spec);
970 testFree(pCkpter);
973 pDb->pCkpter = 0;
975 return rc;
979 ** End of background checkpointer.
980 *************************************************************************/