Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1b0bfc645c | |||
| fcc8954a49 | |||
| e79ffb6570 | |||
| 25f81883b3 |
@@ -1,8 +1,8 @@
|
||||
-----BEGIN PGP SIGNED MESSAGE-----
|
||||
Hash: SHA1
|
||||
|
||||
C Make\ssure\sall\smemory\sfrom\ssqlite3DbMalloc()\sis\sfreed\sby\ssqlite3DbFree()\sand\nall\smemory\sfrom\ssqlite3_malloc()\sis\sfreed\sby\ssqlite3_free().
|
||||
D 2010-07-24T16:34:38
|
||||
C Fixes\sto\sprior\scheckins\sso\sthat\sthey\scompile\sand\srun\seven\sif\sSQLITE_MEMDEBUG\nis\snot\sdefined.
|
||||
D 2010-07-25T02:39:07
|
||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||
F Makefile.in ec08dc838fd8110fe24c92e5130bcd91cbb1ff2e
|
||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||
@@ -109,8 +109,8 @@ F spec.template 86a4a43b99ebb3e75e6b9a735d5fd293a24e90ca
|
||||
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
|
||||
F sqlite3.1 6be1ad09113570e1fc8dcaff84c9b0b337db5ffc
|
||||
F sqlite3.pc.in ae6f59a76e862f5c561eb32a380228a02afc3cad
|
||||
F src/alter.c 8dc27638e7e2553e80b2b621f232be5eb1e85ef3
|
||||
F src/analyze.c c1bcd04b720e3300edafcdfe8bd2ba6bd9a8dca8
|
||||
F src/alter.c 2b9a10affb4d202d51473faee831bb269b1d6dd7
|
||||
F src/analyze.c c949a108d1dc43ca12bdd9a5c7f1ffde11e3061d
|
||||
F src/attach.c 17bec1f18254d9341369f20f90ba24ce35d20d10
|
||||
F src/auth.c 523da7fb4979469955d822ff9298352d6b31de34
|
||||
F src/backup.c 51d83300fe0baee39405c416ceb19a58ed30a8ed
|
||||
@@ -119,7 +119,7 @@ F src/btmutex.c 96a12f50f7a17475155971a241d85ec5171573ff
|
||||
F src/btree.c ed454f53f7bf7fb89c7a89f93fa93c5cd35e7aae
|
||||
F src/btree.h dd83041eda10c17daf023257c1fc883b5f71f85a
|
||||
F src/btreeInt.h b0c87f6725b06a0aa194a6d25d54b16ce9d6e291
|
||||
F src/build.c 6d3bd01296ac236f50519ec74711a38c8cee4fd7
|
||||
F src/build.c b4f3c9ba159a4c6e3f6ba6b254f2b560d0fb7a44
|
||||
F src/callback.c da3c38d0ef5d7f04fae371e519bda61aa9cb1704
|
||||
F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
|
||||
F src/ctime.c 4f3aadad62c6c9f0d4e5a96718516ac4e3c598df
|
||||
@@ -133,16 +133,16 @@ F src/global.c 02335177cf6946fe5525c6f0755cf181140debf3
|
||||
F src/hash.c 458488dcc159c301b8e7686280ab209f1fb915af
|
||||
F src/hash.h 2894c932d84d9f892d4b4023a75e501f83050970
|
||||
F src/hwtime.h d32741c8f4df852c7d959236615444e2b1063b08
|
||||
F src/insert.c ba455ebb100283ccc5da03da3498fcbca48ce6bb
|
||||
F src/insert.c 45d9a637f3ede400339f916d841b3e27bd16827e
|
||||
F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
|
||||
F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
|
||||
F src/lempar.c 7f026423f4d71d989e719a743f98a1cbd4e6d99e
|
||||
F src/loadext.c 6d422ea91cf3d2d00408c5a8f2391cd458da85f8
|
||||
F src/main.c a487fe90aecaccb142e4a6b738c7e26e99145bcd
|
||||
F src/malloc.c 8394eba1c319a6d840ee411e23966d53a87f745a
|
||||
F src/malloc.c 2e38fd5e1539967cf4fa2d96edd82bd9d42183fd
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 89d4ea8d5cdd55635cbaa48ad53132af6294cbb2
|
||||
F src/mem2.c 716e62689d49b01ee742be6c745b1c3bbfbccd18
|
||||
F src/mem2.c 47361e1235c5b74b4a269a62bcefda35a1fc91c4
|
||||
F src/mem3.c 9b237d911ba9904142a804be727cc6664873f8a3
|
||||
F src/mem5.c eb7a5cb98915dd7a086fa415ce3a5a0f20d0acff
|
||||
F src/memjournal.c 4a93a25ad9f76c40afa070ffd7187eb3a5fd7aee
|
||||
@@ -171,11 +171,11 @@ F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506
|
||||
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
|
||||
F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
|
||||
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
|
||||
F src/select.c 74fef1334bec27e606ef0b19e5c41cd0a639e69c
|
||||
F src/select.c b0fec59c8bb5246be793baa40b0518041837cc12
|
||||
F src/shell.c fd4ccdb37c3b68de0623eb938a649e0990710714
|
||||
F src/sqlite.h.in 2585fc82c922f2772e201e60a76d5fd1ca18370e
|
||||
F src/sqlite3ext.h 69dfb8116af51b84a029cddb3b35062354270c89
|
||||
F src/sqliteInt.h e24b335bd57f673c39a9098fdc98d8ad2d5dc3da
|
||||
F src/sqliteInt.h 03ee6d40cf4139b040ab5c7ac6348db54bc78c08
|
||||
F src/sqliteLimit.h 196e2f83c3b444c4548fc1874f52f84fdbda40f3
|
||||
F src/status.c e2ad9f18c16209dab501e26020590fcebb2b751b
|
||||
F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
|
||||
@@ -233,7 +233,7 @@ F src/vtab.c 838248ec64b1eda7afdf9303df30e8f1b96bc60f
|
||||
F src/wal.c 0925601f3299c2941a67c9cfff41ee710f70ca82
|
||||
F src/wal.h 906c85760598b18584921fe08008435aa4eeeeb2
|
||||
F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f
|
||||
F src/where.c 79202ca81e740eeb1f54512147e29b6c518d84ca
|
||||
F src/where.c 8cf9ca6fff115d29f578e282edc72fa0f028864b
|
||||
F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2
|
||||
F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87
|
||||
F test/all.test 6745008c144bd2956d58864d21f7b304689c1cce
|
||||
@@ -841,18 +841,14 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
|
||||
P a6bb2108bfb562a7067ef6150e43382736c0c4f5
|
||||
R 57ad848eef21f87b491f957007f9c787
|
||||
T *bgcolor * #a0c0f0
|
||||
T *branch * malloc-enhancement
|
||||
T *sym-malloc-enhancement *
|
||||
T -sym-trunk *
|
||||
P da2f62c502dfd3b53940b08a971137048732ecea
|
||||
R 0fe67466768edd87682f3d51d160d305
|
||||
U drh
|
||||
Z 0e50e08d3aab5ed9a84c7eabe091b80c
|
||||
Z a683cf41c3a0308a70e3646adfaebebd
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
||||
|
||||
iD8DBQFMSxYioxKgR168RlERAgv5AJwKcCKth9vEcMrMziFc3bR3CMKXOgCeOwNK
|
||||
xsMH21OEzgn+77HK/85/56Q=
|
||||
=jlvX
|
||||
iD8DBQFMS6PPoxKgR168RlERAgMpAJ0ZwfoWu2wFclx1N1sNN+VVUqK3QACgi2LD
|
||||
0zBS3EvSeICS3VbyxSU/aMM=
|
||||
=k4pP
|
||||
-----END PGP SIGNATURE-----
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
ac1f37a647e9ed1c00a901d26d9956a86c40117a
|
||||
548bf3f7d7b962d3eb0f5c874ecf40a8703d4d5d
|
||||
@@ -775,6 +775,7 @@ void sqlite3AlterBeginAddColumn(Parse *pParse, SrcList *pSrc){
|
||||
assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 );
|
||||
pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc);
|
||||
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
|
||||
sqlite3MemLink(pNew, pNew->zName);
|
||||
if( !pNew->aCol || !pNew->zName ){
|
||||
db->mallocFailed = 1;
|
||||
goto exit_begin_add_column;
|
||||
|
||||
+16
-33
@@ -486,28 +486,6 @@ static int analysisLoader(void *pData, int argc, char **argv, char **NotUsed){
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** If the Index.aSample variable is not NULL, delete the aSample[] array
|
||||
** and its contents.
|
||||
*/
|
||||
void sqlite3DeleteIndexSamples(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(0, pIdx->aSample);
|
||||
pIdx->aSample = 0;
|
||||
}
|
||||
#else
|
||||
UNUSED_PARAMETER(pIdx);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The
|
||||
** contents of sqlite_stat1 are used to populate the Index.aiRowEst[]
|
||||
@@ -542,7 +520,11 @@ 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);
|
||||
if( pIdx->aSample ){
|
||||
sqlite3MemUnlink(pIdx, pIdx->aSample);
|
||||
sqlite3DbFree(db, pIdx->aSample);
|
||||
pIdx->aSample = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check to make sure the sqlite_stat1 table exists */
|
||||
@@ -588,20 +570,20 @@ int sqlite3AnalysisLoad(sqlite3 *db, int iDb){
|
||||
int iSample = sqlite3_column_int(pStmt, 1);
|
||||
if( iSample<SQLITE_INDEX_SAMPLES && iSample>=0 ){
|
||||
int eType = sqlite3_column_type(pStmt, 2);
|
||||
IndexSample *aSample = pIdx->aSample;
|
||||
|
||||
if( pIdx->aSample==0 ){
|
||||
if( aSample==0 ){
|
||||
static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES;
|
||||
pIdx->aSample = (IndexSample *)sqlite3DbMallocRaw(0, sz);
|
||||
if( pIdx->aSample==0 ){
|
||||
pIdx->aSample = aSample = sqlite3DbMallocZeroChild(0, sz, pIdx);
|
||||
if( aSample==0 ){
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
}
|
||||
memset(pIdx->aSample, 0, sz);
|
||||
}
|
||||
|
||||
assert( pIdx->aSample );
|
||||
|
||||
assert( aSample );
|
||||
{
|
||||
IndexSample *pSample = &pIdx->aSample[iSample];
|
||||
IndexSample *pSample = &aSample[iSample];
|
||||
pSample->eType = (u8)eType;
|
||||
if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){
|
||||
pSample->u.r = sqlite3_column_double(pStmt, 2);
|
||||
@@ -619,9 +601,10 @@ 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);
|
||||
char *zSample;
|
||||
pSample->u.z = zSample = sqlite3DbMallocRawChild(0,n,aSample);
|
||||
if( zSample ){
|
||||
memcpy(zSample, z, n);
|
||||
}else{
|
||||
db->mallocFailed = 1;
|
||||
break;
|
||||
|
||||
+6
-15
@@ -346,10 +346,6 @@ Index *sqlite3FindIndex(sqlite3 *db, const char *zName, const char *zDb){
|
||||
** Reclaim the memory used by an index
|
||||
*/
|
||||
static void freeIndex(sqlite3 *db, Index *p){
|
||||
#ifndef SQLITE_OMIT_ANALYZE
|
||||
sqlite3DeleteIndexSamples(p);
|
||||
#endif
|
||||
sqlite3DbFree(db, p->zColAff);
|
||||
sqlite3DbFree(db, p);
|
||||
}
|
||||
|
||||
@@ -478,9 +474,6 @@ static void sqliteResetColumnNames(sqlite3 *db, Table *pTable){
|
||||
for(i=0; i<pTable->nCol; i++, pCol++){
|
||||
sqlite3DbFree(db, pCol->zName);
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
sqlite3DbFree(db, pCol->zDflt);
|
||||
sqlite3DbFree(db, pCol->zType);
|
||||
sqlite3DbFree(db, pCol->zColl);
|
||||
}
|
||||
sqlite3DbFree(db, pTable->aCol);
|
||||
}
|
||||
@@ -523,8 +516,6 @@ void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
|
||||
/* Delete the Table structure itself.
|
||||
*/
|
||||
sqliteResetColumnNames(db, pTable);
|
||||
sqlite3DbFree(db, pTable->zName);
|
||||
sqlite3DbFree(db, pTable->zColAff);
|
||||
sqlite3SelectDelete(db, pTable->pSelect);
|
||||
#ifndef SQLITE_OMIT_CHECK
|
||||
sqlite3ExprDelete(db, pTable->pCheck);
|
||||
@@ -813,6 +804,7 @@ void sqlite3StartTable(
|
||||
goto begin_table_error;
|
||||
}
|
||||
pTable->zName = zName;
|
||||
sqlite3MemLink(pTable, zName);
|
||||
pTable->iPKey = -1;
|
||||
pTable->pSchema = db->aDb[iDb].pSchema;
|
||||
pTable->nRef = 1;
|
||||
@@ -945,7 +937,7 @@ void sqlite3AddColumn(Parse *pParse, Token *pName){
|
||||
}
|
||||
if( (p->nCol & 0x7)==0 ){
|
||||
Column *aNew;
|
||||
aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
|
||||
aNew = sqlite3DbRealloc(db, p->aCol, (p->nCol+8)*sizeof(p->aCol[0]));
|
||||
if( aNew==0 ){
|
||||
sqlite3DbFree(db, z);
|
||||
return;
|
||||
@@ -1056,6 +1048,7 @@ void sqlite3AddColumnType(Parse *pParse, Token *pType){
|
||||
pCol = &p->aCol[p->nCol-1];
|
||||
assert( pCol->zType==0 );
|
||||
pCol->zType = sqlite3NameFromToken(pParse->db, pType);
|
||||
sqlite3MemLink(p->aCol, pCol->zType);
|
||||
pCol->affinity = sqlite3AffinityType(pCol->zType);
|
||||
}
|
||||
|
||||
@@ -1086,9 +1079,9 @@ void sqlite3AddDefaultValue(Parse *pParse, ExprSpan *pSpan){
|
||||
*/
|
||||
sqlite3ExprDelete(db, pCol->pDflt);
|
||||
pCol->pDflt = sqlite3ExprDup(db, pSpan->pExpr, EXPRDUP_REDUCE);
|
||||
sqlite3DbFree(db, pCol->zDflt);
|
||||
pCol->zDflt = sqlite3DbStrNDup(db, (char*)pSpan->zStart,
|
||||
(int)(pSpan->zEnd - pSpan->zStart));
|
||||
sqlite3MemLink(p->aCol, pCol->zDflt);
|
||||
}
|
||||
}
|
||||
sqlite3ExprDelete(db, pSpan->pExpr);
|
||||
@@ -1211,6 +1204,7 @@ void sqlite3AddCollateType(Parse *pParse, Token *pToken){
|
||||
if( sqlite3LocateCollSeq(pParse, zColl) ){
|
||||
Index *pIdx;
|
||||
p->aCol[i].zColl = zColl;
|
||||
sqlite3MemLink(p->aCol, zColl);
|
||||
|
||||
/* If the column is declared as "<name> PRIMARY KEY COLLATE <type>",
|
||||
** then an index may have been created on this column before the
|
||||
@@ -2813,10 +2807,7 @@ Index *sqlite3CreateIndex(
|
||||
|
||||
/* Clean up before exiting */
|
||||
exit_create_index:
|
||||
if( pIndex ){
|
||||
sqlite3DbFree(db, pIndex->zColAff);
|
||||
sqlite3DbFree(db, pIndex);
|
||||
}
|
||||
sqlite3DbFree(db, pIndex);
|
||||
sqlite3ExprListDelete(db, pList);
|
||||
sqlite3SrcListDelete(db, pTblName);
|
||||
sqlite3DbFree(db, zName);
|
||||
|
||||
+2
-3
@@ -67,7 +67,7 @@ const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){
|
||||
int n;
|
||||
Table *pTab = pIdx->pTable;
|
||||
sqlite3 *db = sqlite3VdbeDb(v);
|
||||
pIdx->zColAff = (char *)sqlite3DbMallocRaw(0, pIdx->nColumn+2);
|
||||
pIdx->zColAff = (char *)sqlite3DbMallocRawChild(0, pIdx->nColumn+2, pIdx);
|
||||
if( !pIdx->zColAff ){
|
||||
db->mallocFailed = 1;
|
||||
return 0;
|
||||
@@ -109,12 +109,11 @@ void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){
|
||||
int i;
|
||||
sqlite3 *db = sqlite3VdbeDb(v);
|
||||
|
||||
zColAff = (char *)sqlite3DbMallocRaw(0, pTab->nCol+1);
|
||||
zColAff = (char *)sqlite3DbMallocRawChild(0, pTab->nCol+1, pTab);
|
||||
if( !zColAff ){
|
||||
db->mallocFailed = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for(i=0; i<pTab->nCol; i++){
|
||||
zColAff[i] = pTab->aCol[i].affinity;
|
||||
}
|
||||
|
||||
+242
-36
@@ -15,6 +15,86 @@
|
||||
#include "sqliteInt.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
/*
|
||||
** There are two general-purpose memory allocators:
|
||||
**
|
||||
** Simple:
|
||||
**
|
||||
** sqlite3_malloc
|
||||
** sqlite3_free
|
||||
** sqlite3_realloc
|
||||
** sqlite3Malloc
|
||||
** sqlite3MallocSize
|
||||
** sqlite3_mprintf
|
||||
**
|
||||
** Enhanced:
|
||||
**
|
||||
** sqlite3DbMallocRaw
|
||||
** sqlite3DbMallocZero
|
||||
** sqlite3DbFree
|
||||
** sqlite3DbRealloc
|
||||
** sqlite3MPrintf
|
||||
** sqlite3DbMalloc
|
||||
**
|
||||
** All external allocations use the simple memory allocator.
|
||||
** The enhanced allocator is used internally only, and is not
|
||||
** available to extensions or applications.
|
||||
**
|
||||
** The enhanced allocator is a wrapper around the simple allocator that
|
||||
** adds the following capabilities:
|
||||
**
|
||||
** (1) Access to lookaside memory associated with a database connection.
|
||||
**
|
||||
** (2) The ability to link allocations into a hierarchy with automatic
|
||||
** deallocation of all elements of the subhierarchy whenever any
|
||||
** element within the hierarchy is deallocated.
|
||||
**
|
||||
** The two allocators are incompatible in the sense that allocations that
|
||||
** originate from the simple allocator must be deallocated using the simple
|
||||
** deallocator and allocations that originate from the enhanced allocator must
|
||||
** be deallocated using the enhanced deallocator. You cannot check-out
|
||||
** memory from one allocator then return it to the other.
|
||||
*/
|
||||
|
||||
/*
|
||||
** The automatic hierarchical deallocation feature of the enhanced allocator
|
||||
** is implemented by adding an instance of the following structure to the
|
||||
** header of each enhanced allocation.
|
||||
**
|
||||
** In order to preserve alignment, this structure must be a multiple of
|
||||
** 8 bytes in size.
|
||||
*/
|
||||
typedef struct EMemHdr EMemHdr;
|
||||
struct EMemHdr {
|
||||
EMemHdr *pEChild; /* List of children of this node */
|
||||
EMemHdr *pESibling; /* Other nodes that are children of the same parent */
|
||||
#ifdef SQLITE_MEMDEBUG
|
||||
u32 iEMemMagic; /* Magic number for sanity checking */
|
||||
u32 isAChild; /* True if this allocate is a child of another */
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
** Macros for querying and setting debugging fields of the EMemHdr object.
|
||||
*/
|
||||
#ifdef SQLITE_MEMDEBUG
|
||||
# define isValidEMem(E) ((E)->iEMemMagic==0xc0a43fad)
|
||||
# define setValidEMem(E) (E)->iEMemMagic = 0xc0a43fad
|
||||
# define clearValidEMem(E) (E)->iEMemMagic = 0x12345678
|
||||
# define isChildEMem(E) ((E)->isAChild!=0)
|
||||
# define notChildEMem(E) ((E)->isAChild==0)
|
||||
# define setChildEMem(E) (E)->isAChild = 1
|
||||
# define clearChildEMem(E) (E)->isAChild = 0
|
||||
#else
|
||||
# define isValidEMem(E) 1
|
||||
# define setValidEMem(E)
|
||||
# define clearValidEMem(E)
|
||||
# define isChildEMem(E) 1
|
||||
# define notChildEMem(E) 1
|
||||
# define setChildEMem(E)
|
||||
# define clearChildEMem(E)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** This routine runs when the memory allocator sees that the
|
||||
** total memory allocation is about to exceed the soft heap
|
||||
@@ -32,6 +112,10 @@ static void softHeapLimitEnforcer(
|
||||
/*
|
||||
** Set the soft heap-size limit for the library. Passing a zero or
|
||||
** negative value indicates no limit.
|
||||
**
|
||||
** If the total amount of memory allocated (by all threads) exceeds
|
||||
** the soft heap limit, then sqlite3_release_memory() is invoked to
|
||||
** try to free up some memory before proceeding.
|
||||
*/
|
||||
void sqlite3_soft_heap_limit(int n){
|
||||
sqlite3_uint64 iLimit;
|
||||
@@ -368,7 +452,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);
|
||||
@@ -418,21 +502,29 @@ static int isLookaside(sqlite3 *db, void *p){
|
||||
/*
|
||||
** Return the size of a memory allocation previously obtained from
|
||||
** sqlite3Malloc() or sqlite3_malloc().
|
||||
**
|
||||
** The size returned is the usable size and does not include any
|
||||
** bookkeeping overhead or sentinals at the end of the allocation.
|
||||
*/
|
||||
int sqlite3MallocSize(void *p){
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
assert( !sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
|
||||
assert( sqlite3MemdebugNoType(p, MEMTYPE_RECURSIVE) );
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
}
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
||||
int sqlite3DbMallocSize(sqlite3 *db, void *pObj){
|
||||
EMemHdr *p = (EMemHdr*)pObj;
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
if( p ){
|
||||
p--;
|
||||
assert( isValidEMem(p) );
|
||||
}
|
||||
if( isLookaside(db, p) ){
|
||||
return db->lookaside.sz;
|
||||
return db->lookaside.sz - sizeof(EMemHdr);
|
||||
}else{
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
|
||||
assert( sqlite3MemdebugHasType(p,
|
||||
db ? (MEMTYPE_DB|MEMTYPE_HEAP) : MEMTYPE_HEAP) );
|
||||
return sqlite3GlobalConfig.m.xSize(p);
|
||||
return sqlite3GlobalConfig.m.xSize(p) - sizeof(EMemHdr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -441,7 +533,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_RECURSIVE) );
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
if( sqlite3GlobalConfig.bMemstat ){
|
||||
sqlite3_mutex_enter(mem0.mutex);
|
||||
@@ -455,26 +547,47 @@ void sqlite3_free(void *p){
|
||||
|
||||
/*
|
||||
** Free memory that might be associated with a particular database
|
||||
** connection.
|
||||
** connection. All child allocations are also freed.
|
||||
**
|
||||
** pObj must be a top-level allocation in the heirarchy. It is not
|
||||
** allowed to delete a child allocation since that would leave a
|
||||
** dangling child pointer in the parent.
|
||||
*/
|
||||
void sqlite3DbFree(sqlite3 *db, void *p){
|
||||
void sqlite3DbFree(sqlite3 *db, void *pObj){
|
||||
EMemHdr *p = (EMemHdr*)pObj;
|
||||
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( p ) p--;
|
||||
assert( p==0 || notChildEMem(p) ); /* pObj is not child allocation */
|
||||
while( p ){
|
||||
EMemHdr *pNext = p->pESibling;
|
||||
assert( isValidEMem(p) ); /* pObj and all siblings are valid */
|
||||
if( p->pEChild ){
|
||||
clearChildEMem(p->pEChild);
|
||||
sqlite3DbFree(db, (void*)&p->pEChild[1]);
|
||||
}
|
||||
if( isLookaside(db, p) ){
|
||||
LookasideSlot *pBuf = (LookasideSlot*)p;
|
||||
clearValidEMem(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);
|
||||
clearValidEMem(p);
|
||||
sqlite3_free(p);
|
||||
}
|
||||
p = pNext;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Change the size of an existing memory allocation
|
||||
** Change the size of an existing memory allocation.
|
||||
**
|
||||
** This is the same as sqlite3_realloc() except that it assumes that
|
||||
** the memory subsystem has already been initialized.
|
||||
*/
|
||||
void *sqlite3Realloc(void *pOld, int nBytes){
|
||||
int nOld, nNew;
|
||||
@@ -502,7 +615,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);
|
||||
@@ -543,7 +656,7 @@ void *sqlite3MallocZero(int n){
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and zero memory. If the allocation fails, make
|
||||
** Allocate and zero memory. If the allocation fails, set
|
||||
** the mallocFailed flag in the connection pointer.
|
||||
*/
|
||||
void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
||||
@@ -554,6 +667,21 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Allocate and zero memory child memory. If the allocation fails, set
|
||||
** the mallocFailed flag in the connection pointer.
|
||||
*/
|
||||
void *sqlite3DbMallocZeroChild(sqlite3 *db, int n, void *pParent){
|
||||
void *p = sqlite3DbMallocRaw(db, n);
|
||||
if( p ){
|
||||
memset(p, 0, n);
|
||||
sqlite3MemLink(pParent, p);
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Allocate and zero memory. If the allocation fails, make
|
||||
** the mallocFailed flag in the connection pointer.
|
||||
@@ -573,8 +701,9 @@ void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
||||
** that all prior mallocs (ex: "a") worked too.
|
||||
*/
|
||||
void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
void *p;
|
||||
EMemHdr *p;
|
||||
assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
||||
n += sizeof(EMemHdr);
|
||||
#ifndef SQLITE_OMIT_LOOKASIDE
|
||||
if( db ){
|
||||
LookasideSlot *pBuf;
|
||||
@@ -588,7 +717,8 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
if( db->lookaside.nOut>db->lookaside.mxOut ){
|
||||
db->lookaside.mxOut = db->lookaside.nOut;
|
||||
}
|
||||
return (void*)pBuf;
|
||||
p = (EMemHdr*)pBuf;
|
||||
goto finish_emalloc_raw;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -597,49 +727,78 @@ void *sqlite3DbMallocRaw(sqlite3 *db, int n){
|
||||
}
|
||||
#endif
|
||||
p = sqlite3Malloc(n);
|
||||
if( !p && db ){
|
||||
db->mallocFailed = 1;
|
||||
if( !p ){
|
||||
if( db ) db->mallocFailed = 1;
|
||||
return 0;
|
||||
}
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_RECURSIVE |
|
||||
((db && db->lookaside.bEnabled) ? MEMTYPE_DB : MEMTYPE_HEAP));
|
||||
|
||||
finish_emalloc_raw:
|
||||
memset(p, 0, sizeof(EMemHdr));
|
||||
setValidEMem(p);
|
||||
return (void*)&p[1];
|
||||
}
|
||||
|
||||
/*
|
||||
** A convenience wrapper around sqlite3DbMallocRaw() and sqlite3MemLink().
|
||||
*/
|
||||
void *sqlite3DbMallocRawChild(sqlite3 *db, int n, void *pParent){
|
||||
void *p = sqlite3DbMallocRaw(db, n);
|
||||
sqlite3MemLink(pParent, p);
|
||||
return p;
|
||||
}
|
||||
|
||||
/*
|
||||
** Resize the block of memory pointed to by p to n bytes. If the
|
||||
** resize fails, set the mallocFailed flag in the connection object.
|
||||
**
|
||||
** The pOld memory block must not be linked into an allocation hierarchy
|
||||
** as a child. It is OK for the allocation to be the root of a hierarchy
|
||||
** of allocations; the only restriction is that there must be no other
|
||||
** allocations above the pOld allocation in the hierarchy. To resize
|
||||
** an allocation that is a child within a hierarchy, first
|
||||
** unlink the allocation, resize it, then relink it.
|
||||
*/
|
||||
void *sqlite3DbRealloc(sqlite3 *db, void *p, int n){
|
||||
void *pNew = 0;
|
||||
void *sqlite3DbRealloc(sqlite3 *db, void *pOld, int n){
|
||||
EMemHdr *p = (EMemHdr*)pOld;
|
||||
EMemHdr *pNew = 0;
|
||||
assert( db!=0 );
|
||||
assert( sqlite3_mutex_held(db->mutex) );
|
||||
if( db->mallocFailed==0 ){
|
||||
if( p==0 ){
|
||||
return sqlite3DbMallocRaw(db, n);
|
||||
}
|
||||
p--;
|
||||
assert( isValidEMem(p) ); /* pOld obtained from extended allocator */
|
||||
assert( notChildEMem(p) ); /* pOld must not be a child allocation */
|
||||
if( isLookaside(db, p) ){
|
||||
if( n<=db->lookaside.sz ){
|
||||
return p;
|
||||
if( n+sizeof(EMemHdr)<=db->lookaside.sz ){
|
||||
return pOld;
|
||||
}
|
||||
pNew = sqlite3DbMallocRaw(db, n);
|
||||
if( pNew ){
|
||||
memcpy(pNew, p, db->lookaside.sz);
|
||||
sqlite3DbFree(db, p);
|
||||
memcpy(pNew-1, p, db->lookaside.sz);
|
||||
setValidEMem(pNew-1);
|
||||
sqlite3DbFree(db, pOld);
|
||||
}
|
||||
}else{
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_RECURSIVE) );
|
||||
assert( sqlite3MemdebugHasType(p, MEMTYPE_DB|MEMTYPE_HEAP) );
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
||||
pNew = sqlite3_realloc(p, n);
|
||||
pNew = sqlite3_realloc(p, n+sizeof(EMemHdr));
|
||||
if( !pNew ){
|
||||
sqlite3MemdebugSetType(p, MEMTYPE_RECURSIVE|MEMTYPE_HEAP);
|
||||
db->mallocFailed = 1;
|
||||
}else{
|
||||
sqlite3MemdebugSetType(pNew, MEMTYPE_RECURSIVE |
|
||||
(db->lookaside.bEnabled ? MEMTYPE_DB : MEMTYPE_HEAP));
|
||||
setValidEMem(pNew);
|
||||
pNew++;
|
||||
}
|
||||
sqlite3MemdebugSetType(pNew, MEMTYPE_RECURSIVE |
|
||||
(db->lookaside.bEnabled ? MEMTYPE_DB : MEMTYPE_HEAP));
|
||||
}
|
||||
}
|
||||
return pNew;
|
||||
return (void*)pNew;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -690,6 +849,53 @@ char *sqlite3DbStrNDup(sqlite3 *db, const char *z, int n){
|
||||
return zNew;
|
||||
}
|
||||
|
||||
/*
|
||||
** Link extended allocation nodes such that deallocating the parent
|
||||
** causes the child to be automatically deallocated.
|
||||
*/
|
||||
void sqlite3MemLink(void *pParentObj, void *pChildObj){
|
||||
EMemHdr *pParent = (EMemHdr*)pParentObj;
|
||||
EMemHdr *pChild = (EMemHdr*)pChildObj;
|
||||
if( pParent && pChild ){
|
||||
pParent--;
|
||||
assert( isValidEMem(pParent) ); /* pParentObj is an extended allocation */
|
||||
pChild--;
|
||||
assert( isValidEMem(pChild) ); /* pChildObj is an extended allocation */
|
||||
assert( notChildEMem(pChild) ); /* pChildObj not a child of another obj */
|
||||
pChild->pESibling = pParent->pEChild;
|
||||
pParent->pEChild = pChild;
|
||||
setChildEMem(pChild);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** pChildObj is a child object of pParentObj due to a prior call
|
||||
** to sqlite3MemLink(). This routine breaks that linkage, making
|
||||
** pChildObj an independent node that is not a child of any other node.
|
||||
*/
|
||||
void sqlite3MemUnlink(void *pParentObj, void *pChildObj){
|
||||
EMemHdr *pParent = (EMemHdr*)pParentObj;
|
||||
EMemHdr *pChild = (EMemHdr*)pChildObj;
|
||||
EMemHdr **pp;
|
||||
|
||||
assert( pParentObj!=0 );
|
||||
assert( pChildObj!=0 );
|
||||
pParent--;
|
||||
assert( isValidEMem(pParent) ); /* pParentObj is an extended allocation */
|
||||
pChild--;
|
||||
assert( isValidEMem(pChild) ); /* pChildObj is an extended allocation */
|
||||
assert( isChildEMem(pChild) ); /* pChildObj a child of something */
|
||||
for(pp=&pParent->pEChild; (*pp)!=pChild; pp = &(*pp)->pESibling){
|
||||
assert( *pp ); /* pChildObj is a child of pParentObj */
|
||||
assert( isValidEMem(*pp) ); /* All children of pParentObj are valid */
|
||||
assert( isChildEMem(*pp) ); /* All children of pParentObj are children */
|
||||
}
|
||||
*pp = pChild->pESibling;
|
||||
pChild->pESibling = 0;
|
||||
clearChildEMem(pChild);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Create a string from the zFromat argument and the va_list that follows.
|
||||
** Store the string in memory obtained from sqliteMalloc() and make *pz
|
||||
|
||||
+21
@@ -408,6 +408,27 @@ 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.
|
||||
|
||||
@@ -1268,11 +1268,13 @@ static void selectAddColumnTypeAndCollation(
|
||||
for(i=0, pCol=aCol; i<nCol; i++, pCol++){
|
||||
p = a[i].pExpr;
|
||||
pCol->zType = sqlite3DbStrDup(db, columnType(&sNC, p, 0, 0, 0));
|
||||
sqlite3MemLink(aCol, pCol->zType);
|
||||
pCol->affinity = sqlite3ExprAffinity(p);
|
||||
if( pCol->affinity==0 ) pCol->affinity = SQLITE_AFF_NONE;
|
||||
pColl = sqlite3ExprCollSeq(pParse, p);
|
||||
if( pColl ){
|
||||
pCol->zColl = sqlite3DbStrDup(db, pColl->zName);
|
||||
sqlite3MemLink(aCol, pCol->zColl);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3097,6 +3099,7 @@ static int selectExpander(Walker *pWalker, Select *p){
|
||||
if( pTab==0 ) return WRC_Abort;
|
||||
pTab->nRef = 1;
|
||||
pTab->zName = sqlite3MPrintf(db, "sqlite_subquery_%p_", (void*)pTab);
|
||||
sqlite3MemLink(pTab, pTab->zName);
|
||||
while( pSel->pPrior ){ pSel = pSel->pPrior; }
|
||||
selectColumnsFromExprList(pParse, pSel->pEList, &pTab->nCol, &pTab->aCol);
|
||||
pTab->iPKey = -1;
|
||||
|
||||
+10
-1
@@ -2487,12 +2487,16 @@ void *sqlite3Malloc(int);
|
||||
void *sqlite3MallocZero(int);
|
||||
void *sqlite3DbMallocZero(sqlite3*, int);
|
||||
void *sqlite3DbMallocRaw(sqlite3*, int);
|
||||
void *sqlite3DbMallocZeroChild(sqlite3*, int, void *pParent);
|
||||
void *sqlite3DbMallocRawChild(sqlite3*, int, void *pParent);
|
||||
char *sqlite3DbStrDup(sqlite3*,const char*);
|
||||
char *sqlite3DbStrNDup(sqlite3*,const char*, int);
|
||||
void *sqlite3Realloc(void*, int);
|
||||
void *sqlite3DbReallocOrFree(sqlite3 *, void *, int);
|
||||
void *sqlite3DbRealloc(sqlite3 *, void *, int);
|
||||
void sqlite3DbFree(sqlite3*, void*);
|
||||
void sqlite3MemLink(void *pParent, void *pChild);
|
||||
void sqlite3MemUnlink(void *pParent, void *pChild);
|
||||
int sqlite3MallocSize(void*);
|
||||
int sqlite3DbMallocSize(sqlite3*, void*);
|
||||
void *sqlite3ScratchMalloc(int);
|
||||
@@ -2908,7 +2912,6 @@ int sqlite3InvokeBusyHandler(BusyHandler*);
|
||||
int sqlite3FindDb(sqlite3*, Token*);
|
||||
int sqlite3FindDbName(sqlite3 *, const char *);
|
||||
int sqlite3AnalysisLoad(sqlite3*,int iDB);
|
||||
void sqlite3DeleteIndexSamples(Index*);
|
||||
void sqlite3DefaultRowEst(Index*);
|
||||
void sqlite3RegisterLikeFunctions(sqlite3*, int);
|
||||
int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
|
||||
@@ -3118,6 +3121,10 @@ 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.
|
||||
**
|
||||
** sqlite3MemdebugNoType() returns true if none of the bits in its second
|
||||
** argument match the type set by the previous sqlite3MemdebugSetType().
|
||||
**
|
||||
** For example:
|
||||
**
|
||||
** assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
||||
@@ -3136,9 +3143,11 @@ 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 */
|
||||
|
||||
+1
-4
@@ -3805,10 +3805,7 @@ static void whereInfoFree(sqlite3 *db, WhereInfo *pWInfo){
|
||||
}
|
||||
if( pWInfo->a[i].plan.wsFlags & WHERE_TEMP_INDEX ){
|
||||
Index *pIdx = pWInfo->a[i].plan.u.pIdx;
|
||||
if( pIdx ){
|
||||
sqlite3DbFree(db, pIdx->zColAff);
|
||||
sqlite3DbFree(db, pIdx);
|
||||
}
|
||||
sqlite3DbFree(db, pIdx);
|
||||
}
|
||||
}
|
||||
whereClauseClear(pWInfo->pWC);
|
||||
|
||||
Reference in New Issue
Block a user