diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/btmutex.c | 4 | ||||
-rw-r--r-- | src/btree.c | 76 | ||||
-rw-r--r-- | src/btree.h | 3 | ||||
-rw-r--r-- | src/btreeInt.h | 10 | ||||
-rw-r--r-- | src/build.c | 7 | ||||
-rw-r--r-- | src/pragma.c | 5 | ||||
-rw-r--r-- | src/vdbe.c | 16 | ||||
-rw-r--r-- | src/vdbe.h | 4 | ||||
-rw-r--r-- | src/vdbeaux.c | 50 | ||||
-rw-r--r-- | src/vdbeblob.c | 4 |
10 files changed, 131 insertions, 48 deletions
diff --git a/src/btmutex.c b/src/btmutex.c index 61644a8af..1f6343423 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -10,7 +10,7 @@ ** ************************************************************************* ** -** $Id: btmutex.c,v 1.6 2007/08/29 17:43:20 drh Exp $ +** $Id: btmutex.c,v 1.7 2007/08/30 01:19:59 drh Exp $ ** ** This file contains code used to implement mutexes on Btree objects. ** This code really belongs in btree.c. But btree.c is getting too @@ -239,7 +239,7 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){ int i, j; BtShared *pBt; - if( pBtree->sharable==0 ) return; + if( pBtree==0 || pBtree->sharable==0 ) return; #ifndef NDEBUG { for(i=0; i<pArray->nMutex; i++){ diff --git a/src/btree.c b/src/btree.c index abbef41c6..bda46fbad 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.419 2007/08/29 19:15:08 drh Exp $ +** $Id: btree.c,v 1.420 2007/08/30 01:19:59 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. @@ -356,7 +356,10 @@ static void clearCursorPosition(BtCursor *pCur){ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){ int rc; assert( cursorHoldsMutex(pCur) ); - assert( pCur->eState==CURSOR_REQUIRESEEK ); + assert( pCur->eState>=CURSOR_REQUIRESEEK ); + if( pCur->eState==CURSOR_FAULT ){ + return pCur->skip; + } #ifndef SQLITE_OMIT_INCRBLOB if( pCur->isIncrblobHandle ){ return SQLITE_ABORT; @@ -373,7 +376,7 @@ int sqlite3BtreeRestoreOrClearCursorPosition(BtCursor *pCur){ } #define restoreOrClearCursorPosition(p) \ - (p->eState==CURSOR_REQUIRESEEK ? \ + (p->eState>=CURSOR_REQUIRESEEK ? \ sqlite3BtreeRestoreOrClearCursorPosition(p) : \ SQLITE_OK) @@ -2394,18 +2397,51 @@ int sqlite3BtreeCommit(Btree *p){ ** Return the number of write-cursors open on this handle. This is for use ** in assert() expressions, so it is only compiled if NDEBUG is not ** defined. +** +** For the purposes of this routine, a write-cursor is any cursor that +** is capable of writing to the databse. That means the cursor was +** originally opened for writing and the cursor has not be disabled +** by having its state changed to CURSOR_FAULT. */ static int countWriteCursors(BtShared *pBt){ BtCursor *pCur; int r = 0; for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){ - if( pCur->wrFlag ) r++; + if( pCur->wrFlag && pCur->eState!=CURSOR_FAULT ) r++; } return r; } #endif /* +** This routine sets the state to CURSOR_FAULT and the error +** code to errCode for every cursor on BtShared that pBtree +** references. +** +** Every cursor is tripped, including cursors that belong +** to other database connections that happen to be sharing +** the cache with pBtree. +** +** This routine gets called when a rollback occurs. +** All cursors using the same cache must be tripped +** to prevent them from trying to use the btree after +** the rollback. The rollback may have deleted tables +** or moved root pages, so it is not sufficient to +** save the state of the cursor. The cursor must be +** invalidated. +*/ +void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){ + BtCursor *p; + sqlite3BtreeEnter(pBtree); + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + clearCursorPosition(p); + p->eState = CURSOR_FAULT; + p->skip = errCode; + } + sqlite3BtreeLeave(pBtree); +} + +/* ** Rollback the transaction in progress. All cursors will be ** invalided by this operation. Any attempt to use a cursor ** that was open at the beginning of this operation will result @@ -2430,15 +2466,7 @@ int sqlite3BtreeRollback(Btree *p){ ** we cannot simply return the error to the caller. Instead, abort ** all queries that may be using any of the cursors that failed to save. */ - while( pBt->pCursor ){ - sqlite3 *db = pBt->pCursor->pBtree->pSqlite; - if( db ){ - /**** FIX ME: This can deadlock ****/ - sqlite3_mutex_enter(db->mutex); - sqlite3AbortOtherActiveVdbes(db, 0); - sqlite3_mutex_leave(db->mutex); - } - } + sqlite3BtreeTripAllCursors(p, rc); } #endif btreeIntegrity(p); @@ -3343,7 +3371,13 @@ static int moveToRoot(BtCursor *pCur){ BtShared *pBt = p->pBt; assert( cursorHoldsMutex(pCur) ); - if( pCur->eState==CURSOR_REQUIRESEEK ){ + assert( CURSOR_INVALID < CURSOR_REQUIRESEEK ); + assert( CURSOR_VALID < CURSOR_REQUIRESEEK ); + assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); + if( pCur->eState>=CURSOR_REQUIRESEEK ){ + if( pCur->eState==CURSOR_FAULT ){ + return pCur->skip; + } clearCursorPosition(pCur); } pRoot = pCur->pPage; @@ -5514,6 +5548,9 @@ int sqlite3BtreeInsert( if( checkReadLocks(pCur->pBtree, pCur->pgnoRoot, pCur) ){ return SQLITE_LOCKED; /* The table pCur points to has a read lock */ } + if( pCur->eState==CURSOR_FAULT ){ + return pCur->skip; + } /* Save the positions of any other cursors open on this table */ clearCursorPosition(pCur); @@ -5592,6 +5629,9 @@ int sqlite3BtreeDelete(BtCursor *pCur){ return rc; } assert( !pBt->readOnly ); + if( pCur->eState==CURSOR_FAULT ){ + return pCur->skip; + } if( pCur->idx >= pPage->nCell ){ return SQLITE_ERROR; /* The cursor is not pointing to anything */ } @@ -6789,8 +6829,12 @@ int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ assert( cursorHoldsMutex(pCsr) ); assert( sqlite3_mutex_held(pCsr->pBtree->pSqlite->mutex) ); assert(pCsr->isIncrblobHandle); - if( pCsr->eState==CURSOR_REQUIRESEEK ){ - return SQLITE_ABORT; + if( pCsr->eState>=CURSOR_REQUIRESEEK ){ + if( pCsr->eState==CURSOR_FAULT ){ + return pCsr->skip; + }else{ + return SQLITE_ABORT; + } } /* Check some preconditions: diff --git a/src/btree.h b/src/btree.h index 032ce9f28..26418990d 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.91 2007/08/29 17:43:20 drh Exp $ +** @(#) $Id: btree.h,v 1.92 2007/08/30 01:19:59 drh Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -125,6 +125,7 @@ int sqlite3BtreeDropTable(Btree*, int, int*); int sqlite3BtreeClearTable(Btree*, int); int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); +void sqlite3BtreeTripAllCursors(Btree*, int); int sqlite3BtreeCursor( Btree*, /* BTree containing table to open */ diff --git a/src/btreeInt.h b/src/btreeInt.h index 6ce4052fa..09f147423 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.12 2007/08/29 00:33:07 drh Exp $ +** $Id: btreeInt.h,v 1.13 2007/08/30 01:19:59 drh Exp $ ** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to @@ -466,10 +466,18 @@ struct BtCursor { ** in variables BtCursor.pKey and BtCursor.nKey. When a cursor is in ** this state, restoreOrClearCursorPosition() can be called to attempt to ** seek the cursor to the saved position. +** +** CURSOR_FAULT: +** A unrecoverable error (an I/O error or a malloc failure) has occurred +** on a different connection that shares the BtShared cache with this +** cursor. The error has left the cache in an inconsistent state. +** Do nothing else with this cursor. Any attempt to use the cursor +** should return the error code stored in BtCursor.skip */ #define CURSOR_INVALID 0 #define CURSOR_VALID 1 #define CURSOR_REQUIRESEEK 2 +#define CURSOR_FAULT 3 /* ** The TRACE macro will print high-level status information about the diff --git a/src/build.c b/src/build.c index 430fe0070..c8f50e07b 100644 --- a/src/build.c +++ b/src/build.c @@ -22,7 +22,7 @@ ** COMMIT ** ROLLBACK ** -** $Id: build.c,v 1.442 2007/08/29 14:06:23 danielk1977 Exp $ +** $Id: build.c,v 1.443 2007/08/30 01:19:59 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -164,7 +164,7 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeJumpHere(v, pParse->cookieGoto-1); for(iDb=0, mask=1; iDb<db->nDb; mask<<=1, iDb++){ if( (mask & pParse->cookieMask)==0 ) continue; - sqlite3VdbeUsesBtree(v, iDb, db->aDb[iDb].pBt); + sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp(v, OP_Transaction, iDb, (mask & pParse->writeMask)!=0); sqlite3VdbeAddOp(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb]); } @@ -847,6 +847,7 @@ void sqlite3StartTable( ** set them now. */ sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); /* file_format */ + sqlite3VdbeUsesBtree(v, iDb); lbl = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp(v, OP_If, 0, lbl); fileFormat = (db->flags & SQLITE_LegacyFileFmt)!=0 ? @@ -2693,6 +2694,7 @@ void sqlite3MinimumFileFormat(Parse *pParse, int iDb, int minFormat){ v = sqlite3GetVdbe(pParse); if( v ){ sqlite3VdbeAddOp(v, OP_ReadCookie, iDb, 1); + sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0); sqlite3VdbeAddOp(v, OP_Ge, 0, sqlite3VdbeCurrentAddr(v)+3); sqlite3VdbeAddOp(v, OP_Integer, minFormat, 0); @@ -3089,6 +3091,7 @@ void sqlite3BeginTransaction(Parse *pParse, int type){ if( type!=TK_DEFERRED ){ for(i=0; i<db->nDb; i++){ sqlite3VdbeAddOp(v, OP_Transaction, i, (type==TK_EXCLUSIVE)+1); + sqlite3VdbeUsesBtree(v, i); } } sqlite3VdbeAddOp(v, OP_AutoCommit, 0, 0); diff --git a/src/pragma.c b/src/pragma.c index 13eee4848..873c76140 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -11,7 +11,7 @@ ************************************************************************* ** This file contains code used to implement the PRAGMA command. ** -** $Id: pragma.c,v 1.146 2007/08/21 10:44:16 drh Exp $ +** $Id: pragma.c,v 1.147 2007/08/30 01:19:59 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -300,6 +300,7 @@ void sqlite3Pragma( }; int addr; if( sqlite3ReadSchema(pParse) ) goto pragma_out; + sqlite3VdbeUsesBtree(v, iDb); if( !zRight ){ sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "cache_size", P3_STATIC); @@ -455,6 +456,7 @@ void sqlite3Pragma( sqlite3VdbeChangeP2(v, iAddr+2, iAddr+4); sqlite3VdbeChangeP1(v, iAddr+4, eAuto-1); sqlite3VdbeChangeP1(v, iAddr+5, iDb); + sqlite3VdbeUsesBtree(v, iDb); } } } @@ -1043,6 +1045,7 @@ void sqlite3Pragma( ){ int iCookie; /* Cookie index. 0 for schema-cookie, 6 for user-cookie. */ + sqlite3VdbeUsesBtree(v, iDb); switch( zLeft[0] ){ case 's': case 'S': iCookie = 0; diff --git a/src/vdbe.c b/src/vdbe.c index d0baf886b..f01a7930e 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -43,7 +43,7 @@ ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. ** -** $Id: vdbe.c,v 1.647 2007/08/29 12:31:28 danielk1977 Exp $ +** $Id: vdbe.c,v 1.648 2007/08/30 01:19:59 drh Exp $ */ #include "sqliteInt.h" #include <ctype.h> @@ -2421,6 +2421,7 @@ case OP_Statement: { /* no-push */ Btree *pBt; if( i>=0 && i<db->nDb && (pBt = db->aDb[i].pBt)!=0 && !(db->autoCommit) ){ assert( sqlite3BtreeIsInTrans(pBt) ); + assert( (p->btreeMask & (1<<i))!=0 ); if( !sqlite3BtreeIsInStmt(pBt) ){ rc = sqlite3BtreeBeginStmt(pBt); p->openedStatement = 1; @@ -2511,6 +2512,7 @@ case OP_Transaction: { /* no-push */ Btree *pBt; assert( i>=0 && i<db->nDb ); + assert( (p->btreeMask & (1<<i))!=0 ); pBt = db->aDb[i].pBt; if( pBt ){ @@ -2557,6 +2559,7 @@ case OP_ReadCookie: { } assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); + assert( (p->btreeMask & (1<<iDb))!=0 ); /* The indexing of meta values at the schema layer is off by one from ** the indexing in the btree layer. The btree considers meta[0] to ** be the number of free pages in the database (a read-only value) @@ -2585,6 +2588,7 @@ case OP_SetCookie: { /* no-push */ Db *pDb; assert( pOp->p2<SQLITE_N_BTREE_META ); assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); assert( pTos>=p->aStack ); @@ -2629,6 +2633,7 @@ case OP_VerifyCookie: { /* no-push */ int iMeta; Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ rc = sqlite3BtreeGetMeta(pBt, 1, (u32 *)&iMeta); @@ -2720,6 +2725,7 @@ case OP_OpenWrite: { /* no-push */ assert( (pTos->flags & MEM_Dyn)==0 ); pTos--; assert( iDb>=0 && iDb<db->nDb ); + assert( (p->btreeMask & (1<<iDb))!=0 ); pDb = &db->aDb[iDb]; pX = pDb->pBt; assert( pX!=0 ); @@ -4075,6 +4081,7 @@ case OP_Destroy: { rc = SQLITE_LOCKED; }else{ assert( iCnt==1 ); + assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeDropTable(db->aDb[pOp->p2].pBt, pOp->p1, &iMoved); pTos++; pTos->flags = MEM_Int; @@ -4136,6 +4143,7 @@ case OP_Clear: { /* no-push */ } } #endif + assert( (p->btreeMask & (1<<pOp->p2))!=0 ); rc = sqlite3BtreeClearTable(db->aDb[pOp->p2].pBt, pOp->p1); break; } @@ -4166,6 +4174,7 @@ case OP_CreateTable: { int flags; Db *pDb; assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ @@ -4327,6 +4336,8 @@ case OP_IntegrityCk: { aRoot[j] = 0; popStack(&pTos, nRoot); pTos++; + assert( pOp->p2>=0 && pOp->p2<db->nDb ); + assert( (p->btreeMask & (1<<pOp->p2))!=0 ); z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p2].pBt, aRoot, nRoot, pnErr->u.i, &nErr); pnErr->u.i -= nErr; @@ -4701,6 +4712,7 @@ case OP_IncrVacuum: { /* no-push */ Btree *pBt; assert( pOp->p1>=0 && pOp->p1<db->nDb ); + assert( (p->btreeMask & (1<<pOp->p1))!=0 ); pBt = db->aDb[pOp->p1].pBt; rc = sqlite3BtreeIncrVacuum(pBt); if( rc==SQLITE_DONE ){ @@ -4752,6 +4764,8 @@ case OP_TableLock: { /* no-push */ if( isWriteLock ){ p1 = (-1*p1)-1; } + assert( p1>=0 && p1<db->nDb ); + assert( (p->btreeMask & (1<<p1))!=0 ); rc = sqlite3BtreeLockTable(db->aDb[p1].pBt, pOp->p2, isWriteLock); if( rc==SQLITE_LOCKED ){ const char *z = (const char *)pOp->p3; diff --git a/src/vdbe.h b/src/vdbe.h index 181121385..75f186cb0 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -15,7 +15,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.112 2007/08/28 22:24:35 drh Exp $ +** $Id: vdbe.h,v 1.113 2007/08/30 01:19:59 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -120,7 +120,7 @@ void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); void sqlite3VdbeJumpHere(Vdbe*, int addr); void sqlite3VdbeChangeToNoop(Vdbe*, int addr, int N); void sqlite3VdbeChangeP3(Vdbe*, int addr, const char *zP1, int N); -void sqlite3VdbeUsesBtree(Vdbe*, int, Btree*); +void sqlite3VdbeUsesBtree(Vdbe*, int); VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeDelete(Vdbe*); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index b0dd09ee9..4af63d2fc 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -660,12 +660,15 @@ static char *displayP3(Op *pOp, char *zTemp, int nTemp){ ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. ** */ -void sqlite3VdbeUsesBtree(Vdbe *p, int i, Btree *pBtree){ +void sqlite3VdbeUsesBtree(Vdbe *p, int i){ + int mask; assert( i>=0 && i<p->db->nDb ); assert( i<sizeof(p->btreeMask)*8 ); - assert( p->db->aDb[i].pBt==pBtree ); - p->btreeMask |= 1<<i; - sqlite3BtreeMutexArrayInsert(&p->aMutex, pBtree); + mask = 1<<i; + if( (p->btreeMask & mask)==0 ){ + p->btreeMask |= mask; + sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt); + } } @@ -1301,21 +1304,28 @@ static void checkActiveVdbeCnt(sqlite3 *db){ #endif /* -** Find every active VM other than pVdbe and change its status to -** aborted. This happens when one VM causes a rollback due to an -** ON CONFLICT ROLLBACK clause (for example). The other VMs must be -** aborted so that they do not have data rolled out from underneath -** them leading to a segfault. +** For every Btree that in database connection db which +** has been modified, "trip" or invalidate each cursor in +** that Btree might have been modified so that the cursor +** can never be used again. This happens when a rollback +*** occurs. We have to trip all the other cursors, even +** cursor from other VMs in different database connections, +** so that none of them try to use the data at which they +** were pointing and which now may have been changed due +** to the rollback. +** +** Remember that a rollback can delete tables complete and +** reorder rootpages. So it is not sufficient just to save +** the state of the cursor. We have to invalidate the cursor +** so that it is never used again. */ -void sqlite3AbortOtherActiveVdbes(sqlite3 *db, Vdbe *pExcept){ - Vdbe *pOther; - for(pOther=db->pVdbe; pOther; pOther=pOther->pNext){ - if( pOther==pExcept ) continue; - if( pOther->magic!=VDBE_MAGIC_RUN || pOther->pc<0 ) continue; - checkActiveVdbeCnt(db); - closeAllCursorsExceptActiveVtabs(pOther); - checkActiveVdbeCnt(db); - pOther->aborted = 1; +void invalidateCursorsOnModifiedBtrees(sqlite3 *db){ + int i; + for(i=0; i<db->nDb; i++){ + Btree *p = db->aDb[i].pBt; + if( p && sqlite3BtreeIsInTrans(p) ){ + sqlite3BtreeTripAllCursors(p, SQLITE_ABORT); + } } } @@ -1440,7 +1450,7 @@ int sqlite3VdbeHalt(Vdbe *p){ /* We are forced to roll back the active transaction. Before doing ** so, abort any other statements this handle currently has active. */ - sqlite3AbortOtherActiveVdbes(db, p); + invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); db->autoCommit = 1; } @@ -1480,7 +1490,7 @@ int sqlite3VdbeHalt(Vdbe *p){ }else if( p->errorAction==OE_Abort ){ xFunc = sqlite3BtreeRollbackStmt; }else{ - sqlite3AbortOtherActiveVdbes(db, p); + invalidateCursorsOnModifiedBtrees(db); sqlite3RollbackAll(db); db->autoCommit = 1; } diff --git a/src/vdbeblob.c b/src/vdbeblob.c index 161242db5..d56fbd127 100644 --- a/src/vdbeblob.c +++ b/src/vdbeblob.c @@ -12,7 +12,7 @@ ** ** This file contains code used to implement incremental BLOB I/O. ** -** $Id: vdbeblob.c,v 1.15 2007/08/29 17:43:20 drh Exp $ +** $Id: vdbeblob.c,v 1.16 2007/08/30 01:19:59 drh Exp $ */ #include "sqliteInt.h" @@ -164,7 +164,7 @@ int sqlite3_blob_open( sqlite3VdbeChangeP2(v, 1, pTab->pSchema->schema_cookie); /* Make sure a mutex is held on the table to be accessed */ - sqlite3VdbeUsesBtree(v, iDb, db->aDb[iDb].pBt); + sqlite3VdbeUsesBtree(v, iDb); /* Configure the db number pushed onto the stack */ sqlite3VdbeChangeP1(v, 2, iDb); |