]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Many small changes to ensure memory is not leaked after malloc() fails. (CVS 2808)
authordanielk1977 <danielk1977@noemail.net>
Fri, 9 Dec 2005 14:25:08 +0000 (14:25 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Fri, 9 Dec 2005 14:25:08 +0000 (14:25 +0000)
FossilOrigin-Name: 601c335463aaabc2e9918e4b9298cff6161be5c4

18 files changed:
manifest
manifest.uuid
src/attach.c
src/os_unix.c
src/prepare.c
src/shell.c
src/sqliteInt.h
src/test1.c
src/trigger.c
src/util.c
src/vacuum.c
src/vdbe.c
src/vdbeapi.c
src/vdbeaux.c
test/malloc3.test
test/malloc4.test
test/select1.test
test/tester.tcl

index 84ff3b8da0ff004a2df3885099c2cb5337031f50..292d56defc78999a09a76d8d05d0eccebb3bba41 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\smore\sstress\stesting\sto\sautovacuum\sin\san\s(unsuccessful)\sattempt\sto\nreproduce\sa\sreported\sproblem.\s\sEven\sthough\sthese\stests\sdid\snot\suncover\nanything\samiss,\sextra\stests\snever\shurt...\s(CVS\s2807)
-D 2005-12-09T02:35:54
+C Many\ssmall\schanges\sto\sensure\smemory\sis\snot\sleaked\safter\smalloc()\sfails.\s(CVS\s2808)
+D 2005-12-09T14:25:08
 F Makefile.in e3c6b3a38d734d41574c04f2fc90d18de2b87102
 F Makefile.linux-gcc aee18d8a05546dcf1888bd4547e442008a49a092
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -32,7 +32,7 @@ F sqlite3.def c413e514217736884254739a105c8c942fdf0c2f
 F sqlite3.pc.in 985b9bf34192a549d7d370e0f0b6b34a4f61369a
 F src/alter.c 7ed4b794c2e3a8ad8c1effe50202eaef42cedc23
 F src/analyze.c ea42005eed52c382fcc7ef66969e7f1858597633
-F src/attach.c 39e678033d0be197dea4e9d5eeac2b2df51d6c9f
+F src/attach.c ee70131f128d31a9c6dcb8824e8471c91b18601a
 F src/auth.c 31e2304bef67f44d635655f44234387ea7d21454
 F src/btree.c aa88194f460becf8fff6196996d6e38f1b37286e
 F src/btree.h 1ed561263ca0e335bc3e81d761c9d5ff8c22f61e
@@ -55,7 +55,7 @@ F src/os.h d5ae3f4c1c7731437b6cddec279b7c06f761c44e
 F src/os_common.h d74a11728ad2444b6b695b94c28c06881f049e49
 F src/os_test.c 49833426101f99aee4bb5f6a44b7c4b2029fda1c
 F src/os_test.h 903c93554c23d88f34f667f1979e4a1cee792af3
-F src/os_unix.c 76189e4cb5e1ad451293ce1aeb348c3b829e7e13
+F src/os_unix.c 01648f7fa16cddb1f8a11ef2a582348801b185a6
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c d962ac2dd0e482847e42b846d46cd044f97d1c32
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
@@ -63,32 +63,32 @@ F src/pager.c 893cb2106261a4f77d84c1fa0d10a083e889b23b
 F src/pager.h e7b41ce8e7b5f629d456708b7ad9a8c8ede37140
 F src/parse.y 87080d89439925de19f16189310d3dbc7f9ab3f6
 F src/pragma.c a6d3a7c76b4008b8197913de4bd2079e00b145db
-F src/prepare.c e93967011379051316728d316755f533a9bb438c
+F src/prepare.c 7e21ab5e2304e76f5f679d0df19dca3b0e3ff4f2
 F src/printf.c 3ea3a17d25d7ac498efc18007c70371a42c968f8
 F src/random.c ff5e9a8cad790e2a51cd4d2e7737dc8540e09d1d
 F src/select.c 0e4d3627fec4a445b45f6cb471f68aab9c97a8b3
-F src/shell.c 3596c1e559b82663057940d19ba533ad421c7dd3
+F src/shell.c 4872acee1d2a826c73c914961e469e563204b7f9
 F src/sqlite.h.in 8e648e1f386e4509f2f96c09ded7c07b0df0c9a2
-F src/sqliteInt.h bbc310a83a32476aa960055a166af4a0ef503a79
+F src/sqliteInt.h 4247cf9ae4280ae33e5bbc7b45358940ff34b9ce
 F src/table.c 486dcfce532685b53b5a2b5da8bba0ded6fb2316
 F src/tclsqlite.c 328060916c24d328cfab1622c9a0e7ad57c2da8c
-F src/test1.c 0b4bf8ab9afb37f34a83c46f81de54adf519b23d
+F src/test1.c 350129da26943dd62067e75ad71ba85a1a648230
 F src/test2.c 36390cdfc70c08e5ee0b466d0654a117f398bbff
 F src/test3.c f4e6a16a602091696619a1171bda25c0e3df49f7
 F src/test4.c a8fd681e139e1c61f22a77d07fc3a99cb28fff3f
 F src/test5.c 64f08b2a50ef371a1bd68ff206829e7b1b9997f5
 F src/test6.c cb811391ec0b7c75f29e545d4820a9cf19f3637e
 F src/tokenize.c a189d7466524076220f41a21baed05e1639a82f4
-F src/trigger.c 388c13a2f07584decdb88413a63017391f9a7f7a
+F src/trigger.c dfc1f8ee8e71c6482fb269695778c8b51f821c98
 F src/update.c ec8e540617b116725b5a55c8d6b4db8bc67fdd7d
 F src/utf.c bda5eb85039ef16f2d17004c1e18c96e1ab0a80c
-F src/util.c 88ebafb5708f04002e58a373386e3c8c31bfd858
-F src/vacuum.c 4d64ce98a4cb130002cbf8aba018b2567773e6b1
-F src/vdbe.c 7f4a2affee140b7b57952ab2c081a9c22b950638
+F src/util.c c77727365ce60b0e0087550ba88653ad0fcf24ea
+F src/vacuum.c 3cd457f91b05338269f2ea21c4c58dce93f2eef2
+F src/vdbe.c 4f78f1fe3b5e7675ee54a2cc18f14dea4ff5fd94
 F src/vdbe.h 8729a4ee16ff9aeab2af9667df3cf300ff978e13
 F src/vdbeInt.h 0055c37eccbf3a189fd893a90f8eb6a5fa60c871
-F src/vdbeapi.c b80b3a1e8bf7866d96ca76d91bba979155bace57
-F src/vdbeaux.c 90143aaffbe232a700fd05e4b2e7428d512b605b
+F src/vdbeapi.c b270b680cbc5d20b5a1abfdb08339667985df94e
+F src/vdbeaux.c b0a4a4b19b42f18d86c0026a9cc548478dc6a7a7
 F src/vdbefifo.c 9efb94c8c3f4c979ebd0028219483f88e57584f5
 F src/vdbemem.c 1c70555d615c6001c4490dedb6a62cb2884b92ff
 F src/where.c 269569f380ddc018518f67765fe2f0d3c8760e28
@@ -180,8 +180,8 @@ F test/lock3.test 615111293cf32aa2ed16d01c6611737651c96fb9
 F test/main.test b12f01d49a5c805a33fa6c0ef168691f63056e79
 F test/malloc.test a5ed721cf7d1b12602ede4f98c11b65ab1582cc0
 F test/malloc2.test e6e321db96d6c94cb18bf82ad7215070c41e624e
-F test/malloc3.test 0d60a9d19196581e9279c2d040bebd8162ab495b
-F test/malloc4.test 593ae91bf69ab5abc68f7652e679e5d207e23166
+F test/malloc3.test 56372aafe58cc24a31bad4b24ff3f77cf1cc30ab
+F test/malloc4.test 2e29d155eb4b4808019ef47eeedfcbe9e09e0f05
 F test/manydb.test d81debbf5871242e3b5df1d3bb5e14c50431b6f8
 F test/memdb.test 1860e060be810bf0775bc57408a5b7c4954bcaea
 F test/memleak.test df2b2b96e77f8ba159a332299535b1e5f18e49ac
@@ -208,7 +208,7 @@ F test/rollback.test 94cd981ee3a627d9f6466f69dcf1f7dbfe695d7a
 F test/rowid.test 040a3bef06f970c45f5fcd14b2355f7f4d62f0cf
 F test/safety.test 907b64fee719554a3622853812af3886fddbbb4f
 F test/schema.test 8a2ae440fb15f5798a68059e8746402f3137be46
-F test/select1.test 480233d4f5a81d7d59a55e40d05084d97e57ecdf
+F test/select1.test 20275797efe032a56e97687aa06ba65d7a0b9b2b
 F test/select2.test f3c2678c3a9f3cf08ec4988a3845bda64be6d9e3
 F test/select3.test 8fece41cd8f2955131b3f973a7123bec60b6e65e
 F test/select4.test c239f516aa31f42f2ef7c6d7cd01105f08f934ca
@@ -223,7 +223,7 @@ F test/table.test ec0e6c2186bb8f6824f470caa118524dfd8fe057
 F test/tableapi.test 6a66d58b37d46dc0f2b3c7d4bd2617d209399bd1
 F test/tclsqlite.test 2da3e4b3a79b13c1511c9d0cd995e08f8362e782
 F test/temptable.test 7927261befdbc7b0a7ffebb85ecc70a74fa7b15b
-F test/tester.tcl 1dbcac19ed71f68f24b7f5c6c134568d551092ba
+F test/tester.tcl 6effe753bed26313eaab11a382436a99e602fa54
 F test/thread1.test 776c9e459b75ba905193b351926ac4019b049f35
 F test/threadtest1.c 6029d9c5567db28e6dc908a0c63099c3ba6c383b
 F test/threadtest2.c 97a830d53c24c42290501fdfba4a6e5bdd34748b
@@ -327,7 +327,7 @@ F www/tclsqlite.tcl ddcf912ea48695603c8ed7efb29f0812ef8d1b49
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl a99cf5f6d8bd4d5537584a2b342f0fb9fa601d8b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P a7c9e8989c752f6b1148e7cc7bf59bbd8b402e87
-R 0c1000cc2ea9df5aafb3433db2962046
-U drh
-Z 44f91e86d2b44a0b3e1c8417c9a4606c
+P d8a8933ff30b83c0483be214403d92c4dfa9a4af
+R 208b8157372f7c8db3da02b392e698f6
+U danielk1977
+Z 1ece2b1e923caae0853519e926a399e9
index 56042eff3f7bb93c64ca6c8b2ec0fe76acccb242..8b1e95e02836bef438b8159187bd9d861bd3eb98 100644 (file)
@@ -1 +1 @@
-d8a8933ff30b83c0483be214403d92c4dfa9a4af
\ No newline at end of file
+601c335463aaabc2e9918e4b9298cff6161be5c4
\ No newline at end of file
index 0088417502243cd185b8737058bc46a39662ba8f..8003d7cc99edb9f63868560458a9a5a61915f277 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file contains code used to implement the ATTACH and DETACH commands.
 **
-** $Id: attach.c,v 1.36 2005/12/06 17:19:11 danielk1977 Exp $
+** $Id: attach.c,v 1.37 2005/12/09 14:25:08 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -261,12 +261,17 @@ static void codeAttach(
   sqlite3* db = pParse->db;
 
 #ifndef SQLITE_OMIT_AUTHORIZATION
-  char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
-  if( !zAuthArg ){
-    goto attach_end;
-  }
-  if( sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0)!=SQLITE_OK ){
-    goto attach_end;
+  assert( sqlite3Tsd()->mallocFailed || pAuthArg );
+  if( pAuthArg ){
+    char *zAuthArg = sqlite3NameFromToken(&pAuthArg->span);
+    if( !zAuthArg ){
+      goto attach_end;
+    }
+    rc = sqlite3AuthCheck(pParse, type, zAuthArg, 0, 0);
+    sqliteFree(zAuthArg);
+    if(rc!=SQLITE_OK ){
+      goto attach_end;
+    }
   }
 #endif /* SQLITE_OMIT_AUTHORIZATION */
 
@@ -304,7 +309,6 @@ attach_end:
   sqlite3ExprDelete(pFilename);
   sqlite3ExprDelete(pDbname);
   sqlite3ExprDelete(pKey);
-  sqliteFree(zAuthArg);
 }
 
 /*
index 3beaf3b910f270f7f3e4ddae3ae02768b8d1a9ec..c07a80cb91912d0965629178a70dcd22b45827e7 100644 (file)
@@ -633,6 +633,7 @@ static int unixOpenReadWrite(
 static int unixOpenExclusive(const char *zFilename, OsFile **pId, int delFlag){
   int rc;
   unixFile f;
+  int fd;
 
   assert( 0==*pId );
   if( access(zFilename, 0)==0 ){
@@ -1485,6 +1486,8 @@ static int allocateUnixFile(unixFile *pInit, OsFile **pId){
   pNew = sqliteMalloc( sizeof(unixFile) );
   if( pNew==0 ){
     close(pInit->h);
+    releaseLockInfo(pInit->pLock);
+    releaseOpenCnt(pInit->pOpen);
     *pId = 0;
     return SQLITE_NOMEM;
   }else{
index 4a9b2b1cef9ec47de50bc8fd99cc920bb3d11753..9a1d4f3382d3cd9b97a3c0303da4a8caae7228bb 100644 (file)
@@ -13,7 +13,7 @@
 ** interface, and routines that contribute to loading the database schema
 ** from disk.
 **
-** $Id: prepare.c,v 1.5 2005/12/06 12:52:59 danielk1977 Exp $
+** $Id: prepare.c,v 1.6 2005/12/09 14:25:08 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -503,7 +503,7 @@ prepare_out:
     sqlite3Error(db, rc, 0);
   }
 
-  sqlite3ClearMallocFailed();
+  sqlite3MallocClearFailed();
   return rc;
 }
 
index 3296f44bfe64c99fe38837e7dbcb88cea9f6e857..b06f913091abe5b6c3da9ac718ff8a42acc857a8 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains code to implement the "sqlite" command line
 ** utility for accessing SQLite databases.
 **
-** $Id: shell.c,v 1.128 2005/09/11 02:03:04 drh Exp $
+** $Id: shell.c,v 1.129 2005/12/09 14:25:08 danielk1977 Exp $
 */
 #include <stdlib.h>
 #include <string.h>
@@ -81,7 +81,7 @@ static char continuePrompt[20]; /* Continuation prompt. default: "   ...> " */
 /*
 ** Determines if a string is a number of not.
 */
-static int isNumber(const unsigned char *z, int *realnum){
+static int isNumber(const char *z, int *realnum){
   if( *z=='-' || *z=='+' ) z++;
   if( !isdigit(*z) ){
     return 0;
@@ -690,8 +690,9 @@ static int dump_callback(void *pArg, int nArg, char **azArg, char **azCol){
     zSelect = appendText(zSelect, " || ' VALUES(' || ", 0);
     rc = sqlite3_step(pTableInfo);
     while( rc==SQLITE_ROW ){
+      const char *zText = (const char *)sqlite3_column_text(pTableInfo, 1);
       zSelect = appendText(zSelect, "quote(", 0);
-      zSelect = appendText(zSelect, sqlite3_column_text(pTableInfo, 1), '"');
+      zSelect = appendText(zSelect, zText, '"');
       rc = sqlite3_step(pTableInfo);
       if( rc==SQLITE_ROW ){
         zSelect = appendText(zSelect, ") || ', ' || ", 0);
index 6c9ae45115e397dc7b6e3c4b972cd7105fe20516..f9c853a0344c8126d6050968155beed18e75c075 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.430 2005/12/06 17:19:11 danielk1977 Exp $
+** @(#) $Id: sqliteInt.h,v 1.431 2005/12/09 14:25:08 danielk1977 Exp $
 */
 #ifndef _SQLITEINT_H_
 #define _SQLITEINT_H_
@@ -233,47 +233,46 @@ struct BusyHandler {
 */
 #define Addr(X)  ((uptr)X)
 
-/*
-** If memory allocation problems are found, recompile with
-**
-**      -DSQLITE_DEBUG=1
-**
-** to enable some sanity checking on malloc() and free().  To
-** check for memory leaks, recompile with
-**
-**      -DSQLITE_DEBUG=2
-**
-** and a line of text will be written to standard error for
-** each malloc() and free().  This output can be analyzed
-** by an AWK script to determine if there are any leaks.
-*/
 #ifdef SQLITE_MEMDEBUG
-# define sqliteMalloc(X)    sqlite3Malloc_(X,1,__FILE__,__LINE__)
-# define sqliteMallocRaw(X) sqlite3Malloc_(X,0,__FILE__,__LINE__)
-# define sqliteFree(X)      sqlite3Free_(X,__FILE__,__LINE__)
-# define sqliteRealloc(X,Y) sqlite3Realloc_(X,Y,__FILE__,__LINE__)
-# define sqliteStrDup(X)    sqlite3StrDup_(X,__FILE__,__LINE__)
-# define sqliteStrNDup(X,Y) sqlite3StrNDup_(X,Y,__FILE__,__LINE__)
-#else
-# define sqliteFree          sqlite3FreeX
-# define sqliteMalloc        sqlite3Malloc
-# define sqliteMallocRaw     sqlite3MallocRaw
-# define sqliteRealloc       sqlite3Realloc
-# define sqliteStrDup        sqlite3StrDup
-# define sqliteStrNDup       sqlite3StrNDup
-#endif
-
 /*
 ** The following global variables are used for testing and debugging
 ** only.  They only work if SQLITE_DEBUG is defined.
 */
-#ifdef SQLITE_MEMDEBUG
 extern int sqlite3_nMalloc;      /* Number of sqliteMalloc() calls */
 extern int sqlite3_nFree;        /* Number of sqliteFree() calls */
 extern int sqlite3_iMallocFail;  /* Fail sqliteMalloc() after this many calls */
 extern int sqlite3_iMallocReset; /* Set iMallocFail to this when it reaches 0 */
+#define ENTER_MALLOC (\
+  sqlite3Tsd()->zFile = __FILE__, sqlite3Tsd()->iLine = __LINE__ \
+)
+#else
+#define ENTER_MALLOC 0
 #endif
 
+#define sqliteFree(x)          sqlite3FreeX(x)
+#define sqliteMalloc(x)        (ENTER_MALLOC, sqlite3Malloc(x))
+#define sqliteMallocRaw(x)     (ENTER_MALLOC, sqlite3MallocRaw(x))
+#define sqliteRealloc(x,y)     (ENTER_MALLOC, sqlite3Realloc(x,y))
+#define sqliteStrDup(x)        (ENTER_MALLOC, sqlite3StrDup(x))
+#define sqliteStrNDup(x,y)     (ENTER_MALLOC, sqlite3StrNDup(x,y))
+
+/*
+** An instance of this structure is allocated for each thread that uses SQLite.
+*/
+typedef struct SqliteTsd SqliteTsd;
+struct SqliteTsd {
+  int mallocFailed;               /* True after a malloc() has failed */
+#ifndef NDEBUG
+  int mallocAllowed;              /* assert() in sqlite3Malloc() if not set */
+#endif
+#ifdef SQLITE_MEMDEBUG
+  int isFail;              /* True if all malloc() calls should fail */
+  const char *zFile;       /* Filename to associate debugging info with */
+  int iLine;               /* Line number to associate debugging info with */
+  void *pFirst;            /* Pointer to linked list of allocations */
+#endif
+};
+
 /*
 ** Name of the master database table.  The master database table
 ** is a special table that holds the names and attributes of all
@@ -1379,14 +1378,6 @@ typedef struct {
   char **pzErrMsg;    /* Error message stored here */
 } InitData;
 
-/*
-** An instance of this structure is allocated for each thread that uses SQLite.
-*/
-typedef struct SqliteTsd SqliteTsd;
-struct SqliteTsd {
-  int mallocFailed;               /* True after a malloc() has failed */
-};
-
 /*
  * This global flag is set for performance testing of triggers. When it is set
  * SQLite will perform the overhead of building new and old trigger references 
@@ -1417,26 +1408,18 @@ int sqlite3IsNumber(const char*, int*, u8);
 int sqlite3Compare(const char *, const char *);
 int sqlite3SortCompare(const char *, const char *);
 void sqlite3RealToSortable(double r, char *);
-#ifdef SQLITE_MEMDEBUG
-  void *sqlite3Malloc_(int,int,char*,int);
-  void sqlite3Free_(void*,char*,int);
-  void *sqlite3Realloc_(void*,int,char*,int);
-  char *sqlite3StrDup_(const char*,char*,int);
-  char *sqlite3StrNDup_(const char*, int,char*,int);
-  void sqlite3CheckMemory(void*,int);
-#else
-  void *sqlite3Malloc(int);
-  void *sqlite3MallocRaw(int);
-  void sqlite3Free(void*);
-  void *sqlite3Realloc(void*,int);
-  char *sqlite3StrDup(const char*);
-  char *sqlite3StrNDup(const char*, int);
+
+void *sqlite3Malloc(int);
+void *sqlite3MallocRaw(int);
+void sqlite3Free(void*);
+void *sqlite3Realloc(void*,int);
+char *sqlite3StrDup(const char*);
+char *sqlite3StrNDup(const char*, int);
 # define sqlite3CheckMemory(a,b)
-# define sqlite3MallocX sqlite3Malloc
-#endif
 void sqlite3ReallocOrFree(void**,int);
 void sqlite3FreeX(void*);
 void *sqlite3MallocX(int);
+
 char *sqlite3MPrintf(const char*, ...);
 char *sqlite3VMPrintf(const char*, va_list);
 void sqlite3DebugPrintf(const char*, ...);
@@ -1677,9 +1660,17 @@ void sqlite3DefaultRowEst(Index*);
 void sqlite3RegisterLikeFunctions(sqlite3*, int);
 int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
 SqliteTsd *sqlite3Tsd();
-void sqlite3ClearMallocFailed();
 void sqlite3AttachFunctions(sqlite3 *);
 
+void sqlite3MallocClearFailed();
+#ifdef NDEBUG
+  #define sqlite3MallocDisallow()
+  #define sqlite3MallocAllow()
+#else
+  void sqlite3MallocDisallow();
+  void sqlite3MallocAllow();
+#endif
+
 #ifdef SQLITE_SSE
 #include "sseInt.h"
 #endif
index d29346410f40de6085a4ad0682678e306ed930e2..d4e15ac2b6e1dedd7be07eb4ee086c999d4abae9 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test1.c,v 1.171 2005/12/06 12:53:00 danielk1977 Exp $
+** $Id: test1.c,v 1.172 2005/12/09 14:25:08 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "tcl.h"
@@ -832,6 +832,15 @@ static int sqlite_malloc_stat(
   Tcl_AppendResult(interp, zBuf, 0);
   return TCL_OK;
 }
+static int sqlite_malloc_outstanding(
+  void *NotUsed,
+  Tcl_Interp *interp,    /* The TCL interpreter that invoked this command */
+  int argc,              /* Number of arguments */
+  char **argv            /* Text of each argument */
+){
+  extern int sqlite3OutstandingMallocs(Tcl_Interp *interp);
+  return sqlite3OutstandingMallocs(interp);
+}
 #endif
 
 /*
@@ -3082,6 +3091,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
 #ifdef SQLITE_MEMDEBUG
      { "sqlite_malloc_fail",            (Tcl_CmdProc*)sqlite_malloc_fail    },
      { "sqlite_malloc_stat",            (Tcl_CmdProc*)sqlite_malloc_stat    },
+     { "sqlite_malloc_outstanding",   (Tcl_CmdProc*)sqlite_malloc_outstanding},
 #endif
      { "sqlite_bind",                   (Tcl_CmdProc*)test_bind             },
      { "breakpoint",                    (Tcl_CmdProc*)test_breakpoint       },
@@ -3176,6 +3186,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
   extern int sqlite3_sync_count, sqlite3_fullsync_count;
   extern int sqlite3_opentemp_count;
   extern int sqlite3_memUsed;
+  extern int sqlite3_malloc_id;
   extern int sqlite3_memMax;
   extern int sqlite3_like_count;
 #if OS_WIN
@@ -3210,6 +3221,8 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
       (char*)&sqlite3_current_time, TCL_LINK_INT);
   Tcl_LinkVar(interp, "sqlite_os_trace",
       (char*)&sqlite3_os_trace, TCL_LINK_INT);
+  Tcl_LinkVar(interp, "sqlite_malloc_id",
+      (char*)&sqlite3_malloc_id, TCL_LINK_STRING);
 #if OS_WIN
   Tcl_LinkVar(interp, "sqlite_os_type",
       (char*)&sqlite3_os_type, TCL_LINK_INT);
index cc5fc080e77b567fd2bf9f80d30264a95fdfea3f..fc4f172ba9d14e95ec7e019c0fa139e939a89dd6 100644 (file)
@@ -199,7 +199,7 @@ void sqlite3FinishTrigger(
 
   pTrig = pParse->pNewTrigger;
   pParse->pNewTrigger = 0;
-  if( pParse->nErr || pTrig==0 ) goto triggerfinish_cleanup;
+  if( pParse->nErr || !pTrig ) goto triggerfinish_cleanup;
   pTrig->step_list = pStepList;
   while( pStepList ){
     pStepList->pTrig = pTrig;
@@ -312,7 +312,10 @@ static void sqlitePersistTriggerStep(TriggerStep *p){
 */
 TriggerStep *sqlite3TriggerSelectStep(Select *pSelect){
   TriggerStep *pTriggerStep = sqliteMalloc(sizeof(TriggerStep));
-  if( pTriggerStep==0 ) return 0;
+  if( pTriggerStep==0 ) {
+    sqlite3SelectDelete(pSelect);
+    return 0;
+  }
 
   pTriggerStep->op = TK_SELECT;
   pTriggerStep->pSelect = pSelect;
index 88924fbebbe945f75b60c031c5a06177010bfae2..ac8677a79b1b9ecf30e24052b0f28791f50d3375 100644 (file)
 ** This file contains functions for allocating memory, comparing
 ** strings, and stuff like that.
 **
-** $Id: util.c,v 1.149 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: util.c,v 1.150 2005/12/09 14:25:09 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include <stdarg.h>
 #include <ctype.h>
 
-#if SQLITE_MEMDEBUG>2 && defined(__GLIBC__)
-#include <execinfo.h>
-void print_stack_trace(){
-  void *bt[30];
-  int i;
-  int n = backtrace(bt, 30);
+/*
+** MALLOC WRAPPER ARCHITECTURE
+**
+** The sqlite code accesses dynamic memory allocation/deallocation by invoking
+** the following four APIs (which may be implemented as macros).
+**
+**     sqlite3Malloc()
+**     sqlite3MallocRaw()
+**     sqlite3Realloc()
+**     sqlite3ReallocOrFree()
+**     sqlite3Free()
+**
+** The function sqlite3FreeX performs the same task as sqlite3Free and is
+** guaranteed to be a real function.
+**
+** The above APIs are implemented in terms of the functions provided at the Os
+** level (not in this file). The Os level interface is never accessed directly
+** by code outside of this file.
+**
+**     sqlite3OsMalloc()
+**     sqlite3OsRealloc()
+**     sqlite3OsFree()
+**     sqlite3OsAllocationSize()
+**
+** Functions sqlite3MallocRaw() and sqlite3Realloc() may invoke 
+** sqlite3_release_memory() if a call to sqlite3OsMalloc() or
+** sqlite3OsRealloc() fails. Function sqlite3Malloc() usually invokes
+** sqlite3MallocRaw().
+**
+** MALLOC TEST WRAPPER ARCHITECTURE
+**
+** The test wrapper provides extra test facilities to ensure the library 
+** does not leak memory and handles the failure of the underlying (Os level)
+** allocation system correctly. It is only present if the library is 
+** compiled with the SQLITE_MEMDEBUG macro set.
+**
+**     * Guardposts to detect overwrites.
+**     * Ability to cause a specific Malloc() or Realloc() to fail.
+**     * Audit outstanding memory allocations (i.e check for leaks).
+*/
 
-  fprintf(stderr, "STACK: ");
-  for(i=0; i<n;i++){
-    fprintf(stderr, "%p ", bt[i]);
+/*
+** sqlite3OsMalloc
+** sqlite3OsRealloc
+** sqlite3OsOsFree
+** sqlite3OsAllocationSize
+**
+** Implementation of the os level dynamic memory allocation interface in terms
+** of the standard malloc(), realloc() and free() found in many operating
+** systems. No rocket science here.
+*/
+void *sqlite3OsMalloc(int n){
+  char *p = (char *)malloc(n+8);
+  assert(n>0);
+  assert(sizeof(int)<=8);
+  if( p ){
+    *(int *)p = n;
+  }
+  return (void *)(p + 8);
+}
+void *sqlite3OsRealloc(void *p, int n){
+  char *p2 = ((char *)p - 8);
+  assert(n>0);
+  p2 = realloc(p2, n+8);
+  if( p2 ){
+    *(int *)p2 = n;
   }
-  fprintf(stderr, "\n");
+  return (void *)((char *)p2 + 8);
 }
+void sqlite3OsFree(void *p){
+  assert(p);
+  free((void *)((char *)p - 8));
+}
+int sqlite3OsAllocationSize(void *p){
+  return *(int *)((char *)p - 8);
+}
+
+/*
+** TODO!
+*/
+#define sqlite3_release_memory(x) 0
+
+#ifdef SQLITE_MEMDEBUG
+/*--------------------------------------------------------------------------
+** Begin code for memory allocation system test layer.
+**
+** Memory debugging is turned on by defining the SQLITE_MEMDEBUG macro.
+*/
+
+/* Figure out whether or not to store backtrace() information for each malloc.
+** The backtrace() function is only used if SQLITE_MEMDEBUG is set to 2 or 
+** greater and glibc is in use. If we don't want to use backtrace(), then just
+** define it as an empty macro and set the amount of space reserved to 0.
+*/
+#if defined(__GLIBC__) && SQLITE_MEMDEBUG>1
+  extern int backtrace(void **, int);
+  #define TESTALLOC_STACKSIZE 128
+  #define TESTALLOC_STACKFRAMES ((TESTALLOC_STACKSIZE-8)/sizeof(void*))
 #else
-#define print_stack_trace()
+  #define backtrace(x, y) 0
+  #define TESTALLOC_STACKSIZE 0
+  #define TESTALLOC_STACKFRAMES 0
 #endif
 
-#if 0
 /*
-** If malloc() ever fails, this global variable gets set to 1.
-** This causes the library to abort and never again function.
+** Number of 32-bit guard words.  This should probably be a multiple of
+** 2 since on 64-bit machines we want the value returned by sqliteMalloc()
+** to be 8-byte aligned.
 */
-int sqlite3Tsd()->mallocFailed = 0;
-#endif
+#define TESTALLOC_NGUARD 2
 
 /*
-** If SQLITE_MEMDEBUG is defined, then use versions of malloc() and
-** free() that track memory usage and check for buffer overruns.
+** Size reserved for storing file-name along with each malloc()ed blob.
 */
-#ifdef SQLITE_MEMDEBUG
+#define TESTALLOC_FILESIZE 64
+
+/*
+** Size reserved for storing the user string.
+*/
+#define TESTALLOC_USERSIZE 64
+const char *sqlite3_malloc_id = 0;
+
+/*
+** Blocks used by the test layer have the following format:
+**
+**        <sizeof(void *) pNext pointer>
+**        <sizeof(void *) pPrev pointer>
+**        <TESTALLOC_NGUARD 32-bit guard words>
+**            <The application level allocation>
+**        <TESTALLOC_NGUARD 32-bit guard words>
+**        <32-bit line number>
+**        <TESTALLOC_FILESIZE bytes containing null-terminated file name>
+**        <TESTALLOC_STACKSIZE bytes of backtrace() output>
+*/ 
+
+#define TESTALLOC_OFFSET_GUARD1(p)    (sizeof(void *) * 2)
+#define TESTALLOC_OFFSET_DATA(p) ( \
+  TESTALLOC_OFFSET_GUARD1(p) + sizeof(u32) * TESTALLOC_NGUARD \
+)
+#define TESTALLOC_OFFSET_GUARD2(p) ( \
+  TESTALLOC_OFFSET_DATA(p) + sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD \
+)
+#define TESTALLOC_OFFSET_LINENUMBER(p) ( \
+  TESTALLOC_OFFSET_GUARD2(p) + sizeof(u32) * TESTALLOC_NGUARD \
+)
+#define TESTALLOC_OFFSET_FILENAME(p) ( \
+  TESTALLOC_OFFSET_LINENUMBER(p) + sizeof(u32) \
+)
+#define TESTALLOC_OFFSET_USER(p) ( \
+  TESTALLOC_OFFSET_FILENAME(p) + TESTALLOC_FILESIZE \
+)
+#define TESTALLOC_OFFSET_STACK(p) ( \
+  TESTALLOC_OFFSET_USER(p) + TESTALLOC_USERSIZE + 8 - \
+  (TESTALLOC_OFFSET_USER(p) % 8) \
+)
+
+#define TESTALLOC_OVERHEAD ( \
+  sizeof(void *)*2 +                   /* pPrev and pNext pointers */   \
+  TESTALLOC_NGUARD*sizeof(u32)*2 +              /* Guard words */       \
+  sizeof(u32) + TESTALLOC_FILESIZE +   /* File and line number */       \
+  TESTALLOC_USERSIZE +                 /* User string */                \
+  TESTALLOC_STACKSIZE                  /* backtrace() stack */          \
+)
 
 /*
 ** For keeping track of the number of mallocs and frees.   This
@@ -60,35 +193,25 @@ int sqlite3Tsd()->mallocFailed = 0;
 */
 int sqlite3_nMalloc;         /* Number of sqliteMalloc() calls */
 int sqlite3_nFree;           /* Number of sqliteFree() calls */
-int sqlite3_memUsed;         /* Total memory obtained from malloc */
-int sqlite3_memMax;          /* Mem usage high-water mark */
+int sqlite3_memUsed;         /* TODO Total memory obtained from malloc */
+int sqlite3_memMax;          /* TODO Mem usage high-water mark */
 int sqlite3_iMallocFail;     /* Fail sqliteMalloc() after this many calls */
 int sqlite3_iMallocReset = -1; /* When iMallocFail reaches 0, set to this */
-#if SQLITE_MEMDEBUG>1
-static int memcnt = 0;
-#endif
-
-/*
-** Number of 32-bit guard words.  This should probably be a multiple of
-** 2 since on 64-bit machines we want the value returned by sqliteMalloc()
-** to be 8-byte aligned.
-*/
-#define N_GUARD 2
 
 /*
 ** Check for a simulated memory allocation failure.  Return true if
 ** the failure should be simulated.  Return false to proceed as normal.
 */
-static int simulatedMallocFailure(int n, char *zFile, int line){
+static int failMalloc(){
+  SqliteTsd *pTsd = sqlite3Tsd();
+  if( pTsd->isFail ){
+    return 1;
+  }
   if( sqlite3_iMallocFail>=0 ){
     sqlite3_iMallocFail--;
     if( sqlite3_iMallocFail==0 ){
-      sqlite3Tsd()->mallocFailed++;
-#if SQLITE_MEMDEBUG>1
-      fprintf(stderr,"**** failed to allocate %d bytes at %s:%d\n",
-              n, zFile,line);
-#endif
       sqlite3_iMallocFail = sqlite3_iMallocReset;
+      pTsd->isFail = 1;
       return 1;
     }
   }
@@ -96,303 +219,368 @@ static int simulatedMallocFailure(int n, char *zFile, int line){
 }
 
 /*
-** Allocate new memory and set it to zero.  Return NULL if
-** no memory is available.
+** The argument is a pointer returned by sqlite3OsMalloc() or Realloc().
+** assert() that the first and last (TESTALLOC_NGUARD*4) bytes are set to the
+** values set by the applyGuards() function.
 */
-void *sqlite3Malloc_(int n, int bZero, char *zFile, int line){
-  void *p;
-  int *pi;
-  int i, k;
-
-  /* Any malloc() calls between a malloc() failure and clearing the
-  ** mallocFailed flag (done before returning control to the user)
-  ** automatically fail. Although this restriction may have to be relaxed in
-  ** the future, for now it makes the system easier to test.
-  **
-  ** TODO: This will eventually be done as part of the same wrapper that may
-  ** call sqlite3_release_memory(). Above the sqlite3OsMalloc() level.
-  */
-  if( sqlite3Tsd()->mallocFailed ){
-    return 0;
-  }
+static void checkGuards(u32 *p)
+{
+  int i;
+  char *zAlloc = (char *)p;
+  char *z;
 
-  if( n==0 ){
-    return 0;
+  /* First set of guard words */
+  z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
+  for(i=0; i<TESTALLOC_NGUARD; i++){
+    assert(((u32 *)z)[i]==0xdead1122);
   }
-  if( simulatedMallocFailure(n, zFile, line) ){
-    return 0;
-  }
-  sqlite3_memUsed += n;
-  if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed;
-  k = (n+sizeof(int)-1)/sizeof(int);
-  pi = malloc( (N_GUARD*2+1+k)*sizeof(int));
-  if( pi==0 ){
-    if( n>0 ) sqlite3Tsd()->mallocFailed++;
-    return 0;
+
+  /* Second set of guard words */
+  z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
+  for(i=0; i<TESTALLOC_NGUARD; i++){
+    u32 guard = 0;
+    memcpy(&guard, &z[i*sizeof(u32)], sizeof(u32));
+    assert(guard==0xdead3344);
   }
-  sqlite3_nMalloc++;
-  for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
-  pi[N_GUARD] = n;
-  for(i=0; i<N_GUARD; i++) pi[k+1+N_GUARD+i] = 0xdead3344;
-  p = &pi[N_GUARD+1];
-  memset(p, bZero==0, n);
-#if SQLITE_MEMDEBUG>1
-  print_stack_trace();
-  fprintf(stderr,"%06d malloc %d bytes at 0x%x from %s:%d\n",
-      ++memcnt, n, (int)p, zFile,line);
-#endif
-  return p;
 }
 
 /*
-** This version of malloc is always a real function, never a macro
+** The argument is a pointer returned by sqlite3OsMalloc() or Realloc(). The
+** first and last (TESTALLOC_NGUARD*4) bytes are set to known values for use as 
+** guard-posts.
 */
-void *sqlite3MallocX(int n){
-  return sqlite3Malloc_(n, 0, __FILE__, __LINE__);
+static void applyGuards(u32 *p)
+{
+  int i;
+  char *z;
+  char *zAlloc = (char *)p;
+
+  /* First set of guard words */
+  z = &zAlloc[TESTALLOC_OFFSET_GUARD1(p)];
+  for(i=0; i<TESTALLOC_NGUARD; i++){
+    ((u32 *)z)[i] = 0xdead1122;
+  }
+
+  /* Second set of guard words */
+  z = &zAlloc[TESTALLOC_OFFSET_GUARD2(p)];
+  for(i=0; i<TESTALLOC_NGUARD; i++){
+    static const int guard = 0xdead3344;
+    memcpy(&z[i*sizeof(u32)], &guard, sizeof(u32));
+  }
+
+  /* Line number */
+  z = &((char *)z)[TESTALLOC_NGUARD*sizeof(u32)];             /* Guard words */
+  z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
+  memcpy(z, &sqlite3Tsd()->iLine, sizeof(u32));
+
+  /* File name */
+  z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
+  strncpy(z, sqlite3Tsd()->zFile, TESTALLOC_FILESIZE);
+  z[TESTALLOC_FILESIZE - 1] = '\0';
+
+  /* User string */
+  z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
+  z[0] = 0;
+  if( sqlite3_malloc_id ){
+    strncpy(z, sqlite3_malloc_id, TESTALLOC_USERSIZE);
+    z[TESTALLOC_USERSIZE-1] = 0;
+  }
+
+  /* backtrace() stack */
+  z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
+  backtrace((void **)z, TESTALLOC_STACKFRAMES);
+
+  /* Sanity check to make sure checkGuards() is working */
+  checkGuards(p);
+}
+
+static void *getOsPointer(void *p)
+{
+  char *z = (char *)p;
+  return (void *)(&z[-1 * TESTALLOC_OFFSET_DATA(p)]);
 }
 
 /*
-** Check to see if the given pointer was obtained from sqliteMalloc()
-** and is able to hold at least N bytes.  Raise an exception if this
-** is not the case.
-**
-** This routine is used for testing purposes only.
+** The argument points to an Os level allocation. Link it into the threads list
+** of allocations.
 */
-void sqlite3CheckMemory(void *p, int N){
-  int *pi = p;
-  int n, i, k;
-  pi -= N_GUARD+1;
-  for(i=0; i<N_GUARD; i++){
-    assert( pi[i]==0xdead1122 );
-  }
-  n = pi[N_GUARD];
-  assert( N>=0 && N<n );
-  k = (n+sizeof(int)-1)/sizeof(int);
-  for(i=0; i<N_GUARD; i++){
-    assert( pi[k+N_GUARD+1+i]==0xdead3344 );
+static void linkAlloc(void *p){
+  SqliteTsd *pTsd = sqlite3Tsd();
+  void **pp = (void **)p;
+  pp[0] = 0;
+  pp[1] = pTsd->pFirst;
+  if( pTsd->pFirst ){
+    ((void **)pTsd->pFirst)[0] = p;
   }
+  pTsd->pFirst = p;
 }
 
 /*
-** Free memory previously obtained from sqliteMalloc()
+** The argument points to an Os level allocation. Unlinke it from the threads
+** list of allocations.
 */
-void sqlite3Free_(void *p, char *zFile, int line){
-  if( p ){
-    int *pi, i, k, n;
-    pi = p;
-    pi -= N_GUARD+1;
-    sqlite3_nFree++;
-    for(i=0; i<N_GUARD; i++){
-      if( pi[i]!=0xdead1122 ){
-        fprintf(stderr,"Low-end memory corruption at 0x%x\n", (int)p);
-        return;
-      }
+static void unlinkAlloc(void *p)
+{
+  SqliteTsd *pTsd = sqlite3Tsd();
+  void **pp = (void **)p;
+  if( p==pTsd->pFirst ){
+    assert(!pp[0]);
+    assert(!pp[1] || ((void **)(pp[1]))[0]==p);
+    pTsd->pFirst = pp[1];
+    if( pTsd->pFirst ){
+      ((void **)pTsd->pFirst)[0] = 0;
     }
-    n = pi[N_GUARD];
-    sqlite3_memUsed -= n;
-    k = (n+sizeof(int)-1)/sizeof(int);
-    for(i=0; i<N_GUARD; i++){
-      if( pi[k+N_GUARD+1+i]!=0xdead3344 ){
-        fprintf(stderr,"High-end memory corruption at 0x%x\n", (int)p);
-        return;
-      }
+  }else{
+    void **pprev = pp[0];
+    void **pnext = pp[1];
+    assert(pprev);
+    assert(pprev[1]==p);
+    pprev[1] = (void *)pnext;
+    if( pnext ){
+      assert(pnext[0]==p);
+      pnext[0] = (void *)pprev;
     }
-    memset(pi, 0xff, (k+N_GUARD*2+1)*sizeof(int));
-#if SQLITE_MEMDEBUG>1
-    fprintf(stderr,"%06d free %d bytes at 0x%x from %s:%d\n",
-         ++memcnt, n, (int)p, zFile,line);
-#endif
-    free(pi);
   }
 }
 
 /*
-** Resize a prior allocation.  If p==0, then this routine
-** works just like sqliteMalloc().  If n==0, then this routine
-** works just like sqliteFree().
+** Pointer p is a pointer to an OS level allocation that has just been
+** realloc()ed. Set the list pointers that point to this entry to it's new
+** location.
 */
-void *sqlite3Realloc_(void *oldP, int n, char *zFile, int line){
-  int *oldPi, *pi, i, k, oldN, oldK;
-  void *p;
-  if( oldP==0 ){
-    return sqlite3Malloc_(n,1,zFile,line);
-  }
-  if( n==0 ){
-    sqlite3Free_(oldP,zFile,line);
-    return 0;
-  }
-  if( simulatedMallocFailure(n, zFile, line) ){
-    return 0;
+static void relinkAlloc(void *p)
+{
+  void **pp = (void **)p;
+  if( pp[0] ){
+    ((void **)(pp[0]))[1] = p;
+  }else{
+    SqliteTsd *pTsd = sqlite3Tsd();
+    pTsd->pFirst = p;
   }
-  oldPi = oldP;
-  oldPi -= N_GUARD+1;
-  if( oldPi[0]!=0xdead1122 ){
-    fprintf(stderr,"Low-end memory corruption in realloc at 0x%x\n", (int)oldP);
-    return 0;
+  if( pp[1] ){
+    ((void **)(pp[1]))[0] = p;
   }
-  oldN = oldPi[N_GUARD];
-  sqlite3_memUsed -= oldN;
-  oldK = (oldN+sizeof(int)-1)/sizeof(int);
-  for(i=0; i<N_GUARD; i++){
-    if( oldPi[oldK+N_GUARD+1+i]!=0xdead3344 ){
-      fprintf(stderr,"High-end memory corruption in realloc at 0x%x\n",
-              (int)oldP);
-      return 0;
+}
+
+/*
+** This function sets the result of the Tcl interpreter passed as an argument
+** to a list containing an entry for each currently outstanding call made to
+** sqliteMalloc and friends by the current thread.
+**
+** Todo: We could have a version of this function that outputs to stdout, 
+** to debug memory leaks when Tcl is not available.
+*/
+#ifdef TCLSH
+#include <tcl.h>
+int sqlite3OutstandingMallocs(Tcl_Interp *interp){
+  void *p;
+  SqliteTsd *pTsd = sqlite3Tsd();
+  Tcl_Obj *pRes = Tcl_NewObj();
+  Tcl_IncrRefCount(pRes);
+
+  for(p=pTsd->pFirst; p; p=((void **)p)[1]){
+    Tcl_Obj *pEntry = Tcl_NewObj();
+    Tcl_Obj *pStack = Tcl_NewObj();
+    char *z;
+    u32 iLine;
+    int nBytes = sqlite3OsAllocationSize(p) - TESTALLOC_OVERHEAD;
+    char *zAlloc = (char *)p;
+    int i;
+
+    Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(nBytes));
+
+    z = &zAlloc[TESTALLOC_OFFSET_FILENAME(p)];
+    Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
+
+    z = &zAlloc[TESTALLOC_OFFSET_LINENUMBER(p)];
+    memcpy(&iLine, z, sizeof(u32));
+    Tcl_ListObjAppendElement(0, pEntry, Tcl_NewIntObj(iLine));
+
+    z = &zAlloc[TESTALLOC_OFFSET_USER(p)];
+    Tcl_ListObjAppendElement(0, pEntry, Tcl_NewStringObj(z, -1));
+
+    z = &zAlloc[TESTALLOC_OFFSET_STACK(p)];
+    for(i=0; i<TESTALLOC_STACKFRAMES; i++){
+      char zHex[128];
+      sprintf(zHex, "%p", ((void **)z)[i]);
+      Tcl_ListObjAppendElement(0, pStack, Tcl_NewStringObj(zHex, -1));
     }
+
+    Tcl_ListObjAppendElement(0, pEntry, pStack);
+    Tcl_ListObjAppendElement(0, pRes, pEntry);
   }
-  k = (n + sizeof(int) - 1)/sizeof(int);
-  pi = malloc( (k+N_GUARD*2+1)*sizeof(int) );
-  if( pi==0 ){
-    if( n>0 ) sqlite3Tsd()->mallocFailed++;
-    return 0;
-  }
-  for(i=0; i<N_GUARD; i++) pi[i] = 0xdead1122;
-  pi[N_GUARD] = n;
-  sqlite3_memUsed += n;
-  if( sqlite3_memMax<sqlite3_memUsed ) sqlite3_memMax = sqlite3_memUsed;
-  for(i=0; i<N_GUARD; i++) pi[k+N_GUARD+1+i] = 0xdead3344;
-  p = &pi[N_GUARD+1];
-  memcpy(p, oldP, n>oldN ? oldN : n);
-  if( n>oldN ){
-    memset(&((char*)p)[oldN], 0x55, n-oldN);
-  }
-  memset(oldPi, 0xab, (oldK+N_GUARD+2)*sizeof(int));
-  free(oldPi);
-#if SQLITE_MEMDEBUG>1
-  print_stack_trace();
-  fprintf(stderr,"%06d realloc %d to %d bytes at 0x%x to 0x%x at %s:%d\n",
-    ++memcnt, oldN, n, (int)oldP, (int)p, zFile, line);
-#endif
-  return p;
+
+  Tcl_ResetResult(interp);
+  Tcl_SetObjResult(interp, pRes);
+  Tcl_DecrRefCount(pRes);
+  return TCL_OK;
 }
+#endif
 
 /*
-** Make a copy of a string in memory obtained from sqliteMalloc()
+** This is the test layer's wrapper around sqlite3OsMalloc().
 */
-char *sqlite3StrDup_(const char *z, char *zFile, int line){
-  char *zNew;
-  if( z==0 ) return 0;
-  zNew = sqlite3Malloc_(strlen(z)+1, 0, zFile, line);
-  if( zNew ) strcpy(zNew, z);
-  return zNew;
-}
-char *sqlite3StrNDup_(const char *z, int n, char *zFile, int line){
-  char *zNew;
-  if( z==0 ) return 0;
-  zNew = sqlite3Malloc_(n+1, 0, zFile, line);
-  if( zNew ){
-    memcpy(zNew, z, n);
-    zNew[n] = 0;
+static void * OSMALLOC(int n){
+  if( !failMalloc() ){
+    u32 *p;
+    p = (u32 *)sqlite3OsMalloc(n + TESTALLOC_OVERHEAD);
+    assert(p);
+    sqlite3_nMalloc++;
+    applyGuards(p);
+    linkAlloc(p);
+    return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
   }
-  return zNew;
+  return 0;
 }
 
 /*
-** A version of sqliteFree that is always a function, not a macro.
+** This is the test layer's wrapper around sqlite3OsFree(). The argument is a
+** pointer to the space allocated for the application to use.
 */
-void sqlite3FreeX(void *p){
-  sqliteFree(p);
+void OSFREE(void *pFree){
+  u32 *p = (u32 *)getOsPointer(pFree);   /* p points to Os level allocation */
+  checkGuards(p);
+  unlinkAlloc(p);
+  sqlite3OsFree(p);
+  sqlite3_nFree++;
 }
-#endif /* SQLITE_MEMDEBUG */
 
 /*
-** The following versions of malloc() and free() are for use in a
-** normal build.
+** This is the test layer's wrapper around sqlite3OsRealloc().
 */
-#if !defined(SQLITE_MEMDEBUG)
+void * OSREALLOC(void *pRealloc, int n){
+  if( !failMalloc() ){
+    u32 *p = (u32 *)getOsPointer(pRealloc);
+    checkGuards(p);
+    p = sqlite3OsRealloc(p, n + TESTALLOC_OVERHEAD);
+    applyGuards(p);
+    relinkAlloc(p);
+    return (void *)(&p[TESTALLOC_NGUARD + 2*sizeof(void *)/sizeof(u32)]);
+  }
+  return 0;
+}
+
+void OSMALLOC_FAILED(){
+  sqlite3Tsd()->isFail = 0;
+}
 
+#else
+#define OSMALLOC(x) sqlite3OsMalloc(x)
+#define OSREALLOC(x,y) sqlite3OsRealloc(x,y)
+#define OSFREE(x) sqlite3OsFree(x)
+#define OSMALLOC_FAILED()
+#endif
 /*
-** Allocate new memory and set it to zero.  Return NULL if
-** no memory is available.  See also sqliteMallocRaw().
+** End code for memory allocation system test layer.
+**--------------------------------------------------------------------------*/
+
+/*
+** Allocate and return N bytes of uninitialised memory by calling
+** sqlite3OsMalloc(). If the Malloc() call fails, attempt to free memory 
+** by calling sqlite3_release_memory().
 */
-void *sqlite3Malloc(int n){
-  void *p;
-  if( n==0 ) return 0;
-  if( (p = malloc(n))==0 ){
-    if( n>0 ) sqlite3Tsd()->mallocFailed++;
-  }else{
-    memset(p, 0, n);
+void *sqlite3MallocRaw(int n){
+  SqliteTsd *pTsd = sqlite3Tsd();
+  void *p = 0;
+
+  if( n>0 && !pTsd->mallocFailed ){
+    while( !(p = OSMALLOC(n)) && sqlite3_release_memory(n) );
+    if( !p ){
+      sqlite3Tsd()->mallocFailed = 1;
+      OSMALLOC_FAILED();
+    }
   }
   return p;
 }
 
 /*
-** Allocate new memory but do not set it to zero.  Return NULL if
-** no memory is available.  See also sqliteMalloc().
+** Resize the allocation at p to n bytes by calling sqlite3OsRealloc(). The
+** pointer to the new allocation is returned.  If the Realloc() call fails,
+** attempt to free memory by calling sqlite3_release_memory().
 */
-void *sqlite3MallocRaw(int n){
-  void *p;
-  if( n==0 ) return 0;
-  if( (p = malloc(n))==0 ){
-    if( n>0 ) sqlite3Tsd()->mallocFailed++;
+void *sqlite3Realloc(void *p, int n){
+  SqliteTsd *pTsd = sqlite3Tsd();
+  if( pTsd->mallocFailed ){
+    return 0;
+  }
+
+  if( !p ){
+    return sqlite3Malloc(n);
+  }else{
+    void *np = 0;
+    while( !(np = OSREALLOC(p, n)) && sqlite3_release_memory(n) );
+    if( !np ){
+      pTsd->mallocFailed = 1;
+      OSMALLOC_FAILED();
+    }
+    return np;
   }
-  return p;
 }
 
 /*
-** Free memory previously obtained from sqliteMalloc()
+** Free the memory pointed to by p. p must be either a NULL pointer or a 
+** value returned by a previous call to sqlite3Malloc() or sqlite3Realloc().
 */
 void sqlite3FreeX(void *p){
   if( p ){
-    free(p);
+    OSFREE(p);
   }
 }
 
 /*
-** Resize a prior allocation.  If p==0, then this routine
-** works just like sqliteMalloc().  If n==0, then this routine
-** works just like sqliteFree().
+** A version of sqliteMalloc() that is always a function, not a macro.
+** Currently, this is used only to alloc only used drawback.
 */
-void *sqlite3Realloc(void *p, int n){
-  void *p2;
-  if( p==0 ){
-    return sqliteMalloc(n);
-  }
-  if( n==0 ){
-    sqliteFree(p);
-    return 0;
+void *sqlite3MallocX(int n){
+  return sqliteMalloc(n);
+}
+
+/*
+** sqlite3Malloc
+** sqlite3ReallocOrFree
+**
+** These two are implemented as wrappers around sqlite3MallocRaw(), 
+** sqlite3Realloc() and sqlite3Free().
+*/ 
+void *sqlite3Malloc(int n){
+  void *p = sqlite3MallocRaw(n);
+  if( p ){
+    memset(p, 0, n);
   }
-  p2 = realloc(p, n);
-  if( p2==0 ){
-    if( n>0 ) sqlite3Tsd()->mallocFailed++;
+  return p;
+}
+void sqlite3ReallocOrFree(void **pp, int n){
+  void *p = sqlite3Realloc(*pp, n);
+  if( !p ){
+    sqlite3FreeX(*pp);
   }
-  return p2;
+  *pp = p;
 }
 
 /*
-** Make a copy of a string in memory obtained from sqliteMalloc()
+** Make a copy of a string in memory obtained from sqliteMalloc(). These 
+** functions call sqlite3MallocRaw() directly instead of sqliteMalloc(). This
+** is because when memory debugging is turned on, these two functions are 
+** called via macros that record the current file and line number in the
+** SqliteTsd structure.
 */
 char *sqlite3StrDup(const char *z){
   char *zNew;
   if( z==0 ) return 0;
-  zNew = sqliteMallocRaw(strlen(z)+1);
+  zNew = sqlite3MallocRaw(strlen(z)+1);
   if( zNew ) strcpy(zNew, z);
   return zNew;
 }
 char *sqlite3StrNDup(const char *z, int n){
   char *zNew;
   if( z==0 ) return 0;
-  zNew = sqliteMallocRaw(n+1);
+  zNew = sqlite3MallocRaw(n+1);
   if( zNew ){
     memcpy(zNew, z, n);
     zNew[n] = 0;
   }
   return zNew;
 }
-#endif /* !defined(SQLITE_MEMDEBUG) */
-
-/*
-** Reallocate a buffer to a different size.  This is similar to
-** sqliteRealloc() except that if the allocation fails the buffer
-** is freed.
-*/
-void sqlite3ReallocOrFree(void **ppBuf, int newSize){
-  void *pNew = sqliteRealloc(*ppBuf, newSize);
-  if( pNew==0 ){
-    sqliteFree(*ppBuf);
-  }
-  *ppBuf = pNew;
-}
 
 /*
 ** Create a string from the 2nd and subsequent arguments (up to the
@@ -426,11 +614,6 @@ void sqlite3SetString(char **pz, ...){
     zResult += strlen(zResult);
   }
   va_end(ap);
-#ifdef SQLITE_MEMDEBUG
-#if SQLITE_MEMDEBUG>1
-  fprintf(stderr,"string at 0x%x is %s\n", (int)*pz, *pz);
-#endif
-#endif
 }
 
 /*
@@ -1026,14 +1209,44 @@ void *sqlite3TextToPtr(const char *z){
 /*
 ** Return a pointer to the SqliteTsd associated with the calling thread.
 */
-static SqliteTsd tsd = { 0 };
+static SqliteTsd tsd = {
+  0                    /* mallocFailed flag */
+#ifndef NDEBUG
+  , 1                  /* mallocAllowed flag */
+#endif
+#ifndef SQLITE_MEMDEBUG
+  , 0
+  , 0
+  , 0
+  , 0
+#endif
+};
 SqliteTsd *sqlite3Tsd(){
   return &tsd;
 }
 
-void sqlite3ClearMallocFailed(){
+void sqlite3MallocClearFailed(){
   sqlite3Tsd()->mallocFailed = 0;
 }
 
+#ifndef NDEBUG
+/*
+** This function sets a flag in the thread-specific-data structure that will
+** cause an assert to fail if sqliteMalloc() or sqliteRealloc() is called.
+*/
+void sqlite3MallocDisallow(){
+  assert(sqlite3Tsd()->mallocAllowed);
+  sqlite3Tsd()->mallocAllowed = 0;
+}
+
+/*
+** This function clears the flag set in the thread-specific-data structure set
+** by sqlite3MallocDisallow().
+*/
+void sqlite3MallocAllow(){
+  assert(!sqlite3Tsd()->mallocAllowed);
+  sqlite3Tsd()->mallocAllowed = 1;
+}
+#endif
 
 
index b3533acfe55e856c49d2e2fb90f0d7081fa0f08e..8fe0550b3da0dd9a5e2be872824029617c2c3ccb 100644 (file)
@@ -14,7 +14,7 @@
 ** Most of the code in this file may be omitted by defining the
 ** SQLITE_OMIT_VACUUM macro.
 **
-** $Id: vacuum.c,v 1.51 2005/12/06 17:48:32 danielk1977 Exp $
+** $Id: vacuum.c,v 1.52 2005/12/09 14:25:09 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
@@ -311,12 +311,17 @@ end_of_vacuum:
   db->autoCommit = 1;
 
   if( pDetach ){
+    int mf = sqlite3Tsd()->mallocFailed;
+    sqlite3Tsd()->mallocFailed = 0;
+    sqlite3MallocDisallow();
     ((Vdbe *)pDetach)->expired = 0;
     sqlite3_step(pDetach);
     rc2 = sqlite3_finalize(pDetach);
     if( rc==SQLITE_OK ){
       rc = rc2;
     }
+    sqlite3MallocAllow();
+    sqlite3Tsd()->mallocFailed = mf;
   }
 
   /* If one of the execSql() calls above returned SQLITE_NOMEM, then the
index 7a178a2a4ecab7dea403a16b71f011204640c379..b1de8d8d2ff4e9bf3be2eec510c090e4c5a99650 100644 (file)
@@ -43,7 +43,7 @@
 ** in this file for details.  If in doubt, do not deviate from existing
 ** commenting and indentation practices when changing or adding code.
 **
-** $Id: vdbe.c,v 1.502 2005/12/06 12:53:01 danielk1977 Exp $
+** $Id: vdbe.c,v 1.503 2005/12/09 14:25:09 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -390,11 +390,15 @@ int sqlite3VdbeExec(
 
   if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE;
   assert( db->magic==SQLITE_MAGIC_BUSY );
+  pTos = p->pTos;
+  if( p->rc==SQLITE_NOMEM ){
+    /* This happens if a malloc() inside a call to sqlite3_column_text() or
+    ** sqlite3_column_text16() failed.  */
+    goto no_mem;
+  }
   assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY );
   p->rc = SQLITE_OK;
   assert( p->explain==0 );
-  pTos = p->pTos;
-  if( sqlite3Tsd()->mallocFailed ) goto no_mem;
   if( p->popStack ){
     popStack(&pTos, p->popStack);
     p->popStack = 0;
@@ -3898,13 +3902,13 @@ case OP_ParseSchema: {        /* no-push */
   db->init.busy = 1;
   assert(0==sqlite3Tsd()->mallocFailed);
   rc = sqlite3_exec(db, zSql, sqlite3InitCallback, &initData, 0);
+  sqliteFree(zSql);
   db->init.busy = 0;
   sqlite3SafetyOn(db);
   if( rc==SQLITE_NOMEM ){
     sqlite3Tsd()->mallocFailed = 1;
     goto no_mem;
   }
-  sqliteFree(zSql);
   break;  
 }
 
index 5d5e9cbb92038f227e25688d3d10b75950d92c4e..b0a1c612a5f021e2ef61a805ca4bd7ba4720f5aa 100644 (file)
@@ -241,7 +241,7 @@ int sqlite3_step(sqlite3_stmt *pStmt){
 #endif
 
   sqlite3Error(p->db, rc, p->zErrMsg ? "%s" : 0, p->zErrMsg);
-  sqlite3ClearMallocFailed();
+  sqlite3MallocClearFailed();
   return rc;
 }
 
@@ -378,30 +378,78 @@ static Mem *columnMem(sqlite3_stmt *pStmt, int i){
   return &pVm->pTos[(1-vals)+i];
 }
 
+/*
+** This function is called after invoking an sqlite3_value_XXX function on a 
+** column value (i.e. a value returned by evaluating an SQL expression in the
+** select list of a SELECT statement) that may cause a malloc() failure. If 
+** malloc() has failed, the threads mallocFailed flag is cleared and the result
+** code of statement pStmt set to SQLITE_NOMEM.
+**
+** Specificly, this is called from within:
+**
+**     sqlite3_column_int()
+**     sqlite3_column_int64()
+**     sqlite3_column_text()
+**     sqlite3_column_text16()
+**     sqlite3_column_real()
+**     sqlite3_column_bytes()
+**     sqlite3_column_bytes16()
+**
+** But not for sqlite3_column_blob(), which never calls malloc().
+*/
+static void columnMallocFailure(sqlite3_stmt *pStmt)
+{
+  /* If malloc() failed during an encoding conversion within an
+  ** sqlite3_column_XXX API, then set the return code of the statement to
+  ** SQLITE_NOMEM. The next call to _step() (if any) will return SQLITE_ERROR
+  ** and _finalize() will return NOMEM.
+  */
+  if( sqlite3Tsd()->mallocFailed ){
+    ((Vdbe *)pStmt)->rc = SQLITE_NOMEM;
+    sqlite3MallocClearFailed();
+  }
+}
+
 /**************************** sqlite3_column_  *******************************
 ** The following routines are used to access elements of the current row
 ** in the result set.
 */
 const void *sqlite3_column_blob(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_blob( columnMem(pStmt,i) );
+  const void *val;
+  sqlite3MallocDisallow();
+  val = sqlite3_value_blob( columnMem(pStmt,i) );
+  sqlite3MallocAllow();
+  return val;
 }
 int sqlite3_column_bytes(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_bytes( columnMem(pStmt,i) );
+  int val = sqlite3_value_bytes( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 int sqlite3_column_bytes16(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_bytes16( columnMem(pStmt,i) );
+  int val = sqlite3_value_bytes16( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 double sqlite3_column_double(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_double( columnMem(pStmt,i) );
+  double val = sqlite3_value_double( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 int sqlite3_column_int(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_int( columnMem(pStmt,i) );
+  int val = sqlite3_value_int( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 sqlite_int64 sqlite3_column_int64(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_int64( columnMem(pStmt,i) );
+  sqlite_int64 val = sqlite3_value_int64( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 const unsigned char *sqlite3_column_text(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_text( columnMem(pStmt,i) );
+  const unsigned char *val = sqlite3_value_text( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 #if 0
 sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
@@ -410,7 +458,9 @@ sqlite3_value *sqlite3_column_value(sqlite3_stmt *pStmt, int i){
 #endif
 #ifndef SQLITE_OMIT_UTF16
 const void *sqlite3_column_text16(sqlite3_stmt *pStmt, int i){
-  return sqlite3_value_text16( columnMem(pStmt,i) );
+  const void *val = sqlite3_value_text16( columnMem(pStmt,i) );
+  columnMallocFailure(pStmt);
+  return val;
 }
 #endif /* SQLITE_OMIT_UTF16 */
 int sqlite3_column_type(sqlite3_stmt *pStmt, int i){
@@ -452,11 +502,10 @@ static const void *columnName(
   /* A malloc may have failed inside of the xFunc() call. If this is the case,
   ** clear the mallocFailed flag and return NULL.
   */
-  sqlite3ClearMallocFailed();
+  sqlite3MallocClearFailed();
   return ret;
 }
 
-
 /*
 ** Return the name of the Nth column of the result set returned by SQL
 ** statement pStmt.
index 3949087b7567f96cc95775400c070a465bc4740f..4c9cd0b93fc4b59056eb605c3d878e0f7798e3c6 100644 (file)
@@ -415,7 +415,7 @@ static void freeP3(int p3type, void *p3){
 void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
   Op *pOp;
   assert( p->magic==VDBE_MAGIC_INIT );
-  if( p==0 || p->aOp==0 ){
+  if( p==0 || p->aOp==0 || sqlite3Tsd()->mallocFailed ){
     if (n != P3_KEYINFO) {
       freeP3(n, (void*)*(char**)&zP3);
     }
@@ -470,7 +470,7 @@ void sqlite3VdbeChangeP3(Vdbe *p, int addr, const char *zP3, int n){
 void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){
   va_list ap;
   assert( p->nOp>0 );
-  assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 );
+  assert( p->aOp==0 || p->aOp[p->nOp-1].p3==0 || sqlite3Tsd()->mallocFailed );
   va_start(ap, zFormat);
   sqlite3VdbeChangeP3(p, -1, sqlite3VMPrintf(zFormat, ap), P3_DYNAMIC);
   va_end(ap);
index f1259cda1753822a4ecc44063be1815fa39b9876..c1f13652b20fe70391799f0c0d9eecb0863a1af7 100644 (file)
@@ -13,7 +13,7 @@
 # correctly. The emphasis of these tests are the _prepare(), _step() and
 # _finalize() calls.
 #
-# $Id: malloc3.test,v 1.3 2005/12/07 06:27:45 danielk1977 Exp $
+# $Id: malloc3.test,v 1.4 2005/12/09 14:25:12 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -580,14 +580,19 @@ proc run_test {arglist {pcstart 0} {iFailStart 1}} {
 
       default { error "Unknown switch: $k" }
     }
+# if {$iFail > ($iFailStart+1)} return
   }
 }
 
 # Turn of the Tcl interface's prepared statement caching facility.
 db cache size 0
 
-#run_test $::run_test_script 59 97 
-run_test $::run_test_script
+run_test $::run_test_script 
+# run_test [lrange $::run_test_script 0 3] 0 63
 sqlite_malloc_fail 0
+db close
+
+pp_check_for_leaks
+
 finish_test
 
index eee0afc68efe14ea9303236f98c9b49efef3a4c4..c769ff1712f2b36dfddab993d63b9dac0c03e60c 100644 (file)
 # This file contains tests to ensure that the library handles malloc() failures
 # correctly. The emphasis in this file is on sqlite3_column_XXX() APIs.
 #
-# $Id: malloc4.test,v 1.1 2005/12/07 06:27:45 danielk1977 Exp $
+# $Id: malloc4.test,v 1.2 2005/12/09 14:25:12 danielk1977 Exp $
 
 #---------------------------------------------------------------------------
 # NOTES ON EXPECTED BEHAVIOUR
 #
+# [193] When a memory allocation failure occurs during sqlite3_column_name(),
+#       sqlite3_column_name16(), sqlite3_column_decltype(), or
+#       sqlite3_column_decltype16() the function shall return NULL.
+#
 #---------------------------------------------------------------------------
 
 set testdir [file dirname $argv0]
@@ -29,7 +33,6 @@ if {[info command sqlite_malloc_stat]==""} {
    return
 }
 
-
 proc do_stmt_test {id sql} {
   set ::sql $sql
   set go 1
@@ -48,6 +51,18 @@ proc do_stmt_test {id sql} {
     # Test malloc failure in the _name(), _name16(), decltype() and
     # decltype16() APIs. Calls that occur after the malloc() failure should
     # return NULL. No error is raised though.
+    #
+    # ${testid}.2.1 - Call _name()
+    # ${testid}.2.2 - Call _name16()
+    # ${testid}.2.3 - Call _name()
+    # ${testid}.2.4 - Check that the return values of the above three calls are
+    #                 consistent with each other and with the simulated
+    #                 malloc() failures.
+    #
+    # Because the code that implements the _decltype() and _decltype16() APIs
+    # is the same as the _name() and _name16() implementations, we don't worry
+    # about explicitly testing them.
+    #
     do_test ${testid}.2.1 {
       set mf1 [expr [lindex [sqlite_malloc_stat] 2] <= 0]
       set ::name8  [sqlite3_column_name $::STMT 0]
@@ -78,27 +93,83 @@ proc do_stmt_test {id sql} {
       }
     } {1}
 
-    if {$::mallocFailed == 0} {
+    # Step the statement so that we can call _text() and _text16().  Before
+    # running sqlite3_step(), make sure that malloc() is not about to fail.
+    # Memory allocation failures that occur within sqlite3_step() are tested
+    # elsewhere.
+    set mf [lindex [sqlite_malloc_stat] 2]
+    sqlite_malloc_fail 0
+    do_test ${testid}.3 {
+      sqlite3_step $::STMT
+    } {SQLITE_ROW}
+    sqlite_malloc_fail $mf
+
+    # Test for malloc() failures within _text() and _text16().
+    #
+    do_test ${testid}.4.1 {
+      set ::text8 [sqlite3_column_text $::STMT 0]
+      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+      expr {$mf==0 || $::text8 == ""}
+    } {1}
+    do_test ${testid}.4.2 {
+      set ::text16 [sqlite3_column_text16 $::STMT 0]
+      set ::text16 [encoding convertfrom unicode $::text16]
+      set ::text16 [string range $::text16 0 end-1]
+      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+      expr {$mf==0 || $::text16 == ""}
+    } {1}
+    do_test ${testid}.4.3 {
+      set ::text8_2 [sqlite3_column_text $::STMT 0]
+      set mf [expr [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed]
+      expr {$mf==0 || $::text8_2 == "" || ($::text16 == "" && $::text8 != "")}
+    } {1}
+
+    # Test for malloc() failures within _int(), _int64() and _real(). The only
+    # way this can occur is if the string has to be translated from UTF-16 to
+    # UTF-8 before being converted to a numeric value.
+    do_test ${testid}.4.4.1 {
+      set mf [lindex [sqlite_malloc_stat] 2]
       sqlite_malloc_fail 0
-      set go 0
-    }
+      sqlite3_column_text16 $::STMT 0
+      sqlite_malloc_fail $mf
+      sqlite3_column_int $::STMT 0
+    } {0}
+    do_test ${testid}.4.5 {
+      set mf [lindex [sqlite_malloc_stat] 2]
+      sqlite_malloc_fail 0
+      sqlite3_column_text16 $::STMT 0
+      sqlite_malloc_fail $mf
+      sqlite3_column_int64 $::STMT 0
+    } {0}
+
+    do_test ${testid}.4.6 {
+      set mf [lindex [sqlite_malloc_stat] 2]
+      sqlite_malloc_fail 0
+      sqlite3_column_text16 $::STMT 0
+      sqlite_malloc_fail $mf
+      sqlite3_column_double $::STMT 0
+    } {0.0}
 
-if 0 {
+    set mallocFailedAfterStep [expr \
+      [lindex [sqlite_malloc_stat] 2] <= 0 && !$::mallocFailed
+    ]
+
+    sqlite_malloc_fail 0
     # Test that if a malloc() failed the next call to sqlite3_step() returns
-    # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_ROW.
+    # SQLITE_ERROR. If malloc() did not fail, it should return SQLITE_DONE.
     #
-    # Before running sqlite3_step(), make sure that malloc() is not about to
-    # fail. Memory allocation failures that occur within sqlite3_step() are
-    # tested elsewhere.
-    do_test ${testid}.3 {
-      set rc [sqlite3_step $::STMT]
-      list [expr $rc=="SQLITE_ERROR"] [expr $rc=="SQLITE_ROW"]
-    } [list $mallocFailed [expr !$mallocFailed]]
-}
+    do_test ${testid}.5 {
+      sqlite3_step $::STMT
+    } [expr {$mallocFailedAfterStep ? "SQLITE_ERROR" : "SQLITE_DONE"}]
 
-    do_test ${testid}.4 {
+    do_test ${testid}.6 {
       sqlite3_finalize $::STMT
-    } {SQLITE_OK}
+    } [expr {$mallocFailedAfterStep ? "SQLITE_NOMEM" : "SQLITE_OK"}]
+
+    if {$::mallocFailed == 0 && $mallocFailedAfterStep == 0} {
+      sqlite_malloc_fail 0
+      set go 0
+    }
   }
 }
 
@@ -106,7 +177,11 @@ execsql {
   CREATE TABLE tbl(
     the_first_reasonably_long_column_name that_also_has_quite_a_lengthy_type
   );
+  INSERT INTO tbl VALUES(
+    'An extra long string. Far too long to be stored in NBFS bytes.'
+  );
 }
+
 do_stmt_test 1 "SELECT * FROM tbl"
 
 sqlite_malloc_fail 0
index 8a2a4f0b3c943564dbbc62085e4623d60785790f..7445ed1d934bdd9c934402573a94e494ad14b43d 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing the SELECT statement.
 #
-# $Id: select1.test,v 1.43 2005/09/08 10:37:01 drh Exp $
+# $Id: select1.test,v 1.44 2005/12/09 14:25:12 danielk1977 Exp $
 
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
@@ -799,5 +799,4 @@ ifcapable {compound && subquery} {
   } {x 1 x 3}
 } ;# ifcapable compound
 
-
 finish_test
index ab35a64f9425dbc3337027c432419b1cfbabb59b..8cc925cd198983c6d69b209e1faf1caec8cff198 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements some common TCL routines used for regression
 # testing the SQLite library
 #
-# $Id: tester.tcl,v 1.52 2005/11/26 00:25:04 drh Exp $
+# $Id: tester.tcl,v 1.53 2005/12/09 14:25:12 danielk1977 Exp $
 
 # Make sure tclsqlite3 was compiled correctly.  Abort now with an
 # error message if not.
@@ -83,6 +83,7 @@ set maxErr 1000
 #
 proc do_test {name cmd expected} {
   global argv nErr nTest skip_test maxErr
+  set ::sqlite_malloc_id $name
   if {$skip_test} {
     set skip_test 0
     return
@@ -139,7 +140,13 @@ proc finish_test {} {
 proc finalize_testing {} {
   global nTest nErr nProb sqlite_open_file_count
   if {$nErr==0} memleak_check
+
   catch {db close}
+  catch {db2 close}
+  catch {db3 close}
+
+pp_check_for_leaks
+
   puts "$nErr errors out of $nTest tests"
   puts "Failures on these tests: $::failList"
   if {$nProb>0} {
@@ -423,6 +430,59 @@ proc copy_file {from to} {
   }
 }
 
+# This command checks for outstanding calls to sqliteMalloc() from within
+# the current thread. A list is returned with one entry for each outstanding
+# malloc. Each list entry is itself a list of 5 items, as follows:
+#
+#     { <number-bytes> <file-name> <line-number> <test-case> <stack-dump> }
+#
+proc check_for_leaks {} {
+  set ret [list]
+  foreach alloc [sqlite_malloc_outstanding] {
+    foreach {nBytes file iLine userstring backtrace} $alloc {}
+    set stack [list]
+    set skip 0
+
+    # The first command in this block will probably fail on windows. This
+    # means there will be no stack dump available.
+    catch {
+      set stuff [eval "exec addr2line -e ./testfixture -f $backtrace"]
+      foreach {func line} $stuff {
+        if {$func != "??" || $line != "??:0"} {
+          regexp {.*/(.*)} $line dummy line
+          lappend stack "${func}() $line"
+        } else {
+          if {[lindex $stack end] != "..."} {
+            lappend stack "..."
+          }
+        }
+      }
+    }
+
+    if {!$skip} {
+      lappend ret [list $nBytes $file $iLine $userstring $stack]
+    }
+  }
+  return $ret
+}
+
+# Pretty print a report based on the return value of [check_for_leaks] to
+# stdout.
+proc pp_check_for_leaks {} {
+  set l [check_for_leaks]
+  set n 0
+  foreach leak $l {
+    foreach {nBytes file iLine userstring stack} $leak {}
+    puts "$nBytes bytes leaked at $file:$iLine ($userstring)"
+    foreach frame $stack {
+      puts "        $frame"
+    }
+    incr n $nBytes
+  }
+  puts "Memory leaked: $n bytes in [llength $l] allocations"
+  puts ""
+}
+
 # If the library is compiled with the SQLITE_DEFAULT_AUTOVACUUM macro set
 # to non-zero, then set the global variable $AUTOVACUUM to 1.
 set AUTOVACUUM $sqlite_options(default_autovacuum)