aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/attach.c20
-rw-r--r--src/os_unix.c3
-rw-r--r--src/prepare.c4
-rw-r--r--src/shell.c7
-rw-r--r--src/sqliteInt.h103
-rw-r--r--src/test1.c15
-rw-r--r--src/trigger.c7
-rw-r--r--src/util.c731
-rw-r--r--src/vacuum.c7
-rw-r--r--src/vdbe.c12
-rw-r--r--src/vdbeapi.c71
-rw-r--r--src/vdbeaux.c4
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);