diff --git a/manifest b/manifest index b1b531801d..a031cf3eb0 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Change\sthe\sdesign\sof\sthe\s\smutex\sinterface\sto\sallow\sfor\nboth\s"fast"\sand\s"recursive"\smutexes.\s(CVS\s4238) -D 2007-08-16T19:40:17 +C Begin\sadding\smutexes.\s\sCompiles\swithout\sSQLITE_OMIT_SHARED_CACHE\sbut\swe\nget\san\sassertion\sfault\son\sthe\sshared\scache\stesting.\s(CVS\s4239) +D 2007-08-17T01:14:38 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -68,10 +68,10 @@ F src/alter.c f0aac0060ae8102e58f210b44d35b53438d53173 F src/analyze.c a14237d869c6bea0846493b59317e4097e81a0b6 F src/attach.c a52225c75b107be8c5bc144a2b6d20201be3f8f8 F src/auth.c 5ea90bc93dfea46e9fe4bf531e14c7cd98219ecb -F src/btree.c f371e9d7a24ba330c108bf1bb795280051f696ac -F src/btree.h 1d527bf61ed176f980c34999d5793a0fd45dcf8c -F src/btreeInt.h ac1ab1fb624ffbe571786cd2bd9559f9ae336355 -F src/build.c 923d6643c8f59fbcd10cd9e2f2690e82f48db69e +F src/btree.c 409e7362338d3cf1f72961a01e75e9fbb577cc9f +F src/btree.h 91ee529d581c1473d8e6e15299acc3b8de1d0674 +F src/btreeInt.h 6329e955a7dadd8628d5866e2465721b5fd25ef2 +F src/build.c add67be992307b4b11849a6611bfd3352aacde92 F src/callback.c 143436453bb93e831c9574fea0b9b9eb90e40ff3 F src/complete.c ea63834e798a0ab14159bdc6e6cabc3df21aa346 F src/date.c c44aa498ee9a289ba2b2c62e8269b74b1b81351f @@ -85,12 +85,12 @@ F src/insert.c 633322aef1799f6604fa805e12488bc628570b0c F src/legacy.c 6013a7cb7da1b72550b3d35d4fc598b3c3e5b8c1 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35 F src/loadext.c aa1c6e584d39cc241226ec9390387bc2d4a23e8f -F src/main.c 996df547489d4826f70629b16623d7408f55ecd7 +F src/main.c 6e12fdab03efb8fb17aee8cfcd3bc32329cf1cda F src/malloc.c 613c65f12ff0ee4edd017aa458209ab7a23cd7b1 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 F src/mem1.c 2c6a6e3b2c9c7cb8d398a8468095032407c3e0b7 F src/mem2.c 661ca7ebf6e4b964fecc95d24e8c89dbcfc9dfea -F src/mutex.c 679d5d9c99bd302c0c43ee3eba61348c44aea366 +F src/mutex.c 67b2efd36a1e67a7dc7b7fa852fd69953462c943 F src/os.c e2faefbe0f5a8ca5e3b1c49ee1b5c6cfa0f0e279 F src/os.h 8eff07babf74e5bc3f895f8a6c7c294dad5ff997 F src/os_common.h a5c446d3b93f09f369d13bf217de4bed3437dd1c @@ -112,13 +112,13 @@ F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88 F src/select.c 98c367bce3f38c5adfcc97de9ab5c79b0e5dc2b2 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb -F src/sqlite.h.in 8efd7d5aca057a793d221973da1f22a22e69f4db +F src/sqlite.h.in 07eea55853b739b372d4744ceefd08795d013be9 F src/sqlite3ext.h 647a6b8a8f76ff6c9611e4a071531d8e63ff2d6b -F src/sqliteInt.h fa9baff32aef7ca1ecebcd014b3bd75c981829d0 +F src/sqliteInt.h 7298c560e00f2305e2ecc1d2cbdd66134f5049de F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa F src/table.c c725e47f6f3092b9a7b569fc58e408e2173ee008 F src/tclsqlite.c 0606c4f31711492eb4d7480a981eebb80914f3d9 -F src/test1.c 8afb22ec54ee9f28c103c2a212e2e6970626995a +F src/test1.c a1d6eb85149053fac75b01b71439f00908a07c68 F src/test2.c 4db48e4a487d4d18c2926d9600875613ad286ba8 F src/test3.c b87e8fcce45e1d3153aae9f04236076b7707a714 F src/test4.c d22cb3ab4f9fdfd0a595b70d5328cee923b7322c @@ -129,7 +129,7 @@ F src/test8.c 719c284607c1e91a893f5425df1e92b74c859aef F src/test9.c c0f38f7795cc51d37db6c63874d90f40f10d0f0e F src/test_async.c 871ffbe4a520be74b403aca87aa622ebdb690232 F src/test_autoext.c 855157d97aa28cf84233847548bfacda21807436 -F src/test_btree.c 882d59acad48bab3b1fe3daf3645059b590cfc79 +F src/test_btree.c c1308ba0b88ab577fa56c9e493a09829dfcded9c F src/test_config.c 26389b032216e0fb2b544ff48a5e9101bd7b1fb4 F src/test_hexio.c 82916f918687502658f02533b519c38cb180db6d F src/test_loadext.c 22065d601a18878e5542191001f0eaa5d77c0ed8 @@ -152,7 +152,7 @@ F src/vdbeaux.c c6d50887e8f29706ae35b965298e58fa6ba0e9bf F src/vdbeblob.c cf9ee3c7d9977cbd896f8b118da4fb4268637f4f F src/vdbefifo.c 334c838c8f42d61a94813d136019ee566b5dc2f6 F src/vdbemem.c 019952d44066a24aef70ca8c284cfd2d1073c398 -F src/vtab.c 8d65679ab4ef3efce5d946d7f2d2dac5a33313b4 +F src/vtab.c ee29237ecc9b310dc43c0c2ac5caa6c6a20787be F src/where.c 2776a0caf8cbbfd6ec79cfb1cd9bc25074055e5e F tclinstaller.tcl 4356d9d94d2b5ed5e68f9f0c80c4df3048dd7617 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 @@ -529,7 +529,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P 3d60c14a32955b69e714a73372924d421899f83b -R 79bf1accc56116075f09cd253592694c +P 160593dcc5690af715b775c81137c6e09cca6454 +R 5d5493a8682338889467b44d542e4cdc U drh -Z e40cc1b0395242393945aa35a1600f4d +Z d35e1fbff49f629248bfcae9f72cc56d diff --git a/manifest.uuid b/manifest.uuid index fc8a7b8805..8ac8ae7697 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -160593dcc5690af715b775c81137c6e09cca6454 \ No newline at end of file +4c1e9ffebe7c611a8b6a89153ae97ab9bca19ea3 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index ea2651a35d..ce78a46e8d 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.398 2007/08/16 10:09:02 danielk1977 Exp $ +** $Id: btree.c,v 1.399 2007/08/17 01:14:38 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -32,6 +32,36 @@ static const char zMagicHeader[] = SQLITE_FILE_HEADER; int sqlite3_btree_trace=0; /* True to enable tracing */ #endif +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** 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. +*/ +#ifdef SQLITE_TEST +BtShared *sqlite3SharedCacheList = 0; +int sqlite3SharedCacheEnabled = 0; +#else +static BtShared *sqlite3SharedCacheList = 0; +static int sqlite3SharedCacheEnabled = 0; +#endif + +#endif /* SQLITE_OMIT_SHARED_CACHE */ + +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** Enable or disable the shared pager and schema features. +** +** This routine has no effect on existing database connections. +** The shared cache setting effects only future calls to +** sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2(). +*/ +int sqlite3_enable_shared_cache(int enable){ + sqlite3SharedCacheEnabled = enable; + return SQLITE_OK; +} +#endif + /* ** Forward declaration */ @@ -50,8 +80,9 @@ static int checkReadLocks(Btree*,Pgno,BtCursor*); #define queryTableLock(a,b,c) SQLITE_OK #define lockTable(a,b,c) SQLITE_OK #define unlockAllTables(a) -#else +#endif +#ifndef SQLITE_OMIT_SHARED_CACHE /* ** Query to see if btree handle p may obtain a lock of type eLock ** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return @@ -63,7 +94,7 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ BtLock *pIter; /* This is a no-op if the shared-cache is not enabled */ - if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){ + if( !p->sharable ){ return SQLITE_OK; } @@ -96,7 +127,9 @@ static int queryTableLock(Btree *p, Pgno iTab, u8 eLock){ } return SQLITE_OK; } +#endif /* !SQLITE_OMIT_SHARED_CACHE */ +#ifndef SQLITE_OMIT_SHARED_CACHE /* ** Add a lock on the table with root-page iTable to the shared-btree used ** by Btree handle p. Parameter eLock must be either READ_LOCK or @@ -111,7 +144,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){ BtLock *pIter; /* This is a no-op if the shared-cache is not enabled */ - if( 0==sqlite3ThreadDataReadOnly()->useSharedData ){ + if( !p->sharable ){ return SQLITE_OK; } @@ -164,7 +197,9 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){ return SQLITE_OK; } +#endif /* !SQLITE_OMIT_SHARED_CACHE */ +#ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to the lockTable() ** procedure) held by Btree handle p. @@ -172,11 +207,7 @@ static int lockTable(Btree *p, Pgno iTable, u8 eLock){ static void unlockAllTables(Btree *p){ BtLock **ppIter = &p->pBt->pLock; - /* If the shared-cache extension is not enabled, there should be no - ** locks in the BtShared.pLock list, making this procedure a no-op. Assert - ** that this is the case. - */ - assert( sqlite3ThreadDataReadOnly()->useSharedData || 0==*ppIter ); + assert( p->sharable || 0==*ppIter ); while( *ppIter ){ BtLock *pLock = *ppIter; @@ -1023,6 +1054,8 @@ static void pageReinit(DbPage *pData, int pageSize){ ** zFilename is the name of the database file. If zFilename is NULL ** a new database with a random name is created. This randomly named ** database file will be deleted when sqlite3BtreeClose() is called. +** If zFilename is ":memory:" then an in-memory database is created +** that is automatically destroyed when it is closed. */ int sqlite3BtreeOpen( const char *zFilename, /* Name of the file containing the BTree database */ @@ -1030,14 +1063,11 @@ int sqlite3BtreeOpen( Btree **ppBtree, /* Pointer to new Btree object written here */ int flags /* Options */ ){ - BtShared *pBt; /* Shared part of btree structure */ + BtShared *pBt = 0; /* Shared part of btree structure */ Btree *p; /* Handle to return */ int rc = SQLITE_OK; int nReserve; unsigned char zDbHeader[100]; -#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - const ThreadData *pTsdro; -#endif /* Set the variable isMemdb to true for an in-memory database, or ** false for a file-based database. This symbol is only required if @@ -1059,106 +1089,150 @@ int sqlite3BtreeOpen( p->inTrans = TRANS_NONE; p->pSqlite = pSqlite; - /* Try to find an existing Btree structure opened on zFilename. */ #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - pTsdro = sqlite3ThreadDataReadOnly(); - if( pTsdro->useSharedData && zFilename && !isMemdb ){ + /* + ** If this Btree is a candidate for shared cache, try to find an + ** existing BtShared object that we can share with + */ + if( (flags & BTREE_PRIVATE)==0 + && isMemdb==0 + && zFilename && zFilename[0] + && sqlite3SharedCacheEnabled + ){ char *zFullPathname = sqlite3OsFullPathname(zFilename); + sqlite3_mutex *mutexShared; + p->sharable = 1; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; } - for(pBt=pTsdro->pBtree; pBt; pBt=pBt->pNext){ + mutexShared = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex_enter(mutexShared); + for(pBt=sqlite3SharedCacheList; pBt; pBt=pBt->pNext){ assert( pBt->nRef>0 ); if( 0==strcmp(zFullPathname, sqlite3PagerFilename(pBt->pPager)) ){ p->pBt = pBt; - *ppBtree = p; pBt->nRef++; - sqlite3_free(zFullPathname); - return SQLITE_OK; + break; } } + sqlite3_mutex_leave(mutexShared); sqlite3_free(zFullPathname); } #endif - - /* - ** The following asserts make sure that structures used by the btree are - ** the right size. This is to guard against size changes that result - ** when compiling on a different architecture. - */ - assert( sizeof(i64)==8 || sizeof(i64)==4 ); - assert( sizeof(u64)==8 || sizeof(u64)==4 ); - assert( sizeof(u32)==4 ); - assert( sizeof(u16)==2 ); - assert( sizeof(Pgno)==4 ); - - pBt = sqlite3MallocZero( sizeof(*pBt) ); if( pBt==0 ){ - rc = SQLITE_NOMEM; - goto btree_open_out; - } - rc = sqlite3PagerOpen(&pBt->pPager, zFilename, EXTRA_SIZE, flags); - if( rc==SQLITE_OK ){ - rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); - } - if( rc!=SQLITE_OK ){ - goto btree_open_out; - } - p->pBt = pBt; - - sqlite3PagerSetDestructor(pBt->pPager, pageDestructor); - sqlite3PagerSetReiniter(pBt->pPager, pageReinit); - pBt->pCursor = 0; - pBt->pPage1 = 0; - pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); - pBt->pageSize = get2byte(&zDbHeader[16]); - if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE - || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ - pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE; - pBt->maxEmbedFrac = 64; /* 25% */ - pBt->minEmbedFrac = 32; /* 12.5% */ - pBt->minLeafFrac = 32; /* 12.5% */ -#ifndef SQLITE_OMIT_AUTOVACUUM - /* If the magic name ":memory:" will create an in-memory database, then - ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if - ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if - ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a - ** regular file-name. In this case the auto-vacuum applies as per normal. + /* + ** The following asserts make sure that structures used by the btree are + ** the right size. This is to guard against size changes that result + ** when compiling on a different architecture. */ - if( zFilename && !isMemdb ){ - pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); - pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); + assert( sizeof(i64)==8 || sizeof(i64)==4 ); + assert( sizeof(u64)==8 || sizeof(u64)==4 ); + assert( sizeof(u32)==4 ); + assert( sizeof(u16)==2 ); + assert( sizeof(Pgno)==4 ); + + pBt = sqlite3MallocZero( sizeof(*pBt) ); + if( pBt==0 ){ + rc = SQLITE_NOMEM; + goto btree_open_out; + } + rc = sqlite3PagerOpen(&pBt->pPager, zFilename, EXTRA_SIZE, flags); + if( rc==SQLITE_OK ){ + rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); + } + if( rc!=SQLITE_OK ){ + goto btree_open_out; + } + p->pBt = pBt; + + sqlite3PagerSetDestructor(pBt->pPager, pageDestructor); + sqlite3PagerSetReiniter(pBt->pPager, pageReinit); + pBt->pCursor = 0; + pBt->pPage1 = 0; + pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); + pBt->pageSize = get2byte(&zDbHeader[16]); + if( pBt->pageSize<512 || pBt->pageSize>SQLITE_MAX_PAGE_SIZE + || ((pBt->pageSize-1)&pBt->pageSize)!=0 ){ + pBt->pageSize = SQLITE_DEFAULT_PAGE_SIZE; + pBt->maxEmbedFrac = 64; /* 25% */ + pBt->minEmbedFrac = 32; /* 12.5% */ + pBt->minLeafFrac = 32; /* 12.5% */ +#ifndef SQLITE_OMIT_AUTOVACUUM + /* If the magic name ":memory:" will create an in-memory database, then + ** leave the autoVacuum mode at 0 (do not auto-vacuum), even if + ** SQLITE_DEFAULT_AUTOVACUUM is true. On the other hand, if + ** SQLITE_OMIT_MEMORYDB has been defined, then ":memory:" is just a + ** regular file-name. In this case the auto-vacuum applies as per normal. + */ + if( zFilename && !isMemdb ){ + pBt->autoVacuum = (SQLITE_DEFAULT_AUTOVACUUM ? 1 : 0); + pBt->incrVacuum = (SQLITE_DEFAULT_AUTOVACUUM==2 ? 1 : 0); + } +#endif + nReserve = 0; + }else{ + nReserve = zDbHeader[20]; + pBt->maxEmbedFrac = zDbHeader[21]; + pBt->minEmbedFrac = zDbHeader[22]; + pBt->minLeafFrac = zDbHeader[23]; + pBt->pageSizeFixed = 1; +#ifndef SQLITE_OMIT_AUTOVACUUM + pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); + pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); +#endif + } + pBt->usableSize = pBt->pageSize - nReserve; + assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ + sqlite3PagerSetPagesize(pBt->pPager, pBt->pageSize); + +#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) + /* Add the new BtShared object to the linked list sharable BtShareds. + */ + if( p->sharable ){ + sqlite3_mutex *mutexShared; + pBt->nRef = 1; + mutexShared = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + pBt->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + sqlite3_mutex_enter(mutexShared); + pBt->pNext = sqlite3SharedCacheList; + sqlite3SharedCacheList = pBt; + sqlite3_mutex_leave(mutexShared); } -#endif - nReserve = 0; - }else{ - nReserve = zDbHeader[20]; - pBt->maxEmbedFrac = zDbHeader[21]; - pBt->minEmbedFrac = zDbHeader[22]; - pBt->minLeafFrac = zDbHeader[23]; - pBt->pageSizeFixed = 1; -#ifndef SQLITE_OMIT_AUTOVACUUM - pBt->autoVacuum = (get4byte(&zDbHeader[36 + 4*4])?1:0); - pBt->incrVacuum = (get4byte(&zDbHeader[36 + 7*4])?1:0); #endif } - pBt->usableSize = pBt->pageSize - nReserve; - assert( (pBt->pageSize & 7)==0 ); /* 8-byte alignment of pageSize */ - sqlite3PagerSetPagesize(pBt->pPager, pBt->pageSize); #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) - /* Add the new btree to the linked list starting at ThreadData.pBtree. - ** There is no chance that a malloc() may fail inside of the - ** sqlite3ThreadData() call, as the ThreadData structure must have already - ** been allocated for pTsdro->useSharedData to be non-zero. + /* If the new Btree uses a sharable pBtShared, then link the new + ** Btree into the list of all sharable Btrees for the same connection. + ** The list is kept in ascending order by pBtShared address. */ - if( pTsdro->useSharedData && zFilename && !isMemdb ){ - pBt->pNext = pTsdro->pBtree; - sqlite3ThreadData()->pBtree = pBt; + if( p->sharable ){ + int i; + Btree *pSib; + for(i=0; inDb; i++){ + if( (pSib = pSqlite->aDb[i].pBt)!=0 && pSib->sharable ){ + while( pSib->pPrev ){ pSib = pSib->pPrev; } + if( p->pBtpBt ){ + p->pNext = pSib; + p->pPrev = 0; + pSib->pPrev = p; + }else{ + while( pSib->pNext && pSib->pNext->pBt>p->pBt ){ + pSib = pSib->pNext; + } + p->pNext = pSib->pNext; + p->pPrev = pSib; + if( p->pNext ){ + p->pNext->pPrev = p; + } + pSib->pNext = p; + } + break; + } + } } #endif - pBt->nRef = 1; *ppBtree = p; btree_open_out: @@ -1173,6 +1247,43 @@ btree_open_out: return rc; } +/* +** Decrement the BtShared.nRef counter. When it reaches zero, +** remove the BtShared structure from the sharing list. Return +** true if the BtShared.nRef counter reaches zero and return +** false if it is still positive. +*/ +static int removeFromSharingList(BtShared *pBt){ +#ifndef SQLITE_OMIT_SHARED_CACHE + sqlite3_mutex *pMaster; + BtShared *pList; + int removed = 0; + + pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); + sqlite3_mutex_enter(pMaster); + pBt->nRef--; + if( pBt->nRef<=0 ){ + if( sqlite3SharedCacheList==pBt ){ + sqlite3SharedCacheList = pBt->pNext; + }else{ + pList = sqlite3SharedCacheList; + while( pList && pList->pNext!=pBt ){ + pList=pList->pNext; + } + if( pList ){ + pList->pNext = pBt->pNext; + } + } + sqlite3_mutex_free(pBt->mutex); + removed = 1; + } + sqlite3_mutex_leave(pMaster); + return removed; +#else + return 1; +#endif +} + /* ** Close an open database and invalidate all cursors. */ @@ -1180,11 +1291,8 @@ int sqlite3BtreeClose(Btree *p){ BtShared *pBt = p->pBt; BtCursor *pCur; -#ifndef SQLITE_OMIT_SHARED_CACHE - ThreadData *pTsd; -#endif - /* Close all cursors opened via this handle. */ + sqlite3BtreeEnter(p); pCur = pBt->pCursor; while( pCur ){ BtCursor *pTmp = pCur; @@ -1199,48 +1307,131 @@ int sqlite3BtreeClose(Btree *p){ ** this handle. */ sqlite3BtreeRollback(p); - sqlite3_free(p); + sqlite3BtreeLeave(p); -#ifndef SQLITE_OMIT_SHARED_CACHE /* If there are still other outstanding references to the shared-btree ** structure, return now. The remainder of this procedure cleans ** up the shared-btree. */ - assert( pBt->nRef>0 ); - pBt->nRef--; - if( pBt->nRef ){ - return SQLITE_OK; + assert( p->wantToLock==0 && p->locked==0 ); + if( !p->sharable || removeFromSharingList(pBt) ){ + /* The pBt is no longer on the sharing list, so we can access + ** it without having to hold the mutex. + ** + ** Clean out and delete the BtShared object. + */ + assert( !pBt->pCursor ); + assert( pBt->nRef==0 ); + sqlite3PagerClose(pBt->pPager); + if( pBt->xFreeSchema && pBt->pSchema ){ + pBt->xFreeSchema(pBt->pSchema); + } + sqlite3_free(pBt->pSchema); + sqlite3_free(pBt); } - /* Remove the shared-btree from the thread wide list. Call - ** ThreadDataReadOnly() and then cast away the const property of the - ** pointer to avoid allocating thread data if it is not really required. - */ - pTsd = (ThreadData *)sqlite3ThreadDataReadOnly(); - if( pTsd->pBtree==pBt ){ - assert( pTsd==sqlite3ThreadData() ); - pTsd->pBtree = pBt->pNext; - }else{ - BtShared *pPrev; - for(pPrev=pTsd->pBtree; pPrev && pPrev->pNext!=pBt; pPrev=pPrev->pNext){} - if( pPrev ){ - assert( pTsd==sqlite3ThreadData() ); - pPrev->pNext = pBt->pNext; - } +#ifndef SQLITE_OMIT_SHARED_CACHE + else{ + assert( p->wantToLock==0 ); + assert( p->locked==0 ); + assert( p->sharable ); + if( p->pPrev ) p->pPrev->pNext = p->pNext; + if( p->pNext ) p->pNext->pPrev = p->pPrev; } #endif - /* Close the pager and free the shared-btree structure */ - assert( !pBt->pCursor ); - sqlite3PagerClose(pBt->pPager); - if( pBt->xFreeSchema && pBt->pSchema ){ - pBt->xFreeSchema(pBt->pSchema); - } - sqlite3_free(pBt->pSchema); - sqlite3_free(pBt); + sqlite3_free(p); return SQLITE_OK; } +#ifndef SQLITE_OMIT_SHARED_CACHE +/* +** Enter a mutex on the given BTree object. +** +** If the object is not sharable, then no mutex is ever required +** and this routine is a no-op. The underlying mutex is non-recursive. +** But we keep a reference count in Btree.wantToLock so the behavior +** of this interface is recursive. +** +** To avoid deadlocks, multiple Btrees are locked in the same order +** by all database connections. The p->pNext is a list of other +** Btrees belonging to the same database connection as the p Btree +** which need to be locked after p. If we cannot get a lock on +** p, then first unlock all of the others on p->pNext, then wait +** for the lock to become available on p, then relock all of the +** subsequent Btrees that desire a lock. +*/ +void sqlite3BtreeEnter(Btree *p){ + Btree *pLater; + + /* Some basic sanity checking on the Btree. The list of Btrees + ** connected by pNext and pPrev should be in sorted order by + ** Btree.pBt value. All elements of the list should belong to + ** the same connection. Only shared Btrees are on the list. */ + assert( p->pNext==0 || p->pNext->pBt>p->pBt ); + assert( p->pPrev==0 || p->pPrev->pBtpBt ); + assert( p->pNext==0 || p->pNext->pSqlite==p->pSqlite ); + assert( p->pPrev==0 || p->pPrev->pSqlite==p->pSqlite ); + assert( p->sharable || (p->pNext==0 && p->pPrev==0) ); + + /* Check for locking consistency */ + assert( !p->locked || p->wantToLock>0 ); + assert( p->sharable || p->wantToLock==0 ); + + if( !p->sharable ) return; + p->wantToLock++; + if( p->locked ) return; + + /* In most cases, we should be able to acquire the lock we + ** want without having to go throught the ascending lock + ** procedure that follows. Just be sure not to block. + */ + if( sqlite3_mutex_try(p->pBt->mutex)==SQLITE_OK ){ + p->locked = 1; + return; + } + + /* To avoid deadlock, first release all locks with a larger + ** BtShared address. Then acquire our lock. Then reacquire + ** the other BtShared locks that we used to hold in ascending + ** order. + */ + for(pLater=p->pNext; pLater; pLater=pLater->pNext){ + assert( pLater->sharable ); + assert( pLater->pNext==0 || pLater->pNext->pBt>pLater->pBt ); + assert( !pLater->locked || pLater->wantToLock>0 ); + if( pLater->locked ){ + sqlite3_mutex_leave(pLater->pBt->mutex); + pLater->locked = 0; + } + } + sqlite3_mutex_enter(p->pBt->mutex); + for(pLater=p->pNext; pLater; pLater=pLater->pNext){ + if( pLater->wantToLock ){ + sqlite3_mutex_enter(pLater->pBt->mutex); + pLater->locked = 1; + } + } +} +#endif /* !SQLITE_OMIT_SHARED_CACHE */ + +/* +** Exit the recursive mutex on a Btree. +*/ +#ifndef SQLITE_OMIT_SHARED_CACHE +void sqlite3BtreeLeave(Btree *p){ + if( p->sharable ){ + assert( p->wantToLock>0 ); + p->wantToLock--; + if( p->wantToLock==0 ){ + assert( p->locked ); + sqlite3_mutex_leave(p->pBt->mutex); + p->locked = 0; + } + } +} +#endif /* !SQLITE_OMIT_SHARED_CACHE */ + /* ** Change the busy handler callback function. */ diff --git a/src/btree.h b/src/btree.h index bf8929d4f6..d67a432aee 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.82 2007/05/08 21:45:27 drh Exp $ +** @(#) $Id: btree.h,v 1.83 2007/08/17 01:14:38 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -59,6 +59,14 @@ int sqlite3BtreeOpen( #define BTREE_OMIT_JOURNAL 1 /* Do not use journal. No argument */ #define BTREE_NO_READLOCK 2 /* Omit readlocks on readonly files */ #define BTREE_MEMORY 4 /* In-memory DB. No argument */ +#define BTREE_READONLY 8 /* Open the database in read-only mode */ +#define BTREE_READWRITE 16 /* Open for both reading and writing */ +#define BTREE_CREATE 32 /* Create the database if it does not exist */ + +/* Additional values for the 4th argument of sqlite3BtreeOpen that +** are not associated with PAGER_ values. +*/ +#define BTREE_PRIVATE 64 /* Never share with other connections */ int sqlite3BtreeClose(Btree*); int sqlite3BtreeSetBusyHandler(Btree*,BusyHandler*); @@ -87,6 +95,19 @@ void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); int sqlite3BtreeSchemaLocked(Btree *); int sqlite3BtreeLockTable(Btree *, int, u8); +/* +** If we are not using shared cache, then there is no need to +** 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 + void sqlite3BtreeEnter(Btree*); + void sqlite3BtreeLeave(Btree*); +#endif + const char *sqlite3BtreeGetFilename(Btree *); const char *sqlite3BtreeGetDirname(Btree *); const char *sqlite3BtreeGetJournalname(Btree *); diff --git a/src/btreeInt.h b/src/btreeInt.h index 2964ad3b92..179b1f219b 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.5 2007/06/15 12:06:59 drh Exp $ +** $Id: btreeInt.h,v 1.6 2007/08/17 01:14:38 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -248,7 +248,7 @@ typedef struct BtLock BtLock; /* ** Page type flags. An ORed combination of these flags appear as the -** first byte of every BTree page. +** first byte of on-disk image of every BTree page. */ #define PTF_INTKEY 0x01 #define PTF_ZERODATA 0x02 @@ -276,8 +276,8 @@ struct MemPage { u8 hasData; /* True if this page stores data */ u8 hdrOffset; /* 100 for page 1. 0 otherwise */ u8 childPtrSize; /* 0 if leaf==1. 4 if leaf==0 */ - u16 maxLocal; /* Copy of Btree.maxLocal or Btree.maxLeaf */ - u16 minLocal; /* Copy of Btree.minLocal or Btree.minLeaf */ + u16 maxLocal; /* Copy of BtShared.maxLocal or BtShared.maxLeaf */ + u16 minLocal; /* Copy of BtShared.minLocal or BtShared.minLeaf */ u16 cellOffset; /* Index in aData of first cell pointer */ u16 idxParent; /* Index in parent of this node */ u16 nFree; /* Number of free bytes on the page */ @@ -286,8 +286,8 @@ struct MemPage { u8 *pCell; /* Pointers to the body of the overflow cell */ u16 idx; /* Insert this cell before idx-th non-overflow cell */ } aOvfl[5]; - BtShared *pBt; /* Pointer back to BTree structure */ - u8 *aData; /* Pointer back to the start of the page */ + BtShared *pBt; /* Pointer to BtShared that this page is part of */ + u8 *aData; /* Pointer to disk image of the page data */ DbPage *pDbPage; /* Pager page handle */ Pgno pgno; /* Page number for this page */ MemPage *pParent; /* The parent of this page. NULL for root */ @@ -300,11 +300,30 @@ struct MemPage { */ #define EXTRA_SIZE sizeof(MemPage) -/* Btree handle */ +/* A Btree handle +** +** A database connection contains a pointer to an instance of +** this object for every database file that it has open. This structure +** is opaque to the database connection. The database connection cannot +** see the internals of this structure and only deals with pointers to +** this structure. +** +** For some database files, the same underlying database cache might be +** shared between multiple connections. In that case, each contection +** has it own pointer to this object. But each instance of this object +** points to the same BtShared object. The database cache and the +** schema associated with the database file are all contained within +** the BtShared object. +*/ struct Btree { - sqlite3 *pSqlite; - BtShared *pBt; - u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ + sqlite3 *pSqlite; /* The database connection holding this btree */ + BtShared *pBt; /* Sharable content of this btree */ + u8 inTrans; /* TRANS_NONE, TRANS_READ or TRANS_WRITE */ + u8 sharable; /* True if we can share pBt with other pSqlite */ + u8 locked; /* True if pSqlite currently has pBt locked */ + int wantToLock; /* Number of nested calls to sqlite3BtreeEnter() */ + Btree *pNext; /* List of Btrees with the same pSqlite value */ + Btree *pPrev; /* Back pointer of the same list */ }; /* @@ -312,15 +331,21 @@ struct Btree { ** ** If the shared-data extension is enabled, there may be multiple users ** of the Btree structure. At most one of these may open a write transaction, -** but any number may have active read transactions. Variable Btree.pDb -** points to the handle that owns any current write-transaction. +** but any number may have active read transactions. */ #define TRANS_NONE 0 #define TRANS_READ 1 #define TRANS_WRITE 2 /* -** Everything we need to know about an open database +** An instance of this object represents a single database file. +** +** A single database file can be in use as the same time by two +** or more database connections. When two or more connections are +** sharing the same database file, each connection has it own +** private Btree object for the file and each of those Btrees points +** to this one BtShared object. BtShared.nRef is the number of +** connections currently sharing this database file. */ struct BtShared { Pager *pPager; /* The page cache */ @@ -350,6 +375,7 @@ struct BtShared { void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */ void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */ #ifndef SQLITE_OMIT_SHARED_CACHE + sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */ BtLock *pLock; /* List of locks held on this shared-btree struct */ BtShared *pNext; /* Next in ThreadData.pBtree linked list */ #endif @@ -373,9 +399,15 @@ struct CellInfo { }; /* -** A cursor is a pointer to a particular entry in the BTree. +** A cursor is a pointer to a particular entry within a particular +** b-tree within a database file. +** ** The entry is identified by its MemPage and the index in ** MemPage.aCell[] of the entry. +** +** When a single database file can shared by two more database connections, +** but cursors cannot be shared. Each cursor is associated with a +** particular database connection identified BtCursor.pBtree.pSqlite. */ struct BtCursor { Btree *pBtree; /* The Btree to which this cursor belongs */ @@ -530,8 +562,6 @@ struct BtLock { ** of handle p (type Btree*) are internally consistent. */ #define btreeIntegrity(p) \ - assert( p->inTrans!=TRANS_NONE || p->pBt->nTransactionpBt->nRef ); \ - assert( p->pBt->nTransaction<=p->pBt->nRef ); \ assert( p->pBt->inTransaction!=TRANS_NONE || p->pBt->nTransaction==0 ); \ assert( p->pBt->inTransaction>=p->inTrans ); diff --git a/src/build.c b/src/build.c index 510682f411..bd7b7305a2 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.435 2007/08/16 10:09:02 danielk1977 Exp $ +** $Id: build.c,v 1.436 2007/08/17 01:14:38 drh Exp $ */ #include "sqliteInt.h" #include @@ -69,7 +69,7 @@ void sqlite3TableLock( int nBytes; TableLock *p; - if( 0==sqlite3ThreadDataReadOnly()->useSharedData || iDb<0 ){ + if( iDb<0 ){ return; } @@ -100,7 +100,6 @@ void sqlite3TableLock( static void codeTableLocks(Parse *pParse){ int i; Vdbe *pVdbe; - assert( sqlite3ThreadDataReadOnly()->useSharedData || pParse->nTableLock==0 ); if( 0==(pVdbe = sqlite3GetVdbe(pParse)) ){ return; diff --git a/src/main.c b/src/main.c index 8457d0b4d7..4e8b7a8a77 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.382 2007/08/16 13:01:45 drh Exp $ +** $Id: main.c,v 1.383 2007/08/17 01:14:38 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -1235,35 +1235,6 @@ int sqlite3Corrupt(void){ } #endif - -#ifndef SQLITE_OMIT_SHARED_CACHE -/* -** Enable or disable the shared pager and schema features for the -** current thread. -** -** This routine should only be called when there are no open -** database connections. -*/ -int sqlite3_enable_shared_cache(int enable){ - ThreadData *pTd = sqlite3ThreadData(); - if( pTd ){ - /* It is only legal to call sqlite3_enable_shared_cache() when there - ** are no currently open b-trees that were opened by the calling thread. - ** This condition is only easy to detect if the shared-cache were - ** previously enabled (and is being disabled). - */ - if( pTd->pBtree && !enable ){ - assert( pTd->useSharedData ); - return SQLITE_MISUSE; - } - - pTd->useSharedData = enable; - /* sqlite3ReleaseThreadData(); */ - } - return sqlite3ApiExit(0, SQLITE_OK); -} -#endif - /* ** This is a convenience routine that makes sure that all thread-specific ** data for this thread has been deallocated. diff --git a/src/mutex.c b/src/mutex.c index 13e15b5c7b..ffee6d7850 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.3 2007/08/16 19:40:17 drh Exp $ +** $Id: mutex.c,v 1.4 2007/08/17 01:14:38 drh Exp $ */ /* @@ -228,17 +228,19 @@ void sqlite3_mutex_enter(sqlite3_mutex *pMutex){ pthread_mutex_lock(&p->mutex); }else{ struct RMutex *p = (struct RMutex*)pMutex; - pthread_mutex_lock(&p->auxMutex); - if( p->nRef==0 ){ - p->nRef++; - p->owner = pthread_self(); - pthread_mutex_lock(&p->mainMutex); - pthread_mutex_unlock(&p->auxMutex); - }else if( pthread_equal(p->owner, pthread_self()) ){ - p->nRef++; - pthread_mutex_unlock(&p->auxMutex); - }else{ - while( p->nRef ){ + while(1){ + pthread_mutex_lock(&p->auxMutex); + if( p->nRef==0 ){ + p->nRef++; + p->owner = pthread_self(); + pthread_mutex_lock(&p->mainMutex); + pthread_mutex_unlock(&p->auxMutex); + break; + }else if( pthread_equal(p->owner, pthread_self()) ){ + p->nRef++; + pthread_mutex_unlock(&p->auxMutex); + break; + }else{ pthread_mutex_unlock(&p->auxMutex); pthread_mutex_lock(&p->mainMutex); pthread_mutex_unlock(&p->mainMutex); diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 872a0f26e7..8492399d2e 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.226 2007/08/16 19:40:17 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.227 2007/08/17 01:14:38 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -2175,6 +2175,9 @@ int sqlite3_global_recover(void); ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. +** +** These routines must be called from the same thread as +** the SQL function that supplied the sqlite3_value* parameters. */ const void *sqlite3_value_blob(sqlite3_value*); int sqlite3_value_bytes(sqlite3_value*); @@ -2206,6 +2209,9 @@ int sqlite3_value_numeric_type(sqlite3_value*); ** [sqlite3_context | SQL function context] that is the first ** parameter to the callback routine that implements the aggregate ** function. +** +** This routine must be called from the same thread in which +** the aggregate SQL function was originally invoked. */ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); @@ -2216,6 +2222,9 @@ void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); ** and [sqlite3_create_function16()] routines ** used to register user functions is available to ** the implementation of the function using this call. +** +** This routine must be called from the same thread in which +** the SQL function was originally invoked. */ void *sqlite3_user_data(sqlite3_context*); @@ -2248,6 +2257,9 @@ void *sqlite3_user_data(sqlite3_context*); ** In practice, meta-data is preserved between function calls for ** expressions that are constant at compile time. This includes literal ** values and SQL variables. +** +** These routine must be called from the same thread in which +** the SQL function was originally invoked. */ void *sqlite3_get_auxdata(sqlite3_context*, int); void sqlite3_set_auxdata(sqlite3_context*, int, void*, void (*)(void*)); @@ -2294,6 +2306,9 @@ typedef void (*sqlite3_destructor_type)(void*); ** The sqlite3_result_toobig() cause the function implementation ** to throw and error indicating that a string or BLOB is to long ** to represent. +** +** These routines must be called from within the same thread as +** the SQL function associated with the [sqlite3_context] pointer. */ void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); void sqlite3_result_double(sqlite3_context*, double); @@ -3234,8 +3249,9 @@ int sqlite3_unregister_vfs(sqlite3_vfs*); ** The sqlite3_mutex_free() routine deallocates a previously ** allocated dynamic mutex. SQLite is careful to deallocate every ** dynamic mutex that it allocates. The dynamic mutexes must not be in -** use when they are deallocated. Static mutexes do not need to be -** deallocated and SQLite never bothers to do so. +** use when they are deallocated. Attempting to deallocate a static +** mutex results in undefined behavior. SQLite never deallocates +** a static mutex. ** ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. If another thread is already within the mutex, diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 716df8c97c..7a7238320d 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.589 2007/08/16 13:01:45 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.590 2007/08/17 01:14:38 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -399,6 +399,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 */ struct sqlite3InitInfo { /* Information used during initialization */ int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ diff --git a/src/test1.c b/src/test1.c index 791fa9c02c..599ef2fac6 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.262 2007/08/16 13:01:45 drh Exp $ +** $Id: test1.c,v 1.263 2007/08/17 01:14:38 drh Exp $ */ #include "sqliteInt.h" #include "tcl.h" @@ -1312,6 +1312,7 @@ static int test_enable_shared( int rc; int enable; int ret = 0; + extern int sqlite3SharedCacheEnabled; if( objc!=2 ){ Tcl_WrongNumArgs(interp, 1, objv, "BOOLEAN"); @@ -1320,7 +1321,7 @@ static int test_enable_shared( if( Tcl_GetBooleanFromObj(interp, objv[1], &enable) ){ return TCL_ERROR; } - ret = sqlite3ThreadDataReadOnly()->useSharedData; + ret = sqlite3SharedCacheEnabled; rc = sqlite3_enable_shared_cache(enable); if( rc!=SQLITE_OK ){ Tcl_SetResult(interp, (char *)sqlite3ErrStr(rc), TCL_STATIC); diff --git a/src/test_btree.c b/src/test_btree.c index ef9eb35cba..3647a5b0ba 100644 --- a/src/test_btree.c +++ b/src/test_btree.c @@ -13,7 +13,7 @@ ** is not included in the SQLite library. It is used for automated ** testing of the SQLite library. ** -** $Id: test_btree.c,v 1.2 2007/05/08 11:27:16 drh Exp $ +** $Id: test_btree.c,v 1.3 2007/08/17 01:14:39 drh Exp $ */ #include "btreeInt.h" #include @@ -140,17 +140,15 @@ int sqlite3BtreeSharedCacheReport( Tcl_Obj *CONST objv[] ){ #ifndef SQLITE_OMIT_SHARED_CACHE - const ThreadData *pTd = sqlite3ThreadDataReadOnly(); - if( pTd->useSharedData ){ - BtShared *pBt; - Tcl_Obj *pRet = Tcl_NewObj(); - for(pBt=pTd->pBtree; pBt; pBt=pBt->pNext){ - const char *zFile = sqlite3PagerFilename(pBt->pPager); - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1)); - Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef)); - } - Tcl_SetObjResult(interp, pRet); + extern BtShared *sqlite3SharedCacheList; + BtShared *pBt; + Tcl_Obj *pRet = Tcl_NewObj(); + for(pBt=sqlite3SharedCacheList; pBt; pBt=pBt->pNext){ + const char *zFile = sqlite3PagerFilename(pBt->pPager); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewStringObj(zFile, -1)); + Tcl_ListObjAppendElement(interp, pRet, Tcl_NewIntObj(pBt->nRef)); } + Tcl_SetObjResult(interp, pRet); #endif return TCL_OK; } diff --git a/src/vtab.c b/src/vtab.c index 2f305acca0..29dbc3aa9c 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to help implement virtual tables. ** -** $Id: vtab.c,v 1.50 2007/08/16 10:09:03 danielk1977 Exp $ +** $Id: vtab.c,v 1.51 2007/08/17 01:14:39 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE #include "sqliteInt.h" @@ -162,7 +162,7 @@ void sqlite3VtabBeginParse( Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ -#ifndef SQLITE_OMIT_SHARED_CACHE +#if 0 /* FIX ME */ if( sqlite3ThreadDataReadOnly()->useSharedData ){ sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode"); return;