diff options
author | drh <drh@noemail.net> | 2011-04-05 13:12:28 +0000 |
---|---|---|
committer | drh <drh@noemail.net> | 2011-04-05 13:12:28 +0000 |
commit | 959b6a95ee5f398ce3db3b3199f4e579e621e43c (patch) | |
tree | 732ad37cd655bc396a327918c6b60639937e6708 /src | |
parent | 8d129422ed9a04bac684864ba127fa6278cc7513 (diff) | |
parent | 03faf63b57a76c925c0e46e8bcb3b86cfe098590 (diff) | |
download | sqlite-959b6a95ee5f398ce3db3b3199f4e579e621e43c.tar.gz sqlite-959b6a95ee5f398ce3db3b3199f4e579e621e43c.zip |
Merge the scheme-parse-refactor changes into trunk: (1) added
sqlite3SchemaMutexHeld() asserts, (2) Use -1 instead of 0 to mean
"all" in sqlite3ResetInternalSchema(), and other cosmetic changes.
FossilOrigin-Name: 5db4511d8a77b74be3503a7c34257ef6b07541f5
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze.c | 4 | ||||
-rw-r--r-- | src/attach.c | 4 | ||||
-rw-r--r-- | src/backup.c | 2 | ||||
-rw-r--r-- | src/btmutex.c | 25 | ||||
-rw-r--r-- | src/btree.h | 2 | ||||
-rw-r--r-- | src/build.c | 63 | ||||
-rw-r--r-- | src/callback.c | 6 | ||||
-rw-r--r-- | src/fkey.c | 1 | ||||
-rw-r--r-- | src/insert.c | 2 | ||||
-rw-r--r-- | src/main.c | 7 | ||||
-rw-r--r-- | src/pragma.c | 5 | ||||
-rw-r--r-- | src/prepare.c | 7 | ||||
-rw-r--r-- | src/sqliteInt.h | 20 | ||||
-rw-r--r-- | src/status.c | 2 | ||||
-rw-r--r-- | src/trigger.c | 10 | ||||
-rw-r--r-- | src/vacuum.c | 5 | ||||
-rw-r--r-- | src/vdbe.c | 19 | ||||
-rw-r--r-- | src/vdbeaux.c | 2 | ||||
-rw-r--r-- | src/vtab.c | 10 |
19 files changed, 150 insertions, 46 deletions
diff --git a/src/analyze.c b/src/analyze.c index 231d41431..fd040a7ec 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -149,6 +149,7 @@ static void analyzeOneTable( assert( sqlite3BtreeHoldsAllMutexes(db) ); iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, db->aDb[iDb].zName ) ){ @@ -390,6 +391,7 @@ static void analyzeDatabase(Parse *pParse, int iDb){ pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, 0, 0); iMem = pParse->nMem+1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ Table *pTab = (Table*)sqliteHashData(k); analyzeOneTable(pParse, pTab, 0, iStatCur, iMem); @@ -600,9 +602,9 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ assert( iDb>=0 && iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 ); - assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); /* Clear any prior statistics */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); diff --git a/src/attach.c b/src/attach.c index 37b61935f..fe88aa73c 100644 --- a/src/attach.c +++ b/src/attach.c @@ -200,7 +200,7 @@ static void attachFunc( db->aDb[iDb].pBt = 0; db->aDb[iDb].pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->nDb = iDb; if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; @@ -272,7 +272,7 @@ static void detachFunc( sqlite3BtreeClose(pDb->pBt); pDb->pBt = 0; pDb->pSchema = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); return; detach_error: diff --git a/src/backup.c b/src/backup.c index 82be9635b..c062744c5 100644 --- a/src/backup.c +++ b/src/backup.c @@ -401,7 +401,7 @@ int sqlite3_backup_step(sqlite3_backup *p, int nPage){ int nDestTruncate; if( p->pDestDb ){ - sqlite3ResetInternalSchema(p->pDestDb, 0); + sqlite3ResetInternalSchema(p->pDestDb, -1); } /* Set nDestTruncate to the final number of pages in the destination diff --git a/src/btmutex.c b/src/btmutex.c index 42d0bdcbc..33b9f86a0 100644 --- a/src/btmutex.c +++ b/src/btmutex.c @@ -288,6 +288,31 @@ int sqlite3BtreeHoldsAllMutexes(sqlite3 *db){ } #endif /* NDEBUG */ +#ifndef NDEBUG +/* +** Return true if the correct mutexes are held for accessing the +** db->aDb[iDb].pSchema structure. The mutexes required for schema +** access are: +** +** (1) The mutex on db +** (2) if iDb!=1, then the mutex on db->aDb[iDb].pBt. +** +** If pSchema is not NULL, then iDb is computed from pSchema and +** db using sqlite3SchemaToIndex(). +*/ +int sqlite3SchemaMutexHeld(sqlite3 *db, int iDb, Schema *pSchema){ + Btree *p; + assert( db!=0 ); + if( pSchema ) iDb = sqlite3SchemaToIndex(db, pSchema); + assert( iDb>=0 && iDb<db->nDb ); + if( !sqlite3_mutex_held(db->mutex) ) return 0; + if( iDb==1 ) return 1; + p = db->aDb[iDb].pBt; + assert( p!=0 ); + return p->sharable==0 || p->locked==1; +} +#endif /* NDEBUG */ + #else /* SQLITE_THREADSAFE>0 above. SQLITE_THREADSAFE==0 below */ /* ** The following are special cases for mutex enter routines for use diff --git a/src/btree.h b/src/btree.h index 4fd4f6768..3eebfe20b 100644 --- a/src/btree.h +++ b/src/btree.h @@ -220,6 +220,7 @@ void sqlite3BtreeCursorList(Btree*); /* These routines are used inside assert() statements only. */ int sqlite3BtreeHoldsMutex(Btree*); int sqlite3BtreeHoldsAllMutexes(sqlite3*); + int sqlite3SchemaMutexHeld(sqlite3*,int,Schema*); u32 sqlite3BtreeMutexCounter(Btree*); #endif #else @@ -232,6 +233,7 @@ void sqlite3BtreeCursorList(Btree*); # define sqlite3BtreeHoldsMutex(X) 1 # define sqlite3BtreeHoldsAllMutexes(X) 1 +# define sqlite3BtreeSchemaMutexHeld(X,Y) 1 #endif diff --git a/src/build.c b/src/build.c index b4528347d..7e73b6a8e 100644 --- a/src/build.c +++ b/src/build.c @@ -156,6 +156,7 @@ void sqlite3FinishCoding(Parse *pParse){ sqlite3VdbeUsesBtree(v, iDb); sqlite3VdbeAddOp2(v,OP_Transaction, iDb, (mask & pParse->writeMask)!=0); if( db->init.busy==0 ){ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp3(v, OP_VerifyCookie, iDb, pParse->cookieValue[iDb], db->aDb[iDb].pSchema->iGeneration); @@ -271,9 +272,12 @@ Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){ int nName; assert( zName!=0 ); nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDatabase!=0 && sqlite3StrICmp(zDatabase, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&db->aDb[j].pSchema->tblHash, zName, nName); if( p ) break; } @@ -333,11 +337,14 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ Index *p = 0; int i; int nName = sqlite3Strlen30(zName); + /* All mutexes are required for schema access. Make sure we hold them. */ + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ Schema *pSchema = db->aDb[j].pSchema; assert( pSchema ); if( zDb && sqlite3StrICmp(zDb, db->aDb[j].zName) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); p = sqlite3HashFind(&pSchema->idxHash, zName, nName); if( p ) break; } @@ -364,8 +371,10 @@ static void freeIndex(sqlite3 *db, Index *p){ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ Index *pIndex; int len; - Hash *pHash = &db->aDb[iDb].pSchema->idxHash; + Hash *pHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &db->aDb[iDb].pSchema->idxHash; len = sqlite3Strlen30(zIdxName); pIndex = sqlite3HashInsert(pHash, zIdxName, len, 0); if( ALWAYS(pIndex) ){ @@ -393,26 +402,40 @@ void sqlite3UnlinkAndDeleteIndex(sqlite3 *db, int iDb, const char *zIdxName){ ** if there were schema changes during the transaction or if a ** schema-cookie mismatch occurs. ** -** If iDb==0 then reset the internal schema tables for all database -** files. If iDb>=1 then reset the internal schema for only the +** If iDb<0 then reset the internal schema tables for all database +** files. If iDb>=0 then reset the internal schema for only the ** single file indicated. */ void sqlite3ResetInternalSchema(sqlite3 *db, int iDb){ int i, j; - assert( iDb>=0 && iDb<db->nDb ); + assert( iDb<db->nDb ); - if( iDb==0 ){ - sqlite3BtreeEnterAll(db); + if( iDb>=0 ){ + /* Case 1: Reset the single schema identified by iDb */ + Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + if( ALWAYS(pDb->pSchema) ){ + sqlite3SchemaClear(pDb->pSchema); + } + /* If any database other than TEMP is reset, then also reset TEMP + ** since TEMP might be holding triggers that reference tables in the + ** other database. + */ + if( iDb!=1 && (pDb = &db->aDb[1])!=0 && ALWAYS(pDb->pSchema) ){ + sqlite3SchemaClear(pDb->pSchema); + } + return; } - for(i=iDb; i<db->nDb; i++){ + /* Case 2 (from here to the end): Reset all schemas for all attached + ** databases. */ + assert( iDb<0 ); + sqlite3BtreeEnterAll(db); + for(i=0; i<db->nDb; i++){ Db *pDb = &db->aDb[i]; if( pDb->pSchema ){ - assert(i==1 || (pDb->pBt && sqlite3BtreeHoldsMutex(pDb->pBt))); - sqlite3SchemaFree(pDb->pSchema); + sqlite3SchemaClear(pDb->pSchema); } - if( iDb>0 ) return; } - assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); @@ -498,6 +521,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ TESTONLY ( Index *pOld = ) sqlite3HashInsert( &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0 ); + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); assert( pOld==pIndex || pOld==0 ); } freeIndex(db, pIndex); @@ -532,6 +556,7 @@ void sqlite3UnlinkAndDeleteTable(sqlite3 *db, int iDb, const char *zTabName){ assert( db!=0 ); assert( iDb>=0 && iDb<db->nDb ); assert( zTabName ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, @@ -816,6 +841,7 @@ void sqlite3StartTable( */ #ifndef SQLITE_OMIT_AUTOINCREMENT if( !pParse->nested && strcmp(zName, "sqlite_sequence")==0 ){ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTable->pSchema->pSeqTab = pTable; } #endif @@ -1276,6 +1302,7 @@ void sqlite3ChangeCookie(Parse *pParse, int iDb){ int r1 = sqlite3GetTempReg(pParse); sqlite3 *db = pParse->db; Vdbe *v = pParse->pVdbe; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); sqlite3VdbeAddOp2(v, OP_Integer, db->aDb[iDb].pSchema->schema_cookie+1, r1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_SCHEMA_VERSION, r1); sqlite3ReleaseTempReg(pParse, r1); @@ -1578,6 +1605,7 @@ void sqlite3EndTable( */ if( p->tabFlags & TF_Autoincrement ){ Db *pDb = &db->aDb[iDb]; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->pSeqTab==0 ){ sqlite3NestedParse(pParse, "CREATE TABLE %Q.sqlite_sequence(name,seq)", @@ -1598,6 +1626,7 @@ void sqlite3EndTable( if( db->init.busy ){ Table *pOld; Schema *pSchema = p->pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pOld = sqlite3HashInsert(&pSchema->tblHash, p->zName, sqlite3Strlen30(p->zName),p); if( pOld ){ @@ -1782,6 +1811,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ pSelTab->nCol = 0; pSelTab->aCol = 0; sqlite3DeleteTable(db, pSelTab); + assert( sqlite3SchemaMutexHeld(db, 0, pTable->pSchema) ); pTable->pSchema->flags |= DB_UnresetViews; }else{ pTable->nCol = 0; @@ -1802,6 +1832,7 @@ int sqlite3ViewGetColumnNames(Parse *pParse, Table *pTable){ */ static void sqliteViewResetAll(sqlite3 *db, int idx){ HashElem *i; + assert( sqlite3SchemaMutexHeld(db, idx, 0) ); if( !DbHasProperty(db, idx, DB_UnresetViews) ) return; for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); @@ -1835,10 +1866,13 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ ** in order to be certain that we got the right one. */ #ifndef SQLITE_OMIT_AUTOVACUUM -void sqlite3RootPageMoved(Db *pDb, int iFrom, int iTo){ +void sqlite3RootPageMoved(sqlite3 *db, int iDb, int iFrom, int iTo){ HashElem *pElem; Hash *pHash; + Db *pDb; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pDb = &db->aDb[iDb]; pHash = &pDb->pSchema->tblHash; for(pElem=sqliteHashFirst(pHash); pElem; pElem=sqliteHashNext(pElem)){ Table *pTab = sqliteHashData(pElem); @@ -2212,6 +2246,7 @@ void sqlite3CreateForeignKey( pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ + assert( sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey ); @@ -2567,6 +2602,7 @@ Index *sqlite3CreateIndex( pIndex->onError = (u8)onError; pIndex->autoIndex = (u8)(pName==0); pIndex->pSchema = db->aDb[iDb].pSchema; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); /* Check to see if we should honor DESC requests on index columns */ @@ -2696,6 +2732,7 @@ Index *sqlite3CreateIndex( */ if( db->init.busy ){ Index *p; + assert( sqlite3SchemaMutexHeld(db, 0, pIndex->pSchema) ); p = sqlite3HashInsert(&pIndex->pSchema->idxHash, pIndex->zName, sqlite3Strlen30(pIndex->zName), pIndex); @@ -3449,6 +3486,7 @@ void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ assert( iDb<db->nDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDb<SQLITE_MAX_ATTACHED+2 ); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); mask = ((yDbMask)1)<<iDb; if( (pToplevel->cookieMask & mask)==0 ){ pToplevel->cookieMask |= mask; @@ -3576,6 +3614,7 @@ static void reindexDatabases(Parse *pParse, char const *zColl){ HashElem *k; /* For looping over tables in pDb */ Table *pTab; /* A table in the database */ + assert( sqlite3BtreeHoldsAllMutexes(db) ); /* Needed for schema access */ for(iDb=0, pDb=db->aDb; iDb<db->nDb; iDb++, pDb++){ assert( pDb!=0 ); for(k=sqliteHashFirst(&pDb->pSchema->tblHash); k; k=sqliteHashNext(k)){ diff --git a/src/callback.c b/src/callback.c index fdee9bc09..ce849085c 100644 --- a/src/callback.c +++ b/src/callback.c @@ -400,12 +400,12 @@ FuncDef *sqlite3FindFunction( /* ** Free all resources held by the schema structure. The void* argument points ** at a Schema struct. This function does not call sqlite3DbFree(db, ) on the -** pointer itself, it just cleans up subsiduary resources (i.e. the contents +** pointer itself, it just cleans up subsidiary resources (i.e. the contents ** of the schema hash tables). ** ** The Schema.cache_size variable is not cleared. */ -void sqlite3SchemaFree(void *p){ +void sqlite3SchemaClear(void *p){ Hash temp1; Hash temp2; HashElem *pElem; @@ -440,7 +440,7 @@ void sqlite3SchemaFree(void *p){ Schema *sqlite3SchemaGet(sqlite3 *db, Btree *pBt){ Schema * p; if( pBt ){ - p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaFree); + p = (Schema *)sqlite3BtreeSchema(pBt, sizeof(Schema), sqlite3SchemaClear); }else{ p = (Schema *)sqlite3DbMallocZero(0, sizeof(Schema)); } diff --git a/src/fkey.c b/src/fkey.c index ddd7b8557..34fdfda5d 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -1154,6 +1154,7 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){ FKey *pFKey; /* Iterator variable */ FKey *pNext; /* Copy of pFKey->pNextFrom */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, pTab->pSchema) ); for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ diff --git a/src/insert.c b/src/insert.c index 0b7744651..f8112f010 100644 --- a/src/insert.c +++ b/src/insert.c @@ -237,6 +237,7 @@ void sqlite3AutoincrementBegin(Parse *pParse){ for(p = pParse->pAinc; p; p = p->pNext){ pDb = &db->aDb[p->iDb]; memId = p->regCtr; + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); addr = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); @@ -287,6 +288,7 @@ void sqlite3AutoincrementEnd(Parse *pParse){ int memId = p->regCtr; iRec = sqlite3GetTempReg(pParse); + assert( sqlite3SchemaMutexHeld(db, 0, pDb->pSchema) ); sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenWrite); j1 = sqlite3VdbeAddOp1(v, OP_NotNull, memId+1); j2 = sqlite3VdbeAddOp0(v, OP_Rewind); diff --git a/src/main.c b/src/main.c index 03097498c..e05c3885b 100644 --- a/src/main.c +++ b/src/main.c @@ -687,7 +687,8 @@ int sqlite3_close(sqlite3 *db){ } sqlite3_mutex_enter(db->mutex); - sqlite3ResetInternalSchema(db, 0); + /* Force xDestroy calls on all virtual tables */ + sqlite3ResetInternalSchema(db, -1); /* If a transaction is open, the ResetInternalSchema() call above ** will not have called the xDisconnect() method on any virtual @@ -730,7 +731,7 @@ int sqlite3_close(sqlite3 *db){ } } } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); /* Tell the code in notify.c that the connection no longer holds any ** locks and does not require any further unlock-notify callbacks. @@ -821,7 +822,7 @@ void sqlite3RollbackAll(sqlite3 *db){ if( db->flags&SQLITE_InternChanges ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } /* Any deferred constraint violations have now been resolved. */ diff --git a/src/pragma.c b/src/pragma.c index 9d3ec65f9..75ab26d44 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -115,7 +115,7 @@ static int invalidateTempStorage(Parse *pParse){ } sqlite3BtreeClose(db->aDb[1].pBt); db->aDb[1].pBt = 0; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } return SQLITE_OK; } @@ -388,6 +388,7 @@ void sqlite3Pragma( sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3VdbeAddOp2(v, OP_Integer, size, 1); sqlite3VdbeAddOp3(v, OP_SetCookie, iDb, BTREE_DEFAULT_CACHE_SIZE, 1); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pDb->pSchema->cache_size = size; sqlite3BtreeSetCacheSize(pDb->pBt, pDb->pSchema->cache_size); } @@ -690,6 +691,7 @@ void sqlite3Pragma( */ if( sqlite3StrICmp(zLeft,"cache_size")==0 ){ if( sqlite3ReadSchema(pParse) ) goto pragma_out; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( !zRight ){ returnSingleInt(pParse, "cache_size", pDb->pSchema->cache_size); }else{ @@ -1110,6 +1112,7 @@ void sqlite3Pragma( ** Begin by filling registers 2, 3, ... with the root pages numbers ** for all tables and indices in the database. */ + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTbls = &db->aDb[i].pSchema->tblHash; for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){ Table *pTab = sqliteHashData(x); diff --git a/src/prepare.c b/src/prepare.c index 45654ecfc..b5edaf503 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -338,7 +338,7 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ } if( db->mallocFailed ){ rc = SQLITE_NOMEM; - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); } if( rc==SQLITE_OK || (db->flags&SQLITE_RecoveryMode)){ /* Black magic: If the SQLITE_RecoveryMode flag is set, then consider @@ -470,7 +470,9 @@ static void schemaIsValid(Parse *pParse){ ** value stored as part of the in-memory schema representation, ** set Parse.rc to SQLITE_SCHEMA. */ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + sqlite3ResetInternalSchema(db, iDb); pParse->rc = SQLITE_SCHEMA; } @@ -612,9 +614,6 @@ static int sqlite3Prepare( if( pParse->checkSchema ){ schemaIsValid(pParse); } - if( pParse->rc==SQLITE_SCHEMA ){ - sqlite3ResetInternalSchema(db, 0); - } if( db->mallocFailed ){ pParse->rc = SQLITE_NOMEM; } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ba5552dba..b874c5e03 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -668,6 +668,20 @@ struct Db { /* ** An instance of the following structure stores a database schema. +** +** Most Schema objects are associated with a Btree. The exception is +** the Schema for the TEMP databaes (sqlite3.aDb[1]) which is free-standing. +** In shared cache mode, a single Schema object can be shared by multiple +** Btrees that refer to the same underlying BtShared object. +** +** Schema objects are automatically deallocated when the last Btree that +** references them is destroyed. The TEMP Schema is manually freed by +** sqlite3_close(). +* +** A thread must be holding a mutex on the corresponding Btree in order +** to access Schema content. This implies that the thread must also be +** holding a mutex on the sqlite3 connection pointer that owns the Btree. +** For a TEMP Schema, on the connection mutex is required. */ struct Schema { int schema_cookie; /* Database schema version number for this file */ @@ -1184,7 +1198,7 @@ struct CollSeq { ** schema is shared, as the implementation often stores the database ** connection handle passed to it via the xConnect() or xCreate() method ** during initialization internally. This database connection handle may -** then used by the virtual table implementation to access real tables +** then be used by the virtual table implementation to access real tables ** within the database. So that they appear as part of the callers ** transaction, these accesses need to be made via the same database ** connection as that used to execute SQL operations on the virtual table. @@ -2942,7 +2956,7 @@ extern SQLITE_WSD FuncDefHash sqlite3GlobalFunctions; extern int sqlite3PendingByte; #endif #endif -void sqlite3RootPageMoved(Db*, int, int); +void sqlite3RootPageMoved(sqlite3*, int, int, int); void sqlite3Reindex(Parse*, Token*, Token*); void sqlite3AlterFunctions(void); void sqlite3AlterRenameTable(Parse*, SrcList*, Token*); @@ -2969,7 +2983,7 @@ void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); void sqlite3MinimumFileFormat(Parse*, int, int); -void sqlite3SchemaFree(void *); +void sqlite3SchemaClear(void *); Schema *sqlite3SchemaGet(sqlite3 *, Btree *); int sqlite3SchemaToIndex(sqlite3 *db, Schema *); KeyInfo *sqlite3IndexKeyinfo(Parse *, Index *); diff --git a/src/status.c b/src/status.c index 96759fda7..b8c1d58df 100644 --- a/src/status.c +++ b/src/status.c @@ -163,6 +163,7 @@ int sqlite3_db_status( int i; /* Used to iterate through schemas */ int nByte = 0; /* Used to accumulate return value */ + sqlite3BtreeEnterAll(db); db->pnBytesFreed = &nByte; for(i=0; i<db->nDb; i++){ Schema *pSchema = db->aDb[i].pSchema; @@ -189,6 +190,7 @@ int sqlite3_db_status( } } db->pnBytesFreed = 0; + sqlite3BtreeLeaveAll(db); *pHighwater = 0; *pCurrent = nByte; diff --git a/src/trigger.c b/src/trigger.c index d6b61c77b..8a050eac5 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -54,6 +54,7 @@ Trigger *sqlite3TriggerList(Parse *pParse, Table *pTab){ if( pTmpSchema!=pTab->pSchema ){ HashElem *p; + assert( sqlite3SchemaMutexHeld(pParse->db, 0, pTmpSchema) ); for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ Trigger *pTrig = (Trigger *)sqliteHashData(p); if( pTrig->pTabSchema==pTab->pSchema @@ -165,6 +166,7 @@ void sqlite3BeginTrigger( if( !zName || SQLITE_OK!=sqlite3CheckObjectName(pParse, zName) ){ goto trigger_cleanup; } + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( sqlite3HashFind(&(db->aDb[iDb].pSchema->trigHash), zName, sqlite3Strlen30(zName)) ){ if( !noErr ){ @@ -304,6 +306,7 @@ void sqlite3FinishTrigger( if( db->init.busy ){ Trigger *pLink = pTrig; Hash *pHash = &db->aDb[iDb].pSchema->trigHash; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); pTrig = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), pTrig); if( pTrig ){ db->mallocFailed = 1; @@ -485,9 +488,11 @@ void sqlite3DropTrigger(Parse *pParse, SrcList *pName, int noErr){ zDb = pName->a[0].zDatabase; zName = pName->a[0].zName; nName = sqlite3Strlen30(zName); + assert( zDb!=0 || sqlite3BtreeHoldsAllMutexes(db) ); for(i=OMIT_TEMPDB; i<db->nDb; i++){ int j = (i<2) ? i^1 : i; /* Search TEMP before MAIN */ if( zDb && sqlite3StrICmp(db->aDb[j].zName, zDb) ) continue; + assert( sqlite3SchemaMutexHeld(db, j, 0) ); pTrigger = sqlite3HashFind(&(db->aDb[j].pSchema->trigHash), zName, nName); if( pTrigger ) break; } @@ -576,8 +581,11 @@ void sqlite3DropTriggerPtr(Parse *pParse, Trigger *pTrigger){ ** Remove a trigger from the hash tables of the sqlite* pointer. */ void sqlite3UnlinkAndDeleteTrigger(sqlite3 *db, int iDb, const char *zName){ - Hash *pHash = &(db->aDb[iDb].pSchema->trigHash); Trigger *pTrigger; + Hash *pHash; + + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); + pHash = &(db->aDb[iDb].pSchema->trigHash); pTrigger = sqlite3HashInsert(pHash, zName, sqlite3Strlen30(zName), 0); if( ALWAYS(pTrigger) ){ if( pTrigger->pSchema==pTrigger->pTabSchema ){ diff --git a/src/vacuum.c b/src/vacuum.c index 4babf5b40..5a4ed3205 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -335,8 +335,11 @@ end_of_vacuum: pDb->pSchema = 0; } - sqlite3ResetInternalSchema(db, 0); + /* This both clears the schemas and reduces the size of the db->aDb[] + ** array. */ + sqlite3ResetInternalSchema(db, -1); return rc; } + #endif /* SQLITE_OMIT_VACUUM && SQLITE_OMIT_ATTACH */ diff --git a/src/vdbe.c b/src/vdbe.c index 4bfe518a9..94fd0ff62 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -551,7 +551,7 @@ int sqlite3VdbeExec( Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ - u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */ + u8 resetSchemaOnFault = 0; /* Reset schema after an error if positive */ u8 encoding = ENC(db); /* The database encoding */ #ifndef SQLITE_OMIT_PROGRESS_CALLBACK int checkProgress; /* True if progress callbacks are enabled */ @@ -2659,7 +2659,7 @@ case OP_Savepoint: { } if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); sqlite3VdbeMutexResync(p); db->flags = (db->flags | SQLITE_InternChanges); } @@ -2880,6 +2880,7 @@ case OP_SetCookie: { /* in3 */ assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); pDb = &db->aDb[pOp->p1]; assert( pDb->pBt!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ @@ -2926,6 +2927,7 @@ case OP_VerifyCookie: { assert( pOp->p1>=0 && pOp->p1<db->nDb ); assert( (p->btreeMask & (((yDbMask)1)<<pOp->p1))!=0 ); + assert( sqlite3SchemaMutexHeld(db, pOp->p1, 0) ); pBt = db->aDb[pOp->p1].pBt; if( pBt ){ sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&iMeta); @@ -2951,7 +2953,6 @@ case OP_VerifyCookie: { */ if( db->aDb[pOp->p1].pSchema->schema_cookie!=iMeta ){ sqlite3ResetInternalSchema(db, pOp->p1); - sqlite3VdbeMutexResync(p); } p->expired = 1; @@ -3036,6 +3037,7 @@ case OP_OpenWrite: { assert( pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ wrFlag = 1; + assert( sqlite3SchemaMutexHeld(db, iDb, 0) ); if( pDb->pSchema->file_format < p->minWriteFileFormat ){ p->minWriteFileFormat = pDb->pSchema->file_format; } @@ -4531,8 +4533,10 @@ case OP_Destroy: { /* out2-prerelease */ pOut->u.i = iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM if( rc==SQLITE_OK && iMoved!=0 ){ - sqlite3RootPageMoved(&db->aDb[iDb], iMoved, pOp->p1); - resetSchemaOnFault = 1; + sqlite3RootPageMoved(db, iDb, iMoved, pOp->p1); + /* All OP_Destroy operations occur on the same btree */ + assert( resetSchemaOnFault==0 || resetSchemaOnFault==iDb+1 ); + resetSchemaOnFault = iDb+1; } #endif } @@ -5966,9 +5970,8 @@ vdbe_error_halt: sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; - if( resetSchemaOnFault ){ - sqlite3ResetInternalSchema(db, 0); - sqlite3VdbeMutexResync(p); + if( resetSchemaOnFault>0 ){ + sqlite3ResetInternalSchema(db, resetSchemaOnFault-1); } /* This is the only way out of this procedure. We have to diff --git a/src/vdbeaux.c b/src/vdbeaux.c index b28bac4f3..7313ad5c3 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -2278,7 +2278,7 @@ int sqlite3VdbeHalt(Vdbe *p){ /* Rollback or commit any schema changes that occurred. */ if( p->rc!=SQLITE_OK && db->flags&SQLITE_InternChanges ){ - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); db->flags = (db->flags | SQLITE_InternChanges); } diff --git a/src/vtab.c b/src/vtab.c index b9f1e6f34..b052de23a 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -48,7 +48,7 @@ static int createModule( if( pDel==pMod ){ db->mallocFailed = 1; } - sqlite3ResetInternalSchema(db, 0); + sqlite3ResetInternalSchema(db, -1); }else if( xDestroy ){ xDestroy(pAux); } @@ -145,10 +145,9 @@ static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ ** that contains table p is held by the caller. See header comments ** above function sqlite3VtabUnlockList() for an explanation of why ** this makes it safe to access the sqlite3.pDisconnect list of any - ** database connection that may have an entry in the p->pVTable list. */ - assert( db==0 || - sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) - ); + ** database connection that may have an entry in the p->pVTable list. + */ + assert( db==0 || sqlite3SchemaMutexHeld(db, 0, p->pSchema) ); while( pVTable ){ sqlite3 *db2 = pVTable->db; @@ -387,6 +386,7 @@ void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ Schema *pSchema = pTab->pSchema; const char *zName = pTab->zName; int nName = sqlite3Strlen30(zName); + assert( sqlite3SchemaMutexHeld(db, 0, pSchema) ); pOld = sqlite3HashInsert(&pSchema->tblHash, zName, nName, pTab); if( pOld ){ db->mallocFailed = 1; |