diff options
author | drh <drh@noemail.net> | 2007-08-20 22:48:41 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2007-08-20 22:48:41 +0000 |
commit | d677b3d688ae9e0f4bde5599369b8074278ba3c3 (patch) | |
tree | 10af779ae865361152bb5658e9e94d090bca3c73 | |
parent | 7e3c77df0de7a9efa63ee675a80bb8b241f78421 (diff) | |
download | sqlite-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.mk | 4 | ||||
-rw-r--r-- | manifest | 54 | ||||
-rw-r--r-- | manifest.uuid | 2 | ||||
-rw-r--r-- | src/btree.c | 542 | ||||
-rw-r--r-- | src/btree.h | 10 | ||||
-rw-r--r-- | src/btreeInt.h | 14 | ||||
-rw-r--r-- | src/main.c | 8 | ||||
-rw-r--r-- | src/mem2.c | 30 | ||||
-rw-r--r-- | src/mutex.c | 28 | ||||
-rw-r--r-- | src/os.c | 100 | ||||
-rw-r--r-- | src/os_os2.h | 4 | ||||
-rw-r--r-- | src/os_unix.c | 22 | ||||
-rw-r--r-- | src/random.c | 4 | ||||
-rw-r--r-- | src/sqlite.h.in | 51 | ||||
-rw-r--r-- | src/sqliteInt.h | 31 | ||||
-rw-r--r-- | src/test1.c | 6 | ||||
-rw-r--r-- | src/test2.c | 6 | ||||
-rw-r--r-- | src/test4.c | 4 | ||||
-rw-r--r-- | src/test6.c | 30 | ||||
-rw-r--r-- | src/test7.c | 6 | ||||
-rw-r--r-- | src/test_async.c | 13 | ||||
-rw-r--r-- | src/test_config.c | 10 | ||||
-rw-r--r-- | src/test_server.c | 6 |
23 files changed, 761 insertions, 224 deletions
@@ -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 @@ -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) */ @@ -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) */ |