aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/analyze.c16
-rw-r--r--src/build.c54
-rw-r--r--src/fkey.c30
-rw-r--r--src/malloc.c60
-rw-r--r--src/mem2.c23
-rw-r--r--src/sqlite.h.in4
-rw-r--r--src/sqliteInt.h33
-rw-r--r--src/status.c61
-rw-r--r--src/test_malloc.c2
-rw-r--r--src/vdbe.h2
-rw-r--r--src/vdbeaux.c55
-rw-r--r--src/vtab.c2
12 files changed, 230 insertions, 112 deletions
diff --git a/src/analyze.c b/src/analyze.c
index 88a1820ed..42705cafd 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 */
@@ -619,10 +619,8 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
if( n < 1){
pSample->u.z = 0;
}else{
- pSample->u.z = sqlite3Malloc(n);
- if( pSample->u.z ){
- memcpy(pSample->u.z, z, n);
- }else{
+ pSample->u.z = sqlite3DbStrNDup(0, z, n);
+ if( pSample->u.z==0 ){
db->mallocFailed = 1;
break;
}
diff --git a/src/build.c b/src/build.c
index b227143bd..1281af67e 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 40a9720a6..73508bdef 100644
--- a/src/malloc.c
+++ b/src/malloc.c
@@ -368,7 +368,7 @@ void sqlite3ScratchFree(void *p){
|| p<sqlite3GlobalConfig.pScratch
|| p>=(void*)mem0.aScratchFree ){
assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
- assert( !sqlite3MemdebugHasType(p, ~MEMTYPE_SCRATCH) );
+ assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
if( sqlite3GlobalConfig.bMemstat ){
int iSize = sqlite3MallocSize(p);
@@ -409,7 +409,7 @@ void sqlite3ScratchFree(void *p){
*/
#ifndef SQLITE_OMIT_LOOKASIDE
static int isLookaside(sqlite3 *db, void *p){
- return db && p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
+ return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
}
#else
#define isLookaside(A,B) 0
@@ -421,17 +421,17 @@ static int isLookaside(sqlite3 *db, void *p){
*/
int sqlite3MallocSize(void *p){
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
- assert( !sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
+ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
return sqlite3GlobalConfig.m.xSize(p);
}
int sqlite3DbMallocSize(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
- if( isLookaside(db, p) ){
+ if( db && isLookaside(db, p) ){
return db->lookaside.sz;
}else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
- assert( sqlite3MemdebugHasType(p,
- db ? (MEMTYPE_DB|MEMTYPE_HEAP) : MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
return sqlite3GlobalConfig.m.xSize(p);
}
}
@@ -441,7 +441,7 @@ int sqlite3DbMallocSize(sqlite3 *db, void *p){
*/
void sqlite3_free(void *p){
if( p==0 ) return;
- assert( !sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
+ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
if( sqlite3GlobalConfig.bMemstat ){
sqlite3_mutex_enter(mem0.mutex);
@@ -459,18 +459,24 @@ void sqlite3_free(void *p){
*/
void sqlite3DbFree(sqlite3 *db, void *p){
assert( db==0 || sqlite3_mutex_held(db->mutex) );
- if( isLookaside(db, p) ){
- LookasideSlot *pBuf = (LookasideSlot*)p;
- pBuf->pNext = db->lookaside.pFree;
- db->lookaside.pFree = pBuf;
- db->lookaside.nOut--;
- }else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
- assert( sqlite3MemdebugHasType(p,
- db ? (MEMTYPE_DB|MEMTYPE_HEAP) : MEMTYPE_HEAP) );
- sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
- sqlite3_free(p);
+ if( db ){
+ if( db->pnBytesFreed ){
+ *db->pnBytesFreed += sqlite3DbMallocSize(db, p);
+ return;
+ }
+ if( isLookaside(db, p) ){
+ LookasideSlot *pBuf = (LookasideSlot*)p;
+ pBuf->pNext = db->lookaside.pFree;
+ db->lookaside.pFree = pBuf;
+ db->lookaside.nOut--;
+ return;
+ }
}
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
+ sqlite3_free(p);
}
/*
@@ -502,7 +508,7 @@ void *sqlite3Realloc(void *pOld, int nBytes){
sqlite3MallocAlarm(nNew-nOld);
}
assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
- assert( !sqlite3MemdebugHasType(pOld, ~MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
if( pNew==0 && mem0.alarmCallback ){
sqlite3MallocAlarm(nBytes);
@@ -600,8 +606,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
if( !p && db ){
db->mallocFailed = 1;
}
- sqlite3MemdebugSetType(p, MEMTYPE_RECURSIVE |
- ((db && db->lookaside.bEnabled) ? MEMTYPE_DB : MEMTYPE_HEAP));
+ sqlite3MemdebugSetType(p, MEMTYPE_DB |
+ ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
return p;
}
@@ -627,16 +633,16 @@ void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
sqlite3DbFree(db, p);
}
}else{
- assert( sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB|MEMTYPE_HEAP) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
pNew = sqlite3_realloc(p, n);
if( !pNew ){
- sqlite3MemdebugSetType(p, MEMTYPE_RECURSIVE|MEMTYPE_HEAP);
+ sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP);
db->mallocFailed = 1;
}
- sqlite3MemdebugSetType(pNew, MEMTYPE_RECURSIVE |
- (db->lookaside.bEnabled ? MEMTYPE_DB : MEMTYPE_HEAP));
+ sqlite3MemdebugSetType(pNew, MEMTYPE_DB |
+ (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
}
}
return pNew;
diff --git a/src/mem2.c b/src/mem2.c
index 587d9274e..528a020f5 100644
--- a/src/mem2.c
+++ b/src/mem2.c
@@ -407,7 +407,28 @@ int sqlite3MemdebugHasType(void *p, u8 eType){
}
return rc;
}
-
+
+/*
+** Return TRUE if the mask of type in eType matches no bits of the type of the
+** allocation p. Also return true if p==NULL.
+**
+** This routine is designed for use within an assert() statement, to
+** verify the type of an allocation. For example:
+**
+** assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
+*/
+int sqlite3MemdebugNoType(void *p, u8 eType){
+ int rc = 1;
+ if( p ){
+ struct MemBlockHdr *pHdr;
+ pHdr = sqlite3MemsysGetHeader(p);
+ assert( pHdr->iForeGuard==FOREGUARD ); /* Allocation is valid */
+ if( (pHdr->eType&eType)!=0 ){
+ rc = 0;
+ }
+ }
+ return rc;
+}
/*
** Set the number of backtrace levels kept for each allocation.
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 8f7801648..02705769c 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*);
@@ -3118,17 +3120,18 @@ SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
** sqlite3MemdebugHasType() returns true if any of the bits in its second
** argument match the type set by the previous sqlite3MemdebugSetType().
** sqlite3MemdebugHasType() is intended for use inside assert() statements.
-** For example:
**
-** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
+** sqlite3MemdebugNoType() returns true if none of the bits in its second
+** argument match the type set by the previous sqlite3MemdebugSetType().
**
** Perhaps the most important point is the difference between MEMTYPE_HEAP
-** and MEMTYPE_DB. If an allocation is MEMTYPE_DB, that means it might have
-** been allocated by lookaside, except the allocation was too large or
-** lookaside was already full. It is important to verify that allocations
-** that might have been satisfied by lookaside are not passed back to
-** non-lookaside free() routines. Asserts such as the example above are
-** placed on the non-lookaside free() routines to verify this constraint.
+** and MEMTYPE_LOOKASIDE. If an allocation is MEMTYPE_LOOKASIDE, that means
+** it might have been allocated by lookaside, except the allocation was
+** too large or lookaside was already full. It is important to verify
+** that allocations that might have been satisfied by lookaside are not
+** passed back to non-lookaside free() routines. Asserts such as the
+** example above are placed on the non-lookaside free() routines to verify
+** this constraint.
**
** All of this is no-op for a production build. It only comes into
** play when the SQLITE_MEMDEBUG compile-time option is used.
@@ -3136,14 +3139,16 @@ SQLITE_EXTERN void (*sqlite3IoTrace)(const char*,...);
#ifdef SQLITE_MEMDEBUG
void sqlite3MemdebugSetType(void*,u8);
int sqlite3MemdebugHasType(void*,u8);
+ int sqlite3MemdebugNoType(void*,u8);
#else
# define sqlite3MemdebugSetType(X,Y) /* no-op */
# define sqlite3MemdebugHasType(X,Y) 1
+# define sqlite3MemdebugNoType(X,Y) 1
#endif
-#define MEMTYPE_HEAP 0x01 /* General heap allocations */
-#define MEMTYPE_DB 0x02 /* Associated with a database connection */
-#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
-#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
-#define MEMTYPE_RECURSIVE 0x10 /* Experimental */
+#define MEMTYPE_HEAP 0x01 /* General heap allocations */
+#define MEMTYPE_LOOKASIDE 0x02 /* Might have been lookaside memory */
+#define MEMTYPE_SCRATCH 0x04 /* Scratch allocations */
+#define MEMTYPE_PCACHE 0x08 /* Page cache allocations */
+#define MEMTYPE_DB 0x10 /* Uses sqlite3DbMalloc, not sqlite_malloc */
#endif /* _SQLITEINT_H_ */
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 5e233cb0a..7359d18ab 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:
@@ -595,7 +598,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;
}
@@ -608,11 +611,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;
}
}
@@ -1006,6 +1023,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 );
@@ -2339,6 +2361,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){
@@ -2355,16 +2395,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 43081416b..bd4cf441f 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++){