aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2007-08-20 22:48:41 +0000
committerdrh <drh@noemail.net>2007-08-20 22:48:41 +0000
commitd677b3d688ae9e0f4bde5599369b8074278ba3c3 (patch)
tree10af779ae865361152bb5658e9e94d090bca3c73
parent7e3c77df0de7a9efa63ee675a80bb8b241f78421 (diff)
downloadsqlite-d677b3d688ae9e0f4bde5599369b8074278ba3c3.tar.gz
sqlite-d677b3d688ae9e0f4bde5599369b8074278ba3c3.zip
Work toward multithreading support. Currently crashes quickly on a test. (CVS 4253)
FossilOrigin-Name: 1315bd8e125602275fb718780f9b2730bd37f6ab
-rw-r--r--main.mk4
-rw-r--r--manifest54
-rw-r--r--manifest.uuid2
-rw-r--r--src/btree.c542
-rw-r--r--src/btree.h10
-rw-r--r--src/btreeInt.h14
-rw-r--r--src/main.c8
-rw-r--r--src/mem2.c30
-rw-r--r--src/mutex.c28
-rw-r--r--src/os.c100
-rw-r--r--src/os_os2.h4
-rw-r--r--src/os_unix.c22
-rw-r--r--src/random.c4
-rw-r--r--src/sqlite.h.in51
-rw-r--r--src/sqliteInt.h31
-rw-r--r--src/test1.c6
-rw-r--r--src/test2.c6
-rw-r--r--src/test4.c4
-rw-r--r--src/test6.c30
-rw-r--r--src/test7.c6
-rw-r--r--src/test_async.c13
-rw-r--r--src/test_config.c10
-rw-r--r--src/test_server.c6
23 files changed, 761 insertions, 224 deletions
diff --git a/main.mk b/main.mk
index 004be61ae..43ffe004f 100644
--- a/main.mk
+++ b/main.mk
@@ -281,8 +281,8 @@ last_change: $(SRC)
cat $(SRC) | grep '$$Id: ' | sort -k 5 | tail -1 \
| $(NAWK) '{print $$5,$$6}' >last_change
-libsqlite3.a: $(LIBOBJ) $(EXTOBJ)
- $(AR) libsqlite3.a $(LIBOBJ) $(EXTOBJ)
+libsqlite3.a: $(LIBOBJ)
+ $(AR) libsqlite3.a $(LIBOBJ)
$(RANLIB) libsqlite3.a
sqlite3$(EXE): $(TOP)/src/shell.c libsqlite3.a sqlite3.h
diff --git a/manifest b/manifest
index ca173b5a1..fd85a2f2d 100644
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Oops\s-\sa\srandom\sfts2\stest\sI\shad\sabandoned\sslipped\sinto\sthe\sfts3\sbatch.\s(CVS\s4252)
-D 2007-08-20T17:53:00
+C Work\stoward\smultithreading\ssupport.\s\sCurrently\scrashes\squickly\son\sa\stest.\s(CVS\s4253)
+D 2007-08-20T22:48:42
F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe
F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499
F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -63,7 +63,7 @@ F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33
F ext/icu/icu.c 61a345d8126686aa3487aa8d2d0f68abd655f7a4
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895
F ltmain.sh 56abb507100ed2d4261f6dd1653dec3cf4066387
-F main.mk e90dcb16ce27c6d8d8ed18335c4cc03f5a5fed0b
+F main.mk b1d97fe47a0633bc2ee5a09c7fdf7f4105156437
F mkdll.sh 37fa8a7412e51b5ab2bc6d4276135f022a0feffb
F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f
F mkextw.sh 1a866b53637dab137191341cc875575a5ca110fb
@@ -80,9 +80,9 @@ F src/alter.c f0aac0060ae8102e58f210b44d35b53438d53173
F src/analyze.c a14237d869c6bea0846493b59317e4097e81a0b6
F src/attach.c a52225c75b107be8c5bc144a2b6d20201be3f8f8
F src/auth.c 5ea90bc93dfea46e9fe4bf531e14c7cd98219ecb
-F src/btree.c f8a04f35eb81360773899983f7c2008145e13935
-F src/btree.h 91ee529d581c1473d8e6e15299acc3b8de1d0674
-F src/btreeInt.h 9b4ca8999e52f713420e5f297dd86887a7a9820f
+F src/btree.c ae710a86a5ee2f27fbcba34033e4f048bd97cde9
+F src/btree.h 525105564c87111922412368f2e4301c36e74ac1
+F src/btreeInt.h e93edf57832278138b98cf60cbc54241103c6988
F src/build.c add67be992307b4b11849a6611bfd3352aacde92
F src/callback.c 143436453bb93e831c9574fea0b9b9eb90e40ff3
F src/complete.c ea63834e798a0ab14159bdc6e6cabc3df21aa346
@@ -97,20 +97,20 @@ F src/insert.c 633322aef1799f6604fa805e12488bc628570b0c
F src/legacy.c 6013a7cb7da1b72550b3d35d4fc598b3c3e5b8c1
F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35
F src/loadext.c c0ccda3dbda109da087a8fd762deebe5fdf24a1d
-F src/main.c 685aa31a7af0d650c94e6968d713c3a9374aa72e
+F src/main.c 7310dd532c1ff751772b196c98f7e0534afd7e58
F src/malloc.c 613c65f12ff0ee4edd017aa458209ab7a23cd7b1
F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217
F src/mem1.c 30bf8be3846f92fdf88c490c5e5378512383bcbe
-F src/mem2.c 661ca7ebf6e4b964fecc95d24e8c89dbcfc9dfea
-F src/mutex.c 67b2efd36a1e67a7dc7b7fa852fd69953462c943
-F src/os.c 68c46a16bcd47aa3f9418a01dc2812b1ff2c4c17
+F src/mem2.c 482f0aaf14e8ef1db64cb8c5b9a9bfe708297c92
+F src/mutex.c 60051a86c49cfe2ec15dd8b1d9d0d530b8893b07
+F src/os.c c9f91550afa071c5b98042ce14d38a2d87c3f4df
F src/os.h da098cad985b4849fefdd6a96d671b332008aa55
F src/os_common.h a5c446d3b93f09f369d13bf217de4bed3437dd1c
F src/os_os2.c cba4e96fadb949076c717108fe0599d1a3c2e446
-F src/os_os2.h e5f17dd69333632bbc3112881ea407c37d245eb3
+F src/os_os2.h c3f7d0af7e3453d1d7aa81b06c0a56f5a226530b
F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 10fb60a8d9174dcb609d72f6b97211c58da25b47
+F src/os_unix.c 41a737d14d00b93fae8efd0d0555922f07a29ada
F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
F src/os_win.c d868d5f9e95ec9c1b9e2a30c54c996053db6dddd
F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
@@ -120,35 +120,35 @@ F src/parse.y c03529c3b82702ada98ce405b390e3a9409708cf
F src/pragma.c 8f5e37c3cf6dbdeb3645bb80cc58cfc3324c0178
F src/prepare.c 03292beeffce2d65debab12508a8ec1f5aec7241
F src/printf.c a8f46e0ed360c18d40e89aa636533be300b406c2
-F src/random.c 00b30565f018f3a256c157432935de070231c73b
+F src/random.c af7264b4ed93330b3bf40dd123e16e7f8f62e5bb
F src/select.c 98c367bce3f38c5adfcc97de9ab5c79b0e5dc2b2
F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96
F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb
-F src/sqlite.h.in a0baef0f4c969a4eb9dfc9096bf527ca543485e5
+F src/sqlite.h.in 09d8d2780c2c25dedbcfdfcafc39e8b213fc8432
F src/sqlite3ext.h 647a6b8a8f76ff6c9611e4a071531d8e63ff2d6b
-F src/sqliteInt.h 8932f2a2f855e5ce566ab3805002f074eb56ae11
+F src/sqliteInt.h 6891d6864a6b9fc661eadaa0328e81dff7523584
F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa
F src/table.c c725e47f6f3092b9a7b569fc58e408e2173ee008
F src/tclsqlite.c 0606c4f31711492eb4d7480a981eebb80914f3d9
-F src/test1.c a226ab03048491aa6c5d43d26097df96bdb162e7
-F src/test2.c 016380989929fd41bdf790058b1f2fd698f8af28
+F src/test1.c 6ae17d70dac0f14ab632f66c026a44fb0c71f22e
+F src/test2.c 4506b6635e193a19a9bccdbe366ff4aed09cbe79
F src/test3.c b87e8fcce45e1d3153aae9f04236076b7707a714
-F src/test4.c d22cb3ab4f9fdfd0a595b70d5328cee923b7322c
+F src/test4.c 1f4d2ed89867bac187d49ed8004f121592987d3e
F src/test5.c 7bc8a87c2b6fd076ec2ca9972946e71a367883ad
-F src/test6.c d67117b2c1df9b07889f3f3779ecc9ec2663fd1e
-F src/test7.c 91d914c2c2b2806157213f41f4185ad3a4970c07
+F src/test6.c b0090b4826d5e06df2ff2d5acaddf3f8f708fcd2
+F src/test7.c 0f9d91ce8018740d5eb6e258f0fac2a2943c40f0
F src/test8.c 719c284607c1e91a893f5425df1e92b74c859aef
F src/test9.c c0f38f7795cc51d37db6c63874d90f40f10d0f0e
-F src/test_async.c 871ffbe4a520be74b403aca87aa622ebdb690232
+F src/test_async.c ba48913e4bab43b149747d3bdd3b75b1f66658ec
F src/test_autoext.c 855157d97aa28cf84233847548bfacda21807436
F src/test_btree.c c1308ba0b88ab577fa56c9e493a09829dfcded9c
-F src/test_config.c 1d0da26fd51757765f2073c57a925db8ddc7120c
+F src/test_config.c ddced28aedfa324292005d991d74b595d76a838a
F src/test_hexio.c 82916f918687502658f02533b519c38cb180db6d
F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8
F src/test_malloc.c d9ba6be85f9c4a439b19f6e0a72d91c369d72c63
F src/test_md5.c d9f828765b242ff86f58cd879259c3da4eaede02
F src/test_schema.c 12c9de7661d6294eec2d57afbb52e2af1128084f
-F src/test_server.c 76c0baf509abe65ca6e5c7974ab0097cfdd8b833
+F src/test_server.c 319f6b1a99bab5f7149387442243d6e65a8ab4eb
F src/test_tclvar.c 160290ba5c19964da7cb97defc6c104712074b69
F src/tokenize.c 3a3fd71cfb2abb8e11ed6ab6b764b790c534defc
F src/trigger.c dccc6fbf37d12193c90ede5b026bbd195d505ff4
@@ -557,7 +557,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130
F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5
-P 24739f148e7687532fd04794a041aade8626c630
-R 91022a585940fe2f4f8ce2cc48394d34
-U shess
-Z 50354b12eb6453839bc699aa3eacaa47
+P 709f2aa18a4802a7ca6638c00b2b99440b4a3191
+R dff2f29b0cfe4e6e9309aeb129fa4ba9
+U drh
+Z b2675de4c3469ebcd0e4f20229838975
diff --git a/manifest.uuid b/manifest.uuid
index f401abc2b..3bf1833f5 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-709f2aa18a4802a7ca6638c00b2b99440b4a3191 \ No newline at end of file
+1315bd8e125602275fb718780f9b2730bd37f6ab \ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index d5710b71b..0f665f46f 100644
--- a/src/btree.c
+++ b/src/btree.c
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btree.c,v 1.402 2007/08/20 13:14:29 drh Exp $
+** $Id: btree.c,v 1.403 2007/08/20 22:48:42 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
@@ -23,7 +23,6 @@
*/
static const char zMagicHeader[] = SQLITE_FILE_HEADER;
-
/*
** Set this global variable to 1 to enable tracing using the TRACE
** macro.
@@ -36,7 +35,9 @@ int sqlite3_btree_trace=0; /* True to enable tracing */
/*
** A flag to indicate whether or not shared cache is enabled. Also,
** a list of BtShared objects that are eligible for participation
-** in shared cache.
+** in shared cache. The variables have file scope during normal builds,
+** but the test harness needs to access these variables so make them
+** global for test builds.
*/
#ifdef SQLITE_TEST
BtShared *sqlite3SharedCacheList = 0;
@@ -45,7 +46,6 @@ int sqlite3SharedCacheEnabled = 0;
static BtShared *sqlite3SharedCacheList = 0;
static int sqlite3SharedCacheEnabled = 0;
#endif
-
#endif /* SQLITE_OMIT_SHARED_CACHE */
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -62,6 +62,7 @@ int sqlite3_enable_shared_cache(int enable){
}
#endif
+
/*
** Forward declaration
*/
@@ -93,6 +94,8 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){
BtShared *pBt = p->pBt;
BtLock *pIter;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+
/* This is a no-op if the shared-cache is not enabled */
if( !p->sharable ){
return SQLITE_OK;
@@ -143,6 +146,8 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
BtLock *pLock = 0;
BtLock *pIter;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+
/* This is a no-op if the shared-cache is not enabled */
if( !p->sharable ){
return SQLITE_OK;
@@ -207,6 +212,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){
static void unlockAllTables(Btree *p){
BtLock **ppIter = &p->pBt->pLock;
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
assert( p->sharable || 0==*ppIter );
while( *ppIter ){
@@ -238,6 +244,7 @@ static void invalidateOverflowCache(BtCursor *pCur){
*/
static void invalidateAllOverflowCache(BtShared *pBt){
BtCursor *p;
+ assert( sqlite3_mutex_held(pBt->mutex) );
for(p=pBt->pCursor; p; p=p->pNext){
invalidateOverflowCache(p);
}
@@ -256,6 +263,7 @@ static int saveCursorPosition(BtCursor *pCur){
assert( CURSOR_VALID==pCur->eState );
assert( 0==pCur->pKey );
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
rc = sqlite3BtreeKeySize(pCur, &pCur->nKey);
@@ -297,6 +305,7 @@ static int saveCursorPosition(BtCursor *pCur){
*/
static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
BtCursor *p;
+ assert( sqlite3_mutex_held(pBt->mutex) );
for(p=pBt->pCursor; p; p=p->pNext){
if( p!=pExcept && (0==iRoot || p->pgnoRoot==iRoot) &&
p->eState==CURSOR_VALID ){
@@ -313,6 +322,7 @@ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){
** Clear the current cursor position.
*/
static void clearCursorPosition(BtCursor *pCur){
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
sqlite3_free(pCur->pKey);
pCur->pKey = 0;
pCur->eState = CURSOR_INVALID;
@@ -359,9 +369,11 @@ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){
** input page number.
*/
static Pgno ptrmapPageno(BtShared *pBt, Pgno pgno){
- int nPagesPerMapPage = (pBt->usableSize/5)+1;
- int iPtrMap = (pgno-2)/nPagesPerMapPage;
- int ret = (iPtrMap*nPagesPerMapPage) + 2;
+ int nPagesPerMapPage, iPtrMap, ret;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ nPagesPerMapPage = (pBt->usableSize/5)+1;
+ iPtrMap = (pgno-2)/nPagesPerMapPage;
+ ret = (iPtrMap*nPagesPerMapPage) + 2;
if( ret==PENDING_BYTE_PAGE(pBt) ){
ret++;
}
@@ -382,6 +394,7 @@ static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){
int offset; /* Offset in pointer map page */
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
/* The master-journal page number must never be used as a pointer map page */
assert( 0==PTRMAP_ISPAGE(pBt, PENDING_BYTE_PAGE(pBt)) );
@@ -424,6 +437,8 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){
int offset; /* Offset of entry in pointer map */
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+
iPtrmap = PTRMAP_PAGENO(pBt, key);
rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage);
if( rc!=0 ){
@@ -466,6 +481,7 @@ u8 *sqlite3BtreeFindCell(MemPage *pPage, int iCell){
*/
static u8 *findOverflowCell(MemPage *pPage, int iCell){
int i;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
for(i=pPage->nOverflow-1; i>=0; i--){
int k;
struct _OvflCell *pOvfl;
@@ -498,6 +514,8 @@ void sqlite3BtreeParseCellPtr(
int n; /* Number bytes in cell content header */
u32 nPayload; /* Number of bytes of cell payload */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
pInfo->pCell = pCell;
assert( pPage->leaf==0 || pPage->leaf==1 );
n = pPage->childPtrSize;
@@ -610,6 +628,7 @@ static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){
*/
static int ptrmapPutOvfl(MemPage *pPage, int iCell){
u8 *pCell;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pCell = findOverflowCell(pPage, iCell);
return ptrmapPutOvflPtr(pPage, pCell);
}
@@ -639,6 +658,7 @@ static int defragmentPage(MemPage *pPage){
assert( pPage->pBt!=0 );
assert( pPage->pBt->usableSize <= SQLITE_MAX_PAGE_SIZE );
assert( pPage->nOverflow==0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
temp = sqlite3_malloc( pPage->pBt->pageSize );
if( temp==0 ) return SQLITE_NOMEM;
data = pPage->aData;
@@ -695,6 +715,7 @@ static int allocateSpace(MemPage *pPage, int nByte){
data = pPage->aData;
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( pPage->pBt );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( nByte<4 ) nByte = 4;
if( pPage->nFree<nByte || pPage->nOverflow>0 ) return 0;
pPage->nFree -= nByte;
@@ -753,6 +774,7 @@ static void freeSpace(MemPage *pPage, int start, int size){
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) );
assert( (start + size)<=pPage->pBt->usableSize );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( size<4 ) size = 4;
#ifdef SQLITE_SECURE_DELETE
@@ -813,6 +835,7 @@ static void decodeFlags(MemPage *pPage, int flagByte){
BtShared *pBt; /* A copy of pPage->pBt */
assert( pPage->hdrOffset==(pPage->pgno==1 ? 100 : 0) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pPage->intKey = (flagByte & (PTF_INTKEY|PTF_LEAFDATA))!=0;
pPage->zeroData = (flagByte & PTF_ZERODATA)!=0;
pPage->leaf = (flagByte & PTF_LEAF)!=0;
@@ -859,6 +882,7 @@ int sqlite3BtreeInitPage(
pBt = pPage->pBt;
assert( pBt!=0 );
assert( pParent==0 || pParent->pBt==pBt );
+ assert( sqlite3_mutex_held(pBt->mutex) );
assert( pPage->pgno==sqlite3PagerPagenumber(pPage->pDbPage) );
assert( pPage->aData == &((unsigned char*)pPage)[-pBt->pageSize] );
if( pPage->pParent!=pParent && (pPage->pParent!=0 || pPage->isInit) ){
@@ -929,6 +953,7 @@ static void zeroPage(MemPage *pPage, int flags){
assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno );
assert( &data[pBt->pageSize] == (unsigned char*)pPage );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3_mutex_held(pBt->mutex) );
memset(&data[hdr], 0, pBt->usableSize - hdr);
data[hdr] = flags;
first = hdr + 8 + 4*((flags&PTF_LEAF)==0);
@@ -966,6 +991,7 @@ int sqlite3BtreeGetPage(
MemPage *pPage;
DbPage *pDbPage;
+ assert( sqlite3_mutex_held(pBt->mutex) );
rc = sqlite3PagerAcquire(pBt->pPager, pgno, (DbPage**)&pDbPage, noContent);
if( rc ) return rc;
pPage = (MemPage *)sqlite3PagerGetExtra(pDbPage);
@@ -990,6 +1016,7 @@ static int getAndInitPage(
MemPage *pParent /* Parent of the page */
){
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno==0 ){
return SQLITE_CORRUPT_BKPT;
}
@@ -1009,6 +1036,7 @@ static void releasePage(MemPage *pPage){
assert( pPage->aData );
assert( pPage->pBt );
assert( &pPage->aData[pPage->pBt->pageSize]==(unsigned char*)pPage );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
sqlite3PagerUnref(pPage->pDbPage);
}
}
@@ -1022,6 +1050,7 @@ static void pageDestructor(DbPage *pData, int pageSize){
MemPage *pPage;
assert( (pageSize & 7)==0 );
pPage = (MemPage *)sqlite3PagerGetExtra(pData);
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->pParent ){
MemPage *pParent = pPage->pParent;
pPage->pParent = 0;
@@ -1042,6 +1071,7 @@ static void pageReinit(DbPage *pData, int pageSize){
MemPage *pPage;
assert( (pageSize & 7)==0 );
pPage = (MemPage *)sqlite3PagerGetExtra(pData);
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->isInit ){
pPage->isInit = 0;
sqlite3BtreeInitPage(pPage, pPage->pParent);
@@ -1063,13 +1093,20 @@ int sqlite3BtreeOpen(
Btree **ppBtree, /* Pointer to new Btree object written here */
int flags /* Options */
){
- sqlite3_vfs *pVfs = (pSqlite?pSqlite->pVfs:sqlite3_find_vfs(0));
+ sqlite3_vfs *pVfs; /* The VFS to use for this btree */
BtShared *pBt = 0; /* Shared part of btree structure */
Btree *p; /* Handle to return */
int rc = SQLITE_OK;
int nReserve;
unsigned char zDbHeader[100];
+ if( pSqlite ){
+ pVfs = pSqlite->pVfs;
+ }else{
+ pVfs = sqlite3_vfs_find(0);
+ }
+ assert( pSqlite==0 || sqlite3_mutex_held(pSqlite->mutex) );
+
/* Set the variable isMemdb to true for an in-memory database, or
** false for a file-based database. This symbol is only required if
** either of the shared-data or autovacuum features are compiled
@@ -1261,6 +1298,7 @@ static int removeFromSharingList(BtShared *pBt){
BtShared *pList;
int removed = 0;
+ assert( sqlite3_mutex_notheld(pBt->mutex) );
pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
sqlite3_mutex_enter(pMaster);
pBt->nRef--;
@@ -1345,7 +1383,7 @@ int sqlite3BtreeClose(Btree *p){
return SQLITE_OK;
}
-#ifndef SQLITE_OMIT_SHARED_CACHE
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
/*
** Enter a mutex on the given BTree object.
**
@@ -1379,6 +1417,9 @@ void sqlite3BtreeEnter(Btree *p){
assert( !p->locked || p->wantToLock>0 );
assert( p->sharable || p->wantToLock==0 );
+ /* We should already hold a lock on the database connection */
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+
if( !p->sharable ) return;
p->wantToLock++;
if( p->locked ) return;
@@ -1419,7 +1460,7 @@ void sqlite3BtreeEnter(Btree *p){
/*
** Exit the recursive mutex on a Btree.
*/
-#ifndef SQLITE_OMIT_SHARED_CACHE
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
void sqlite3BtreeLeave(Btree *p){
if( p->sharable ){
assert( p->wantToLock>0 );
@@ -1431,6 +1472,22 @@ void sqlite3BtreeLeave(Btree *p){
}
}
}
+#endif
+
+
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
+/*
+** Short-cuts for entering and leaving mutexes on a cursor.
+*/
+static void cursorLeave(BtCursor *p){
+ sqlite3BtreeLeave(p->pBt);
+}
+static void cursorEnter(BtCursor *pCur){
+ sqlite3BtreeEnter(pCur->pBt);
+}
+#else
+# define cursorEnter(X)
+# define cursorLeave(X)
#endif /* !SQLITE_OMIT_SHARED_CACHE */
/*
@@ -1438,8 +1495,10 @@ void sqlite3BtreeLeave(Btree *p){
*/
int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
pBt->pBusyHandler = pHandler;
sqlite3PagerSetBusyhandler(pBt->pPager, pHandler);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -1460,7 +1519,9 @@ int sqlite3BtreeSetBusyHandler(Btree *p, BusyHandler *pHandler){
*/
int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
sqlite3PagerSetCachesize(pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -1475,7 +1536,9 @@ int sqlite3BtreeSetCacheSize(Btree *p, int mxPage){
#ifndef SQLITE_OMIT_PAGER_PRAGMAS
int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
sqlite3PagerSetSafetyLevel(pBt->pPager, level, fullSync);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
#endif
@@ -1486,8 +1549,12 @@ int sqlite3BtreeSetSafetyLevel(Btree *p, int level, int fullSync){
*/
int sqlite3BtreeSyncDisabled(Btree *p){
BtShared *pBt = p->pBt;
+ int rc;
assert( pBt && pBt->pPager );
- return sqlite3PagerNosync(pBt->pPager);
+ sqlite3BtreeEnter(p);
+ rc = sqlite3PagerNosync(pBt->pPager);
+ sqlite3BtreeLeave(p);
+ return rc;
}
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM)
@@ -1508,7 +1575,9 @@ int sqlite3BtreeSyncDisabled(Btree *p){
*/
int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
if( pBt->pageSizeFixed ){
+ sqlite3BtreeLeave(p);
return SQLITE_READONLY;
}
if( nReserve<0 ){
@@ -1521,6 +1590,7 @@ int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve){
pBt->pageSize = sqlite3PagerSetPagesize(pBt->pPager, pageSize);
}
pBt->usableSize = pBt->pageSize - nReserve;
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -1531,7 +1601,11 @@ int sqlite3BtreeGetPageSize(Btree *p){
return p->pBt->pageSize;
}
int sqlite3BtreeGetReserve(Btree *p){
- return p->pBt->pageSize - p->pBt->usableSize;
+ int n;
+ sqlite3BtreeEnter(p);
+ n = p->pBt->pageSize - p->pBt->usableSize;
+ sqlite3BtreeLeave(p);
+ return n;
}
/*
@@ -1540,7 +1614,11 @@ int sqlite3BtreeGetReserve(Btree *p){
** Regardless of the value of mxPage, return the maximum page count.
*/
int sqlite3BtreeMaxPageCount(Btree *p, int mxPage){
- return sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+ int n;
+ sqlite3BtreeEnter(p);
+ n = sqlite3PagerMaxPageCount(p->pBt->pPager, mxPage);
+ sqlite3BtreeLeave(p);
+ return n;
}
#endif /* !defined(SQLITE_OMIT_PAGER_PRAGMAS) || !defined(SQLITE_OMIT_VACUUM) */
@@ -1555,12 +1633,17 @@ int sqlite3BtreeSetAutoVacuum(Btree *p, int autoVacuum){
return SQLITE_READONLY;
#else
BtShared *pBt = p->pBt;
+ int rc = SQLITE_OK;
int av = (autoVacuum?1:0);
+
+ sqlite3BtreeEnter(p);
if( pBt->pageSizeFixed && av!=pBt->autoVacuum ){
- return SQLITE_READONLY;
+ rc = SQLITE_READONLY;
+ }else{
+ pBt->autoVacuum = av;
}
- pBt->autoVacuum = av;
- return SQLITE_OK;
+ sqlite3BtreeLeave(p);
+ return rc;
#endif
}
@@ -1572,11 +1655,15 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
#ifdef SQLITE_OMIT_AUTOVACUUM
return BTREE_AUTOVACUUM_NONE;
#else
- return (
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = (
(!p->pBt->autoVacuum)?BTREE_AUTOVACUUM_NONE:
(!p->pBt->incrVacuum)?BTREE_AUTOVACUUM_FULL:
BTREE_AUTOVACUUM_INCR
);
+ sqlite3BtreeLeave(p);
+ return rc;
#endif
}
@@ -1593,6 +1680,8 @@ int sqlite3BtreeGetAutoVacuum(Btree *p){
static int lockBtree(BtShared *pBt){
int rc, pageSize;
MemPage *pPage1;
+
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pBt->pPage1 ) return SQLITE_OK;
rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0);
if( rc!=SQLITE_OK ) return rc;
@@ -1668,6 +1757,9 @@ page1_init_failed:
*/
static int lockBtreeWithRetry(Btree *pRef){
int rc = SQLITE_OK;
+
+ assert( sqlite3_mutex_held(pRef->pSqlite->mutex) );
+ assert( sqlite3_mutex_held(pRef->pBt->mutex) );
if( pRef->inTrans==TRANS_NONE ){
u8 inTransaction = pRef->pBt->inTransaction;
btreeIntegrity(pRef);
@@ -1694,6 +1786,7 @@ static int lockBtreeWithRetry(Btree *pRef){
** If there is a transaction in progress, this routine is a no-op.
*/
static void unlockBtreeIfUnused(BtShared *pBt){
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){
if( sqlite3PagerRefcount(pBt->pPager)>=1 ){
if( pBt->pPage1->aData==0 ){
@@ -1717,6 +1810,8 @@ static int newDatabase(BtShared *pBt){
MemPage *pP1;
unsigned char *data;
int rc;
+
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( sqlite3PagerPagecount(pBt->pPager)>0 ) return SQLITE_OK;
pP1 = pBt->pPage1;
assert( pP1!=0 );
@@ -1783,6 +1878,7 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
BtShared *pBt = p->pBt;
int rc = SQLITE_OK;
+ sqlite3BtreeEnter(p);
btreeIntegrity(p);
/* If the btree is already in a write-transaction, or it
@@ -1790,12 +1886,13 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
** is requested, this is a no-op.
*/
if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
- return SQLITE_OK;
+ goto trans_begun;
}
/* Write transactions are not possible on a read-only database */
if( pBt->readOnly && wrflag ){
- return SQLITE_READONLY;
+ rc = SQLITE_READONLY;
+ goto trans_begun;
}
/* If another database handle has already opened a write transaction
@@ -1803,7 +1900,8 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
** requested, return SQLITE_BUSY.
*/
if( pBt->inTransaction==TRANS_WRITE && wrflag ){
- return SQLITE_BUSY;
+ rc = SQLITE_BUSY;
+ goto trans_begun;
}
do {
@@ -1840,7 +1938,10 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
}
}
+
+trans_begun:
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -1859,6 +1960,7 @@ static int setChildPtrmaps(MemPage *pPage){
int isInitOrig = pPage->isInit;
Pgno pgno = pPage->pgno;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
rc = sqlite3BtreeInitPage(pPage, pPage->pParent);
if( rc!=SQLITE_OK ){
goto set_child_ptrmaps_out;
@@ -1906,6 +2008,7 @@ set_child_ptrmaps_out:
** overflow page in the list.
*/
static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( eType==PTRMAP_OVERFLOW2 ){
/* The pointer is always the first 4 bytes of the page in this case. */
if( get4byte(pPage->aData)!=iFrom ){
@@ -1971,6 +2074,7 @@ static int relocatePage(
assert( eType==PTRMAP_OVERFLOW2 || eType==PTRMAP_OVERFLOW1 ||
eType==PTRMAP_BTREE || eType==PTRMAP_ROOTPAGE );
+ assert( sqlite3_mutex_held(pBt->mutex) );
/* Move page iDbPage from it's current location to page number iFreePage */
TRACE(("AUTOVACUUM: Moving %d to free page %d (ptr page %d type %d)\n",
@@ -2049,6 +2153,7 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
Pgno iLastPg; /* Last page in the database */
Pgno nFreeList; /* Number of pages still on the free-list */
+ assert( sqlite3_mutex_held(pBt->mutex) );
iLastPg = pBt->nTrunc;
if( iLastPg==0 ){
iLastPg = sqlite3PagerPagecount(pBt->pPager);
@@ -2143,13 +2248,19 @@ static int incrVacuumStep(BtShared *pBt, Pgno nFin){
** SQLITE_OK is returned. Otherwise an SQLite error code.
*/
int sqlite3BtreeIncrVacuum(Btree *p){
+ int rc;
BtShared *pBt = p->pBt;
+
+ sqlite3BtreeEnter(p);
assert( pBt->inTransaction==TRANS_WRITE && p->inTrans==TRANS_WRITE );
if( !pBt->autoVacuum ){
- return SQLITE_DONE;
+ rc = SQLITE_DONE;
+ }else{
+ invalidateAllOverflowCache(pBt);
+ rc = incrVacuumStep(pBt, 0);
}
- invalidateAllOverflowCache(pBt);
- return incrVacuumStep(pBt, 0);
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -2168,6 +2279,7 @@ static int autoVacuumCommit(BtShared *pBt, Pgno *pnTrunc){
int nRef = sqlite3PagerRefcount(pPager);
#endif
+ assert( sqlite3_mutex_held(pBt->mutex) );
invalidateAllOverflowCache(pBt);
assert(pBt->autoVacuum);
if( !pBt->incrVacuum ){
@@ -2255,15 +2367,18 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
if( p->inTrans==TRANS_WRITE ){
BtShared *pBt = p->pBt;
Pgno nTrunc = 0;
+ sqlite3BtreeEnter(p);
#ifndef SQLITE_OMIT_AUTOVACUUM
if( pBt->autoVacuum ){
rc = autoVacuumCommit(pBt, &nTrunc);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
}
#endif
rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc);
+ sqlite3BtreeLeave(p);
}
return rc;
}
@@ -2285,6 +2400,7 @@ int sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
int sqlite3BtreeCommitPhaseTwo(Btree *p){
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
btreeIntegrity(p);
/* If the handle has a write-transaction open, commit the shared-btrees
@@ -2296,6 +2412,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
assert( pBt->nTransaction>0 );
rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
pBt->inTransaction = TRANS_READ;
@@ -2322,6 +2439,7 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
unlockBtreeIfUnused(pBt);
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return SQLITE_OK;
}
@@ -2330,10 +2448,12 @@ int sqlite3BtreeCommitPhaseTwo(Btree *p){
*/
int sqlite3BtreeCommit(Btree *p){
int rc;
+ sqlite3BtreeEnter(p);
rc = sqlite3BtreeCommitPhaseOne(p, 0);
if( rc==SQLITE_OK ){
rc = sqlite3BtreeCommitPhaseTwo(p);
}
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2367,6 +2487,7 @@ int sqlite3BtreeRollback(Btree *p){
BtShared *pBt = p->pBt;
MemPage *pPage1;
+ sqlite3BtreeEnter(p);
rc = saveAllCursors(pBt, 0, 0);
#ifndef SQLITE_OMIT_SHARED_CACHE
if( rc!=SQLITE_OK ){
@@ -2424,6 +2545,7 @@ int sqlite3BtreeRollback(Btree *p){
unlockBtreeIfUnused(pBt);
btreeIntegrity(p);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2445,12 +2567,15 @@ int sqlite3BtreeRollback(Btree *p){
int sqlite3BtreeBeginStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
if( (p->inTrans!=TRANS_WRITE) || pBt->inStmt ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else{
+ assert( pBt->inTransaction==TRANS_WRITE );
+ rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
+ pBt->inStmt = 1;
}
- assert( pBt->inTransaction==TRANS_WRITE );
- rc = pBt->readOnly ? SQLITE_OK : sqlite3PagerStmtBegin(pBt->pPager);
- pBt->inStmt = 1;
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2462,12 +2587,14 @@ int sqlite3BtreeBeginStmt(Btree *p){
int sqlite3BtreeCommitStmt(Btree *p){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
if( pBt->inStmt && !pBt->readOnly ){
rc = sqlite3PagerStmtCommit(pBt->pPager);
}else{
rc = SQLITE_OK;
}
pBt->inStmt = 0;
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2482,6 +2609,7 @@ int sqlite3BtreeCommitStmt(Btree *p){
int sqlite3BtreeRollbackStmt(Btree *p){
int rc = SQLITE_OK;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
sqlite3MallocDisallow();
if( pBt->inStmt && !pBt->readOnly ){
rc = sqlite3PagerStmtRollback(pBt->pPager);
@@ -2489,6 +2617,7 @@ int sqlite3BtreeRollbackStmt(Btree *p){
pBt->inStmt = 0;
}
sqlite3MallocAllow();
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -2542,7 +2671,7 @@ static int dfltCompare(
** default comparison function is used. The comparison function is
** always ignored for INTKEY tables.
*/
-int sqlite3BtreeCursor(
+static int btreeCursor(
Btree *p, /* The btree */
int iTable, /* Root page of table to open */
int wrFlag, /* 1 to write. 0 read-only */
@@ -2554,6 +2683,7 @@ int sqlite3BtreeCursor(
BtCursor *pCur;
BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
*ppCur = 0;
if( wrFlag ){
if( pBt->readOnly ){
@@ -2605,6 +2735,7 @@ int sqlite3BtreeCursor(
*ppCur = pCur;
return SQLITE_OK;
+
create_cursor_exception:
if( pCur ){
releasePage(pCur->pPage);
@@ -2613,6 +2744,21 @@ create_cursor_exception:
unlockBtreeIfUnused(pBt);
return rc;
}
+int sqlite3BtreeCursor(
+ Btree *p, /* The btree */
+ int iTable, /* Root page of table to open */
+ int wrFlag, /* 1 to write. 0 read-only */
+ int (*xCmp)(void*,int,const void*,int,const void*), /* Key Comparison func */
+ void *pArg, /* First arg to xCompare() */
+ BtCursor **ppCur /* Write new cursor here */
+){
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = btreeCursor(p, iTable, wrFlag, xCmp, pArg, ppCur);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
+
/*
** Close a cursor. The read lock on the database file is released
@@ -2620,6 +2766,8 @@ create_cursor_exception:
*/
int sqlite3BtreeCloseCursor(BtCursor *pCur){
BtShared *pBt = pCur->pBtree->pBt;
+
+ cursorEnter(pCur);
clearCursorPosition(pCur);
if( pCur->pPrev ){
pCur->pPrev->pNext = pCur->pNext;
@@ -2632,6 +2780,7 @@ int sqlite3BtreeCloseCursor(BtCursor *pCur){
releasePage(pCur->pPage);
unlockBtreeIfUnused(pBt);
invalidateOverflowCache(pCur);
+ cursorLeave(pCur);
sqlite3_free(pCur);
return SQLITE_OK;
}
@@ -2645,7 +2794,9 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
pTempCur->pNext = 0;
pTempCur->pPrev = 0;
if( pTempCur->pPage ){
+ cursorEnter(pCur);
sqlite3PagerRef(pTempCur->pPage->pDbPage);
+ cursorLeave(pCur);
}
}
@@ -2655,7 +2806,9 @@ void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){
*/
void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
if( pCur->pPage ){
+ cursorEnter(pCur);
sqlite3PagerUnref(pCur->pPage->pDbPage);
+ cursorLeave(pCur);
}
}
@@ -2712,7 +2865,10 @@ void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){
** itself, not the number of bytes in the key.
*/
int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2722,6 +2878,7 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
*pSize = pCur->info.nKey;
}
}
+ sqlite3BtreeLeave(pCur->pBtree);
return rc;
}
@@ -2733,7 +2890,10 @@ int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
** the database is empty) then *pSize is set to 0.
*/
int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
if( pCur->eState==CURSOR_INVALID ){
@@ -2744,6 +2904,7 @@ int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){
*pSize = pCur->info.nData;
}
}
+ sqlite3BtreeLeave(pCur->pBtree);
return rc;
}
@@ -2773,6 +2934,7 @@ static int getOverflowPage(
Pgno next = 0;
int rc;
+ assert( sqlite3_mutex_held(pBt->mutex) );
/* One of these must not be NULL. Otherwise, why call this function? */
assert(ppPage || pPgnoNext);
@@ -2912,6 +3074,7 @@ static int accessPayload(
assert( pCur->eState==CURSOR_VALID );
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
assert( offset>=0 );
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
getCellInfo(pCur);
aPayload = pCur->info.pCell + pCur->info.nHeader;
@@ -3035,17 +3198,22 @@ static int accessPayload(
** the available payload.
*/
int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
if( pCur->pPage->intKey ){
+ sqlite3BtreeLeave(pCur->pBtree);
return SQLITE_CORRUPT_BKPT;
}
assert( pCur->pPage->intKey==0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
rc = accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0, 0);
}
+ sqlite3BtreeLeave(pCur->pBtree);
return rc;
}
@@ -3059,13 +3227,17 @@ int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
** the available payload.
*/
int sqlite3BtreeData(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){
- int rc = restoreOrClearCursorPosition(pCur);
+ int rc;
+
+ sqlite3BtreeEnter(pCur->pBtree);
+ rc = restoreOrClearCursorPosition(pCur);
if( rc==SQLITE_OK ){
assert( pCur->eState==CURSOR_VALID );
assert( pCur->pPage!=0 );
assert( pCur->idx>=0 && pCur->idx<pCur->pPage->nCell );
rc = accessPayload(pCur, offset, amt, pBuf, 1, 0);
}
+ sqlite3BtreeLeave(pCur->pBtree);
return rc;
}
@@ -3100,6 +3272,7 @@ static const unsigned char *fetchPayload(
assert( pCur!=0 && pCur->pPage!=0 );
assert( pCur->eState==CURSOR_VALID );
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
pPage = pCur->pPage;
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
getCellInfo(pCur);
@@ -3130,18 +3303,23 @@ static const unsigned char *fetchPayload(
** b-tree page. Write the number of available bytes into *pAmt.
**
** The pointer returned is ephemeral. The key/data may move
-** or be destroyed on the next call to any Btree routine.
+** or be destroyed on the next call to any Btree routine,
+** including calls from other threads against the same cache.
+** Hence, a mutex on the BtShared should be held prior to calling
+** this routine.
**
** These routines is used to get quick access to key and data
** in the common case where no overflow pages are used.
*/
const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 0);
}
return 0;
}
const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
if( pCur->eState==CURSOR_VALID ){
return (const void*)fetchPayload(pCur, pAmt, 1);
}
@@ -3159,6 +3337,7 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
MemPage *pOldPage;
BtShared *pBt = pCur->pBtree->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
assert( pCur->eState==CURSOR_VALID );
rc = getAndInitPage(pBt, newPgno, &pNewPage, pCur->pPage);
if( rc ) return rc;
@@ -3185,7 +3364,10 @@ static int moveToChild(BtCursor *pCur, u32 newPgno){
** 1 is pointing to.
*/
int sqlite3BtreeIsRootPage(MemPage *pPage){
- MemPage *pParent = pPage->pParent;
+ MemPage *pParent;
+
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+ pParent = pPage->pParent;
if( pParent==0 ) return 1;
if( pParent->pgno>1 ) return 0;
if( get2byte(&pParent->aData[pParent->hdrOffset+3])==0 ) return 1;
@@ -3205,6 +3387,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
MemPage *pPage;
int idxParent;
+ sqlite3BtreeEnter(pCur->pBtree);
assert( pCur->eState==CURSOR_VALID );
pPage = pCur->pPage;
assert( pPage!=0 );
@@ -3218,6 +3401,7 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
pCur->info.nSize = 0;
assert( pParent->idxShift==0 );
pCur->idx = idxParent;
+ sqlite3BtreeLeave(pCur->pBtree);
}
/*
@@ -3226,8 +3410,11 @@ void sqlite3BtreeMoveToParent(BtCursor *pCur){
static int moveToRoot(BtCursor *pCur){
MemPage *pRoot;
int rc = SQLITE_OK;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pCur->eState==CURSOR_REQUIRESEEK ){
clearCursorPosition(pCur);
}
@@ -3267,17 +3454,18 @@ static int moveToRoot(BtCursor *pCur){
*/
static int moveToLeftmost(BtCursor *pCur){
Pgno pgno;
- int rc;
+ int rc = SQLITE_OK;
MemPage *pPage;
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
assert( pCur->eState==CURSOR_VALID );
- while( !(pPage = pCur->pPage)->leaf ){
+ while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
assert( pCur->idx>=0 && pCur->idx<pPage->nCell );
pgno = get4byte(findCell(pPage, pCur->idx));
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
}
- return SQLITE_OK;
+ return rc;
}
/*
@@ -3292,18 +3480,21 @@ static int moveToLeftmost(BtCursor *pCur){
*/
static int moveToRightmost(BtCursor *pCur){
Pgno pgno;
- int rc;
+ int rc = SQLITE_OK;
MemPage *pPage;
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
assert( pCur->eState==CURSOR_VALID );
- while( !(pPage = pCur->pPage)->leaf ){
+ while( rc==SQLITE_OK && !(pPage = pCur->pPage)->leaf ){
pgno = get4byte(&pPage->aData[pPage->hdrOffset+8]);
pCur->idx = pPage->nCell;
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
}
- pCur->idx = pPage->nCell - 1;
- pCur->info.nSize = 0;
+ if( rc==SQLITE_OK ){
+ pCur->idx = pPage->nCell - 1;
+ pCur->info.nSize = 0;
+ }
return SQLITE_OK;
}
@@ -3313,16 +3504,21 @@ static int moveToRightmost(BtCursor *pCur){
*/
int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
int rc;
+
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
rc = moveToRoot(pCur);
- if( rc ) return rc;
- if( pCur->eState==CURSOR_INVALID ){
- assert( pCur->pPage->nCell==0 );
- *pRes = 1;
- return SQLITE_OK;
+ if( rc==SQLITE_OK ){
+ if( pCur->eState==CURSOR_INVALID ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ rc = SQLITE_OK;
+ }else{
+ assert( pCur->pPage->nCell>0 );
+ *pRes = 0;
+ rc = moveToLeftmost(pCur);
+ }
}
- assert( pCur->pPage->nCell>0 );
- *pRes = 0;
- rc = moveToLeftmost(pCur);
return rc;
}
@@ -3332,16 +3528,20 @@ int sqlite3BtreeFirst(BtCursor *pCur, int *pRes){
*/
int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
int rc;
+
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
rc = moveToRoot(pCur);
- if( rc ) return rc;
- if( CURSOR_INVALID==pCur->eState ){
- assert( pCur->pPage->nCell==0 );
- *pRes = 1;
- return SQLITE_OK;
+ if( rc==SQLITE_OK ){
+ if( CURSOR_INVALID==pCur->eState ){
+ assert( pCur->pPage->nCell==0 );
+ *pRes = 1;
+ }else{
+ assert( pCur->eState==CURSOR_VALID );
+ *pRes = 0;
+ rc = moveToRightmost(pCur);
+ }
}
- assert( pCur->eState==CURSOR_VALID );
- *pRes = 0;
- rc = moveToRightmost(pCur);
return rc;
}
@@ -3371,6 +3571,7 @@ int sqlite3BtreeLast(BtCursor *pCur, int *pRes){
**
** *pRes>0 The cursor is left pointing at an entry that
** is larger than pKey.
+**
*/
int sqlite3BtreeMoveto(
BtCursor *pCur, /* The cursor to be moved */
@@ -3380,8 +3581,13 @@ int sqlite3BtreeMoveto(
int *pRes /* Search result flag */
){
int rc;
+
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
rc = moveToRoot(pCur);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
assert( pCur->pPage );
assert( pCur->pPage->isInit );
if( pCur->eState==CURSOR_INVALID ){
@@ -3435,7 +3641,9 @@ int sqlite3BtreeMoveto(
rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
sqlite3_free(pCellKey);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
}
}
if( c==0 ){
@@ -3482,6 +3690,7 @@ int sqlite3BtreeMoveto(
/* NOT REACHED */
}
+
/*
** Return TRUE if the cursor is not pointing at an entry of the table.
**
@@ -3503,7 +3712,7 @@ int sqlite3BtreeEof(BtCursor *pCur){
** was already pointing to the last entry in the database before
** this routine was called, then set *pRes=1.
*/
-int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+static int btreeNext(BtCursor *pCur, int *pRes){
int rc;
MemPage *pPage;
@@ -3541,6 +3750,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
if( sqlite3BtreeIsRootPage(pPage) ){
*pRes = 1;
pCur->eState = CURSOR_INVALID;
+ cursorLeave(pCur);
return SQLITE_OK;
}
sqlite3BtreeMoveToParent(pCur);
@@ -3561,6 +3771,14 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
rc = moveToLeftmost(pCur);
return rc;
}
+int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
+ int rc;
+ cursorEnter(pCur);
+ rc = btreeNext(pCur, pRes);
+ cursorLeave(pCur);
+ return rc;
+}
+
/*
** Step the cursor to the back to the previous entry in the database. If
@@ -3568,7 +3786,7 @@ int sqlite3BtreeNext(BtCursor *pCur, int *pRes){
** was already pointing to the first entry in the database before
** this routine was called, then set *pRes=1.
*/
-int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+static int btreePrevious(BtCursor *pCur, int *pRes){
int rc;
Pgno pgno;
MemPage *pPage;
@@ -3594,7 +3812,9 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
if( !pPage->leaf ){
pgno = get4byte( findCell(pPage, pCur->idx) );
rc = moveToChild(pCur, pgno);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
rc = moveToRightmost(pCur);
}else{
while( pCur->idx==0 ){
@@ -3617,6 +3837,13 @@ int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
*pRes = 0;
return rc;
}
+int sqlite3BtreePrevious(BtCursor *pCur, int *pRes){
+ int rc;
+ cursorEnter(pCur);
+ rc = btreePrevious(pCur, pRes);
+ cursorLeave(pCur);
+ return rc;
+}
/*
** Allocate a new page from the database file.
@@ -3653,6 +3880,7 @@ static int allocateBtreePage(
MemPage *pTrunk = 0;
MemPage *pPrevTrunk = 0;
+ assert( sqlite3_mutex_held(pBt->mutex) );
pPage1 = pBt->pPage1;
n = get4byte(&pPage1->aData[36]);
if( n>0 ){
@@ -3885,6 +4113,7 @@ static int freePage(MemPage *pPage){
int rc, n, k;
/* Prepare the page for freeing */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
assert( pPage->pgno>1 );
pPage->isInit = 0;
releasePage(pPage->pParent);
@@ -3967,6 +4196,7 @@ static int clearCell(MemPage *pPage, unsigned char *pCell){
int nOvfl;
int ovflPageSize;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
sqlite3BtreeParseCellPtr(pPage, pCell, &info);
if( info.iOverflow==0 ){
return SQLITE_OK; /* No overflow pages. Return without doing anything */
@@ -4023,6 +4253,8 @@ static int fillInCell(
int nHeader;
CellInfo info;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
/* Fill in the header. */
nHeader = 0;
if( !pPage->leaf ){
@@ -4135,6 +4367,7 @@ static int reparentPage(BtShared *pBt, Pgno pgno, MemPage *pNewParent, int idx){
MemPage *pThis;
DbPage *pDbPage;
+ assert( sqlite3_mutex_held(pBt->mutex) );
assert( pNewParent!=0 );
if( pgno==0 ) return SQLITE_OK;
assert( pBt->pPager!=0 );
@@ -4178,6 +4411,7 @@ static int reparentChildPages(MemPage *pPage){
BtShared *pBt = pPage->pBt;
int rc = SQLITE_OK;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->leaf ) return SQLITE_OK;
for(i=0; i<pPage->nCell; i++){
@@ -4212,6 +4446,7 @@ static void dropCell(MemPage *pPage, int idx, int sz){
assert( idx>=0 && idx<pPage->nCell );
assert( sz==cellSize(pPage, idx) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
data = pPage->aData;
ptr = &data[pPage->cellOffset + 2*idx];
pc = get2byte(ptr);
@@ -4265,6 +4500,7 @@ static int insertCell(
assert( i>=0 && i<=pPage->nCell+pPage->nOverflow );
assert( sz==cellSizePtr(pPage, pCell) );
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->nOverflow || sz+2>pPage->nFree ){
if( pTemp ){
memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip);
@@ -4339,6 +4575,7 @@ static void assemblePage(
u8 *data; /* Data for the page */
assert( pPage->nOverflow==0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
totalSize = 0;
for(i=0; i<nCell; i++){
totalSize += aSize[i];
@@ -4413,6 +4650,8 @@ static int balance_quick(MemPage *pPage, MemPage *pParent){
int parentSize; /* Size of new divider cell */
u8 parentCell[64]; /* Space for the new divider cell */
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
/* Allocate a new page. Insert the overflow cell from pPage
** into it. Then remove the overflow cell from pPage.
*/
@@ -4537,6 +4776,8 @@ static int balance_nonroot(MemPage *pPage){
u8 *aFrom = 0;
#endif
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
+
/*
** Find the parent page.
*/
@@ -5084,6 +5325,7 @@ static int balance_shallower(MemPage *pPage){
assert( pPage->pParent==0 );
assert( pPage->nCell==0 );
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
pBt = pPage->pBt;
mxCellPerPage = MX_CELL(pBt);
apCell = sqlite3_malloc( mxCellPerPage*(sizeof(u8*)+sizeof(int)) );
@@ -5188,6 +5430,7 @@ static int balance_deeper(MemPage *pPage){
assert( pPage->pParent==0 );
assert( pPage->nOverflow>0 );
pBt = pPage->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
rc = allocateBtreePage(pBt, &pChild, &pgnoChild, pPage->pgno, 0);
if( rc ) return rc;
assert( sqlite3PagerIswriteable(pChild->pDbPage) );
@@ -5236,6 +5479,7 @@ balancedeeper_out:
*/
static int balance(MemPage *pPage, int insert){
int rc = SQLITE_OK;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
if( pPage->pParent==0 ){
if( pPage->nOverflow>0 ){
rc = balance_deeper(pPage);
@@ -5272,6 +5516,8 @@ static int checkReadLocks(Btree *pBtree, Pgno pgnoRoot, BtCursor *pExclude){
BtCursor *p;
BtShared *pBt = pBtree->pBt;
sqlite3 *db = pBtree->pSqlite;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( sqlite3_mutex_held(db->mutex) );
for(p=pBt->pCursor; p; p=p->pNext){
if( p==pExclude ) continue;
if( p->eState!=CURSOR_VALID ) continue;
@@ -5309,19 +5555,25 @@ int sqlite3BtreeInsert(
int loc;
int szNew;
MemPage *pPage;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
unsigned char *oldCell;
unsigned char *newCell = 0;
+ sqlite3BtreeEnter(p);
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction before doing an insert */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ sqlite3BtreeLeave(p);
+ return rc;
}
assert( !pBt->readOnly );
if( !pCur->wrFlag ){
+ sqlite3BtreeLeave(p);
return SQLITE_PERM; /* Cursor not open for writing */
}
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+ sqlite3BtreeLeave(p);
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
@@ -5331,6 +5583,7 @@ int sqlite3BtreeInsert(
SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) ||
SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc))
){
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5342,7 +5595,10 @@ int sqlite3BtreeInsert(
loc==0 ? "overwrite" : "new entry"));
assert( pPage->isInit );
rc = sqlite3PagerWrite(pPage->pDbPage);
- if( rc ) return rc;
+ if( rc ){
+ sqlite3BtreeLeave(p);
+ return rc;
+ }
newCell = sqlite3_malloc( MX_CELL_SIZE(pBt) );
if( newCell==0 ) return SQLITE_NOMEM;
rc = fillInCell(pPage, newCell, pKey, nKey, pData, nData, nZero, &szNew);
@@ -5377,6 +5633,7 @@ int sqlite3BtreeInsert(
}
end_insert:
sqlite3_free(newCell);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5389,21 +5646,28 @@ int sqlite3BtreeDelete(BtCursor *pCur){
unsigned char *pCell;
int rc;
Pgno pgnoChild = 0;
- BtShared *pBt = pCur->pBtree->pBt;
+ Btree *p = pCur->pBtree;
+ BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
assert( pPage->isInit );
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction before doing a delete */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ sqlite3BtreeLeave(p);
+ return rc;
}
assert( !pBt->readOnly );
if( pCur->idx >= pPage->nCell ){
+ sqlite3BtreeLeave(p);
return SQLITE_ERROR; /* The cursor is not pointing to anything */
}
if( !pCur->wrFlag ){
+ sqlite3BtreeLeave(p);
return SQLITE_PERM; /* Did not open this cursor for writing */
}
if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){
+ sqlite3BtreeLeave(p);
return SQLITE_LOCKED; /* The table pCur points to has a read lock */
}
@@ -5417,6 +5681,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur))!=0 ||
(rc = sqlite3PagerWrite(pPage->pDbPage))!=0
){
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5429,7 +5694,10 @@ int sqlite3BtreeDelete(BtCursor *pCur){
pgnoChild = get4byte(pCell);
}
rc = clearCell(pPage, pCell);
- if( rc ) return rc;
+ if( rc ){
+ sqlite3BtreeLeave(p);
+ return rc;
+ }
if( !pPage->leaf ){
/*
@@ -5486,6 +5754,7 @@ int sqlite3BtreeDelete(BtCursor *pCur){
if( rc==SQLITE_OK ){
moveToRoot(pCur);
}
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5500,20 +5769,24 @@ int sqlite3BtreeDelete(BtCursor *pCur){
** BTREE_INTKEY|BTREE_LEAFDATA Used for SQL tables with rowid keys
** BTREE_ZERODATA Used for SQL indices
*/
-int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+static int btreeCreateTable(Btree *p, int *piTable, int flags){
BtShared *pBt = p->pBt;
MemPage *pRoot;
Pgno pgnoRoot;
int rc;
+
if( pBt->inTransaction!=TRANS_WRITE ){
/* Must start a transaction first */
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ return rc;
}
assert( !pBt->readOnly );
#ifdef SQLITE_OMIT_AUTOVACUUM
rc = allocateBtreePage(pBt, &pRoot, &pgnoRoot, 1, 0);
- if( rc ) return rc;
+ if( rc ){
+ return rc;
+ }
#else
if( pBt->autoVacuum ){
Pgno pgnoMove; /* Move a page here to make room for the root-page */
@@ -5531,7 +5804,9 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
** created so far, so the new root-page is (meta[3]+1).
*/
rc = sqlite3BtreeGetMeta(p, 4, &pgnoRoot);
- if( rc!=SQLITE_OK ) return rc;
+ if( rc!=SQLITE_OK ){
+ return rc;
+ }
pgnoRoot++;
/* The new root-page may not be allocated on a pointer-map page, or the
@@ -5624,6 +5899,13 @@ int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
*piTable = (int)pgnoRoot;
return SQLITE_OK;
}
+int sqlite3BtreeCreateTable(Btree *p, int *piTable, int flags){
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = btreeCreateTable(p, piTable, flags);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
/*
** Erase the given database page and all its children. Return
@@ -5640,6 +5922,7 @@ static int clearDatabasePage(
unsigned char *pCell;
int i;
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( pgno>sqlite3PagerPagecount(pBt->pPager) ){
return SQLITE_CORRUPT_BKPT;
}
@@ -5682,20 +5965,18 @@ cleardatabasepage_out:
int sqlite3BtreeClearTable(Btree *p, int iTable){
int rc;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
if( p->inTrans!=TRANS_WRITE ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
- }
- rc = checkReadLocks(p, iTable, 0);
- if( rc ){
- return rc;
- }
-
- /* Save the position of all cursors open on this table */
- if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
- return rc;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else if( (rc = checkReadLocks(p, iTable, 0))!=SQLITE_OK ){
+ /* nothing to do */
+ }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){
+ /* nothing to do */
+ }else{
+ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
}
-
- return clearDatabasePage(pBt, (Pgno)iTable, 0, 0);
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -5718,11 +5999,12 @@ int sqlite3BtreeClearTable(Btree *p, int iTable){
** The last root page is recorded in meta[3] and the value of
** meta[3] is updated by this procedure.
*/
-int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+static int btreeDropTable(Btree *p, int iTable, int *piMoved){
int rc;
MemPage *pPage = 0;
BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
if( p->inTrans!=TRANS_WRITE ){
return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
}
@@ -5824,6 +6106,13 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
}
return rc;
}
+int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
+ int rc;
+ sqlite3BtreeEnter(p);
+ rc = btreeDropTable(p, iTable, piMoved);
+ sqlite3BtreeLeave(p);
+ return rc;
+}
/*
@@ -5842,6 +6131,8 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
unsigned char *pP1;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
+
/* Reading a meta-data value requires a read-lock on page 1 (and hence
** the sqlite_master table. We grab this lock regardless of whether or
** not the SQLITE_ReadUncommitted flag is set (the table rooted at page
@@ -5849,12 +6140,16 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
*/
rc = queryTableLock(p, 1, READ_LOCK);
if( rc!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return rc;
}
assert( idx>=0 && idx<=15 );
rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage);
- if( rc ) return rc;
+ if( rc ){
+ sqlite3BtreeLeave(p);
+ return rc;
+ }
pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage);
*pMeta = get4byte(&pP1[36 + idx*4]);
sqlite3PagerUnref(pDbPage);
@@ -5868,6 +6163,7 @@ int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
/* Grab the read-lock on page 1. */
rc = lockTable(p, 1, READ_LOCK);
+ sqlite3BtreeLeave(p);
return rc;
}
@@ -5880,20 +6176,24 @@ int sqlite3BtreeUpdateMeta(Btree *p, int idx, u32 iMeta){
unsigned char *pP1;
int rc;
assert( idx>=1 && idx<=15 );
+ sqlite3BtreeEnter(p);
if( p->inTrans!=TRANS_WRITE ){
- return pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
- }
- assert( pBt->pPage1!=0 );
- pP1 = pBt->pPage1->aData;
- rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
- if( rc ) return rc;
- put4byte(&pP1[36 + idx*4], iMeta);
- if( idx==7 ){
- assert( pBt->autoVacuum || iMeta==0 );
- assert( iMeta==0 || iMeta==1 );
- pBt->incrVacuum = iMeta;
+ rc = pBt->readOnly ? SQLITE_READONLY : SQLITE_ERROR;
+ }else{
+ assert( pBt->pPage1!=0 );
+ pP1 = pBt->pPage1->aData;
+ rc = sqlite3PagerWrite(pBt->pPage1->pDbPage);
+ if( rc==SQLITE_OK ){
+ put4byte(&pP1[36 + idx*4], iMeta);
+ if( idx==7 ){
+ assert( pBt->autoVacuum || iMeta==0 );
+ assert( iMeta==0 || iMeta==1 );
+ pBt->incrVacuum = iMeta;
+ }
+ }
}
- return SQLITE_OK;
+ sqlite3BtreeLeave(p);
+ return rc;
}
/*
@@ -5905,6 +6205,7 @@ int sqlite3BtreeFlags(BtCursor *pCur){
** restoreOrClearCursorPosition() here.
*/
MemPage *pPage = pCur->pPage;
+ assert( sqlite3_mutex_held(pPage->pBt->mutex) );
return pPage ? pPage->aData[pPage->hdrOffset] : 0;
}
@@ -5914,6 +6215,8 @@ int sqlite3BtreeFlags(BtCursor *pCur){
** testing and debugging only.
*/
Pager *sqlite3BtreePager(Btree *p){
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
return p->pBt->pPager;
}
@@ -6260,8 +6563,10 @@ char *sqlite3BtreeIntegrityCheck(
IntegrityCk sCheck;
BtShared *pBt = p->pBt;
+ sqlite3BtreeEnter(p);
nRef = sqlite3PagerRefcount(pBt->pPager);
if( lockBtreeWithRetry(p)!=SQLITE_OK ){
+ sqlite3BtreeLeave(p);
return sqlite3StrDup("Unable to acquire a read lock on the database");
}
sCheck.pBt = pBt;
@@ -6277,12 +6582,14 @@ char *sqlite3BtreeIntegrityCheck(
#endif
if( sCheck.nPage==0 ){
unlockBtreeIfUnused(pBt);
+ sqlite3BtreeLeave(p);
return 0;
}
sCheck.anRef = sqlite3_malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) );
if( !sCheck.anRef ){
unlockBtreeIfUnused(pBt);
*pnErr = 1;
+ sqlite3BtreeLeave(p);
return sqlite3MPrintf(p->pSqlite, "Unable to malloc %d bytes",
(sCheck.nPage+1)*sizeof(sCheck.anRef[0]));
}
@@ -6344,6 +6651,7 @@ char *sqlite3BtreeIntegrityCheck(
/* Clean up and report errors.
*/
+ sqlite3BtreeLeave(p);
sqlite3_free(sCheck.anRef);
*pnErr = sCheck.nErr;
return sCheck.zErrMsg;
@@ -6355,6 +6663,7 @@ char *sqlite3BtreeIntegrityCheck(
*/
const char *sqlite3BtreeGetFilename(Btree *p){
assert( p->pBt->pPager!=0 );
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
return sqlite3PagerFilename(p->pBt->pPager);
}
@@ -6363,6 +6672,7 @@ const char *sqlite3BtreeGetFilename(Btree *p){
*/
const char *sqlite3BtreeGetDirname(Btree *p){
assert( p->pBt->pPager!=0 );
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
return sqlite3PagerDirname(p->pBt->pPager);
}
@@ -6373,6 +6683,7 @@ const char *sqlite3BtreeGetDirname(Btree *p){
*/
const char *sqlite3BtreeGetJournalname(Btree *p){
assert( p->pBt->pPager!=0 );
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
return sqlite3PagerJournalname(p->pBt->pPager);
}
@@ -6384,7 +6695,7 @@ const char *sqlite3BtreeGetJournalname(Btree *p){
** The size of file pBtFrom may be reduced by this operation.
** If anything goes wrong, the transaction on pBtFrom is rolled back.
*/
-int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+static int btreeCopyFile(Btree *pTo, Btree *pFrom){
int rc = SQLITE_OK;
Pgno i, nPage, nToPage, iSkip;
@@ -6436,12 +6747,24 @@ int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
}
return rc;
}
+int sqlite3BtreeCopyFile(Btree *pTo, Btree *pFrom){
+ int rc;
+ sqlite3BtreeEnter(pTo);
+ sqlite3BtreeEnter(pFrom);
+ rc = btreeCopyFile(pTo, pFrom);
+ sqlite3BtreeLeave(pFrom);
+ sqlite3BtreeLeave(pTo);
+ return rc;
+}
+
#endif /* SQLITE_OMIT_VACUUM */
/*
** Return non-zero if a transaction is active.
*/
int sqlite3BtreeIsInTrans(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
return (p && (p->inTrans==TRANS_WRITE));
}
@@ -6449,6 +6772,8 @@ int sqlite3BtreeIsInTrans(Btree *p){
** Return non-zero if a statement transaction is active.
*/
int sqlite3BtreeIsInStmt(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
return (p->pBt && p->pBt->inStmt);
}
@@ -6456,6 +6781,8 @@ int sqlite3BtreeIsInStmt(Btree *p){
** Return non-zero if a read (or write) transaction is active.
*/
int sqlite3BtreeIsInReadTrans(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
return (p && (p->inTrans!=TRANS_NONE));
}
@@ -6477,6 +6804,8 @@ int sqlite3BtreeIsInReadTrans(Btree *p){
*/
void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
BtShared *pBt = p->pBt;
+ assert( sqlite3_mutex_held(pBt->mutex) );
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
if( !pBt->pSchema ){
pBt->pSchema = sqlite3MallocZero(nBytes);
pBt->xFreeSchema = xFree;
@@ -6489,6 +6818,8 @@ void *sqlite3BtreeSchema(Btree *p, int nBytes, void(*xFree)(void *)){
** handle holds an exclusive lock on the sqlite_master table.
*/
int sqlite3BtreeSchemaLocked(Btree *p){
+ assert( sqlite3_mutex_held(p->pBt->mutex) );
+ assert( sqlite3_mutex_held(p->pSqlite->mutex) );
return (queryTableLock(p, MASTER_ROOT, READ_LOCK)!=SQLITE_OK);
}
@@ -6502,10 +6833,12 @@ int sqlite3BtreeSchemaLocked(Btree *p){
int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
int rc = SQLITE_OK;
u8 lockType = (isWriteLock?WRITE_LOCK:READ_LOCK);
+ sqlite3BtreeEnter(p);
rc = queryTableLock(p, iTab, lockType);
if( rc==SQLITE_OK ){
rc = lockTable(p, iTab, lockType);
}
+ sqlite3BtreeLeave(p);
return rc;
}
#endif
@@ -6519,7 +6852,8 @@ int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
** to change the length of the data stored.
*/
int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
-
+ assert( sqlite3_mutex_held(pCsr->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCsr->pBtree->pSqlite->mutex) );
assert(pCsr->isIncrblobHandle);
if( pCsr->eState==CURSOR_REQUIRESEEK ){
return SQLITE_ABORT;
@@ -6556,6 +6890,8 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){
** sqlite3BtreePutData()).
*/
void sqlite3BtreeCacheOverflow(BtCursor *pCur){
+ assert( sqlite3_mutex_held(pCur->pBtree->pBt->mutex) );
+ assert( sqlite3_mutex_held(pCur->pBtree->pSqlite->mutex) );
assert(!pCur->isIncrblobHandle);
assert(!pCur->aOverflow);
pCur->isIncrblobHandle = 1;
diff --git a/src/btree.h b/src/btree.h
index d67a432ae..5f15d101b 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -13,7 +13,7 @@
** subsystem. See comments in the source code for a detailed description
** of what each interface routine does.
**
-** @(#) $Id: btree.h,v 1.83 2007/08/17 01:14:38 drh Exp $
+** @(#) $Id: btree.h,v 1.84 2007/08/20 22:48:42 drh Exp $
*/
#ifndef _BTREE_H_
#define _BTREE_H_
@@ -100,12 +100,12 @@ int sqlite3BtreeLockTable(Btree *, int, u8);
** use mutexes to access the BtShared structures. So make the
** Enter and Leave procedures no-ops.
*/
-#ifdef SQLITE_OMIT_SHARED_CACHE
-# define sqlite3BtreeEnter(X)
-# define sqlite3BtreeLeave(X)
-#else
+#if SQLITE_THREADSAFE && !defined(SQLITE_OMIT_SHARED_CACHE)
void sqlite3BtreeEnter(Btree*);
void sqlite3BtreeLeave(Btree*);
+#else
+# define sqlite3BtreeEnter(X)
+# define sqlite3BtreeLeave(X)
#endif
const char *sqlite3BtreeGetFilename(Btree *);
diff --git a/src/btreeInt.h b/src/btreeInt.h
index 343d1eb6e..6d2335870 100644
--- a/src/btreeInt.h
+++ b/src/btreeInt.h
@@ -9,7 +9,7 @@
** May you share freely, never taking more than you give.
**
*************************************************************************
-** $Id: btreeInt.h,v 1.7 2007/08/20 13:14:29 drh Exp $
+** $Id: btreeInt.h,v 1.8 2007/08/20 22:48:42 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
@@ -264,6 +264,9 @@ typedef struct BtLock BtLock;
** walk up the BTree from any leaf to the root. Care must be taken to
** unref() the parent page pointer when this page is no longer referenced.
** The pageDestructor() routine handles that chore.
+**
+** Access to all fields of this structure is controlled by the mutex
+** stored in MemPage.pBt->mutex.
*/
struct MemPage {
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
@@ -315,8 +318,7 @@ struct MemPage {
** schema associated with the database file are all contained within
** the BtShared object.
**
-** All fields in this structure are accessed under the sqlite3.pMutex
-** mutex.
+** All fields in this structure are accessed under the sqlite3.mutex.
*/
struct Btree {
sqlite3 *pSqlite; /* The database connection holding this btree */
@@ -416,9 +418,9 @@ struct CellInfo {
** but cursors cannot be shared. Each cursor is associated with a
** particular database connection identified BtCursor.pBtree.pSqlite.
**
-** The fields in this structure are accessed under the sqlite3.pMutex
-** mutex, specifically the BtCurser.pBtree->pSqlite->pMutex mutex.
-** The pNext and pPrev fields also require the BtShared.mutex mutex.
+** Fields in this structure are accessed under the BtShared.mutex
+** mutex. The pBtree field is safe to access under the
+** BtShared->pBtree->pSqlite->mutex mutex.
*/
struct BtCursor {
Btree *pBtree; /* The Btree to which this cursor belongs */
diff --git a/src/main.c b/src/main.c
index 9cb779c6c..f0bbe7da0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -14,7 +14,7 @@
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
-** $Id: main.c,v 1.388 2007/08/20 17:37:48 shess Exp $
+** $Id: main.c,v 1.389 2007/08/20 22:48:42 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -218,8 +218,8 @@ int sqlite3_close(sqlite3 *db){
** structure?
*/
sqlite3_free(db->aDb[1].pSchema);
+ sqlite3_vfs_release(db->pVfs);
sqlite3_free(db);
- /* sqlite3ReleaseThreadData(); */
return SQLITE_OK;
}
@@ -900,7 +900,7 @@ static int openDatabase(
/* Allocate the sqlite data structure */
db = sqlite3MallocZero( sizeof(sqlite3) );
if( db==0 ) goto opendb_out;
- db->pVfs = sqlite3_find_vfs(0);
+ db->pVfs = sqlite3_vfs_find(0);
db->errMask = 0xff;
db->priorNewRowid = 0;
db->magic = SQLITE_MAGIC_BUSY;
@@ -1385,7 +1385,7 @@ int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
*/
int sqlite3_sleep(int ms){
sqlite3_vfs *pVfs;
- pVfs = sqlite3_find_vfs(0);
+ pVfs = sqlite3_vfs_find(0);
/* This function works in milliseconds, but the underlying OsSleep()
** API uses microseconds. Hence the 1000's.
diff --git a/src/mem2.c b/src/mem2.c
index f9cd933b5..a88802dce 100644
--- a/src/mem2.c
+++ b/src/mem2.c
@@ -12,7 +12,7 @@
** This file contains the C functions that implement a memory
** allocation subsystem for use by SQLite.
**
-** $Id: mem2.c,v 1.4 2007/08/16 19:40:17 drh Exp $
+** $Id: mem2.c,v 1.5 2007/08/20 22:48:43 drh Exp $
*/
/*
@@ -132,6 +132,12 @@ static struct {
int iFail; /* Decrement and fail malloc when this is 1 */
int iReset; /* When malloc fails set iiFail to this value */
int iFailCnt; /* Number of failures */
+
+ /*
+ ** sqlite3MallocDisallow() increments the following counter.
+ ** sqlite3MallocAllow() decrements it.
+ */
+ int disallow; /* Do not allow memory allocation */
} mem = { /* This variable holds all of the local data */
@@ -254,6 +260,7 @@ void *sqlite3_malloc(unsigned int nByte){
mem.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM);
}
sqlite3_mutex_enter(mem.mutex);
+ assert( mem.disallow==0 );
if( mem.nowUsed+nByte>=mem.alarmThreshold ){
sqlite3MemsysAlarm(nByte);
}
@@ -367,6 +374,7 @@ void *sqlite3_realloc(void *pPrior, unsigned int nByte){
sqlite3_free(pPrior);
return 0;
}
+ assert( mem.disallow==0 );
pOldHdr = sqlite3MemsysGetHeader(pPrior);
pNew = sqlite3_malloc(nByte);
if( pNew ){
@@ -440,4 +448,24 @@ int sqlite3_memdebug_fail(int iFail, int iRepeat){
return n;
}
+/*
+** The following two routines are used to assert that no memory
+** allocations occur between one call and the next. The use of
+** these routines does not change the computed results in any way.
+** These routines are like asserts.
+*/
+void sqlite3MallocDisallow(void){
+ assert( mem.mutex!=0 );
+ sqlite3_mutex_enter(mem.mutex);
+ mem.disallow++;
+ sqlite3_mutex_leave(mem.mutex);
+}
+void sqlite3MallocAllow(void){
+ assert( mem.mutex );
+ sqlite3_mutex_enter(mem.mutex);
+ assert( mem.disallow>0 );
+ mem.disallow--;
+ sqlite3_mutex_leave(mem.mutex);
+}
+
#endif /* SQLITE_MEMDEBUG && !SQLITE_OMIT_MEMORY_ALLOCATION */
diff --git a/src/mutex.c b/src/mutex.c
index ffee6d785..619a1afc4 100644
--- a/src/mutex.c
+++ b/src/mutex.c
@@ -12,7 +12,7 @@
** This file contains the C functions that implement mutexes for
** use by the SQLite core.
**
-** $Id: mutex.c,v 1.4 2007/08/17 01:14:38 drh Exp $
+** $Id: mutex.c,v 1.5 2007/08/20 22:48:43 drh Exp $
*/
/*
@@ -41,7 +41,7 @@
** that means that a mutex could not be allocated.
*/
sqlite3_mutex *sqlite3_mutex_alloc(int idNotUsed){
- return (sqlite3_mutex*)sqlite3_mutex_alloc;
+ return (sqlite3_mutex*)8;
}
/*
@@ -64,13 +64,24 @@ void sqlite3_mutex_enter(sqlite3_mutex *pNotUsed){}
int sqlite3_mutex_try(sqlite3_mutex *pNotUsed){ return SQLITE_OK; }
/*
-** The sqlite3_mutex_exit() routine exits a mutex that was
+** The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered or
** is not currently allocated. SQLite will never do either.
*/
void sqlite3_mutex_leave(sqlite3_mutex *pNotUsed){}
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+int sqlite3_mutex_held(sqlite3_mutex *pNotUsed){
+ return 1;
+}
+int sqlite3_mutex_notheld(sqlite3_mutex *pNotUsed){
+ return 1;
+}
+
#if 0
/**************** Non-recursive Pthread Mutex Implementation *****************
**
@@ -293,6 +304,17 @@ void sqlite3_mutex_leave(sqlite3_mutex *pMutex){
pthread_mutex_unlock(&p->auxMutex);
}
}
+
+/*
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
+** intended for use inside assert() statements.
+*/
+int sqlite3_mutex_held(sqlite3_mutex *pNotUsed){
+ return 1;
+}
+int sqlite3_mutex_notheld(sqlite3_mutex *pNotUsed){
+ return 1;
+}
#endif /* non-recursive pthreads */
#endif /* !defined(SQLITE_MUTEX_APPDEF) */
diff --git a/src/os.c b/src/os.c
index 73f067bbc..aeafb26da 100644
--- a/src/os.c
+++ b/src/os.c
@@ -74,6 +74,10 @@ int sqlite3OsDeviceCharacteristics(sqlite3_file *id){
}
#endif
+/*
+** The next group of routines are convenience wrappers around the
+** VFS methods.
+*/
int sqlite3OsOpen(
sqlite3_vfs *pVfs,
const char *zPath,
@@ -81,9 +85,6 @@ int sqlite3OsOpen(
int flags,
int *pFlagsOut
){
-#if defined(SQLITE_TEST) && !defined(SQLITE_OMIT_DISKIO)
- return sqlite3CrashFileOpen(pVfs, zPath, pFile, flags, pFlagsOut);
-#endif
return pVfs->xOpen(pVfs->pAppData, zPath, pFile, flags, pFlagsOut);
}
int sqlite3OsDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
@@ -154,7 +155,96 @@ int sqlite3OsCloseFree(sqlite3_file *pFile){
*/
extern sqlite3_vfs sqlite3DefaultVfs;
-sqlite3_vfs *sqlite3_find_vfs(const char *zVfs){
- return &sqlite3DefaultVfs;
+/*
+** The list of all registered VFS implementations.
+*/
+static sqlite3_vfs *vfsList = &sqlite3DefaultVfs;
+
+/*
+** Locate a VFS by name. If no name is given, simply return the
+** first VFS on the list.
+*/
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfs){
+ sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_vfs *pVfs;
+ sqlite3_mutex_enter(mutex);
+ for(pVfs = vfsList; pVfs; pVfs=pVfs->pNext){
+ if( zVfs==0 ) break;
+ if( strcmp(zVfs, pVfs->zName)==0 ) break;
+ }
+ if( pVfs ){
+ pVfs->nRef++;
+ assert( pVfs->nRef==1 || pVfs->vfsMutex!=0 );
+ assert( pVfs->nRef>1 || pVfs->vfsMutex==0 );
+ if( pVfs->vfsMutex==0 ){
+ pVfs->vfsMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
+ }
+ }
+ sqlite3_mutex_leave(mutex);
+ return pVfs;
+}
+
+/*
+** Release a VFS once it is no longer needed.
+*/
+int sqlite3_vfs_release(sqlite3_vfs *pVfs){
+ sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(mutex);
+ assert( pVfs->nRef>0 );
+ pVfs->nRef--;
+ if( pVfs->nRef==0 ){
+ sqlite3_mutex_free(pVfs->vfsMutex);
+ pVfs->vfsMutex = 0;
+ }
+ sqlite3_mutex_leave(mutex);
+ return SQLITE_OK;
}
+/*
+** Unlink a VFS from the linked list
+*/
+static void vfsUnlink(sqlite3_vfs *pVfs){
+ assert( sqlite3_mutex_held(sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER)) );
+ if( vfsList==pVfs ){
+ vfsList = pVfs->pNext;
+ }else{
+ sqlite3_vfs *p = vfsList;
+ while( p->pNext && p->pNext!=pVfs ){
+ p = p->pNext;
+ }
+ if( p->pNext==pVfs ){
+ p->pNext = pVfs->pNext;
+ }
+ }
+}
+
+/*
+** Register a VFS with the system. It is harmless to register the same
+** VFS multiple times. The new VFS becomes the default if makeDflt is
+** true.
+*/
+int sqlite3_vfs_register(sqlite3_vfs *pVfs, int makeDflt){
+ sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(mutex);
+ vfsUnlink(pVfs);
+ if( makeDflt || vfsList==0 ){
+ pVfs->pNext = vfsList;
+ vfsList = pVfs;
+ }else{
+ pVfs->pNext = vfsList->pNext;
+ pVfs->pNext = pVfs;
+ }
+ sqlite3_mutex_leave(mutex);
+ return SQLITE_OK;
+}
+
+/*
+** Unregister a VFS so that it is no longer accessible.
+*/
+int sqlite3_vfs_unregister(sqlite3_vfs *pVfs){
+ sqlite3_mutex *mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER);
+ sqlite3_mutex_enter(mutex);
+ vfsUnlink(pVfs);
+ sqlite3_mutex_leave(mutex);
+ return SQLITE_OK;
+}
diff --git a/src/os_os2.h b/src/os_os2.h
index b7381857f..de7f385fb 100644
--- a/src/os_os2.h
+++ b/src/os_os2.h
@@ -25,12 +25,12 @@
/*
** Macros used to determine whether or not to use threads. The
-** SQLITE_UNIX_THREADS macro is defined if we are synchronizing for
+** SQLITE_OS2_THREADS macro is defined if we are synchronizing for
** Posix threads and SQLITE_W32_THREADS is defined if we are
** synchronizing using Win32 threads.
*/
/* this mutex implementation only available with EMX */
-#if defined(THREADSAFE) && THREADSAFE
+#if SQLITE_THREADSAFE
# include <sys/builtin.h>
# include <sys/smutex.h>
# define SQLITE_OS2_THREADS 1
diff --git a/src/os_unix.c b/src/os_unix.c
index a67748f61..0544e071c 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -59,10 +59,7 @@
** If we are to be thread-safe, include the pthreads header and define
** the SQLITE_UNIX_THREADS macro.
*/
-#ifndef THREADSAFE
-# define THREADSAFE 1
-#endif
-#if THREADSAFE
+#if SQLITE_THREADSAFE
# include <pthread.h>
# define SQLITE_UNIX_THREADS 1
#endif
@@ -102,7 +99,7 @@ struct unixFile {
unsigned char locktype; /* The type of lock held on this fd */
unsigned char isOpen; /* True if needs to be closed */
int dirfd; /* File descriptor for the directory */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
pthread_t tid; /* The thread that "owns" this unixFile */
#endif
};
@@ -150,7 +147,7 @@ struct unixFile {
** The threadid macro resolves to the thread-id or to 0. Used for
** testing and debugging only.
*/
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
#define threadid pthread_self()
#else
#define threadid 0
@@ -172,7 +169,7 @@ struct unixFile {
** recomputed because its key includes the thread-id. See the
** transferOwnership() function below for additional information
*/
-#if defined(SQLITE_UNIX_THREADS)
+#if SQLITE_THREADSAFE
# define SET_THREADID(X) (X)->tid = pthread_self()
# define CHECK_THREADID(X) (threadsOverrideEachOthersLocks==0 && \
!pthread_equal((X)->tid, pthread_self()))
@@ -293,7 +290,7 @@ struct unixFile {
struct lockKey {
dev_t dev; /* Device number */
ino_t ino; /* Inode number */
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
pthread_t tid; /* Thread ID or zero if threads can override each other */
#endif
};
@@ -383,7 +380,7 @@ static void leaveMutex(){
sqlite3_mutex_leave(sqlite3_mutex_alloc(SQLITE_MUTEX_GLOBAL));
}
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
/*
** This variable records whether or not threads can override each others
** locks.
@@ -520,7 +517,7 @@ static void testThreadLockingBehavior(int fd_orig){
close(fd);
threadsOverrideEachOthersLocks = d[0].result==0 && d[1].result==0;
}
-#endif /* SQLITE_UNIX_THREADS */
+#endif /* SQLITE_THREADSAFE */
/*
** Release a lockInfo structure previously allocated by findLockInfo().
@@ -648,7 +645,7 @@ static int findLockInfo(
memset(&key1, 0, sizeof(key1));
key1.dev = statbuf.st_dev;
key1.ino = statbuf.st_ino;
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
if( threadsOverrideEachOthersLocks<0 ){
testThreadLockingBehavior(fd);
}
@@ -744,7 +741,7 @@ static const char *locktypeName(int locktype){
** If the unixFile is locked and an ownership is wrong, then return
** SQLITE_MISUSE. SQLITE_OK is returned if everything works.
*/
-#ifdef SQLITE_UNIX_THREADS
+#if SQLITE_THREADSAFE
static int transferOwnership(unixFile *pFile){
int rc;
pthread_t hSelf;
@@ -2717,7 +2714,6 @@ sqlite3_vfs sqlite3DefaultVfs = {
0, /* nRef */
0, /* vfsMutex */
0, /* pNext */
- 0, /* pPrev */
"unix", /* zName */
0, /* pAppData */
diff --git a/src/random.c b/src/random.c
index 27ee0e508..58aa07d80 100644
--- a/src/random.c
+++ b/src/random.c
@@ -15,7 +15,7 @@
** Random numbers are used by some of the database backends in order
** to generate random integer keys for tables or random filenames.
**
-** $Id: random.c,v 1.17 2007/08/17 15:53:37 danielk1977 Exp $
+** $Id: random.c,v 1.18 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -63,7 +63,7 @@ static int randomByte(void){
char k[256];
prng.j = 0;
prng.i = 0;
- sqlite3OsRandomness(sqlite3_find_vfs(0), 256, k);
+ sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k);
for(i=0; i<256; i++){
prng.s[i] = i;
}
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 55f2a50e6..d70e0444e 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -30,7 +30,7 @@
** the version number) and changes its name to "sqlite3.h" as
** part of the build process.
**
-** @(#) $Id: sqlite.h.in,v 1.230 2007/08/18 10:59:21 danielk1977 Exp $
+** @(#) $Id: sqlite.h.in,v 1.231 2007/08/20 22:48:43 drh Exp $
*/
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
@@ -548,7 +548,7 @@ typedef struct sqlite3_mutex sqlite3_mutex;
** Registered vfs modules are kept on a linked list formed by
** the pNext and pPrev pointers. The [sqlite3_register_vfs()]
** and [sqlite3_unregister_vfs()] interfaces manage this list
-** in a thread-safe way. The [sqlite3_find_vfs()] searches the
+** in a thread-safe way. The [sqlite3_acquire_vfs()] searches the
** list.
**
** The zName field holds the name of the VFS module. The name must
@@ -636,7 +636,6 @@ struct sqlite3_vfs {
int nRef; /* Number of references to this structure */
sqlite3_mutex *vfsMutex; /* A mutex for this VFS */
sqlite3_vfs *pNext; /* Next registered VFS */
- sqlite3_vfs *pPrev; /* Previous registered VFS */
const char *zName; /* Name of this virtual file system */
void *pAppData; /* Application context */
int (*xOpen)(void *pAppData, const char *zName, sqlite3_file*,
@@ -859,6 +858,13 @@ int sqlite3_complete16(const void *sql);
** connection. Setting a new busy handler clears any previous one.
** Note that calling [sqlite3_busy_timeout()] will also set or clear
** the busy handler.
+**
+** When operating in [sqlite3_enable_shared_cache | shared cache mode],
+** only a single busy handler can be defined for each database file.
+** So if two database connections share a single cache, then changing
+** the busy handler on one connection will also change the busy
+** handler in the other connection. The busy handler is invoked
+** in the thread that was running when the SQLITE_BUSY was hit.
*/
int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*);
@@ -3162,24 +3168,28 @@ int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset);
** New VFSes can be registered and existing VFSes can be unregistered.
** The following interfaces are provided.
**
-** The sqlite3_find_vfs() interface returns a pointer to a VFS given its
+** The sqlite3_vfs_find() interface returns a pointer to a VFS given its
** name. Names are case sensitive. If there is no match, a NULL
** pointer is returned. If zVfsName is NULL then the default
-** VFS is returned.
+** VFS is returned. If a valid VFS pointer is returned, its
+** vfsMutex field will have been initialized and nRef will be
+** greater than zero. The sqlite3_vfs_release() function should
+** be used to release the VFS when it is no longer needed.
**
-** New VFSes are registered with sqlite3_register_vfs(). Each
+** New VFSes are registered with sqlite3_vfs_register(). Each
** new VFS becomes the default VFS if the makeDflt flag is set.
** The same VFS can be registered multiple times without injury.
** To make an existing VFS into the default VFS, register it again
** with the makeDflt flag set.
**
-** Unregister a VFS with the sqlite3_unregister_vfs() interface.
+** Unregister a VFS with the sqlite3_vfs_unregister() interface.
** If the default VFS is unregistered, another VFS is chosen as
** the default. The choice for the new VFS is arbitrary.
*/
-sqlite3_vfs *sqlite3_find_vfs(const char *zVfsName);
-int sqlite3_register_vfs(sqlite3_vfs*, int makeDflt);
-int sqlite3_unregister_vfs(sqlite3_vfs*);
+sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName);
+int sqlite3_vfs_release(sqlite3_vfs*);
+int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt);
+int sqlite3_vfs_unregister(sqlite3_vfs*);
/*
** CAPI3REF: Mutexes
@@ -3273,12 +3283,33 @@ int sqlite3_unregister_vfs(sqlite3_vfs*);
** previously entered by the same thread. The behavior
** is undefined if the mutex is not currently entered or
** is not currently allocated. SQLite will never do either.
+**
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines
+** are intended for use inside assert() statements. They should
+** return true if the mutex in their argument is held or not held,
+** respectively, by the current thread. The implementation is
+** not required to provided working implementations of these
+** routines as their intended use is within assert() statements
+** only. If the implementation does not provide working
+** versions of these routines, it must at least provide stubs
+** that always return true.
+**
+** If the argument to sqlite3_mutex_held() is a NULL pointer then
+** the routine should return 1. This seems counter-intuitive since
+** clearly the mutex cannot be held if it does not exist. But the
+** the reason the mutex does not exist is because the build is not
+** using mutexes. And we do not want the assert() containing the
+** call to sqlite3_mutex_held() to fail, so a non-zero return is
+** the appropriate thing to do. The sqlite3_mutex_notheld()
+** interface should also return 1 when given a NULL pointer.
*/
sqlite3_mutex *sqlite3_mutex_alloc(int);
void sqlite3_mutex_free(sqlite3_mutex*);
void sqlite3_mutex_enter(sqlite3_mutex*);
int sqlite3_mutex_try(sqlite3_mutex*);
void sqlite3_mutex_leave(sqlite3_mutex*);
+int sqlite3_mutex_held(sqlite3_mutex*);
+int sqlite3_mutex_notheld(sqlite3_mutex*);
#define SQLITE_MUTEX_FAST 0
#define SQLITE_MUTEX_RECURSIVE 1
#define SQLITE_MUTEX_STATIC_MASTER 2
diff --git a/src/sqliteInt.h b/src/sqliteInt.h
index a3899c572..63bb6a3d9 100644
--- a/src/sqliteInt.h
+++ b/src/sqliteInt.h
@@ -11,7 +11,7 @@
*************************************************************************
** Internal interface definitions for SQLite.
**
-** @(#) $Id: sqliteInt.h,v 1.592 2007/08/20 14:23:44 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.593 2007/08/20 22:48:43 drh Exp $
*/
#ifndef _SQLITEINT_H_
#define _SQLITEINT_H_
@@ -34,6 +34,19 @@
#endif
/*
+** The SQLITE_THREADSAFE macro must be defined as either 0 or 1.
+** Older versions of SQLite used an optional THREADSAFE macro.
+** We support that for legacy
+*/
+#if !defined(SQLITE_THREADSAFE)
+#if defined(THREADSAFE)
+# define SQLITE_THREADSAFE THREADSAFE
+#else
+# define SQLTIE_THREADSAFE 1
+#endif
+#endif
+
+/*
** These #defines should enable >2GB file support on Posix if the
** underlying operating system supports it. If the OS lacks
** large file support, or if the OS is windows, these should be no-ops.
@@ -399,7 +412,7 @@ struct sqlite3 {
int magic; /* Magic number for detect library misuse */
int nChange; /* Value returned by sqlite3_changes() */
int nTotalChange; /* Value returned by sqlite3_total_changes() */
- sqlite3_mutex *pMutex; /* Connection mutex */
+ sqlite3_mutex *mutex; /* Connection mutex */
struct sqlite3InitInfo { /* Information used during initialization */
int iDb; /* When back is being initialized */
int newTnum; /* Rootpage of table being initialized */
@@ -1818,10 +1831,17 @@ void sqlite3Parser(void*, int, Token, Parse*);
#endif
/*
-** FIX ME: create these routines
+** The MallocDisallow() and MallocAllow() routines are like asserts.
+** Call them around a section of code that you do not expect to do
+** any memory allocation.
*/
-#define sqlite3MallocDisallow()
-#define sqlite3MallocAllow()
+#ifdef SQLITE_MEMDEBUG
+ void sqlite3MallocDisallow(void);
+ void sqlite3MallocAllow(void);
+#else
+# define sqlite3MallocDisallow()
+# define sqlite3MallocAllow()
+#endif
#ifdef SQLITE_OMIT_VIRTUALTABLE
@@ -1850,7 +1870,6 @@ void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**);
int sqlite3Reprepare(Vdbe*);
void sqlite3ExprListCheckLength(Parse*, ExprList*, int, const char*);
CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *);
-int sqlite3CrashFileOpen(sqlite3_vfs*, const char*, sqlite3_file*, int,int*);
#if SQLITE_MAX_EXPR_DEPTH>0
void sqlite3ExprSetHeight(Expr *);
diff --git a/src/test1.c b/src/test1.c
index 012f22a71..d46be4526 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
-** $Id: test1.c,v 1.264 2007/08/17 15:53:37 danielk1977 Exp $
+** $Id: test1.c,v 1.265 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -4330,7 +4330,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
extern int sqlite3_pager_writedb_count;
extern int sqlite3_pager_writej_count;
extern int sqlite3_pager_pgfree_count;
-#if OS_UNIX && defined(SQLITE_TEST) && defined(THREADSAFE) && THREADSAFE
+#if OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
extern int threadsOverrideEachOthersLocks;
#endif
#if OS_WIN
@@ -4380,7 +4380,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
Tcl_LinkVar(interp, "unaligned_string_counter",
(char*)&unaligned_string_counter, TCL_LINK_INT);
#endif
-#if OS_UNIX && defined(SQLITE_TEST) && defined(THREADSAFE) && THREADSAFE
+#if OS_UNIX && defined(SQLITE_TEST) && SQLITE_THREADSAFE
Tcl_LinkVar(interp, "threadsOverrideEachOthersLocks",
(char*)&threadsOverrideEachOthersLocks, TCL_LINK_INT);
#endif
diff --git a/src/test2.c b/src/test2.c
index c819ee3ea..676b188ba 100644
--- a/src/test2.c
+++ b/src/test2.c
@@ -13,7 +13,7 @@
** is not included in the SQLite library. It is used for automated
** testing of the SQLite library.
**
-** $Id: test2.c,v 1.48 2007/08/20 14:23:44 danielk1977 Exp $
+** $Id: test2.c,v 1.49 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
@@ -78,7 +78,7 @@ static int pager_open(
return TCL_ERROR;
}
if( Tcl_GetInt(interp, argv[2], &nPage) ) return TCL_ERROR;
- rc = sqlite3PagerOpen(sqlite3_find_vfs(0), &pPager, argv[1], 0, 0);
+ rc = sqlite3PagerOpen(sqlite3_vfs_find(0), &pPager, argv[1], 0, 0);
if( rc!=SQLITE_OK ){
Tcl_AppendResult(interp, errorName(rc), 0);
return TCL_ERROR;
@@ -540,7 +540,7 @@ static int fake_big_file(
}
if( Tcl_GetInt(interp, argv[1], &n) ) return TCL_ERROR;
- pVfs = sqlite3_find_vfs(0);
+ pVfs = sqlite3_vfs_find(0);
rc = sqlite3OsOpenMalloc(pVfs, argv[2], &fd,
(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE|SQLITE_OPEN_MAIN_DB), 0
);
diff --git a/src/test4.c b/src/test4.c
index 520ad2a68..221db0fb4 100644
--- a/src/test4.c
+++ b/src/test4.c
@@ -11,12 +11,12 @@
*************************************************************************
** Code for testing the the SQLite library in a multithreaded environment.
**
-** $Id: test4.c,v 1.18 2007/08/16 04:30:40 drh Exp $
+** $Id: test4.c,v 1.19 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
-#if defined(OS_UNIX) && OS_UNIX==1 && defined(THREADSAFE) && THREADSAFE==1
+#if defined(OS_UNIX) && OS_UNIX==1 && SQLITE_THREADSAFE
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
diff --git a/src/test6.c b/src/test6.c
index 66b012846..df7418f09 100644
--- a/src/test6.c
+++ b/src/test6.c
@@ -452,6 +452,14 @@ static const sqlite3_io_methods CrashFileVtab = {
};
/*
+** Application data for the crash VFS
+*/
+struct crashAppData {
+ int (*xOpen)(void*,const char*,sqlite3_file*,int,int*); /* Original xOpen */
+ void *pAppData; /* Original pAppData */
+};
+
+/*
** Open a crash-file file handle. The vfs pVfs is used to open
** the underlying real file.
**
@@ -461,7 +469,7 @@ static const sqlite3_io_methods CrashFileVtab = {
** sqlite3_malloc(). The assumption here is (pVfs->szOsFile) is
** equal or greater than sizeof(CrashFile).
*/
-int sqlite3CrashFileOpen(
+static int sqlite3CrashFileOpen(
sqlite3_vfs *pVfs,
const char *zName,
sqlite3_file *pFile,
@@ -499,7 +507,8 @@ int sqlite3CrashFileOpen(
sqlite3OsClose(pFile);
}
}else{
- rc = pVfs->xOpen(pVfs->pAppData, zName, pFile, flags, pOutFlags);
+ struct crashAppData *pData = (struct crashAppData*)pVfs->pAppData;
+ rc = pData->xOpen(pData->pAppData, zName, pFile, flags, pOutFlags);
}
return rc;
}
@@ -528,11 +537,26 @@ static int crashParamsObjCmd(
int objc,
Tcl_Obj *CONST objv[]
){
- sqlite3_vfs *pVfs;
int i;
int iDelay;
const char *zCrashFile;
int nCrashFile;
+ static sqlite3_vfs crashVfs, *pOriginalVfs;
+ static struct crashAppData appData;
+
+ if( pOriginalVfs==0 ){
+ pOriginalVfs = sqlite3_vfs_find(0);
+ crashVfs = *pOriginalVfs;
+ crashVfs.xOpen = sqlite3CrashFileOpen;
+ crashVfs.vfsMutex = 0;
+ crashVfs.nRef = 0;
+ crashVfs.pAppData = &appData;
+ appData.xOpen = pOriginalVfs->xOpen;
+ appData.pAppData = pOriginalVfs->pAppData;
+ sqlite3_vfs_release(pOriginalVfs);
+ sqlite3_vfs_unregister(pOriginalVfs);
+ sqlite3_vfs_register(&crashVfs, 1);
+ }
int iDc = 0;
int iSectorSize = 0;
diff --git a/src/test7.c b/src/test7.c
index 17c2a4984..f3411f90a 100644
--- a/src/test7.c
+++ b/src/test7.c
@@ -12,17 +12,17 @@
** Code for testing the client/server version of the SQLite library.
** Derived from test4.c.
**
-** $Id: test7.c,v 1.5 2007/08/16 04:30:40 drh Exp $
+** $Id: test7.c,v 1.6 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
#include "os.h"
/*
-** This test only works on UNIX with a THREADSAFE build that includes
+** This test only works on UNIX with a SQLITE_THREADSAFE build that includes
** the SQLITE_SERVER option.
*/
-#if OS_UNIX && defined(THREADSAFE) && THREADSAFE==1 && \
+#if OS_UNIX && SQLITE_THREADSAFE && \
defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
#include <stdlib.h>
diff --git a/src/test_async.c b/src/test_async.c
index 9f38f2c7c..ead844297 100644
--- a/src/test_async.c
+++ b/src/test_async.c
@@ -74,11 +74,6 @@
#include "os.h"
#include <tcl.h>
-/* If the THREADSAFE macro is not set, assume that it is turned off. */
-#ifndef THREADSAFE
-# define THREADSAFE 0
-#endif
-
/*
** This test uses pthreads and hence only works on unix and with
** a threadsafe build of SQLite. It also requires that the redefinable
@@ -86,7 +81,7 @@
** default. If a required element is missing, almost all of the code
** in this file is commented out.
*/
-#if OS_UNIX && THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
+#if OS_UNIX && SQLITE_THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
/*
** This demo uses pthreads. If you do not have a pthreads implementation
@@ -1249,7 +1244,7 @@ static int testAsyncWait(
}
-#endif /* OS_UNIX and THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
+#endif /* OS_UNIX and SQLITE_THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
/*
** This routine registers the custom TCL commands defined in this
@@ -1257,7 +1252,7 @@ static int testAsyncWait(
** of this module.
*/
int Sqlitetestasync_Init(Tcl_Interp *interp){
-#if OS_UNIX && THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
+#if OS_UNIX && SQLITE_THREADSAFE && defined(SQLITE_ENABLE_REDEF_IO)
Tcl_CreateObjCommand(interp,"sqlite3async_enable",testAsyncEnable,0,0);
Tcl_CreateObjCommand(interp,"sqlite3async_halt",testAsyncHalt,0,0);
Tcl_CreateObjCommand(interp,"sqlite3async_delay",testAsyncDelay,0,0);
@@ -1265,6 +1260,6 @@ int Sqlitetestasync_Init(Tcl_Interp *interp){
Tcl_CreateObjCommand(interp,"sqlite3async_wait",testAsyncWait,0,0);
Tcl_LinkVar(interp, "sqlite3async_trace",
(char*)&sqlite3async_trace, TCL_LINK_INT);
-#endif /* OS_UNIX and THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
+#endif /* OS_UNIX and SQLITE_THREADSAFE and defined(SQLITE_ENABLE_REDEF_IO) */
return TCL_OK;
}
diff --git a/src/test_config.c b/src/test_config.c
index 78ec7af40..e839910d2 100644
--- a/src/test_config.c
+++ b/src/test_config.c
@@ -16,7 +16,7 @@
** The focus of this file is providing the TCL testing layer
** access to compile-time constants.
**
-** $Id: test_config.c,v 1.8 2007/08/20 17:37:48 shess Exp $
+** $Id: test_config.c,v 1.9 2007/08/20 22:48:43 drh Exp $
*/
#include "sqliteInt.h"
#include "tcl.h"
@@ -197,12 +197,6 @@ static void set_options(Tcl_Interp *interp){
Tcl_SetVar2(interp, "sqlite_options", "fts2", "0", TCL_GLOBAL_ONLY);
#endif
-#ifdef SQLITE_ENABLE_FTS3
- Tcl_SetVar2(interp, "sqlite_options", "fts3", "1", TCL_GLOBAL_ONLY);
-#else
- Tcl_SetVar2(interp, "sqlite_options", "fts3", "0", TCL_GLOBAL_ONLY);
-#endif
-
#ifdef SQLITE_OMIT_GLOBALRECOVER
Tcl_SetVar2(interp, "sqlite_options", "globalrecover", "0", TCL_GLOBAL_ONLY);
#else
@@ -334,7 +328,7 @@ Tcl_SetVar2(interp, "sqlite_options", "long_double",
Tcl_SetVar2(interp, "sqlite_options", "tclvar", "1", TCL_GLOBAL_ONLY);
#endif
-#if defined(THREADSAFE) && THREADSAFE
+#if SQLITE_THREADSAFE
Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "1", TCL_GLOBAL_ONLY);
#else
Tcl_SetVar2(interp, "sqlite_options", "threadsafe", "0", TCL_GLOBAL_ONLY);
diff --git a/src/test_server.c b/src/test_server.c
index db213cf67..48cd122b8 100644
--- a/src/test_server.c
+++ b/src/test_server.c
@@ -197,11 +197,11 @@
*/
/*
-** Only compile the code in this file on UNIX with a THREADSAFE build
+** Only compile the code in this file on UNIX with a SQLITE_THREADSAFE build
** and only if the SQLITE_SERVER macro is defined.
*/
#if defined(SQLITE_SERVER) && !defined(SQLITE_OMIT_SHARED_CACHE)
-#if defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE
+#if defined(OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE
/*
** We require only pthreads and the public interface of SQLite.
@@ -483,5 +483,5 @@ void sqlite3_server_stop(void){
pthread_mutex_unlock(&g.serverMutex);
}
-#endif /* defined(OS_UNIX) && OS_UNIX && defined(THREADSAFE) && THREADSAFE */
+#endif /* defined(OS_UNIX) && OS_UNIX && SQLITE_THREADSAFE */
#endif /* defined(SQLITE_SERVER) */