diff options
author | dan <dan@noemail.net> | 2010-07-24 11:28:28 +0000 |
---|---|---|
committer | dan <dan@noemail.net> | 2010-07-24 11:28:28 +0000 |
commit | d46def77db74a7dfadabb16da57c25d83af0c248 (patch) | |
tree | 8483a41f5c4a98751242c17018d127276a5eae6c /src | |
parent | 5419ee5f2f068601ebc494b104f92cadfc7de5e7 (diff) | |
download | sqlite-d46def77db74a7dfadabb16da57c25d83af0c248.tar.gz sqlite-d46def77db74a7dfadabb16da57c25d83af0c248.zip |
Experimental code to measure memory consumed by database schemas and prepared statements.
FossilOrigin-Name: 9aa30342f4de4eff630520ea8e07ad253d3f0877
Diffstat (limited to 'src')
-rw-r--r-- | src/analyze.c | 10 | ||||
-rw-r--r-- | src/build.c | 54 | ||||
-rw-r--r-- | src/fkey.c | 30 | ||||
-rw-r--r-- | src/malloc.c | 8 | ||||
-rw-r--r-- | src/sqlite.h.in | 4 | ||||
-rw-r--r-- | src/sqliteInt.h | 4 | ||||
-rw-r--r-- | src/status.c | 61 | ||||
-rw-r--r-- | src/test_malloc.c | 2 | ||||
-rw-r--r-- | src/vdbe.h | 2 | ||||
-rw-r--r-- | src/vdbeaux.c | 55 | ||||
-rw-r--r-- | src/vtab.c | 2 |
11 files changed, 164 insertions, 68 deletions
diff --git a/src/analyze.c b/src/analyze.c index c41fba343..d96deb3c2 100644 --- a/src/analyze.c +++ b/src/analyze.c @@ -490,18 +490,17 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){ ** If the Index.aSample variable is not NULL, delete the aSample[] array ** and its contents. */ -void sqlite3DeleteIndexSamples(Index *pIdx){ +void sqlite3DeleteIndexSamples(sqlite3 *db, Index *pIdx){ #ifdef SQLITE_ENABLE_STAT2 if( pIdx->aSample ){ int j; for(j=0; j<SQLITE_INDEX_SAMPLES; j++){ IndexSample *p = &pIdx->aSample[j]; if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ - sqlite3_free(p->u.z); + sqlite3DbFree(db, p->u.z); } } - sqlite3DbFree(0, pIdx->aSample); - pIdx->aSample = 0; + sqlite3DbFree(db, pIdx->aSample); } #else UNUSED_PARAMETER(pIdx); @@ -542,7 +541,8 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); - sqlite3DeleteIndexSamples(pIdx); + sqlite3DeleteIndexSamples(db, pIdx); + pIdx->aSample = 0; } /* Check to make sure the sqlite_stat1 table exists */ diff --git a/src/build.c b/src/build.c index 1ca6e2469..989489f1e 100644 --- a/src/build.c +++ b/src/build.c @@ -347,31 +347,13 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){ */ static void freeIndex(sqlite3 *db, Index *p){ #ifndef SQLITE_OMIT_ANALYZE - sqlite3DeleteIndexSamples(p); + sqlite3DeleteIndexSamples(db, p); #endif sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } /* -** Remove the given index from the index hash table, and free -** its memory structures. -** -** The index is removed from the database hash tables but -** it is not unlinked from the Table that it indexes. -** Unlinking from the Table must be done by the calling function. -*/ -static void sqlite3DeleteIndex(sqlite3 *db, Index *p){ - Index *pOld; - const char *zName = p->zName; - - pOld = sqlite3HashInsert(&p->pSchema->idxHash, zName, - sqlite3Strlen30(zName), 0); - assert( pOld==0 || pOld==p ); - freeIndex(db, p); -} - -/* ** For the index called zIdxName which is found in the database iDb, ** unlike that index from its Table then remove the index from ** the index hash table and free all memory structures associated @@ -468,9 +450,10 @@ void sqlite3CommitInternalChanges(sqlite3 *db){ } /* -** Clear the column names from a table or view. +** Delete memory allocated for the column names of a table or view (the +** Table.aCol[] array). */ -static void sqliteResetColumnNames(sqlite3 *db, Table *pTable){ +static void sqliteDeleteColumnNames(sqlite3 *db, Table *pTable){ int i; Column *pCol; assert( pTable!=0 ); @@ -484,8 +467,6 @@ static void sqliteResetColumnNames(sqlite3 *db, Table *pTable){ } sqlite3DbFree(db, pTable->aCol); } - pTable->aCol = 0; - pTable->nCol = 0; } /* @@ -500,21 +481,24 @@ static void sqliteResetColumnNames(sqlite3 *db, Table *pTable){ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ Index *pIndex, *pNext; - if( pTable==0 ) return; + assert( !pTable || pTable->nRef>0 ); /* Do not delete the table until the reference count reaches zero. */ - pTable->nRef--; - if( pTable->nRef>0 ){ - return; - } - assert( pTable->nRef==0 ); + if( !pTable ) return; + if( ((!db || db->pnBytesFreed==0) && (--pTable->nRef)>0) ) return; - /* Delete all indices associated with this table - */ + /* Delete all indices associated with this table. */ for(pIndex = pTable->pIndex; pIndex; pIndex=pNext){ pNext = pIndex->pNext; assert( pIndex->pSchema==pTable->pSchema ); - sqlite3DeleteIndex(db, pIndex); + if( !db || db->pnBytesFreed==0 ){ + char *zName = pIndex->zName; + TESTONLY ( Index *pOld = ) sqlite3HashInsert( + &pIndex->pSchema->idxHash, zName, sqlite3Strlen30(zName), 0 + ); + assert( pOld==pIndex || pOld==0 ); + } + freeIndex(db, pIndex); } /* Delete any foreign keys attached to this table. */ @@ -522,7 +506,7 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){ /* Delete the Table structure itself. */ - sqliteResetColumnNames(db, pTable); + sqliteDeleteColumnNames(db, pTable); sqlite3DbFree(db, pTable->zName); sqlite3DbFree(db, pTable->zColAff); sqlite3SelectDelete(db, pTable->pSelect); @@ -1817,7 +1801,9 @@ static void sqliteViewResetAll(sqlite3 *db, int idx){ for(i=sqliteHashFirst(&db->aDb[idx].pSchema->tblHash); i;i=sqliteHashNext(i)){ Table *pTab = sqliteHashData(i); if( pTab->pSelect ){ - sqliteResetColumnNames(db, pTab); + sqliteDeleteColumnNames(db, pTab); + pTab->aCol = 0; + pTab->nCol = 0; } } DbClearProperty(db, idx, DB_UnresetViews); diff --git a/src/fkey.c b/src/fkey.c index 217badffa..399e35005 100644 --- a/src/fkey.c +++ b/src/fkey.c @@ -1158,28 +1158,30 @@ void sqlite3FkDelete(sqlite3 *db, Table *pTab){ for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ /* Remove the FK from the fkeyHash hash table. */ - if( pFKey->pPrevTo ){ - pFKey->pPrevTo->pNextTo = pFKey->pNextTo; - }else{ - void *data = (void *)pFKey->pNextTo; - const char *z = (data ? pFKey->pNextTo->zTo : pFKey->zTo); - sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), data); - } - if( pFKey->pNextTo ){ - pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; + if( !db || db->pnBytesFreed==0 ){ + if( pFKey->pPrevTo ){ + pFKey->pPrevTo->pNextTo = pFKey->pNextTo; + }else{ + void *p = (void *)pFKey->pNextTo; + const char *z = (p ? pFKey->pNextTo->zTo : pFKey->zTo); + sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), p); + } + if( pFKey->pNextTo ){ + pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; + } } + /* EV: R-30323-21917 Each foreign key constraint in SQLite is + ** classified as either immediate or deferred. + */ + assert( pFKey->isDeferred==0 || pFKey->isDeferred==1 ); + /* Delete any triggers created to implement actions for this FK. */ #ifndef SQLITE_OMIT_TRIGGER fkTriggerDelete(db, pFKey->apTrigger[0]); fkTriggerDelete(db, pFKey->apTrigger[1]); #endif - /* EV: R-30323-21917 Each foreign key constraint in SQLite is - ** classified as either immediate or deferred. - */ - assert( pFKey->isDeferred==0 || pFKey->isDeferred==1 ); - pNext = pFKey->pNextFrom; sqlite3DbFree(db, pFKey); } diff --git a/src/malloc.c b/src/malloc.c index e34c27975..05ea51006 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -455,7 +455,13 @@ void sqlite3_free(void *p){ */ void sqlite3DbFree(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( isLookaside(db, p) ){ + if( db && db->pnBytesFreed ){ + if( isLookaside(db, p) ){ + *db->pnBytesFreed += db->lookaside.sz; + }else{ + *db->pnBytesFreed += sqlite3MallocSize(p); + } + }else if( isLookaside(db, p) ){ LookasideSlot *pBuf = (LookasideSlot*)p; pBuf->pNext = db->lookaside.pFree; db->lookaside.pFree = pBuf; diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 711e815e0..3e18d22c4 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5252,7 +5252,9 @@ int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 -#define SQLITE_DBSTATUS_MAX 1 /* Largest defined DBSTATUS */ +#define SQLITE_DBSTATUS_SCHEMA_USED 2 +#define SQLITE_DBSTATUS_STMT_USED 3 +#define SQLITE_DBSTATUS_MAX 3 /* Largest defined DBSTATUS */ /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index a9a0885c1..66f1cffb5 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -860,6 +860,8 @@ struct sqlite3 { int nStatement; /* Number of nested statement-transactions */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ i64 nDeferredCons; /* Net deferred constraints this transaction. */ + int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ + SubProgram *pSubProgram; /* List of sub-programs already visited*/ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MASTER @@ -2908,7 +2910,7 @@ int sqlite3InvokeBusyHandler(BusyHandler*); int sqlite3FindDb(sqlite3*, Token*); int sqlite3FindDbName(sqlite3 *, const char *); int sqlite3AnalysisLoad(sqlite3*,int iDB); -void sqlite3DeleteIndexSamples(Index*); +void sqlite3DeleteIndexSamples(sqlite3*,Index*); void sqlite3DefaultRowEst(Index*); void sqlite3RegisterLikeFunctions(sqlite3*, int); int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); diff --git a/src/status.c b/src/status.c index be2e6dc2f..711827b50 100644 --- a/src/status.c +++ b/src/status.c @@ -14,6 +14,7 @@ ** functionality. */ #include "sqliteInt.h" +#include "vdbeInt.h" /* ** Variables in which to record status information. @@ -136,6 +137,66 @@ int sqlite3_db_status( *pHighwater = 0; break; } + + case SQLITE_DBSTATUS_SCHEMA_USED: { + int i; /* Used to iterate through schemas */ + int nByte = 0; /* Used to accumulate return value */ + + assert( db->pSubProgram==0 ); + db->pnBytesFreed = &nByte; + for(i=0; i<db->nDb; i++){ + Schema *pSchema = db->aDb[i].pSchema; + if( pSchema ){ + HashElem *p; + + nByte += sizeof(HashElem) * ( + pSchema->tblHash.count + + pSchema->trigHash.count + + pSchema->idxHash.count + + pSchema->fkeyHash.count + ); + nByte += sqlite3MallocSize(pSchema->tblHash.ht); + nByte += sqlite3MallocSize(pSchema->trigHash.ht); + nByte += sqlite3MallocSize(pSchema->idxHash.ht); + nByte += sqlite3MallocSize(pSchema->fkeyHash.ht); + + for(p=sqliteHashFirst(&pSchema->trigHash); p; p=sqliteHashNext(p)){ + sqlite3DeleteTrigger(db, (Trigger*)sqliteHashData(p)); + } + for(p=sqliteHashFirst(&pSchema->tblHash); p; p=sqliteHashNext(p)){ + sqlite3DeleteTable(db, (Table *)sqliteHashData(p)); + } + } + } + db->pnBytesFreed = 0; + + *pHighwater = 0; + *pCurrent = nByte; + break; + } + + case SQLITE_DBSTATUS_STMT_USED: { + struct Vdbe *pVdbe; /* Used to iterate through VMs */ + int nByte = 0; /* Used to accumulate return value */ + + db->pnBytesFreed = &nByte; + for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pNext){ + SubProgram *pSub, *pNext; + sqlite3VdbeDeleteObject(db, pVdbe); + for(pSub=db->pSubProgram; pSub; pSub=pNext){ + pNext = pSub->pNext; + pSub->pNext = 0; + } + db->pSubProgram = 0; + } + db->pnBytesFreed = 0; + + *pHighwater = 0; + *pCurrent = nByte; + + break; + } + default: { rc = SQLITE_ERROR; } diff --git a/src/test_malloc.c b/src/test_malloc.c index 1267f6e78..e32b78e20 100644 --- a/src/test_malloc.c +++ b/src/test_malloc.c @@ -1288,6 +1288,8 @@ static int test_db_status( } aOp[] = { { "SQLITE_DBSTATUS_LOOKASIDE_USED", SQLITE_DBSTATUS_LOOKASIDE_USED }, { "SQLITE_DBSTATUS_CACHE_USED", SQLITE_DBSTATUS_CACHE_USED }, + { "SQLITE_DBSTATUS_SCHEMA_USED", SQLITE_DBSTATUS_SCHEMA_USED }, + { "SQLITE_DBSTATUS_STMT_USED", SQLITE_DBSTATUS_STMT_USED } }; Tcl_Obj *pResult; if( objc!=4 ){ diff --git a/src/vdbe.h b/src/vdbe.h index c234d51a5..be77e2ff6 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -83,6 +83,7 @@ struct SubProgram { int nCsr; /* Number of cursors required */ int nRef; /* Number of pointers to this structure */ void *token; /* id that may be used to recursive triggers */ + SubProgram *pNext; /* Next sub-program already visited */ }; /* @@ -184,6 +185,7 @@ VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); int sqlite3VdbeMakeLabel(Vdbe*); void sqlite3VdbeRunOnlyOnce(Vdbe*); void sqlite3VdbeDelete(Vdbe*); +void sqlite3VdbeDeleteObject(sqlite3*,Vdbe*); void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); int sqlite3VdbeFinalize(Vdbe*); void sqlite3VdbeResolveLabel(Vdbe*, int); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index cf53f90e3..a297ddd8d 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -573,11 +573,14 @@ static void freeEphemeralFunction(sqlite3 *db, FuncDef *pDef){ } } +static void vdbeFreeOpArray(sqlite3 *, Op *, int); + /* ** Delete a P4 value if necessary. */ static void freeP4(sqlite3 *db, int p4type, void *p4){ if( p4 ){ + assert( db ); switch( p4type ){ case P4_REAL: case P4_INT64: @@ -592,7 +595,7 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ case P4_VDBEFUNC: { VdbeFunc *pVdbeFunc = (VdbeFunc *)p4; freeEphemeralFunction(db, pVdbeFunc->pFunc); - sqlite3VdbeDeleteAuxData(pVdbeFunc, 0); + if( db->pnBytesFreed==0 ) sqlite3VdbeDeleteAuxData(pVdbeFunc, 0); sqlite3DbFree(db, pVdbeFunc); break; } @@ -605,11 +608,25 @@ static void freeP4(sqlite3 *db, int p4type, void *p4){ break; } case P4_VTAB : { - sqlite3VtabUnlock((VTable *)p4); + if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4); break; } case P4_SUBPROGRAM : { - sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1); + if( db->pnBytesFreed ){ + SubProgram *p = (SubProgram *)p4; + SubProgram *pDone; + for(pDone=db->pSubProgram; pDone; pDone=pDone->pNext){ + if( pDone==p ) break; + } + if( !pDone ){ + p->pNext = db->pSubProgram; + db->pSubProgram = p; + vdbeFreeOpArray(db, p->aOp, p->nOp); + sqlite3DbFree(db, p); + } + }else{ + sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1); + } break; } } @@ -1003,6 +1020,11 @@ static void releaseMemArray(Mem *p, int N){ Mem *pEnd; sqlite3 *db = p->db; u8 malloc_failed = db->mallocFailed; + if( db->pnBytesFreed ){ + for(pEnd=&p[N]; p<pEnd; p++){ + sqlite3DbFree(db, p->zMalloc); + } + }else for(pEnd=&p[N]; p<pEnd; p++){ assert( (&p[1])==pEnd || p[0].db==p[1].db ); @@ -2336,6 +2358,24 @@ void sqlite3VdbeDeleteAuxData(VdbeFunc *pVdbeFunc, int mask){ } /* +** Free all memory associated with the Vdbe passed as the second argument. +** The difference between this function and sqlite3VdbeDelete() is that +** VdbeDelete() also unlinks the Vdbe from the list of VMs associated with +** the database connection. +*/ +void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){ + assert( p->db==0 || p->db==db ); + releaseMemArray(p->aVar, p->nVar); + releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + vdbeFreeOpArray(db, p->aOp, p->nOp); + sqlite3DbFree(db, p->aLabel); + sqlite3DbFree(db, p->aColName); + sqlite3DbFree(db, p->zSql); + sqlite3DbFree(db, p->pFree); + sqlite3DbFree(db, p); +} + +/* ** Delete an entire VDBE. */ void sqlite3VdbeDelete(Vdbe *p){ @@ -2352,16 +2392,9 @@ void sqlite3VdbeDelete(Vdbe *p){ if( p->pNext ){ p->pNext->pPrev = p->pPrev; } - releaseMemArray(p->aVar, p->nVar); - releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); - vdbeFreeOpArray(db, p->aOp, p->nOp); - sqlite3DbFree(db, p->aLabel); - sqlite3DbFree(db, p->aColName); - sqlite3DbFree(db, p->zSql); p->magic = VDBE_MAGIC_DEAD; - sqlite3DbFree(db, p->pFree); p->db = 0; - sqlite3DbFree(db, p); + sqlite3VdbeDeleteObject(db, p); } /* diff --git a/src/vtab.c b/src/vtab.c index 5bf867604..e57f4e480 100644 --- a/src/vtab.c +++ b/src/vtab.c @@ -222,7 +222,7 @@ void sqlite3VtabUnlockList(sqlite3 *db){ ** database connection. */ void sqlite3VtabClear(sqlite3 *db, Table *p){ - vtabDisconnectAll(0, p); + if( !db || db->pnBytesFreed==0 ) vtabDisconnectAll(0, p); if( p->azModuleArg ){ int i; for(i=0; i<p->nModuleArg; i++){ |