aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordrh <drh@noemail.net>2011-04-05 13:12:28 +0000
committerdrh <drh@noemail.net>2011-04-05 13:12:28 +0000
commit959b6a95ee5f398ce3db3b3199f4e579e621e43c (patch)
tree732ad37cd655bc396a327918c6b60639937e6708 /src
parent8d129422ed9a04bac684864ba127fa6278cc7513 (diff)
parent03faf63b57a76c925c0e46e8bcb3b86cfe098590 (diff)
downloadsqlite-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.c4
-rw-r--r--src/attach.c4
-rw-r--r--src/backup.c2
-rw-r--r--src/btmutex.c25
-rw-r--r--src/btree.h2
-rw-r--r--src/build.c63
-rw-r--r--src/callback.c6
-rw-r--r--src/fkey.c1
-rw-r--r--src/insert.c2
-rw-r--r--src/main.c7
-rw-r--r--src/pragma.c5
-rw-r--r--src/prepare.c7
-rw-r--r--src/sqliteInt.h20
-rw-r--r--src/status.c2
-rw-r--r--src/trigger.c10
-rw-r--r--src/vacuum.c5
-rw-r--r--src/vdbe.c19
-rw-r--r--src/vdbeaux.c2
-rw-r--r--src/vtab.c10
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;