diff --git a/main.mk b/main.mk index f9f2979cb8..41f9344f60 100644 --- a/main.mk +++ b/main.mk @@ -58,7 +58,8 @@ TCCX = $(TCC) $(OPTS) $(THREADSAFE) $(USLEEP) -I. -I$(TOP)/src LIBOBJ+= alter.o analyze.o attach.o auth.o btree.o build.o \ callback.o complete.o date.o delete.o \ expr.o func.o hash.o insert.o loadext.o \ - main.o malloc.o opcodes.o os.o os_os2.o os_unix.o os_win.o \ + main.o malloc.o mem1.o mutex.o \ + opcodes.o os.o os_os2.o os_unix.o os_win.o \ pager.o parse.o pragma.o prepare.o printf.o random.o \ select.o table.o tclsqlite.o tokenize.o trigger.o \ update.o util.o vacuum.o \ @@ -91,6 +92,8 @@ SRC = \ $(TOP)/src/loadext.c \ $(TOP)/src/main.c \ $(TOP)/src/malloc.c \ + $(TOP)/src/mem.c \ + $(TOP)/src/mutex.c \ $(TOP)/src/os.c \ $(TOP)/src/os_os2.c \ $(TOP)/src/os_unix.c \ @@ -341,6 +344,12 @@ main.o: $(TOP)/src/main.c $(HDR) malloc.o: $(TOP)/src/malloc.c $(HDR) $(TCCX) -c $(TOP)/src/malloc.c +mem1.o: $(TOP)/src/mem1.c $(HDR) + $(TCCX) -c $(TOP)/src/mem1.c + +mutex.o: $(TOP)/src/mutex.c $(HDR) + $(TCCX) -c $(TOP)/src/mutex.c + pager.o: $(TOP)/src/pager.c $(HDR) $(TOP)/src/pager.h $(TCCX) -c $(TOP)/src/pager.c diff --git a/manifest b/manifest index 4a647c2925..1824ae98be 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Revisions\sto\sthe\sinterface\sdesign\sfor\s3.5.\s(CVS\s4225) -D 2007-08-15T11:28:56 +C Add\sinitial\simplementations\sof\smutex\sand\smemory\ssubsystem\smodules.\s(CVS\s4226) +D 2007-08-15T13:04:54 F Makefile.in 0c0e53720f658c7a551046442dd7afba0b72bfbe F Makefile.linux-gcc 65241babba6faf1152bf86574477baab19190499 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028 @@ -51,7 +51,7 @@ F ext/icu/README.txt 3b130aa66e7a681136f6add198b076a2f90d1e33 F ext/icu/icu.c 61a345d8126686aa3487aa8d2d0f68abd655f7a4 F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 F ltmain.sh 56abb507100ed2d4261f6dd1653dec3cf4066387 -F main.mk 570e26f4e1b2060ad1cf08e36a8ce29065b1db75 +F main.mk c9336cf989f5e2076e5fbda909fa57cca24b6515 F mkdll.sh 37fa8a7412e51b5ab2bc6d4276135f022a0feffb F mkextu.sh 416f9b7089d80e5590a29692c9d9280a10dbad9f F mkextw.sh 1a866b53637dab137191341cc875575a5ca110fb @@ -85,9 +85,11 @@ F src/insert.c ca135e919c2a9241e83e8dd74316677fdd54fb6f F src/legacy.c 388c71ad7fbcd898ba1bcbfc98a3ac954bfa5d01 F src/limits.h 71ab25f17e35e0a9f3f6f234b8ed49cc56731d35 F src/loadext.c 6c24ee62adfe7fbfb2f2dd43ff18e5534b19010f -F src/main.c 345ffcf44506bda3b53355c08cf22aecbded27e7 +F src/main.c f12d230c1226d3f43c1f4595af1c25ccbe3017c7 F src/malloc.c 3850ab4a2edfb190ffee353c5674ebd8c6b4ccc7 F src/md5.c c5fdfa5c2593eaee2e32a5ce6c6927c986eaf217 +F src/mem1.c f4127f6f6f183b66f7c4345e33c9fe19a43e9942 +F src/mutex.c 667dae0de95f8fb92a3ffc8c3f20c0d26115a1a6 F src/os.c 1f10b47acc1177fb9225edb4f5f0d25ed716f9cb F src/os.h cea2e179bb33f4fc09dbb9fcd51b2246544bd2db F src/os_common.h a5c446d3b93f09f369d13bf217de4bed3437dd1c @@ -109,9 +111,9 @@ F src/random.c 6119474a6f6917f708c1dee25b9a8e519a620e88 F src/select.c 3b167744fc375bddfddcef87feb18f5171737677 F src/server.c 087b92a39d883e3fa113cae259d64e4c7438bc96 F src/shell.c ac29402b538515fa4697282387be9c1205e6e9eb -F src/sqlite.h.in 9f35010f878acf0c161ea8324f96b22a81eb59f9 +F src/sqlite.h.in 025e9fd6c519f2945296a9db46ca9da4571c22d7 F src/sqlite3ext.h a27bedc222df5e5f0f458ac99726d0483b953a91 -F src/sqliteInt.h aaf44ba52e0afe1327baae8082e3662d7b36723b +F src/sqliteInt.h fc9f6e8d916e182c04983a089c4ce4057fac5003 F src/sqliteLimit.h f14609c27636ebc217c9603ade26dbdd7d0f6afa F src/table.c a8de75bcedf84d4060d804264b067ab3b1a3561d F src/tclsqlite.c 648e6f53041ce4974234d4963e71680926760925 @@ -525,7 +527,7 @@ F www/tclsqlite.tcl 8be95ee6dba05eabcd27a9d91331c803f2ce2130 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b F www/whentouse.tcl fc46eae081251c3c181bd79c5faef8195d7991a5 -P 0b5b526c9de22a34c5051d14735a062ed3f898eb -R e61d44cb3c83a44c033e2ccb600c0264 +P 174116f7c0ceeceb5e32868b29fabf8a6943cbf6 +R 7f615db37ae8f52942fff60e5e86001f U drh -Z ff0efbdc8d85c0f34cf38329a6a4a48d +Z 657d3d4bb780276ea6842b10c93c7b7e diff --git a/manifest.uuid b/manifest.uuid index 0a2b6d7cc2..631c3b8115 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -174116f7c0ceeceb5e32868b29fabf8a6943cbf6 \ No newline at end of file +c0fa3769790af199a4c8715c80bb8ff900730520 \ No newline at end of file diff --git a/src/main.c b/src/main.c index a8c69cbe16..b797f774fd 100644 --- a/src/main.c +++ b/src/main.c @@ -14,7 +14,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.378 2007/08/13 15:28:34 danielk1977 Exp $ +** $Id: main.c,v 1.379 2007/08/15 13:04:54 drh Exp $ */ #include "sqliteInt.h" #include "os.h" @@ -421,30 +421,6 @@ void sqlite3_interrupt(sqlite3 *db){ } } -/* -** Memory allocation routines that use SQLites internal memory -** memory allocator. Depending on how SQLite is compiled, the -** internal memory allocator might be just an alias for the -** system default malloc/realloc/free. Or the built-in allocator -** might do extra stuff like put sentinals around buffers to -** check for overruns or look for memory leaks. -** -** Use sqlite3_free() to free memory returned by sqlite3_mprintf(). -*/ -void sqlite3_free(void *p){ if( p ) sqlite3OsFree(p); } -void *sqlite3_malloc(int nByte){ return nByte>0 ? sqlite3OsMalloc(nByte) : 0; } -void *sqlite3_realloc(void *pOld, int nByte){ - if( pOld ){ - if( nByte>0 ){ - return sqlite3OsRealloc(pOld, nByte); - }else{ - sqlite3OsFree(pOld); - return 0; - } - }else{ - return sqlite3_malloc(nByte); - } -} /* ** This function is exactly the same as sqlite3_create_function(), except diff --git a/src/mem1.c b/src/mem1.c new file mode 100644 index 0000000000..ba4c8e950b --- /dev/null +++ b/src/mem1.c @@ -0,0 +1,204 @@ +/* +** 2007 August 14 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement a memory +** allocation subsystem for use by SQLite. +** +** $Id: mem1.c,v 1.1 2007/08/15 13:04:54 drh Exp $ +*/ + +/* +** We will eventually construct multiple memory allocation subsystems +** suitable for use in various contexts: +** +** * Normal multi-threaded builds +** * Normal single-threaded builds +** * Debugging builds +** +** This initial version is suitable for use in normal multi-threaded +** builds. We envision that alternative versions will be stored in +** separate source files. #ifdefs will be used to select the code from +** one of the various memN.c source files for use in any given build. +*/ +#include "sqliteInt.h" + + +/* +** Mutex to control access to the memory allocation subsystem. +*/ +static sqlite3_mutex *memMutex = 0; + +/* +** Current allocation and high-water mark. +*/ +static sqlite3_uint64 nowUsed = 0; +static sqlite3_uint64 mxUsed = 0; + +/* +** The alarm callback and its arguments. The memMutex lock will +** be held while the callback is running. Recursive calls into +** the memory subsystem are allowed, but no new callbacks will be +** issued. The alarmBusy variable is set to prevent recursive +** callbacks. +*/ +static void (*alarmCallback)(void*, sqlite3_uint64, unsigned) = 0; +static void *alarmArg = 0; +static sqlite3_uint64 alarmThreshold = (((sqlite3_uint64)1)<<63); +static int alarmBusy = 0; + + +/* +** Return the amount of memory currently checked out. +*/ +sqlite3_uint64 sqlite3_memory_used(void){ + sqlite3_uint64 n; + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + n = nowUsed; + sqlite3_mutex_leave(memMutex); + return n; +} + +/* +** Return the maximum amount of memory that has ever been +** checked out since either the beginning of this process +** or since the most recent reset. +*/ +sqlite3_uint64 sqlite3_memory_highwater(int resetFlag){ + sqlite3_uint64 n; + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + n = mxUsed; + if( resetFlag ){ + mxUsed = nowUsed; + } + sqlite3_mutex_leave(memMutex); + return n; +} + +/* +** Change the alarm callback +*/ +int sqlite3_memory_alarm( + void(*xCallback)(void *pArg, sqlite3_uint64 used, unsigned int N), + void *pArg, + sqlite3_uint64 iThreshold +){ + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + alarmCallback = xCallback; + alarmArg = pArg; + alarmThreshold = iThreshold; + sqlite3_mutex_leave(memMutex); + return SQLITE_OK; +} + +/* +** Trigger the alarm +*/ +static void sqlite3MemsysAlarm(unsigned nByte){ + if( alarmCallback==0 || alarmBusy ) return; + alarmBusy = 1; + alarmCallback(alarmArg, nowUsed, nByte); + alarmBusy = 0; +} + +/* +** Allocate nBytes of memory +*/ +void *sqlite3_malloc(unsigned int nBytes){ + sqlite3_uint64 *p; + if( memMutex==0 ){ + memMutex = sqlite3_mutex_alloc(1); + } + sqlite3_mutex_enter(memMutex, 1); + if( nowUsed+nBytes>=alarmThreshold ){ + sqlite3MemsysAlarm(nBytes); + } + p = malloc(nBytes+8); + if( p==0 ){ + sqlite3MemsysAlarm(nBytes); + p = malloc(nBytes+8); + } + if( p ){ + p[0] = nBytes; + p++; + nowUsed += nBytes; + if( nowUsed>mxUsed ){ + mxUsed = nowUsed; + } + } + sqlite3_mutex_leave(memMutex); + return (void*)p; +} + +/* +** Free memory. +*/ +void sqlite3_free(void *pPrior){ + sqlite3_uint64 *p; + unsigned nByte; + if( pPrior==0 ){ + return; + } + assert( memMutex!=0 ); + p = pPrior; + p--; + nByte = (unsigned int)*p; + sqlite3_mutex_enter(memMutex, 1); + nowUsed -= nByte; + free(p); + sqlite3_mutex_leave(memMutex); +} + +/* +** Change the size of an existing memory allocation +*/ +void *sqlite3_realloc(void *pPrior, unsigned int nBytes){ + unsigned nOld; + sqlite3_uint64 *p; + if( pPrior==0 ){ + return sqlite3_malloc(nBytes); + } + if( nBytes==0 ){ + sqlite3_free(pPrior); + return; + } + p = pPrior; + p--; + nOld = (unsigned int)p[0]; + assert( memMutex!=0 ); + sqlite3_mutex_enter(memMutex, 1); + if( nowUsed+nBytes-nOld>=alarmThreshold ){ + sqlite3MemsysAlarm(nBytes-nOld); + } + p = realloc(p, nBytes+8); + if( p==0 ){ + sqlite3MemsysAlarm(nBytes); + p = realloc(p, nBytes+8); + } + if( p ){ + p[0] = nBytes; + p++; + nowUsed += nBytes-nOld; + if( nowUsed>mxUsed ){ + mxUsed = nowUsed; + } + } + sqlite3_mutex_leave(memMutex); + return (void*)p; +} diff --git a/src/mutex.c b/src/mutex.c new file mode 100644 index 0000000000..5c9e151204 --- /dev/null +++ b/src/mutex.c @@ -0,0 +1,238 @@ +/* +** 2007 August 14 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains the C functions that implement mutexes for +** use by the SQLite core. +** +** $Id: mutex.c,v 1.1 2007/08/15 13:04:54 drh Exp $ +*/ + +/* +** If SQLITE_MUTEX_APPDEF is defined, then this whole module is +** omitted and equivalent functionality just be provided by the +** application that links against the SQLite library. +*/ +#ifndef SQLITE_MUTEX_APPDEF + +/* +** The start of real code +*/ +#include "sqliteInt.h" + +/************************ No-op Mutex Implementation ********************** +** +** This first implementation of mutexes is really a no-op. In other words, +** no real locking occurs. This implementation is appropriate for use +** in single threaded applications which do not want the extra overhead +** of thread locking primitives. +*/ + +/* +** The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. If it returns NULL +** that means that a mutex could not be allocated. SQLite +** will unwind its stack and return an error. The argument +** to sqlite3_mutex_alloc() is usually zero, which causes +** any space required for the mutex to be obtained from +** sqlite3_malloc(). However if the argument is a positive +** integer less than SQLITE_NUM_STATIC_MUTEX, then a pointer +** to a static mutex is returned. There are a finite number +** of static mutexes. Static mutexes should not be passed +** to sqlite3_mutex_free(). The allocation of a static +** mutex cannot fail. +*/ +sqlite3_mutex *sqlite3_mutex_alloc(int idNotUsed){ + return (sqlite3_mutex*)sqlite3_mutex_alloc; +} + +/* +** This routine deallocates a previously +** allocated mutex. SQLite is careful to deallocate every +** mutex that it allocates. +*/ +void sqlite3_mutex_free(sqlite3_mutex *pNotUsed){} + +/* +** The sqlite3_mutex_enter() routine attempts to enter a +** mutex. If another thread is already within the mutex, +** sqlite3_mutex_enter() will return SQLITE_BUSY if blockFlag +** is zero, or it will block and wait for the other thread to +** exit if blockFlag is non-zero. Mutexes are recursive. The +** same thread can enter a single mutex multiple times. Each +** entrance must be matched with a corresponding exit before +** another thread is able to enter the mutex. +*/ +int sqlite3_mutex_enter(sqlite3_mutex *pNotUsed, int blockFlag){ + return SQLITE_OK; +} + +/* +** The sqlite3_mutex_exit() routine exits a mutex that was +** previously entered by the same thread. The behavior +** is undefined if the mutex is not currently entered or +** is not currently allocated. SQLite will never do either. +*/ +void sqlite3_mutex_leave(sqlite3_mutex *pNotUsed){ + return; +} + +/* +** The sqlite3_mutex_serialize() routine is used to serialize +** execution of a subroutine. The subroutine given in the argument +** is invoked. But only one thread at a time is allowed to be +** running a subroutine using sqlite3_mutex_serialize(). +*/ +int sqlite3_mutex_serialize(void (*xCallback)(void*), void *pArg){ + xCallback(pArg); +} + +#if 0 +/**************** Non-recursive Pthread Mutex Implementation ***************** +** +** This implementation of mutexes is built using a version of pthreads that +** does not have native support for recursive mutexes. +*/ + +/* +** Each recursive mutex is an instance of the following structure. +*/ +struct RMutex { + int nRef; /* Number of entrances */ + pthread_mutex_t auxMutex; /* Mutex controlling access to nRef and owner */ + pthread_mutex_t mainMutex; /* Mutex controlling the lock */ + pthread_t owner; /* Thread that is within this mutex */ +}; + +/* +** Static mutexes +*/ +static struct RMutex rmutexes[] = { + { 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, }, + { 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, }, + { 0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, }, +}; + +/* +** A mutex used for serialization. +*/ +static RMutex serialMutex = + {0, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, }; + +/* +** The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. If it returns NULL +** that means that a mutex could not be allocated. SQLite +** will unwind its stack and return an error. The argument +** to sqlite3_mutex_alloc() is usually zero, which causes +** any space required for the mutex to be obtained from +** sqlite3_malloc(). However if the argument is a positive +** integer less than SQLITE_NUM_STATIC_MUTEX, then a pointer +** to a static mutex is returned. There are a finite number +** of static mutexes. Static mutexes should not be passed +** to sqlite3_mutex_free(). The allocation of a static +** mutex cannot fail. +*/ +sqlite3_mutex *sqlite3_mutex_alloc(int id){ + struct RMutex *p; + if( id>0 ){ + if( id>sizeof(rmutexes)/sizeof(rmutexes[0]) ){ + p = 0; + }else{ + p = &rmutexes[id-1]; + } + }else{ + p = sqlite3_malloc( sizeof(*p) ); + if( p ){ + p->nRef = 0; + pthread_mutex_init(&p->mutex, 0); + } + } + return (sqlite3_mutex*)p; +} + +/* +** This routine deallocates a previously +** allocated mutex. SQLite is careful to deallocate every +** mutex that it allocates. +*/ +void sqlite3_mutex_free(sqlite3_mutex *pMutex){ + struct RMutex *p = (struct RMutex*)pMutex; + assert( p->nRef==0 ); + pthread_mutex_destroy(&p->mutex); + sqlite3_free(p); +} + +/* +** The sqlite3_mutex_enter() routine attempts to enter a +** mutex. If another thread is already within the mutex, +** sqlite3_mutex_enter() will return SQLITE_BUSY if blockFlag +** is zero, or it will block and wait for the other thread to +** exit if blockFlag is non-zero. Mutexes are recursive. The +** same thread can enter a single mutex multiple times. Each +** entrance must be matched with a corresponding exit before +** another thread is able to enter the mutex. +*/ +int sqlite3_mutex_enter(sqlite3_mutex *pMutex, int blockFlag){ + struct RMutex *p = (struct RMutex*)pMutex; + while(1){ + pthread_mutex_lock(&p->auxMutex); + if( p->nRef==0 ){ + p->nRef++; + p->owner = pthread_self(); + pthread_mutex_lock(&p->mainMutex); + pthread_mutex_unlock(&p->auxMutex); + return SQLITE_OK; + }else if( pthread_equal(p->owner, pthread_self()) ){ + p->nRef++; + pthread_mutex_unlock(&p->auxMutex); + return SQLITE_OK; + }else if( !blockFlag ){ + pthread_mutex_unlock(&p->auxMutex); + return SQLITE_BUSY; + }else{ + pthread_mutex_unlock(&p->auxMutex); + pthread_mutex_lock(&p->mainMutex); + pthread_mutex_unlock(&p->mainMutex); + } + } + /* NOTREACHED */ +} + +/* +** The sqlite3_mutex_exit() routine exits a mutex that was +** previously entered by the same thread. The behavior +** is undefined if the mutex is not currently entered or +** is not currently allocated. SQLite will never do either. +*/ +void sqlite3_mutex_leave(sqlite3_mutex *pMutex){ + struct RMutex *p = (struct RMutex*)pMutex; + pthread_mutex_lock(&p->auxMutex); + p->nRef--; + if( p->nRef<=0 ){ + pthread_mutex_unlock(&p->mainMutex); + } + pthread_mutex_unlock(&p->auxMutex); +} + +/* +** The sqlite3_mutex_serialize() routine is used to serialize +** execution of a subroutine. The subroutine given in the argument +** is invoked. But only one thread at a time is allowed to be +** running a subroutine using sqlite3_mutex_serialize(). +*/ +int sqlite3_mutex_serialize(void (*xCallback)(void*), void *pArg){ + sqlite3_mutex_enter(&serialMutex, 1); + xCallback(pArg); + sqlite3_mutex_leave(&serialMutex); +} +#endif /* non-recursive pthreads */ + +#endif /* !defined(SQLITE_MUTEX_APPDEF) */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index aaedaded90..c614201cf8 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -30,7 +30,7 @@ ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. ** -** @(#) $Id: sqlite.h.in,v 1.222 2007/08/15 11:28:56 drh Exp $ +** @(#) $Id: sqlite.h.in,v 1.223 2007/08/15 13:04:54 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -1020,10 +1020,7 @@ char *sqlite3_vmprintf(const char*, va_list); char *sqlite3_snprintf(int,char*,const char*, ...); /* -** CAPI3REF: Memory Allocation Functions -** -** The SQLite sources include a memory allocation subsystem -** that implements the interfaces shown here. +** CAPI3REF: Memory Allocation Subsystem ** ** The SQLite core uses these three routines for all of its own ** internal memory allocation needs. The default implementation @@ -1031,14 +1028,14 @@ char *sqlite3_snprintf(int,char*,const char*, ...); ** and free() provided by the standard C library. However, if ** SQLite is compiled with the following C preprocessor macro ** -**
SQLITE_OMIT_MEMORY_ALLOCATION+**
SQLITE_OMIT_MEMORY_ALLOCATION** ** then no implementation is provided for these routines by ** SQLite. The application that links against SQLite is ** expected to provide its own implementation. */ -void *sqlite3_malloc(int); -void *sqlite3_realloc(void*, int); +void *sqlite3_malloc(unsigned int); +void *sqlite3_realloc(void*, unsigned int); void sqlite3_free(void*); /* diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 21e5dbd3c0..019b0ef041 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -11,7 +11,7 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.585 2007/08/08 12:11:21 drh Exp $ +** @(#) $Id: sqliteInt.h,v 1.586 2007/08/15 13:04:54 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ @@ -441,6 +441,7 @@ struct Schema { ** consistently. */ struct sqlite3 { + sqlite3_vfs *pVfs; /* OS Interface */ int nDb; /* Number of backends currently in use */ Db *aDb; /* All backends */ int flags; /* Miscellanous flags. See below */