3 ** This file contains the TestDb bt wrapper.
6 #include "lsmtest_tdb.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.
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 */
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.
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.
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().
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 */
62 /* Background checkpointer used by mt connections */
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(
78 BtDb
*pBt
= (BtDb
*)pVfs
->pVfsCtx
;
79 if( pBt
->bCrash
) return SQLITE4_IOERR
;
80 return pBt
->pVfs
->xFullpath(pEnv
, pBt
->pVfs
, z
, pzOut
);
87 int flags
, bt_file
**ppFile
90 BtDb
*pBt
= (BtDb
*)pVfs
->pVfsCtx
;
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
){
99 }else if( flags
& BT_OPEN_LOG
){
102 if( (flags
& BT_OPEN_SHARED
)==0 ){
107 rc
= pBt
->pVfs
->xOpen(pEnv
, pVfs
, zFile
, flags
, &p
->pFile
);
108 if( rc
!=SQLITE4_OK
){
115 *ppFile
= (bt_file
*)p
;
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
){
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
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
);
153 int iOpt
= (testPrngValue(i
) % 3) + 1;
155 aWrite
= p
->apSector
[i
];
157 if( aTmp
==0 ) aTmp
= testMalloc(p
->nSectorSize
);
159 testPrngArray(i
*13, (u32
*)aWrite
, nWrite
/sizeof(u32
));
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"
171 rc
= p
->pBt
->pVfs
->xWrite(p
->pFile
, iSOff
, aWrite
, nWrite
);
175 testFree(p
->apSector
[i
]);
183 static int btSaveSectors(BtFile
*p
, sqlite4_int64 iOff
, int nBuf
){
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
++){
200 sqlite4_int64 iSOff
= iSector
* p
->nSectorSize
;
201 u8
*aBuf
= testMalloc(p
->nSectorSize
);
202 nRead
= MIN(p
->nSectorSize
, (iSz
- iSOff
));
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
);
216 p
->apSector
[iSector
] = aBuf
;
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
){
239 BtFile
*p
= (BtFile
*)pFile
;
243 if( pBt
->bCrash
) return SQLITE4_IOERR
;
244 if( pBt
->nCrashSync
){
246 pBt
->bCrash
= (pBt
->nCrashSync
==0);
248 btFlushSectors(pBt
->apFile
[0], 0);
249 btFlushSectors(pBt
->apFile
[1], 1);
252 btFlushSectors(p
, 0);
257 if( rc
==SQLITE4_OK
){
258 rc
= p
->pVfs
->xSync(p
->pFile
);
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
){
270 assert( p
->nRef
>=0 );
271 if( p
->nRef
<=0 ) testFree(p
);
274 static int btVfsClose(bt_file
*pFile
){
275 BtFile
*p
= (BtFile
*)pFile
;
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
);
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
);
331 if( p
->apFile
[0] ) p
->apFile
[0]->pBt
= 0;
332 if( p
->apFile
[1] ) p
->apFile
[1]->pBt
= 0;
338 static int btMinTransaction(BtDb
*p
, int iMin
, int *piLevel
){
342 iLevel
= sqlite4BtTransactionLevel(p
->pBt
);
344 rc
= sqlite4BtBegin(p
->pBt
, iMin
);
352 static int btRestoreTransaction(BtDb
*p
, int iLevel
, int rcin
){
355 if( rc
==SQLITE4_OK
){
356 rc
= sqlite4BtCommit(p
->pBt
, iLevel
);
358 sqlite4BtRollback(p
->pBt
, iLevel
);
360 assert( iLevel
==sqlite4BtTransactionLevel(p
->pBt
) );
365 static int bt_write(TestDb
*pTestDb
, void *pK
, int nK
, void *pV
, int nV
){
366 BtDb
*p
= (BtDb
*)pTestDb
;
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
);
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(
385 void *pKey1
, int nKey1
,
386 void *pKey2
, int nKey2
388 BtDb
*p
= (BtDb
*)pTestDb
;
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
){
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
);
438 void **ppVal
, int *pnVal
440 BtDb
*p
= (BtDb
*)pTestDb
;
445 iLevel
= sqlite4BtTransactionLevel(p
->pBt
);
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
){
458 rc
= sqlite4BtCsrData(pCsr
, 0, -1, &pV
, &nV
);
459 if( rc
==SQLITE4_OK
){
462 p
->aBuffer
= (u8
*)malloc(nV
*2);
465 memcpy(p
->aBuffer
, pV
, nV
);
467 *ppVal
= (void*)(p
->aBuffer
);
470 }else if( rc
==SQLITE4_INEXACT
|| rc
==SQLITE4_NOTFOUND
){
475 sqlite4BtCsrClose(pCsr
);
478 if( iLevel
==0 ) sqlite4BtCommit(p
->pBt
, 0);
486 void *pFirst
, int nFirst
,
487 void *pLast
, int nLast
,
488 void (*xCallback
)(void *, void *, int , void *, int)
490 BtDb
*p
= (BtDb
*)pTestDb
;
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
){
504 rc
= sqlite4BtCsrSeek(pCsr
, pLast
, nLast
, BT_SEEK_LE
);
506 rc
= sqlite4BtCsrLast(pCsr
);
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;
526 int nCmp
= MIN(nK
, nFirst
);
527 res
= memcmp(pFirst
, pK
, nCmp
);
528 if( res
>0 || (res
==0 && nK
<nFirst
) ) break;
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
);
541 rc
= sqlite4BtCsrPrev(pCsr
);
543 rc
= sqlite4BtCsrNext(pCsr
);
546 if( rc
==SQLITE4_NOTFOUND
) rc
= SQLITE4_OK
;
548 sqlite4BtCsrClose(pCsr
);
551 rc
= btRestoreTransaction(p
, iLevel
, rc
);
555 static int bt_begin(TestDb
*pTestDb
, int iLvl
){
556 BtDb
*p
= (BtDb
*)pTestDb
;
557 int rc
= sqlite4BtBegin(p
->pBt
, iLvl
);
561 static int bt_commit(TestDb
*pTestDb
, int iLvl
){
562 BtDb
*p
= (BtDb
*)pTestDb
;
563 int rc
= sqlite4BtCommit(p
->pBt
, iLvl
);
567 static int bt_rollback(TestDb
*pTestDb
, int iLvl
){
568 BtDb
*p
= (BtDb
*)pTestDb
;
569 int rc
= sqlite4BtRollback(p
->pBt
, iLvl
);
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
;
585 while( *p
==' ' ) p
++;
587 while( *p
&& *p
!='=' ) p
++;
588 if( *p
==0 ) return 1;
591 memcpy(pOut
, pStart
, n
);
598 while( *p
&& *p
!=' ' ) p
++;
601 memcpy(pOut
, pStart
, n
);
610 static int testParseInt(const char *z
, int *piVal
){
614 while( *p
>='0' && *p
<='9' ){
615 i
= i
*10 + (*p
- '0');
618 if( *p
=='K' || *p
=='k' ){
621 }else if( *p
=='M' || *p
=='m' ){
626 if( *p
) return SQLITE4_ERROR
;
631 static int testBtConfigure(BtDb
*pDb
, const char *zCfg
, int *pbMt
){
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
},
645 { "fastinsert", -2 },
648 const char *z
= zCfg
;
654 aSpace
= (char*)testMalloc(n
+2);
655 while( rc
==SQLITE4_OK
&& 0==testParseOption(&z
, &zOpt
, &zArg
, aSpace
) ){
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
){
669 pDb
->bFastInsert
= 1;
672 rc
= sqlite4BtControl(pDb
->pBt
, aParam
[i
].eParam
, (void*)&iVal
);
685 const char *zFilename
,
690 static const DatabaseMethods SqlMethods
= {
704 sqlite4_env
*pEnv
= sqlite4_env_default();
706 if( bClear
&& zFilename
&& zFilename
[0] ){
707 char *zLog
= sqlite3_mprintf("%s-wal", zFilename
);
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
;
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
){
750 rc
= bgc_attach(p
, zSpec
);
751 sqlite4BtControl(pBt
, BT_CONTROL_AUTOCKPT
, (void*)&nAuto
);
755 if( rc
!=SQLITE4_OK
&& p
){
765 const char *zFilename
,
769 return test_bt_open("fast=1", zFilename
, bClear
, ppDb
);
774 const char *zFilename
,
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
;
796 /*************************************************************************
797 ** Beginning of code for background checkpointer.
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 */
818 static void *bgc_main(void *pArg
){
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
;
832 sqlite4BtBegin(db
, 1);
833 sqlite4BtCommit(db
, 0);
834 sqlite4BtControl(db
, BT_CONTROL_LOGSIZE
, (void*)&nLog
);
836 if( nLog
>=pCkpter
->nLogsize
){
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
);
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
);
866 pthread_cond_signal(&p
->ckpter_cond
);
867 pthread_mutex_unlock(&p
->ckpter_mutex
);
871 static int bgc_attach(BtDb
*pDb
, const char *zSpec
){
877 /* Figure out the full path to the database opened by handle pDb. */
878 info
.eType
= BT_INFO_FILENAME
;
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. */
888 for(pCkpter
=gBgc
.pCkpter
; pCkpter
; pCkpter
=pCkpter
->pNext
){
889 if( n
==pCkpter
->file
.n
&& 0==memcmp(info
.output
.p
, pCkpter
->file
.p
, n
) ){
894 /* Failed to find a suitable checkpointer. Create a new one. */
898 pCkpter
= testMalloc(sizeof(bt_ckpter
));
899 memcpy(&pCkpter
->file
, &info
.output
, sizeof(sqlite4_buffer
));
901 pCkpter
->pNext
= gBgc
.pCkpter
;
902 pCkpter
->nLogsize
= 1000;
903 gBgc
.pCkpter
= pCkpter
;
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);
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
);
926 /* Assuming a checkpointer was encountered or effected, attach the
927 ** connection to it. */
929 pDb
->pCkpter
= pCkpter
;
932 sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb
->pEnv
, SQLITE4_MUTEX_STATIC_KV
));
933 sqlite4_buffer_clear(&info
.output
);
937 static int bgc_detach(BtDb
*pDb
){
939 bt_ckpter
*pCkpter
= pDb
->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
));
945 if( pCkpter
->nRef
==0 ){
948 *pp
= pCkpter
->pNext
;
949 for(pp
=&gBgc
.pCkpter
; *pp
!=pCkpter
; pp
=&((*pp
)->pNext
));
952 sqlite4_mutex_leave(sqlite4_mutex_alloc(pDb
->pEnv
,SQLITE4_MUTEX_STATIC_KV
));
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
);
979 ** End of background checkpointer.
980 *************************************************************************/