From: dan Date: Sat, 24 Jul 2010 11:28:28 +0000 (+0000) Subject: Experimental code to measure memory consumed by database schemas and prepared statements. X-Git-Tag: version-3.7.2~93^2~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=d46def77db74a7dfadabb16da57c25d83af0c248;p=thirdparty%2Fsqlite.git Experimental code to measure memory consumed by database schemas and prepared statements. FossilOrigin-Name: 9aa30342f4de4eff630520ea8e07ad253d3f0877 --- diff --git a/manifest b/manifest index b5028c764f..93627ae072 100644 --- a/manifest +++ b/manifest @@ -1,8 +1,5 @@ ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA1 - -C Fix\sa\stypo\sin\sthe\sOS/2\svfs\scode. -D 2010-07-23T22:26:21 +C Experimental\scode\sto\smeasure\smemory\sconsumed\sby\sdatabase\sschemas\sand\sprepared\sstatements. +D 2010-07-24T11:28:29 F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0 F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654 @@ -110,7 +107,7 @@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad F src/alter.c 8dc27638e7e2553e80b2b621f232be5eb1e85ef3 -F src/analyze.c 4ffed15a35cb669920348365fe71ce4d8800dc28 +F src/analyze.c 6c2bdb1d13138d084b14010cd9fef4ff34e0d313 F src/attach.c 17bec1f18254d9341369f20f90ba24ce35d20d10 F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34 F src/backup.c 51d83300fe0baee39405c416ceb19a58ed30a8ed @@ -119,7 +116,7 @@ F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff F src/btree.c 9a214e6141555b183216b73ace058c7a499cdbe2 F src/btree.h dd83041eda10c17daf023257c1fc883b5f71f85a F src/btreeInt.h b0c87f6725b06a0aa194a6d25d54b16ce9d6e291 -F src/build.c 8ccdb8ff8e86cbf5b891d4b236e50a91e69732d3 +F src/build.c fdf428a35c0bd6256373e7779f3744ad4c8ff2eb F src/callback.c 4e565b7f387e3261a19ecaf0cbbdebfb91988ea4 F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 4f3aadad62c6c9f0d4e5a96718516ac4e3c598df @@ -127,7 +124,7 @@ F src/date.c 5dd8448a0bfea8d31fb14cff487d0c06ff8c8b20 F src/delete.c 7ed8a8c8b5f748ece92df173d7e0f7810c899ebd F src/expr.c 7490fc3b16414d1a45d5acadf559317f9244891f F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb -F src/fkey.c 62eed881a26a93e576b8cfeedea91952c90a8666 +F src/fkey.c cacfe3e24b311e4e089a9c470bdb73196af6f729 F src/func.c 0c28599430856631216b6c0131c51c89bf516026 F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3 F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af @@ -139,7 +136,7 @@ F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e F src/loadext.c 1c7a61ce1281041f437333f366a96aa0d29bb581 F src/main.c a487fe90aecaccb142e4a6b738c7e26e99145bcd -F src/malloc.c f8b9bcd134ebfc95103d6834882567e66176d455 +F src/malloc.c d321131022bd83f95f871cde212ddb01ed217f2f F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2 F src/mem2.c 2ee7bdacda8299b5a91cff9f7ee3e46573195c38 @@ -173,11 +170,11 @@ F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 F src/select.c 74fef1334bec27e606ef0b19e5c41cd0a639e69c F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714 -F src/sqlite.h.in 2585fc82c922f2772e201e60a76d5fd1ca18370e +F src/sqlite.h.in 05469b38a929b31278c7ed30b3d3f4b4e17817b6 F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89 -F src/sqliteInt.h 13819304fa198f4a3ff0e6aa8d3a94df662d2829 +F src/sqliteInt.h 0ec613ec2fbb26a1f363425c945a22667d659af5 F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3 -F src/status.c e2ad9f18c16209dab501e26020590fcebb2b751b +F src/status.c ab77d17fbfe0ab800b3c54dd6853e30b1dc1634e F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e F src/tclsqlite.c ae1e4fb653c91ddad7e2534d209711a12604ccc4 F src/test1.c ff3b4533fc4d78d1bff2ef831a5791db55096ed3 @@ -203,7 +200,7 @@ F src/test_intarray.c d879bbf8e4ce085ab966d1f3c896a7c8b4f5fc99 F src/test_intarray.h 489edb9068bb926583445cb02589344961054207 F src/test_journal.c 424a334cdfdc8a6f975abe3641440147bded3185 F src/test_loadext.c df586c27176e3c2cb2e099c78da67bf14379a56e -F src/test_malloc.c 4ab85f2b8ae3a237f4e6557b0a641181a19ffab1 +F src/test_malloc.c 76bce0d5846dbfc9f24a65efab781b1ffb441380 F src/test_mutex.c ce06b59aca168cd8c520b77159a24352a7469bd3 F src/test_onefile.c 40cf9e212a377a6511469384a64b01e6e34b2eec F src/test_osinst.c f408c6a181f2fb04c56273afd5c3e1e82f60392c @@ -222,14 +219,14 @@ F src/utf.c 1baeeac91707a4df97ccc6141ec0f808278af685 F src/util.c 32aebf04c10e51ad3977a928b7416bed671b620b F src/vacuum.c 241a8386727c1497eba4955933356dfba6ff8c9f F src/vdbe.c 6294de3327e09d14e9c06ecfd10e57c2d8e85307 -F src/vdbe.h 471f6a3dcec4817ca33596fe7f6654d56c0e75f3 +F src/vdbe.h 7c5de1c7e2e6bc1f301bb95297aeb5d1b8455764 F src/vdbeInt.h 19ebc8c2a2e938340051ee65af3f377fb99102d1 F src/vdbeapi.c dc3138f10afbc95ed3c21dd25abb154504b1db9d -F src/vdbeaux.c 7f99c1f00e4b31e8b28d8a87ecc2322bb46ae99c +F src/vdbeaux.c 9f18d228ee96b26e10f908f8f9597c700347df71 F src/vdbeblob.c 258a6010ba7a82b72b327fb24c55790655689256 F src/vdbemem.c 5e579abf6532001dfbee0e640dc34eae897a9807 F src/vdbetrace.c 864cef96919323482ebd9986f2132435115e9cc2 -F src/vtab.c 4b09cb71090b8a9ce8fdc451816ed2bd62861dcc +F src/vtab.c 74bc8e035f3762d3cab1498dbbf2117e798540a5 F src/wal.c 0925601f3299c2941a67c9cfff41ee710f70ca82 F src/wal.h 906c85760598b18584921fe08008435aa4eeeeb2 F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f @@ -333,7 +330,7 @@ F test/createtab.test 199cf68f44e5d9e87a0b8afc7130fdeb4def3272 F test/cse.test 277350a26264495e86b1785f34d2d0c8600e021c F test/ctime.test 7bd009071e242aac4f18521581536b652b789a47 F test/date.test 6354b883f922c38046a8efbad187cc95df6da023 -F test/dbstatus.test 838447a0ecca1232675b025c0a518a9ef0f8057e +F test/dbstatus.test 6e4c7a82053f8bd0e218a11f6675ea927b3b2afc F test/default.test 6faf23ccb300114924353007795aa9a8ec0aa9dc F test/delete.test f7629d9eb245dfca170169cc5c7a735dec34aeb4 F test/delete2.test 3a03f2cca1f9a67ec469915cb8babd6485db43fa @@ -841,14 +838,10 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224 F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f -P 54e5886d841af69c8fa965bbcd637441d4a398ba -R 691d9a52bb7cbfd5e8dcb9749da71253 -U drh -Z 52e232c87c73a10044c307be36db6f45 ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.6 (GNU/Linux) - -iD8DBQFMShcQoxKgR168RlERAmjGAJoCLXWaZ2DuRMaoWPWt76CoNmu7twCfbMSo -7kHJSzk9KhxmMxRjeT/UwcA= -=7tfR ------END PGP SIGNATURE----- +P a6bb2108bfb562a7067ef6150e43382736c0c4f5 +R 5ce07ed2a0b02fd2492d8af031b47b67 +T *branch * experimental +T *sym-experimental * +T -sym-trunk * +U dan +Z 4780c44291ffd7692ca32c6ddced636f diff --git a/manifest.uuid b/manifest.uuid index 57ab5036dc..5222381d13 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -a6bb2108bfb562a7067ef6150e43382736c0c4f5 \ No newline at end of file +9aa30342f4de4eff630520ea8e07ad253d3f0877 \ No newline at end of file diff --git a/src/analyze.c b/src/analyze.c index c41fba3437..d96deb3c21 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; jaSample[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 1ca6e24693..989489f1e4 100644 --- a/src/build.c +++ b/src/build.c @@ -347,30 +347,12 @@ 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 @@ -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 217badffa7..399e35005a 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 e34c27975f..05ea510063 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 711e815e0f..3e18d22c41 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 a9a0885c18..66f1cffb5b 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 be2e6dc2f6..711827b50c 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; inDb; 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 1267f6e78f..e32b78e20b 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 c234d51a5b..be77e2ff6b 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 cf53f90e31..a297ddd8dd 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]; pzMalloc); + } + }else for(pEnd=&p[N]; pdb==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. */ @@ -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 5bf8676043..e57f4e4807 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; inModuleArg; i++){ diff --git a/test/dbstatus.test b/test/dbstatus.test index c7a6b5847d..f8e17ed3b2 100644 --- a/test/dbstatus.test +++ b/test/dbstatus.test @@ -42,4 +42,248 @@ do_test dbstatus-1.2 { lindex [sqlite3_db_status db SQLITE_DBSTATUS_CACHE_USED 0] 1 } [expr {$BASESZ + 10*$PAGESZ}] + +proc lookaside {db} { + expr { $::lookaside_buffer_size * + [lindex [sqlite3_db_status $db SQLITE_DBSTATUS_LOOKASIDE_USED 0] 1] + } +} + +#--------------------------------------------------------------------------- +# Run the dbstatus-2 and dbstatus-3 tests with a couple of different +# lookaside buffer sizes. +# +foreach ::lookaside_buffer_size {0 64 120} { + + #------------------------------------------------------------------------- + # Tests for SQLITE_DBSTATUS_SCHEMA_USED. + # + # Each test in the following block works as follows. Each test uses a + # different database schema. + # + # 1. Open a connection to an empty database. Disable statement caching. + # + # 2. Execute the SQL to create the database schema. Measure the total + # heap and lookaside memory allocated by SQLite, and the memory + # allocated for the database schema according to sqlite3_db_status(). + # + # 3. Drop all tables in the database schema. Measure the total memory + # and the schema memory again. + # + # 4. Repeat step 2. + # + # 5. Repeat step 3. + # + # Then test that: + # + # a) The difference in schema memory quantities in steps 2 and 3 is the + # same as the difference in total memory in steps 2 and 3. + # + # b) Step 4 reports the same amount of schema and total memory used as + # in step 2. + # + # c) Step 5 reports the same amount of schema and total memory used as + # in step 3. + # + foreach {tn schema} { + 1 { CREATE TABLE t1(a, b) } + 2 { CREATE TABLE t1(a PRIMARY KEY, b REFERENCES t1, c UNIQUE) } + 3 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a, b); + } + 4 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE TRIGGER AFTER INSERT ON t1 BEGIN + INSERT INTO t2 VALUES(new.a, new.b); + SELECT * FROM t1, t2 WHERE a=c AND b=d GROUP BY b HAVING a>5 ORDER BY a; + END; + } + 5 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE VIEW v1 AS SELECT * FROM t1 UNION SELECT * FROM t2; + } + 6 { + CREATE TABLE t1(a, b); + CREATE INDEX i1 ON t1(a); + CREATE INDEX i2 ON t1(a,b); + CREATE INDEX i3 ON t1(b,b); + INSERT INTO t1 VALUES(randomblob(20), randomblob(25)); + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + INSERT INTO t1 SELECT randomblob(20), randomblob(25) FROM t1; + ANALYZE; + } + 7 { + CREATE TABLE t1(a, b); + CREATE TABLE t2(c, d); + CREATE VIEW v1 AS + SELECT * FROM t1 + UNION + SELECT * FROM t2 + UNION ALL + SELECT c||b, d||a FROM t2 LEFT OUTER JOIN t1 GROUP BY c, d + ORDER BY 1, 2 + ; + CREATE TRIGGER tr1 INSTEAD OF INSERT ON v1 BEGIN + SELECT * FROM v1; + UPDATE t1 SET a=5, b=(SELECT c FROM t2); + END; + SELECT * FROM v1; + } + } { + set tn "$::lookaside_buffer_size-$tn" + + # Step 1. + db close + file delete -force test.db + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 + db cache size 0 + + # Step 2. + execsql $schema + set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc1 [lookaside db] + set nSchema1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 3. + drop_all_tables + set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc2 [lookaside db] + set nSchema2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 4. + execsql $schema + set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc3 [lookaside db] + set nSchema3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + # Step 5. + drop_all_tables + set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc4 [lookaside db] + set nSchema4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_SCHEMA_USED 0] 1] + + set nFree [expr {$nAlloc1-$nAlloc2}] + do_test dbstatus-2.$tn.a { expr {$nSchema1-$nSchema2} } $nFree + do_test dbstatus-2.$tn.b { list $nAlloc1 $nSchema1 } "$nAlloc3 $nSchema3" + do_test dbstatus-2.$tn.c { list $nAlloc2 $nSchema2 } "$nAlloc4 $nSchema4" + } + + #------------------------------------------------------------------------- + # Tests for SQLITE_DBSTATUS_STMT_USED. + # + # Each test in the following block works as follows. Each test uses a + # different database schema. + # + # 1. Open a connection to an empty database. Initialized the database + # schema. + # + # 2. Prepare a bunch of SQL statements. Measure the total heap and + # lookaside memory allocated by SQLite, and the memory allocated + # for the prepared statements according to sqlite3_db_status(). + # + # 3. Finalize all prepared statements Measure the total memory + # and the prepared statement memory again. + # + # 4. Repeat step 2. + # + # 5. Repeat step 3. + # + # Then test that: + # + # a) The difference in schema memory quantities in steps 2 and 3 is the + # same as the difference in total memory in steps 2 and 3. + # + # b) Step 4 reports the same amount of schema and total memory used as + # in step 2. + # + # c) Step 5 reports the same amount of schema and total memory used as + # in step 3. + # + foreach {tn schema statements} { + 1 { CREATE TABLE t1(a, b) } { + SELECT * FROM t1; + INSERT INTO t1 VALUES(1, 2); + INSERT INTO t1 SELECT * FROM t1; + UPDATE t1 SET a=5; + DELETE FROM t1; + } + 2 { + PRAGMA recursive_triggers = 1; + CREATE TABLE t1(a, b); + CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t1 VALUES(new.a-1, new.b); + END; + } { + INSERT INTO t1 VALUES(5, 'x'); + } + 3 { + PRAGMA recursive_triggers = 1; + CREATE TABLE t1(a, b); + CREATE TABLE t2(a, b); + CREATE TRIGGER tr1 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t2 VALUES(new.a-1, new.b); + END; + CREATE TRIGGER tr2 AFTER INSERT ON t1 WHEN (new.a>0) BEGIN + INSERT INTO t1 VALUES(new.a-1, new.b); + END; + } { + INSERT INTO t1 VALUES(10, 'x'); + } + 4 { + CREATE TABLE t1(a, b); + } { + SELECT count(*) FROM t1 WHERE upper(a)='ABC'; + } + } { + set tn "$::lookaside_buffer_size-$tn" + + # Step 1. + db close + file delete -force test.db + sqlite3 db test.db + sqlite3_db_config_lookaside db 0 $::lookaside_buffer_size 500 + db cache size 1000 + + execsql $schema + db cache flush + + # Step 2. + execsql $statements + set nAlloc1 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc1 [lookaside db] + set nStmt1 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + execsql $statements + + # Step 3. + db cache flush + set nAlloc2 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc3 [lookaside db] + set nStmt2 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + + # Step 3. + execsql $statements + set nAlloc3 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc3 [lookaside db] + set nStmt3 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + execsql $statements + + # Step 4. + db cache flush + set nAlloc4 [lindex [sqlite3_status SQLITE_STATUS_MEMORY_USED 0] 1] + incr nAlloc4 [lookaside db] + set nStmt4 [lindex [sqlite3_db_status db SQLITE_DBSTATUS_STMT_USED 0] 1] + + set nFree [expr {$nAlloc1-$nAlloc2}] + do_test dbstatus-3.$tn.a { list $nStmt2 } {0} + do_test dbstatus-3.$tn.b { list $nStmt1 } [list $nFree] + do_test dbstatus-3.$tn.c { list $nAlloc1 $nStmt1 } [list $nAlloc3 $nStmt3] + do_test dbstatus-3.$tn.d { list $nAlloc2 $nStmt2 } [list $nAlloc4 $nStmt4] + } +} + finish_test