aboutsummaryrefslogtreecommitdiff
path: root/src/btree.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/btree.c')
-rw-r--r--src/btree.c119
1 files changed, 43 insertions, 76 deletions
diff --git a/src/btree.c b/src/btree.c
index e371f6184..8b57837b2 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.647 2009/07/02 05:23:26 danielk1977 Exp $
+** $Id: btree.c,v 1.648 2009/07/02 07:47:33 danielk1977 Exp $
**
** This file implements a external (disk-based) database using BTrees.
** See the header comment on "btreeInt.h" for additional information.
@@ -366,7 +366,10 @@ static void clearAllSharedCacheTableLocks(Btree *p){
assert( pLock->pBtree->inTrans>=pLock->eLock );
if( pLock->pBtree==p ){
*ppIter = pLock->pNext;
- sqlite3_free(pLock);
+ assert( pLock->iTable!=1 || pLock==&p->lock );
+ if( pLock->iTable!=1 ){
+ sqlite3_free(pLock);
+ }
}else{
ppIter = &pLock->pNext;
}
@@ -1605,6 +1608,10 @@ int sqlite3BtreeOpen(
}
p->inTrans = TRANS_NONE;
p->db = db;
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ p->lock.pBtree = p;
+ p->lock.iTable = 1;
+#endif
#if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO)
/*
@@ -2376,6 +2383,13 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
}
#endif
+ /* Any read-only or read-write transaction implies a read-lock on
+ ** page 1. So if some other shared-cache client already has a write-lock
+ ** on page 1, the transaction cannot be opened. */
+ if( SQLITE_OK!=(rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK)) ){
+ goto trans_begun;
+ }
+
do {
/* Call lockBtree() until either pBt->pPage1 is populated or
** lockBtree() returns something other than SQLITE_OK. lockBtree()
@@ -2406,6 +2420,14 @@ int sqlite3BtreeBeginTrans(Btree *p, int wrflag){
if( rc==SQLITE_OK ){
if( p->inTrans==TRANS_NONE ){
pBt->nTransaction++;
+#ifndef SQLITE_OMIT_SHARED_CACHE
+ if( p->sharable ){
+ assert( p->lock.pBtree==p && p->lock.iTable==1 );
+ p->lock.eLock = READ_LOCK;
+ p->lock.pNext = pBt->pLock;
+ pBt->pLock = &p->lock;
+ }
+#endif
}
p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);
if( p->inTrans>pBt->inTransaction ){
@@ -3188,17 +3210,11 @@ static int btreeCursor(
assert( sqlite3BtreeHoldsMutex(p) );
assert( wrFlag==0 || wrFlag==1 );
- /* The following assert statements verify that if this is a sharable b-tree
- ** database, the connection is holding the required table locks, and that
- ** no other connection has any open cursor that conflicts with this lock.
- **
- ** The exception to this is read-only cursors open on the schema table.
- ** Such a cursor is opened without a lock while reading the database
- ** schema. This is safe because BtShared.mutex is held for the entire
- ** lifetime of this cursor. */
- assert( (iTable==1 && wrFlag==0)
- || hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1)
- );
+ /* The following assert statements verify that if this is a sharable
+ ** b-tree database, the connection is holding the required table locks,
+ ** and that no other connection has any open cursor that conflicts with
+ ** this lock. */
+ assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) );
assert( wrFlag==0 || !hasReadConflicts(p, iTable) );
if( NEVER(wrFlag && pBt->readOnly) ){
@@ -6663,10 +6679,7 @@ static int btreeCreateTable(Btree *p, int *piTable, int flags){
** root page of the new table should go. meta[3] is the largest root-page
** created so far, so the new root-page is (meta[3]+1).
*/
- rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
- if( rc!=SQLITE_OK ){
- return rc;
- }
+ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot);
pgnoRoot++;
/* The new root-page may not be allocated on a pointer-map page, or the
@@ -6901,11 +6914,7 @@ static int btreeDropTable(Btree *p, Pgno iTable, int *piMoved){
#else
if( pBt->autoVacuum ){
Pgno maxRootPgno;
- rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
- if( rc!=SQLITE_OK ){
- releasePage(pPage);
- return rc;
- }
+ sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno);
if( iTable==maxRootPgno ){
/* If the table being dropped is the table with the largest root-page
@@ -6981,6 +6990,9 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
/*
+** This function may only be called if the b-tree connection already
+** has a read or write transaction open on the database.
+**
** Read the meta-information out of a database file. Meta[0]
** is the number of free pages currently in the database. Meta[1]
** through meta[15] are available for use by higher layers. Meta[0]
@@ -6990,71 +7002,24 @@ int sqlite3BtreeDropTable(Btree *p, int iTable, int *piMoved){
** layer (and the SetCookie and ReadCookie opcodes) the number of
** free pages is not visible. So Cookie[0] is the same as Meta[1].
*/
-int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
- DbPage *pDbPage = 0;
- int rc;
- unsigned char *pP1;
+void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){
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
- ** 1 is treated as a special case by querySharedCacheTableLock()
- ** and setSharedCacheTableLock()).
- */
- rc = querySharedCacheTableLock(p, 1, READ_LOCK);
- if( rc!=SQLITE_OK ){
- sqlite3BtreeLeave(p);
- return rc;
- }
-
+ assert( p->inTrans>TRANS_NONE );
+ assert( SQLITE_OK==querySharedCacheTableLock(p, 1, READ_LOCK) );
+ assert( pBt->pPage1 );
assert( idx>=0 && idx<=15 );
- if( pBt->pPage1 ){
- /* The b-tree is already holding a reference to page 1 of the database
- ** file. In this case the required meta-data value can be read directly
- ** from the page data of this reference. This is slightly faster than
- ** requesting a new reference from the pager layer.
- */
- pP1 = (unsigned char *)pBt->pPage1->aData;
- }else{
- /* The b-tree does not have a reference to page 1 of the database file.
- ** Obtain one from the pager layer.
- */
- rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage);
- if( rc ){
- sqlite3BtreeLeave(p);
- return rc;
- }
- pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage);
- }
- *pMeta = get4byte(&pP1[36 + idx*4]);
- /* If the b-tree is not holding a reference to page 1, then one was
- ** requested from the pager layer in the above block. Release it now.
- */
- if( !pBt->pPage1 ){
- sqlite3PagerUnref(pDbPage);
- }
+ *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]);
- /* If autovacuumed is disabled in this build but we are trying to
- ** access an autovacuumed database, then make the database readonly.
- */
+ /* If auto-vacuum is disabled in this build and this is an auto-vacuum
+ ** database, mark the database as read-only. */
#ifdef SQLITE_OMIT_AUTOVACUUM
if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1;
#endif
- /* If there is currently an open transaction, grab a read-lock
- ** on page 1 of the database file. This is done to make sure that
- ** no other connection can modify the meta value just read from
- ** the database until the transaction is concluded.
- */
- if( p->inTrans>0 ){
- rc = setSharedCacheTableLock(p, 1, READ_LOCK);
- }
sqlite3BtreeLeave(p);
- return rc;
}
/*
@@ -7740,10 +7705,12 @@ int sqlite3BtreeSchemaLocked(Btree *p){
*/
int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){
int rc = SQLITE_OK;
+ assert( p->inTrans!=TRANS_NONE );
if( p->sharable ){
u8 lockType = READ_LOCK + isWriteLock;
assert( READ_LOCK+1==WRITE_LOCK );
assert( isWriteLock==0 || isWriteLock==1 );
+
sqlite3BtreeEnter(p);
rc = querySharedCacheTableLock(p, iTab, lockType);
if( rc==SQLITE_OK ){