diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/attach.c | 20 | ||||
-rw-r--r-- | src/os_unix.c | 3 | ||||
-rw-r--r-- | src/prepare.c | 4 | ||||
-rw-r--r-- | src/shell.c | 7 | ||||
-rw-r--r-- | src/sqliteInt.h | 103 | ||||
-rw-r--r-- | src/test1.c | 15 | ||||
-rw-r--r-- | src/trigger.c | 7 | ||||
-rw-r--r-- | src/util.c | 731 | ||||
-rw-r--r-- | src/vacuum.c | 7 | ||||
-rw-r--r-- | src/vdbe.c | 12 | ||||
-rw-r--r-- | src/vdbeapi.c | 71 | ||||
-rw-r--r-- | src/vdbeaux.c | 4 |
12 files changed, 635 insertions, 349 deletions
diff --git a/src/attach.c b/src/attach.c index 008841750..8003d7cc9 100644 --- a/src/attach.c +++ b/src/attach.c @@ -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); } /* diff --git a/src/os_unix.c b/src/os_unix.c index 3beaf3b91..c07a80cb9 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -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{ diff --git a/src/prepare.c b/src/prepare.c index 4a9b2b1ce..9a1d4f338 100644 --- a/src/prepare.c +++ b/src/prepare.c @@ -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; } diff --git a/src/shell.c b/src/shell.c index 3296f44bf..b06f91309 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 6c9ae4511..f9c853a03 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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 @@ -1380,14 +1379,6 @@ typedef struct { } 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 * even when no triggers exist @@ -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 diff --git a/src/test1.c b/src/test1.c index d29346410..d4e15ac2b 100644 --- a/src/test1.c +++ b/src/test1.c @@ -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); diff --git a/src/trigger.c b/src/trigger.c index cc5fc080e..fc4f172ba 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -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; diff --git a/src/util.c b/src/util.c index 88924fbeb..ac8677a79 100644 --- a/src/util.c +++ b/src/util.c @@ -14,42 +14,175 @@ ** 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 diff --git a/src/vacuum.c b/src/vacuum.c index b3533acfe..8fe0550b3 100644 --- a/src/vacuum.c +++ b/src/vacuum.c @@ -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 diff --git a/src/vdbe.c b/src/vdbe.c index 7a178a2a4..b1de8d8d2 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -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; } diff --git a/src/vdbeapi.c b/src/vdbeapi.c index 5d5e9cbb9..b0a1c612a 100644 --- a/src/vdbeapi.c +++ b/src/vdbeapi.c @@ -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. diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 3949087b7..4c9cd0b93 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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); |