mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Remove the BtreeMutexArray object - use the Vdbe.btreeMask field to accomplish
the same result. Add a generation counter to btree mutexes in order to assert that mutexes are never temporarily dropped over a range of instructions in order to do deadlock avoidance in some subroutine. Lock all btrees in any Vdbe program that uses OP_ParseSchema. FossilOrigin-Name: d81708f7d1eee399bfe76f6b8dac950a85dc2582
This commit is contained in:
22
manifest
22
manifest
@@ -1,5 +1,5 @@
|
||||
C Make\ssure\sthat\sthe\sconstant\s1\sis\scast\sto\syDbType\sbefore\sshifting\sto\screate\nan\sattached\sdatabase\smask.\s\sThis\scheck-in\sis\sa\sfollow-up\sand\sfix\sto\sthe\n[7aaf8772274422]\schange\sthat\sincreases\sthe\smaximum\snumber\sof\sattached\sdatabases\nfrom\s30\sto\s62.
|
||||
D 2011-04-03T18:19:25.822
|
||||
C Remove\sthe\sBtreeMutexArray\sobject\s-\suse\sthe\sVdbe.btreeMask\sfield\sto\saccomplish\nthe\ssame\sresult.\s\sAdd\sa\sgeneration\scounter\sto\sbtree\smutexes\sin\sorder\sto\sassert\nthat\smutexes\sare\snever\stemporarily\sdropped\sover\sa\srange\sof\sinstructions\sin\sorder\nto\sdo\sdeadlock\savoidance\sin\ssome\ssubroutine.\s\sLock\sall\sbtrees\sin\sany\sVdbe\nprogram\sthat\suses\sOP_ParseSchema.
|
||||
D 2011-04-04T00:14:43.936
|
||||
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
|
||||
F Makefile.in 7a4d9524721d40ef9ee26f93f9bd6a51dba106f2
|
||||
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
|
||||
@@ -120,10 +120,10 @@ F src/attach.c 438ea6f6b5d5961c1f49b737f2ce0f14ce7c6877
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 537f89c7ef5021cb580f31f782e556ffffcb2ed1
|
||||
F src/bitvec.c af50f1c8c0ff54d6bdb7a80e2fceca5a93670bef
|
||||
F src/btmutex.c 5ba4ca0216bd9406eccbf50aaf35fef6912c4e2c
|
||||
F src/btmutex.c 3e595ee1bb99e3a1f16824137b435ffc97c98e5f
|
||||
F src/btree.c 107723ed4f9bdb55213ba6164c30c49af75f4bf9
|
||||
F src/btree.h 8d36f774ec4b1d0027b8966f8c03d9a72a518c14
|
||||
F src/btreeInt.h 29ab8a4172f12b04fac34c5a0b76d0df750ddc34
|
||||
F src/btree.h a840a20c1969391f98ee06960d5ee2dc460186b3
|
||||
F src/btreeInt.h 6714ce2f5e879eb9a904a6a4575dc4faa4f29991
|
||||
F src/build.c 3a8c6c4b1e16798755d46e699b7fcc12b9f27b2b
|
||||
F src/callback.c 5069f224882cbdccd559f591271d28d7f37745bc
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
@@ -235,11 +235,11 @@ F src/update.c 81911be16ece3c3e7716aa18565b4814ec41f8b9
|
||||
F src/utf.c d83650c3ea08f7407bd9d0839d9885241c209c60
|
||||
F src/util.c cd997077bad039efc0597eb027c929658f93c018
|
||||
F src/vacuum.c 924bd1bcee2dfb05376f79845bd3b4cec7b54b2f
|
||||
F src/vdbe.c d975adcef86051b6b51c5d2394dd2288e0b0ca81
|
||||
F src/vdbe.c e4d83957b4d102d542fb249bb46c7b55cbb67e4f
|
||||
F src/vdbe.h 4de0efb4b0fdaaa900cf419b35c458933ef1c6d2
|
||||
F src/vdbeInt.h 8ee9302ecc1036509956df8aceea6b0b5e0e231c
|
||||
F src/vdbeInt.h 7e2f028ecc1a9faa6f253e7aa8d89cae03662bae
|
||||
F src/vdbeapi.c a09ad9164cafc505250d5dd6b69660c960f1308c
|
||||
F src/vdbeaux.c ad921cf58e8c20e1c0a9ef0b7e0f557eabdc7d07
|
||||
F src/vdbeaux.c 1a98cc953a316739c3e9a2930ae0eee517d37def
|
||||
F src/vdbeblob.c c3ccb7c8732858c680f442932e66ad06bb036562
|
||||
F src/vdbemem.c 0498796b6ffbe45e32960d6a1f5adfb6e419883b
|
||||
F src/vdbetrace.c 3ba13bc32bdf16d2bdea523245fd16736bed67b5
|
||||
@@ -926,7 +926,7 @@ F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/split-sqlite3c.tcl d9be87f1c340285a3e081eb19b4a247981ed290c
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P 28c5f12e98655d5c6167b13e8a15085eca43dd5b
|
||||
R 62a2deace5cc72cdc4511f7f5b5d9fa5
|
||||
P e2a09ea73c76a0bec1e09d1fc11092517e3ebdf9
|
||||
R 5eec5b6c06a45dcfcec6cc2765ee2ab8
|
||||
U drh
|
||||
Z 905bef73ef8c243b27a93f7a5f38565e
|
||||
Z 9f8e37bd1010a379addad04d1c835970
|
||||
|
@@ -1 +1 @@
|
||||
e2a09ea73c76a0bec1e09d1fc11092517e3ebdf9
|
||||
d81708f7d1eee399bfe76f6b8dac950a85dc2582
|
137
src/btmutex.c
137
src/btmutex.c
@@ -39,15 +39,34 @@ static void lockBtreeMutex(Btree *p){
|
||||
** clear the p->locked boolean.
|
||||
*/
|
||||
static void unlockBtreeMutex(Btree *p){
|
||||
BtShared *pBt = p->pBt;
|
||||
assert( p->locked==1 );
|
||||
assert( sqlite3_mutex_held(p->pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(pBt->mutex) );
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
assert( p->db==p->pBt->db );
|
||||
assert( p->db==pBt->db );
|
||||
|
||||
sqlite3_mutex_leave(p->pBt->mutex);
|
||||
pBt->iMutexCounter++;
|
||||
sqlite3_mutex_leave(pBt->mutex);
|
||||
p->locked = 0;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
/*
|
||||
** Return the number of times that the mutex has been exited for
|
||||
** the given btree.
|
||||
**
|
||||
** This is a small circular counter that wraps around to zero on
|
||||
** overflow. It is used only for sanity checking - to verify that
|
||||
** mutexes are held continously by asserting that the value of
|
||||
** this counter at the beginning of a region is the same as at
|
||||
** the end.
|
||||
*/
|
||||
u32 sqlite3BtreeMutexCounter(Btree *p){
|
||||
assert( p->locked==1 || p->sharable==0 );
|
||||
return p->pBt->iMutexCounter;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Enter a mutex on the given BTree object.
|
||||
**
|
||||
@@ -92,6 +111,24 @@ void sqlite3BtreeEnter(Btree *p){
|
||||
p->wantToLock++;
|
||||
if( p->locked ) return;
|
||||
|
||||
/* Increment the mutex counter on all locked btrees in the same
|
||||
** database connection. This simulates the unlocking that would
|
||||
** occur on a worst-case mutex dead-lock avoidance scenario.
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
{
|
||||
int ii;
|
||||
sqlite3 *db = p->db;
|
||||
Btree *pOther;
|
||||
for(ii=0; ii<db->nDb; ii++){
|
||||
if( ii==1 ) continue;
|
||||
pOther = db->aDb[ii].pBt;
|
||||
if( pOther==0 || pOther->sharable==0 || pOther->locked==0 ) continue;
|
||||
pOther->pBt->iMutexCounter++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
@@ -251,97 +288,17 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){
|
||||
}
|
||||
#endif /* NDEBUG */
|
||||
|
||||
#else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */
|
||||
/*
|
||||
** Add a new Btree pointer to a BtreeMutexArray.
|
||||
** if the pointer can possibly be shared with
|
||||
** another database connection.
|
||||
** The following are special cases for mutex enter routines for use
|
||||
** in single threaded applications that use shared cache. Except for
|
||||
** these two routines, all mutex operations are no-ops in that case and
|
||||
** are null #defines in btree.h.
|
||||
**
|
||||
** The pointers are kept in sorted order by pBtree->pBt. That
|
||||
** way when we go to enter all the mutexes, we can enter them
|
||||
** in order without every having to backup and retry and without
|
||||
** worrying about deadlock.
|
||||
**
|
||||
** The number of shared btrees will always be small (usually 0 or 1)
|
||||
** so an insertion sort is an adequate algorithm here.
|
||||
** If shared cache is disabled, then all btree mutex routines, including
|
||||
** the ones below, are no-ops and are null #defines in btree.h.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray *pArray, Btree *pBtree){
|
||||
int i, j;
|
||||
BtShared *pBt;
|
||||
if( pBtree==0 || pBtree->sharable==0 ) return;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
assert( pArray->aBtree[i]!=pBtree );
|
||||
}
|
||||
}
|
||||
#endif
|
||||
assert( pArray->nMutex>=0 );
|
||||
assert( pArray->nMutex<ArraySize(pArray->aBtree)-1 );
|
||||
pBt = pBtree->pBt;
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
assert( pArray->aBtree[i]!=pBtree );
|
||||
if( pArray->aBtree[i]->pBt>pBt ){
|
||||
for(j=pArray->nMutex; j>i; j--){
|
||||
pArray->aBtree[j] = pArray->aBtree[j-1];
|
||||
}
|
||||
pArray->aBtree[i] = pBtree;
|
||||
pArray->nMutex++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
pArray->aBtree[pArray->nMutex++] = pBtree;
|
||||
}
|
||||
|
||||
/*
|
||||
** Enter the mutex of every btree in the array. This routine is
|
||||
** called at the beginning of sqlite3VdbeExec(). The mutexes are
|
||||
** exited at the end of the same function.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray *pArray){
|
||||
int i;
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
Btree *p = pArray->aBtree[i];
|
||||
/* Some basic sanity checking */
|
||||
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( !p->locked || p->wantToLock>0 );
|
||||
|
||||
/* We should already hold a lock on the database connection */
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
/* The Btree is sharable because only sharable Btrees are entered
|
||||
** into the array in the first place. */
|
||||
assert( p->sharable );
|
||||
|
||||
p->wantToLock++;
|
||||
if( !p->locked ){
|
||||
lockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Leave the mutex of every btree in the group.
|
||||
*/
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray *pArray){
|
||||
int i;
|
||||
for(i=0; i<pArray->nMutex; i++){
|
||||
Btree *p = pArray->aBtree[i];
|
||||
/* Some basic sanity checking */
|
||||
assert( i==0 || pArray->aBtree[i-1]->pBt<p->pBt );
|
||||
assert( p->locked );
|
||||
assert( p->wantToLock>0 );
|
||||
|
||||
/* We should already hold a lock on the database connection */
|
||||
assert( sqlite3_mutex_held(p->db->mutex) );
|
||||
|
||||
p->wantToLock--;
|
||||
if( p->wantToLock==0 ){
|
||||
unlockBtreeMutex(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
void sqlite3BtreeEnter(Btree *p){
|
||||
p->pBt->db = p->db;
|
||||
}
|
||||
|
20
src/btree.h
20
src/btree.h
@@ -39,18 +39,6 @@
|
||||
typedef struct Btree Btree;
|
||||
typedef struct BtCursor BtCursor;
|
||||
typedef struct BtShared BtShared;
|
||||
typedef struct BtreeMutexArray BtreeMutexArray;
|
||||
|
||||
/*
|
||||
** This structure records all of the Btrees that need to hold
|
||||
** a mutex before we enter sqlite3VdbeExec(). The Btrees are
|
||||
** are placed in aBtree[] in order of aBtree[]->pBt. That way,
|
||||
** we can always lock and unlock them all quickly.
|
||||
*/
|
||||
struct BtreeMutexArray {
|
||||
int nMutex;
|
||||
Btree *aBtree[SQLITE_MAX_ATTACHED+1];
|
||||
};
|
||||
|
||||
|
||||
int sqlite3BtreeOpen(
|
||||
@@ -228,23 +216,19 @@ void sqlite3BtreeCursorList(Btree*);
|
||||
void sqlite3BtreeEnterCursor(BtCursor*);
|
||||
void sqlite3BtreeLeaveCursor(BtCursor*);
|
||||
void sqlite3BtreeLeaveAll(sqlite3*);
|
||||
void sqlite3BtreeMutexArrayEnter(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayLeave(BtreeMutexArray*);
|
||||
void sqlite3BtreeMutexArrayInsert(BtreeMutexArray*, Btree*);
|
||||
#ifndef NDEBUG
|
||||
/* These routines are used inside assert() statements only. */
|
||||
int sqlite3BtreeHoldsMutex(Btree*);
|
||||
int sqlite3BtreeHoldsAllMutexes(sqlite3*);
|
||||
u32 sqlite3BtreeMutexCounter(Btree*);
|
||||
#endif
|
||||
#else
|
||||
|
||||
# define sqlite3BtreeLeave(X)
|
||||
# define sqlite3BtreeMutexCounter(X) 0
|
||||
# define sqlite3BtreeEnterCursor(X)
|
||||
# define sqlite3BtreeLeaveCursor(X)
|
||||
# define sqlite3BtreeLeaveAll(X)
|
||||
# define sqlite3BtreeMutexArrayEnter(X)
|
||||
# define sqlite3BtreeMutexArrayLeave(X)
|
||||
# define sqlite3BtreeMutexArrayInsert(X,Y)
|
||||
|
||||
# define sqlite3BtreeHoldsMutex(X) 1
|
||||
# define sqlite3BtreeHoldsAllMutexes(X) 1
|
||||
|
@@ -426,7 +426,7 @@ struct BtShared {
|
||||
u32 nPage; /* Number of pages in the database */
|
||||
void *pSchema; /* Pointer to space allocated by sqlite3BtreeSchema() */
|
||||
void (*xFreeSchema)(void*); /* Destructor for BtShared.pSchema */
|
||||
sqlite3_mutex *mutex; /* Non-recursive mutex required to access this struct */
|
||||
sqlite3_mutex *mutex; /* Non-recursive mutex required to access this object */
|
||||
Bitvec *pHasContent; /* Set of pages moved to free-list this transaction */
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int nRef; /* Number of references to this structure */
|
||||
@@ -435,6 +435,7 @@ struct BtShared {
|
||||
Btree *pWriter; /* Btree with currently open write transaction */
|
||||
u8 isExclusive; /* True if pWriter has an EXCLUSIVE lock on the db */
|
||||
u8 isPending; /* If waiting for read-locks to clear */
|
||||
u16 iMutexCounter; /* The number of mutex_leave(mutex) calls */
|
||||
#endif
|
||||
u8 *pTmpSpace; /* BtShared.pageSize bytes of space for tmp use */
|
||||
};
|
||||
|
81
src/vdbe.c
81
src/vdbe.c
@@ -571,7 +571,7 @@ int sqlite3VdbeExec(
|
||||
/*** INSERT STACK UNION HERE ***/
|
||||
|
||||
assert( p->magic==VDBE_MAGIC_RUN ); /* sqlite3_step() verifies this */
|
||||
sqlite3VdbeMutexArrayEnter(p);
|
||||
sqlite3VdbeEnter(p);
|
||||
if( p->rc==SQLITE_NOMEM ){
|
||||
/* This happens if a malloc() inside a call to sqlite3_column_text() or
|
||||
** sqlite3_column_text16() failed. */
|
||||
@@ -1394,6 +1394,7 @@ case OP_Function: {
|
||||
ctx.pColl = pOp[-1].p4.pColl;
|
||||
}
|
||||
(*ctx.pFunc->xFunc)(&ctx, n, apVal); /* IMP: R-24505-23230 */
|
||||
sqlite3VdbeMutexResync(p);
|
||||
if( db->mallocFailed ){
|
||||
/* Even though a malloc() has failed, the implementation of the
|
||||
** user function may have called an sqlite3_result_XXX() function
|
||||
@@ -1404,6 +1405,17 @@ case OP_Function: {
|
||||
goto no_mem;
|
||||
}
|
||||
|
||||
/* The app-defined function has done something that as caused this
|
||||
** statement to expire. (Perhaps the function called sqlite3_exec()
|
||||
** with a CREATE TABLE statement.)
|
||||
*/
|
||||
#if 0
|
||||
if( p->expired ){
|
||||
rc = SQLITE_ABORT;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If any auxiliary data functions have been called by this user function,
|
||||
** immediately call the destructor for any non-static values.
|
||||
*/
|
||||
@@ -2650,6 +2662,7 @@ case OP_Savepoint: {
|
||||
if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
|
||||
sqlite3ExpirePreparedStatements(db);
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
sqlite3VdbeMutexResync(p);
|
||||
db->flags = (db->flags | SQLITE_InternChanges);
|
||||
}
|
||||
}
|
||||
@@ -2940,6 +2953,7 @@ case OP_VerifyCookie: {
|
||||
*/
|
||||
if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){
|
||||
sqlite3ResetInternalSchema(db, pOp->p1);
|
||||
sqlite3VdbeMutexResync(p);
|
||||
}
|
||||
|
||||
p->expired = 1;
|
||||
@@ -4622,29 +4636,22 @@ case OP_ParseSchema: {
|
||||
char *zSql;
|
||||
InitData initData;
|
||||
|
||||
/* Any prepared statement that invokes this opcode will hold mutexes
|
||||
** on every btree. This is a prerequisite for invoking
|
||||
** sqlite3InitCallback().
|
||||
*/
|
||||
#ifdef SQLITE_DEBUG
|
||||
for(iDb=0; iDb<db->nDb; iDb++){
|
||||
assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
}
|
||||
#endif
|
||||
assert( p->btreeMask == ~(yDbMask)0 );
|
||||
|
||||
|
||||
iDb = pOp->p1;
|
||||
assert( iDb>=0 && iDb<db->nDb );
|
||||
|
||||
/* When this opcode is invoked, it is guaranteed that the b-tree mutex
|
||||
** is held and the schema is loaded for database iDb. However, at the
|
||||
** start of the sqlite3_exec() call below, SQLite will invoke
|
||||
** sqlite3BtreeEnterAll(). If all mutexes are not already held, the iDb
|
||||
** mutex may be temporarily released to avoid deadlock. If this happens,
|
||||
** then some other thread may delete the in-memory schema of database iDb
|
||||
** before the SQL statement runs. The schema will not be reloaded because
|
||||
** the db->init.busy flag is set. This can result in a "no such table:
|
||||
** sqlite_master" or "malformed database schema" error being returned to
|
||||
** the user.
|
||||
**
|
||||
** To avoid this, obtain all mutexes and check that no other thread has
|
||||
** deleted the schema before calling sqlite3_exec(). If we find that the
|
||||
** another thread has deleted the schema, there is no need to update it.
|
||||
** The updated schema will be loaded from disk when it is next required.
|
||||
*/
|
||||
assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) );
|
||||
assert( DbHasProperty(db, iDb, DB_SchemaLoaded) );
|
||||
sqlite3BtreeEnterAll(db);
|
||||
if( DbHasProperty(db, iDb, DB_SchemaLoaded) ){
|
||||
/* Used to be a conditional */ {
|
||||
zMaster = SCHEMA_TABLE(iDb);
|
||||
initData.db = db;
|
||||
initData.iDb = pOp->p1;
|
||||
@@ -4665,7 +4672,6 @@ case OP_ParseSchema: {
|
||||
db->init.busy = 0;
|
||||
}
|
||||
}
|
||||
sqlite3BtreeLeaveAll(db);
|
||||
if( rc==SQLITE_NOMEM ){
|
||||
goto no_mem;
|
||||
}
|
||||
@@ -5190,11 +5196,25 @@ case OP_AggStep: {
|
||||
ctx.pColl = pOp[-1].p4.pColl;
|
||||
}
|
||||
(ctx.pFunc->xStep)(&ctx, n, apVal); /* IMP: R-24505-23230 */
|
||||
sqlite3VdbeMutexResync(p);
|
||||
if( ctx.isError ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&ctx.s));
|
||||
rc = ctx.isError;
|
||||
}
|
||||
|
||||
/* The app-defined function has done something that as caused this
|
||||
** statement to expire. (Perhaps the function called sqlite3_exec()
|
||||
** with a CREATE TABLE statement.)
|
||||
*/
|
||||
#if 0
|
||||
if( p->expired ){
|
||||
rc = SQLITE_ABORT;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
sqlite3VdbeMemRelease(&ctx.s);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5216,8 +5236,11 @@ case OP_AggFinal: {
|
||||
pMem = &aMem[pOp->p1];
|
||||
assert( (pMem->flags & ~(MEM_Null|MEM_Agg))==0 );
|
||||
rc = sqlite3VdbeMemFinalize(pMem, pOp->p4.pFunc);
|
||||
sqlite3VdbeMutexResync(p);
|
||||
if( rc ){
|
||||
sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(pMem));
|
||||
}else if( p->expired ){
|
||||
rc = SQLITE_ABORT;
|
||||
}
|
||||
sqlite3VdbeChangeEncoding(pMem, encoding);
|
||||
UPDATE_MAX_BLOBSIZE(pMem);
|
||||
@@ -5294,7 +5317,7 @@ case OP_JournalMode: { /* out2-prerelease */
|
||||
|
||||
/* This opcode is used in two places: PRAGMA journal_mode and ATTACH.
|
||||
** In PRAGMA journal_mode, the sqlite3VdbeUsesBtree() routine is called
|
||||
** when the statment is prepared and so p->aMutex.nMutex>0. All mutexes
|
||||
** when the statement is prepared and so p->btreeMask!=0. All mutexes
|
||||
** are already acquired. But when used in ATTACH, sqlite3VdbeUsesBtree()
|
||||
** is not called when the statement is prepared because it requires the
|
||||
** iDb index of the database as a parameter, and the database has not
|
||||
@@ -5303,12 +5326,11 @@ case OP_JournalMode: { /* out2-prerelease */
|
||||
** No other mutexes are required by the ATTACH command so this is safe
|
||||
** to do.
|
||||
*/
|
||||
assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 || p->aMutex.nMutex==0 );
|
||||
if( p->aMutex.nMutex==0 ){
|
||||
if( p->btreeMask==0 ){
|
||||
/* This occurs right after ATTACH. Get a mutex on the newly ATTACHed
|
||||
** database. */
|
||||
sqlite3VdbeUsesBtree(p, pOp->p1);
|
||||
sqlite3VdbeMutexArrayEnter(p);
|
||||
sqlite3VdbeEnter(p);
|
||||
}
|
||||
|
||||
pBt = db->aDb[pOp->p1].pBt;
|
||||
@@ -5946,13 +5968,16 @@ vdbe_error_halt:
|
||||
sqlite3VdbeHalt(p);
|
||||
if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1;
|
||||
rc = SQLITE_ERROR;
|
||||
if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0);
|
||||
if( resetSchemaOnFault ){
|
||||
sqlite3ResetInternalSchema(db, 0);
|
||||
sqlite3VdbeMutexResync(p);
|
||||
}
|
||||
|
||||
/* This is the only way out of this procedure. We have to
|
||||
** release the mutexes on btrees that were acquired at the
|
||||
** top. */
|
||||
vdbe_return:
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
sqlite3VdbeLeave(p);
|
||||
return rc;
|
||||
|
||||
/* Jump to here if a string or blob larger than SQLITE_MAX_LENGTH
|
||||
|
@@ -303,9 +303,9 @@ struct Vdbe {
|
||||
u8 isPrepareV2; /* True if prepared with prepare_v2() */
|
||||
int nChange; /* Number of db changes made since last reset */
|
||||
yDbMask btreeMask; /* Bitmask of db->aDb[] entries referenced */
|
||||
u32 iMutexCounter; /* Mutex counter upon sqlite3VdbeEnter() */
|
||||
int iStatement; /* Statement number (or 0 if has not opened stmt) */
|
||||
int aCounter[3]; /* Counters used by sqlite3_stmt_status() */
|
||||
BtreeMutexArray aMutex; /* An array of Btree used here and needing locks */
|
||||
#ifndef SQLITE_OMIT_TRACE
|
||||
i64 startTime; /* Time when query started - used for profiling */
|
||||
#endif
|
||||
@@ -387,6 +387,9 @@ int sqlite3VdbeCloseStatement(Vdbe *, int);
|
||||
void sqlite3VdbeFrameDelete(VdbeFrame*);
|
||||
int sqlite3VdbeFrameRestore(VdbeFrame *);
|
||||
void sqlite3VdbeMemStoreType(Mem *pMem);
|
||||
void sqlite3VdbeEnter(Vdbe*);
|
||||
void sqlite3VdbeLeave(Vdbe*);
|
||||
void sqlite3VdbeMutexResync(Vdbe*);
|
||||
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbeMemPrepareToChange(Vdbe*,Mem*);
|
||||
@@ -398,12 +401,6 @@ int sqlite3VdbeCheckFk(Vdbe *, int);
|
||||
# define sqlite3VdbeCheckFk(p,i) 0
|
||||
#endif
|
||||
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
void sqlite3VdbeMutexArrayEnter(Vdbe *p);
|
||||
#else
|
||||
# define sqlite3VdbeMutexArrayEnter(p)
|
||||
#endif
|
||||
|
||||
int sqlite3VdbeMemTranslate(Mem*, u8);
|
||||
#ifdef SQLITE_DEBUG
|
||||
void sqlite3VdbePrintSql(Vdbe*);
|
||||
|
164
src/vdbeaux.c
164
src/vdbeaux.c
@@ -157,6 +157,11 @@ int sqlite3VdbeAddOp3(Vdbe *p, int op, int p1, int p2, int p3){
|
||||
pOp->p4.p = 0;
|
||||
pOp->p4type = P4_NOTUSED;
|
||||
p->expired = 0;
|
||||
if( op==OP_ParseSchema ){
|
||||
/* Any program that uses the OP_ParseSchema opcode needs to lock
|
||||
** all btrees. */
|
||||
p->btreeMask = ~(yDbMask)0;
|
||||
}
|
||||
#ifdef SQLITE_DEBUG
|
||||
pOp->zComment = 0;
|
||||
if( sqlite3VdbeAddopTrace ) sqlite3VdbePrintOp(0, i, &p->aOp[i]);
|
||||
@@ -457,7 +462,7 @@ VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){
|
||||
assert( aOp && !p->db->mallocFailed );
|
||||
|
||||
/* Check that sqlite3VdbeUsesBtree() was not called on this VM */
|
||||
assert( p->aMutex.nMutex==0 );
|
||||
assert( p->btreeMask==0 );
|
||||
|
||||
resolveP2Values(p, pnMaxArg);
|
||||
*pnOp = p->nOp;
|
||||
@@ -945,22 +950,123 @@ static char *displayP4(Op *pOp, char *zTemp, int nTemp){
|
||||
/*
|
||||
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
|
||||
**
|
||||
** The prepared statement has to know in advance which Btree objects
|
||||
** will be used so that it can acquire mutexes on them all in sorted
|
||||
** order (via sqlite3VdbeMutexArrayEnter(). Mutexes are acquired
|
||||
** in order (and released in reverse order) to avoid deadlocks.
|
||||
** The prepared statements need to know in advance the complete set of
|
||||
** attached databases that they will be using. A mask of these databases
|
||||
** is maintained in p->btreeMask and is used for locking and other purposes.
|
||||
*/
|
||||
void sqlite3VdbeUsesBtree(Vdbe *p, int i){
|
||||
yDbMask mask;
|
||||
assert( i>=0 && i<p->db->nDb && i<sizeof(yDbMask)*8 );
|
||||
assert( i<(int)sizeof(p->btreeMask)*8 );
|
||||
mask = ((yDbMask)1)<<i;
|
||||
if( (p->btreeMask & mask)==0 ){
|
||||
p->btreeMask |= mask;
|
||||
sqlite3BtreeMutexArrayInsert(&p->aMutex, p->db->aDb[i].pBt);
|
||||
}
|
||||
p->btreeMask |= ((yDbMask)1)<<i;
|
||||
}
|
||||
|
||||
/*
|
||||
** Compute the sum of all mutex counters for all btrees in the
|
||||
** given prepared statement.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
static u32 mutexCounterSum(Vdbe *p){
|
||||
u32 cntSum = 0;
|
||||
#ifdef SQLITE_DEBUG
|
||||
int i;
|
||||
yDbMask mask;
|
||||
sqlite3 *db = p->db;
|
||||
Db *aDb = db->aDb;
|
||||
int nDb = db->nDb;
|
||||
for(i=0, mask=1; i<nDb; i++, mask += mask){
|
||||
if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
|
||||
cntSum += sqlite3BtreeMutexCounter(aDb[i].pBt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return cntSum;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
|
||||
** this routine obtains the mutex associated with each BtShared structure
|
||||
** that may be accessed by the VM passed as an argument. In doing so it also
|
||||
** sets the BtShared.db member of each of the BtShared structures, ensuring
|
||||
** that the correct busy-handler callback is invoked if required.
|
||||
**
|
||||
** If SQLite is not threadsafe but does support shared-cache mode, then
|
||||
** sqlite3BtreeEnter() is invoked to set the BtShared.db variables
|
||||
** of all of BtShared structures accessible via the database handle
|
||||
** associated with the VM.
|
||||
**
|
||||
** If SQLite is not threadsafe and does not support shared-cache mode, this
|
||||
** function is a no-op.
|
||||
**
|
||||
** The p->btreeMask field is a bitmask of all btrees that the prepared
|
||||
** statement p will ever use. Let N be the number of bits in p->btreeMask
|
||||
** corresponding to btrees that use shared cache. Then the runtime of
|
||||
** this routine is N*N. But as N is rarely more than 1, this should not
|
||||
** be a problem.
|
||||
*/
|
||||
void sqlite3VdbeEnter(Vdbe *p){
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int i;
|
||||
yDbMask mask;
|
||||
sqlite3 *db = p->db;
|
||||
Db *aDb = db->aDb;
|
||||
int nDb = db->nDb;
|
||||
for(i=0, mask=1; i<nDb; i++, mask += mask){
|
||||
if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
|
||||
sqlite3BtreeEnter(aDb[i].pBt);
|
||||
}
|
||||
}
|
||||
p->iMutexCounter = mutexCounterSum(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock all of the btrees previously locked by a call to sqlite3VdbeEnter().
|
||||
*/
|
||||
void sqlite3VdbeLeave(Vdbe *p){
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
int i;
|
||||
yDbMask mask;
|
||||
sqlite3 *db = p->db;
|
||||
Db *aDb = db->aDb;
|
||||
int nDb = db->nDb;
|
||||
|
||||
/* Assert that the all mutexes have been held continously since
|
||||
** the most recent sqlite3VdbeEnter() or sqlite3VdbeMutexResync().
|
||||
*/
|
||||
assert( mutexCounterSum(p) == p->iMutexCounter );
|
||||
|
||||
for(i=0, mask=1; i<nDb; i++, mask += mask){
|
||||
if( i!=1 && (mask & p->btreeMask)!=0 && ALWAYS(aDb[i].pBt!=0) ){
|
||||
sqlite3BtreeLeave(aDb[i].pBt);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Recompute the sum of the mutex counters on all btrees used by the
|
||||
** prepared statement p.
|
||||
**
|
||||
** Call this routine while holding a sqlite3VdbeEnter() after doing something
|
||||
** that might cause one or more of the individual mutexes held by the
|
||||
** prepared statement to be released. Calling sqlite3BtreeEnter() on
|
||||
** any BtShared mutex which is not used by the prepared statement is one
|
||||
** way to cause one or more of the mutexes in the prepared statement
|
||||
** to be temporarily released. The anti-deadlocking logic in
|
||||
** sqlite3BtreeEnter() can cause mutexes to be released temporarily then
|
||||
** reacquired.
|
||||
**
|
||||
** Calling this routine is an acknowledgement that some of the individual
|
||||
** mutexes in the prepared statement might have been released and reacquired.
|
||||
** So checks to verify that mutex-protected content did not change
|
||||
** unexpectedly should accompany any call to this routine.
|
||||
*/
|
||||
void sqlite3VdbeMutexResync(Vdbe *p){
|
||||
#if !defined(SQLITE_OMIT_SHARED_CACHE) && defined(SQLITE_DEBUG)
|
||||
p->iMutexCounter = mutexCounterSum(p);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
|
||||
/*
|
||||
@@ -1959,33 +2065,6 @@ int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** If SQLite is compiled to support shared-cache mode and to be threadsafe,
|
||||
** this routine obtains the mutex associated with each BtShared structure
|
||||
** that may be accessed by the VM passed as an argument. In doing so it
|
||||
** sets the BtShared.db member of each of the BtShared structures, ensuring
|
||||
** that the correct busy-handler callback is invoked if required.
|
||||
**
|
||||
** If SQLite is not threadsafe but does support shared-cache mode, then
|
||||
** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables
|
||||
** of all of BtShared structures accessible via the database handle
|
||||
** associated with the VM. Of course only a subset of these structures
|
||||
** will be accessed by the VM, and we could use Vdbe.btreeMask to figure
|
||||
** that subset out, but there is no advantage to doing so.
|
||||
**
|
||||
** If SQLite is not threadsafe and does not support shared-cache mode, this
|
||||
** function is a no-op.
|
||||
*/
|
||||
#ifndef SQLITE_OMIT_SHARED_CACHE
|
||||
void sqlite3VdbeMutexArrayEnter(Vdbe *p){
|
||||
#if SQLITE_THREADSAFE
|
||||
sqlite3BtreeMutexArrayEnter(&p->aMutex);
|
||||
#else
|
||||
sqlite3BtreeEnterAll(p->db);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This function is called when a transaction opened by the database
|
||||
** handle associated with the VM passed as an argument is about to be
|
||||
@@ -2058,7 +2137,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
int isSpecialError; /* Set to true if a 'special' error */
|
||||
|
||||
/* Lock all btrees used by the statement */
|
||||
sqlite3VdbeMutexArrayEnter(p);
|
||||
sqlite3VdbeEnter(p);
|
||||
|
||||
/* Check for one of the special errors */
|
||||
mrc = p->rc & 0xff;
|
||||
@@ -2112,7 +2191,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
rc = sqlite3VdbeCheckFk(p, 1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( NEVER(p->readOnly) ){
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
sqlite3VdbeLeave(p);
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
rc = SQLITE_CONSTRAINT;
|
||||
@@ -2124,7 +2203,7 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
rc = vdbeCommit(db, p);
|
||||
}
|
||||
if( rc==SQLITE_BUSY && p->readOnly ){
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
sqlite3VdbeLeave(p);
|
||||
return SQLITE_BUSY;
|
||||
}else if( rc!=SQLITE_OK ){
|
||||
p->rc = rc;
|
||||
@@ -2196,7 +2275,8 @@ int sqlite3VdbeHalt(Vdbe *p){
|
||||
}
|
||||
|
||||
/* Release the locks */
|
||||
sqlite3BtreeMutexArrayLeave(&p->aMutex);
|
||||
sqlite3VdbeMutexResync(p);
|
||||
sqlite3VdbeLeave(p);
|
||||
}
|
||||
|
||||
/* We have successfully halted and closed the VM. Record this fact. */
|
||||
|
Reference in New Issue
Block a user