Index: src/status.c ================================================================== --- src/status.c +++ src/status.c @@ -280,11 +280,10 @@ db->pnBytesFreed = &nByte; for(i=0; inDb; i++){ Schema *pSchema = db->aDb[i].pSchema; if( ALWAYS(pSchema!=0) ){ HashElem *p; - int nStart = nByte; nByte += sqlite3GlobalConfig.m.xRoundup(sizeof(HashElem)) * ( pSchema->tblHash.count + pSchema->trigHash.count + pSchema->idxHash.count Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -409,10 +409,90 @@ if( pMem->flags & (MEM_Str|MEM_Blob) ){ return computeNumericType(pMem); } return 0; } + +/* +** This is the implementation of the OP_ParseSchema opcode. It is factored +** out of the main sqlite3VdbeExec() routine because it is not a performance- +** critical opcode and by factoring it out, it frees up registers in order +** to help the compiler optimizer do a better job with the other opcodes +** that are performance critical. +*/ +static SQLITE_NOINLINE int parseSchemaOp(Vdbe *p, VdbeOp *pOp, sqlite3 *db){ + int iDb; + const char *zMaster; + char *zSql; + InitData initData; + int bRelease; + int rc = SQLITE_OK; + + /* 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; iDbnDb; iDb++){ + assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); + } +#endif + + iDb = pOp->p1; + assert( iDb>=0 && iDbnDb ); + assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); + +#ifndef SQLITE_OMIT_ALTERTABLE + if( pOp->p4.z==0 ){ + assert( !IsReuseSchema(db) || iDb==1 ); + sqlite3SchemaClear(db->aDb[iDb].pSchema); + db->mDbFlags &= ~DBFLAG_SchemaKnownOk; + rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); + db->mDbFlags |= DBFLAG_SchemaChange; + p->expired = 0; + }else +#endif + { + zMaster = MASTER_NAME; + initData.db = db; + initData.iDb = iDb; + initData.pzErrMsg = &p->zErrMsg; + initData.mInitFlags = 0; + zSql = sqlite3MPrintf(db, + "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", + db->aDb[iDb].zDbSName, zMaster, pOp->p4.z); + if( zSql==0 ){ + rc = SQLITE_NOMEM_BKPT; + }else{ + bRelease = sqlite3LockReusableSchema(db); + if( IsReuseSchema(db) ){ + rc = sqlite3Init(db, &p->zErrMsg); + if( rc ){ + sqlite3UnlockReusableSchema(db, bRelease); + return rc; + } + } + assert( db->init.busy==0 ); + db->init.busy = 1; + initData.rc = SQLITE_OK; + initData.nInitRow = 0; + assert( !db->mallocFailed ); + rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); + sqlite3UnlockReusableSchema(db, bRelease); + if( rc==SQLITE_OK ) rc = initData.rc; + if( rc==SQLITE_OK && initData.nInitRow==0 ){ + /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse + ** at least one SQL statement. Any less than that indicates that + ** the sqlite_master table is corrupt. */ + rc = SQLITE_CORRUPT_BKPT; + } + sqlite3DbFreeNN(db, zSql); + db->init.busy = 0; + } + } + return rc; +} #ifdef SQLITE_DEBUG /* ** Write a nice string representation of the contents of cell pMem ** into buffer zBuf, length nBuf. @@ -5755,78 +5835,11 @@ ** ** This opcode invokes the parser to create a new virtual machine, ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { - int iDb; - const char *zMaster; - char *zSql; - InitData initData; - int bRelease; - - /* 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; iDbnDb; iDb++){ - assert( iDb==1 || sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); - } -#endif - - iDb = pOp->p1; - assert( iDb>=0 && iDbnDb ); - assert( DbHasProperty(db, iDb, DB_SchemaLoaded) ); - -#ifndef SQLITE_OMIT_ALTERTABLE - if( pOp->p4.z==0 ){ - assert( !IsReuseSchema(db) || iDb==1 ); - sqlite3SchemaClear(db->aDb[iDb].pSchema); - db->mDbFlags &= ~DBFLAG_SchemaKnownOk; - rc = sqlite3InitOne(db, iDb, &p->zErrMsg, INITFLAG_AlterTable); - db->mDbFlags |= DBFLAG_SchemaChange; - p->expired = 0; - }else -#endif - { - zMaster = MASTER_NAME; - initData.db = db; - initData.iDb = iDb; - initData.pzErrMsg = &p->zErrMsg; - initData.mInitFlags = 0; - zSql = sqlite3MPrintf(db, - "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s ORDER BY rowid", - db->aDb[iDb].zDbSName, zMaster, pOp->p4.z); - if( zSql==0 ){ - rc = SQLITE_NOMEM_BKPT; - }else{ - bRelease = sqlite3LockReusableSchema(db); - if( IsReuseSchema(db) ){ - rc = sqlite3Init(db, &p->zErrMsg); - if( rc ){ - sqlite3UnlockReusableSchema(db, bRelease); - goto abort_due_to_error; - } - } - assert( db->init.busy==0 ); - db->init.busy = 1; - initData.rc = SQLITE_OK; - initData.nInitRow = 0; - assert( !db->mallocFailed ); - rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0); - sqlite3UnlockReusableSchema(db, bRelease); - if( rc==SQLITE_OK ) rc = initData.rc; - if( rc==SQLITE_OK && initData.nInitRow==0 ){ - /* The OP_ParseSchema opcode with a non-NULL P4 argument should parse - ** at least one SQL statement. Any less than that indicates that - ** the sqlite_master table is corrupt. */ - rc = SQLITE_CORRUPT_BKPT; - } - sqlite3DbFreeNN(db, zSql); - db->init.busy = 0; - } - } + rc = parseSchemaOp(p, pOp, db); if( rc ){ sqlite3ResetAllSchemasOfConnection(db); if( rc==SQLITE_NOMEM ){ goto no_mem; }