Index: third_party/sqlite/amalgamation/sqlite3.01.c |
diff --git a/third_party/sqlite/amalgamation/sqlite3.01.c b/third_party/sqlite/amalgamation/sqlite3.01.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..78a0aa3cf5cdbe2f562185e7b78b54cde75e00fc |
--- /dev/null |
+++ b/third_party/sqlite/amalgamation/sqlite3.01.c |
@@ -0,0 +1,22708 @@ |
+/************** Begin file mutex_unix.c **************************************/ |
+/* |
+** 2007 August 28 |
+** |
+** 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 pthreads |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+/* |
+** The code in this file is only used if we are compiling threadsafe |
+** under unix with pthreads. |
+** |
+** Note that this implementation requires a version of pthreads that |
+** supports recursive mutexes. |
+*/ |
+#ifdef SQLITE_MUTEX_PTHREADS |
+ |
+#include <pthread.h> |
+ |
+/* |
+** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields |
+** are necessary under two condidtions: (1) Debug builds and (2) using |
+** home-grown mutexes. Encapsulate these conditions into a single #define. |
+*/ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX) |
+# define SQLITE_MUTEX_NREF 1 |
+#else |
+# define SQLITE_MUTEX_NREF 0 |
+#endif |
+ |
+/* |
+** Each recursive mutex is an instance of the following structure. |
+*/ |
+struct sqlite3_mutex { |
+ pthread_mutex_t mutex; /* Mutex controlling the lock */ |
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) |
+ int id; /* Mutex type */ |
+#endif |
+#if SQLITE_MUTEX_NREF |
+ volatile int nRef; /* Number of entrances */ |
+ volatile pthread_t owner; /* Thread that is within this mutex */ |
+ int trace; /* True to trace changes */ |
+#endif |
+}; |
+#if SQLITE_MUTEX_NREF |
+#define SQLITE3_MUTEX_INITIALIZER {PTHREAD_MUTEX_INITIALIZER,0,0,(pthread_t)0,0} |
+#elif defined(SQLITE_ENABLE_API_ARMOR) |
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0 } |
+#else |
+#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } |
+#endif |
+ |
+/* |
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
+** intended for use only inside assert() statements. On some platforms, |
+** there might be race conditions that can cause these routines to |
+** deliver incorrect results. In particular, if pthread_equal() is |
+** not an atomic operation, then these routines might delivery |
+** incorrect results. On most platforms, pthread_equal() is a |
+** comparison of two integers and is therefore atomic. But we are |
+** told that HPUX is not such a platform. If so, then these routines |
+** will not always work correctly on HPUX. |
+** |
+** On those platforms where pthread_equal() is not atomic, SQLite |
+** should be compiled without -DSQLITE_DEBUG and with -DNDEBUG to |
+** make sure no assert() statements are evaluated and hence these |
+** routines are never called. |
+*/ |
+#if !defined(NDEBUG) || defined(SQLITE_DEBUG) |
+static int pthreadMutexHeld(sqlite3_mutex *p){ |
+ return (p->nRef!=0 && pthread_equal(p->owner, pthread_self())); |
+} |
+static int pthreadMutexNotheld(sqlite3_mutex *p){ |
+ return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0; |
+} |
+#endif |
+ |
+/* |
+** Try to provide a memory barrier operation, needed for initialization |
+** and also for the implementation of xShmBarrier in the VFS in cases |
+** where SQLite is compiled without mutexes. |
+*/ |
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ |
+#if defined(SQLITE_MEMORY_BARRIER) |
+ SQLITE_MEMORY_BARRIER; |
+#elif defined(__GNUC__) && GCC_VERSION>=4001000 |
+ __sync_synchronize(); |
+#endif |
+} |
+ |
+/* |
+** Initialize and deinitialize the mutex subsystem. |
+*/ |
+static int pthreadMutexInit(void){ return SQLITE_OK; } |
+static int pthreadMutexEnd(void){ return SQLITE_OK; } |
+ |
+/* |
+** 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 one of these integer constants: |
+** |
+** <ul> |
+** <li> SQLITE_MUTEX_FAST |
+** <li> SQLITE_MUTEX_RECURSIVE |
+** <li> SQLITE_MUTEX_STATIC_MASTER |
+** <li> SQLITE_MUTEX_STATIC_MEM |
+** <li> SQLITE_MUTEX_STATIC_OPEN |
+** <li> SQLITE_MUTEX_STATIC_PRNG |
+** <li> SQLITE_MUTEX_STATIC_LRU |
+** <li> SQLITE_MUTEX_STATIC_PMEM |
+** <li> SQLITE_MUTEX_STATIC_APP1 |
+** <li> SQLITE_MUTEX_STATIC_APP2 |
+** <li> SQLITE_MUTEX_STATIC_APP3 |
+** <li> SQLITE_MUTEX_STATIC_VFS1 |
+** <li> SQLITE_MUTEX_STATIC_VFS2 |
+** <li> SQLITE_MUTEX_STATIC_VFS3 |
+** </ul> |
+** |
+** The first two constants cause sqlite3_mutex_alloc() to create |
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
+** The mutex implementation does not need to make a distinction |
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does |
+** not want to. But SQLite will only request a recursive mutex in |
+** cases where it really needs one. If a faster non-recursive mutex |
+** implementation is available on the host platform, the mutex subsystem |
+** might return such a mutex in response to SQLITE_MUTEX_FAST. |
+** |
+** The other allowed parameters to sqlite3_mutex_alloc() each return |
+** a pointer to a static preexisting mutex. Six static mutexes are |
+** used by the current version of SQLite. Future versions of SQLite |
+** may add additional static mutexes. Static mutexes are for internal |
+** use by SQLite only. Applications that use SQLite mutexes should |
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or |
+** SQLITE_MUTEX_RECURSIVE. |
+** |
+** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST |
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() |
+** returns a different mutex on every call. But for the static |
+** mutex types, the same mutex is returned on every call that has |
+** the same type number. |
+*/ |
+static sqlite3_mutex *pthreadMutexAlloc(int iType){ |
+ static sqlite3_mutex staticMutexes[] = { |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER |
+ }; |
+ sqlite3_mutex *p; |
+ switch( iType ){ |
+ case SQLITE_MUTEX_RECURSIVE: { |
+ p = sqlite3MallocZero( sizeof(*p) ); |
+ if( p ){ |
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
+ /* If recursive mutexes are not available, we will have to |
+ ** build our own. See below. */ |
+ pthread_mutex_init(&p->mutex, 0); |
+#else |
+ /* Use a recursive mutex if it is available */ |
+ pthread_mutexattr_t recursiveAttr; |
+ pthread_mutexattr_init(&recursiveAttr); |
+ pthread_mutexattr_settype(&recursiveAttr, PTHREAD_MUTEX_RECURSIVE); |
+ pthread_mutex_init(&p->mutex, &recursiveAttr); |
+ pthread_mutexattr_destroy(&recursiveAttr); |
+#endif |
+ } |
+ break; |
+ } |
+ case SQLITE_MUTEX_FAST: { |
+ p = sqlite3MallocZero( sizeof(*p) ); |
+ if( p ){ |
+ pthread_mutex_init(&p->mutex, 0); |
+ } |
+ break; |
+ } |
+ default: { |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ if( iType-2<0 || iType-2>=ArraySize(staticMutexes) ){ |
+ (void)SQLITE_MISUSE_BKPT; |
+ return 0; |
+ } |
+#endif |
+ p = &staticMutexes[iType-2]; |
+ break; |
+ } |
+ } |
+#if SQLITE_MUTEX_NREF || defined(SQLITE_ENABLE_API_ARMOR) |
+ if( p ) p->id = iType; |
+#endif |
+ return p; |
+} |
+ |
+ |
+/* |
+** This routine deallocates a previously |
+** allocated mutex. SQLite is careful to deallocate every |
+** mutex that it allocates. |
+*/ |
+static void pthreadMutexFree(sqlite3_mutex *p){ |
+ assert( p->nRef==0 ); |
+#if SQLITE_ENABLE_API_ARMOR |
+ if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ) |
+#endif |
+ { |
+ pthread_mutex_destroy(&p->mutex); |
+ sqlite3_free(p); |
+ } |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ else{ |
+ (void)SQLITE_MISUSE_BKPT; |
+ } |
+#endif |
+} |
+ |
+/* |
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt |
+** to enter a mutex. If another thread is already within the mutex, |
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return |
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK |
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can |
+** be entered multiple times by the same thread. In such cases the, |
+** mutex must be exited an equal number of times before another thread |
+** can enter. If the same thread tries to enter any other kind of mutex |
+** more than once, the behavior is undefined. |
+*/ |
+static void pthreadMutexEnter(sqlite3_mutex *p){ |
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); |
+ |
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
+ /* If recursive mutexes are not available, then we have to grow |
+ ** our own. This implementation assumes that pthread_equal() |
+ ** is atomic - that it cannot be deceived into thinking self |
+ ** and p->owner are equal if p->owner changes between two values |
+ ** that are not equal to self while the comparison is taking place. |
+ ** This implementation also assumes a coherent cache - that |
+ ** separate processes cannot read different values from the same |
+ ** address at the same time. If either of these two conditions |
+ ** are not met, then the mutexes will fail and problems will result. |
+ */ |
+ { |
+ pthread_t self = pthread_self(); |
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){ |
+ p->nRef++; |
+ }else{ |
+ pthread_mutex_lock(&p->mutex); |
+ assert( p->nRef==0 ); |
+ p->owner = self; |
+ p->nRef = 1; |
+ } |
+ } |
+#else |
+ /* Use the built-in recursive mutexes if they are available. |
+ */ |
+ pthread_mutex_lock(&p->mutex); |
+#if SQLITE_MUTEX_NREF |
+ assert( p->nRef>0 || p->owner==0 ); |
+ p->owner = pthread_self(); |
+ p->nRef++; |
+#endif |
+#endif |
+ |
+#ifdef SQLITE_DEBUG |
+ if( p->trace ){ |
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
+ } |
+#endif |
+} |
+static int pthreadMutexTry(sqlite3_mutex *p){ |
+ int rc; |
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) ); |
+ |
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
+ /* If recursive mutexes are not available, then we have to grow |
+ ** our own. This implementation assumes that pthread_equal() |
+ ** is atomic - that it cannot be deceived into thinking self |
+ ** and p->owner are equal if p->owner changes between two values |
+ ** that are not equal to self while the comparison is taking place. |
+ ** This implementation also assumes a coherent cache - that |
+ ** separate processes cannot read different values from the same |
+ ** address at the same time. If either of these two conditions |
+ ** are not met, then the mutexes will fail and problems will result. |
+ */ |
+ { |
+ pthread_t self = pthread_self(); |
+ if( p->nRef>0 && pthread_equal(p->owner, self) ){ |
+ p->nRef++; |
+ rc = SQLITE_OK; |
+ }else if( pthread_mutex_trylock(&p->mutex)==0 ){ |
+ assert( p->nRef==0 ); |
+ p->owner = self; |
+ p->nRef = 1; |
+ rc = SQLITE_OK; |
+ }else{ |
+ rc = SQLITE_BUSY; |
+ } |
+ } |
+#else |
+ /* Use the built-in recursive mutexes if they are available. |
+ */ |
+ if( pthread_mutex_trylock(&p->mutex)==0 ){ |
+#if SQLITE_MUTEX_NREF |
+ p->owner = pthread_self(); |
+ p->nRef++; |
+#endif |
+ rc = SQLITE_OK; |
+ }else{ |
+ rc = SQLITE_BUSY; |
+ } |
+#endif |
+ |
+#ifdef SQLITE_DEBUG |
+ if( rc==SQLITE_OK && p->trace ){ |
+ printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
+ } |
+#endif |
+ return rc; |
+} |
+ |
+/* |
+** The sqlite3_mutex_leave() 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. |
+*/ |
+static void pthreadMutexLeave(sqlite3_mutex *p){ |
+ assert( pthreadMutexHeld(p) ); |
+#if SQLITE_MUTEX_NREF |
+ p->nRef--; |
+ if( p->nRef==0 ) p->owner = 0; |
+#endif |
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); |
+ |
+#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX |
+ if( p->nRef==0 ){ |
+ pthread_mutex_unlock(&p->mutex); |
+ } |
+#else |
+ pthread_mutex_unlock(&p->mutex); |
+#endif |
+ |
+#ifdef SQLITE_DEBUG |
+ if( p->trace ){ |
+ printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef); |
+ } |
+#endif |
+} |
+ |
+SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ |
+ static const sqlite3_mutex_methods sMutex = { |
+ pthreadMutexInit, |
+ pthreadMutexEnd, |
+ pthreadMutexAlloc, |
+ pthreadMutexFree, |
+ pthreadMutexEnter, |
+ pthreadMutexTry, |
+ pthreadMutexLeave, |
+#ifdef SQLITE_DEBUG |
+ pthreadMutexHeld, |
+ pthreadMutexNotheld |
+#else |
+ 0, |
+ 0 |
+#endif |
+ }; |
+ |
+ return &sMutex; |
+} |
+ |
+#endif /* SQLITE_MUTEX_PTHREADS */ |
+ |
+/************** End of mutex_unix.c ******************************************/ |
+/************** Begin file mutex_w32.c ***************************************/ |
+/* |
+** 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 Win32. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+#if SQLITE_OS_WIN |
+/* |
+** Include code that is common to all os_*.c files |
+*/ |
+/************** Include os_common.h in the middle of mutex_w32.c *************/ |
+/************** Begin file os_common.h ***************************************/ |
+/* |
+** 2004 May 22 |
+** |
+** 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 macros and a little bit of code that is common to |
+** all of the platform-specific files (os_*.c) and is #included into those |
+** files. |
+** |
+** This file should be #included by the os_*.c files only. It is not a |
+** general purpose header file. |
+*/ |
+#ifndef _OS_COMMON_H_ |
+#define _OS_COMMON_H_ |
+ |
+/* |
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG |
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the |
+** switch. The following code should catch this problem at compile-time. |
+*/ |
+#ifdef MEMORY_DEBUG |
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." |
+#endif |
+ |
+/* |
+** Macros for performance tracing. Normally turned off. Only works |
+** on i486 hardware. |
+*/ |
+#ifdef SQLITE_PERFORMANCE_TRACE |
+ |
+/* |
+** hwtime.h contains inline assembler code for implementing |
+** high-performance timing routines. |
+*/ |
+/************** Include hwtime.h in the middle of os_common.h ****************/ |
+/************** Begin file hwtime.h ******************************************/ |
+/* |
+** 2008 May 27 |
+** |
+** 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 inline asm code for retrieving "high-performance" |
+** counters for x86 class CPUs. |
+*/ |
+#ifndef _HWTIME_H_ |
+#define _HWTIME_H_ |
+ |
+/* |
+** The following routine only works on pentium-class (or newer) processors. |
+** It uses the RDTSC opcode to read the cycle count value out of the |
+** processor and returns that value. This can be used for high-res |
+** profiling. |
+*/ |
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \ |
+ (defined(i386) || defined(__i386__) || defined(_M_IX86)) |
+ |
+ #if defined(__GNUC__) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned int lo, hi; |
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
+ return (sqlite_uint64)hi << 32 | lo; |
+ } |
+ |
+ #elif defined(_MSC_VER) |
+ |
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ |
+ __asm { |
+ rdtsc |
+ ret ; return value at EDX:EAX |
+ } |
+ } |
+ |
+ #endif |
+ |
+#elif (defined(__GNUC__) && defined(__x86_64__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long val; |
+ __asm__ __volatile__ ("rdtsc" : "=A" (val)); |
+ return val; |
+ } |
+ |
+#elif (defined(__GNUC__) && defined(__ppc__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long long retval; |
+ unsigned long junk; |
+ __asm__ __volatile__ ("\n\ |
+ 1: mftbu %1\n\ |
+ mftb %L0\n\ |
+ mftbu %0\n\ |
+ cmpw %0,%1\n\ |
+ bne 1b" |
+ : "=r" (retval), "=r" (junk)); |
+ return retval; |
+ } |
+ |
+#else |
+ |
+ #error Need implementation of sqlite3Hwtime() for your platform. |
+ |
+ /* |
+ ** To compile without implementing sqlite3Hwtime() for your platform, |
+ ** you can remove the above #error and use the following |
+ ** stub function. You will lose timing support for many |
+ ** of the debugging and testing utilities, but it should at |
+ ** least compile and run. |
+ */ |
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } |
+ |
+#endif |
+ |
+#endif /* !defined(_HWTIME_H_) */ |
+ |
+/************** End of hwtime.h **********************************************/ |
+/************** Continuing where we left off in os_common.h ******************/ |
+ |
+static sqlite_uint64 g_start; |
+static sqlite_uint64 g_elapsed; |
+#define TIMER_START g_start=sqlite3Hwtime() |
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start |
+#define TIMER_ELAPSED g_elapsed |
+#else |
+#define TIMER_START |
+#define TIMER_END |
+#define TIMER_ELAPSED ((sqlite_uint64)0) |
+#endif |
+ |
+/* |
+** If we compile with the SQLITE_TEST macro set, then the following block |
+** of code will give us the ability to simulate a disk I/O error. This |
+** is used for testing the I/O recovery logic. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ |
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ |
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ |
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ |
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */ |
+SQLITE_API int sqlite3_diskfull_pending = 0; |
+SQLITE_API int sqlite3_diskfull = 0; |
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) |
+#define SimulateIOError(CODE) \ |
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ |
+ || sqlite3_io_error_pending-- == 1 ) \ |
+ { local_ioerr(); CODE; } |
+static void local_ioerr(){ |
+ IOTRACE(("IOERR\n")); |
+ sqlite3_io_error_hit++; |
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; |
+} |
+#define SimulateDiskfullError(CODE) \ |
+ if( sqlite3_diskfull_pending ){ \ |
+ if( sqlite3_diskfull_pending == 1 ){ \ |
+ local_ioerr(); \ |
+ sqlite3_diskfull = 1; \ |
+ sqlite3_io_error_hit = 1; \ |
+ CODE; \ |
+ }else{ \ |
+ sqlite3_diskfull_pending--; \ |
+ } \ |
+ } |
+#else |
+#define SimulateIOErrorBenign(X) |
+#define SimulateIOError(A) |
+#define SimulateDiskfullError(A) |
+#endif |
+ |
+/* |
+** When testing, keep a count of the number of open files. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_open_file_count = 0; |
+#define OpenCounter(X) sqlite3_open_file_count+=(X) |
+#else |
+#define OpenCounter(X) |
+#endif |
+ |
+#endif /* !defined(_OS_COMMON_H_) */ |
+ |
+/************** End of os_common.h *******************************************/ |
+/************** Continuing where we left off in mutex_w32.c ******************/ |
+ |
+/* |
+** Include the header file for the Windows VFS. |
+*/ |
+/************** Include os_win.h in the middle of mutex_w32.c ****************/ |
+/************** Begin file os_win.h ******************************************/ |
+/* |
+** 2013 November 25 |
+** |
+** 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 code that is specific to Windows. |
+*/ |
+#ifndef _OS_WIN_H_ |
+#define _OS_WIN_H_ |
+ |
+/* |
+** Include the primary Windows SDK header file. |
+*/ |
+#include "windows.h" |
+ |
+#ifdef __CYGWIN__ |
+# include <sys/cygwin.h> |
+# include <errno.h> /* amalgamator: dontcache */ |
+#endif |
+ |
+/* |
+** Determine if we are dealing with Windows NT. |
+** |
+** We ought to be able to determine if we are compiling for Windows 9x or |
+** Windows NT using the _WIN32_WINNT macro as follows: |
+** |
+** #if defined(_WIN32_WINNT) |
+** # define SQLITE_OS_WINNT 1 |
+** #else |
+** # define SQLITE_OS_WINNT 0 |
+** #endif |
+** |
+** However, Visual Studio 2005 does not set _WIN32_WINNT by default, as |
+** it ought to, so the above test does not work. We'll just assume that |
+** everything is Windows NT unless the programmer explicitly says otherwise |
+** by setting SQLITE_OS_WINNT to 0. |
+*/ |
+#if SQLITE_OS_WIN && !defined(SQLITE_OS_WINNT) |
+# define SQLITE_OS_WINNT 1 |
+#endif |
+ |
+/* |
+** Determine if we are dealing with Windows CE - which has a much reduced |
+** API. |
+*/ |
+#if defined(_WIN32_WCE) |
+# define SQLITE_OS_WINCE 1 |
+#else |
+# define SQLITE_OS_WINCE 0 |
+#endif |
+ |
+/* |
+** Determine if we are dealing with WinRT, which provides only a subset of |
+** the full Win32 API. |
+*/ |
+#if !defined(SQLITE_OS_WINRT) |
+# define SQLITE_OS_WINRT 0 |
+#endif |
+ |
+/* |
+** For WinCE, some API function parameters do not appear to be declared as |
+** volatile. |
+*/ |
+#if SQLITE_OS_WINCE |
+# define SQLITE_WIN32_VOLATILE |
+#else |
+# define SQLITE_WIN32_VOLATILE volatile |
+#endif |
+ |
+/* |
+** For some Windows sub-platforms, the _beginthreadex() / _endthreadex() |
+** functions are not available (e.g. those not using MSVC, Cygwin, etc). |
+*/ |
+#if SQLITE_OS_WIN && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ |
+ SQLITE_THREADSAFE>0 && !defined(__CYGWIN__) |
+# define SQLITE_OS_WIN_THREADS 1 |
+#else |
+# define SQLITE_OS_WIN_THREADS 0 |
+#endif |
+ |
+#endif /* _OS_WIN_H_ */ |
+ |
+/************** End of os_win.h **********************************************/ |
+/************** Continuing where we left off in mutex_w32.c ******************/ |
+#endif |
+ |
+/* |
+** The code in this file is only used if we are compiling multithreaded |
+** on a Win32 system. |
+*/ |
+#ifdef SQLITE_MUTEX_W32 |
+ |
+/* |
+** Each recursive mutex is an instance of the following structure. |
+*/ |
+struct sqlite3_mutex { |
+ CRITICAL_SECTION mutex; /* Mutex controlling the lock */ |
+ int id; /* Mutex type */ |
+#ifdef SQLITE_DEBUG |
+ volatile int nRef; /* Number of enterances */ |
+ volatile DWORD owner; /* Thread holding this mutex */ |
+ volatile int trace; /* True to trace changes */ |
+#endif |
+}; |
+ |
+/* |
+** These are the initializer values used when declaring a "static" mutex |
+** on Win32. It should be noted that all mutexes require initialization |
+** on the Win32 platform. |
+*/ |
+#define SQLITE_W32_MUTEX_INITIALIZER { 0 } |
+ |
+#ifdef SQLITE_DEBUG |
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0, \ |
+ 0L, (DWORD)0, 0 } |
+#else |
+#define SQLITE3_MUTEX_INITIALIZER { SQLITE_W32_MUTEX_INITIALIZER, 0 } |
+#endif |
+ |
+#ifdef SQLITE_DEBUG |
+/* |
+** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are |
+** intended for use only inside assert() statements. |
+*/ |
+static int winMutexHeld(sqlite3_mutex *p){ |
+ return p->nRef!=0 && p->owner==GetCurrentThreadId(); |
+} |
+ |
+static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){ |
+ return p->nRef==0 || p->owner!=tid; |
+} |
+ |
+static int winMutexNotheld(sqlite3_mutex *p){ |
+ DWORD tid = GetCurrentThreadId(); |
+ return winMutexNotheld2(p, tid); |
+} |
+#endif |
+ |
+/* |
+** Try to provide a memory barrier operation, needed for initialization |
+** and also for the xShmBarrier method of the VFS in cases when SQLite is |
+** compiled without mutexes (SQLITE_THREADSAFE=0). |
+*/ |
+SQLITE_PRIVATE void sqlite3MemoryBarrier(void){ |
+#if defined(SQLITE_MEMORY_BARRIER) |
+ SQLITE_MEMORY_BARRIER; |
+#elif defined(__GNUC__) |
+ __sync_synchronize(); |
+#elif !defined(SQLITE_DISABLE_INTRINSIC) && \ |
+ defined(_MSC_VER) && _MSC_VER>=1300 |
+ _ReadWriteBarrier(); |
+#elif defined(MemoryBarrier) |
+ MemoryBarrier(); |
+#endif |
+} |
+ |
+/* |
+** Initialize and deinitialize the mutex subsystem. |
+*/ |
+static sqlite3_mutex winMutex_staticMutexes[] = { |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER, |
+ SQLITE3_MUTEX_INITIALIZER |
+}; |
+ |
+static int winMutex_isInit = 0; |
+static int winMutex_isNt = -1; /* <0 means "need to query" */ |
+ |
+/* As the winMutexInit() and winMutexEnd() functions are called as part |
+** of the sqlite3_initialize() and sqlite3_shutdown() processing, the |
+** "interlocked" magic used here is probably not strictly necessary. |
+*/ |
+static LONG SQLITE_WIN32_VOLATILE winMutex_lock = 0; |
+ |
+SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void); /* os_win.c */ |
+SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */ |
+ |
+static int winMutexInit(void){ |
+ /* The first to increment to 1 does actual initialization */ |
+ if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ |
+ int i; |
+ for(i=0; i<ArraySize(winMutex_staticMutexes); i++){ |
+#if SQLITE_OS_WINRT |
+ InitializeCriticalSectionEx(&winMutex_staticMutexes[i].mutex, 0, 0); |
+#else |
+ InitializeCriticalSection(&winMutex_staticMutexes[i].mutex); |
+#endif |
+ } |
+ winMutex_isInit = 1; |
+ }else{ |
+ /* Another thread is (in the process of) initializing the static |
+ ** mutexes */ |
+ while( !winMutex_isInit ){ |
+ sqlite3_win32_sleep(1); |
+ } |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+static int winMutexEnd(void){ |
+ /* The first to decrement to 0 does actual shutdown |
+ ** (which should be the last to shutdown.) */ |
+ if( InterlockedCompareExchange(&winMutex_lock, 0, 1)==1 ){ |
+ if( winMutex_isInit==1 ){ |
+ int i; |
+ for(i=0; i<ArraySize(winMutex_staticMutexes); i++){ |
+ DeleteCriticalSection(&winMutex_staticMutexes[i].mutex); |
+ } |
+ winMutex_isInit = 0; |
+ } |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** 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 one of these integer constants: |
+** |
+** <ul> |
+** <li> SQLITE_MUTEX_FAST |
+** <li> SQLITE_MUTEX_RECURSIVE |
+** <li> SQLITE_MUTEX_STATIC_MASTER |
+** <li> SQLITE_MUTEX_STATIC_MEM |
+** <li> SQLITE_MUTEX_STATIC_OPEN |
+** <li> SQLITE_MUTEX_STATIC_PRNG |
+** <li> SQLITE_MUTEX_STATIC_LRU |
+** <li> SQLITE_MUTEX_STATIC_PMEM |
+** <li> SQLITE_MUTEX_STATIC_APP1 |
+** <li> SQLITE_MUTEX_STATIC_APP2 |
+** <li> SQLITE_MUTEX_STATIC_APP3 |
+** <li> SQLITE_MUTEX_STATIC_VFS1 |
+** <li> SQLITE_MUTEX_STATIC_VFS2 |
+** <li> SQLITE_MUTEX_STATIC_VFS3 |
+** </ul> |
+** |
+** The first two constants cause sqlite3_mutex_alloc() to create |
+** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE |
+** is used but not necessarily so when SQLITE_MUTEX_FAST is used. |
+** The mutex implementation does not need to make a distinction |
+** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does |
+** not want to. But SQLite will only request a recursive mutex in |
+** cases where it really needs one. If a faster non-recursive mutex |
+** implementation is available on the host platform, the mutex subsystem |
+** might return such a mutex in response to SQLITE_MUTEX_FAST. |
+** |
+** The other allowed parameters to sqlite3_mutex_alloc() each return |
+** a pointer to a static preexisting mutex. Six static mutexes are |
+** used by the current version of SQLite. Future versions of SQLite |
+** may add additional static mutexes. Static mutexes are for internal |
+** use by SQLite only. Applications that use SQLite mutexes should |
+** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or |
+** SQLITE_MUTEX_RECURSIVE. |
+** |
+** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST |
+** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() |
+** returns a different mutex on every call. But for the static |
+** mutex types, the same mutex is returned on every call that has |
+** the same type number. |
+*/ |
+static sqlite3_mutex *winMutexAlloc(int iType){ |
+ sqlite3_mutex *p; |
+ |
+ switch( iType ){ |
+ case SQLITE_MUTEX_FAST: |
+ case SQLITE_MUTEX_RECURSIVE: { |
+ p = sqlite3MallocZero( sizeof(*p) ); |
+ if( p ){ |
+ p->id = iType; |
+#ifdef SQLITE_DEBUG |
+#ifdef SQLITE_WIN32_MUTEX_TRACE_DYNAMIC |
+ p->trace = 1; |
+#endif |
+#endif |
+#if SQLITE_OS_WINRT |
+ InitializeCriticalSectionEx(&p->mutex, 0, 0); |
+#else |
+ InitializeCriticalSection(&p->mutex); |
+#endif |
+ } |
+ break; |
+ } |
+ default: { |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ if( iType-2<0 || iType-2>=ArraySize(winMutex_staticMutexes) ){ |
+ (void)SQLITE_MISUSE_BKPT; |
+ return 0; |
+ } |
+#endif |
+ p = &winMutex_staticMutexes[iType-2]; |
+ p->id = iType; |
+#ifdef SQLITE_DEBUG |
+#ifdef SQLITE_WIN32_MUTEX_TRACE_STATIC |
+ p->trace = 1; |
+#endif |
+#endif |
+ break; |
+ } |
+ } |
+ return p; |
+} |
+ |
+ |
+/* |
+** This routine deallocates a previously |
+** allocated mutex. SQLite is careful to deallocate every |
+** mutex that it allocates. |
+*/ |
+static void winMutexFree(sqlite3_mutex *p){ |
+ assert( p ); |
+ assert( p->nRef==0 && p->owner==0 ); |
+ if( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE ){ |
+ DeleteCriticalSection(&p->mutex); |
+ sqlite3_free(p); |
+ }else{ |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ (void)SQLITE_MISUSE_BKPT; |
+#endif |
+ } |
+} |
+ |
+/* |
+** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt |
+** to enter a mutex. If another thread is already within the mutex, |
+** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return |
+** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK |
+** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can |
+** be entered multiple times by the same thread. In such cases the, |
+** mutex must be exited an equal number of times before another thread |
+** can enter. If the same thread tries to enter any other kind of mutex |
+** more than once, the behavior is undefined. |
+*/ |
+static void winMutexEnter(sqlite3_mutex *p){ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
+ DWORD tid = GetCurrentThreadId(); |
+#endif |
+#ifdef SQLITE_DEBUG |
+ assert( p ); |
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); |
+#else |
+ assert( p ); |
+#endif |
+ assert( winMutex_isInit==1 ); |
+ EnterCriticalSection(&p->mutex); |
+#ifdef SQLITE_DEBUG |
+ assert( p->nRef>0 || p->owner==0 ); |
+ p->owner = tid; |
+ p->nRef++; |
+ if( p->trace ){ |
+ OSTRACE(("ENTER-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n", |
+ tid, p, p->trace, p->nRef)); |
+ } |
+#endif |
+} |
+ |
+static int winMutexTry(sqlite3_mutex *p){ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
+ DWORD tid = GetCurrentThreadId(); |
+#endif |
+ int rc = SQLITE_BUSY; |
+ assert( p ); |
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) ); |
+ /* |
+ ** The sqlite3_mutex_try() routine is very rarely used, and when it |
+ ** is used it is merely an optimization. So it is OK for it to always |
+ ** fail. |
+ ** |
+ ** The TryEnterCriticalSection() interface is only available on WinNT. |
+ ** And some windows compilers complain if you try to use it without |
+ ** first doing some #defines that prevent SQLite from building on Win98. |
+ ** For that reason, we will omit this optimization for now. See |
+ ** ticket #2685. |
+ */ |
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0400 |
+ assert( winMutex_isInit==1 ); |
+ assert( winMutex_isNt>=-1 && winMutex_isNt<=1 ); |
+ if( winMutex_isNt<0 ){ |
+ winMutex_isNt = sqlite3_win32_is_nt(); |
+ } |
+ assert( winMutex_isNt==0 || winMutex_isNt==1 ); |
+ if( winMutex_isNt && TryEnterCriticalSection(&p->mutex) ){ |
+#ifdef SQLITE_DEBUG |
+ p->owner = tid; |
+ p->nRef++; |
+#endif |
+ rc = SQLITE_OK; |
+ } |
+#else |
+ UNUSED_PARAMETER(p); |
+#endif |
+#ifdef SQLITE_DEBUG |
+ if( p->trace ){ |
+ OSTRACE(("TRY-MUTEX tid=%lu, mutex=%p (%d), owner=%lu, nRef=%d, rc=%s\n", |
+ tid, p, p->trace, p->owner, p->nRef, sqlite3ErrName(rc))); |
+ } |
+#endif |
+ return rc; |
+} |
+ |
+/* |
+** The sqlite3_mutex_leave() 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. |
+*/ |
+static void winMutexLeave(sqlite3_mutex *p){ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
+ DWORD tid = GetCurrentThreadId(); |
+#endif |
+ assert( p ); |
+#ifdef SQLITE_DEBUG |
+ assert( p->nRef>0 ); |
+ assert( p->owner==tid ); |
+ p->nRef--; |
+ if( p->nRef==0 ) p->owner = 0; |
+ assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE ); |
+#endif |
+ assert( winMutex_isInit==1 ); |
+ LeaveCriticalSection(&p->mutex); |
+#ifdef SQLITE_DEBUG |
+ if( p->trace ){ |
+ OSTRACE(("LEAVE-MUTEX tid=%lu, mutex=%p (%d), nRef=%d\n", |
+ tid, p, p->trace, p->nRef)); |
+ } |
+#endif |
+} |
+ |
+SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){ |
+ static const sqlite3_mutex_methods sMutex = { |
+ winMutexInit, |
+ winMutexEnd, |
+ winMutexAlloc, |
+ winMutexFree, |
+ winMutexEnter, |
+ winMutexTry, |
+ winMutexLeave, |
+#ifdef SQLITE_DEBUG |
+ winMutexHeld, |
+ winMutexNotheld |
+#else |
+ 0, |
+ 0 |
+#endif |
+ }; |
+ return &sMutex; |
+} |
+ |
+#endif /* SQLITE_MUTEX_W32 */ |
+ |
+/************** End of mutex_w32.c *******************************************/ |
+/************** Begin file malloc.c ******************************************/ |
+/* |
+** 2001 September 15 |
+** |
+** 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. |
+** |
+************************************************************************* |
+** |
+** Memory allocation functions used throughout sqlite. |
+*/ |
+/* #include "sqliteInt.h" */ |
+/* #include <stdarg.h> */ |
+ |
+/* |
+** Attempt to release up to n bytes of non-essential memory currently |
+** held by SQLite. An example of non-essential memory is memory used to |
+** cache database pages that are not currently in use. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_release_memory(int n){ |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+ return sqlite3PcacheReleaseMemory(n); |
+#else |
+ /* IMPLEMENTATION-OF: R-34391-24921 The sqlite3_release_memory() routine |
+ ** is a no-op returning zero if SQLite is not compiled with |
+ ** SQLITE_ENABLE_MEMORY_MANAGEMENT. */ |
+ UNUSED_PARAMETER(n); |
+ return 0; |
+#endif |
+} |
+ |
+/* |
+** An instance of the following object records the location of |
+** each unused scratch buffer. |
+*/ |
+typedef struct ScratchFreeslot { |
+ struct ScratchFreeslot *pNext; /* Next unused scratch buffer */ |
+} ScratchFreeslot; |
+ |
+/* |
+** State information local to the memory allocation subsystem. |
+*/ |
+static SQLITE_WSD struct Mem0Global { |
+ sqlite3_mutex *mutex; /* Mutex to serialize access */ |
+ sqlite3_int64 alarmThreshold; /* The soft heap limit */ |
+ |
+ /* |
+ ** Pointers to the end of sqlite3GlobalConfig.pScratch memory |
+ ** (so that a range test can be used to determine if an allocation |
+ ** being freed came from pScratch) and a pointer to the list of |
+ ** unused scratch allocations. |
+ */ |
+ void *pScratchEnd; |
+ ScratchFreeslot *pScratchFree; |
+ u32 nScratchFree; |
+ |
+ /* |
+ ** True if heap is nearly "full" where "full" is defined by the |
+ ** sqlite3_soft_heap_limit() setting. |
+ */ |
+ int nearlyFull; |
+} mem0 = { 0, 0, 0, 0, 0, 0 }; |
+ |
+#define mem0 GLOBAL(struct Mem0Global, mem0) |
+ |
+/* |
+** Return the memory allocator mutex. sqlite3_status() needs it. |
+*/ |
+SQLITE_PRIVATE sqlite3_mutex *sqlite3MallocMutex(void){ |
+ return mem0.mutex; |
+} |
+ |
+#ifndef SQLITE_OMIT_DEPRECATED |
+/* |
+** Deprecated external interface. It used to set an alarm callback |
+** that was invoked when memory usage grew too large. Now it is a |
+** no-op. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_memory_alarm( |
+ void(*xCallback)(void *pArg, sqlite3_int64 used,int N), |
+ void *pArg, |
+ sqlite3_int64 iThreshold |
+){ |
+ (void)xCallback; |
+ (void)pArg; |
+ (void)iThreshold; |
+ return SQLITE_OK; |
+} |
+#endif |
+ |
+/* |
+** Set the soft heap-size limit for the library. Passing a zero or |
+** negative value indicates no limit. |
+*/ |
+SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_soft_heap_limit64(sqlite3_int64 n){ |
+ sqlite3_int64 priorLimit; |
+ sqlite3_int64 excess; |
+ sqlite3_int64 nUsed; |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ int rc = sqlite3_initialize(); |
+ if( rc ) return -1; |
+#endif |
+ sqlite3_mutex_enter(mem0.mutex); |
+ priorLimit = mem0.alarmThreshold; |
+ if( n<0 ){ |
+ sqlite3_mutex_leave(mem0.mutex); |
+ return priorLimit; |
+ } |
+ mem0.alarmThreshold = n; |
+ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
+ mem0.nearlyFull = (n>0 && n<=nUsed); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ excess = sqlite3_memory_used() - n; |
+ if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff)); |
+ return priorLimit; |
+} |
+SQLITE_API void SQLITE_STDCALL sqlite3_soft_heap_limit(int n){ |
+ if( n<0 ) n = 0; |
+ sqlite3_soft_heap_limit64(n); |
+} |
+ |
+/* |
+** Initialize the memory allocation subsystem. |
+*/ |
+SQLITE_PRIVATE int sqlite3MallocInit(void){ |
+ int rc; |
+ if( sqlite3GlobalConfig.m.xMalloc==0 ){ |
+ sqlite3MemSetDefault(); |
+ } |
+ memset(&mem0, 0, sizeof(mem0)); |
+ mem0.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); |
+ if( sqlite3GlobalConfig.pScratch && sqlite3GlobalConfig.szScratch>=100 |
+ && sqlite3GlobalConfig.nScratch>0 ){ |
+ int i, n, sz; |
+ ScratchFreeslot *pSlot; |
+ sz = ROUNDDOWN8(sqlite3GlobalConfig.szScratch); |
+ sqlite3GlobalConfig.szScratch = sz; |
+ pSlot = (ScratchFreeslot*)sqlite3GlobalConfig.pScratch; |
+ n = sqlite3GlobalConfig.nScratch; |
+ mem0.pScratchFree = pSlot; |
+ mem0.nScratchFree = n; |
+ for(i=0; i<n-1; i++){ |
+ pSlot->pNext = (ScratchFreeslot*)(sz+(char*)pSlot); |
+ pSlot = pSlot->pNext; |
+ } |
+ pSlot->pNext = 0; |
+ mem0.pScratchEnd = (void*)&pSlot[1]; |
+ }else{ |
+ mem0.pScratchEnd = 0; |
+ sqlite3GlobalConfig.pScratch = 0; |
+ sqlite3GlobalConfig.szScratch = 0; |
+ sqlite3GlobalConfig.nScratch = 0; |
+ } |
+ if( sqlite3GlobalConfig.pPage==0 || sqlite3GlobalConfig.szPage<512 |
+ || sqlite3GlobalConfig.nPage<=0 ){ |
+ sqlite3GlobalConfig.pPage = 0; |
+ sqlite3GlobalConfig.szPage = 0; |
+ } |
+ rc = sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData); |
+ if( rc!=SQLITE_OK ) memset(&mem0, 0, sizeof(mem0)); |
+ return rc; |
+} |
+ |
+/* |
+** Return true if the heap is currently under memory pressure - in other |
+** words if the amount of heap used is close to the limit set by |
+** sqlite3_soft_heap_limit(). |
+*/ |
+SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){ |
+ return mem0.nearlyFull; |
+} |
+ |
+/* |
+** Deinitialize the memory allocation subsystem. |
+*/ |
+SQLITE_PRIVATE void sqlite3MallocEnd(void){ |
+ if( sqlite3GlobalConfig.m.xShutdown ){ |
+ sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData); |
+ } |
+ memset(&mem0, 0, sizeof(mem0)); |
+} |
+ |
+/* |
+** Return the amount of memory currently checked out. |
+*/ |
+SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_used(void){ |
+ sqlite3_int64 res, mx; |
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, 0); |
+ return res; |
+} |
+ |
+/* |
+** 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. |
+*/ |
+SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_memory_highwater(int resetFlag){ |
+ sqlite3_int64 res, mx; |
+ sqlite3_status64(SQLITE_STATUS_MEMORY_USED, &res, &mx, resetFlag); |
+ return mx; |
+} |
+ |
+/* |
+** Trigger the alarm |
+*/ |
+static void sqlite3MallocAlarm(int nByte){ |
+ if( mem0.alarmThreshold<=0 ) return; |
+ sqlite3_mutex_leave(mem0.mutex); |
+ sqlite3_release_memory(nByte); |
+ sqlite3_mutex_enter(mem0.mutex); |
+} |
+ |
+/* |
+** Do a memory allocation with statistics and alarms. Assume the |
+** lock is already held. |
+*/ |
+static int mallocWithAlarm(int n, void **pp){ |
+ int nFull; |
+ void *p; |
+ assert( sqlite3_mutex_held(mem0.mutex) ); |
+ nFull = sqlite3GlobalConfig.m.xRoundup(n); |
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n); |
+ if( mem0.alarmThreshold>0 ){ |
+ sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); |
+ if( nUsed >= mem0.alarmThreshold - nFull ){ |
+ mem0.nearlyFull = 1; |
+ sqlite3MallocAlarm(nFull); |
+ }else{ |
+ mem0.nearlyFull = 0; |
+ } |
+ } |
+ p = sqlite3GlobalConfig.m.xMalloc(nFull); |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+ if( p==0 && mem0.alarmThreshold>0 ){ |
+ sqlite3MallocAlarm(nFull); |
+ p = sqlite3GlobalConfig.m.xMalloc(nFull); |
+ } |
+#endif |
+ if( p ){ |
+ nFull = sqlite3MallocSize(p); |
+ sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nFull); |
+ sqlite3StatusUp(SQLITE_STATUS_MALLOC_COUNT, 1); |
+ } |
+ *pp = p; |
+ return nFull; |
+} |
+ |
+/* |
+** Allocate memory. This routine is like sqlite3_malloc() except that it |
+** assumes the memory subsystem has already been initialized. |
+*/ |
+SQLITE_PRIVATE void *sqlite3Malloc(u64 n){ |
+ void *p; |
+ if( n==0 || n>=0x7fffff00 ){ |
+ /* A memory allocation of a number of bytes which is near the maximum |
+ ** signed integer value might cause an integer overflow inside of the |
+ ** xMalloc(). Hence we limit the maximum size to 0x7fffff00, giving |
+ ** 255 bytes of overhead. SQLite itself will never use anything near |
+ ** this amount. The only way to reach the limit is with sqlite3_malloc() */ |
+ p = 0; |
+ }else if( sqlite3GlobalConfig.bMemstat ){ |
+ sqlite3_mutex_enter(mem0.mutex); |
+ mallocWithAlarm((int)n, &p); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ p = sqlite3GlobalConfig.m.xMalloc((int)n); |
+ } |
+ assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-11148-40995 */ |
+ return p; |
+} |
+ |
+/* |
+** This version of the memory allocation is for use by the application. |
+** First make sure the memory subsystem is initialized, then do the |
+** allocation. |
+*/ |
+SQLITE_API void *SQLITE_STDCALL sqlite3_malloc(int n){ |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ return n<=0 ? 0 : sqlite3Malloc(n); |
+} |
+SQLITE_API void *SQLITE_STDCALL sqlite3_malloc64(sqlite3_uint64 n){ |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ return sqlite3Malloc(n); |
+} |
+ |
+/* |
+** Each thread may only have a single outstanding allocation from |
+** xScratchMalloc(). We verify this constraint in the single-threaded |
+** case by setting scratchAllocOut to 1 when an allocation |
+** is outstanding clearing it when the allocation is freed. |
+*/ |
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) |
+static int scratchAllocOut = 0; |
+#endif |
+ |
+ |
+/* |
+** Allocate memory that is to be used and released right away. |
+** This routine is similar to alloca() in that it is not intended |
+** for situations where the memory might be held long-term. This |
+** routine is intended to get memory to old large transient data |
+** structures that would not normally fit on the stack of an |
+** embedded processor. |
+*/ |
+SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){ |
+ void *p; |
+ assert( n>0 ); |
+ |
+ sqlite3_mutex_enter(mem0.mutex); |
+ sqlite3StatusHighwater(SQLITE_STATUS_SCRATCH_SIZE, n); |
+ if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){ |
+ p = mem0.pScratchFree; |
+ mem0.pScratchFree = mem0.pScratchFree->pNext; |
+ mem0.nScratchFree--; |
+ sqlite3StatusUp(SQLITE_STATUS_SCRATCH_USED, 1); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ sqlite3_mutex_leave(mem0.mutex); |
+ p = sqlite3Malloc(n); |
+ if( sqlite3GlobalConfig.bMemstat && p ){ |
+ sqlite3_mutex_enter(mem0.mutex); |
+ sqlite3StatusUp(SQLITE_STATUS_SCRATCH_OVERFLOW, sqlite3MallocSize(p)); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ } |
+ sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH); |
+ } |
+ assert( sqlite3_mutex_notheld(mem0.mutex) ); |
+ |
+ |
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) |
+ /* EVIDENCE-OF: R-12970-05880 SQLite will not use more than one scratch |
+ ** buffers per thread. |
+ ** |
+ ** This can only be checked in single-threaded mode. |
+ */ |
+ assert( scratchAllocOut==0 ); |
+ if( p ) scratchAllocOut++; |
+#endif |
+ |
+ return p; |
+} |
+SQLITE_PRIVATE void sqlite3ScratchFree(void *p){ |
+ if( p ){ |
+ |
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG) |
+ /* Verify that no more than two scratch allocation per thread |
+ ** is outstanding at one time. (This is only checked in the |
+ ** single-threaded case since checking in the multi-threaded case |
+ ** would be much more complicated.) */ |
+ assert( scratchAllocOut>=1 && scratchAllocOut<=2 ); |
+ scratchAllocOut--; |
+#endif |
+ |
+ if( SQLITE_WITHIN(p, sqlite3GlobalConfig.pScratch, mem0.pScratchEnd) ){ |
+ /* Release memory from the SQLITE_CONFIG_SCRATCH allocation */ |
+ ScratchFreeslot *pSlot; |
+ pSlot = (ScratchFreeslot*)p; |
+ sqlite3_mutex_enter(mem0.mutex); |
+ pSlot->pNext = mem0.pScratchFree; |
+ mem0.pScratchFree = pSlot; |
+ mem0.nScratchFree++; |
+ assert( mem0.nScratchFree <= (u32)sqlite3GlobalConfig.nScratch ); |
+ sqlite3StatusDown(SQLITE_STATUS_SCRATCH_USED, 1); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ /* Release memory back to the heap */ |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) ); |
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_SCRATCH) ); |
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
+ if( sqlite3GlobalConfig.bMemstat ){ |
+ int iSize = sqlite3MallocSize(p); |
+ sqlite3_mutex_enter(mem0.mutex); |
+ sqlite3StatusDown(SQLITE_STATUS_SCRATCH_OVERFLOW, iSize); |
+ sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, iSize); |
+ sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); |
+ sqlite3GlobalConfig.m.xFree(p); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ sqlite3GlobalConfig.m.xFree(p); |
+ } |
+ } |
+ } |
+} |
+ |
+/* |
+** TRUE if p is a lookaside memory allocation from db |
+*/ |
+#ifndef SQLITE_OMIT_LOOKASIDE |
+static int isLookaside(sqlite3 *db, void *p){ |
+ return SQLITE_WITHIN(p, db->lookaside.pStart, db->lookaside.pEnd); |
+} |
+#else |
+#define isLookaside(A,B) 0 |
+#endif |
+ |
+/* |
+** Return the size of a memory allocation previously obtained from |
+** sqlite3Malloc() or sqlite3_malloc(). |
+*/ |
+SQLITE_PRIVATE int sqlite3MallocSize(void *p){ |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); |
+ return sqlite3GlobalConfig.m.xSize(p); |
+} |
+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ |
+ assert( p!=0 ); |
+ if( db==0 || !isLookaside(db,p) ){ |
+#if SQLITE_DEBUG |
+ if( db==0 ){ |
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); |
+ }else{ |
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ } |
+#endif |
+ return sqlite3GlobalConfig.m.xSize(p); |
+ }else{ |
+ assert( sqlite3_mutex_held(db->mutex) ); |
+ return db->lookaside.sz; |
+ } |
+} |
+SQLITE_API sqlite3_uint64 SQLITE_STDCALL sqlite3_msize(void *p){ |
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); |
+ return p ? sqlite3GlobalConfig.m.xSize(p) : 0; |
+} |
+ |
+/* |
+** Free memory previously obtained from sqlite3Malloc(). |
+*/ |
+SQLITE_API void SQLITE_STDCALL sqlite3_free(void *p){ |
+ if( p==0 ) return; /* IMP: R-49053-54554 */ |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) ); |
+ assert( sqlite3MemdebugNoType(p, (u8)~MEMTYPE_HEAP) ); |
+ if( sqlite3GlobalConfig.bMemstat ){ |
+ sqlite3_mutex_enter(mem0.mutex); |
+ sqlite3StatusDown(SQLITE_STATUS_MEMORY_USED, sqlite3MallocSize(p)); |
+ sqlite3StatusDown(SQLITE_STATUS_MALLOC_COUNT, 1); |
+ sqlite3GlobalConfig.m.xFree(p); |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ sqlite3GlobalConfig.m.xFree(p); |
+ } |
+} |
+ |
+/* |
+** Add the size of memory allocation "p" to the count in |
+** *db->pnBytesFreed. |
+*/ |
+static SQLITE_NOINLINE void measureAllocationSize(sqlite3 *db, void *p){ |
+ *db->pnBytesFreed += sqlite3DbMallocSize(db,p); |
+} |
+ |
+/* |
+** Free memory that might be associated with a particular database |
+** connection. |
+*/ |
+SQLITE_PRIVATE void sqlite3DbFree(sqlite3 *db, void *p){ |
+ assert( db==0 || sqlite3_mutex_held(db->mutex) ); |
+ if( p==0 ) return; |
+ if( db ){ |
+ if( db->pnBytesFreed ){ |
+ measureAllocationSize(db, p); |
+ return; |
+ } |
+ if( isLookaside(db, p) ){ |
+ LookasideSlot *pBuf = (LookasideSlot*)p; |
+#if SQLITE_DEBUG |
+ /* Trash all content in the buffer being freed */ |
+ memset(p, 0xaa, db->lookaside.sz); |
+#endif |
+ pBuf->pNext = db->lookaside.pFree; |
+ db->lookaside.pFree = pBuf; |
+ db->lookaside.nOut--; |
+ return; |
+ } |
+ } |
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) ); |
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
+ sqlite3_free(p); |
+} |
+ |
+/* |
+** Change the size of an existing memory allocation |
+*/ |
+SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, u64 nBytes){ |
+ int nOld, nNew, nDiff; |
+ void *pNew; |
+ assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) ); |
+ assert( sqlite3MemdebugNoType(pOld, (u8)~MEMTYPE_HEAP) ); |
+ if( pOld==0 ){ |
+ return sqlite3Malloc(nBytes); /* IMP: R-04300-56712 */ |
+ } |
+ if( nBytes==0 ){ |
+ sqlite3_free(pOld); /* IMP: R-26507-47431 */ |
+ return 0; |
+ } |
+ if( nBytes>=0x7fffff00 ){ |
+ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */ |
+ return 0; |
+ } |
+ nOld = sqlite3MallocSize(pOld); |
+ /* IMPLEMENTATION-OF: R-46199-30249 SQLite guarantees that the second |
+ ** argument to xRealloc is always a value returned by a prior call to |
+ ** xRoundup. */ |
+ nNew = sqlite3GlobalConfig.m.xRoundup((int)nBytes); |
+ if( nOld==nNew ){ |
+ pNew = pOld; |
+ }else if( sqlite3GlobalConfig.bMemstat ){ |
+ sqlite3_mutex_enter(mem0.mutex); |
+ sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes); |
+ nDiff = nNew - nOld; |
+ if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= |
+ mem0.alarmThreshold-nDiff ){ |
+ sqlite3MallocAlarm(nDiff); |
+ } |
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
+ if( pNew==0 && mem0.alarmThreshold>0 ){ |
+ sqlite3MallocAlarm((int)nBytes); |
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
+ } |
+ if( pNew ){ |
+ nNew = sqlite3MallocSize(pNew); |
+ sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld); |
+ } |
+ sqlite3_mutex_leave(mem0.mutex); |
+ }else{ |
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); |
+ } |
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-11148-40995 */ |
+ return pNew; |
+} |
+ |
+/* |
+** The public interface to sqlite3Realloc. Make sure that the memory |
+** subsystem is initialized prior to invoking sqliteRealloc. |
+*/ |
+SQLITE_API void *SQLITE_STDCALL sqlite3_realloc(void *pOld, int n){ |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ if( n<0 ) n = 0; /* IMP: R-26507-47431 */ |
+ return sqlite3Realloc(pOld, n); |
+} |
+SQLITE_API void *SQLITE_STDCALL sqlite3_realloc64(void *pOld, sqlite3_uint64 n){ |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ return sqlite3Realloc(pOld, n); |
+} |
+ |
+ |
+/* |
+** Allocate and zero memory. |
+*/ |
+SQLITE_PRIVATE void *sqlite3MallocZero(u64 n){ |
+ void *p = sqlite3Malloc(n); |
+ if( p ){ |
+ memset(p, 0, (size_t)n); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Allocate and zero memory. If the allocation fails, make |
+** the mallocFailed flag in the connection pointer. |
+*/ |
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, u64 n){ |
+ void *p = sqlite3DbMallocRaw(db, n); |
+ if( p ){ |
+ memset(p, 0, (size_t)n); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Allocate and zero memory. If the allocation fails, make |
+** the mallocFailed flag in the connection pointer. |
+** |
+** If db!=0 and db->mallocFailed is true (indicating a prior malloc |
+** failure on the same database connection) then always return 0. |
+** Hence for a particular database connection, once malloc starts |
+** failing, it fails consistently until mallocFailed is reset. |
+** This is an important assumption. There are many places in the |
+** code that do things like this: |
+** |
+** int *a = (int*)sqlite3DbMallocRaw(db, 100); |
+** int *b = (int*)sqlite3DbMallocRaw(db, 200); |
+** if( b ) a[10] = 9; |
+** |
+** In other words, if a subsequent malloc (ex: "b") worked, it is assumed |
+** that all prior mallocs (ex: "a") worked too. |
+*/ |
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, u64 n){ |
+ void *p; |
+ assert( db==0 || sqlite3_mutex_held(db->mutex) ); |
+ assert( db==0 || db->pnBytesFreed==0 ); |
+#ifndef SQLITE_OMIT_LOOKASIDE |
+ if( db ){ |
+ LookasideSlot *pBuf; |
+ if( db->mallocFailed ){ |
+ return 0; |
+ } |
+ if( db->lookaside.bEnabled ){ |
+ if( n>db->lookaside.sz ){ |
+ db->lookaside.anStat[1]++; |
+ }else if( (pBuf = db->lookaside.pFree)==0 ){ |
+ db->lookaside.anStat[2]++; |
+ }else{ |
+ db->lookaside.pFree = pBuf->pNext; |
+ db->lookaside.nOut++; |
+ db->lookaside.anStat[0]++; |
+ if( db->lookaside.nOut>db->lookaside.mxOut ){ |
+ db->lookaside.mxOut = db->lookaside.nOut; |
+ } |
+ return (void*)pBuf; |
+ } |
+ } |
+ } |
+#else |
+ if( db && db->mallocFailed ){ |
+ return 0; |
+ } |
+#endif |
+ p = sqlite3Malloc(n); |
+ if( !p && db ){ |
+ db->mallocFailed = 1; |
+ } |
+ sqlite3MemdebugSetType(p, |
+ (db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP); |
+ return p; |
+} |
+ |
+/* |
+** Resize the block of memory pointed to by p to n bytes. If the |
+** resize fails, set the mallocFailed flag in the connection object. |
+*/ |
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, u64 n){ |
+ void *pNew = 0; |
+ assert( db!=0 ); |
+ assert( sqlite3_mutex_held(db->mutex) ); |
+ if( db->mallocFailed==0 ){ |
+ if( p==0 ){ |
+ return sqlite3DbMallocRaw(db, n); |
+ } |
+ if( isLookaside(db, p) ){ |
+ if( n<=db->lookaside.sz ){ |
+ return p; |
+ } |
+ pNew = sqlite3DbMallocRaw(db, n); |
+ if( pNew ){ |
+ memcpy(pNew, p, db->lookaside.sz); |
+ sqlite3DbFree(db, p); |
+ } |
+ }else{ |
+ assert( sqlite3MemdebugHasType(p, (MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ assert( sqlite3MemdebugNoType(p, (u8)~(MEMTYPE_LOOKASIDE|MEMTYPE_HEAP)) ); |
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
+ pNew = sqlite3_realloc64(p, n); |
+ if( !pNew ){ |
+ db->mallocFailed = 1; |
+ } |
+ sqlite3MemdebugSetType(pNew, |
+ (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP)); |
+ } |
+ } |
+ return pNew; |
+} |
+ |
+/* |
+** Attempt to reallocate p. If the reallocation fails, then free p |
+** and set the mallocFailed flag in the database connection. |
+*/ |
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, u64 n){ |
+ void *pNew; |
+ pNew = sqlite3DbRealloc(db, p, n); |
+ if( !pNew ){ |
+ sqlite3DbFree(db, p); |
+ } |
+ return pNew; |
+} |
+ |
+/* |
+** 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 |
+** ThreadData structure. |
+*/ |
+SQLITE_PRIVATE char *sqlite3DbStrDup(sqlite3 *db, const char *z){ |
+ char *zNew; |
+ size_t n; |
+ if( z==0 ){ |
+ return 0; |
+ } |
+ n = sqlite3Strlen30(z) + 1; |
+ assert( (n&0x7fffffff)==n ); |
+ zNew = sqlite3DbMallocRaw(db, (int)n); |
+ if( zNew ){ |
+ memcpy(zNew, z, n); |
+ } |
+ return zNew; |
+} |
+SQLITE_PRIVATE char *sqlite3DbStrNDup(sqlite3 *db, const char *z, u64 n){ |
+ char *zNew; |
+ if( z==0 ){ |
+ return 0; |
+ } |
+ assert( (n&0x7fffffff)==n ); |
+ zNew = sqlite3DbMallocRaw(db, n+1); |
+ if( zNew ){ |
+ memcpy(zNew, z, (size_t)n); |
+ zNew[n] = 0; |
+ } |
+ return zNew; |
+} |
+ |
+/* |
+** Free any prior content in *pz and replace it with a copy of zNew. |
+*/ |
+SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zNew){ |
+ sqlite3DbFree(db, *pz); |
+ *pz = sqlite3DbStrDup(db, zNew); |
+} |
+ |
+/* |
+** Take actions at the end of an API call to indicate an OOM error |
+*/ |
+static SQLITE_NOINLINE int apiOomError(sqlite3 *db){ |
+ db->mallocFailed = 0; |
+ sqlite3Error(db, SQLITE_NOMEM); |
+ return SQLITE_NOMEM; |
+} |
+ |
+/* |
+** This function must be called before exiting any API function (i.e. |
+** returning control to the user) that has called sqlite3_malloc or |
+** sqlite3_realloc. |
+** |
+** The returned value is normally a copy of the second argument to this |
+** function. However, if a malloc() failure has occurred since the previous |
+** invocation SQLITE_NOMEM is returned instead. |
+** |
+** If an OOM as occurred, then the connection error-code (the value |
+** returned by sqlite3_errcode()) is set to SQLITE_NOMEM. |
+*/ |
+SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){ |
+ /* If the db handle must hold the connection handle mutex here. |
+ ** Otherwise the read (and possible write) of db->mallocFailed |
+ ** is unsafe, as is the call to sqlite3Error(). |
+ */ |
+ assert( db!=0 ); |
+ assert( sqlite3_mutex_held(db->mutex) ); |
+ if( db->mallocFailed || rc==SQLITE_IOERR_NOMEM ){ |
+ return apiOomError(db); |
+ } |
+ return rc & db->errMask; |
+} |
+ |
+/************** End of malloc.c **********************************************/ |
+/************** Begin file printf.c ******************************************/ |
+/* |
+** The "printf" code that follows dates from the 1980's. It is in |
+** the public domain. |
+** |
+************************************************************************** |
+** |
+** This file contains code for a set of "printf"-like routines. These |
+** routines format strings much like the printf() from the standard C |
+** library, though the implementation here has enhancements to support |
+** SQLite. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+/* |
+** Conversion types fall into various categories as defined by the |
+** following enumeration. |
+*/ |
+#define etRADIX 1 /* Integer types. %d, %x, %o, and so forth */ |
+#define etFLOAT 2 /* Floating point. %f */ |
+#define etEXP 3 /* Exponentional notation. %e and %E */ |
+#define etGENERIC 4 /* Floating or exponential, depending on exponent. %g */ |
+#define etSIZE 5 /* Return number of characters processed so far. %n */ |
+#define etSTRING 6 /* Strings. %s */ |
+#define etDYNSTRING 7 /* Dynamically allocated strings. %z */ |
+#define etPERCENT 8 /* Percent symbol. %% */ |
+#define etCHARX 9 /* Characters. %c */ |
+/* The rest are extensions, not normally found in printf() */ |
+#define etSQLESCAPE 10 /* Strings with '\'' doubled. %q */ |
+#define etSQLESCAPE2 11 /* Strings with '\'' doubled and enclosed in '', |
+ NULL pointers replaced by SQL NULL. %Q */ |
+#define etTOKEN 12 /* a pointer to a Token structure */ |
+#define etSRCLIST 13 /* a pointer to a SrcList */ |
+#define etPOINTER 14 /* The %p conversion */ |
+#define etSQLESCAPE3 15 /* %w -> Strings with '\"' doubled */ |
+#define etORDINAL 16 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ |
+ |
+#define etINVALID 0 /* Any unrecognized conversion type */ |
+ |
+ |
+/* |
+** An "etByte" is an 8-bit unsigned value. |
+*/ |
+typedef unsigned char etByte; |
+ |
+/* |
+** Each builtin conversion character (ex: the 'd' in "%d") is described |
+** by an instance of the following structure |
+*/ |
+typedef struct et_info { /* Information about each format field */ |
+ char fmttype; /* The format field code letter */ |
+ etByte base; /* The base for radix conversion */ |
+ etByte flags; /* One or more of FLAG_ constants below */ |
+ etByte type; /* Conversion paradigm */ |
+ etByte charset; /* Offset into aDigits[] of the digits string */ |
+ etByte prefix; /* Offset into aPrefix[] of the prefix string */ |
+} et_info; |
+ |
+/* |
+** Allowed values for et_info.flags |
+*/ |
+#define FLAG_SIGNED 1 /* True if the value to convert is signed */ |
+#define FLAG_INTERN 2 /* True if for internal use only */ |
+#define FLAG_STRING 4 /* Allow infinity precision */ |
+ |
+ |
+/* |
+** The following table is searched linearly, so it is good to put the |
+** most frequently used conversion types first. |
+*/ |
+static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
+static const char aPrefix[] = "-x0\000X0"; |
+static const et_info fmtinfo[] = { |
+ { 'd', 10, 1, etRADIX, 0, 0 }, |
+ { 's', 0, 4, etSTRING, 0, 0 }, |
+ { 'g', 0, 1, etGENERIC, 30, 0 }, |
+ { 'z', 0, 4, etDYNSTRING, 0, 0 }, |
+ { 'q', 0, 4, etSQLESCAPE, 0, 0 }, |
+ { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, |
+ { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, |
+ { 'c', 0, 0, etCHARX, 0, 0 }, |
+ { 'o', 8, 0, etRADIX, 0, 2 }, |
+ { 'u', 10, 0, etRADIX, 0, 0 }, |
+ { 'x', 16, 0, etRADIX, 16, 1 }, |
+ { 'X', 16, 0, etRADIX, 0, 4 }, |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+ { 'f', 0, 1, etFLOAT, 0, 0 }, |
+ { 'e', 0, 1, etEXP, 30, 0 }, |
+ { 'E', 0, 1, etEXP, 14, 0 }, |
+ { 'G', 0, 1, etGENERIC, 14, 0 }, |
+#endif |
+ { 'i', 10, 1, etRADIX, 0, 0 }, |
+ { 'n', 0, 0, etSIZE, 0, 0 }, |
+ { '%', 0, 0, etPERCENT, 0, 0 }, |
+ { 'p', 16, 0, etPOINTER, 0, 1 }, |
+ |
+/* All the rest have the FLAG_INTERN bit set and are thus for internal |
+** use only */ |
+ { 'T', 0, 2, etTOKEN, 0, 0 }, |
+ { 'S', 0, 2, etSRCLIST, 0, 0 }, |
+ { 'r', 10, 3, etORDINAL, 0, 0 }, |
+}; |
+ |
+/* |
+** If SQLITE_OMIT_FLOATING_POINT is defined, then none of the floating point |
+** conversions will work. |
+*/ |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+/* |
+** "*val" is a double such that 0.1 <= *val < 10.0 |
+** Return the ascii code for the leading digit of *val, then |
+** multiply "*val" by 10.0 to renormalize. |
+** |
+** Example: |
+** input: *val = 3.14159 |
+** output: *val = 1.4159 function return = '3' |
+** |
+** The counter *cnt is incremented each time. After counter exceeds |
+** 16 (the number of significant digits in a 64-bit float) '0' is |
+** always returned. |
+*/ |
+static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){ |
+ int digit; |
+ LONGDOUBLE_TYPE d; |
+ if( (*cnt)<=0 ) return '0'; |
+ (*cnt)--; |
+ digit = (int)*val; |
+ d = digit; |
+ digit += '0'; |
+ *val = (*val - d)*10.0; |
+ return (char)digit; |
+} |
+#endif /* SQLITE_OMIT_FLOATING_POINT */ |
+ |
+/* |
+** Set the StrAccum object to an error mode. |
+*/ |
+static void setStrAccumError(StrAccum *p, u8 eError){ |
+ assert( eError==STRACCUM_NOMEM || eError==STRACCUM_TOOBIG ); |
+ p->accError = eError; |
+ p->nAlloc = 0; |
+} |
+ |
+/* |
+** Extra argument values from a PrintfArguments object |
+*/ |
+static sqlite3_int64 getIntArg(PrintfArguments *p){ |
+ if( p->nArg<=p->nUsed ) return 0; |
+ return sqlite3_value_int64(p->apArg[p->nUsed++]); |
+} |
+static double getDoubleArg(PrintfArguments *p){ |
+ if( p->nArg<=p->nUsed ) return 0.0; |
+ return sqlite3_value_double(p->apArg[p->nUsed++]); |
+} |
+static char *getTextArg(PrintfArguments *p){ |
+ if( p->nArg<=p->nUsed ) return 0; |
+ return (char*)sqlite3_value_text(p->apArg[p->nUsed++]); |
+} |
+ |
+ |
+/* |
+** On machines with a small stack size, you can redefine the |
+** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired. |
+*/ |
+#ifndef SQLITE_PRINT_BUF_SIZE |
+# define SQLITE_PRINT_BUF_SIZE 70 |
+#endif |
+#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */ |
+ |
+/* |
+** Render a string given by "fmt" into the StrAccum object. |
+*/ |
+SQLITE_PRIVATE void sqlite3VXPrintf( |
+ StrAccum *pAccum, /* Accumulate results here */ |
+ u32 bFlags, /* SQLITE_PRINTF_* flags */ |
+ const char *fmt, /* Format string */ |
+ va_list ap /* arguments */ |
+){ |
+ int c; /* Next character in the format string */ |
+ char *bufpt; /* Pointer to the conversion buffer */ |
+ int precision; /* Precision of the current field */ |
+ int length; /* Length of the field */ |
+ int idx; /* A general purpose loop counter */ |
+ int width; /* Width of the current field */ |
+ etByte flag_leftjustify; /* True if "-" flag is present */ |
+ etByte flag_plussign; /* True if "+" flag is present */ |
+ etByte flag_blanksign; /* True if " " flag is present */ |
+ etByte flag_alternateform; /* True if "#" flag is present */ |
+ etByte flag_altform2; /* True if "!" flag is present */ |
+ etByte flag_zeropad; /* True if field width constant starts with zero */ |
+ etByte flag_long; /* True if "l" flag is present */ |
+ etByte flag_longlong; /* True if the "ll" flag is present */ |
+ etByte done; /* Loop termination flag */ |
+ etByte xtype = 0; /* Conversion paradigm */ |
+ u8 bArgList; /* True for SQLITE_PRINTF_SQLFUNC */ |
+ u8 useIntern; /* Ok to use internal conversions (ex: %T) */ |
+ char prefix; /* Prefix character. "+" or "-" or " " or '\0'. */ |
+ sqlite_uint64 longvalue; /* Value for integer types */ |
+ LONGDOUBLE_TYPE realvalue; /* Value for real types */ |
+ const et_info *infop; /* Pointer to the appropriate info structure */ |
+ char *zOut; /* Rendering buffer */ |
+ int nOut; /* Size of the rendering buffer */ |
+ char *zExtra = 0; /* Malloced memory used by some conversion */ |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+ int exp, e2; /* exponent of real numbers */ |
+ int nsd; /* Number of significant digits returned */ |
+ double rounder; /* Used for rounding floating point values */ |
+ etByte flag_dp; /* True if decimal point should be shown */ |
+ etByte flag_rtz; /* True if trailing zeros should be removed */ |
+#endif |
+ PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */ |
+ char buf[etBUFSIZE]; /* Conversion buffer */ |
+ |
+ bufpt = 0; |
+ if( bFlags ){ |
+ if( (bArgList = (bFlags & SQLITE_PRINTF_SQLFUNC))!=0 ){ |
+ pArgList = va_arg(ap, PrintfArguments*); |
+ } |
+ useIntern = bFlags & SQLITE_PRINTF_INTERNAL; |
+ }else{ |
+ bArgList = useIntern = 0; |
+ } |
+ for(; (c=(*fmt))!=0; ++fmt){ |
+ if( c!='%' ){ |
+ bufpt = (char *)fmt; |
+#if HAVE_STRCHRNUL |
+ fmt = strchrnul(fmt, '%'); |
+#else |
+ do{ fmt++; }while( *fmt && *fmt != '%' ); |
+#endif |
+ sqlite3StrAccumAppend(pAccum, bufpt, (int)(fmt - bufpt)); |
+ if( *fmt==0 ) break; |
+ } |
+ if( (c=(*++fmt))==0 ){ |
+ sqlite3StrAccumAppend(pAccum, "%", 1); |
+ break; |
+ } |
+ /* Find out what flags are present */ |
+ flag_leftjustify = flag_plussign = flag_blanksign = |
+ flag_alternateform = flag_altform2 = flag_zeropad = 0; |
+ done = 0; |
+ do{ |
+ switch( c ){ |
+ case '-': flag_leftjustify = 1; break; |
+ case '+': flag_plussign = 1; break; |
+ case ' ': flag_blanksign = 1; break; |
+ case '#': flag_alternateform = 1; break; |
+ case '!': flag_altform2 = 1; break; |
+ case '0': flag_zeropad = 1; break; |
+ default: done = 1; break; |
+ } |
+ }while( !done && (c=(*++fmt))!=0 ); |
+ /* Get the field width */ |
+ if( c=='*' ){ |
+ if( bArgList ){ |
+ width = (int)getIntArg(pArgList); |
+ }else{ |
+ width = va_arg(ap,int); |
+ } |
+ if( width<0 ){ |
+ flag_leftjustify = 1; |
+ width = width >= -2147483647 ? -width : 0; |
+ } |
+ c = *++fmt; |
+ }else{ |
+ unsigned wx = 0; |
+ while( c>='0' && c<='9' ){ |
+ wx = wx*10 + c - '0'; |
+ c = *++fmt; |
+ } |
+ testcase( wx>0x7fffffff ); |
+ width = wx & 0x7fffffff; |
+ } |
+ assert( width>=0 ); |
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT |
+ if( width>SQLITE_PRINTF_PRECISION_LIMIT ){ |
+ width = SQLITE_PRINTF_PRECISION_LIMIT; |
+ } |
+#endif |
+ |
+ /* Get the precision */ |
+ if( c=='.' ){ |
+ c = *++fmt; |
+ if( c=='*' ){ |
+ if( bArgList ){ |
+ precision = (int)getIntArg(pArgList); |
+ }else{ |
+ precision = va_arg(ap,int); |
+ } |
+ c = *++fmt; |
+ if( precision<0 ){ |
+ precision = precision >= -2147483647 ? -precision : -1; |
+ } |
+ }else{ |
+ unsigned px = 0; |
+ while( c>='0' && c<='9' ){ |
+ px = px*10 + c - '0'; |
+ c = *++fmt; |
+ } |
+ testcase( px>0x7fffffff ); |
+ precision = px & 0x7fffffff; |
+ } |
+ }else{ |
+ precision = -1; |
+ } |
+ assert( precision>=(-1) ); |
+#ifdef SQLITE_PRINTF_PRECISION_LIMIT |
+ if( precision>SQLITE_PRINTF_PRECISION_LIMIT ){ |
+ precision = SQLITE_PRINTF_PRECISION_LIMIT; |
+ } |
+#endif |
+ |
+ |
+ /* Get the conversion type modifier */ |
+ if( c=='l' ){ |
+ flag_long = 1; |
+ c = *++fmt; |
+ if( c=='l' ){ |
+ flag_longlong = 1; |
+ c = *++fmt; |
+ }else{ |
+ flag_longlong = 0; |
+ } |
+ }else{ |
+ flag_long = flag_longlong = 0; |
+ } |
+ /* Fetch the info entry for the field */ |
+ infop = &fmtinfo[0]; |
+ xtype = etINVALID; |
+ for(idx=0; idx<ArraySize(fmtinfo); idx++){ |
+ if( c==fmtinfo[idx].fmttype ){ |
+ infop = &fmtinfo[idx]; |
+ if( useIntern || (infop->flags & FLAG_INTERN)==0 ){ |
+ xtype = infop->type; |
+ }else{ |
+ return; |
+ } |
+ break; |
+ } |
+ } |
+ |
+ /* |
+ ** At this point, variables are initialized as follows: |
+ ** |
+ ** flag_alternateform TRUE if a '#' is present. |
+ ** flag_altform2 TRUE if a '!' is present. |
+ ** flag_plussign TRUE if a '+' is present. |
+ ** flag_leftjustify TRUE if a '-' is present or if the |
+ ** field width was negative. |
+ ** flag_zeropad TRUE if the width began with 0. |
+ ** flag_long TRUE if the letter 'l' (ell) prefixed |
+ ** the conversion character. |
+ ** flag_longlong TRUE if the letter 'll' (ell ell) prefixed |
+ ** the conversion character. |
+ ** flag_blanksign TRUE if a ' ' is present. |
+ ** width The specified field width. This is |
+ ** always non-negative. Zero is the default. |
+ ** precision The specified precision. The default |
+ ** is -1. |
+ ** xtype The class of the conversion. |
+ ** infop Pointer to the appropriate info struct. |
+ */ |
+ switch( xtype ){ |
+ case etPOINTER: |
+ flag_longlong = sizeof(char*)==sizeof(i64); |
+ flag_long = sizeof(char*)==sizeof(long int); |
+ /* Fall through into the next case */ |
+ case etORDINAL: |
+ case etRADIX: |
+ if( infop->flags & FLAG_SIGNED ){ |
+ i64 v; |
+ if( bArgList ){ |
+ v = getIntArg(pArgList); |
+ }else if( flag_longlong ){ |
+ v = va_arg(ap,i64); |
+ }else if( flag_long ){ |
+ v = va_arg(ap,long int); |
+ }else{ |
+ v = va_arg(ap,int); |
+ } |
+ if( v<0 ){ |
+ if( v==SMALLEST_INT64 ){ |
+ longvalue = ((u64)1)<<63; |
+ }else{ |
+ longvalue = -v; |
+ } |
+ prefix = '-'; |
+ }else{ |
+ longvalue = v; |
+ if( flag_plussign ) prefix = '+'; |
+ else if( flag_blanksign ) prefix = ' '; |
+ else prefix = 0; |
+ } |
+ }else{ |
+ if( bArgList ){ |
+ longvalue = (u64)getIntArg(pArgList); |
+ }else if( flag_longlong ){ |
+ longvalue = va_arg(ap,u64); |
+ }else if( flag_long ){ |
+ longvalue = va_arg(ap,unsigned long int); |
+ }else{ |
+ longvalue = va_arg(ap,unsigned int); |
+ } |
+ prefix = 0; |
+ } |
+ if( longvalue==0 ) flag_alternateform = 0; |
+ if( flag_zeropad && precision<width-(prefix!=0) ){ |
+ precision = width-(prefix!=0); |
+ } |
+ if( precision<etBUFSIZE-10 ){ |
+ nOut = etBUFSIZE; |
+ zOut = buf; |
+ }else{ |
+ nOut = precision + 10; |
+ zOut = zExtra = sqlite3Malloc( nOut ); |
+ if( zOut==0 ){ |
+ setStrAccumError(pAccum, STRACCUM_NOMEM); |
+ return; |
+ } |
+ } |
+ bufpt = &zOut[nOut-1]; |
+ if( xtype==etORDINAL ){ |
+ static const char zOrd[] = "thstndrd"; |
+ int x = (int)(longvalue % 10); |
+ if( x>=4 || (longvalue/10)%10==1 ){ |
+ x = 0; |
+ } |
+ *(--bufpt) = zOrd[x*2+1]; |
+ *(--bufpt) = zOrd[x*2]; |
+ } |
+ { |
+ const char *cset = &aDigits[infop->charset]; |
+ u8 base = infop->base; |
+ do{ /* Convert to ascii */ |
+ *(--bufpt) = cset[longvalue%base]; |
+ longvalue = longvalue/base; |
+ }while( longvalue>0 ); |
+ } |
+ length = (int)(&zOut[nOut-1]-bufpt); |
+ for(idx=precision-length; idx>0; idx--){ |
+ *(--bufpt) = '0'; /* Zero pad */ |
+ } |
+ if( prefix ) *(--bufpt) = prefix; /* Add sign */ |
+ if( flag_alternateform && infop->prefix ){ /* Add "0" or "0x" */ |
+ const char *pre; |
+ char x; |
+ pre = &aPrefix[infop->prefix]; |
+ for(; (x=(*pre))!=0; pre++) *(--bufpt) = x; |
+ } |
+ length = (int)(&zOut[nOut-1]-bufpt); |
+ break; |
+ case etFLOAT: |
+ case etEXP: |
+ case etGENERIC: |
+ if( bArgList ){ |
+ realvalue = getDoubleArg(pArgList); |
+ }else{ |
+ realvalue = va_arg(ap,double); |
+ } |
+#ifdef SQLITE_OMIT_FLOATING_POINT |
+ length = 0; |
+#else |
+ if( precision<0 ) precision = 6; /* Set default precision */ |
+ if( realvalue<0.0 ){ |
+ realvalue = -realvalue; |
+ prefix = '-'; |
+ }else{ |
+ if( flag_plussign ) prefix = '+'; |
+ else if( flag_blanksign ) prefix = ' '; |
+ else prefix = 0; |
+ } |
+ if( xtype==etGENERIC && precision>0 ) precision--; |
+ testcase( precision>0xfff ); |
+ for(idx=precision&0xfff, rounder=0.5; idx>0; idx--, rounder*=0.1){} |
+ if( xtype==etFLOAT ) realvalue += rounder; |
+ /* Normalize realvalue to within 10.0 > realvalue >= 1.0 */ |
+ exp = 0; |
+ if( sqlite3IsNaN((double)realvalue) ){ |
+ bufpt = "NaN"; |
+ length = 3; |
+ break; |
+ } |
+ if( realvalue>0.0 ){ |
+ LONGDOUBLE_TYPE scale = 1.0; |
+ while( realvalue>=1e100*scale && exp<=350 ){ scale *= 1e100;exp+=100;} |
+ while( realvalue>=1e10*scale && exp<=350 ){ scale *= 1e10; exp+=10; } |
+ while( realvalue>=10.0*scale && exp<=350 ){ scale *= 10.0; exp++; } |
+ realvalue /= scale; |
+ while( realvalue<1e-8 ){ realvalue *= 1e8; exp-=8; } |
+ while( realvalue<1.0 ){ realvalue *= 10.0; exp--; } |
+ if( exp>350 ){ |
+ bufpt = buf; |
+ buf[0] = prefix; |
+ memcpy(buf+(prefix!=0),"Inf",4); |
+ length = 3+(prefix!=0); |
+ break; |
+ } |
+ } |
+ bufpt = buf; |
+ /* |
+ ** If the field type is etGENERIC, then convert to either etEXP |
+ ** or etFLOAT, as appropriate. |
+ */ |
+ if( xtype!=etFLOAT ){ |
+ realvalue += rounder; |
+ if( realvalue>=10.0 ){ realvalue *= 0.1; exp++; } |
+ } |
+ if( xtype==etGENERIC ){ |
+ flag_rtz = !flag_alternateform; |
+ if( exp<-4 || exp>precision ){ |
+ xtype = etEXP; |
+ }else{ |
+ precision = precision - exp; |
+ xtype = etFLOAT; |
+ } |
+ }else{ |
+ flag_rtz = flag_altform2; |
+ } |
+ if( xtype==etEXP ){ |
+ e2 = 0; |
+ }else{ |
+ e2 = exp; |
+ } |
+ if( MAX(e2,0)+(i64)precision+(i64)width > etBUFSIZE - 15 ){ |
+ bufpt = zExtra |
+ = sqlite3Malloc( MAX(e2,0)+(i64)precision+(i64)width+15 ); |
+ if( bufpt==0 ){ |
+ setStrAccumError(pAccum, STRACCUM_NOMEM); |
+ return; |
+ } |
+ } |
+ zOut = bufpt; |
+ nsd = 16 + flag_altform2*10; |
+ flag_dp = (precision>0 ?1:0) | flag_alternateform | flag_altform2; |
+ /* The sign in front of the number */ |
+ if( prefix ){ |
+ *(bufpt++) = prefix; |
+ } |
+ /* Digits prior to the decimal point */ |
+ if( e2<0 ){ |
+ *(bufpt++) = '0'; |
+ }else{ |
+ for(; e2>=0; e2--){ |
+ *(bufpt++) = et_getdigit(&realvalue,&nsd); |
+ } |
+ } |
+ /* The decimal point */ |
+ if( flag_dp ){ |
+ *(bufpt++) = '.'; |
+ } |
+ /* "0" digits after the decimal point but before the first |
+ ** significant digit of the number */ |
+ for(e2++; e2<0; precision--, e2++){ |
+ assert( precision>0 ); |
+ *(bufpt++) = '0'; |
+ } |
+ /* Significant digits after the decimal point */ |
+ while( (precision--)>0 ){ |
+ *(bufpt++) = et_getdigit(&realvalue,&nsd); |
+ } |
+ /* Remove trailing zeros and the "." if no digits follow the "." */ |
+ if( flag_rtz && flag_dp ){ |
+ while( bufpt[-1]=='0' ) *(--bufpt) = 0; |
+ assert( bufpt>zOut ); |
+ if( bufpt[-1]=='.' ){ |
+ if( flag_altform2 ){ |
+ *(bufpt++) = '0'; |
+ }else{ |
+ *(--bufpt) = 0; |
+ } |
+ } |
+ } |
+ /* Add the "eNNN" suffix */ |
+ if( xtype==etEXP ){ |
+ *(bufpt++) = aDigits[infop->charset]; |
+ if( exp<0 ){ |
+ *(bufpt++) = '-'; exp = -exp; |
+ }else{ |
+ *(bufpt++) = '+'; |
+ } |
+ if( exp>=100 ){ |
+ *(bufpt++) = (char)((exp/100)+'0'); /* 100's digit */ |
+ exp %= 100; |
+ } |
+ *(bufpt++) = (char)(exp/10+'0'); /* 10's digit */ |
+ *(bufpt++) = (char)(exp%10+'0'); /* 1's digit */ |
+ } |
+ *bufpt = 0; |
+ |
+ /* The converted number is in buf[] and zero terminated. Output it. |
+ ** Note that the number is in the usual order, not reversed as with |
+ ** integer conversions. */ |
+ length = (int)(bufpt-zOut); |
+ bufpt = zOut; |
+ |
+ /* Special case: Add leading zeros if the flag_zeropad flag is |
+ ** set and we are not left justified */ |
+ if( flag_zeropad && !flag_leftjustify && length < width){ |
+ int i; |
+ int nPad = width - length; |
+ for(i=width; i>=nPad; i--){ |
+ bufpt[i] = bufpt[i-nPad]; |
+ } |
+ i = prefix!=0; |
+ while( nPad-- ) bufpt[i++] = '0'; |
+ length = width; |
+ } |
+#endif /* !defined(SQLITE_OMIT_FLOATING_POINT) */ |
+ break; |
+ case etSIZE: |
+ if( !bArgList ){ |
+ *(va_arg(ap,int*)) = pAccum->nChar; |
+ } |
+ length = width = 0; |
+ break; |
+ case etPERCENT: |
+ buf[0] = '%'; |
+ bufpt = buf; |
+ length = 1; |
+ break; |
+ case etCHARX: |
+ if( bArgList ){ |
+ bufpt = getTextArg(pArgList); |
+ c = bufpt ? bufpt[0] : 0; |
+ }else{ |
+ c = va_arg(ap,int); |
+ } |
+ if( precision>1 ){ |
+ width -= precision-1; |
+ if( width>1 && !flag_leftjustify ){ |
+ sqlite3AppendChar(pAccum, width-1, ' '); |
+ width = 0; |
+ } |
+ sqlite3AppendChar(pAccum, precision-1, c); |
+ } |
+ length = 1; |
+ buf[0] = c; |
+ bufpt = buf; |
+ break; |
+ case etSTRING: |
+ case etDYNSTRING: |
+ if( bArgList ){ |
+ bufpt = getTextArg(pArgList); |
+ xtype = etSTRING; |
+ }else{ |
+ bufpt = va_arg(ap,char*); |
+ } |
+ if( bufpt==0 ){ |
+ bufpt = ""; |
+ }else if( xtype==etDYNSTRING ){ |
+ zExtra = bufpt; |
+ } |
+ if( precision>=0 ){ |
+ for(length=0; length<precision && bufpt[length]; length++){} |
+ }else{ |
+ length = sqlite3Strlen30(bufpt); |
+ } |
+ break; |
+ case etSQLESCAPE: /* Escape ' characters */ |
+ case etSQLESCAPE2: /* Escape ' and enclose in '...' */ |
+ case etSQLESCAPE3: { /* Escape " characters */ |
+ int i, j, k, n, isnull; |
+ int needQuote; |
+ char ch; |
+ char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ |
+ char *escarg; |
+ |
+ if( bArgList ){ |
+ escarg = getTextArg(pArgList); |
+ }else{ |
+ escarg = va_arg(ap,char*); |
+ } |
+ isnull = escarg==0; |
+ if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); |
+ k = precision; |
+ for(i=n=0; k!=0 && (ch=escarg[i])!=0; i++, k--){ |
+ if( ch==q ) n++; |
+ } |
+ needQuote = !isnull && xtype==etSQLESCAPE2; |
+ n += i + 3; |
+ if( n>etBUFSIZE ){ |
+ bufpt = zExtra = sqlite3Malloc( n ); |
+ if( bufpt==0 ){ |
+ setStrAccumError(pAccum, STRACCUM_NOMEM); |
+ return; |
+ } |
+ }else{ |
+ bufpt = buf; |
+ } |
+ j = 0; |
+ if( needQuote ) bufpt[j++] = q; |
+ k = i; |
+ for(i=0; i<k; i++){ |
+ bufpt[j++] = ch = escarg[i]; |
+ if( ch==q ) bufpt[j++] = ch; |
+ } |
+ if( needQuote ) bufpt[j++] = q; |
+ bufpt[j] = 0; |
+ length = j; |
+ /* The precision in %q and %Q means how many input characters to |
+ ** consume, not the length of the output... |
+ ** if( precision>=0 && precision<length ) length = precision; */ |
+ break; |
+ } |
+ case etTOKEN: { |
+ Token *pToken = va_arg(ap, Token*); |
+ assert( bArgList==0 ); |
+ if( pToken && pToken->n ){ |
+ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n); |
+ } |
+ length = width = 0; |
+ break; |
+ } |
+ case etSRCLIST: { |
+ SrcList *pSrc = va_arg(ap, SrcList*); |
+ int k = va_arg(ap, int); |
+ struct SrcList_item *pItem = &pSrc->a[k]; |
+ assert( bArgList==0 ); |
+ assert( k>=0 && k<pSrc->nSrc ); |
+ if( pItem->zDatabase ){ |
+ sqlite3StrAccumAppendAll(pAccum, pItem->zDatabase); |
+ sqlite3StrAccumAppend(pAccum, ".", 1); |
+ } |
+ sqlite3StrAccumAppendAll(pAccum, pItem->zName); |
+ length = width = 0; |
+ break; |
+ } |
+ default: { |
+ assert( xtype==etINVALID ); |
+ return; |
+ } |
+ }/* End switch over the format type */ |
+ /* |
+ ** The text of the conversion is pointed to by "bufpt" and is |
+ ** "length" characters long. The field width is "width". Do |
+ ** the output. |
+ */ |
+ width -= length; |
+ if( width>0 && !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' '); |
+ sqlite3StrAccumAppend(pAccum, bufpt, length); |
+ if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' '); |
+ |
+ if( zExtra ){ |
+ sqlite3DbFree(pAccum->db, zExtra); |
+ zExtra = 0; |
+ } |
+ }/* End for loop over the format string */ |
+} /* End of function */ |
+ |
+/* |
+** Enlarge the memory allocation on a StrAccum object so that it is |
+** able to accept at least N more bytes of text. |
+** |
+** Return the number of bytes of text that StrAccum is able to accept |
+** after the attempted enlargement. The value returned might be zero. |
+*/ |
+static int sqlite3StrAccumEnlarge(StrAccum *p, int N){ |
+ char *zNew; |
+ assert( p->nChar+(i64)N >= p->nAlloc ); /* Only called if really needed */ |
+ if( p->accError ){ |
+ testcase(p->accError==STRACCUM_TOOBIG); |
+ testcase(p->accError==STRACCUM_NOMEM); |
+ return 0; |
+ } |
+ if( p->mxAlloc==0 ){ |
+ N = p->nAlloc - p->nChar - 1; |
+ setStrAccumError(p, STRACCUM_TOOBIG); |
+ return N; |
+ }else{ |
+ char *zOld = p->bMalloced ? p->zText : 0; |
+ i64 szNew = p->nChar; |
+ assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) ); |
+ szNew += N + 1; |
+ if( szNew+p->nChar<=p->mxAlloc ){ |
+ /* Force exponential buffer size growth as long as it does not overflow, |
+ ** to avoid having to call this routine too often */ |
+ szNew += p->nChar; |
+ } |
+ if( szNew > p->mxAlloc ){ |
+ sqlite3StrAccumReset(p); |
+ setStrAccumError(p, STRACCUM_TOOBIG); |
+ return 0; |
+ }else{ |
+ p->nAlloc = (int)szNew; |
+ } |
+ if( p->db ){ |
+ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc); |
+ }else{ |
+ zNew = sqlite3_realloc64(zOld, p->nAlloc); |
+ } |
+ if( zNew ){ |
+ assert( p->zText!=0 || p->nChar==0 ); |
+ if( !p->bMalloced && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar); |
+ p->zText = zNew; |
+ p->nAlloc = sqlite3DbMallocSize(p->db, zNew); |
+ p->bMalloced = 1; |
+ }else{ |
+ sqlite3StrAccumReset(p); |
+ setStrAccumError(p, STRACCUM_NOMEM); |
+ return 0; |
+ } |
+ } |
+ return N; |
+} |
+ |
+/* |
+** Append N copies of character c to the given string buffer. |
+*/ |
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){ |
+ testcase( p->nChar + (i64)N > 0x7fffffff ); |
+ if( p->nChar+(i64)N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ){ |
+ return; |
+ } |
+ assert( (p->zText==p->zBase)==(p->bMalloced==0) ); |
+ while( (N--)>0 ) p->zText[p->nChar++] = c; |
+} |
+ |
+/* |
+** The StrAccum "p" is not large enough to accept N new bytes of z[]. |
+** So enlarge if first, then do the append. |
+** |
+** This is a helper routine to sqlite3StrAccumAppend() that does special-case |
+** work (enlarging the buffer) using tail recursion, so that the |
+** sqlite3StrAccumAppend() routine can use fast calling semantics. |
+*/ |
+static void SQLITE_NOINLINE enlargeAndAppend(StrAccum *p, const char *z, int N){ |
+ N = sqlite3StrAccumEnlarge(p, N); |
+ if( N>0 ){ |
+ memcpy(&p->zText[p->nChar], z, N); |
+ p->nChar += N; |
+ } |
+ assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) ); |
+} |
+ |
+/* |
+** Append N bytes of text from z to the StrAccum object. Increase the |
+** size of the memory allocation for StrAccum if necessary. |
+*/ |
+SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){ |
+ assert( z!=0 || N==0 ); |
+ assert( p->zText!=0 || p->nChar==0 || p->accError ); |
+ assert( N>=0 ); |
+ assert( p->accError==0 || p->nAlloc==0 ); |
+ if( p->nChar+N >= p->nAlloc ){ |
+ enlargeAndAppend(p,z,N); |
+ }else{ |
+ assert( p->zText ); |
+ p->nChar += N; |
+ memcpy(&p->zText[p->nChar-N], z, N); |
+ } |
+} |
+ |
+/* |
+** Append the complete text of zero-terminated string z[] to the p string. |
+*/ |
+SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum *p, const char *z){ |
+ sqlite3StrAccumAppend(p, z, sqlite3Strlen30(z)); |
+} |
+ |
+ |
+/* |
+** Finish off a string by making sure it is zero-terminated. |
+** Return a pointer to the resulting string. Return a NULL |
+** pointer if any kind of error was encountered. |
+*/ |
+SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){ |
+ if( p->zText ){ |
+ assert( (p->zText==p->zBase)==(p->bMalloced==0) ); |
+ p->zText[p->nChar] = 0; |
+ if( p->mxAlloc>0 && p->bMalloced==0 ){ |
+ p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 ); |
+ if( p->zText ){ |
+ memcpy(p->zText, p->zBase, p->nChar+1); |
+ p->bMalloced = 1; |
+ }else{ |
+ setStrAccumError(p, STRACCUM_NOMEM); |
+ } |
+ } |
+ } |
+ return p->zText; |
+} |
+ |
+/* |
+** Reset an StrAccum string. Reclaim all malloced memory. |
+*/ |
+SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){ |
+ assert( (p->zText==0 || p->zText==p->zBase)==(p->bMalloced==0) ); |
+ if( p->bMalloced ){ |
+ sqlite3DbFree(p->db, p->zText); |
+ p->bMalloced = 0; |
+ } |
+ p->zText = 0; |
+} |
+ |
+/* |
+** Initialize a string accumulator. |
+** |
+** p: The accumulator to be initialized. |
+** db: Pointer to a database connection. May be NULL. Lookaside |
+** memory is used if not NULL. db->mallocFailed is set appropriately |
+** when not NULL. |
+** zBase: An initial buffer. May be NULL in which case the initial buffer |
+** is malloced. |
+** n: Size of zBase in bytes. If total space requirements never exceed |
+** n then no memory allocations ever occur. |
+** mx: Maximum number of bytes to accumulate. If mx==0 then no memory |
+** allocations will ever occur. |
+*/ |
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, sqlite3 *db, char *zBase, int n, int mx){ |
+ p->zText = p->zBase = zBase; |
+ p->db = db; |
+ p->nChar = 0; |
+ p->nAlloc = n; |
+ p->mxAlloc = mx; |
+ p->accError = 0; |
+ p->bMalloced = 0; |
+} |
+ |
+/* |
+** Print into memory obtained from sqliteMalloc(). Use the internal |
+** %-conversion extensions. |
+*/ |
+SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3 *db, const char *zFormat, va_list ap){ |
+ char *z; |
+ char zBase[SQLITE_PRINT_BUF_SIZE]; |
+ StrAccum acc; |
+ assert( db!=0 ); |
+ sqlite3StrAccumInit(&acc, db, zBase, sizeof(zBase), |
+ db->aLimit[SQLITE_LIMIT_LENGTH]); |
+ sqlite3VXPrintf(&acc, SQLITE_PRINTF_INTERNAL, zFormat, ap); |
+ z = sqlite3StrAccumFinish(&acc); |
+ if( acc.accError==STRACCUM_NOMEM ){ |
+ db->mallocFailed = 1; |
+ } |
+ return z; |
+} |
+ |
+/* |
+** Print into memory obtained from sqliteMalloc(). Use the internal |
+** %-conversion extensions. |
+*/ |
+SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3 *db, const char *zFormat, ...){ |
+ va_list ap; |
+ char *z; |
+ va_start(ap, zFormat); |
+ z = sqlite3VMPrintf(db, zFormat, ap); |
+ va_end(ap); |
+ return z; |
+} |
+ |
+/* |
+** Print into memory obtained from sqlite3_malloc(). Omit the internal |
+** %-conversion extensions. |
+*/ |
+SQLITE_API char *SQLITE_STDCALL sqlite3_vmprintf(const char *zFormat, va_list ap){ |
+ char *z; |
+ char zBase[SQLITE_PRINT_BUF_SIZE]; |
+ StrAccum acc; |
+ |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ if( zFormat==0 ){ |
+ (void)SQLITE_MISUSE_BKPT; |
+ return 0; |
+ } |
+#endif |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ sqlite3StrAccumInit(&acc, 0, zBase, sizeof(zBase), SQLITE_MAX_LENGTH); |
+ sqlite3VXPrintf(&acc, 0, zFormat, ap); |
+ z = sqlite3StrAccumFinish(&acc); |
+ return z; |
+} |
+ |
+/* |
+** Print into memory obtained from sqlite3_malloc()(). Omit the internal |
+** %-conversion extensions. |
+*/ |
+SQLITE_API char *SQLITE_CDECL sqlite3_mprintf(const char *zFormat, ...){ |
+ va_list ap; |
+ char *z; |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return 0; |
+#endif |
+ va_start(ap, zFormat); |
+ z = sqlite3_vmprintf(zFormat, ap); |
+ va_end(ap); |
+ return z; |
+} |
+ |
+/* |
+** sqlite3_snprintf() works like snprintf() except that it ignores the |
+** current locale settings. This is important for SQLite because we |
+** are not able to use a "," as the decimal point in place of "." as |
+** specified by some locales. |
+** |
+** Oops: The first two arguments of sqlite3_snprintf() are backwards |
+** from the snprintf() standard. Unfortunately, it is too late to change |
+** this without breaking compatibility, so we just have to live with the |
+** mistake. |
+** |
+** sqlite3_vsnprintf() is the varargs version. |
+*/ |
+SQLITE_API char *SQLITE_STDCALL sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){ |
+ StrAccum acc; |
+ if( n<=0 ) return zBuf; |
+#ifdef SQLITE_ENABLE_API_ARMOR |
+ if( zBuf==0 || zFormat==0 ) { |
+ (void)SQLITE_MISUSE_BKPT; |
+ if( zBuf ) zBuf[0] = 0; |
+ return zBuf; |
+ } |
+#endif |
+ sqlite3StrAccumInit(&acc, 0, zBuf, n, 0); |
+ sqlite3VXPrintf(&acc, 0, zFormat, ap); |
+ return sqlite3StrAccumFinish(&acc); |
+} |
+SQLITE_API char *SQLITE_CDECL sqlite3_snprintf(int n, char *zBuf, const char *zFormat, ...){ |
+ char *z; |
+ va_list ap; |
+ va_start(ap,zFormat); |
+ z = sqlite3_vsnprintf(n, zBuf, zFormat, ap); |
+ va_end(ap); |
+ return z; |
+} |
+ |
+/* |
+** This is the routine that actually formats the sqlite3_log() message. |
+** We house it in a separate routine from sqlite3_log() to avoid using |
+** stack space on small-stack systems when logging is disabled. |
+** |
+** sqlite3_log() must render into a static buffer. It cannot dynamically |
+** allocate memory because it might be called while the memory allocator |
+** mutex is held. |
+** |
+** sqlite3VXPrintf() might ask for *temporary* memory allocations for |
+** certain format characters (%q) or for very large precisions or widths. |
+** Care must be taken that any sqlite3_log() calls that occur while the |
+** memory mutex is held do not use these mechanisms. |
+*/ |
+static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ |
+ StrAccum acc; /* String accumulator */ |
+ char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */ |
+ |
+ sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); |
+ sqlite3VXPrintf(&acc, 0, zFormat, ap); |
+ sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode, |
+ sqlite3StrAccumFinish(&acc)); |
+} |
+ |
+/* |
+** Format and write a message to the log if logging is enabled. |
+*/ |
+SQLITE_API void SQLITE_CDECL sqlite3_log(int iErrCode, const char *zFormat, ...){ |
+ va_list ap; /* Vararg list */ |
+ if( sqlite3GlobalConfig.xLog ){ |
+ va_start(ap, zFormat); |
+ renderLogMsg(iErrCode, zFormat, ap); |
+ va_end(ap); |
+ } |
+} |
+ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
+/* |
+** A version of printf() that understands %lld. Used for debugging. |
+** The printf() built into some versions of windows does not understand %lld |
+** and segfaults if you give it a long long int. |
+*/ |
+SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){ |
+ va_list ap; |
+ StrAccum acc; |
+ char zBuf[500]; |
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); |
+ va_start(ap,zFormat); |
+ sqlite3VXPrintf(&acc, 0, zFormat, ap); |
+ va_end(ap); |
+ sqlite3StrAccumFinish(&acc); |
+ fprintf(stdout,"%s", zBuf); |
+ fflush(stdout); |
+} |
+#endif |
+ |
+ |
+/* |
+** variable-argument wrapper around sqlite3VXPrintf(). The bFlags argument |
+** can contain the bit SQLITE_PRINTF_INTERNAL enable internal formats. |
+*/ |
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, u32 bFlags, const char *zFormat, ...){ |
+ va_list ap; |
+ va_start(ap,zFormat); |
+ sqlite3VXPrintf(p, bFlags, zFormat, ap); |
+ va_end(ap); |
+} |
+ |
+/************** End of printf.c **********************************************/ |
+/************** Begin file treeview.c ****************************************/ |
+/* |
+** 2015-06-08 |
+** |
+** 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 C code to implement the TreeView debugging routines. |
+** These routines print a parse tree to standard output for debugging and |
+** analysis. |
+** |
+** The interfaces in this file is only available when compiling |
+** with SQLITE_DEBUG. |
+*/ |
+/* #include "sqliteInt.h" */ |
+#ifdef SQLITE_DEBUG |
+ |
+/* |
+** Add a new subitem to the tree. The moreToFollow flag indicates that this |
+** is not the last item in the tree. |
+*/ |
+static TreeView *sqlite3TreeViewPush(TreeView *p, u8 moreToFollow){ |
+ if( p==0 ){ |
+ p = sqlite3_malloc64( sizeof(*p) ); |
+ if( p==0 ) return 0; |
+ memset(p, 0, sizeof(*p)); |
+ }else{ |
+ p->iLevel++; |
+ } |
+ assert( moreToFollow==0 || moreToFollow==1 ); |
+ if( p->iLevel<sizeof(p->bLine) ) p->bLine[p->iLevel] = moreToFollow; |
+ return p; |
+} |
+ |
+/* |
+** Finished with one layer of the tree |
+*/ |
+static void sqlite3TreeViewPop(TreeView *p){ |
+ if( p==0 ) return; |
+ p->iLevel--; |
+ if( p->iLevel<0 ) sqlite3_free(p); |
+} |
+ |
+/* |
+** Generate a single line of output for the tree, with a prefix that contains |
+** all the appropriate tree lines |
+*/ |
+static void sqlite3TreeViewLine(TreeView *p, const char *zFormat, ...){ |
+ va_list ap; |
+ int i; |
+ StrAccum acc; |
+ char zBuf[500]; |
+ sqlite3StrAccumInit(&acc, 0, zBuf, sizeof(zBuf), 0); |
+ if( p ){ |
+ for(i=0; i<p->iLevel && i<sizeof(p->bLine)-1; i++){ |
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "| " : " ", 4); |
+ } |
+ sqlite3StrAccumAppend(&acc, p->bLine[i] ? "|-- " : "'-- ", 4); |
+ } |
+ va_start(ap, zFormat); |
+ sqlite3VXPrintf(&acc, 0, zFormat, ap); |
+ va_end(ap); |
+ if( zBuf[acc.nChar-1]!='\n' ) sqlite3StrAccumAppend(&acc, "\n", 1); |
+ sqlite3StrAccumFinish(&acc); |
+ fprintf(stdout,"%s", zBuf); |
+ fflush(stdout); |
+} |
+ |
+/* |
+** Shorthand for starting a new tree item that consists of a single label |
+*/ |
+static void sqlite3TreeViewItem(TreeView *p, const char *zLabel,u8 moreFollows){ |
+ p = sqlite3TreeViewPush(p, moreFollows); |
+ sqlite3TreeViewLine(p, "%s", zLabel); |
+} |
+ |
+/* |
+** Generate a human-readable description of a WITH clause. |
+*/ |
+SQLITE_PRIVATE void sqlite3TreeViewWith(TreeView *pView, const With *pWith, u8 moreToFollow){ |
+ int i; |
+ if( pWith==0 ) return; |
+ if( pWith->nCte==0 ) return; |
+ if( pWith->pOuter ){ |
+ sqlite3TreeViewLine(pView, "WITH (0x%p, pOuter=0x%p)",pWith,pWith->pOuter); |
+ }else{ |
+ sqlite3TreeViewLine(pView, "WITH (0x%p)", pWith); |
+ } |
+ if( pWith->nCte>0 ){ |
+ pView = sqlite3TreeViewPush(pView, 1); |
+ for(i=0; i<pWith->nCte; i++){ |
+ StrAccum x; |
+ char zLine[1000]; |
+ const struct Cte *pCte = &pWith->a[i]; |
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); |
+ sqlite3XPrintf(&x, 0, "%s", pCte->zName); |
+ if( pCte->pCols && pCte->pCols->nExpr>0 ){ |
+ char cSep = '('; |
+ int j; |
+ for(j=0; j<pCte->pCols->nExpr; j++){ |
+ sqlite3XPrintf(&x, 0, "%c%s", cSep, pCte->pCols->a[j].zName); |
+ cSep = ','; |
+ } |
+ sqlite3XPrintf(&x, 0, ")"); |
+ } |
+ sqlite3XPrintf(&x, 0, " AS"); |
+ sqlite3StrAccumFinish(&x); |
+ sqlite3TreeViewItem(pView, zLine, i<pWith->nCte-1); |
+ sqlite3TreeViewSelect(pView, pCte->pSelect, 0); |
+ sqlite3TreeViewPop(pView); |
+ } |
+ sqlite3TreeViewPop(pView); |
+ } |
+} |
+ |
+ |
+/* |
+** Generate a human-readable description of a the Select object. |
+*/ |
+SQLITE_PRIVATE void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){ |
+ int n = 0; |
+ int cnt = 0; |
+ pView = sqlite3TreeViewPush(pView, moreToFollow); |
+ if( p->pWith ){ |
+ sqlite3TreeViewWith(pView, p->pWith, 1); |
+ cnt = 1; |
+ sqlite3TreeViewPush(pView, 1); |
+ } |
+ do{ |
+ sqlite3TreeViewLine(pView, "SELECT%s%s (0x%p) selFlags=0x%x", |
+ ((p->selFlags & SF_Distinct) ? " DISTINCT" : ""), |
+ ((p->selFlags & SF_Aggregate) ? " agg_flag" : ""), p, p->selFlags |
+ ); |
+ if( cnt++ ) sqlite3TreeViewPop(pView); |
+ if( p->pPrior ){ |
+ n = 1000; |
+ }else{ |
+ n = 0; |
+ if( p->pSrc && p->pSrc->nSrc ) n++; |
+ if( p->pWhere ) n++; |
+ if( p->pGroupBy ) n++; |
+ if( p->pHaving ) n++; |
+ if( p->pOrderBy ) n++; |
+ if( p->pLimit ) n++; |
+ if( p->pOffset ) n++; |
+ } |
+ sqlite3TreeViewExprList(pView, p->pEList, (n--)>0, "result-set"); |
+ if( p->pSrc && p->pSrc->nSrc ){ |
+ int i; |
+ pView = sqlite3TreeViewPush(pView, (n--)>0); |
+ sqlite3TreeViewLine(pView, "FROM"); |
+ for(i=0; i<p->pSrc->nSrc; i++){ |
+ struct SrcList_item *pItem = &p->pSrc->a[i]; |
+ StrAccum x; |
+ char zLine[100]; |
+ sqlite3StrAccumInit(&x, 0, zLine, sizeof(zLine), 0); |
+ sqlite3XPrintf(&x, 0, "{%d,*}", pItem->iCursor); |
+ if( pItem->zDatabase ){ |
+ sqlite3XPrintf(&x, 0, " %s.%s", pItem->zDatabase, pItem->zName); |
+ }else if( pItem->zName ){ |
+ sqlite3XPrintf(&x, 0, " %s", pItem->zName); |
+ } |
+ if( pItem->pTab ){ |
+ sqlite3XPrintf(&x, 0, " tabname=%Q", pItem->pTab->zName); |
+ } |
+ if( pItem->zAlias ){ |
+ sqlite3XPrintf(&x, 0, " (AS %s)", pItem->zAlias); |
+ } |
+ if( pItem->fg.jointype & JT_LEFT ){ |
+ sqlite3XPrintf(&x, 0, " LEFT-JOIN"); |
+ } |
+ sqlite3StrAccumFinish(&x); |
+ sqlite3TreeViewItem(pView, zLine, i<p->pSrc->nSrc-1); |
+ if( pItem->pSelect ){ |
+ sqlite3TreeViewSelect(pView, pItem->pSelect, 0); |
+ } |
+ if( pItem->fg.isTabFunc ){ |
+ sqlite3TreeViewExprList(pView, pItem->u1.pFuncArg, 0, "func-args:"); |
+ } |
+ sqlite3TreeViewPop(pView); |
+ } |
+ sqlite3TreeViewPop(pView); |
+ } |
+ if( p->pWhere ){ |
+ sqlite3TreeViewItem(pView, "WHERE", (n--)>0); |
+ sqlite3TreeViewExpr(pView, p->pWhere, 0); |
+ sqlite3TreeViewPop(pView); |
+ } |
+ if( p->pGroupBy ){ |
+ sqlite3TreeViewExprList(pView, p->pGroupBy, (n--)>0, "GROUPBY"); |
+ } |
+ if( p->pHaving ){ |
+ sqlite3TreeViewItem(pView, "HAVING", (n--)>0); |
+ sqlite3TreeViewExpr(pView, p->pHaving, 0); |
+ sqlite3TreeViewPop(pView); |
+ } |
+ if( p->pOrderBy ){ |
+ sqlite3TreeViewExprList(pView, p->pOrderBy, (n--)>0, "ORDERBY"); |
+ } |
+ if( p->pLimit ){ |
+ sqlite3TreeViewItem(pView, "LIMIT", (n--)>0); |
+ sqlite3TreeViewExpr(pView, p->pLimit, 0); |
+ sqlite3TreeViewPop(pView); |
+ } |
+ if( p->pOffset ){ |
+ sqlite3TreeViewItem(pView, "OFFSET", (n--)>0); |
+ sqlite3TreeViewExpr(pView, p->pOffset, 0); |
+ sqlite3TreeViewPop(pView); |
+ } |
+ if( p->pPrior ){ |
+ const char *zOp = "UNION"; |
+ switch( p->op ){ |
+ case TK_ALL: zOp = "UNION ALL"; break; |
+ case TK_INTERSECT: zOp = "INTERSECT"; break; |
+ case TK_EXCEPT: zOp = "EXCEPT"; break; |
+ } |
+ sqlite3TreeViewItem(pView, zOp, 1); |
+ } |
+ p = p->pPrior; |
+ }while( p!=0 ); |
+ sqlite3TreeViewPop(pView); |
+} |
+ |
+/* |
+** Generate a human-readable explanation of an expression tree. |
+*/ |
+SQLITE_PRIVATE void sqlite3TreeViewExpr(TreeView *pView, const Expr *pExpr, u8 moreToFollow){ |
+ const char *zBinOp = 0; /* Binary operator */ |
+ const char *zUniOp = 0; /* Unary operator */ |
+ char zFlgs[30]; |
+ pView = sqlite3TreeViewPush(pView, moreToFollow); |
+ if( pExpr==0 ){ |
+ sqlite3TreeViewLine(pView, "nil"); |
+ sqlite3TreeViewPop(pView); |
+ return; |
+ } |
+ if( pExpr->flags ){ |
+ sqlite3_snprintf(sizeof(zFlgs),zFlgs," flags=0x%x",pExpr->flags); |
+ }else{ |
+ zFlgs[0] = 0; |
+ } |
+ switch( pExpr->op ){ |
+ case TK_AGG_COLUMN: { |
+ sqlite3TreeViewLine(pView, "AGG{%d:%d}%s", |
+ pExpr->iTable, pExpr->iColumn, zFlgs); |
+ break; |
+ } |
+ case TK_COLUMN: { |
+ if( pExpr->iTable<0 ){ |
+ /* This only happens when coding check constraints */ |
+ sqlite3TreeViewLine(pView, "COLUMN(%d)%s", pExpr->iColumn, zFlgs); |
+ }else{ |
+ sqlite3TreeViewLine(pView, "{%d:%d}%s", |
+ pExpr->iTable, pExpr->iColumn, zFlgs); |
+ } |
+ break; |
+ } |
+ case TK_INTEGER: { |
+ if( pExpr->flags & EP_IntValue ){ |
+ sqlite3TreeViewLine(pView, "%d", pExpr->u.iValue); |
+ }else{ |
+ sqlite3TreeViewLine(pView, "%s", pExpr->u.zToken); |
+ } |
+ break; |
+ } |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+ case TK_FLOAT: { |
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); |
+ break; |
+ } |
+#endif |
+ case TK_STRING: { |
+ sqlite3TreeViewLine(pView,"%Q", pExpr->u.zToken); |
+ break; |
+ } |
+ case TK_NULL: { |
+ sqlite3TreeViewLine(pView,"NULL"); |
+ break; |
+ } |
+#ifndef SQLITE_OMIT_BLOB_LITERAL |
+ case TK_BLOB: { |
+ sqlite3TreeViewLine(pView,"%s", pExpr->u.zToken); |
+ break; |
+ } |
+#endif |
+ case TK_VARIABLE: { |
+ sqlite3TreeViewLine(pView,"VARIABLE(%s,%d)", |
+ pExpr->u.zToken, pExpr->iColumn); |
+ break; |
+ } |
+ case TK_REGISTER: { |
+ sqlite3TreeViewLine(pView,"REGISTER(%d)", pExpr->iTable); |
+ break; |
+ } |
+ case TK_ID: { |
+ sqlite3TreeViewLine(pView,"ID \"%w\"", pExpr->u.zToken); |
+ break; |
+ } |
+#ifndef SQLITE_OMIT_CAST |
+ case TK_CAST: { |
+ /* Expressions of the form: CAST(pLeft AS token) */ |
+ sqlite3TreeViewLine(pView,"CAST %Q", pExpr->u.zToken); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); |
+ break; |
+ } |
+#endif /* SQLITE_OMIT_CAST */ |
+ case TK_LT: zBinOp = "LT"; break; |
+ case TK_LE: zBinOp = "LE"; break; |
+ case TK_GT: zBinOp = "GT"; break; |
+ case TK_GE: zBinOp = "GE"; break; |
+ case TK_NE: zBinOp = "NE"; break; |
+ case TK_EQ: zBinOp = "EQ"; break; |
+ case TK_IS: zBinOp = "IS"; break; |
+ case TK_ISNOT: zBinOp = "ISNOT"; break; |
+ case TK_AND: zBinOp = "AND"; break; |
+ case TK_OR: zBinOp = "OR"; break; |
+ case TK_PLUS: zBinOp = "ADD"; break; |
+ case TK_STAR: zBinOp = "MUL"; break; |
+ case TK_MINUS: zBinOp = "SUB"; break; |
+ case TK_REM: zBinOp = "REM"; break; |
+ case TK_BITAND: zBinOp = "BITAND"; break; |
+ case TK_BITOR: zBinOp = "BITOR"; break; |
+ case TK_SLASH: zBinOp = "DIV"; break; |
+ case TK_LSHIFT: zBinOp = "LSHIFT"; break; |
+ case TK_RSHIFT: zBinOp = "RSHIFT"; break; |
+ case TK_CONCAT: zBinOp = "CONCAT"; break; |
+ case TK_DOT: zBinOp = "DOT"; break; |
+ |
+ case TK_UMINUS: zUniOp = "UMINUS"; break; |
+ case TK_UPLUS: zUniOp = "UPLUS"; break; |
+ case TK_BITNOT: zUniOp = "BITNOT"; break; |
+ case TK_NOT: zUniOp = "NOT"; break; |
+ case TK_ISNULL: zUniOp = "ISNULL"; break; |
+ case TK_NOTNULL: zUniOp = "NOTNULL"; break; |
+ |
+ case TK_COLLATE: { |
+ sqlite3TreeViewLine(pView, "COLLATE %Q", pExpr->u.zToken); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); |
+ break; |
+ } |
+ |
+ case TK_AGG_FUNCTION: |
+ case TK_FUNCTION: { |
+ ExprList *pFarg; /* List of function arguments */ |
+ if( ExprHasProperty(pExpr, EP_TokenOnly) ){ |
+ pFarg = 0; |
+ }else{ |
+ pFarg = pExpr->x.pList; |
+ } |
+ if( pExpr->op==TK_AGG_FUNCTION ){ |
+ sqlite3TreeViewLine(pView, "AGG_FUNCTION%d %Q", |
+ pExpr->op2, pExpr->u.zToken); |
+ }else{ |
+ sqlite3TreeViewLine(pView, "FUNCTION %Q", pExpr->u.zToken); |
+ } |
+ if( pFarg ){ |
+ sqlite3TreeViewExprList(pView, pFarg, 0, 0); |
+ } |
+ break; |
+ } |
+#ifndef SQLITE_OMIT_SUBQUERY |
+ case TK_EXISTS: { |
+ sqlite3TreeViewLine(pView, "EXISTS-expr"); |
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); |
+ break; |
+ } |
+ case TK_SELECT: { |
+ sqlite3TreeViewLine(pView, "SELECT-expr"); |
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); |
+ break; |
+ } |
+ case TK_IN: { |
+ sqlite3TreeViewLine(pView, "IN"); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); |
+ if( ExprHasProperty(pExpr, EP_xIsSelect) ){ |
+ sqlite3TreeViewSelect(pView, pExpr->x.pSelect, 0); |
+ }else{ |
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); |
+ } |
+ break; |
+ } |
+#endif /* SQLITE_OMIT_SUBQUERY */ |
+ |
+ /* |
+ ** x BETWEEN y AND z |
+ ** |
+ ** This is equivalent to |
+ ** |
+ ** x>=y AND x<=z |
+ ** |
+ ** X is stored in pExpr->pLeft. |
+ ** Y is stored in pExpr->pList->a[0].pExpr. |
+ ** Z is stored in pExpr->pList->a[1].pExpr. |
+ */ |
+ case TK_BETWEEN: { |
+ Expr *pX = pExpr->pLeft; |
+ Expr *pY = pExpr->x.pList->a[0].pExpr; |
+ Expr *pZ = pExpr->x.pList->a[1].pExpr; |
+ sqlite3TreeViewLine(pView, "BETWEEN"); |
+ sqlite3TreeViewExpr(pView, pX, 1); |
+ sqlite3TreeViewExpr(pView, pY, 1); |
+ sqlite3TreeViewExpr(pView, pZ, 0); |
+ break; |
+ } |
+ case TK_TRIGGER: { |
+ /* If the opcode is TK_TRIGGER, then the expression is a reference |
+ ** to a column in the new.* or old.* pseudo-tables available to |
+ ** trigger programs. In this case Expr.iTable is set to 1 for the |
+ ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn |
+ ** is set to the column of the pseudo-table to read, or to -1 to |
+ ** read the rowid field. |
+ */ |
+ sqlite3TreeViewLine(pView, "%s(%d)", |
+ pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn); |
+ break; |
+ } |
+ case TK_CASE: { |
+ sqlite3TreeViewLine(pView, "CASE"); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); |
+ sqlite3TreeViewExprList(pView, pExpr->x.pList, 0, 0); |
+ break; |
+ } |
+#ifndef SQLITE_OMIT_TRIGGER |
+ case TK_RAISE: { |
+ const char *zType = "unk"; |
+ switch( pExpr->affinity ){ |
+ case OE_Rollback: zType = "rollback"; break; |
+ case OE_Abort: zType = "abort"; break; |
+ case OE_Fail: zType = "fail"; break; |
+ case OE_Ignore: zType = "ignore"; break; |
+ } |
+ sqlite3TreeViewLine(pView, "RAISE %s(%Q)", zType, pExpr->u.zToken); |
+ break; |
+ } |
+#endif |
+ default: { |
+ sqlite3TreeViewLine(pView, "op=%d", pExpr->op); |
+ break; |
+ } |
+ } |
+ if( zBinOp ){ |
+ sqlite3TreeViewLine(pView, "%s%s", zBinOp, zFlgs); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 1); |
+ sqlite3TreeViewExpr(pView, pExpr->pRight, 0); |
+ }else if( zUniOp ){ |
+ sqlite3TreeViewLine(pView, "%s%s", zUniOp, zFlgs); |
+ sqlite3TreeViewExpr(pView, pExpr->pLeft, 0); |
+ } |
+ sqlite3TreeViewPop(pView); |
+} |
+ |
+/* |
+** Generate a human-readable explanation of an expression list. |
+*/ |
+SQLITE_PRIVATE void sqlite3TreeViewExprList( |
+ TreeView *pView, |
+ const ExprList *pList, |
+ u8 moreToFollow, |
+ const char *zLabel |
+){ |
+ int i; |
+ pView = sqlite3TreeViewPush(pView, moreToFollow); |
+ if( zLabel==0 || zLabel[0]==0 ) zLabel = "LIST"; |
+ if( pList==0 ){ |
+ sqlite3TreeViewLine(pView, "%s (empty)", zLabel); |
+ }else{ |
+ sqlite3TreeViewLine(pView, "%s", zLabel); |
+ for(i=0; i<pList->nExpr; i++){ |
+ int j = pList->a[i].u.x.iOrderByCol; |
+ if( j ){ |
+ sqlite3TreeViewPush(pView, 0); |
+ sqlite3TreeViewLine(pView, "iOrderByCol=%d", j); |
+ } |
+ sqlite3TreeViewExpr(pView, pList->a[i].pExpr, i<pList->nExpr-1); |
+ if( j ) sqlite3TreeViewPop(pView); |
+ } |
+ } |
+ sqlite3TreeViewPop(pView); |
+} |
+ |
+#endif /* SQLITE_DEBUG */ |
+ |
+/************** End of treeview.c ********************************************/ |
+/************** Begin file random.c ******************************************/ |
+/* |
+** 2001 September 15 |
+** |
+** 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 code to implement a pseudo-random number |
+** generator (PRNG) for SQLite. |
+** |
+** Random numbers are used by some of the database backends in order |
+** to generate random integer keys for tables or random filenames. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+ |
+/* All threads share a single random number generator. |
+** This structure is the current state of the generator. |
+*/ |
+static SQLITE_WSD struct sqlite3PrngType { |
+ unsigned char isInit; /* True if initialized */ |
+ unsigned char i, j; /* State variables */ |
+ unsigned char s[256]; /* State variables */ |
+} sqlite3Prng; |
+ |
+/* |
+** Return N random bytes. |
+*/ |
+SQLITE_API void SQLITE_STDCALL sqlite3_randomness(int N, void *pBuf){ |
+ unsigned char t; |
+ unsigned char *zBuf = pBuf; |
+ |
+ /* The "wsdPrng" macro will resolve to the pseudo-random number generator |
+ ** state vector. If writable static data is unsupported on the target, |
+ ** we have to locate the state vector at run-time. In the more common |
+ ** case where writable static data is supported, wsdPrng can refer directly |
+ ** to the "sqlite3Prng" state vector declared above. |
+ */ |
+#ifdef SQLITE_OMIT_WSD |
+ struct sqlite3PrngType *p = &GLOBAL(struct sqlite3PrngType, sqlite3Prng); |
+# define wsdPrng p[0] |
+#else |
+# define wsdPrng sqlite3Prng |
+#endif |
+ |
+#if SQLITE_THREADSAFE |
+ sqlite3_mutex *mutex; |
+#endif |
+ |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ if( sqlite3_initialize() ) return; |
+#endif |
+ |
+#if SQLITE_THREADSAFE |
+ mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG); |
+#endif |
+ |
+ sqlite3_mutex_enter(mutex); |
+ if( N<=0 || pBuf==0 ){ |
+ wsdPrng.isInit = 0; |
+ sqlite3_mutex_leave(mutex); |
+ return; |
+ } |
+ |
+ /* Initialize the state of the random number generator once, |
+ ** the first time this routine is called. The seed value does |
+ ** not need to contain a lot of randomness since we are not |
+ ** trying to do secure encryption or anything like that... |
+ ** |
+ ** Nothing in this file or anywhere else in SQLite does any kind of |
+ ** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random |
+ ** number generator) not as an encryption device. |
+ */ |
+ if( !wsdPrng.isInit ){ |
+ int i; |
+ char k[256]; |
+ wsdPrng.j = 0; |
+ wsdPrng.i = 0; |
+ sqlite3OsRandomness(sqlite3_vfs_find(0), 256, k); |
+ for(i=0; i<256; i++){ |
+ wsdPrng.s[i] = (u8)i; |
+ } |
+ for(i=0; i<256; i++){ |
+ wsdPrng.j += wsdPrng.s[i] + k[i]; |
+ t = wsdPrng.s[wsdPrng.j]; |
+ wsdPrng.s[wsdPrng.j] = wsdPrng.s[i]; |
+ wsdPrng.s[i] = t; |
+ } |
+ wsdPrng.isInit = 1; |
+ } |
+ |
+ assert( N>0 ); |
+ do{ |
+ wsdPrng.i++; |
+ t = wsdPrng.s[wsdPrng.i]; |
+ wsdPrng.j += t; |
+ wsdPrng.s[wsdPrng.i] = wsdPrng.s[wsdPrng.j]; |
+ wsdPrng.s[wsdPrng.j] = t; |
+ t += wsdPrng.s[wsdPrng.i]; |
+ *(zBuf++) = wsdPrng.s[t]; |
+ }while( --N ); |
+ sqlite3_mutex_leave(mutex); |
+} |
+ |
+#ifndef SQLITE_OMIT_BUILTIN_TEST |
+/* |
+** For testing purposes, we sometimes want to preserve the state of |
+** PRNG and restore the PRNG to its saved state at a later time, or |
+** to reset the PRNG to its initial state. These routines accomplish |
+** those tasks. |
+** |
+** The sqlite3_test_control() interface calls these routines to |
+** control the PRNG. |
+*/ |
+static SQLITE_WSD struct sqlite3PrngType sqlite3SavedPrng; |
+SQLITE_PRIVATE void sqlite3PrngSaveState(void){ |
+ memcpy( |
+ &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), |
+ &GLOBAL(struct sqlite3PrngType, sqlite3Prng), |
+ sizeof(sqlite3Prng) |
+ ); |
+} |
+SQLITE_PRIVATE void sqlite3PrngRestoreState(void){ |
+ memcpy( |
+ &GLOBAL(struct sqlite3PrngType, sqlite3Prng), |
+ &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng), |
+ sizeof(sqlite3Prng) |
+ ); |
+} |
+#endif /* SQLITE_OMIT_BUILTIN_TEST */ |
+ |
+/************** End of random.c **********************************************/ |
+/************** Begin file threads.c *****************************************/ |
+/* |
+** 2012 July 21 |
+** |
+** 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 presents a simple cross-platform threading interface for |
+** use internally by SQLite. |
+** |
+** A "thread" can be created using sqlite3ThreadCreate(). This thread |
+** runs independently of its creator until it is joined using |
+** sqlite3ThreadJoin(), at which point it terminates. |
+** |
+** Threads do not have to be real. It could be that the work of the |
+** "thread" is done by the main thread at either the sqlite3ThreadCreate() |
+** or sqlite3ThreadJoin() call. This is, in fact, what happens in |
+** single threaded systems. Nothing in SQLite requires multiple threads. |
+** This interface exists so that applications that want to take advantage |
+** of multiple cores can do so, while also allowing applications to stay |
+** single-threaded if desired. |
+*/ |
+/* #include "sqliteInt.h" */ |
+#if SQLITE_OS_WIN |
+/* # include "os_win.h" */ |
+#endif |
+ |
+#if SQLITE_MAX_WORKER_THREADS>0 |
+ |
+/********************************* Unix Pthreads ****************************/ |
+#if SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) && SQLITE_THREADSAFE>0 |
+ |
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ |
+/* #include <pthread.h> */ |
+ |
+/* A running thread */ |
+struct SQLiteThread { |
+ pthread_t tid; /* Thread ID */ |
+ int done; /* Set to true when thread finishes */ |
+ void *pOut; /* Result returned by the thread */ |
+ void *(*xTask)(void*); /* The thread routine */ |
+ void *pIn; /* Argument to the thread */ |
+}; |
+ |
+/* Create a new thread */ |
+SQLITE_PRIVATE int sqlite3ThreadCreate( |
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */ |
+ void *(*xTask)(void*), /* Routine to run in a separate thread */ |
+ void *pIn /* Argument passed into xTask() */ |
+){ |
+ SQLiteThread *p; |
+ int rc; |
+ |
+ assert( ppThread!=0 ); |
+ assert( xTask!=0 ); |
+ /* This routine is never used in single-threaded mode */ |
+ assert( sqlite3GlobalConfig.bCoreMutex!=0 ); |
+ |
+ *ppThread = 0; |
+ p = sqlite3Malloc(sizeof(*p)); |
+ if( p==0 ) return SQLITE_NOMEM; |
+ memset(p, 0, sizeof(*p)); |
+ p->xTask = xTask; |
+ p->pIn = pIn; |
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a |
+ ** function that returns SQLITE_ERROR when passed the argument 200, that |
+ ** forces worker threads to run sequentially and deterministically |
+ ** for testing purposes. */ |
+ if( sqlite3FaultSim(200) ){ |
+ rc = 1; |
+ }else{ |
+ rc = pthread_create(&p->tid, 0, xTask, pIn); |
+ } |
+ if( rc ){ |
+ p->done = 1; |
+ p->pOut = xTask(pIn); |
+ } |
+ *ppThread = p; |
+ return SQLITE_OK; |
+} |
+ |
+/* Get the results of the thread */ |
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ |
+ int rc; |
+ |
+ assert( ppOut!=0 ); |
+ if( NEVER(p==0) ) return SQLITE_NOMEM; |
+ if( p->done ){ |
+ *ppOut = p->pOut; |
+ rc = SQLITE_OK; |
+ }else{ |
+ rc = pthread_join(p->tid, ppOut) ? SQLITE_ERROR : SQLITE_OK; |
+ } |
+ sqlite3_free(p); |
+ return rc; |
+} |
+ |
+#endif /* SQLITE_OS_UNIX && defined(SQLITE_MUTEX_PTHREADS) */ |
+/******************************** End Unix Pthreads *************************/ |
+ |
+ |
+/********************************* Win32 Threads ****************************/ |
+#if SQLITE_OS_WIN_THREADS |
+ |
+#define SQLITE_THREADS_IMPLEMENTED 1 /* Prevent the single-thread code below */ |
+#include <process.h> |
+ |
+/* A running thread */ |
+struct SQLiteThread { |
+ void *tid; /* The thread handle */ |
+ unsigned id; /* The thread identifier */ |
+ void *(*xTask)(void*); /* The routine to run as a thread */ |
+ void *pIn; /* Argument to xTask */ |
+ void *pResult; /* Result of xTask */ |
+}; |
+ |
+/* Thread procedure Win32 compatibility shim */ |
+static unsigned __stdcall sqlite3ThreadProc( |
+ void *pArg /* IN: Pointer to the SQLiteThread structure */ |
+){ |
+ SQLiteThread *p = (SQLiteThread *)pArg; |
+ |
+ assert( p!=0 ); |
+#if 0 |
+ /* |
+ ** This assert appears to trigger spuriously on certain |
+ ** versions of Windows, possibly due to _beginthreadex() |
+ ** and/or CreateThread() not fully setting their thread |
+ ** ID parameter before starting the thread. |
+ */ |
+ assert( p->id==GetCurrentThreadId() ); |
+#endif |
+ assert( p->xTask!=0 ); |
+ p->pResult = p->xTask(p->pIn); |
+ |
+ _endthreadex(0); |
+ return 0; /* NOT REACHED */ |
+} |
+ |
+/* Create a new thread */ |
+SQLITE_PRIVATE int sqlite3ThreadCreate( |
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */ |
+ void *(*xTask)(void*), /* Routine to run in a separate thread */ |
+ void *pIn /* Argument passed into xTask() */ |
+){ |
+ SQLiteThread *p; |
+ |
+ assert( ppThread!=0 ); |
+ assert( xTask!=0 ); |
+ *ppThread = 0; |
+ p = sqlite3Malloc(sizeof(*p)); |
+ if( p==0 ) return SQLITE_NOMEM; |
+ /* If the SQLITE_TESTCTRL_FAULT_INSTALL callback is registered to a |
+ ** function that returns SQLITE_ERROR when passed the argument 200, that |
+ ** forces worker threads to run sequentially and deterministically |
+ ** (via the sqlite3FaultSim() term of the conditional) for testing |
+ ** purposes. */ |
+ if( sqlite3GlobalConfig.bCoreMutex==0 || sqlite3FaultSim(200) ){ |
+ memset(p, 0, sizeof(*p)); |
+ }else{ |
+ p->xTask = xTask; |
+ p->pIn = pIn; |
+ p->tid = (void*)_beginthreadex(0, 0, sqlite3ThreadProc, p, 0, &p->id); |
+ if( p->tid==0 ){ |
+ memset(p, 0, sizeof(*p)); |
+ } |
+ } |
+ if( p->xTask==0 ){ |
+ p->id = GetCurrentThreadId(); |
+ p->pResult = xTask(pIn); |
+ } |
+ *ppThread = p; |
+ return SQLITE_OK; |
+} |
+ |
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject); /* os_win.c */ |
+ |
+/* Get the results of the thread */ |
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ |
+ DWORD rc; |
+ BOOL bRc; |
+ |
+ assert( ppOut!=0 ); |
+ if( NEVER(p==0) ) return SQLITE_NOMEM; |
+ if( p->xTask==0 ){ |
+ /* assert( p->id==GetCurrentThreadId() ); */ |
+ rc = WAIT_OBJECT_0; |
+ assert( p->tid==0 ); |
+ }else{ |
+ assert( p->id!=0 && p->id!=GetCurrentThreadId() ); |
+ rc = sqlite3Win32Wait((HANDLE)p->tid); |
+ assert( rc!=WAIT_IO_COMPLETION ); |
+ bRc = CloseHandle((HANDLE)p->tid); |
+ assert( bRc ); |
+ } |
+ if( rc==WAIT_OBJECT_0 ) *ppOut = p->pResult; |
+ sqlite3_free(p); |
+ return (rc==WAIT_OBJECT_0) ? SQLITE_OK : SQLITE_ERROR; |
+} |
+ |
+#endif /* SQLITE_OS_WIN_THREADS */ |
+/******************************** End Win32 Threads *************************/ |
+ |
+ |
+/********************************* Single-Threaded **************************/ |
+#ifndef SQLITE_THREADS_IMPLEMENTED |
+/* |
+** This implementation does not actually create a new thread. It does the |
+** work of the thread in the main thread, when either the thread is created |
+** or when it is joined |
+*/ |
+ |
+/* A running thread */ |
+struct SQLiteThread { |
+ void *(*xTask)(void*); /* The routine to run as a thread */ |
+ void *pIn; /* Argument to xTask */ |
+ void *pResult; /* Result of xTask */ |
+}; |
+ |
+/* Create a new thread */ |
+SQLITE_PRIVATE int sqlite3ThreadCreate( |
+ SQLiteThread **ppThread, /* OUT: Write the thread object here */ |
+ void *(*xTask)(void*), /* Routine to run in a separate thread */ |
+ void *pIn /* Argument passed into xTask() */ |
+){ |
+ SQLiteThread *p; |
+ |
+ assert( ppThread!=0 ); |
+ assert( xTask!=0 ); |
+ *ppThread = 0; |
+ p = sqlite3Malloc(sizeof(*p)); |
+ if( p==0 ) return SQLITE_NOMEM; |
+ if( (SQLITE_PTR_TO_INT(p)/17)&1 ){ |
+ p->xTask = xTask; |
+ p->pIn = pIn; |
+ }else{ |
+ p->xTask = 0; |
+ p->pResult = xTask(pIn); |
+ } |
+ *ppThread = p; |
+ return SQLITE_OK; |
+} |
+ |
+/* Get the results of the thread */ |
+SQLITE_PRIVATE int sqlite3ThreadJoin(SQLiteThread *p, void **ppOut){ |
+ |
+ assert( ppOut!=0 ); |
+ if( NEVER(p==0) ) return SQLITE_NOMEM; |
+ if( p->xTask ){ |
+ *ppOut = p->xTask(p->pIn); |
+ }else{ |
+ *ppOut = p->pResult; |
+ } |
+ sqlite3_free(p); |
+ |
+#if defined(SQLITE_TEST) |
+ { |
+ void *pTstAlloc = sqlite3Malloc(10); |
+ if (!pTstAlloc) return SQLITE_NOMEM; |
+ sqlite3_free(pTstAlloc); |
+ } |
+#endif |
+ |
+ return SQLITE_OK; |
+} |
+ |
+#endif /* !defined(SQLITE_THREADS_IMPLEMENTED) */ |
+/****************************** End Single-Threaded *************************/ |
+#endif /* SQLITE_MAX_WORKER_THREADS>0 */ |
+ |
+/************** End of threads.c *********************************************/ |
+/************** Begin file utf.c *********************************************/ |
+/* |
+** 2004 April 13 |
+** |
+** 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 routines used to translate between UTF-8, |
+** UTF-16, UTF-16BE, and UTF-16LE. |
+** |
+** Notes on UTF-8: |
+** |
+** Byte-0 Byte-1 Byte-2 Byte-3 Value |
+** 0xxxxxxx 00000000 00000000 0xxxxxxx |
+** 110yyyyy 10xxxxxx 00000000 00000yyy yyxxxxxx |
+** 1110zzzz 10yyyyyy 10xxxxxx 00000000 zzzzyyyy yyxxxxxx |
+** 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx 000uuuuu zzzzyyyy yyxxxxxx |
+** |
+** |
+** Notes on UTF-16: (with wwww+1==uuuuu) |
+** |
+** Word-0 Word-1 Value |
+** 110110ww wwzzzzyy 110111yy yyxxxxxx 000uuuuu zzzzyyyy yyxxxxxx |
+** zzzzyyyy yyxxxxxx 00000000 zzzzyyyy yyxxxxxx |
+** |
+** |
+** BOM or Byte Order Mark: |
+** 0xff 0xfe little-endian utf-16 follows |
+** 0xfe 0xff big-endian utf-16 follows |
+** |
+*/ |
+/* #include "sqliteInt.h" */ |
+/* #include <assert.h> */ |
+/* #include "vdbeInt.h" */ |
+ |
+#if !defined(SQLITE_AMALGAMATION) && SQLITE_BYTEORDER==0 |
+/* |
+** The following constant value is used by the SQLITE_BIGENDIAN and |
+** SQLITE_LITTLEENDIAN macros. |
+*/ |
+SQLITE_PRIVATE const int sqlite3one = 1; |
+#endif /* SQLITE_AMALGAMATION && SQLITE_BYTEORDER==0 */ |
+ |
+/* |
+** This lookup table is used to help decode the first byte of |
+** a multi-byte UTF8 character. |
+*/ |
+static const unsigned char sqlite3Utf8Trans1[] = { |
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, |
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, |
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, |
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, |
+ 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, |
+}; |
+ |
+ |
+#define WRITE_UTF8(zOut, c) { \ |
+ if( c<0x00080 ){ \ |
+ *zOut++ = (u8)(c&0xFF); \ |
+ } \ |
+ else if( c<0x00800 ){ \ |
+ *zOut++ = 0xC0 + (u8)((c>>6)&0x1F); \ |
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
+ } \ |
+ else if( c<0x10000 ){ \ |
+ *zOut++ = 0xE0 + (u8)((c>>12)&0x0F); \ |
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ |
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
+ }else{ \ |
+ *zOut++ = 0xF0 + (u8)((c>>18) & 0x07); \ |
+ *zOut++ = 0x80 + (u8)((c>>12) & 0x3F); \ |
+ *zOut++ = 0x80 + (u8)((c>>6) & 0x3F); \ |
+ *zOut++ = 0x80 + (u8)(c & 0x3F); \ |
+ } \ |
+} |
+ |
+#define WRITE_UTF16LE(zOut, c) { \ |
+ if( c<=0xFFFF ){ \ |
+ *zOut++ = (u8)(c&0x00FF); \ |
+ *zOut++ = (u8)((c>>8)&0x00FF); \ |
+ }else{ \ |
+ *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ |
+ *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ |
+ *zOut++ = (u8)(c&0x00FF); \ |
+ *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ |
+ } \ |
+} |
+ |
+#define WRITE_UTF16BE(zOut, c) { \ |
+ if( c<=0xFFFF ){ \ |
+ *zOut++ = (u8)((c>>8)&0x00FF); \ |
+ *zOut++ = (u8)(c&0x00FF); \ |
+ }else{ \ |
+ *zOut++ = (u8)(0x00D8 + (((c-0x10000)>>18)&0x03)); \ |
+ *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ |
+ *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ |
+ *zOut++ = (u8)(c&0x00FF); \ |
+ } \ |
+} |
+ |
+#define READ_UTF16LE(zIn, TERM, c){ \ |
+ c = (*zIn++); \ |
+ c += ((*zIn++)<<8); \ |
+ if( c>=0xD800 && c<0xE000 && TERM ){ \ |
+ int c2 = (*zIn++); \ |
+ c2 += ((*zIn++)<<8); \ |
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ |
+ } \ |
+} |
+ |
+#define READ_UTF16BE(zIn, TERM, c){ \ |
+ c = ((*zIn++)<<8); \ |
+ c += (*zIn++); \ |
+ if( c>=0xD800 && c<0xE000 && TERM ){ \ |
+ int c2 = ((*zIn++)<<8); \ |
+ c2 += (*zIn++); \ |
+ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ |
+ } \ |
+} |
+ |
+/* |
+** Translate a single UTF-8 character. Return the unicode value. |
+** |
+** During translation, assume that the byte that zTerm points |
+** is a 0x00. |
+** |
+** Write a pointer to the next unread byte back into *pzNext. |
+** |
+** Notes On Invalid UTF-8: |
+** |
+** * This routine never allows a 7-bit character (0x00 through 0x7f) to |
+** be encoded as a multi-byte character. Any multi-byte character that |
+** attempts to encode a value between 0x00 and 0x7f is rendered as 0xfffd. |
+** |
+** * This routine never allows a UTF16 surrogate value to be encoded. |
+** If a multi-byte character attempts to encode a value between |
+** 0xd800 and 0xe000 then it is rendered as 0xfffd. |
+** |
+** * Bytes in the range of 0x80 through 0xbf which occur as the first |
+** byte of a character are interpreted as single-byte characters |
+** and rendered as themselves even though they are technically |
+** invalid characters. |
+** |
+** * This routine accepts over-length UTF8 encodings |
+** for unicode values 0x80 and greater. It does not change over-length |
+** encodings to 0xfffd as some systems recommend. |
+*/ |
+#define READ_UTF8(zIn, zTerm, c) \ |
+ c = *(zIn++); \ |
+ if( c>=0xc0 ){ \ |
+ c = sqlite3Utf8Trans1[c-0xc0]; \ |
+ while( zIn!=zTerm && (*zIn & 0xc0)==0x80 ){ \ |
+ c = (c<<6) + (0x3f & *(zIn++)); \ |
+ } \ |
+ if( c<0x80 \ |
+ || (c&0xFFFFF800)==0xD800 \ |
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } \ |
+ } |
+SQLITE_PRIVATE u32 sqlite3Utf8Read( |
+ const unsigned char **pz /* Pointer to string from which to read char */ |
+){ |
+ unsigned int c; |
+ |
+ /* Same as READ_UTF8() above but without the zTerm parameter. |
+ ** For this routine, we assume the UTF8 string is always zero-terminated. |
+ */ |
+ c = *((*pz)++); |
+ if( c>=0xc0 ){ |
+ c = sqlite3Utf8Trans1[c-0xc0]; |
+ while( (*(*pz) & 0xc0)==0x80 ){ |
+ c = (c<<6) + (0x3f & *((*pz)++)); |
+ } |
+ if( c<0x80 |
+ || (c&0xFFFFF800)==0xD800 |
+ || (c&0xFFFFFFFE)==0xFFFE ){ c = 0xFFFD; } |
+ } |
+ return c; |
+} |
+ |
+ |
+ |
+ |
+/* |
+** If the TRANSLATE_TRACE macro is defined, the value of each Mem is |
+** printed on stderr on the way into and out of sqlite3VdbeMemTranslate(). |
+*/ |
+/* #define TRANSLATE_TRACE 1 */ |
+ |
+#ifndef SQLITE_OMIT_UTF16 |
+/* |
+** This routine transforms the internal text encoding used by pMem to |
+** desiredEnc. It is an error if the string is already of the desired |
+** encoding, or if *pMem does not contain a string value. |
+*/ |
+SQLITE_PRIVATE SQLITE_NOINLINE int sqlite3VdbeMemTranslate(Mem *pMem, u8 desiredEnc){ |
+ int len; /* Maximum length of output string in bytes */ |
+ unsigned char *zOut; /* Output buffer */ |
+ unsigned char *zIn; /* Input iterator */ |
+ unsigned char *zTerm; /* End of input */ |
+ unsigned char *z; /* Output iterator */ |
+ unsigned int c; |
+ |
+ assert( pMem->db==0 || sqlite3_mutex_held(pMem->db->mutex) ); |
+ assert( pMem->flags&MEM_Str ); |
+ assert( pMem->enc!=desiredEnc ); |
+ assert( pMem->enc!=0 ); |
+ assert( pMem->n>=0 ); |
+ |
+#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) |
+ { |
+ char zBuf[100]; |
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf); |
+ fprintf(stderr, "INPUT: %s\n", zBuf); |
+ } |
+#endif |
+ |
+ /* If the translation is between UTF-16 little and big endian, then |
+ ** all that is required is to swap the byte order. This case is handled |
+ ** differently from the others. |
+ */ |
+ if( pMem->enc!=SQLITE_UTF8 && desiredEnc!=SQLITE_UTF8 ){ |
+ u8 temp; |
+ int rc; |
+ rc = sqlite3VdbeMemMakeWriteable(pMem); |
+ if( rc!=SQLITE_OK ){ |
+ assert( rc==SQLITE_NOMEM ); |
+ return SQLITE_NOMEM; |
+ } |
+ zIn = (u8*)pMem->z; |
+ zTerm = &zIn[pMem->n&~1]; |
+ while( zIn<zTerm ){ |
+ temp = *zIn; |
+ *zIn = *(zIn+1); |
+ zIn++; |
+ *zIn++ = temp; |
+ } |
+ pMem->enc = desiredEnc; |
+ goto translate_out; |
+ } |
+ |
+ /* Set len to the maximum number of bytes required in the output buffer. */ |
+ if( desiredEnc==SQLITE_UTF8 ){ |
+ /* When converting from UTF-16, the maximum growth results from |
+ ** translating a 2-byte character to a 4-byte UTF-8 character. |
+ ** A single byte is required for the output string |
+ ** nul-terminator. |
+ */ |
+ pMem->n &= ~1; |
+ len = pMem->n * 2 + 1; |
+ }else{ |
+ /* When converting from UTF-8 to UTF-16 the maximum growth is caused |
+ ** when a 1-byte UTF-8 character is translated into a 2-byte UTF-16 |
+ ** character. Two bytes are required in the output buffer for the |
+ ** nul-terminator. |
+ */ |
+ len = pMem->n * 2 + 2; |
+ } |
+ |
+ /* Set zIn to point at the start of the input buffer and zTerm to point 1 |
+ ** byte past the end. |
+ ** |
+ ** Variable zOut is set to point at the output buffer, space obtained |
+ ** from sqlite3_malloc(). |
+ */ |
+ zIn = (u8*)pMem->z; |
+ zTerm = &zIn[pMem->n]; |
+ zOut = sqlite3DbMallocRaw(pMem->db, len); |
+ if( !zOut ){ |
+ return SQLITE_NOMEM; |
+ } |
+ z = zOut; |
+ |
+ if( pMem->enc==SQLITE_UTF8 ){ |
+ if( desiredEnc==SQLITE_UTF16LE ){ |
+ /* UTF-8 -> UTF-16 Little-endian */ |
+ while( zIn<zTerm ){ |
+ READ_UTF8(zIn, zTerm, c); |
+ WRITE_UTF16LE(z, c); |
+ } |
+ }else{ |
+ assert( desiredEnc==SQLITE_UTF16BE ); |
+ /* UTF-8 -> UTF-16 Big-endian */ |
+ while( zIn<zTerm ){ |
+ READ_UTF8(zIn, zTerm, c); |
+ WRITE_UTF16BE(z, c); |
+ } |
+ } |
+ pMem->n = (int)(z - zOut); |
+ *z++ = 0; |
+ }else{ |
+ assert( desiredEnc==SQLITE_UTF8 ); |
+ if( pMem->enc==SQLITE_UTF16LE ){ |
+ /* UTF-16 Little-endian -> UTF-8 */ |
+ while( zIn<zTerm ){ |
+ READ_UTF16LE(zIn, zIn<zTerm, c); |
+ WRITE_UTF8(z, c); |
+ } |
+ }else{ |
+ /* UTF-16 Big-endian -> UTF-8 */ |
+ while( zIn<zTerm ){ |
+ READ_UTF16BE(zIn, zIn<zTerm, c); |
+ WRITE_UTF8(z, c); |
+ } |
+ } |
+ pMem->n = (int)(z - zOut); |
+ } |
+ *z = 0; |
+ assert( (pMem->n+(desiredEnc==SQLITE_UTF8?1:2))<=len ); |
+ |
+ c = pMem->flags; |
+ sqlite3VdbeMemRelease(pMem); |
+ pMem->flags = MEM_Str|MEM_Term|(c&MEM_AffMask); |
+ pMem->enc = desiredEnc; |
+ pMem->z = (char*)zOut; |
+ pMem->zMalloc = pMem->z; |
+ pMem->szMalloc = sqlite3DbMallocSize(pMem->db, pMem->z); |
+ |
+translate_out: |
+#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG) |
+ { |
+ char zBuf[100]; |
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf); |
+ fprintf(stderr, "OUTPUT: %s\n", zBuf); |
+ } |
+#endif |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** This routine checks for a byte-order mark at the beginning of the |
+** UTF-16 string stored in *pMem. If one is present, it is removed and |
+** the encoding of the Mem adjusted. This routine does not do any |
+** byte-swapping, it just sets Mem.enc appropriately. |
+** |
+** The allocation (static, dynamic etc.) and encoding of the Mem may be |
+** changed by this function. |
+*/ |
+SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){ |
+ int rc = SQLITE_OK; |
+ u8 bom = 0; |
+ |
+ assert( pMem->n>=0 ); |
+ if( pMem->n>1 ){ |
+ u8 b1 = *(u8 *)pMem->z; |
+ u8 b2 = *(((u8 *)pMem->z) + 1); |
+ if( b1==0xFE && b2==0xFF ){ |
+ bom = SQLITE_UTF16BE; |
+ } |
+ if( b1==0xFF && b2==0xFE ){ |
+ bom = SQLITE_UTF16LE; |
+ } |
+ } |
+ |
+ if( bom ){ |
+ rc = sqlite3VdbeMemMakeWriteable(pMem); |
+ if( rc==SQLITE_OK ){ |
+ pMem->n -= 2; |
+ memmove(pMem->z, &pMem->z[2], pMem->n); |
+ pMem->z[pMem->n] = '\0'; |
+ pMem->z[pMem->n+1] = '\0'; |
+ pMem->flags |= MEM_Term; |
+ pMem->enc = bom; |
+ } |
+ } |
+ return rc; |
+} |
+#endif /* SQLITE_OMIT_UTF16 */ |
+ |
+/* |
+** pZ is a UTF-8 encoded unicode string. If nByte is less than zero, |
+** return the number of unicode characters in pZ up to (but not including) |
+** the first 0x00 byte. If nByte is not less than zero, return the |
+** number of unicode characters in the first nByte of pZ (or up to |
+** the first 0x00, whichever comes first). |
+*/ |
+SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *zIn, int nByte){ |
+ int r = 0; |
+ const u8 *z = (const u8*)zIn; |
+ const u8 *zTerm; |
+ if( nByte>=0 ){ |
+ zTerm = &z[nByte]; |
+ }else{ |
+ zTerm = (const u8*)(-1); |
+ } |
+ assert( z<=zTerm ); |
+ while( *z!=0 && z<zTerm ){ |
+ SQLITE_SKIP_UTF8(z); |
+ r++; |
+ } |
+ return r; |
+} |
+ |
+/* This test function is not currently used by the automated test-suite. |
+** Hence it is only available in debug builds. |
+*/ |
+#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG) |
+/* |
+** Translate UTF-8 to UTF-8. |
+** |
+** This has the effect of making sure that the string is well-formed |
+** UTF-8. Miscoded characters are removed. |
+** |
+** The translation is done in-place and aborted if the output |
+** overruns the input. |
+*/ |
+SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){ |
+ unsigned char *zOut = zIn; |
+ unsigned char *zStart = zIn; |
+ u32 c; |
+ |
+ while( zIn[0] && zOut<=zIn ){ |
+ c = sqlite3Utf8Read((const u8**)&zIn); |
+ if( c!=0xfffd ){ |
+ WRITE_UTF8(zOut, c); |
+ } |
+ } |
+ *zOut = 0; |
+ return (int)(zOut - zStart); |
+} |
+#endif |
+ |
+#ifndef SQLITE_OMIT_UTF16 |
+/* |
+** Convert a UTF-16 string in the native encoding into a UTF-8 string. |
+** Memory to hold the UTF-8 string is obtained from sqlite3_malloc and must |
+** be freed by the calling function. |
+** |
+** NULL is returned if there is an allocation error. |
+*/ |
+SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *db, const void *z, int nByte, u8 enc){ |
+ Mem m; |
+ memset(&m, 0, sizeof(m)); |
+ m.db = db; |
+ sqlite3VdbeMemSetStr(&m, z, nByte, enc, SQLITE_STATIC); |
+ sqlite3VdbeChangeEncoding(&m, SQLITE_UTF8); |
+ if( db->mallocFailed ){ |
+ sqlite3VdbeMemRelease(&m); |
+ m.z = 0; |
+ } |
+ assert( (m.flags & MEM_Term)!=0 || db->mallocFailed ); |
+ assert( (m.flags & MEM_Str)!=0 || db->mallocFailed ); |
+ assert( m.z || db->mallocFailed ); |
+ return m.z; |
+} |
+ |
+/* |
+** zIn is a UTF-16 encoded unicode string at least nChar characters long. |
+** Return the number of bytes in the first nChar unicode characters |
+** in pZ. nChar must be non-negative. |
+*/ |
+SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *zIn, int nChar){ |
+ int c; |
+ unsigned char const *z = zIn; |
+ int n = 0; |
+ |
+ if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ |
+ while( n<nChar ){ |
+ READ_UTF16BE(z, 1, c); |
+ n++; |
+ } |
+ }else{ |
+ while( n<nChar ){ |
+ READ_UTF16LE(z, 1, c); |
+ n++; |
+ } |
+ } |
+ return (int)(z-(unsigned char const *)zIn); |
+} |
+ |
+#if defined(SQLITE_TEST) |
+/* |
+** This routine is called from the TCL test function "translate_selftest". |
+** It checks that the primitives for serializing and deserializing |
+** characters in each encoding are inverses of each other. |
+*/ |
+SQLITE_PRIVATE void sqlite3UtfSelfTest(void){ |
+ unsigned int i, t; |
+ unsigned char zBuf[20]; |
+ unsigned char *z; |
+ int n; |
+ unsigned int c; |
+ |
+ for(i=0; i<0x00110000; i++){ |
+ z = zBuf; |
+ WRITE_UTF8(z, i); |
+ n = (int)(z-zBuf); |
+ assert( n>0 && n<=4 ); |
+ z[0] = 0; |
+ z = zBuf; |
+ c = sqlite3Utf8Read((const u8**)&z); |
+ t = i; |
+ if( i>=0xD800 && i<=0xDFFF ) t = 0xFFFD; |
+ if( (i&0xFFFFFFFE)==0xFFFE ) t = 0xFFFD; |
+ assert( c==t ); |
+ assert( (z-zBuf)==n ); |
+ } |
+ for(i=0; i<0x00110000; i++){ |
+ if( i>=0xD800 && i<0xE000 ) continue; |
+ z = zBuf; |
+ WRITE_UTF16LE(z, i); |
+ n = (int)(z-zBuf); |
+ assert( n>0 && n<=4 ); |
+ z[0] = 0; |
+ z = zBuf; |
+ READ_UTF16LE(z, 1, c); |
+ assert( c==i ); |
+ assert( (z-zBuf)==n ); |
+ } |
+ for(i=0; i<0x00110000; i++){ |
+ if( i>=0xD800 && i<0xE000 ) continue; |
+ z = zBuf; |
+ WRITE_UTF16BE(z, i); |
+ n = (int)(z-zBuf); |
+ assert( n>0 && n<=4 ); |
+ z[0] = 0; |
+ z = zBuf; |
+ READ_UTF16BE(z, 1, c); |
+ assert( c==i ); |
+ assert( (z-zBuf)==n ); |
+ } |
+} |
+#endif /* SQLITE_TEST */ |
+#endif /* SQLITE_OMIT_UTF16 */ |
+ |
+/************** End of utf.c *************************************************/ |
+/************** Begin file util.c ********************************************/ |
+/* |
+** 2001 September 15 |
+** |
+** 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. |
+** |
+************************************************************************* |
+** Utility functions used throughout sqlite. |
+** |
+** This file contains functions for allocating memory, comparing |
+** strings, and stuff like that. |
+** |
+*/ |
+/* #include "sqliteInt.h" */ |
+/* #include <stdarg.h> */ |
+#if HAVE_ISNAN || SQLITE_HAVE_ISNAN |
+# include <math.h> |
+#endif |
+ |
+/* |
+** Routine needed to support the testcase() macro. |
+*/ |
+#ifdef SQLITE_COVERAGE_TEST |
+SQLITE_PRIVATE void sqlite3Coverage(int x){ |
+ static unsigned dummy = 0; |
+ dummy += (unsigned)x; |
+} |
+#endif |
+ |
+/* |
+** Give a callback to the test harness that can be used to simulate faults |
+** in places where it is difficult or expensive to do so purely by means |
+** of inputs. |
+** |
+** The intent of the integer argument is to let the fault simulator know |
+** which of multiple sqlite3FaultSim() calls has been hit. |
+** |
+** Return whatever integer value the test callback returns, or return |
+** SQLITE_OK if no test callback is installed. |
+*/ |
+#ifndef SQLITE_OMIT_BUILTIN_TEST |
+SQLITE_PRIVATE int sqlite3FaultSim(int iTest){ |
+ int (*xCallback)(int) = sqlite3GlobalConfig.xTestCallback; |
+ return xCallback ? xCallback(iTest) : SQLITE_OK; |
+} |
+#endif |
+ |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+/* |
+** Return true if the floating point value is Not a Number (NaN). |
+** |
+** Use the math library isnan() function if compiled with SQLITE_HAVE_ISNAN. |
+** Otherwise, we have our own implementation that works on most systems. |
+*/ |
+SQLITE_PRIVATE int sqlite3IsNaN(double x){ |
+ int rc; /* The value return */ |
+#if !SQLITE_HAVE_ISNAN && !HAVE_ISNAN |
+ /* |
+ ** Systems that support the isnan() library function should probably |
+ ** make use of it by compiling with -DSQLITE_HAVE_ISNAN. But we have |
+ ** found that many systems do not have a working isnan() function so |
+ ** this implementation is provided as an alternative. |
+ ** |
+ ** This NaN test sometimes fails if compiled on GCC with -ffast-math. |
+ ** On the other hand, the use of -ffast-math comes with the following |
+ ** warning: |
+ ** |
+ ** This option [-ffast-math] should never be turned on by any |
+ ** -O option since it can result in incorrect output for programs |
+ ** which depend on an exact implementation of IEEE or ISO |
+ ** rules/specifications for math functions. |
+ ** |
+ ** Under MSVC, this NaN test may fail if compiled with a floating- |
+ ** point precision mode other than /fp:precise. From the MSDN |
+ ** documentation: |
+ ** |
+ ** The compiler [with /fp:precise] will properly handle comparisons |
+ ** involving NaN. For example, x != x evaluates to true if x is NaN |
+ ** ... |
+ */ |
+#ifdef __FAST_MATH__ |
+# error SQLite will not work correctly with the -ffast-math option of GCC. |
+#endif |
+ volatile double y = x; |
+ volatile double z = y; |
+ rc = (y!=z); |
+#else /* if HAVE_ISNAN */ |
+ rc = isnan(x); |
+#endif /* HAVE_ISNAN */ |
+ testcase( rc ); |
+ return rc; |
+} |
+#endif /* SQLITE_OMIT_FLOATING_POINT */ |
+ |
+/* |
+** Compute a string length that is limited to what can be stored in |
+** lower 30 bits of a 32-bit signed integer. |
+** |
+** The value returned will never be negative. Nor will it ever be greater |
+** than the actual length of the string. For very long strings (greater |
+** than 1GiB) the value returned might be less than the true string length. |
+*/ |
+SQLITE_PRIVATE int sqlite3Strlen30(const char *z){ |
+ if( z==0 ) return 0; |
+ return 0x3fffffff & (int)strlen(z); |
+} |
+ |
+/* |
+** Set the current error code to err_code and clear any prior error message. |
+*/ |
+SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){ |
+ assert( db!=0 ); |
+ db->errCode = err_code; |
+ if( db->pErr ) sqlite3ValueSetNull(db->pErr); |
+} |
+ |
+/* |
+** Set the most recent error code and error string for the sqlite |
+** handle "db". The error code is set to "err_code". |
+** |
+** If it is not NULL, string zFormat specifies the format of the |
+** error string in the style of the printf functions: The following |
+** format characters are allowed: |
+** |
+** %s Insert a string |
+** %z A string that should be freed after use |
+** %d Insert an integer |
+** %T Insert a token |
+** %S Insert the first element of a SrcList |
+** |
+** zFormat and any string tokens that follow it are assumed to be |
+** encoded in UTF-8. |
+** |
+** To clear the most recent error for sqlite handle "db", sqlite3Error |
+** should be called with err_code set to SQLITE_OK and zFormat set |
+** to NULL. |
+*/ |
+SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3 *db, int err_code, const char *zFormat, ...){ |
+ assert( db!=0 ); |
+ db->errCode = err_code; |
+ if( zFormat==0 ){ |
+ sqlite3Error(db, err_code); |
+ }else if( db->pErr || (db->pErr = sqlite3ValueNew(db))!=0 ){ |
+ char *z; |
+ va_list ap; |
+ va_start(ap, zFormat); |
+ z = sqlite3VMPrintf(db, zFormat, ap); |
+ va_end(ap); |
+ sqlite3ValueSetStr(db->pErr, -1, z, SQLITE_UTF8, SQLITE_DYNAMIC); |
+ } |
+} |
+ |
+/* |
+** Add an error message to pParse->zErrMsg and increment pParse->nErr. |
+** The following formatting characters are allowed: |
+** |
+** %s Insert a string |
+** %z A string that should be freed after use |
+** %d Insert an integer |
+** %T Insert a token |
+** %S Insert the first element of a SrcList |
+** |
+** This function should be used to report any error that occurs while |
+** compiling an SQL statement (i.e. within sqlite3_prepare()). The |
+** last thing the sqlite3_prepare() function does is copy the error |
+** stored by this function into the database handle using sqlite3Error(). |
+** Functions sqlite3Error() or sqlite3ErrorWithMsg() should be used |
+** during statement execution (sqlite3_step() etc.). |
+*/ |
+SQLITE_PRIVATE void sqlite3ErrorMsg(Parse *pParse, const char *zFormat, ...){ |
+ char *zMsg; |
+ va_list ap; |
+ sqlite3 *db = pParse->db; |
+ va_start(ap, zFormat); |
+ zMsg = sqlite3VMPrintf(db, zFormat, ap); |
+ va_end(ap); |
+ if( db->suppressErr ){ |
+ sqlite3DbFree(db, zMsg); |
+ }else{ |
+ pParse->nErr++; |
+ sqlite3DbFree(db, pParse->zErrMsg); |
+ pParse->zErrMsg = zMsg; |
+ pParse->rc = SQLITE_ERROR; |
+ } |
+} |
+ |
+/* |
+** Convert an SQL-style quoted string into a normal string by removing |
+** the quote characters. The conversion is done in-place. If the |
+** input does not begin with a quote character, then this routine |
+** is a no-op. |
+** |
+** The input string must be zero-terminated. A new zero-terminator |
+** is added to the dequoted string. |
+** |
+** The return value is -1 if no dequoting occurs or the length of the |
+** dequoted string, exclusive of the zero terminator, if dequoting does |
+** occur. |
+** |
+** 2002-Feb-14: This routine is extended to remove MS-Access style |
+** brackets from around identifiers. For example: "[a-b-c]" becomes |
+** "a-b-c". |
+*/ |
+SQLITE_PRIVATE int sqlite3Dequote(char *z){ |
+ char quote; |
+ int i, j; |
+ if( z==0 ) return -1; |
+ quote = z[0]; |
+ switch( quote ){ |
+ case '\'': break; |
+ case '"': break; |
+ case '`': break; /* For MySQL compatibility */ |
+ case '[': quote = ']'; break; /* For MS SqlServer compatibility */ |
+ default: return -1; |
+ } |
+ for(i=1, j=0;; i++){ |
+ assert( z[i] ); |
+ if( z[i]==quote ){ |
+ if( z[i+1]==quote ){ |
+ z[j++] = quote; |
+ i++; |
+ }else{ |
+ break; |
+ } |
+ }else{ |
+ z[j++] = z[i]; |
+ } |
+ } |
+ z[j] = 0; |
+ return j; |
+} |
+ |
+/* Convenient short-hand */ |
+#define UpperToLower sqlite3UpperToLower |
+ |
+/* |
+** Some systems have stricmp(). Others have strcasecmp(). Because |
+** there is no consistency, we will define our own. |
+** |
+** IMPLEMENTATION-OF: R-30243-02494 The sqlite3_stricmp() and |
+** sqlite3_strnicmp() APIs allow applications and extensions to compare |
+** the contents of two buffers containing UTF-8 strings in a |
+** case-independent fashion, using the same definition of "case |
+** independence" that SQLite uses internally when comparing identifiers. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_stricmp(const char *zLeft, const char *zRight){ |
+ register unsigned char *a, *b; |
+ if( zLeft==0 ){ |
+ return zRight ? -1 : 0; |
+ }else if( zRight==0 ){ |
+ return 1; |
+ } |
+ a = (unsigned char *)zLeft; |
+ b = (unsigned char *)zRight; |
+ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
+ return UpperToLower[*a] - UpperToLower[*b]; |
+} |
+SQLITE_API int SQLITE_STDCALL sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ |
+ register unsigned char *a, *b; |
+ if( zLeft==0 ){ |
+ return zRight ? -1 : 0; |
+ }else if( zRight==0 ){ |
+ return 1; |
+ } |
+ a = (unsigned char *)zLeft; |
+ b = (unsigned char *)zRight; |
+ while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } |
+ return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b]; |
+} |
+ |
+/* |
+** The string z[] is an text representation of a real number. |
+** Convert this string to a double and write it into *pResult. |
+** |
+** The string z[] is length bytes in length (bytes, not characters) and |
+** uses the encoding enc. The string is not necessarily zero-terminated. |
+** |
+** Return TRUE if the result is a valid real number (or integer) and FALSE |
+** if the string is empty or contains extraneous text. Valid numbers |
+** are in one of these formats: |
+** |
+** [+-]digits[E[+-]digits] |
+** [+-]digits.[digits][E[+-]digits] |
+** [+-].digits[E[+-]digits] |
+** |
+** Leading and trailing whitespace is ignored for the purpose of determining |
+** validity. |
+** |
+** If some prefix of the input string is a valid number, this routine |
+** returns FALSE but it still converts the prefix and writes the result |
+** into *pResult. |
+*/ |
+SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult, int length, u8 enc){ |
+#ifndef SQLITE_OMIT_FLOATING_POINT |
+ int incr; |
+ const char *zEnd = z + length; |
+ /* sign * significand * (10 ^ (esign * exponent)) */ |
+ int sign = 1; /* sign of significand */ |
+ i64 s = 0; /* significand */ |
+ int d = 0; /* adjust exponent for shifting decimal point */ |
+ int esign = 1; /* sign of exponent */ |
+ int e = 0; /* exponent */ |
+ int eValid = 1; /* True exponent is either not used or is well-formed */ |
+ double result; |
+ int nDigits = 0; |
+ int nonNum = 0; |
+ |
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
+ *pResult = 0.0; /* Default return value, in case of an error */ |
+ |
+ if( enc==SQLITE_UTF8 ){ |
+ incr = 1; |
+ }else{ |
+ int i; |
+ incr = 2; |
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); |
+ for(i=3-enc; i<length && z[i]==0; i+=2){} |
+ nonNum = i<length; |
+ zEnd = z+i+enc-3; |
+ z += (enc&1); |
+ } |
+ |
+ /* skip leading spaces */ |
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; |
+ if( z>=zEnd ) return 0; |
+ |
+ /* get sign of significand */ |
+ if( *z=='-' ){ |
+ sign = -1; |
+ z+=incr; |
+ }else if( *z=='+' ){ |
+ z+=incr; |
+ } |
+ |
+ /* skip leading zeroes */ |
+ while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++; |
+ |
+ /* copy max significant digits to significand */ |
+ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
+ s = s*10 + (*z - '0'); |
+ z+=incr, nDigits++; |
+ } |
+ |
+ /* skip non-significant significand digits |
+ ** (increase exponent by d to shift decimal left) */ |
+ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++, d++; |
+ if( z>=zEnd ) goto do_atof_calc; |
+ |
+ /* if decimal point is present */ |
+ if( *z=='.' ){ |
+ z+=incr; |
+ /* copy digits from after decimal to significand |
+ ** (decrease exponent by d to shift decimal right) */ |
+ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ |
+ s = s*10 + (*z - '0'); |
+ z+=incr, nDigits++, d--; |
+ } |
+ /* skip non-significant digits */ |
+ while( z<zEnd && sqlite3Isdigit(*z) ) z+=incr, nDigits++; |
+ } |
+ if( z>=zEnd ) goto do_atof_calc; |
+ |
+ /* if exponent is present */ |
+ if( *z=='e' || *z=='E' ){ |
+ z+=incr; |
+ eValid = 0; |
+ if( z>=zEnd ) goto do_atof_calc; |
+ /* get sign of exponent */ |
+ if( *z=='-' ){ |
+ esign = -1; |
+ z+=incr; |
+ }else if( *z=='+' ){ |
+ z+=incr; |
+ } |
+ /* copy digits to exponent */ |
+ while( z<zEnd && sqlite3Isdigit(*z) ){ |
+ e = e<10000 ? (e*10 + (*z - '0')) : 10000; |
+ z+=incr; |
+ eValid = 1; |
+ } |
+ } |
+ |
+ /* skip trailing spaces */ |
+ if( nDigits && eValid ){ |
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr; |
+ } |
+ |
+do_atof_calc: |
+ /* adjust exponent by d, and update sign */ |
+ e = (e*esign) + d; |
+ if( e<0 ) { |
+ esign = -1; |
+ e *= -1; |
+ } else { |
+ esign = 1; |
+ } |
+ |
+ /* if 0 significand */ |
+ if( !s ) { |
+ /* In the IEEE 754 standard, zero is signed. |
+ ** Add the sign if we've seen at least one digit */ |
+ result = (sign<0 && nDigits) ? -(double)0 : (double)0; |
+ } else { |
+ /* attempt to reduce exponent */ |
+ if( esign>0 ){ |
+ while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10; |
+ }else{ |
+ while( !(s%10) && e>0 ) e--,s/=10; |
+ } |
+ |
+ /* adjust the sign of significand */ |
+ s = sign<0 ? -s : s; |
+ |
+ /* if exponent, scale significand as appropriate |
+ ** and store in result. */ |
+ if( e ){ |
+ LONGDOUBLE_TYPE scale = 1.0; |
+ /* attempt to handle extremely small/large numbers better */ |
+ if( e>307 && e<342 ){ |
+ while( e%308 ) { scale *= 1.0e+1; e -= 1; } |
+ if( esign<0 ){ |
+ result = s / scale; |
+ result /= 1.0e+308; |
+ }else{ |
+ result = s * scale; |
+ result *= 1.0e+308; |
+ } |
+ }else if( e>=342 ){ |
+ if( esign<0 ){ |
+ result = 0.0*s; |
+ }else{ |
+ result = 1e308*1e308*s; /* Infinity */ |
+ } |
+ }else{ |
+ /* 1.0e+22 is the largest power of 10 than can be |
+ ** represented exactly. */ |
+ while( e%22 ) { scale *= 1.0e+1; e -= 1; } |
+ while( e>0 ) { scale *= 1.0e+22; e -= 22; } |
+ if( esign<0 ){ |
+ result = s / scale; |
+ }else{ |
+ result = s * scale; |
+ } |
+ } |
+ } else { |
+ result = (double)s; |
+ } |
+ } |
+ |
+ /* store the result */ |
+ *pResult = result; |
+ |
+ /* return true if number and no extra non-whitespace chracters after */ |
+ return z>=zEnd && nDigits>0 && eValid && nonNum==0; |
+#else |
+ return !sqlite3Atoi64(z, pResult, length, enc); |
+#endif /* SQLITE_OMIT_FLOATING_POINT */ |
+} |
+ |
+/* |
+** Compare the 19-character string zNum against the text representation |
+** value 2^63: 9223372036854775808. Return negative, zero, or positive |
+** if zNum is less than, equal to, or greater than the string. |
+** Note that zNum must contain exactly 19 characters. |
+** |
+** Unlike memcmp() this routine is guaranteed to return the difference |
+** in the values of the last digit if the only difference is in the |
+** last digit. So, for example, |
+** |
+** compare2pow63("9223372036854775800", 1) |
+** |
+** will return -8. |
+*/ |
+static int compare2pow63(const char *zNum, int incr){ |
+ int c = 0; |
+ int i; |
+ /* 012345678901234567 */ |
+ const char *pow63 = "922337203685477580"; |
+ for(i=0; c==0 && i<18; i++){ |
+ c = (zNum[i*incr]-pow63[i])*10; |
+ } |
+ if( c==0 ){ |
+ c = zNum[18*incr] - '8'; |
+ testcase( c==(-1) ); |
+ testcase( c==0 ); |
+ testcase( c==(+1) ); |
+ } |
+ return c; |
+} |
+ |
+/* |
+** Convert zNum to a 64-bit signed integer. zNum must be decimal. This |
+** routine does *not* accept hexadecimal notation. |
+** |
+** If the zNum value is representable as a 64-bit twos-complement |
+** integer, then write that value into *pNum and return 0. |
+** |
+** If zNum is exactly 9223372036854775808, return 2. This special |
+** case is broken out because while 9223372036854775808 cannot be a |
+** signed 64-bit integer, its negative -9223372036854775808 can be. |
+** |
+** If zNum is too big for a 64-bit integer and is not |
+** 9223372036854775808 or if zNum contains any non-numeric text, |
+** then return 1. |
+** |
+** length is the number of bytes in the string (bytes, not characters). |
+** The string is not necessarily zero-terminated. The encoding is |
+** given by enc. |
+*/ |
+SQLITE_PRIVATE int sqlite3Atoi64(const char *zNum, i64 *pNum, int length, u8 enc){ |
+ int incr; |
+ u64 u = 0; |
+ int neg = 0; /* assume positive */ |
+ int i; |
+ int c = 0; |
+ int nonNum = 0; |
+ const char *zStart; |
+ const char *zEnd = zNum + length; |
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE ); |
+ if( enc==SQLITE_UTF8 ){ |
+ incr = 1; |
+ }else{ |
+ incr = 2; |
+ assert( SQLITE_UTF16LE==2 && SQLITE_UTF16BE==3 ); |
+ for(i=3-enc; i<length && zNum[i]==0; i+=2){} |
+ nonNum = i<length; |
+ zEnd = zNum+i+enc-3; |
+ zNum += (enc&1); |
+ } |
+ while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr; |
+ if( zNum<zEnd ){ |
+ if( *zNum=='-' ){ |
+ neg = 1; |
+ zNum+=incr; |
+ }else if( *zNum=='+' ){ |
+ zNum+=incr; |
+ } |
+ } |
+ zStart = zNum; |
+ while( zNum<zEnd && zNum[0]=='0' ){ zNum+=incr; } /* Skip leading zeros. */ |
+ for(i=0; &zNum[i]<zEnd && (c=zNum[i])>='0' && c<='9'; i+=incr){ |
+ u = u*10 + c - '0'; |
+ } |
+ if( u>LARGEST_INT64 ){ |
+ *pNum = neg ? SMALLEST_INT64 : LARGEST_INT64; |
+ }else if( neg ){ |
+ *pNum = -(i64)u; |
+ }else{ |
+ *pNum = (i64)u; |
+ } |
+ testcase( i==18 ); |
+ testcase( i==19 ); |
+ testcase( i==20 ); |
+ if( (c!=0 && &zNum[i]<zEnd) || (i==0 && zStart==zNum) |
+ || i>19*incr || nonNum ){ |
+ /* zNum is empty or contains non-numeric text or is longer |
+ ** than 19 digits (thus guaranteeing that it is too large) */ |
+ return 1; |
+ }else if( i<19*incr ){ |
+ /* Less than 19 digits, so we know that it fits in 64 bits */ |
+ assert( u<=LARGEST_INT64 ); |
+ return 0; |
+ }else{ |
+ /* zNum is a 19-digit numbers. Compare it against 9223372036854775808. */ |
+ c = compare2pow63(zNum, incr); |
+ if( c<0 ){ |
+ /* zNum is less than 9223372036854775808 so it fits */ |
+ assert( u<=LARGEST_INT64 ); |
+ return 0; |
+ }else if( c>0 ){ |
+ /* zNum is greater than 9223372036854775808 so it overflows */ |
+ return 1; |
+ }else{ |
+ /* zNum is exactly 9223372036854775808. Fits if negative. The |
+ ** special case 2 overflow if positive */ |
+ assert( u-1==LARGEST_INT64 ); |
+ return neg ? 0 : 2; |
+ } |
+ } |
+} |
+ |
+/* |
+** Transform a UTF-8 integer literal, in either decimal or hexadecimal, |
+** into a 64-bit signed integer. This routine accepts hexadecimal literals, |
+** whereas sqlite3Atoi64() does not. |
+** |
+** Returns: |
+** |
+** 0 Successful transformation. Fits in a 64-bit signed integer. |
+** 1 Integer too large for a 64-bit signed integer or is malformed |
+** 2 Special case of 9223372036854775808 |
+*/ |
+SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char *z, i64 *pOut){ |
+#ifndef SQLITE_OMIT_HEX_INTEGER |
+ if( z[0]=='0' |
+ && (z[1]=='x' || z[1]=='X') |
+ && sqlite3Isxdigit(z[2]) |
+ ){ |
+ u64 u = 0; |
+ int i, k; |
+ for(i=2; z[i]=='0'; i++){} |
+ for(k=i; sqlite3Isxdigit(z[k]); k++){ |
+ u = u*16 + sqlite3HexToInt(z[k]); |
+ } |
+ memcpy(pOut, &u, 8); |
+ return (z[k]==0 && k-i<=16) ? 0 : 1; |
+ }else |
+#endif /* SQLITE_OMIT_HEX_INTEGER */ |
+ { |
+ return sqlite3Atoi64(z, pOut, sqlite3Strlen30(z), SQLITE_UTF8); |
+ } |
+} |
+ |
+/* |
+** If zNum represents an integer that will fit in 32-bits, then set |
+** *pValue to that integer and return true. Otherwise return false. |
+** |
+** This routine accepts both decimal and hexadecimal notation for integers. |
+** |
+** Any non-numeric characters that following zNum are ignored. |
+** This is different from sqlite3Atoi64() which requires the |
+** input number to be zero-terminated. |
+*/ |
+SQLITE_PRIVATE int sqlite3GetInt32(const char *zNum, int *pValue){ |
+ sqlite_int64 v = 0; |
+ int i, c; |
+ int neg = 0; |
+ if( zNum[0]=='-' ){ |
+ neg = 1; |
+ zNum++; |
+ }else if( zNum[0]=='+' ){ |
+ zNum++; |
+ } |
+#ifndef SQLITE_OMIT_HEX_INTEGER |
+ else if( zNum[0]=='0' |
+ && (zNum[1]=='x' || zNum[1]=='X') |
+ && sqlite3Isxdigit(zNum[2]) |
+ ){ |
+ u32 u = 0; |
+ zNum += 2; |
+ while( zNum[0]=='0' ) zNum++; |
+ for(i=0; sqlite3Isxdigit(zNum[i]) && i<8; i++){ |
+ u = u*16 + sqlite3HexToInt(zNum[i]); |
+ } |
+ if( (u&0x80000000)==0 && sqlite3Isxdigit(zNum[i])==0 ){ |
+ memcpy(pValue, &u, 4); |
+ return 1; |
+ }else{ |
+ return 0; |
+ } |
+ } |
+#endif |
+ while( zNum[0]=='0' ) zNum++; |
+ for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){ |
+ v = v*10 + c; |
+ } |
+ |
+ /* The longest decimal representation of a 32 bit integer is 10 digits: |
+ ** |
+ ** 1234567890 |
+ ** 2^31 -> 2147483648 |
+ */ |
+ testcase( i==10 ); |
+ if( i>10 ){ |
+ return 0; |
+ } |
+ testcase( v-neg==2147483647 ); |
+ if( v-neg>2147483647 ){ |
+ return 0; |
+ } |
+ if( neg ){ |
+ v = -v; |
+ } |
+ *pValue = (int)v; |
+ return 1; |
+} |
+ |
+/* |
+** Return a 32-bit integer value extracted from a string. If the |
+** string is not an integer, just return 0. |
+*/ |
+SQLITE_PRIVATE int sqlite3Atoi(const char *z){ |
+ int x = 0; |
+ if( z ) sqlite3GetInt32(z, &x); |
+ return x; |
+} |
+ |
+/* |
+** The variable-length integer encoding is as follows: |
+** |
+** KEY: |
+** A = 0xxxxxxx 7 bits of data and one flag bit |
+** B = 1xxxxxxx 7 bits of data and one flag bit |
+** C = xxxxxxxx 8 bits of data |
+** |
+** 7 bits - A |
+** 14 bits - BA |
+** 21 bits - BBA |
+** 28 bits - BBBA |
+** 35 bits - BBBBA |
+** 42 bits - BBBBBA |
+** 49 bits - BBBBBBA |
+** 56 bits - BBBBBBBA |
+** 64 bits - BBBBBBBBC |
+*/ |
+ |
+/* |
+** Write a 64-bit variable-length integer to memory starting at p[0]. |
+** The length of data write will be between 1 and 9 bytes. The number |
+** of bytes written is returned. |
+** |
+** A variable-length integer consists of the lower 7 bits of each byte |
+** for all bytes that have the 8th bit set and one byte with the 8th |
+** bit clear. Except, if we get to the 9th byte, it stores the full |
+** 8 bits and is the last byte. |
+*/ |
+static int SQLITE_NOINLINE putVarint64(unsigned char *p, u64 v){ |
+ int i, j, n; |
+ u8 buf[10]; |
+ if( v & (((u64)0xff000000)<<32) ){ |
+ p[8] = (u8)v; |
+ v >>= 8; |
+ for(i=7; i>=0; i--){ |
+ p[i] = (u8)((v & 0x7f) | 0x80); |
+ v >>= 7; |
+ } |
+ return 9; |
+ } |
+ n = 0; |
+ do{ |
+ buf[n++] = (u8)((v & 0x7f) | 0x80); |
+ v >>= 7; |
+ }while( v!=0 ); |
+ buf[0] &= 0x7f; |
+ assert( n<=9 ); |
+ for(i=0, j=n-1; j>=0; j--, i++){ |
+ p[i] = buf[j]; |
+ } |
+ return n; |
+} |
+SQLITE_PRIVATE int sqlite3PutVarint(unsigned char *p, u64 v){ |
+ if( v<=0x7f ){ |
+ p[0] = v&0x7f; |
+ return 1; |
+ } |
+ if( v<=0x3fff ){ |
+ p[0] = ((v>>7)&0x7f)|0x80; |
+ p[1] = v&0x7f; |
+ return 2; |
+ } |
+ return putVarint64(p,v); |
+} |
+ |
+/* |
+** Bitmasks used by sqlite3GetVarint(). These precomputed constants |
+** are defined here rather than simply putting the constant expressions |
+** inline in order to work around bugs in the RVT compiler. |
+** |
+** SLOT_2_0 A mask for (0x7f<<14) | 0x7f |
+** |
+** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0 |
+*/ |
+#define SLOT_2_0 0x001fc07f |
+#define SLOT_4_2_0 0xf01fc07f |
+ |
+ |
+/* |
+** Read a 64-bit variable-length integer from memory starting at p[0]. |
+** Return the number of bytes read. The value is stored in *v. |
+*/ |
+SQLITE_PRIVATE u8 sqlite3GetVarint(const unsigned char *p, u64 *v){ |
+ u32 a,b,s; |
+ |
+ a = *p; |
+ /* a: p0 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ *v = a; |
+ return 1; |
+ } |
+ |
+ p++; |
+ b = *p; |
+ /* b: p1 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ a &= 0x7f; |
+ a = a<<7; |
+ a |= b; |
+ *v = a; |
+ return 2; |
+ } |
+ |
+ /* Verify that constants are precomputed correctly */ |
+ assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) ); |
+ assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) ); |
+ |
+ p++; |
+ a = a<<14; |
+ a |= *p; |
+ /* a: p0<<14 | p2 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ a &= SLOT_2_0; |
+ b &= 0x7f; |
+ b = b<<7; |
+ a |= b; |
+ *v = a; |
+ return 3; |
+ } |
+ |
+ /* CSE1 from below */ |
+ a &= SLOT_2_0; |
+ p++; |
+ b = b<<14; |
+ b |= *p; |
+ /* b: p1<<14 | p3 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ b &= SLOT_2_0; |
+ /* moved CSE1 up */ |
+ /* a &= (0x7f<<14)|(0x7f); */ |
+ a = a<<7; |
+ a |= b; |
+ *v = a; |
+ return 4; |
+ } |
+ |
+ /* a: p0<<14 | p2 (masked) */ |
+ /* b: p1<<14 | p3 (unmasked) */ |
+ /* 1:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ |
+ /* moved CSE1 up */ |
+ /* a &= (0x7f<<14)|(0x7f); */ |
+ b &= SLOT_2_0; |
+ s = a; |
+ /* s: p0<<14 | p2 (masked) */ |
+ |
+ p++; |
+ a = a<<14; |
+ a |= *p; |
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ /* we can skip these cause they were (effectively) done above |
+ ** while calculating s */ |
+ /* a &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ |
+ /* b &= (0x7f<<14)|(0x7f); */ |
+ b = b<<7; |
+ a |= b; |
+ s = s>>18; |
+ *v = ((u64)s)<<32 | a; |
+ return 5; |
+ } |
+ |
+ /* 2:save off p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ |
+ s = s<<7; |
+ s |= b; |
+ /* s: p0<<21 | p1<<14 | p2<<7 | p3 (masked) */ |
+ |
+ p++; |
+ b = b<<14; |
+ b |= *p; |
+ /* b: p1<<28 | p3<<14 | p5 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ /* we can skip this cause it was (effectively) done above in calc'ing s */ |
+ /* b &= (0x7f<<28)|(0x7f<<14)|(0x7f); */ |
+ a &= SLOT_2_0; |
+ a = a<<7; |
+ a |= b; |
+ s = s>>18; |
+ *v = ((u64)s)<<32 | a; |
+ return 6; |
+ } |
+ |
+ p++; |
+ a = a<<14; |
+ a |= *p; |
+ /* a: p2<<28 | p4<<14 | p6 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ a &= SLOT_4_2_0; |
+ b &= SLOT_2_0; |
+ b = b<<7; |
+ a |= b; |
+ s = s>>11; |
+ *v = ((u64)s)<<32 | a; |
+ return 7; |
+ } |
+ |
+ /* CSE2 from below */ |
+ a &= SLOT_2_0; |
+ p++; |
+ b = b<<14; |
+ b |= *p; |
+ /* b: p3<<28 | p5<<14 | p7 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ b &= SLOT_4_2_0; |
+ /* moved CSE2 up */ |
+ /* a &= (0x7f<<14)|(0x7f); */ |
+ a = a<<7; |
+ a |= b; |
+ s = s>>4; |
+ *v = ((u64)s)<<32 | a; |
+ return 8; |
+ } |
+ |
+ p++; |
+ a = a<<15; |
+ a |= *p; |
+ /* a: p4<<29 | p6<<15 | p8 (unmasked) */ |
+ |
+ /* moved CSE2 up */ |
+ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */ |
+ b &= SLOT_2_0; |
+ b = b<<8; |
+ a |= b; |
+ |
+ s = s<<4; |
+ b = p[-4]; |
+ b &= 0x7f; |
+ b = b>>3; |
+ s |= b; |
+ |
+ *v = ((u64)s)<<32 | a; |
+ |
+ return 9; |
+} |
+ |
+/* |
+** Read a 32-bit variable-length integer from memory starting at p[0]. |
+** Return the number of bytes read. The value is stored in *v. |
+** |
+** If the varint stored in p[0] is larger than can fit in a 32-bit unsigned |
+** integer, then set *v to 0xffffffff. |
+** |
+** A MACRO version, getVarint32, is provided which inlines the |
+** single-byte case. All code should use the MACRO version as |
+** this function assumes the single-byte case has already been handled. |
+*/ |
+SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){ |
+ u32 a,b; |
+ |
+ /* The 1-byte case. Overwhelmingly the most common. Handled inline |
+ ** by the getVarin32() macro */ |
+ a = *p; |
+ /* a: p0 (unmasked) */ |
+#ifndef getVarint32 |
+ if (!(a&0x80)) |
+ { |
+ /* Values between 0 and 127 */ |
+ *v = a; |
+ return 1; |
+ } |
+#endif |
+ |
+ /* The 2-byte case */ |
+ p++; |
+ b = *p; |
+ /* b: p1 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ /* Values between 128 and 16383 */ |
+ a &= 0x7f; |
+ a = a<<7; |
+ *v = a | b; |
+ return 2; |
+ } |
+ |
+ /* The 3-byte case */ |
+ p++; |
+ a = a<<14; |
+ a |= *p; |
+ /* a: p0<<14 | p2 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ /* Values between 16384 and 2097151 */ |
+ a &= (0x7f<<14)|(0x7f); |
+ b &= 0x7f; |
+ b = b<<7; |
+ *v = a | b; |
+ return 3; |
+ } |
+ |
+ /* A 32-bit varint is used to store size information in btrees. |
+ ** Objects are rarely larger than 2MiB limit of a 3-byte varint. |
+ ** A 3-byte varint is sufficient, for example, to record the size |
+ ** of a 1048569-byte BLOB or string. |
+ ** |
+ ** We only unroll the first 1-, 2-, and 3- byte cases. The very |
+ ** rare larger cases can be handled by the slower 64-bit varint |
+ ** routine. |
+ */ |
+#if 1 |
+ { |
+ u64 v64; |
+ u8 n; |
+ |
+ p -= 2; |
+ n = sqlite3GetVarint(p, &v64); |
+ assert( n>3 && n<=9 ); |
+ if( (v64 & SQLITE_MAX_U32)!=v64 ){ |
+ *v = 0xffffffff; |
+ }else{ |
+ *v = (u32)v64; |
+ } |
+ return n; |
+ } |
+ |
+#else |
+ /* For following code (kept for historical record only) shows an |
+ ** unrolling for the 3- and 4-byte varint cases. This code is |
+ ** slightly faster, but it is also larger and much harder to test. |
+ */ |
+ p++; |
+ b = b<<14; |
+ b |= *p; |
+ /* b: p1<<14 | p3 (unmasked) */ |
+ if (!(b&0x80)) |
+ { |
+ /* Values between 2097152 and 268435455 */ |
+ b &= (0x7f<<14)|(0x7f); |
+ a &= (0x7f<<14)|(0x7f); |
+ a = a<<7; |
+ *v = a | b; |
+ return 4; |
+ } |
+ |
+ p++; |
+ a = a<<14; |
+ a |= *p; |
+ /* a: p0<<28 | p2<<14 | p4 (unmasked) */ |
+ if (!(a&0x80)) |
+ { |
+ /* Values between 268435456 and 34359738367 */ |
+ a &= SLOT_4_2_0; |
+ b &= SLOT_4_2_0; |
+ b = b<<7; |
+ *v = a | b; |
+ return 5; |
+ } |
+ |
+ /* We can only reach this point when reading a corrupt database |
+ ** file. In that case we are not in any hurry. Use the (relatively |
+ ** slow) general-purpose sqlite3GetVarint() routine to extract the |
+ ** value. */ |
+ { |
+ u64 v64; |
+ u8 n; |
+ |
+ p -= 4; |
+ n = sqlite3GetVarint(p, &v64); |
+ assert( n>5 && n<=9 ); |
+ *v = (u32)v64; |
+ return n; |
+ } |
+#endif |
+} |
+ |
+/* |
+** Return the number of bytes that will be needed to store the given |
+** 64-bit integer. |
+*/ |
+SQLITE_PRIVATE int sqlite3VarintLen(u64 v){ |
+ int i; |
+ for(i=1; (v >>= 7)!=0; i++){ assert( i<9 ); } |
+ return i; |
+} |
+ |
+ |
+/* |
+** Read or write a four-byte big-endian integer value. |
+*/ |
+SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){ |
+#if SQLITE_BYTEORDER==4321 |
+ u32 x; |
+ memcpy(&x,p,4); |
+ return x; |
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ |
+ && defined(__GNUC__) && GCC_VERSION>=4003000 |
+ u32 x; |
+ memcpy(&x,p,4); |
+ return __builtin_bswap32(x); |
+#elif SQLITE_BYTEORDER==1234 && !defined(SQLITE_DISABLE_INTRINSIC) \ |
+ && defined(_MSC_VER) && _MSC_VER>=1300 |
+ u32 x; |
+ memcpy(&x,p,4); |
+ return _byteswap_ulong(x); |
+#else |
+ testcase( p[0]&0x80 ); |
+ return ((unsigned)p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3]; |
+#endif |
+} |
+SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){ |
+#if SQLITE_BYTEORDER==4321 |
+ memcpy(p,&v,4); |
+#elif SQLITE_BYTEORDER==1234 && defined(__GNUC__) && GCC_VERSION>=4003000 |
+ u32 x = __builtin_bswap32(v); |
+ memcpy(p,&x,4); |
+#elif SQLITE_BYTEORDER==1234 && defined(_MSC_VER) && _MSC_VER>=1300 |
+ u32 x = _byteswap_ulong(v); |
+ memcpy(p,&x,4); |
+#else |
+ p[0] = (u8)(v>>24); |
+ p[1] = (u8)(v>>16); |
+ p[2] = (u8)(v>>8); |
+ p[3] = (u8)v; |
+#endif |
+} |
+ |
+ |
+ |
+/* |
+** Translate a single byte of Hex into an integer. |
+** This routine only works if h really is a valid hexadecimal |
+** character: 0..9a..fA..F |
+*/ |
+SQLITE_PRIVATE u8 sqlite3HexToInt(int h){ |
+ assert( (h>='0' && h<='9') || (h>='a' && h<='f') || (h>='A' && h<='F') ); |
+#ifdef SQLITE_ASCII |
+ h += 9*(1&(h>>6)); |
+#endif |
+#ifdef SQLITE_EBCDIC |
+ h += 9*(1&~(h>>4)); |
+#endif |
+ return (u8)(h & 0xf); |
+} |
+ |
+#if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) |
+/* |
+** Convert a BLOB literal of the form "x'hhhhhh'" into its binary |
+** value. Return a pointer to its binary value. Space to hold the |
+** binary value has been obtained from malloc and must be freed by |
+** the calling routine. |
+*/ |
+SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3 *db, const char *z, int n){ |
+ char *zBlob; |
+ int i; |
+ |
+ zBlob = (char *)sqlite3DbMallocRaw(db, n/2 + 1); |
+ n--; |
+ if( zBlob ){ |
+ for(i=0; i<n; i+=2){ |
+ zBlob[i/2] = (sqlite3HexToInt(z[i])<<4) | sqlite3HexToInt(z[i+1]); |
+ } |
+ zBlob[i/2] = 0; |
+ } |
+ return zBlob; |
+} |
+#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */ |
+ |
+/* |
+** Log an error that is an API call on a connection pointer that should |
+** not have been used. The "type" of connection pointer is given as the |
+** argument. The zType is a word like "NULL" or "closed" or "invalid". |
+*/ |
+static void logBadConnection(const char *zType){ |
+ sqlite3_log(SQLITE_MISUSE, |
+ "API call with %s database connection pointer", |
+ zType |
+ ); |
+} |
+ |
+/* |
+** Check to make sure we have a valid db pointer. This test is not |
+** foolproof but it does provide some measure of protection against |
+** misuse of the interface such as passing in db pointers that are |
+** NULL or which have been previously closed. If this routine returns |
+** 1 it means that the db pointer is valid and 0 if it should not be |
+** dereferenced for any reason. The calling function should invoke |
+** SQLITE_MISUSE immediately. |
+** |
+** sqlite3SafetyCheckOk() requires that the db pointer be valid for |
+** use. sqlite3SafetyCheckSickOrOk() allows a db pointer that failed to |
+** open properly and is not fit for general use but which can be |
+** used as an argument to sqlite3_errmsg() or sqlite3_close(). |
+*/ |
+SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3 *db){ |
+ u32 magic; |
+ if( db==0 ){ |
+ logBadConnection("NULL"); |
+ return 0; |
+ } |
+ magic = db->magic; |
+ if( magic!=SQLITE_MAGIC_OPEN ){ |
+ if( sqlite3SafetyCheckSickOrOk(db) ){ |
+ testcase( sqlite3GlobalConfig.xLog!=0 ); |
+ logBadConnection("unopened"); |
+ } |
+ return 0; |
+ }else{ |
+ return 1; |
+ } |
+} |
+SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3 *db){ |
+ u32 magic; |
+ magic = db->magic; |
+ if( magic!=SQLITE_MAGIC_SICK && |
+ magic!=SQLITE_MAGIC_OPEN && |
+ magic!=SQLITE_MAGIC_BUSY ){ |
+ testcase( sqlite3GlobalConfig.xLog!=0 ); |
+ logBadConnection("invalid"); |
+ return 0; |
+ }else{ |
+ return 1; |
+ } |
+} |
+ |
+/* |
+** Attempt to add, substract, or multiply the 64-bit signed value iB against |
+** the other 64-bit signed integer at *pA and store the result in *pA. |
+** Return 0 on success. Or if the operation would have resulted in an |
+** overflow, leave *pA unchanged and return 1. |
+*/ |
+SQLITE_PRIVATE int sqlite3AddInt64(i64 *pA, i64 iB){ |
+ i64 iA = *pA; |
+ testcase( iA==0 ); testcase( iA==1 ); |
+ testcase( iB==-1 ); testcase( iB==0 ); |
+ if( iB>=0 ){ |
+ testcase( iA>0 && LARGEST_INT64 - iA == iB ); |
+ testcase( iA>0 && LARGEST_INT64 - iA == iB - 1 ); |
+ if( iA>0 && LARGEST_INT64 - iA < iB ) return 1; |
+ }else{ |
+ testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 1 ); |
+ testcase( iA<0 && -(iA + LARGEST_INT64) == iB + 2 ); |
+ if( iA<0 && -(iA + LARGEST_INT64) > iB + 1 ) return 1; |
+ } |
+ *pA += iB; |
+ return 0; |
+} |
+SQLITE_PRIVATE int sqlite3SubInt64(i64 *pA, i64 iB){ |
+ testcase( iB==SMALLEST_INT64+1 ); |
+ if( iB==SMALLEST_INT64 ){ |
+ testcase( (*pA)==(-1) ); testcase( (*pA)==0 ); |
+ if( (*pA)>=0 ) return 1; |
+ *pA -= iB; |
+ return 0; |
+ }else{ |
+ return sqlite3AddInt64(pA, -iB); |
+ } |
+} |
+#define TWOPOWER32 (((i64)1)<<32) |
+#define TWOPOWER31 (((i64)1)<<31) |
+SQLITE_PRIVATE int sqlite3MulInt64(i64 *pA, i64 iB){ |
+ i64 iA = *pA; |
+ i64 iA1, iA0, iB1, iB0, r; |
+ |
+ iA1 = iA/TWOPOWER32; |
+ iA0 = iA % TWOPOWER32; |
+ iB1 = iB/TWOPOWER32; |
+ iB0 = iB % TWOPOWER32; |
+ if( iA1==0 ){ |
+ if( iB1==0 ){ |
+ *pA *= iB; |
+ return 0; |
+ } |
+ r = iA0*iB1; |
+ }else if( iB1==0 ){ |
+ r = iA1*iB0; |
+ }else{ |
+ /* If both iA1 and iB1 are non-zero, overflow will result */ |
+ return 1; |
+ } |
+ testcase( r==(-TWOPOWER31)-1 ); |
+ testcase( r==(-TWOPOWER31) ); |
+ testcase( r==TWOPOWER31 ); |
+ testcase( r==TWOPOWER31-1 ); |
+ if( r<(-TWOPOWER31) || r>=TWOPOWER31 ) return 1; |
+ r *= TWOPOWER32; |
+ if( sqlite3AddInt64(&r, iA0*iB0) ) return 1; |
+ *pA = r; |
+ return 0; |
+} |
+ |
+/* |
+** Compute the absolute value of a 32-bit signed integer, of possible. Or |
+** if the integer has a value of -2147483648, return +2147483647 |
+*/ |
+SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
+ if( x>=0 ) return x; |
+ if( x==(int)0x80000000 ) return 0x7fffffff; |
+ return -x; |
+} |
+ |
+#ifdef SQLITE_ENABLE_8_3_NAMES |
+/* |
+** If SQLITE_ENABLE_8_3_NAMES is set at compile-time and if the database |
+** filename in zBaseFilename is a URI with the "8_3_names=1" parameter and |
+** if filename in z[] has a suffix (a.k.a. "extension") that is longer than |
+** three characters, then shorten the suffix on z[] to be the last three |
+** characters of the original suffix. |
+** |
+** If SQLITE_ENABLE_8_3_NAMES is set to 2 at compile-time, then always |
+** do the suffix shortening regardless of URI parameter. |
+** |
+** Examples: |
+** |
+** test.db-journal => test.nal |
+** test.db-wal => test.wal |
+** test.db-shm => test.shm |
+** test.db-mj7f3319fa => test.9fa |
+*/ |
+SQLITE_PRIVATE void sqlite3FileSuffix3(const char *zBaseFilename, char *z){ |
+#if SQLITE_ENABLE_8_3_NAMES<2 |
+ if( sqlite3_uri_boolean(zBaseFilename, "8_3_names", 0) ) |
+#endif |
+ { |
+ int i, sz; |
+ sz = sqlite3Strlen30(z); |
+ for(i=sz-1; i>0 && z[i]!='/' && z[i]!='.'; i--){} |
+ if( z[i]=='.' && ALWAYS(sz>i+4) ) memmove(&z[i+1], &z[sz-3], 4); |
+ } |
+} |
+#endif |
+ |
+/* |
+** Find (an approximate) sum of two LogEst values. This computation is |
+** not a simple "+" operator because LogEst is stored as a logarithmic |
+** value. |
+** |
+*/ |
+SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst a, LogEst b){ |
+ static const unsigned char x[] = { |
+ 10, 10, /* 0,1 */ |
+ 9, 9, /* 2,3 */ |
+ 8, 8, /* 4,5 */ |
+ 7, 7, 7, /* 6,7,8 */ |
+ 6, 6, 6, /* 9,10,11 */ |
+ 5, 5, 5, /* 12-14 */ |
+ 4, 4, 4, 4, /* 15-18 */ |
+ 3, 3, 3, 3, 3, 3, /* 19-24 */ |
+ 2, 2, 2, 2, 2, 2, 2, /* 25-31 */ |
+ }; |
+ if( a>=b ){ |
+ if( a>b+49 ) return a; |
+ if( a>b+31 ) return a+1; |
+ return a+x[a-b]; |
+ }else{ |
+ if( b>a+49 ) return b; |
+ if( b>a+31 ) return b+1; |
+ return b+x[b-a]; |
+ } |
+} |
+ |
+/* |
+** Convert an integer into a LogEst. In other words, compute an |
+** approximation for 10*log2(x). |
+*/ |
+SQLITE_PRIVATE LogEst sqlite3LogEst(u64 x){ |
+ static LogEst a[] = { 0, 2, 3, 5, 6, 7, 8, 9 }; |
+ LogEst y = 40; |
+ if( x<8 ){ |
+ if( x<2 ) return 0; |
+ while( x<8 ){ y -= 10; x <<= 1; } |
+ }else{ |
+ while( x>255 ){ y += 40; x >>= 4; } |
+ while( x>15 ){ y += 10; x >>= 1; } |
+ } |
+ return a[x&7] + y - 10; |
+} |
+ |
+#ifndef SQLITE_OMIT_VIRTUALTABLE |
+/* |
+** Convert a double into a LogEst |
+** In other words, compute an approximation for 10*log2(x). |
+*/ |
+SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double x){ |
+ u64 a; |
+ LogEst e; |
+ assert( sizeof(x)==8 && sizeof(a)==8 ); |
+ if( x<=1 ) return 0; |
+ if( x<=2000000000 ) return sqlite3LogEst((u64)x); |
+ memcpy(&a, &x, 8); |
+ e = (a>>52) - 1022; |
+ return e*10; |
+} |
+#endif /* SQLITE_OMIT_VIRTUALTABLE */ |
+ |
+/* |
+** Convert a LogEst into an integer. |
+*/ |
+SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst x){ |
+ u64 n; |
+ if( x<10 ) return 1; |
+ n = x%10; |
+ x /= 10; |
+ if( n>=5 ) n -= 2; |
+ else if( n>=1 ) n -= 1; |
+ if( x>=3 ){ |
+ return x>60 ? (u64)LARGEST_INT64 : (n+8)<<(x-3); |
+ } |
+ return (n+8)>>(3-x); |
+} |
+ |
+/************** End of util.c ************************************************/ |
+/************** Begin file hash.c ********************************************/ |
+/* |
+** 2001 September 22 |
+** |
+** 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 is the implementation of generic hash-tables |
+** used in SQLite. |
+*/ |
+/* #include "sqliteInt.h" */ |
+/* #include <assert.h> */ |
+ |
+/* Turn bulk memory into a hash table object by initializing the |
+** fields of the Hash structure. |
+** |
+** "pNew" is a pointer to the hash table that is to be initialized. |
+*/ |
+SQLITE_PRIVATE void sqlite3HashInit(Hash *pNew){ |
+ assert( pNew!=0 ); |
+ pNew->first = 0; |
+ pNew->count = 0; |
+ pNew->htsize = 0; |
+ pNew->ht = 0; |
+} |
+ |
+/* Remove all entries from a hash table. Reclaim all memory. |
+** Call this routine to delete a hash table or to reset a hash table |
+** to the empty state. |
+*/ |
+SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){ |
+ HashElem *elem; /* For looping over all elements of the table */ |
+ |
+ assert( pH!=0 ); |
+ elem = pH->first; |
+ pH->first = 0; |
+ sqlite3_free(pH->ht); |
+ pH->ht = 0; |
+ pH->htsize = 0; |
+ while( elem ){ |
+ HashElem *next_elem = elem->next; |
+ sqlite3_free(elem); |
+ elem = next_elem; |
+ } |
+ pH->count = 0; |
+} |
+ |
+/* |
+** The hashing function. |
+*/ |
+static unsigned int strHash(const char *z){ |
+ unsigned int h = 0; |
+ unsigned char c; |
+ while( (c = (unsigned char)*z++)!=0 ){ |
+ h = (h<<3) ^ h ^ sqlite3UpperToLower[c]; |
+ } |
+ return h; |
+} |
+ |
+ |
+/* Link pNew element into the hash table pH. If pEntry!=0 then also |
+** insert pNew into the pEntry hash bucket. |
+*/ |
+static void insertElement( |
+ Hash *pH, /* The complete hash table */ |
+ struct _ht *pEntry, /* The entry into which pNew is inserted */ |
+ HashElem *pNew /* The element to be inserted */ |
+){ |
+ HashElem *pHead; /* First element already in pEntry */ |
+ if( pEntry ){ |
+ pHead = pEntry->count ? pEntry->chain : 0; |
+ pEntry->count++; |
+ pEntry->chain = pNew; |
+ }else{ |
+ pHead = 0; |
+ } |
+ if( pHead ){ |
+ pNew->next = pHead; |
+ pNew->prev = pHead->prev; |
+ if( pHead->prev ){ pHead->prev->next = pNew; } |
+ else { pH->first = pNew; } |
+ pHead->prev = pNew; |
+ }else{ |
+ pNew->next = pH->first; |
+ if( pH->first ){ pH->first->prev = pNew; } |
+ pNew->prev = 0; |
+ pH->first = pNew; |
+ } |
+} |
+ |
+ |
+/* Resize the hash table so that it cantains "new_size" buckets. |
+** |
+** The hash table might fail to resize if sqlite3_malloc() fails or |
+** if the new size is the same as the prior size. |
+** Return TRUE if the resize occurs and false if not. |
+*/ |
+static int rehash(Hash *pH, unsigned int new_size){ |
+ struct _ht *new_ht; /* The new hash table */ |
+ HashElem *elem, *next_elem; /* For looping over existing elements */ |
+ |
+#if SQLITE_MALLOC_SOFT_LIMIT>0 |
+ if( new_size*sizeof(struct _ht)>SQLITE_MALLOC_SOFT_LIMIT ){ |
+ new_size = SQLITE_MALLOC_SOFT_LIMIT/sizeof(struct _ht); |
+ } |
+ if( new_size==pH->htsize ) return 0; |
+#endif |
+ |
+ /* The inability to allocates space for a larger hash table is |
+ ** a performance hit but it is not a fatal error. So mark the |
+ ** allocation as a benign. Use sqlite3Malloc()/memset(0) instead of |
+ ** sqlite3MallocZero() to make the allocation, as sqlite3MallocZero() |
+ ** only zeroes the requested number of bytes whereas this module will |
+ ** use the actual amount of space allocated for the hash table (which |
+ ** may be larger than the requested amount). |
+ */ |
+ sqlite3BeginBenignMalloc(); |
+ new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) ); |
+ sqlite3EndBenignMalloc(); |
+ |
+ if( new_ht==0 ) return 0; |
+ sqlite3_free(pH->ht); |
+ pH->ht = new_ht; |
+ pH->htsize = new_size = sqlite3MallocSize(new_ht)/sizeof(struct _ht); |
+ memset(new_ht, 0, new_size*sizeof(struct _ht)); |
+ for(elem=pH->first, pH->first=0; elem; elem = next_elem){ |
+ unsigned int h = strHash(elem->pKey) % new_size; |
+ next_elem = elem->next; |
+ insertElement(pH, &new_ht[h], elem); |
+ } |
+ return 1; |
+} |
+ |
+/* This function (for internal use only) locates an element in an |
+** hash table that matches the given key. The hash for this key is |
+** also computed and returned in the *pH parameter. |
+*/ |
+static HashElem *findElementWithHash( |
+ const Hash *pH, /* The pH to be searched */ |
+ const char *pKey, /* The key we are searching for */ |
+ unsigned int *pHash /* Write the hash value here */ |
+){ |
+ HashElem *elem; /* Used to loop thru the element list */ |
+ int count; /* Number of elements left to test */ |
+ unsigned int h; /* The computed hash */ |
+ |
+ if( pH->ht ){ |
+ struct _ht *pEntry; |
+ h = strHash(pKey) % pH->htsize; |
+ pEntry = &pH->ht[h]; |
+ elem = pEntry->chain; |
+ count = pEntry->count; |
+ }else{ |
+ h = 0; |
+ elem = pH->first; |
+ count = pH->count; |
+ } |
+ *pHash = h; |
+ while( count-- ){ |
+ assert( elem!=0 ); |
+ if( sqlite3StrICmp(elem->pKey,pKey)==0 ){ |
+ return elem; |
+ } |
+ elem = elem->next; |
+ } |
+ return 0; |
+} |
+ |
+/* Remove a single entry from the hash table given a pointer to that |
+** element and a hash on the element's key. |
+*/ |
+static void removeElementGivenHash( |
+ Hash *pH, /* The pH containing "elem" */ |
+ HashElem* elem, /* The element to be removed from the pH */ |
+ unsigned int h /* Hash value for the element */ |
+){ |
+ struct _ht *pEntry; |
+ if( elem->prev ){ |
+ elem->prev->next = elem->next; |
+ }else{ |
+ pH->first = elem->next; |
+ } |
+ if( elem->next ){ |
+ elem->next->prev = elem->prev; |
+ } |
+ if( pH->ht ){ |
+ pEntry = &pH->ht[h]; |
+ if( pEntry->chain==elem ){ |
+ pEntry->chain = elem->next; |
+ } |
+ pEntry->count--; |
+ assert( pEntry->count>=0 ); |
+ } |
+ sqlite3_free( elem ); |
+ pH->count--; |
+ if( pH->count==0 ){ |
+ assert( pH->first==0 ); |
+ assert( pH->count==0 ); |
+ sqlite3HashClear(pH); |
+ } |
+} |
+ |
+/* Attempt to locate an element of the hash table pH with a key |
+** that matches pKey. Return the data for this element if it is |
+** found, or NULL if there is no match. |
+*/ |
+SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey){ |
+ HashElem *elem; /* The element that matches key */ |
+ unsigned int h; /* A hash on key */ |
+ |
+ assert( pH!=0 ); |
+ assert( pKey!=0 ); |
+ elem = findElementWithHash(pH, pKey, &h); |
+ return elem ? elem->data : 0; |
+} |
+ |
+/* Insert an element into the hash table pH. The key is pKey |
+** and the data is "data". |
+** |
+** If no element exists with a matching key, then a new |
+** element is created and NULL is returned. |
+** |
+** If another element already exists with the same key, then the |
+** new data replaces the old data and the old data is returned. |
+** The key is not copied in this instance. If a malloc fails, then |
+** the new data is returned and the hash table is unchanged. |
+** |
+** If the "data" parameter to this function is NULL, then the |
+** element corresponding to "key" is removed from the hash table. |
+*/ |
+SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, void *data){ |
+ unsigned int h; /* the hash of the key modulo hash table size */ |
+ HashElem *elem; /* Used to loop thru the element list */ |
+ HashElem *new_elem; /* New element added to the pH */ |
+ |
+ assert( pH!=0 ); |
+ assert( pKey!=0 ); |
+ elem = findElementWithHash(pH,pKey,&h); |
+ if( elem ){ |
+ void *old_data = elem->data; |
+ if( data==0 ){ |
+ removeElementGivenHash(pH,elem,h); |
+ }else{ |
+ elem->data = data; |
+ elem->pKey = pKey; |
+ } |
+ return old_data; |
+ } |
+ if( data==0 ) return 0; |
+ new_elem = (HashElem*)sqlite3Malloc( sizeof(HashElem) ); |
+ if( new_elem==0 ) return data; |
+ new_elem->pKey = pKey; |
+ new_elem->data = data; |
+ pH->count++; |
+ if( pH->count>=10 && pH->count > 2*pH->htsize ){ |
+ if( rehash(pH, pH->count*2) ){ |
+ assert( pH->htsize>0 ); |
+ h = strHash(pKey) % pH->htsize; |
+ } |
+ } |
+ insertElement(pH, pH->ht ? &pH->ht[h] : 0, new_elem); |
+ return 0; |
+} |
+ |
+/************** End of hash.c ************************************************/ |
+/************** Begin file opcodes.c *****************************************/ |
+/* Automatically generated. Do not edit */ |
+/* See the tool/mkopcodec.tcl script for details. */ |
+#if !defined(SQLITE_OMIT_EXPLAIN) \ |
+ || defined(VDBE_PROFILE) \ |
+ || defined(SQLITE_DEBUG) |
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS) || defined(SQLITE_DEBUG) |
+# define OpHelp(X) "\0" X |
+#else |
+# define OpHelp(X) |
+#endif |
+SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ |
+ static const char *const azName[] = { "?", |
+ /* 1 */ "Savepoint" OpHelp(""), |
+ /* 2 */ "AutoCommit" OpHelp(""), |
+ /* 3 */ "Transaction" OpHelp(""), |
+ /* 4 */ "SorterNext" OpHelp(""), |
+ /* 5 */ "PrevIfOpen" OpHelp(""), |
+ /* 6 */ "NextIfOpen" OpHelp(""), |
+ /* 7 */ "Prev" OpHelp(""), |
+ /* 8 */ "Next" OpHelp(""), |
+ /* 9 */ "Checkpoint" OpHelp(""), |
+ /* 10 */ "JournalMode" OpHelp(""), |
+ /* 11 */ "Vacuum" OpHelp(""), |
+ /* 12 */ "VFilter" OpHelp("iplan=r[P3] zplan='P4'"), |
+ /* 13 */ "VUpdate" OpHelp("data=r[P3@P2]"), |
+ /* 14 */ "Goto" OpHelp(""), |
+ /* 15 */ "Gosub" OpHelp(""), |
+ /* 16 */ "Return" OpHelp(""), |
+ /* 17 */ "InitCoroutine" OpHelp(""), |
+ /* 18 */ "EndCoroutine" OpHelp(""), |
+ /* 19 */ "Not" OpHelp("r[P2]= !r[P1]"), |
+ /* 20 */ "Yield" OpHelp(""), |
+ /* 21 */ "HaltIfNull" OpHelp("if r[P3]=null halt"), |
+ /* 22 */ "Halt" OpHelp(""), |
+ /* 23 */ "Integer" OpHelp("r[P2]=P1"), |
+ /* 24 */ "Int64" OpHelp("r[P2]=P4"), |
+ /* 25 */ "String" OpHelp("r[P2]='P4' (len=P1)"), |
+ /* 26 */ "Null" OpHelp("r[P2..P3]=NULL"), |
+ /* 27 */ "SoftNull" OpHelp("r[P1]=NULL"), |
+ /* 28 */ "Blob" OpHelp("r[P2]=P4 (len=P1)"), |
+ /* 29 */ "Variable" OpHelp("r[P2]=parameter(P1,P4)"), |
+ /* 30 */ "Move" OpHelp("r[P2@P3]=r[P1@P3]"), |
+ /* 31 */ "Copy" OpHelp("r[P2@P3+1]=r[P1@P3+1]"), |
+ /* 32 */ "SCopy" OpHelp("r[P2]=r[P1]"), |
+ /* 33 */ "IntCopy" OpHelp("r[P2]=r[P1]"), |
+ /* 34 */ "ResultRow" OpHelp("output=r[P1@P2]"), |
+ /* 35 */ "CollSeq" OpHelp(""), |
+ /* 36 */ "Function0" OpHelp("r[P3]=func(r[P2@P5])"), |
+ /* 37 */ "Function" OpHelp("r[P3]=func(r[P2@P5])"), |
+ /* 38 */ "AddImm" OpHelp("r[P1]=r[P1]+P2"), |
+ /* 39 */ "MustBeInt" OpHelp(""), |
+ /* 40 */ "RealAffinity" OpHelp(""), |
+ /* 41 */ "Cast" OpHelp("affinity(r[P1])"), |
+ /* 42 */ "Permutation" OpHelp(""), |
+ /* 43 */ "Compare" OpHelp("r[P1@P3] <-> r[P2@P3]"), |
+ /* 44 */ "Jump" OpHelp(""), |
+ /* 45 */ "Once" OpHelp(""), |
+ /* 46 */ "If" OpHelp(""), |
+ /* 47 */ "IfNot" OpHelp(""), |
+ /* 48 */ "Column" OpHelp("r[P3]=PX"), |
+ /* 49 */ "Affinity" OpHelp("affinity(r[P1@P2])"), |
+ /* 50 */ "MakeRecord" OpHelp("r[P3]=mkrec(r[P1@P2])"), |
+ /* 51 */ "Count" OpHelp("r[P2]=count()"), |
+ /* 52 */ "ReadCookie" OpHelp(""), |
+ /* 53 */ "SetCookie" OpHelp(""), |
+ /* 54 */ "ReopenIdx" OpHelp("root=P2 iDb=P3"), |
+ /* 55 */ "OpenRead" OpHelp("root=P2 iDb=P3"), |
+ /* 56 */ "OpenWrite" OpHelp("root=P2 iDb=P3"), |
+ /* 57 */ "OpenAutoindex" OpHelp("nColumn=P2"), |
+ /* 58 */ "OpenEphemeral" OpHelp("nColumn=P2"), |
+ /* 59 */ "SorterOpen" OpHelp(""), |
+ /* 60 */ "SequenceTest" OpHelp("if( cursor[P1].ctr++ ) pc = P2"), |
+ /* 61 */ "OpenPseudo" OpHelp("P3 columns in r[P2]"), |
+ /* 62 */ "Close" OpHelp(""), |
+ /* 63 */ "ColumnsUsed" OpHelp(""), |
+ /* 64 */ "SeekLT" OpHelp("key=r[P3@P4]"), |
+ /* 65 */ "SeekLE" OpHelp("key=r[P3@P4]"), |
+ /* 66 */ "SeekGE" OpHelp("key=r[P3@P4]"), |
+ /* 67 */ "SeekGT" OpHelp("key=r[P3@P4]"), |
+ /* 68 */ "Seek" OpHelp("intkey=r[P2]"), |
+ /* 69 */ "NoConflict" OpHelp("key=r[P3@P4]"), |
+ /* 70 */ "NotFound" OpHelp("key=r[P3@P4]"), |
+ /* 71 */ "Or" OpHelp("r[P3]=(r[P1] || r[P2])"), |
+ /* 72 */ "And" OpHelp("r[P3]=(r[P1] && r[P2])"), |
+ /* 73 */ "Found" OpHelp("key=r[P3@P4]"), |
+ /* 74 */ "NotExists" OpHelp("intkey=r[P3]"), |
+ /* 75 */ "Sequence" OpHelp("r[P2]=cursor[P1].ctr++"), |
+ /* 76 */ "IsNull" OpHelp("if r[P1]==NULL goto P2"), |
+ /* 77 */ "NotNull" OpHelp("if r[P1]!=NULL goto P2"), |
+ /* 78 */ "Ne" OpHelp("if r[P1]!=r[P3] goto P2"), |
+ /* 79 */ "Eq" OpHelp("if r[P1]==r[P3] goto P2"), |
+ /* 80 */ "Gt" OpHelp("if r[P1]>r[P3] goto P2"), |
+ /* 81 */ "Le" OpHelp("if r[P1]<=r[P3] goto P2"), |
+ /* 82 */ "Lt" OpHelp("if r[P1]<r[P3] goto P2"), |
+ /* 83 */ "Ge" OpHelp("if r[P1]>=r[P3] goto P2"), |
+ /* 84 */ "NewRowid" OpHelp("r[P2]=rowid"), |
+ /* 85 */ "BitAnd" OpHelp("r[P3]=r[P1]&r[P2]"), |
+ /* 86 */ "BitOr" OpHelp("r[P3]=r[P1]|r[P2]"), |
+ /* 87 */ "ShiftLeft" OpHelp("r[P3]=r[P2]<<r[P1]"), |
+ /* 88 */ "ShiftRight" OpHelp("r[P3]=r[P2]>>r[P1]"), |
+ /* 89 */ "Add" OpHelp("r[P3]=r[P1]+r[P2]"), |
+ /* 90 */ "Subtract" OpHelp("r[P3]=r[P2]-r[P1]"), |
+ /* 91 */ "Multiply" OpHelp("r[P3]=r[P1]*r[P2]"), |
+ /* 92 */ "Divide" OpHelp("r[P3]=r[P2]/r[P1]"), |
+ /* 93 */ "Remainder" OpHelp("r[P3]=r[P2]%r[P1]"), |
+ /* 94 */ "Concat" OpHelp("r[P3]=r[P2]+r[P1]"), |
+ /* 95 */ "Insert" OpHelp("intkey=r[P3] data=r[P2]"), |
+ /* 96 */ "BitNot" OpHelp("r[P1]= ~r[P1]"), |
+ /* 97 */ "String8" OpHelp("r[P2]='P4'"), |
+ /* 98 */ "InsertInt" OpHelp("intkey=P3 data=r[P2]"), |
+ /* 99 */ "Delete" OpHelp(""), |
+ /* 100 */ "ResetCount" OpHelp(""), |
+ /* 101 */ "SorterCompare" OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"), |
+ /* 102 */ "SorterData" OpHelp("r[P2]=data"), |
+ /* 103 */ "RowKey" OpHelp("r[P2]=key"), |
+ /* 104 */ "RowData" OpHelp("r[P2]=data"), |
+ /* 105 */ "Rowid" OpHelp("r[P2]=rowid"), |
+ /* 106 */ "NullRow" OpHelp(""), |
+ /* 107 */ "Last" OpHelp(""), |
+ /* 108 */ "SorterSort" OpHelp(""), |
+ /* 109 */ "Sort" OpHelp(""), |
+ /* 110 */ "Rewind" OpHelp(""), |
+ /* 111 */ "SorterInsert" OpHelp(""), |
+ /* 112 */ "IdxInsert" OpHelp("key=r[P2]"), |
+ /* 113 */ "IdxDelete" OpHelp("key=r[P2@P3]"), |
+ /* 114 */ "IdxRowid" OpHelp("r[P2]=rowid"), |
+ /* 115 */ "IdxLE" OpHelp("key=r[P3@P4]"), |
+ /* 116 */ "IdxGT" OpHelp("key=r[P3@P4]"), |
+ /* 117 */ "IdxLT" OpHelp("key=r[P3@P4]"), |
+ /* 118 */ "IdxGE" OpHelp("key=r[P3@P4]"), |
+ /* 119 */ "Destroy" OpHelp(""), |
+ /* 120 */ "Clear" OpHelp(""), |
+ /* 121 */ "ResetSorter" OpHelp(""), |
+ /* 122 */ "CreateIndex" OpHelp("r[P2]=root iDb=P1"), |
+ /* 123 */ "CreateTable" OpHelp("r[P2]=root iDb=P1"), |
+ /* 124 */ "ParseSchema" OpHelp(""), |
+ /* 125 */ "LoadAnalysis" OpHelp(""), |
+ /* 126 */ "DropTable" OpHelp(""), |
+ /* 127 */ "DropIndex" OpHelp(""), |
+ /* 128 */ "DropTrigger" OpHelp(""), |
+ /* 129 */ "IntegrityCk" OpHelp(""), |
+ /* 130 */ "RowSetAdd" OpHelp("rowset(P1)=r[P2]"), |
+ /* 131 */ "RowSetRead" OpHelp("r[P3]=rowset(P1)"), |
+ /* 132 */ "RowSetTest" OpHelp("if r[P3] in rowset(P1) goto P2"), |
+ /* 133 */ "Real" OpHelp("r[P2]=P4"), |
+ /* 134 */ "Program" OpHelp(""), |
+ /* 135 */ "Param" OpHelp(""), |
+ /* 136 */ "FkCounter" OpHelp("fkctr[P1]+=P2"), |
+ /* 137 */ "FkIfZero" OpHelp("if fkctr[P1]==0 goto P2"), |
+ /* 138 */ "MemMax" OpHelp("r[P1]=max(r[P1],r[P2])"), |
+ /* 139 */ "IfPos" OpHelp("if r[P1]>0 then r[P1]-=P3, goto P2"), |
+ /* 140 */ "SetIfNotPos" OpHelp("if r[P1]<=0 then r[P2]=P3"), |
+ /* 141 */ "IfNotZero" OpHelp("if r[P1]!=0 then r[P1]-=P3, goto P2"), |
+ /* 142 */ "DecrJumpZero" OpHelp("if (--r[P1])==0 goto P2"), |
+ /* 143 */ "JumpZeroIncr" OpHelp("if (r[P1]++)==0 ) goto P2"), |
+ /* 144 */ "AggStep0" OpHelp("accum=r[P3] step(r[P2@P5])"), |
+ /* 145 */ "AggStep" OpHelp("accum=r[P3] step(r[P2@P5])"), |
+ /* 146 */ "AggFinal" OpHelp("accum=r[P1] N=P2"), |
+ /* 147 */ "IncrVacuum" OpHelp(""), |
+ /* 148 */ "Expire" OpHelp(""), |
+ /* 149 */ "TableLock" OpHelp("iDb=P1 root=P2 write=P3"), |
+ /* 150 */ "VBegin" OpHelp(""), |
+ /* 151 */ "VCreate" OpHelp(""), |
+ /* 152 */ "VDestroy" OpHelp(""), |
+ /* 153 */ "VOpen" OpHelp(""), |
+ /* 154 */ "VColumn" OpHelp("r[P3]=vcolumn(P2)"), |
+ /* 155 */ "VNext" OpHelp(""), |
+ /* 156 */ "VRename" OpHelp(""), |
+ /* 157 */ "Pagecount" OpHelp(""), |
+ /* 158 */ "MaxPgcnt" OpHelp(""), |
+ /* 159 */ "Init" OpHelp("Start at P2"), |
+ /* 160 */ "CursorHint" OpHelp(""), |
+ /* 161 */ "Noop" OpHelp(""), |
+ /* 162 */ "Explain" OpHelp(""), |
+ }; |
+ return azName[i]; |
+} |
+#endif |
+ |
+/************** End of opcodes.c *********************************************/ |
+/************** Begin file os_unix.c *****************************************/ |
+/* |
+** 2004 May 22 |
+** |
+** 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 VFS implementation for unix-like operating systems |
+** include Linux, MacOSX, *BSD, QNX, VxWorks, AIX, HPUX, and others. |
+** |
+** There are actually several different VFS implementations in this file. |
+** The differences are in the way that file locking is done. The default |
+** implementation uses Posix Advisory Locks. Alternative implementations |
+** use flock(), dot-files, various proprietary locking schemas, or simply |
+** skip locking all together. |
+** |
+** This source file is organized into divisions where the logic for various |
+** subfunctions is contained within the appropriate division. PLEASE |
+** KEEP THE STRUCTURE OF THIS FILE INTACT. New code should be placed |
+** in the correct division and should be clearly labeled. |
+** |
+** The layout of divisions is as follows: |
+** |
+** * General-purpose declarations and utility functions. |
+** * Unique file ID logic used by VxWorks. |
+** * Various locking primitive implementations (all except proxy locking): |
+** + for Posix Advisory Locks |
+** + for no-op locks |
+** + for dot-file locks |
+** + for flock() locking |
+** + for named semaphore locks (VxWorks only) |
+** + for AFP filesystem locks (MacOSX only) |
+** * sqlite3_file methods not associated with locking. |
+** * Definitions of sqlite3_io_methods objects for all locking |
+** methods plus "finder" functions for each locking method. |
+** * sqlite3_vfs method implementations. |
+** * Locking primitives for the proxy uber-locking-method. (MacOSX only) |
+** * Definitions of sqlite3_vfs objects for all locking methods |
+** plus implementations of sqlite3_os_init() and sqlite3_os_end(). |
+*/ |
+/* #include "sqliteInt.h" */ |
+#if SQLITE_OS_UNIX /* This file is used on unix only */ |
+ |
+/* |
+** There are various methods for file locking used for concurrency |
+** control: |
+** |
+** 1. POSIX locking (the default), |
+** 2. No locking, |
+** 3. Dot-file locking, |
+** 4. flock() locking, |
+** 5. AFP locking (OSX only), |
+** 6. Named POSIX semaphores (VXWorks only), |
+** 7. proxy locking. (OSX only) |
+** |
+** Styles 4, 5, and 7 are only available of SQLITE_ENABLE_LOCKING_STYLE |
+** is defined to 1. The SQLITE_ENABLE_LOCKING_STYLE also enables automatic |
+** selection of the appropriate locking style based on the filesystem |
+** where the database is located. |
+*/ |
+#if !defined(SQLITE_ENABLE_LOCKING_STYLE) |
+# if defined(__APPLE__) |
+# define SQLITE_ENABLE_LOCKING_STYLE 1 |
+# else |
+# define SQLITE_ENABLE_LOCKING_STYLE 0 |
+# endif |
+#endif |
+ |
+/* |
+** standard include files. |
+*/ |
+#include <sys/types.h> |
+#include <sys/stat.h> |
+#include <fcntl.h> |
+#include <unistd.h> |
+/* #include <time.h> */ |
+#include <sys/time.h> |
+#include <errno.h> |
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
+# include <sys/mman.h> |
+#endif |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+# include <sys/ioctl.h> |
+# include <sys/file.h> |
+# include <sys/param.h> |
+#endif /* SQLITE_ENABLE_LOCKING_STYLE */ |
+ |
+#if defined(__APPLE__) && ((__MAC_OS_X_VERSION_MIN_REQUIRED > 1050) || \ |
+ (__IPHONE_OS_VERSION_MIN_REQUIRED > 2000)) |
+# if (!defined(TARGET_OS_EMBEDDED) || (TARGET_OS_EMBEDDED==0)) \ |
+ && (!defined(TARGET_IPHONE_SIMULATOR) || (TARGET_IPHONE_SIMULATOR==0)) |
+# define HAVE_GETHOSTUUID 1 |
+# else |
+# warning "gethostuuid() is disabled." |
+# endif |
+#endif |
+ |
+ |
+#if OS_VXWORKS |
+/* # include <sys/ioctl.h> */ |
+# include <semaphore.h> |
+# include <limits.h> |
+#endif /* OS_VXWORKS */ |
+ |
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE |
+# include <sys/mount.h> |
+#endif |
+ |
+#ifdef HAVE_UTIME |
+# include <utime.h> |
+#endif |
+ |
+/* |
+** Allowed values of unixFile.fsFlags |
+*/ |
+#define SQLITE_FSFLAGS_IS_MSDOS 0x1 |
+ |
+/* |
+** If we are to be thread-safe, include the pthreads header and define |
+** the SQLITE_UNIX_THREADS macro. |
+*/ |
+#if SQLITE_THREADSAFE |
+/* # include <pthread.h> */ |
+# define SQLITE_UNIX_THREADS 1 |
+#endif |
+ |
+/* |
+** Default permissions when creating a new file |
+*/ |
+#ifndef SQLITE_DEFAULT_FILE_PERMISSIONS |
+# define SQLITE_DEFAULT_FILE_PERMISSIONS 0644 |
+#endif |
+ |
+/* |
+** Default permissions when creating auto proxy dir |
+*/ |
+#ifndef SQLITE_DEFAULT_PROXYDIR_PERMISSIONS |
+# define SQLITE_DEFAULT_PROXYDIR_PERMISSIONS 0755 |
+#endif |
+ |
+/* |
+** Maximum supported path-length. |
+*/ |
+#define MAX_PATHNAME 512 |
+ |
+/* Always cast the getpid() return type for compatibility with |
+** kernel modules in VxWorks. */ |
+#define osGetpid(X) (pid_t)getpid() |
+ |
+/* |
+** Only set the lastErrno if the error code is a real error and not |
+** a normal expected return code of SQLITE_BUSY or SQLITE_OK |
+*/ |
+#define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) |
+ |
+/* Forward references */ |
+typedef struct unixShm unixShm; /* Connection shared memory */ |
+typedef struct unixShmNode unixShmNode; /* Shared memory instance */ |
+typedef struct unixInodeInfo unixInodeInfo; /* An i-node */ |
+typedef struct UnixUnusedFd UnixUnusedFd; /* An unused file descriptor */ |
+ |
+/* |
+** Sometimes, after a file handle is closed by SQLite, the file descriptor |
+** cannot be closed immediately. In these cases, instances of the following |
+** structure are used to store the file descriptor while waiting for an |
+** opportunity to either close or reuse it. |
+*/ |
+struct UnixUnusedFd { |
+ int fd; /* File descriptor to close */ |
+ int flags; /* Flags this file descriptor was opened with */ |
+ UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ |
+}; |
+ |
+/* |
+** The unixFile structure is subclass of sqlite3_file specific to the unix |
+** VFS implementations. |
+*/ |
+typedef struct unixFile unixFile; |
+struct unixFile { |
+ sqlite3_io_methods const *pMethod; /* Always the first entry */ |
+ sqlite3_vfs *pVfs; /* The VFS that created this unixFile */ |
+ unixInodeInfo *pInode; /* Info about locks on this inode */ |
+ int h; /* The file descriptor */ |
+ unsigned char eFileLock; /* The type of lock held on this fd */ |
+ unsigned short int ctrlFlags; /* Behavioral bits. UNIXFILE_* flags */ |
+ int lastErrno; /* The unix errno from last I/O error */ |
+ void *lockingContext; /* Locking style specific state */ |
+ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ |
+ const char *zPath; /* Name of the file */ |
+ unixShm *pShm; /* Shared memory segment information */ |
+ int szChunk; /* Configured by FCNTL_CHUNK_SIZE */ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ int nFetchOut; /* Number of outstanding xFetch refs */ |
+ sqlite3_int64 mmapSize; /* Usable size of mapping at pMapRegion */ |
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapping at pMapRegion */ |
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ |
+ void *pMapRegion; /* Memory mapped region */ |
+#endif |
+#ifdef __QNXNTO__ |
+ int sectorSize; /* Device sector size */ |
+ int deviceCharacteristics; /* Precomputed device characteristics */ |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ int openFlags; /* The flags specified at open() */ |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) |
+ unsigned fsFlags; /* cached details from statfs() */ |
+#endif |
+#if OS_VXWORKS |
+ struct vxworksFileId *pId; /* Unique file ID */ |
+#endif |
+#ifdef SQLITE_DEBUG |
+ /* The next group of variables are used to track whether or not the |
+ ** transaction counter in bytes 24-27 of database files are updated |
+ ** whenever any part of the database changes. An assertion fault will |
+ ** occur if a file is updated without also updating the transaction |
+ ** counter. This test is made to avoid new problems similar to the |
+ ** one described by ticket #3584. |
+ */ |
+ unsigned char transCntrChng; /* True if the transaction counter changed */ |
+ unsigned char dbUpdate; /* True if any part of database file changed */ |
+ unsigned char inNormalWrite; /* True if in a normal write operation */ |
+ |
+#endif |
+ |
+#ifdef SQLITE_TEST |
+ /* In test mode, increase the size of this structure a bit so that |
+ ** it is larger than the struct CrashFile defined in test6.c. |
+ */ |
+ char aPadding[32]; |
+#endif |
+}; |
+ |
+/* This variable holds the process id (pid) from when the xRandomness() |
+** method was called. If xOpen() is called from a different process id, |
+** indicating that a fork() has occurred, the PRNG will be reset. |
+*/ |
+static pid_t randomnessPid = 0; |
+ |
+/* |
+** Allowed values for the unixFile.ctrlFlags bitmask: |
+*/ |
+#define UNIXFILE_EXCL 0x01 /* Connections from one process only */ |
+#define UNIXFILE_RDONLY 0x02 /* Connection is read only */ |
+#define UNIXFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ |
+#ifndef SQLITE_DISABLE_DIRSYNC |
+# define UNIXFILE_DIRSYNC 0x08 /* Directory sync needed */ |
+#else |
+# define UNIXFILE_DIRSYNC 0x00 |
+#endif |
+#define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ |
+#define UNIXFILE_DELETE 0x20 /* Delete on close */ |
+#define UNIXFILE_URI 0x40 /* Filename might have query parameters */ |
+#define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ |
+ |
+/* |
+** Include code that is common to all os_*.c files |
+*/ |
+/************** Include os_common.h in the middle of os_unix.c ***************/ |
+/************** Begin file os_common.h ***************************************/ |
+/* |
+** 2004 May 22 |
+** |
+** 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 macros and a little bit of code that is common to |
+** all of the platform-specific files (os_*.c) and is #included into those |
+** files. |
+** |
+** This file should be #included by the os_*.c files only. It is not a |
+** general purpose header file. |
+*/ |
+#ifndef _OS_COMMON_H_ |
+#define _OS_COMMON_H_ |
+ |
+/* |
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG |
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the |
+** switch. The following code should catch this problem at compile-time. |
+*/ |
+#ifdef MEMORY_DEBUG |
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." |
+#endif |
+ |
+/* |
+** Macros for performance tracing. Normally turned off. Only works |
+** on i486 hardware. |
+*/ |
+#ifdef SQLITE_PERFORMANCE_TRACE |
+ |
+/* |
+** hwtime.h contains inline assembler code for implementing |
+** high-performance timing routines. |
+*/ |
+/************** Include hwtime.h in the middle of os_common.h ****************/ |
+/************** Begin file hwtime.h ******************************************/ |
+/* |
+** 2008 May 27 |
+** |
+** 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 inline asm code for retrieving "high-performance" |
+** counters for x86 class CPUs. |
+*/ |
+#ifndef _HWTIME_H_ |
+#define _HWTIME_H_ |
+ |
+/* |
+** The following routine only works on pentium-class (or newer) processors. |
+** It uses the RDTSC opcode to read the cycle count value out of the |
+** processor and returns that value. This can be used for high-res |
+** profiling. |
+*/ |
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \ |
+ (defined(i386) || defined(__i386__) || defined(_M_IX86)) |
+ |
+ #if defined(__GNUC__) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned int lo, hi; |
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
+ return (sqlite_uint64)hi << 32 | lo; |
+ } |
+ |
+ #elif defined(_MSC_VER) |
+ |
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ |
+ __asm { |
+ rdtsc |
+ ret ; return value at EDX:EAX |
+ } |
+ } |
+ |
+ #endif |
+ |
+#elif (defined(__GNUC__) && defined(__x86_64__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long val; |
+ __asm__ __volatile__ ("rdtsc" : "=A" (val)); |
+ return val; |
+ } |
+ |
+#elif (defined(__GNUC__) && defined(__ppc__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long long retval; |
+ unsigned long junk; |
+ __asm__ __volatile__ ("\n\ |
+ 1: mftbu %1\n\ |
+ mftb %L0\n\ |
+ mftbu %0\n\ |
+ cmpw %0,%1\n\ |
+ bne 1b" |
+ : "=r" (retval), "=r" (junk)); |
+ return retval; |
+ } |
+ |
+#else |
+ |
+ #error Need implementation of sqlite3Hwtime() for your platform. |
+ |
+ /* |
+ ** To compile without implementing sqlite3Hwtime() for your platform, |
+ ** you can remove the above #error and use the following |
+ ** stub function. You will lose timing support for many |
+ ** of the debugging and testing utilities, but it should at |
+ ** least compile and run. |
+ */ |
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } |
+ |
+#endif |
+ |
+#endif /* !defined(_HWTIME_H_) */ |
+ |
+/************** End of hwtime.h **********************************************/ |
+/************** Continuing where we left off in os_common.h ******************/ |
+ |
+static sqlite_uint64 g_start; |
+static sqlite_uint64 g_elapsed; |
+#define TIMER_START g_start=sqlite3Hwtime() |
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start |
+#define TIMER_ELAPSED g_elapsed |
+#else |
+#define TIMER_START |
+#define TIMER_END |
+#define TIMER_ELAPSED ((sqlite_uint64)0) |
+#endif |
+ |
+/* |
+** If we compile with the SQLITE_TEST macro set, then the following block |
+** of code will give us the ability to simulate a disk I/O error. This |
+** is used for testing the I/O recovery logic. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ |
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ |
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ |
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ |
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */ |
+SQLITE_API int sqlite3_diskfull_pending = 0; |
+SQLITE_API int sqlite3_diskfull = 0; |
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) |
+#define SimulateIOError(CODE) \ |
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ |
+ || sqlite3_io_error_pending-- == 1 ) \ |
+ { local_ioerr(); CODE; } |
+static void local_ioerr(){ |
+ IOTRACE(("IOERR\n")); |
+ sqlite3_io_error_hit++; |
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; |
+} |
+#define SimulateDiskfullError(CODE) \ |
+ if( sqlite3_diskfull_pending ){ \ |
+ if( sqlite3_diskfull_pending == 1 ){ \ |
+ local_ioerr(); \ |
+ sqlite3_diskfull = 1; \ |
+ sqlite3_io_error_hit = 1; \ |
+ CODE; \ |
+ }else{ \ |
+ sqlite3_diskfull_pending--; \ |
+ } \ |
+ } |
+#else |
+#define SimulateIOErrorBenign(X) |
+#define SimulateIOError(A) |
+#define SimulateDiskfullError(A) |
+#endif |
+ |
+/* |
+** When testing, keep a count of the number of open files. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_open_file_count = 0; |
+#define OpenCounter(X) sqlite3_open_file_count+=(X) |
+#else |
+#define OpenCounter(X) |
+#endif |
+ |
+#endif /* !defined(_OS_COMMON_H_) */ |
+ |
+/************** End of os_common.h *******************************************/ |
+/************** Continuing where we left off in os_unix.c ********************/ |
+ |
+/* |
+** Define various macros that are missing from some systems. |
+*/ |
+#ifndef O_LARGEFILE |
+# define O_LARGEFILE 0 |
+#endif |
+#ifdef SQLITE_DISABLE_LFS |
+# undef O_LARGEFILE |
+# define O_LARGEFILE 0 |
+#endif |
+#ifndef O_NOFOLLOW |
+# define O_NOFOLLOW 0 |
+#endif |
+#ifndef O_BINARY |
+# define O_BINARY 0 |
+#endif |
+ |
+/* |
+** The threadid macro resolves to the thread-id or to 0. Used for |
+** testing and debugging only. |
+*/ |
+#if SQLITE_THREADSAFE |
+#define threadid pthread_self() |
+#else |
+#define threadid 0 |
+#endif |
+ |
+/* |
+** HAVE_MREMAP defaults to true on Linux and false everywhere else. |
+*/ |
+#if !defined(HAVE_MREMAP) |
+# if defined(__linux__) && defined(_GNU_SOURCE) |
+# define HAVE_MREMAP 1 |
+# else |
+# define HAVE_MREMAP 0 |
+# endif |
+#endif |
+ |
+/* |
+** Explicitly call the 64-bit version of lseek() on Android. Otherwise, lseek() |
+** is the 32-bit version, even if _FILE_OFFSET_BITS=64 is defined. |
+*/ |
+#ifdef __ANDROID__ |
+# define lseek lseek64 |
+#endif |
+ |
+/* |
+** Different Unix systems declare open() in different ways. Same use |
+** open(const char*,int,mode_t). Others use open(const char*,int,...). |
+** The difference is important when using a pointer to the function. |
+** |
+** The safest way to deal with the problem is to always use this wrapper |
+** which always has the same well-defined interface. |
+*/ |
+static int posixOpen(const char *zFile, int flags, int mode){ |
+ return open(zFile, flags, mode); |
+} |
+ |
+/* Forward reference */ |
+static int openDirectory(const char*, int*); |
+static int unixGetpagesize(void); |
+ |
+/* |
+** Many system calls are accessed through pointer-to-functions so that |
+** they may be overridden at runtime to facilitate fault injection during |
+** testing and sandboxing. The following array holds the names and pointers |
+** to all overrideable system calls. |
+*/ |
+static struct unix_syscall { |
+ const char *zName; /* Name of the system call */ |
+ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ |
+ sqlite3_syscall_ptr pDefault; /* Default value */ |
+} aSyscall[] = { |
+ { "open", (sqlite3_syscall_ptr)posixOpen, 0 }, |
+#define osOpen ((int(*)(const char*,int,int))aSyscall[0].pCurrent) |
+ |
+ { "close", (sqlite3_syscall_ptr)close, 0 }, |
+#define osClose ((int(*)(int))aSyscall[1].pCurrent) |
+ |
+ { "access", (sqlite3_syscall_ptr)access, 0 }, |
+#define osAccess ((int(*)(const char*,int))aSyscall[2].pCurrent) |
+ |
+ { "getcwd", (sqlite3_syscall_ptr)getcwd, 0 }, |
+#define osGetcwd ((char*(*)(char*,size_t))aSyscall[3].pCurrent) |
+ |
+ { "stat", (sqlite3_syscall_ptr)stat, 0 }, |
+#define osStat ((int(*)(const char*,struct stat*))aSyscall[4].pCurrent) |
+ |
+/* |
+** The DJGPP compiler environment looks mostly like Unix, but it |
+** lacks the fcntl() system call. So redefine fcntl() to be something |
+** that always succeeds. This means that locking does not occur under |
+** DJGPP. But it is DOS - what did you expect? |
+*/ |
+#ifdef __DJGPP__ |
+ { "fstat", 0, 0 }, |
+#define osFstat(a,b,c) 0 |
+#else |
+ { "fstat", (sqlite3_syscall_ptr)fstat, 0 }, |
+#define osFstat ((int(*)(int,struct stat*))aSyscall[5].pCurrent) |
+#endif |
+ |
+ { "ftruncate", (sqlite3_syscall_ptr)ftruncate, 0 }, |
+#define osFtruncate ((int(*)(int,off_t))aSyscall[6].pCurrent) |
+ |
+ { "fcntl", (sqlite3_syscall_ptr)fcntl, 0 }, |
+#define osFcntl ((int(*)(int,int,...))aSyscall[7].pCurrent) |
+ |
+ { "read", (sqlite3_syscall_ptr)read, 0 }, |
+#define osRead ((ssize_t(*)(int,void*,size_t))aSyscall[8].pCurrent) |
+ |
+#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE |
+ { "pread", (sqlite3_syscall_ptr)pread, 0 }, |
+#else |
+ { "pread", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osPread ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[9].pCurrent) |
+ |
+#if defined(USE_PREAD64) |
+ { "pread64", (sqlite3_syscall_ptr)pread64, 0 }, |
+#else |
+ { "pread64", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osPread64 ((ssize_t(*)(int,void*,size_t,off_t))aSyscall[10].pCurrent) |
+ |
+ { "write", (sqlite3_syscall_ptr)write, 0 }, |
+#define osWrite ((ssize_t(*)(int,const void*,size_t))aSyscall[11].pCurrent) |
+ |
+#if defined(USE_PREAD) || SQLITE_ENABLE_LOCKING_STYLE |
+ { "pwrite", (sqlite3_syscall_ptr)pwrite, 0 }, |
+#else |
+ { "pwrite", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osPwrite ((ssize_t(*)(int,const void*,size_t,off_t))\ |
+ aSyscall[12].pCurrent) |
+ |
+#if defined(USE_PREAD64) |
+ { "pwrite64", (sqlite3_syscall_ptr)pwrite64, 0 }, |
+#else |
+ { "pwrite64", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off_t))\ |
+ aSyscall[13].pCurrent) |
+ |
+ { "fchmod", (sqlite3_syscall_ptr)fchmod, 0 }, |
+#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent) |
+ |
+#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
+ { "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 }, |
+#else |
+ { "fallocate", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent) |
+ |
+ { "unlink", (sqlite3_syscall_ptr)unlink, 0 }, |
+#define osUnlink ((int(*)(const char*))aSyscall[16].pCurrent) |
+ |
+ { "openDirectory", (sqlite3_syscall_ptr)openDirectory, 0 }, |
+#define osOpenDirectory ((int(*)(const char*,int*))aSyscall[17].pCurrent) |
+ |
+ { "mkdir", (sqlite3_syscall_ptr)mkdir, 0 }, |
+#define osMkdir ((int(*)(const char*,mode_t))aSyscall[18].pCurrent) |
+ |
+ { "rmdir", (sqlite3_syscall_ptr)rmdir, 0 }, |
+#define osRmdir ((int(*)(const char*))aSyscall[19].pCurrent) |
+ |
+ { "fchown", (sqlite3_syscall_ptr)fchown, 0 }, |
+#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].pCurrent) |
+ |
+ { "geteuid", (sqlite3_syscall_ptr)geteuid, 0 }, |
+#define osGeteuid ((uid_t(*)(void))aSyscall[21].pCurrent) |
+ |
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
+ { "mmap", (sqlite3_syscall_ptr)mmap, 0 }, |
+#define osMmap ((void*(*)(void*,size_t,int,int,int,off_t))aSyscall[22].pCurrent) |
+ |
+ { "munmap", (sqlite3_syscall_ptr)munmap, 0 }, |
+#define osMunmap ((void*(*)(void*,size_t))aSyscall[23].pCurrent) |
+ |
+#if HAVE_MREMAP |
+ { "mremap", (sqlite3_syscall_ptr)mremap, 0 }, |
+#else |
+ { "mremap", (sqlite3_syscall_ptr)0, 0 }, |
+#endif |
+#define osMremap ((void*(*)(void*,size_t,size_t,int,...))aSyscall[24].pCurrent) |
+ |
+ { "getpagesize", (sqlite3_syscall_ptr)unixGetpagesize, 0 }, |
+#define osGetpagesize ((int(*)(void))aSyscall[25].pCurrent) |
+ |
+ { "readlink", (sqlite3_syscall_ptr)readlink, 0 }, |
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[26].pCurrent) |
+ |
+#endif |
+ |
+}; /* End of the overrideable system calls */ |
+ |
+ |
+/* |
+** On some systems, calls to fchown() will trigger a message in a security |
+** log if they come from non-root processes. So avoid calling fchown() if |
+** we are not running as root. |
+*/ |
+static int robustFchown(int fd, uid_t uid, gid_t gid){ |
+#if OS_VXWORKS |
+ return 0; |
+#else |
+ return osGeteuid() ? 0 : osFchown(fd,uid,gid); |
+#endif |
+} |
+ |
+/* |
+** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
+** "unix" VFSes. Return SQLITE_OK opon successfully updating the |
+** system call pointer, or SQLITE_NOTFOUND if there is no configurable |
+** system call named zName. |
+*/ |
+static int unixSetSystemCall( |
+ sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ |
+ const char *zName, /* Name of system call to override */ |
+ sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ |
+){ |
+ unsigned int i; |
+ int rc = SQLITE_NOTFOUND; |
+ |
+ UNUSED_PARAMETER(pNotUsed); |
+ if( zName==0 ){ |
+ /* If no zName is given, restore all system calls to their default |
+ ** settings and return NULL |
+ */ |
+ rc = SQLITE_OK; |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( aSyscall[i].pDefault ){ |
+ aSyscall[i].pCurrent = aSyscall[i].pDefault; |
+ } |
+ } |
+ }else{ |
+ /* If zName is specified, operate on only the one system call |
+ ** specified. |
+ */ |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ){ |
+ if( aSyscall[i].pDefault==0 ){ |
+ aSyscall[i].pDefault = aSyscall[i].pCurrent; |
+ } |
+ rc = SQLITE_OK; |
+ if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault; |
+ aSyscall[i].pCurrent = pNewFunc; |
+ break; |
+ } |
+ } |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Return the value of a system call. Return NULL if zName is not a |
+** recognized system call name. NULL is also returned if the system call |
+** is currently undefined. |
+*/ |
+static sqlite3_syscall_ptr unixGetSystemCall( |
+ sqlite3_vfs *pNotUsed, |
+ const char *zName |
+){ |
+ unsigned int i; |
+ |
+ UNUSED_PARAMETER(pNotUsed); |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent; |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** Return the name of the first system call after zName. If zName==NULL |
+** then return the name of the first system call. Return NULL if zName |
+** is the last system call or if zName is not the name of a valid |
+** system call. |
+*/ |
+static const char *unixNextSystemCall(sqlite3_vfs *p, const char *zName){ |
+ int i = -1; |
+ |
+ UNUSED_PARAMETER(p); |
+ if( zName ){ |
+ for(i=0; i<ArraySize(aSyscall)-1; i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break; |
+ } |
+ } |
+ for(i++; i<ArraySize(aSyscall); i++){ |
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName; |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** Do not accept any file descriptor less than this value, in order to avoid |
+** opening database file using file descriptors that are commonly used for |
+** standard input, output, and error. |
+*/ |
+#ifndef SQLITE_MINIMUM_FILE_DESCRIPTOR |
+# define SQLITE_MINIMUM_FILE_DESCRIPTOR 3 |
+#endif |
+ |
+/* |
+** Invoke open(). Do so multiple times, until it either succeeds or |
+** fails for some reason other than EINTR. |
+** |
+** If the file creation mode "m" is 0 then set it to the default for |
+** SQLite. The default is SQLITE_DEFAULT_FILE_PERMISSIONS (normally |
+** 0644) as modified by the system umask. If m is not 0, then |
+** make the file creation mode be exactly m ignoring the umask. |
+** |
+** The m parameter will be non-zero only when creating -wal, -journal, |
+** and -shm files. We want those files to have *exactly* the same |
+** permissions as their original database, unadulterated by the umask. |
+** In that way, if a database file is -rw-rw-rw or -rw-rw-r-, and a |
+** transaction crashes and leaves behind hot journals, then any |
+** process that is able to write to the database will also be able to |
+** recover the hot journals. |
+*/ |
+static int robust_open(const char *z, int f, mode_t m){ |
+ int fd; |
+ mode_t m2 = m ? m : SQLITE_DEFAULT_FILE_PERMISSIONS; |
+ while(1){ |
+#if defined(O_CLOEXEC) |
+ fd = osOpen(z,f|O_CLOEXEC,m2); |
+#else |
+ fd = osOpen(z,f,m2); |
+#endif |
+ if( fd<0 ){ |
+ if( errno==EINTR ) continue; |
+ break; |
+ } |
+ if( fd>=SQLITE_MINIMUM_FILE_DESCRIPTOR ) break; |
+ osClose(fd); |
+ sqlite3_log(SQLITE_WARNING, |
+ "attempt to open \"%s\" as file descriptor %d", z, fd); |
+ fd = -1; |
+ if( osOpen("/dev/null", f, m)<0 ) break; |
+ } |
+ if( fd>=0 ){ |
+ if( m!=0 ){ |
+ struct stat statbuf; |
+ if( osFstat(fd, &statbuf)==0 |
+ && statbuf.st_size==0 |
+ && (statbuf.st_mode&0777)!=m |
+ ){ |
+ osFchmod(fd, m); |
+ } |
+ } |
+#if defined(FD_CLOEXEC) && (!defined(O_CLOEXEC) || O_CLOEXEC==0) |
+ osFcntl(fd, F_SETFD, osFcntl(fd, F_GETFD, 0) | FD_CLOEXEC); |
+#endif |
+ } |
+ return fd; |
+} |
+ |
+/* |
+** Helper functions to obtain and relinquish the global mutex. The |
+** global mutex is used to protect the unixInodeInfo and |
+** vxworksFileId objects used by this file, all of which may be |
+** shared by multiple threads. |
+** |
+** Function unixMutexHeld() is used to assert() that the global mutex |
+** is held when required. This function is only used as part of assert() |
+** statements. e.g. |
+** |
+** unixEnterMutex() |
+** assert( unixMutexHeld() ); |
+** unixEnterLeave() |
+*/ |
+static void unixEnterMutex(void){ |
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+static void unixLeaveMutex(void){ |
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+#ifdef SQLITE_DEBUG |
+static int unixMutexHeld(void) { |
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+#endif |
+ |
+ |
+#ifdef SQLITE_HAVE_OS_TRACE |
+/* |
+** Helper function for printing out trace information from debugging |
+** binaries. This returns the string representation of the supplied |
+** integer lock-type. |
+*/ |
+static const char *azFileLock(int eFileLock){ |
+ switch( eFileLock ){ |
+ case NO_LOCK: return "NONE"; |
+ case SHARED_LOCK: return "SHARED"; |
+ case RESERVED_LOCK: return "RESERVED"; |
+ case PENDING_LOCK: return "PENDING"; |
+ case EXCLUSIVE_LOCK: return "EXCLUSIVE"; |
+ } |
+ return "ERROR"; |
+} |
+#endif |
+ |
+#ifdef SQLITE_LOCK_TRACE |
+/* |
+** Print out information about all locking operations. |
+** |
+** This routine is used for troubleshooting locks on multithreaded |
+** platforms. Enable by compiling with the -DSQLITE_LOCK_TRACE |
+** command-line option on the compiler. This code is normally |
+** turned off. |
+*/ |
+static int lockTrace(int fd, int op, struct flock *p){ |
+ char *zOpName, *zType; |
+ int s; |
+ int savedErrno; |
+ if( op==F_GETLK ){ |
+ zOpName = "GETLK"; |
+ }else if( op==F_SETLK ){ |
+ zOpName = "SETLK"; |
+ }else{ |
+ s = osFcntl(fd, op, p); |
+ sqlite3DebugPrintf("fcntl unknown %d %d %d\n", fd, op, s); |
+ return s; |
+ } |
+ if( p->l_type==F_RDLCK ){ |
+ zType = "RDLCK"; |
+ }else if( p->l_type==F_WRLCK ){ |
+ zType = "WRLCK"; |
+ }else if( p->l_type==F_UNLCK ){ |
+ zType = "UNLCK"; |
+ }else{ |
+ assert( 0 ); |
+ } |
+ assert( p->l_whence==SEEK_SET ); |
+ s = osFcntl(fd, op, p); |
+ savedErrno = errno; |
+ sqlite3DebugPrintf("fcntl %d %d %s %s %d %d %d %d\n", |
+ threadid, fd, zOpName, zType, (int)p->l_start, (int)p->l_len, |
+ (int)p->l_pid, s); |
+ if( s==(-1) && op==F_SETLK && (p->l_type==F_RDLCK || p->l_type==F_WRLCK) ){ |
+ struct flock l2; |
+ l2 = *p; |
+ osFcntl(fd, F_GETLK, &l2); |
+ if( l2.l_type==F_RDLCK ){ |
+ zType = "RDLCK"; |
+ }else if( l2.l_type==F_WRLCK ){ |
+ zType = "WRLCK"; |
+ }else if( l2.l_type==F_UNLCK ){ |
+ zType = "UNLCK"; |
+ }else{ |
+ assert( 0 ); |
+ } |
+ sqlite3DebugPrintf("fcntl-failure-reason: %s %d %d %d\n", |
+ zType, (int)l2.l_start, (int)l2.l_len, (int)l2.l_pid); |
+ } |
+ errno = savedErrno; |
+ return s; |
+} |
+#undef osFcntl |
+#define osFcntl lockTrace |
+#endif /* SQLITE_LOCK_TRACE */ |
+ |
+/* |
+** Retry ftruncate() calls that fail due to EINTR |
+** |
+** All calls to ftruncate() within this file should be made through |
+** this wrapper. On the Android platform, bypassing the logic below |
+** could lead to a corrupt database. |
+*/ |
+static int robust_ftruncate(int h, sqlite3_int64 sz){ |
+ int rc; |
+#ifdef __ANDROID__ |
+ /* On Android, ftruncate() always uses 32-bit offsets, even if |
+ ** _FILE_OFFSET_BITS=64 is defined. This means it is unsafe to attempt to |
+ ** truncate a file to any size larger than 2GiB. Silently ignore any |
+ ** such attempts. */ |
+ if( sz>(sqlite3_int64)0x7FFFFFFF ){ |
+ rc = SQLITE_OK; |
+ }else |
+#endif |
+ do{ rc = osFtruncate(h,sz); }while( rc<0 && errno==EINTR ); |
+ return rc; |
+} |
+ |
+/* |
+** This routine translates a standard POSIX errno code into something |
+** useful to the clients of the sqlite3 functions. Specifically, it is |
+** intended to translate a variety of "try again" errors into SQLITE_BUSY |
+** and a variety of "please close the file descriptor NOW" errors into |
+** SQLITE_IOERR |
+** |
+** Errors during initialization of locks, or file system support for locks, |
+** should handle ENOLCK, ENOTSUP, EOPNOTSUPP separately. |
+*/ |
+static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) { |
+ assert( (sqliteIOErr == SQLITE_IOERR_LOCK) || |
+ (sqliteIOErr == SQLITE_IOERR_UNLOCK) || |
+ (sqliteIOErr == SQLITE_IOERR_RDLOCK) || |
+ (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ); |
+ switch (posixError) { |
+ case EACCES: |
+ case EAGAIN: |
+ case ETIMEDOUT: |
+ case EBUSY: |
+ case EINTR: |
+ case ENOLCK: |
+ /* random NFS retry error, unless during file system support |
+ * introspection, in which it actually means what it says */ |
+ return SQLITE_BUSY; |
+ |
+ case EPERM: |
+ return SQLITE_PERM; |
+ |
+ default: |
+ return sqliteIOErr; |
+ } |
+} |
+ |
+ |
+/****************************************************************************** |
+****************** Begin Unique File ID Utility Used By VxWorks *************** |
+** |
+** On most versions of unix, we can get a unique ID for a file by concatenating |
+** the device number and the inode number. But this does not work on VxWorks. |
+** On VxWorks, a unique file id must be based on the canonical filename. |
+** |
+** A pointer to an instance of the following structure can be used as a |
+** unique file ID in VxWorks. Each instance of this structure contains |
+** a copy of the canonical filename. There is also a reference count. |
+** The structure is reclaimed when the number of pointers to it drops to |
+** zero. |
+** |
+** There are never very many files open at one time and lookups are not |
+** a performance-critical path, so it is sufficient to put these |
+** structures on a linked list. |
+*/ |
+struct vxworksFileId { |
+ struct vxworksFileId *pNext; /* Next in a list of them all */ |
+ int nRef; /* Number of references to this one */ |
+ int nName; /* Length of the zCanonicalName[] string */ |
+ char *zCanonicalName; /* Canonical filename */ |
+}; |
+ |
+#if OS_VXWORKS |
+/* |
+** All unique filenames are held on a linked list headed by this |
+** variable: |
+*/ |
+static struct vxworksFileId *vxworksFileList = 0; |
+ |
+/* |
+** Simplify a filename into its canonical form |
+** by making the following changes: |
+** |
+** * removing any trailing and duplicate / |
+** * convert /./ into just / |
+** * convert /A/../ where A is any simple name into just / |
+** |
+** Changes are made in-place. Return the new name length. |
+** |
+** The original filename is in z[0..n-1]. Return the number of |
+** characters in the simplified name. |
+*/ |
+static int vxworksSimplifyName(char *z, int n){ |
+ int i, j; |
+ while( n>1 && z[n-1]=='/' ){ n--; } |
+ for(i=j=0; i<n; i++){ |
+ if( z[i]=='/' ){ |
+ if( z[i+1]=='/' ) continue; |
+ if( z[i+1]=='.' && i+2<n && z[i+2]=='/' ){ |
+ i += 1; |
+ continue; |
+ } |
+ if( z[i+1]=='.' && i+3<n && z[i+2]=='.' && z[i+3]=='/' ){ |
+ while( j>0 && z[j-1]!='/' ){ j--; } |
+ if( j>0 ){ j--; } |
+ i += 2; |
+ continue; |
+ } |
+ } |
+ z[j++] = z[i]; |
+ } |
+ z[j] = 0; |
+ return j; |
+} |
+ |
+/* |
+** Find a unique file ID for the given absolute pathname. Return |
+** a pointer to the vxworksFileId object. This pointer is the unique |
+** file ID. |
+** |
+** The nRef field of the vxworksFileId object is incremented before |
+** the object is returned. A new vxworksFileId object is created |
+** and added to the global list if necessary. |
+** |
+** If a memory allocation error occurs, return NULL. |
+*/ |
+static struct vxworksFileId *vxworksFindFileId(const char *zAbsoluteName){ |
+ struct vxworksFileId *pNew; /* search key and new file ID */ |
+ struct vxworksFileId *pCandidate; /* For looping over existing file IDs */ |
+ int n; /* Length of zAbsoluteName string */ |
+ |
+ assert( zAbsoluteName[0]=='/' ); |
+ n = (int)strlen(zAbsoluteName); |
+ pNew = sqlite3_malloc64( sizeof(*pNew) + (n+1) ); |
+ if( pNew==0 ) return 0; |
+ pNew->zCanonicalName = (char*)&pNew[1]; |
+ memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); |
+ n = vxworksSimplifyName(pNew->zCanonicalName, n); |
+ |
+ /* Search for an existing entry that matching the canonical name. |
+ ** If found, increment the reference count and return a pointer to |
+ ** the existing file ID. |
+ */ |
+ unixEnterMutex(); |
+ for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){ |
+ if( pCandidate->nName==n |
+ && memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0 |
+ ){ |
+ sqlite3_free(pNew); |
+ pCandidate->nRef++; |
+ unixLeaveMutex(); |
+ return pCandidate; |
+ } |
+ } |
+ |
+ /* No match was found. We will make a new file ID */ |
+ pNew->nRef = 1; |
+ pNew->nName = n; |
+ pNew->pNext = vxworksFileList; |
+ vxworksFileList = pNew; |
+ unixLeaveMutex(); |
+ return pNew; |
+} |
+ |
+/* |
+** Decrement the reference count on a vxworksFileId object. Free |
+** the object when the reference count reaches zero. |
+*/ |
+static void vxworksReleaseFileId(struct vxworksFileId *pId){ |
+ unixEnterMutex(); |
+ assert( pId->nRef>0 ); |
+ pId->nRef--; |
+ if( pId->nRef==0 ){ |
+ struct vxworksFileId **pp; |
+ for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){} |
+ assert( *pp==pId ); |
+ *pp = pId->pNext; |
+ sqlite3_free(pId); |
+ } |
+ unixLeaveMutex(); |
+} |
+#endif /* OS_VXWORKS */ |
+/*************** End of Unique File ID Utility Used By VxWorks **************** |
+******************************************************************************/ |
+ |
+ |
+/****************************************************************************** |
+*************************** Posix Advisory Locking **************************** |
+** |
+** POSIX advisory locks are broken by design. ANSI STD 1003.1 (1996) |
+** section 6.5.2.2 lines 483 through 490 specify that when a process |
+** sets or clears a lock, that operation overrides any prior locks set |
+** by the same process. It does not explicitly say so, but this implies |
+** that it overrides locks set by the same process using a different |
+** file descriptor. Consider this test case: |
+** |
+** int fd1 = open("./file1", O_RDWR|O_CREAT, 0644); |
+** int fd2 = open("./file2", O_RDWR|O_CREAT, 0644); |
+** |
+** Suppose ./file1 and ./file2 are really the same file (because |
+** one is a hard or symbolic link to the other) then if you set |
+** an exclusive lock on fd1, then try to get an exclusive lock |
+** on fd2, it works. I would have expected the second lock to |
+** fail since there was already a lock on the file due to fd1. |
+** But not so. Since both locks came from the same process, the |
+** second overrides the first, even though they were on different |
+** file descriptors opened on different file names. |
+** |
+** This means that we cannot use POSIX locks to synchronize file access |
+** among competing threads of the same process. POSIX locks will work fine |
+** to synchronize access for threads in separate processes, but not |
+** threads within the same process. |
+** |
+** To work around the problem, SQLite has to manage file locks internally |
+** on its own. Whenever a new database is opened, we have to find the |
+** specific inode of the database file (the inode is determined by the |
+** st_dev and st_ino fields of the stat structure that fstat() fills in) |
+** and check for locks already existing on that inode. When locks are |
+** created or removed, we have to look at our own internal record of the |
+** locks to see if another thread has previously set a lock on that same |
+** inode. |
+** |
+** (Aside: The use of inode numbers as unique IDs does not work on VxWorks. |
+** For VxWorks, we have to use the alternative unique ID system based on |
+** canonical filename and implemented in the previous division.) |
+** |
+** The sqlite3_file structure for POSIX is no longer just an integer file |
+** descriptor. It is now a structure that holds the integer file |
+** descriptor and a pointer to a structure that describes the internal |
+** locks on the corresponding inode. There is one locking structure |
+** per inode, so if the same inode is opened twice, both unixFile structures |
+** point to the same locking structure. The locking structure keeps |
+** a reference count (so we will know when to delete it) and a "cnt" |
+** field that tells us its internal lock status. cnt==0 means the |
+** file is unlocked. cnt==-1 means the file has an exclusive lock. |
+** cnt>0 means there are cnt shared locks on the file. |
+** |
+** Any attempt to lock or unlock a file first checks the locking |
+** structure. The fcntl() system call is only invoked to set a |
+** POSIX lock if the internal lock structure transitions between |
+** a locked and an unlocked state. |
+** |
+** But wait: there are yet more problems with POSIX advisory locks. |
+** |
+** If you close a file descriptor that points to a file that has locks, |
+** all locks on that file that are owned by the current process are |
+** released. To work around this problem, each unixInodeInfo object |
+** maintains a count of the number of pending locks on tha inode. |
+** When an attempt is made to close an unixFile, if there are |
+** other unixFile open on the same inode that are holding locks, the call |
+** to close() the file descriptor is deferred until all of the locks clear. |
+** The unixInodeInfo structure keeps a list of file descriptors that need to |
+** be closed and that list is walked (and cleared) when the last lock |
+** clears. |
+** |
+** Yet another problem: LinuxThreads do not play well with posix locks. |
+** |
+** Many older versions of linux use the LinuxThreads library which is |
+** not posix compliant. Under LinuxThreads, a lock created by thread |
+** A cannot be modified or overridden by a different thread B. |
+** Only thread A can modify the lock. Locking behavior is correct |
+** if the appliation uses the newer Native Posix Thread Library (NPTL) |
+** on linux - with NPTL a lock created by thread A can override locks |
+** in thread B. But there is no way to know at compile-time which |
+** threading library is being used. So there is no way to know at |
+** compile-time whether or not thread A can override locks on thread B. |
+** One has to do a run-time check to discover the behavior of the |
+** current process. |
+** |
+** SQLite used to support LinuxThreads. But support for LinuxThreads |
+** was dropped beginning with version 3.7.0. SQLite will still work with |
+** LinuxThreads provided that (1) there is no more than one connection |
+** per database file in the same process and (2) database connections |
+** do not move across threads. |
+*/ |
+ |
+/* |
+** An instance of the following structure serves as the key used |
+** to locate a particular unixInodeInfo object. |
+*/ |
+struct unixFileId { |
+ dev_t dev; /* Device number */ |
+#if OS_VXWORKS |
+ struct vxworksFileId *pId; /* Unique file ID for vxworks. */ |
+#else |
+ ino_t ino; /* Inode number */ |
+#endif |
+}; |
+ |
+/* |
+** An instance of the following structure is allocated for each open |
+** inode. Or, on LinuxThreads, there is one of these structures for |
+** each inode opened by each thread. |
+** |
+** A single inode can have multiple file descriptors, so each unixFile |
+** structure contains a pointer to an instance of this object and this |
+** object keeps a count of the number of unixFile pointing to it. |
+*/ |
+struct unixInodeInfo { |
+ struct unixFileId fileId; /* The lookup key */ |
+ int nShared; /* Number of SHARED locks held */ |
+ unsigned char eFileLock; /* One of SHARED_LOCK, RESERVED_LOCK etc. */ |
+ unsigned char bProcessLock; /* An exclusive process lock is held */ |
+ int nRef; /* Number of pointers to this structure */ |
+ unixShmNode *pShmNode; /* Shared memory associated with this inode */ |
+ int nLock; /* Number of outstanding file locks */ |
+ UnixUnusedFd *pUnused; /* Unused file descriptors to close */ |
+ unixInodeInfo *pNext; /* List of all unixInodeInfo objects */ |
+ unixInodeInfo *pPrev; /* .... doubly linked */ |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ unsigned long long sharedByte; /* for AFP simulated shared lock */ |
+#endif |
+#if OS_VXWORKS |
+ sem_t *pSem; /* Named POSIX semaphore */ |
+ char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ |
+#endif |
+}; |
+ |
+/* |
+** A lists of all unixInodeInfo objects. |
+*/ |
+static unixInodeInfo *inodeList = 0; |
+ |
+/* |
+** |
+** This function - unixLogErrorAtLine(), is only ever called via the macro |
+** unixLogError(). |
+** |
+** It is invoked after an error occurs in an OS function and errno has been |
+** set. It logs a message using sqlite3_log() containing the current value of |
+** errno and, if possible, the human-readable equivalent from strerror() or |
+** strerror_r(). |
+** |
+** The first argument passed to the macro should be the error code that |
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). |
+** The two subsequent arguments should be the name of the OS function that |
+** failed (e.g. "unlink", "open") and the associated file-system path, |
+** if any. |
+*/ |
+#define unixLogError(a,b,c) unixLogErrorAtLine(a,b,c,__LINE__) |
+static int unixLogErrorAtLine( |
+ int errcode, /* SQLite error code */ |
+ const char *zFunc, /* Name of OS function that failed */ |
+ const char *zPath, /* File path associated with error */ |
+ int iLine /* Source line number where error occurred */ |
+){ |
+ char *zErr; /* Message from strerror() or equivalent */ |
+ int iErrno = errno; /* Saved syscall error number */ |
+ |
+ /* If this is not a threadsafe build (SQLITE_THREADSAFE==0), then use |
+ ** the strerror() function to obtain the human-readable error message |
+ ** equivalent to errno. Otherwise, use strerror_r(). |
+ */ |
+#if SQLITE_THREADSAFE && defined(HAVE_STRERROR_R) |
+ char aErr[80]; |
+ memset(aErr, 0, sizeof(aErr)); |
+ zErr = aErr; |
+ |
+ /* If STRERROR_R_CHAR_P (set by autoconf scripts) or __USE_GNU is defined, |
+ ** assume that the system provides the GNU version of strerror_r() that |
+ ** returns a pointer to a buffer containing the error message. That pointer |
+ ** may point to aErr[], or it may point to some static storage somewhere. |
+ ** Otherwise, assume that the system provides the POSIX version of |
+ ** strerror_r(), which always writes an error message into aErr[]. |
+ ** |
+ ** If the code incorrectly assumes that it is the POSIX version that is |
+ ** available, the error message will often be an empty string. Not a |
+ ** huge problem. Incorrectly concluding that the GNU version is available |
+ ** could lead to a segfault though. |
+ */ |
+#if defined(STRERROR_R_CHAR_P) || defined(__USE_GNU) |
+ zErr = |
+# endif |
+ strerror_r(iErrno, aErr, sizeof(aErr)-1); |
+ |
+#elif SQLITE_THREADSAFE |
+ /* This is a threadsafe build, but strerror_r() is not available. */ |
+ zErr = ""; |
+#else |
+ /* Non-threadsafe build, use strerror(). */ |
+ zErr = strerror(iErrno); |
+#endif |
+ |
+ if( zPath==0 ) zPath = ""; |
+ sqlite3_log(errcode, |
+ "os_unix.c:%d: (%d) %s(%s) - %s", |
+ iLine, iErrno, zFunc, zPath, zErr |
+ ); |
+ |
+ return errcode; |
+} |
+ |
+/* |
+** Close a file descriptor. |
+** |
+** We assume that close() almost always works, since it is only in a |
+** very sick application or on a very sick platform that it might fail. |
+** If it does fail, simply leak the file descriptor, but do log the |
+** error. |
+** |
+** Note that it is not safe to retry close() after EINTR since the |
+** file descriptor might have already been reused by another thread. |
+** So we don't even try to recover from an EINTR. Just log the error |
+** and move on. |
+*/ |
+static void robust_close(unixFile *pFile, int h, int lineno){ |
+ if( osClose(h) ){ |
+ unixLogErrorAtLine(SQLITE_IOERR_CLOSE, "close", |
+ pFile ? pFile->zPath : 0, lineno); |
+ } |
+} |
+ |
+/* |
+** Set the pFile->lastErrno. Do this in a subroutine as that provides |
+** a convenient place to set a breakpoint. |
+*/ |
+static void storeLastErrno(unixFile *pFile, int error){ |
+ pFile->lastErrno = error; |
+} |
+ |
+/* |
+** Close all file descriptors accumuated in the unixInodeInfo->pUnused list. |
+*/ |
+static void closePendingFds(unixFile *pFile){ |
+ unixInodeInfo *pInode = pFile->pInode; |
+ UnixUnusedFd *p; |
+ UnixUnusedFd *pNext; |
+ for(p=pInode->pUnused; p; p=pNext){ |
+ pNext = p->pNext; |
+ robust_close(pFile, p->fd, __LINE__); |
+ sqlite3_free(p); |
+ } |
+ pInode->pUnused = 0; |
+} |
+ |
+/* |
+** Release a unixInodeInfo structure previously allocated by findInodeInfo(). |
+** |
+** The mutex entered using the unixEnterMutex() function must be held |
+** when this function is called. |
+*/ |
+static void releaseInodeInfo(unixFile *pFile){ |
+ unixInodeInfo *pInode = pFile->pInode; |
+ assert( unixMutexHeld() ); |
+ if( ALWAYS(pInode) ){ |
+ pInode->nRef--; |
+ if( pInode->nRef==0 ){ |
+ assert( pInode->pShmNode==0 ); |
+ closePendingFds(pFile); |
+ if( pInode->pPrev ){ |
+ assert( pInode->pPrev->pNext==pInode ); |
+ pInode->pPrev->pNext = pInode->pNext; |
+ }else{ |
+ assert( inodeList==pInode ); |
+ inodeList = pInode->pNext; |
+ } |
+ if( pInode->pNext ){ |
+ assert( pInode->pNext->pPrev==pInode ); |
+ pInode->pNext->pPrev = pInode->pPrev; |
+ } |
+ sqlite3_free(pInode); |
+ } |
+ } |
+} |
+ |
+/* |
+** Given a file descriptor, locate the unixInodeInfo object that |
+** describes that file descriptor. Create a new one if necessary. The |
+** return value might be uninitialized if an error occurs. |
+** |
+** The mutex entered using the unixEnterMutex() function must be held |
+** when this function is called. |
+** |
+** Return an appropriate error code. |
+*/ |
+static int findInodeInfo( |
+ unixFile *pFile, /* Unix file with file desc used in the key */ |
+ unixInodeInfo **ppInode /* Return the unixInodeInfo object here */ |
+){ |
+ int rc; /* System call return code */ |
+ int fd; /* The file descriptor for pFile */ |
+ struct unixFileId fileId; /* Lookup key for the unixInodeInfo */ |
+ struct stat statbuf; /* Low-level file information */ |
+ unixInodeInfo *pInode = 0; /* Candidate unixInodeInfo object */ |
+ |
+ assert( unixMutexHeld() ); |
+ |
+ /* Get low-level information about the file that we can used to |
+ ** create a unique name for the file. |
+ */ |
+ fd = pFile->h; |
+ rc = osFstat(fd, &statbuf); |
+ if( rc!=0 ){ |
+ storeLastErrno(pFile, errno); |
+#if defined(EOVERFLOW) && defined(SQLITE_DISABLE_LFS) |
+ if( pFile->lastErrno==EOVERFLOW ) return SQLITE_NOLFS; |
+#endif |
+ return SQLITE_IOERR; |
+ } |
+ |
+#ifdef __APPLE__ |
+ /* On OS X on an msdos filesystem, the inode number is reported |
+ ** incorrectly for zero-size files. See ticket #3260. To work |
+ ** around this problem (we consider it a bug in OS X, not SQLite) |
+ ** we always increase the file size to 1 by writing a single byte |
+ ** prior to accessing the inode number. The one byte written is |
+ ** an ASCII 'S' character which also happens to be the first byte |
+ ** in the header of every SQLite database. In this way, if there |
+ ** is a race condition such that another thread has already populated |
+ ** the first page of the database, no damage is done. |
+ */ |
+ if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){ |
+ do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR ); |
+ if( rc!=1 ){ |
+ storeLastErrno(pFile, errno); |
+ return SQLITE_IOERR; |
+ } |
+ rc = osFstat(fd, &statbuf); |
+ if( rc!=0 ){ |
+ storeLastErrno(pFile, errno); |
+ return SQLITE_IOERR; |
+ } |
+ } |
+#endif |
+ |
+ memset(&fileId, 0, sizeof(fileId)); |
+ fileId.dev = statbuf.st_dev; |
+#if OS_VXWORKS |
+ fileId.pId = pFile->pId; |
+#else |
+ fileId.ino = statbuf.st_ino; |
+#endif |
+ pInode = inodeList; |
+ while( pInode && memcmp(&fileId, &pInode->fileId, sizeof(fileId)) ){ |
+ pInode = pInode->pNext; |
+ } |
+ if( pInode==0 ){ |
+ pInode = sqlite3_malloc64( sizeof(*pInode) ); |
+ if( pInode==0 ){ |
+ return SQLITE_NOMEM; |
+ } |
+ memset(pInode, 0, sizeof(*pInode)); |
+ memcpy(&pInode->fileId, &fileId, sizeof(fileId)); |
+ pInode->nRef = 1; |
+ pInode->pNext = inodeList; |
+ pInode->pPrev = 0; |
+ if( inodeList ) inodeList->pPrev = pInode; |
+ inodeList = pInode; |
+ }else{ |
+ pInode->nRef++; |
+ } |
+ *ppInode = pInode; |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Return TRUE if pFile has been renamed or unlinked since it was first opened. |
+*/ |
+static int fileHasMoved(unixFile *pFile){ |
+#if OS_VXWORKS |
+ return pFile->pInode!=0 && pFile->pId!=pFile->pInode->fileId.pId; |
+#else |
+ struct stat buf; |
+ |
+ /* TODO(shess): This check doesn't work when the Chromium's WebDB code is |
+ ** running in the sandbox. |
+ */ |
+ return 0; |
+ |
+ return pFile->pInode!=0 && |
+ (osStat(pFile->zPath, &buf)!=0 || buf.st_ino!=pFile->pInode->fileId.ino); |
+#endif |
+} |
+ |
+ |
+/* |
+** Check a unixFile that is a database. Verify the following: |
+** |
+** (1) There is exactly one hard link on the file |
+** (2) The file is not a symbolic link |
+** (3) The file has not been renamed or unlinked |
+** |
+** Issue sqlite3_log(SQLITE_WARNING,...) messages if anything is not right. |
+*/ |
+static void verifyDbFile(unixFile *pFile){ |
+ struct stat buf; |
+ int rc; |
+ rc = osFstat(pFile->h, &buf); |
+ if( rc!=0 ){ |
+ sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath); |
+ return; |
+ } |
+ if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){ |
+ sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath); |
+ return; |
+ } |
+ if( buf.st_nlink>1 ){ |
+ sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath); |
+ return; |
+ } |
+ if( fileHasMoved(pFile) ){ |
+ sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath); |
+ return; |
+ } |
+} |
+ |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+*/ |
+static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ |
+ int rc = SQLITE_OK; |
+ int reserved = 0; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ |
+ assert( pFile ); |
+ assert( pFile->eFileLock<=SHARED_LOCK ); |
+ unixEnterMutex(); /* Because pFile->pInode is shared across threads */ |
+ |
+ /* Check if a thread in this process holds such a lock */ |
+ if( pFile->pInode->eFileLock>SHARED_LOCK ){ |
+ reserved = 1; |
+ } |
+ |
+ /* Otherwise see if some other process holds it. |
+ */ |
+#ifndef __DJGPP__ |
+ if( !reserved && !pFile->pInode->bProcessLock ){ |
+ struct flock lock; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = RESERVED_BYTE; |
+ lock.l_len = 1; |
+ lock.l_type = F_WRLCK; |
+ if( osFcntl(pFile->h, F_GETLK, &lock) ){ |
+ rc = SQLITE_IOERR_CHECKRESERVEDLOCK; |
+ storeLastErrno(pFile, errno); |
+ } else if( lock.l_type!=F_UNLCK ){ |
+ reserved = 1; |
+ } |
+ } |
+#endif |
+ |
+ unixLeaveMutex(); |
+ OSTRACE(("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved)); |
+ |
+ *pResOut = reserved; |
+ return rc; |
+} |
+ |
+/* |
+** Attempt to set a system-lock on the file pFile. The lock is |
+** described by pLock. |
+** |
+** If the pFile was opened read/write from unix-excl, then the only lock |
+** ever obtained is an exclusive lock, and it is obtained exactly once |
+** the first time any lock is attempted. All subsequent system locking |
+** operations become no-ops. Locking operations still happen internally, |
+** in order to coordinate access between separate database connections |
+** within this process, but all of that is handled in memory and the |
+** operating system does not participate. |
+** |
+** This function is a pass-through to fcntl(F_SETLK) if pFile is using |
+** any VFS other than "unix-excl" or if pFile is opened on "unix-excl" |
+** and is read-only. |
+** |
+** Zero is returned if the call completes successfully, or -1 if a call |
+** to fcntl() fails. In this case, errno is set appropriately (by fcntl()). |
+*/ |
+static int unixFileLock(unixFile *pFile, struct flock *pLock){ |
+ int rc; |
+ unixInodeInfo *pInode = pFile->pInode; |
+ assert( unixMutexHeld() ); |
+ assert( pInode!=0 ); |
+ if( (pFile->ctrlFlags & (UNIXFILE_EXCL|UNIXFILE_RDONLY))==UNIXFILE_EXCL ){ |
+ if( pInode->bProcessLock==0 ){ |
+ struct flock lock; |
+ assert( pInode->nLock==0 ); |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = SHARED_SIZE; |
+ lock.l_type = F_WRLCK; |
+ rc = osFcntl(pFile->h, F_SETLK, &lock); |
+ if( rc<0 ) return rc; |
+ pInode->bProcessLock = 1; |
+ pInode->nLock++; |
+ }else{ |
+ rc = 0; |
+ } |
+ }else{ |
+ rc = osFcntl(pFile->h, F_SETLK, pLock); |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+*/ |
+static int unixLock(sqlite3_file *id, int eFileLock){ |
+ /* The following describes the implementation of the various locks and |
+ ** lock transitions in terms of the POSIX advisory shared and exclusive |
+ ** lock primitives (called read-locks and write-locks below, to avoid |
+ ** confusion with SQLite lock names). The algorithms are complicated |
+ ** slightly in order to be compatible with windows systems simultaneously |
+ ** accessing the same database file, in case that is ever required. |
+ ** |
+ ** Symbols defined in os.h indentify the 'pending byte' and the 'reserved |
+ ** byte', each single bytes at well known offsets, and the 'shared byte |
+ ** range', a range of 510 bytes at a well known offset. |
+ ** |
+ ** To obtain a SHARED lock, a read-lock is obtained on the 'pending |
+ ** byte'. If this is successful, a random byte from the 'shared byte |
+ ** range' is read-locked and the lock on the 'pending byte' released. |
+ ** |
+ ** A process may only obtain a RESERVED lock after it has a SHARED lock. |
+ ** A RESERVED lock is implemented by grabbing a write-lock on the |
+ ** 'reserved byte'. |
+ ** |
+ ** A process may only obtain a PENDING lock after it has obtained a |
+ ** SHARED lock. A PENDING lock is implemented by obtaining a write-lock |
+ ** on the 'pending byte'. This ensures that no new SHARED locks can be |
+ ** obtained, but existing SHARED locks are allowed to persist. A process |
+ ** does not have to obtain a RESERVED lock on the way to a PENDING lock. |
+ ** This property is used by the algorithm for rolling back a journal file |
+ ** after a crash. |
+ ** |
+ ** An EXCLUSIVE lock, obtained after a PENDING lock is held, is |
+ ** implemented by obtaining a write-lock on the entire 'shared byte |
+ ** range'. Since all other locks require a read-lock on one of the bytes |
+ ** within this range, this ensures that no other locks are held on the |
+ ** database. |
+ ** |
+ ** The reason a single byte cannot be used instead of the 'shared byte |
+ ** range' is that some versions of windows do not support read-locks. By |
+ ** locking a random byte from a range, concurrent SHARED locks may exist |
+ ** even if the locking primitive used is always a write-lock. |
+ */ |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile*)id; |
+ unixInodeInfo *pInode; |
+ struct flock lock; |
+ int tErrno = 0; |
+ |
+ assert( pFile ); |
+ OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, |
+ azFileLock(eFileLock), azFileLock(pFile->eFileLock), |
+ azFileLock(pFile->pInode->eFileLock), pFile->pInode->nShared, |
+ osGetpid(0))); |
+ |
+ /* If there is already a lock of this type or more restrictive on the |
+ ** unixFile, do nothing. Don't use the end_lock: exit path, as |
+ ** unixEnterMutex() hasn't been called yet. |
+ */ |
+ if( pFile->eFileLock>=eFileLock ){ |
+ OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, |
+ azFileLock(eFileLock))); |
+ return SQLITE_OK; |
+ } |
+ |
+ /* Make sure the locking sequence is correct. |
+ ** (1) We never move from unlocked to anything higher than shared lock. |
+ ** (2) SQLite never explicitly requests a pendig lock. |
+ ** (3) A shared lock is always held when a reserve lock is requested. |
+ */ |
+ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); |
+ assert( eFileLock!=PENDING_LOCK ); |
+ assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); |
+ |
+ /* This mutex is needed because pFile->pInode is shared across threads |
+ */ |
+ unixEnterMutex(); |
+ pInode = pFile->pInode; |
+ |
+ /* If some thread using this PID has a lock via a different unixFile* |
+ ** handle that precludes the requested lock, return BUSY. |
+ */ |
+ if( (pFile->eFileLock!=pInode->eFileLock && |
+ (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) |
+ ){ |
+ rc = SQLITE_BUSY; |
+ goto end_lock; |
+ } |
+ |
+ /* If a SHARED lock is requested, and some thread using this PID already |
+ ** has a SHARED or RESERVED lock, then increment reference counts and |
+ ** return SQLITE_OK. |
+ */ |
+ if( eFileLock==SHARED_LOCK && |
+ (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ |
+ assert( eFileLock==SHARED_LOCK ); |
+ assert( pFile->eFileLock==0 ); |
+ assert( pInode->nShared>0 ); |
+ pFile->eFileLock = SHARED_LOCK; |
+ pInode->nShared++; |
+ pInode->nLock++; |
+ goto end_lock; |
+ } |
+ |
+ |
+ /* A PENDING lock is needed before acquiring a SHARED lock and before |
+ ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will |
+ ** be released. |
+ */ |
+ lock.l_len = 1L; |
+ lock.l_whence = SEEK_SET; |
+ if( eFileLock==SHARED_LOCK |
+ || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) |
+ ){ |
+ lock.l_type = (eFileLock==SHARED_LOCK?F_RDLCK:F_WRLCK); |
+ lock.l_start = PENDING_BYTE; |
+ if( unixFileLock(pFile, &lock) ){ |
+ tErrno = errno; |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ if( rc!=SQLITE_BUSY ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ goto end_lock; |
+ } |
+ } |
+ |
+ |
+ /* If control gets to this point, then actually go ahead and make |
+ ** operating system calls for the specified lock. |
+ */ |
+ if( eFileLock==SHARED_LOCK ){ |
+ assert( pInode->nShared==0 ); |
+ assert( pInode->eFileLock==0 ); |
+ assert( rc==SQLITE_OK ); |
+ |
+ /* Now get the read-lock */ |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = SHARED_SIZE; |
+ if( unixFileLock(pFile, &lock) ){ |
+ tErrno = errno; |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ } |
+ |
+ /* Drop the temporary PENDING lock */ |
+ lock.l_start = PENDING_BYTE; |
+ lock.l_len = 1L; |
+ lock.l_type = F_UNLCK; |
+ if( unixFileLock(pFile, &lock) && rc==SQLITE_OK ){ |
+ /* This could happen with a network mount */ |
+ tErrno = errno; |
+ rc = SQLITE_IOERR_UNLOCK; |
+ } |
+ |
+ if( rc ){ |
+ if( rc!=SQLITE_BUSY ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ goto end_lock; |
+ }else{ |
+ pFile->eFileLock = SHARED_LOCK; |
+ pInode->nLock++; |
+ pInode->nShared = 1; |
+ } |
+ }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ |
+ /* We are trying for an exclusive lock but another thread in this |
+ ** same process is still holding a shared lock. */ |
+ rc = SQLITE_BUSY; |
+ }else{ |
+ /* The request was for a RESERVED or EXCLUSIVE lock. It is |
+ ** assumed that there is a SHARED or greater lock on the file |
+ ** already. |
+ */ |
+ assert( 0!=pFile->eFileLock ); |
+ lock.l_type = F_WRLCK; |
+ |
+ assert( eFileLock==RESERVED_LOCK || eFileLock==EXCLUSIVE_LOCK ); |
+ if( eFileLock==RESERVED_LOCK ){ |
+ lock.l_start = RESERVED_BYTE; |
+ lock.l_len = 1L; |
+ }else{ |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = SHARED_SIZE; |
+ } |
+ |
+ if( unixFileLock(pFile, &lock) ){ |
+ tErrno = errno; |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ if( rc!=SQLITE_BUSY ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ } |
+ } |
+ |
+ |
+#ifdef SQLITE_DEBUG |
+ /* Set up the transaction-counter change checking flags when |
+ ** transitioning from a SHARED to a RESERVED lock. The change |
+ ** from SHARED to RESERVED marks the beginning of a normal |
+ ** write operation (not a hot journal rollback). |
+ */ |
+ if( rc==SQLITE_OK |
+ && pFile->eFileLock<=SHARED_LOCK |
+ && eFileLock==RESERVED_LOCK |
+ ){ |
+ pFile->transCntrChng = 0; |
+ pFile->dbUpdate = 0; |
+ pFile->inNormalWrite = 1; |
+ } |
+#endif |
+ |
+ |
+ if( rc==SQLITE_OK ){ |
+ pFile->eFileLock = eFileLock; |
+ pInode->eFileLock = eFileLock; |
+ }else if( eFileLock==EXCLUSIVE_LOCK ){ |
+ pFile->eFileLock = PENDING_LOCK; |
+ pInode->eFileLock = PENDING_LOCK; |
+ } |
+ |
+end_lock: |
+ unixLeaveMutex(); |
+ OSTRACE(("LOCK %d %s %s (unix)\n", pFile->h, azFileLock(eFileLock), |
+ rc==SQLITE_OK ? "ok" : "failed")); |
+ return rc; |
+} |
+ |
+/* |
+** Add the file descriptor used by file handle pFile to the corresponding |
+** pUnused list. |
+*/ |
+static void setPendingFd(unixFile *pFile){ |
+ unixInodeInfo *pInode = pFile->pInode; |
+ UnixUnusedFd *p = pFile->pUnused; |
+ p->pNext = pInode->pUnused; |
+ pInode->pUnused = p; |
+ pFile->h = -1; |
+ pFile->pUnused = 0; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+** |
+** If handleNFSUnlock is true, then on downgrading an EXCLUSIVE_LOCK to SHARED |
+** the byte range is divided into 2 parts and the first part is unlocked then |
+** set to a read lock, then the other part is simply unlocked. This works |
+** around a bug in BSD NFS lockd (also seen on MacOSX 10.3+) that fails to |
+** remove the write lock on a region when a read lock is set. |
+*/ |
+static int posixUnlock(sqlite3_file *id, int eFileLock, int handleNFSUnlock){ |
+ unixFile *pFile = (unixFile*)id; |
+ unixInodeInfo *pInode; |
+ struct flock lock; |
+ int rc = SQLITE_OK; |
+ |
+ assert( pFile ); |
+ OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, eFileLock, |
+ pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, |
+ osGetpid(0))); |
+ |
+ assert( eFileLock<=SHARED_LOCK ); |
+ if( pFile->eFileLock<=eFileLock ){ |
+ return SQLITE_OK; |
+ } |
+ unixEnterMutex(); |
+ pInode = pFile->pInode; |
+ assert( pInode->nShared!=0 ); |
+ if( pFile->eFileLock>SHARED_LOCK ){ |
+ assert( pInode->eFileLock==pFile->eFileLock ); |
+ |
+#ifdef SQLITE_DEBUG |
+ /* When reducing a lock such that other processes can start |
+ ** reading the database file again, make sure that the |
+ ** transaction counter was updated if any part of the database |
+ ** file changed. If the transaction counter is not updated, |
+ ** other connections to the same file might not realize that |
+ ** the file has changed and hence might not know to flush their |
+ ** cache. The use of a stale cache can lead to database corruption. |
+ */ |
+ pFile->inNormalWrite = 0; |
+#endif |
+ |
+ /* downgrading to a shared lock on NFS involves clearing the write lock |
+ ** before establishing the readlock - to avoid a race condition we downgrade |
+ ** the lock in 2 blocks, so that part of the range will be covered by a |
+ ** write lock until the rest is covered by a read lock: |
+ ** 1: [WWWWW] |
+ ** 2: [....W] |
+ ** 3: [RRRRW] |
+ ** 4: [RRRR.] |
+ */ |
+ if( eFileLock==SHARED_LOCK ){ |
+#if !defined(__APPLE__) || !SQLITE_ENABLE_LOCKING_STYLE |
+ (void)handleNFSUnlock; |
+ assert( handleNFSUnlock==0 ); |
+#endif |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+ if( handleNFSUnlock ){ |
+ int tErrno; /* Error code from system call errors */ |
+ off_t divSize = SHARED_SIZE - 1; |
+ |
+ lock.l_type = F_UNLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = divSize; |
+ if( unixFileLock(pFile, &lock)==(-1) ){ |
+ tErrno = errno; |
+ rc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, tErrno); |
+ goto end_unlock; |
+ } |
+ lock.l_type = F_RDLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = divSize; |
+ if( unixFileLock(pFile, &lock)==(-1) ){ |
+ tErrno = errno; |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); |
+ if( IS_LOCK_ERROR(rc) ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ goto end_unlock; |
+ } |
+ lock.l_type = F_UNLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = SHARED_FIRST+divSize; |
+ lock.l_len = SHARED_SIZE-divSize; |
+ if( unixFileLock(pFile, &lock)==(-1) ){ |
+ tErrno = errno; |
+ rc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, tErrno); |
+ goto end_unlock; |
+ } |
+ }else |
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ |
+ { |
+ lock.l_type = F_RDLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = SHARED_FIRST; |
+ lock.l_len = SHARED_SIZE; |
+ if( unixFileLock(pFile, &lock) ){ |
+ /* In theory, the call to unixFileLock() cannot fail because another |
+ ** process is holding an incompatible lock. If it does, this |
+ ** indicates that the other process is not following the locking |
+ ** protocol. If this happens, return SQLITE_IOERR_RDLOCK. Returning |
+ ** SQLITE_BUSY would confuse the upper layer (in practice it causes |
+ ** an assert to fail). */ |
+ rc = SQLITE_IOERR_RDLOCK; |
+ storeLastErrno(pFile, errno); |
+ goto end_unlock; |
+ } |
+ } |
+ } |
+ lock.l_type = F_UNLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = PENDING_BYTE; |
+ lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE ); |
+ if( unixFileLock(pFile, &lock)==0 ){ |
+ pInode->eFileLock = SHARED_LOCK; |
+ }else{ |
+ rc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, errno); |
+ goto end_unlock; |
+ } |
+ } |
+ if( eFileLock==NO_LOCK ){ |
+ /* Decrement the shared lock counter. Release the lock using an |
+ ** OS call only when all threads in this same process have released |
+ ** the lock. |
+ */ |
+ pInode->nShared--; |
+ if( pInode->nShared==0 ){ |
+ lock.l_type = F_UNLCK; |
+ lock.l_whence = SEEK_SET; |
+ lock.l_start = lock.l_len = 0L; |
+ if( unixFileLock(pFile, &lock)==0 ){ |
+ pInode->eFileLock = NO_LOCK; |
+ }else{ |
+ rc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, errno); |
+ pInode->eFileLock = NO_LOCK; |
+ pFile->eFileLock = NO_LOCK; |
+ } |
+ } |
+ |
+ /* Decrement the count of locks against this same file. When the |
+ ** count reaches zero, close any other file descriptors whose close |
+ ** was deferred because of outstanding locks. |
+ */ |
+ pInode->nLock--; |
+ assert( pInode->nLock>=0 ); |
+ if( pInode->nLock==0 ){ |
+ closePendingFds(pFile); |
+ } |
+ } |
+ |
+end_unlock: |
+ unixLeaveMutex(); |
+ if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; |
+ return rc; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+*/ |
+static int unixUnlock(sqlite3_file *id, int eFileLock){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 ); |
+#endif |
+ return posixUnlock(id, eFileLock, 0); |
+} |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+static int unixMapfile(unixFile *pFd, i64 nByte); |
+static void unixUnmapfile(unixFile *pFd); |
+#endif |
+ |
+/* |
+** This function performs the parts of the "close file" operation |
+** common to all locking schemes. It closes the directory and file |
+** handles, if they are valid, and sets all fields of the unixFile |
+** structure to 0. |
+** |
+** It is *not* necessary to hold the mutex when this routine is called, |
+** even on VxWorks. A mutex will be acquired on VxWorks by the |
+** vxworksReleaseFileId() routine. |
+*/ |
+static int closeUnixFile(sqlite3_file *id){ |
+ unixFile *pFile = (unixFile*)id; |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ unixUnmapfile(pFile); |
+#endif |
+ if( pFile->h>=0 ){ |
+ robust_close(pFile, pFile->h, __LINE__); |
+ pFile->h = -1; |
+ } |
+#if OS_VXWORKS |
+ if( pFile->pId ){ |
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){ |
+ osUnlink(pFile->pId->zCanonicalName); |
+ } |
+ vxworksReleaseFileId(pFile->pId); |
+ pFile->pId = 0; |
+ } |
+#endif |
+#ifdef SQLITE_UNLINK_AFTER_CLOSE |
+ if( pFile->ctrlFlags & UNIXFILE_DELETE ){ |
+ osUnlink(pFile->zPath); |
+ sqlite3_free(*(char**)&pFile->zPath); |
+ pFile->zPath = 0; |
+ } |
+#endif |
+ OSTRACE(("CLOSE %-3d\n", pFile->h)); |
+ OpenCounter(-1); |
+ sqlite3_free(pFile->pUnused); |
+ memset(pFile, 0, sizeof(unixFile)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Close a file. |
+*/ |
+static int unixClose(sqlite3_file *id){ |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile *)id; |
+ verifyDbFile(pFile); |
+ unixUnlock(id, NO_LOCK); |
+ unixEnterMutex(); |
+ |
+ /* unixFile.pInode is always valid here. Otherwise, a different close |
+ ** routine (e.g. nolockClose()) would be called instead. |
+ */ |
+ assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); |
+ if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ |
+ /* If there are outstanding locks, do not actually close the file just |
+ ** yet because that would clear those locks. Instead, add the file |
+ ** descriptor to pInode->pUnused list. It will be automatically closed |
+ ** when the last lock is cleared. |
+ */ |
+ setPendingFd(pFile); |
+ } |
+ releaseInodeInfo(pFile); |
+ rc = closeUnixFile(id); |
+ unixLeaveMutex(); |
+ return rc; |
+} |
+ |
+/************** End of the posix advisory lock implementation ***************** |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+****************************** No-op Locking ********************************** |
+** |
+** Of the various locking implementations available, this is by far the |
+** simplest: locking is ignored. No attempt is made to lock the database |
+** file for reading or writing. |
+** |
+** This locking mode is appropriate for use on read-only databases |
+** (ex: databases that are burned into CD-ROM, for example.) It can |
+** also be used if the application employs some external mechanism to |
+** prevent simultaneous access of the same database by two or more |
+** database connections. But there is a serious risk of database |
+** corruption if this locking mode is used in situations where multiple |
+** database connections are accessing the same database file at the same |
+** time and one or more of those connections are writing. |
+*/ |
+ |
+static int nolockCheckReservedLock(sqlite3_file *NotUsed, int *pResOut){ |
+ UNUSED_PARAMETER(NotUsed); |
+ *pResOut = 0; |
+ return SQLITE_OK; |
+} |
+static int nolockLock(sqlite3_file *NotUsed, int NotUsed2){ |
+ UNUSED_PARAMETER2(NotUsed, NotUsed2); |
+ return SQLITE_OK; |
+} |
+static int nolockUnlock(sqlite3_file *NotUsed, int NotUsed2){ |
+ UNUSED_PARAMETER2(NotUsed, NotUsed2); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Close the file. |
+*/ |
+static int nolockClose(sqlite3_file *id) { |
+ return closeUnixFile(id); |
+} |
+ |
+/******************* End of the no-op lock implementation ********************* |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+************************* Begin dot-file Locking ****************************** |
+** |
+** The dotfile locking implementation uses the existence of separate lock |
+** files (really a directory) to control access to the database. This works |
+** on just about every filesystem imaginable. But there are serious downsides: |
+** |
+** (1) There is zero concurrency. A single reader blocks all other |
+** connections from reading or writing the database. |
+** |
+** (2) An application crash or power loss can leave stale lock files |
+** sitting around that need to be cleared manually. |
+** |
+** Nevertheless, a dotlock is an appropriate locking mode for use if no |
+** other locking strategy is available. |
+** |
+** Dotfile locking works by creating a subdirectory in the same directory as |
+** the database and with the same name but with a ".lock" extension added. |
+** The existence of a lock directory implies an EXCLUSIVE lock. All other |
+** lock types (SHARED, RESERVED, PENDING) are mapped into EXCLUSIVE. |
+*/ |
+ |
+/* |
+** The file suffix added to the data base filename in order to create the |
+** lock directory. |
+*/ |
+#define DOTLOCK_SUFFIX ".lock" |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+** |
+** In dotfile locking, either a lock exists or it does not. So in this |
+** variation of CheckReservedLock(), *pResOut is set to true if any lock |
+** is held on the file and false if the file is unlocked. |
+*/ |
+static int dotlockCheckReservedLock(sqlite3_file *id, int *pResOut) { |
+ int rc = SQLITE_OK; |
+ int reserved = 0; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ |
+ assert( pFile ); |
+ reserved = osAccess((const char*)pFile->lockingContext, 0)==0; |
+ OSTRACE(("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved)); |
+ *pResOut = reserved; |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+** |
+** With dotfile locking, we really only support state (4): EXCLUSIVE. |
+** But we track the other locking levels internally. |
+*/ |
+static int dotlockLock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ char *zLockFile = (char *)pFile->lockingContext; |
+ int rc = SQLITE_OK; |
+ |
+ |
+ /* If we have any lock, then the lock file already exists. All we have |
+ ** to do is adjust our internal record of the lock level. |
+ */ |
+ if( pFile->eFileLock > NO_LOCK ){ |
+ pFile->eFileLock = eFileLock; |
+ /* Always update the timestamp on the old file */ |
+#ifdef HAVE_UTIME |
+ utime(zLockFile, NULL); |
+#else |
+ utimes(zLockFile, NULL); |
+#endif |
+ return SQLITE_OK; |
+ } |
+ |
+ /* grab an exclusive lock */ |
+ rc = osMkdir(zLockFile, 0777); |
+ if( rc<0 ){ |
+ /* failed to open/create the lock directory */ |
+ int tErrno = errno; |
+ if( EEXIST == tErrno ){ |
+ rc = SQLITE_BUSY; |
+ } else { |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ if( rc!=SQLITE_BUSY ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ } |
+ return rc; |
+ } |
+ |
+ /* got it, set the type and return ok */ |
+ pFile->eFileLock = eFileLock; |
+ return rc; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+** |
+** When the locking level reaches NO_LOCK, delete the lock file. |
+*/ |
+static int dotlockUnlock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ char *zLockFile = (char *)pFile->lockingContext; |
+ int rc; |
+ |
+ assert( pFile ); |
+ OSTRACE(("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, eFileLock, |
+ pFile->eFileLock, osGetpid(0))); |
+ assert( eFileLock<=SHARED_LOCK ); |
+ |
+ /* no-op if possible */ |
+ if( pFile->eFileLock==eFileLock ){ |
+ return SQLITE_OK; |
+ } |
+ |
+ /* To downgrade to shared, simply update our internal notion of the |
+ ** lock state. No need to mess with the file on disk. |
+ */ |
+ if( eFileLock==SHARED_LOCK ){ |
+ pFile->eFileLock = SHARED_LOCK; |
+ return SQLITE_OK; |
+ } |
+ |
+ /* To fully unlock the database, delete the lock file */ |
+ assert( eFileLock==NO_LOCK ); |
+ rc = osRmdir(zLockFile); |
+ if( rc<0 ){ |
+ int tErrno = errno; |
+ if( tErrno==ENOENT ){ |
+ rc = SQLITE_OK; |
+ }else{ |
+ rc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ return rc; |
+ } |
+ pFile->eFileLock = NO_LOCK; |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Close a file. Make sure the lock has been released before closing. |
+*/ |
+static int dotlockClose(sqlite3_file *id) { |
+ unixFile *pFile = (unixFile*)id; |
+ assert( id!=0 ); |
+ dotlockUnlock(id, NO_LOCK); |
+ sqlite3_free(pFile->lockingContext); |
+ return closeUnixFile(id); |
+} |
+/****************** End of the dot-file lock implementation ******************* |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+************************** Begin flock Locking ******************************** |
+** |
+** Use the flock() system call to do file locking. |
+** |
+** flock() locking is like dot-file locking in that the various |
+** fine-grain locking levels supported by SQLite are collapsed into |
+** a single exclusive lock. In other words, SHARED, RESERVED, and |
+** PENDING locks are the same thing as an EXCLUSIVE lock. SQLite |
+** still works when you do this, but concurrency is reduced since |
+** only a single process can be reading the database at a time. |
+** |
+** Omit this section if SQLITE_ENABLE_LOCKING_STYLE is turned off |
+*/ |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ |
+/* |
+** Retry flock() calls that fail with EINTR |
+*/ |
+#ifdef EINTR |
+static int robust_flock(int fd, int op){ |
+ int rc; |
+ do{ rc = flock(fd,op); }while( rc<0 && errno==EINTR ); |
+ return rc; |
+} |
+#else |
+# define robust_flock(a,b) flock(a,b) |
+#endif |
+ |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+*/ |
+static int flockCheckReservedLock(sqlite3_file *id, int *pResOut){ |
+ int rc = SQLITE_OK; |
+ int reserved = 0; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ |
+ assert( pFile ); |
+ |
+ /* Check if a thread in this process holds such a lock */ |
+ if( pFile->eFileLock>SHARED_LOCK ){ |
+ reserved = 1; |
+ } |
+ |
+ /* Otherwise see if some other process holds it. */ |
+ if( !reserved ){ |
+ /* attempt to get the lock */ |
+ int lrc = robust_flock(pFile->h, LOCK_EX | LOCK_NB); |
+ if( !lrc ){ |
+ /* got the lock, unlock it */ |
+ lrc = robust_flock(pFile->h, LOCK_UN); |
+ if ( lrc ) { |
+ int tErrno = errno; |
+ /* unlock failed with an error */ |
+ lrc = SQLITE_IOERR_UNLOCK; |
+ storeLastErrno(pFile, tErrno); |
+ rc = lrc; |
+ } |
+ } else { |
+ int tErrno = errno; |
+ reserved = 1; |
+ /* someone else might have it reserved */ |
+ lrc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ if( IS_LOCK_ERROR(lrc) ){ |
+ storeLastErrno(pFile, tErrno); |
+ rc = lrc; |
+ } |
+ } |
+ } |
+ OSTRACE(("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved)); |
+ |
+#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
+ if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ |
+ rc = SQLITE_OK; |
+ reserved=1; |
+ } |
+#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ |
+ *pResOut = reserved; |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** flock() only really support EXCLUSIVE locks. We track intermediate |
+** lock states in the sqlite3_file structure, but all locks SHARED or |
+** above are really EXCLUSIVE locks and exclude all other processes from |
+** access the file. |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+*/ |
+static int flockLock(sqlite3_file *id, int eFileLock) { |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ assert( pFile ); |
+ |
+ /* if we already have a lock, it is exclusive. |
+ ** Just adjust level and punt on outta here. */ |
+ if (pFile->eFileLock > NO_LOCK) { |
+ pFile->eFileLock = eFileLock; |
+ return SQLITE_OK; |
+ } |
+ |
+ /* grab an exclusive lock */ |
+ |
+ if (robust_flock(pFile->h, LOCK_EX | LOCK_NB)) { |
+ int tErrno = errno; |
+ /* didn't get, must be busy */ |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); |
+ if( IS_LOCK_ERROR(rc) ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ } else { |
+ /* got it, set the type and return ok */ |
+ pFile->eFileLock = eFileLock; |
+ } |
+ OSTRACE(("LOCK %d %s %s (flock)\n", pFile->h, azFileLock(eFileLock), |
+ rc==SQLITE_OK ? "ok" : "failed")); |
+#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
+ if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ |
+ rc = SQLITE_BUSY; |
+ } |
+#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ |
+ return rc; |
+} |
+ |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+*/ |
+static int flockUnlock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ assert( pFile ); |
+ OSTRACE(("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, eFileLock, |
+ pFile->eFileLock, osGetpid(0))); |
+ assert( eFileLock<=SHARED_LOCK ); |
+ |
+ /* no-op if possible */ |
+ if( pFile->eFileLock==eFileLock ){ |
+ return SQLITE_OK; |
+ } |
+ |
+ /* shared can just be set because we always have an exclusive */ |
+ if (eFileLock==SHARED_LOCK) { |
+ pFile->eFileLock = eFileLock; |
+ return SQLITE_OK; |
+ } |
+ |
+ /* no, really, unlock. */ |
+ if( robust_flock(pFile->h, LOCK_UN) ){ |
+#ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS |
+ return SQLITE_OK; |
+#endif /* SQLITE_IGNORE_FLOCK_LOCK_ERRORS */ |
+ return SQLITE_IOERR_UNLOCK; |
+ }else{ |
+ pFile->eFileLock = NO_LOCK; |
+ return SQLITE_OK; |
+ } |
+} |
+ |
+/* |
+** Close a file. |
+*/ |
+static int flockClose(sqlite3_file *id) { |
+ assert( id!=0 ); |
+ flockUnlock(id, NO_LOCK); |
+ return closeUnixFile(id); |
+} |
+ |
+#endif /* SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORK */ |
+ |
+/******************* End of the flock lock implementation ********************* |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+************************ Begin Named Semaphore Locking ************************ |
+** |
+** Named semaphore locking is only supported on VxWorks. |
+** |
+** Semaphore locking is like dot-lock and flock in that it really only |
+** supports EXCLUSIVE locking. Only a single process can read or write |
+** the database file at a time. This reduces potential concurrency, but |
+** makes the lock implementation much easier. |
+*/ |
+#if OS_VXWORKS |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+*/ |
+static int semXCheckReservedLock(sqlite3_file *id, int *pResOut) { |
+ int rc = SQLITE_OK; |
+ int reserved = 0; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ |
+ assert( pFile ); |
+ |
+ /* Check if a thread in this process holds such a lock */ |
+ if( pFile->eFileLock>SHARED_LOCK ){ |
+ reserved = 1; |
+ } |
+ |
+ /* Otherwise see if some other process holds it. */ |
+ if( !reserved ){ |
+ sem_t *pSem = pFile->pInode->pSem; |
+ |
+ if( sem_trywait(pSem)==-1 ){ |
+ int tErrno = errno; |
+ if( EAGAIN != tErrno ){ |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); |
+ storeLastErrno(pFile, tErrno); |
+ } else { |
+ /* someone else has the lock when we are in NO_LOCK */ |
+ reserved = (pFile->eFileLock < SHARED_LOCK); |
+ } |
+ }else{ |
+ /* we could have it if we want it */ |
+ sem_post(pSem); |
+ } |
+ } |
+ OSTRACE(("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved)); |
+ |
+ *pResOut = reserved; |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** Semaphore locks only really support EXCLUSIVE locks. We track intermediate |
+** lock states in the sqlite3_file structure, but all locks SHARED or |
+** above are really EXCLUSIVE locks and exclude all other processes from |
+** access the file. |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+*/ |
+static int semXLock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ sem_t *pSem = pFile->pInode->pSem; |
+ int rc = SQLITE_OK; |
+ |
+ /* if we already have a lock, it is exclusive. |
+ ** Just adjust level and punt on outta here. */ |
+ if (pFile->eFileLock > NO_LOCK) { |
+ pFile->eFileLock = eFileLock; |
+ rc = SQLITE_OK; |
+ goto sem_end_lock; |
+ } |
+ |
+ /* lock semaphore now but bail out when already locked. */ |
+ if( sem_trywait(pSem)==-1 ){ |
+ rc = SQLITE_BUSY; |
+ goto sem_end_lock; |
+ } |
+ |
+ /* got it, set the type and return ok */ |
+ pFile->eFileLock = eFileLock; |
+ |
+ sem_end_lock: |
+ return rc; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+*/ |
+static int semXUnlock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ sem_t *pSem = pFile->pInode->pSem; |
+ |
+ assert( pFile ); |
+ assert( pSem ); |
+ OSTRACE(("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, eFileLock, |
+ pFile->eFileLock, osGetpid(0))); |
+ assert( eFileLock<=SHARED_LOCK ); |
+ |
+ /* no-op if possible */ |
+ if( pFile->eFileLock==eFileLock ){ |
+ return SQLITE_OK; |
+ } |
+ |
+ /* shared can just be set because we always have an exclusive */ |
+ if (eFileLock==SHARED_LOCK) { |
+ pFile->eFileLock = eFileLock; |
+ return SQLITE_OK; |
+ } |
+ |
+ /* no, really unlock. */ |
+ if ( sem_post(pSem)==-1 ) { |
+ int rc, tErrno = errno; |
+ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); |
+ if( IS_LOCK_ERROR(rc) ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ return rc; |
+ } |
+ pFile->eFileLock = NO_LOCK; |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+ ** Close a file. |
+ */ |
+static int semXClose(sqlite3_file *id) { |
+ if( id ){ |
+ unixFile *pFile = (unixFile*)id; |
+ semXUnlock(id, NO_LOCK); |
+ assert( pFile ); |
+ unixEnterMutex(); |
+ releaseInodeInfo(pFile); |
+ unixLeaveMutex(); |
+ closeUnixFile(id); |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+#endif /* OS_VXWORKS */ |
+/* |
+** Named semaphore locking is only available on VxWorks. |
+** |
+*************** End of the named semaphore lock implementation **************** |
+******************************************************************************/ |
+ |
+ |
+/****************************************************************************** |
+*************************** Begin AFP Locking ********************************* |
+** |
+** AFP is the Apple Filing Protocol. AFP is a network filesystem found |
+** on Apple Macintosh computers - both OS9 and OSX. |
+** |
+** Third-party implementations of AFP are available. But this code here |
+** only works on OSX. |
+*/ |
+ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+/* |
+** The afpLockingContext structure contains all afp lock specific state |
+*/ |
+typedef struct afpLockingContext afpLockingContext; |
+struct afpLockingContext { |
+ int reserved; |
+ const char *dbPath; /* Name of the open file */ |
+}; |
+ |
+struct ByteRangeLockPB2 |
+{ |
+ unsigned long long offset; /* offset to first byte to lock */ |
+ unsigned long long length; /* nbr of bytes to lock */ |
+ unsigned long long retRangeStart; /* nbr of 1st byte locked if successful */ |
+ unsigned char unLockFlag; /* 1 = unlock, 0 = lock */ |
+ unsigned char startEndFlag; /* 1=rel to end of fork, 0=rel to start */ |
+ int fd; /* file desc to assoc this lock with */ |
+}; |
+ |
+#define afpfsByteRangeLock2FSCTL _IOWR('z', 23, struct ByteRangeLockPB2) |
+ |
+/* |
+** This is a utility for setting or clearing a bit-range lock on an |
+** AFP filesystem. |
+** |
+** Return SQLITE_OK on success, SQLITE_BUSY on failure. |
+*/ |
+static int afpSetLock( |
+ const char *path, /* Name of the file to be locked or unlocked */ |
+ unixFile *pFile, /* Open file descriptor on path */ |
+ unsigned long long offset, /* First byte to be locked */ |
+ unsigned long long length, /* Number of bytes to lock */ |
+ int setLockFlag /* True to set lock. False to clear lock */ |
+){ |
+ struct ByteRangeLockPB2 pb; |
+ int err; |
+ |
+ pb.unLockFlag = setLockFlag ? 0 : 1; |
+ pb.startEndFlag = 0; |
+ pb.offset = offset; |
+ pb.length = length; |
+ pb.fd = pFile->h; |
+ |
+ OSTRACE(("AFPSETLOCK [%s] for %d%s in range %llx:%llx\n", |
+ (setLockFlag?"ON":"OFF"), pFile->h, (pb.fd==-1?"[testval-1]":""), |
+ offset, length)); |
+ err = fsctl(path, afpfsByteRangeLock2FSCTL, &pb, 0); |
+ if ( err==-1 ) { |
+ int rc; |
+ int tErrno = errno; |
+ OSTRACE(("AFPSETLOCK failed to fsctl() '%s' %d %s\n", |
+ path, tErrno, strerror(tErrno))); |
+#ifdef SQLITE_IGNORE_AFP_LOCK_ERRORS |
+ rc = SQLITE_BUSY; |
+#else |
+ rc = sqliteErrorFromPosixError(tErrno, |
+ setLockFlag ? SQLITE_IOERR_LOCK : SQLITE_IOERR_UNLOCK); |
+#endif /* SQLITE_IGNORE_AFP_LOCK_ERRORS */ |
+ if( IS_LOCK_ERROR(rc) ){ |
+ storeLastErrno(pFile, tErrno); |
+ } |
+ return rc; |
+ } else { |
+ return SQLITE_OK; |
+ } |
+} |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+*/ |
+static int afpCheckReservedLock(sqlite3_file *id, int *pResOut){ |
+ int rc = SQLITE_OK; |
+ int reserved = 0; |
+ unixFile *pFile = (unixFile*)id; |
+ afpLockingContext *context; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ |
+ assert( pFile ); |
+ context = (afpLockingContext *) pFile->lockingContext; |
+ if( context->reserved ){ |
+ *pResOut = 1; |
+ return SQLITE_OK; |
+ } |
+ unixEnterMutex(); /* Because pFile->pInode is shared across threads */ |
+ |
+ /* Check if a thread in this process holds such a lock */ |
+ if( pFile->pInode->eFileLock>SHARED_LOCK ){ |
+ reserved = 1; |
+ } |
+ |
+ /* Otherwise see if some other process holds it. |
+ */ |
+ if( !reserved ){ |
+ /* lock the RESERVED byte */ |
+ int lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); |
+ if( SQLITE_OK==lrc ){ |
+ /* if we succeeded in taking the reserved lock, unlock it to restore |
+ ** the original state */ |
+ lrc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); |
+ } else { |
+ /* if we failed to get the lock then someone else must have it */ |
+ reserved = 1; |
+ } |
+ if( IS_LOCK_ERROR(lrc) ){ |
+ rc=lrc; |
+ } |
+ } |
+ |
+ unixLeaveMutex(); |
+ OSTRACE(("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved)); |
+ |
+ *pResOut = reserved; |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+*/ |
+static int afpLock(sqlite3_file *id, int eFileLock){ |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile*)id; |
+ unixInodeInfo *pInode = pFile->pInode; |
+ afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; |
+ |
+ assert( pFile ); |
+ OSTRACE(("LOCK %d %s was %s(%s,%d) pid=%d (afp)\n", pFile->h, |
+ azFileLock(eFileLock), azFileLock(pFile->eFileLock), |
+ azFileLock(pInode->eFileLock), pInode->nShared , osGetpid(0))); |
+ |
+ /* If there is already a lock of this type or more restrictive on the |
+ ** unixFile, do nothing. Don't use the afp_end_lock: exit path, as |
+ ** unixEnterMutex() hasn't been called yet. |
+ */ |
+ if( pFile->eFileLock>=eFileLock ){ |
+ OSTRACE(("LOCK %d %s ok (already held) (afp)\n", pFile->h, |
+ azFileLock(eFileLock))); |
+ return SQLITE_OK; |
+ } |
+ |
+ /* Make sure the locking sequence is correct |
+ ** (1) We never move from unlocked to anything higher than shared lock. |
+ ** (2) SQLite never explicitly requests a pendig lock. |
+ ** (3) A shared lock is always held when a reserve lock is requested. |
+ */ |
+ assert( pFile->eFileLock!=NO_LOCK || eFileLock==SHARED_LOCK ); |
+ assert( eFileLock!=PENDING_LOCK ); |
+ assert( eFileLock!=RESERVED_LOCK || pFile->eFileLock==SHARED_LOCK ); |
+ |
+ /* This mutex is needed because pFile->pInode is shared across threads |
+ */ |
+ unixEnterMutex(); |
+ pInode = pFile->pInode; |
+ |
+ /* If some thread using this PID has a lock via a different unixFile* |
+ ** handle that precludes the requested lock, return BUSY. |
+ */ |
+ if( (pFile->eFileLock!=pInode->eFileLock && |
+ (pInode->eFileLock>=PENDING_LOCK || eFileLock>SHARED_LOCK)) |
+ ){ |
+ rc = SQLITE_BUSY; |
+ goto afp_end_lock; |
+ } |
+ |
+ /* If a SHARED lock is requested, and some thread using this PID already |
+ ** has a SHARED or RESERVED lock, then increment reference counts and |
+ ** return SQLITE_OK. |
+ */ |
+ if( eFileLock==SHARED_LOCK && |
+ (pInode->eFileLock==SHARED_LOCK || pInode->eFileLock==RESERVED_LOCK) ){ |
+ assert( eFileLock==SHARED_LOCK ); |
+ assert( pFile->eFileLock==0 ); |
+ assert( pInode->nShared>0 ); |
+ pFile->eFileLock = SHARED_LOCK; |
+ pInode->nShared++; |
+ pInode->nLock++; |
+ goto afp_end_lock; |
+ } |
+ |
+ /* A PENDING lock is needed before acquiring a SHARED lock and before |
+ ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will |
+ ** be released. |
+ */ |
+ if( eFileLock==SHARED_LOCK |
+ || (eFileLock==EXCLUSIVE_LOCK && pFile->eFileLock<PENDING_LOCK) |
+ ){ |
+ int failed; |
+ failed = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 1); |
+ if (failed) { |
+ rc = failed; |
+ goto afp_end_lock; |
+ } |
+ } |
+ |
+ /* If control gets to this point, then actually go ahead and make |
+ ** operating system calls for the specified lock. |
+ */ |
+ if( eFileLock==SHARED_LOCK ){ |
+ int lrc1, lrc2, lrc1Errno = 0; |
+ long lk, mask; |
+ |
+ assert( pInode->nShared==0 ); |
+ assert( pInode->eFileLock==0 ); |
+ |
+ mask = (sizeof(long)==8) ? LARGEST_INT64 : 0x7fffffff; |
+ /* Now get the read-lock SHARED_LOCK */ |
+ /* note that the quality of the randomness doesn't matter that much */ |
+ lk = random(); |
+ pInode->sharedByte = (lk & mask)%(SHARED_SIZE - 1); |
+ lrc1 = afpSetLock(context->dbPath, pFile, |
+ SHARED_FIRST+pInode->sharedByte, 1, 1); |
+ if( IS_LOCK_ERROR(lrc1) ){ |
+ lrc1Errno = pFile->lastErrno; |
+ } |
+ /* Drop the temporary PENDING lock */ |
+ lrc2 = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); |
+ |
+ if( IS_LOCK_ERROR(lrc1) ) { |
+ storeLastErrno(pFile, lrc1Errno); |
+ rc = lrc1; |
+ goto afp_end_lock; |
+ } else if( IS_LOCK_ERROR(lrc2) ){ |
+ rc = lrc2; |
+ goto afp_end_lock; |
+ } else if( lrc1 != SQLITE_OK ) { |
+ rc = lrc1; |
+ } else { |
+ pFile->eFileLock = SHARED_LOCK; |
+ pInode->nLock++; |
+ pInode->nShared = 1; |
+ } |
+ }else if( eFileLock==EXCLUSIVE_LOCK && pInode->nShared>1 ){ |
+ /* We are trying for an exclusive lock but another thread in this |
+ ** same process is still holding a shared lock. */ |
+ rc = SQLITE_BUSY; |
+ }else{ |
+ /* The request was for a RESERVED or EXCLUSIVE lock. It is |
+ ** assumed that there is a SHARED or greater lock on the file |
+ ** already. |
+ */ |
+ int failed = 0; |
+ assert( 0!=pFile->eFileLock ); |
+ if (eFileLock >= RESERVED_LOCK && pFile->eFileLock < RESERVED_LOCK) { |
+ /* Acquire a RESERVED lock */ |
+ failed = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1,1); |
+ if( !failed ){ |
+ context->reserved = 1; |
+ } |
+ } |
+ if (!failed && eFileLock == EXCLUSIVE_LOCK) { |
+ /* Acquire an EXCLUSIVE lock */ |
+ |
+ /* Remove the shared lock before trying the range. we'll need to |
+ ** reestablish the shared lock if we can't get the afpUnlock |
+ */ |
+ if( !(failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST + |
+ pInode->sharedByte, 1, 0)) ){ |
+ int failed2 = SQLITE_OK; |
+ /* now attemmpt to get the exclusive lock range */ |
+ failed = afpSetLock(context->dbPath, pFile, SHARED_FIRST, |
+ SHARED_SIZE, 1); |
+ if( failed && (failed2 = afpSetLock(context->dbPath, pFile, |
+ SHARED_FIRST + pInode->sharedByte, 1, 1)) ){ |
+ /* Can't reestablish the shared lock. Sqlite can't deal, this is |
+ ** a critical I/O error |
+ */ |
+ rc = ((failed & SQLITE_IOERR) == SQLITE_IOERR) ? failed2 : |
+ SQLITE_IOERR_LOCK; |
+ goto afp_end_lock; |
+ } |
+ }else{ |
+ rc = failed; |
+ } |
+ } |
+ if( failed ){ |
+ rc = failed; |
+ } |
+ } |
+ |
+ if( rc==SQLITE_OK ){ |
+ pFile->eFileLock = eFileLock; |
+ pInode->eFileLock = eFileLock; |
+ }else if( eFileLock==EXCLUSIVE_LOCK ){ |
+ pFile->eFileLock = PENDING_LOCK; |
+ pInode->eFileLock = PENDING_LOCK; |
+ } |
+ |
+afp_end_lock: |
+ unixLeaveMutex(); |
+ OSTRACE(("LOCK %d %s %s (afp)\n", pFile->h, azFileLock(eFileLock), |
+ rc==SQLITE_OK ? "ok" : "failed")); |
+ return rc; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+*/ |
+static int afpUnlock(sqlite3_file *id, int eFileLock) { |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile*)id; |
+ unixInodeInfo *pInode; |
+ afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; |
+ int skipShared = 0; |
+#ifdef SQLITE_TEST |
+ int h = pFile->h; |
+#endif |
+ |
+ assert( pFile ); |
+ OSTRACE(("UNLOCK %d %d was %d(%d,%d) pid=%d (afp)\n", pFile->h, eFileLock, |
+ pFile->eFileLock, pFile->pInode->eFileLock, pFile->pInode->nShared, |
+ osGetpid(0))); |
+ |
+ assert( eFileLock<=SHARED_LOCK ); |
+ if( pFile->eFileLock<=eFileLock ){ |
+ return SQLITE_OK; |
+ } |
+ unixEnterMutex(); |
+ pInode = pFile->pInode; |
+ assert( pInode->nShared!=0 ); |
+ if( pFile->eFileLock>SHARED_LOCK ){ |
+ assert( pInode->eFileLock==pFile->eFileLock ); |
+ SimulateIOErrorBenign(1); |
+ SimulateIOError( h=(-1) ) |
+ SimulateIOErrorBenign(0); |
+ |
+#ifdef SQLITE_DEBUG |
+ /* When reducing a lock such that other processes can start |
+ ** reading the database file again, make sure that the |
+ ** transaction counter was updated if any part of the database |
+ ** file changed. If the transaction counter is not updated, |
+ ** other connections to the same file might not realize that |
+ ** the file has changed and hence might not know to flush their |
+ ** cache. The use of a stale cache can lead to database corruption. |
+ */ |
+ assert( pFile->inNormalWrite==0 |
+ || pFile->dbUpdate==0 |
+ || pFile->transCntrChng==1 ); |
+ pFile->inNormalWrite = 0; |
+#endif |
+ |
+ if( pFile->eFileLock==EXCLUSIVE_LOCK ){ |
+ rc = afpSetLock(context->dbPath, pFile, SHARED_FIRST, SHARED_SIZE, 0); |
+ if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1) ){ |
+ /* only re-establish the shared lock if necessary */ |
+ int sharedLockByte = SHARED_FIRST+pInode->sharedByte; |
+ rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 1); |
+ } else { |
+ skipShared = 1; |
+ } |
+ } |
+ if( rc==SQLITE_OK && pFile->eFileLock>=PENDING_LOCK ){ |
+ rc = afpSetLock(context->dbPath, pFile, PENDING_BYTE, 1, 0); |
+ } |
+ if( rc==SQLITE_OK && pFile->eFileLock>=RESERVED_LOCK && context->reserved ){ |
+ rc = afpSetLock(context->dbPath, pFile, RESERVED_BYTE, 1, 0); |
+ if( !rc ){ |
+ context->reserved = 0; |
+ } |
+ } |
+ if( rc==SQLITE_OK && (eFileLock==SHARED_LOCK || pInode->nShared>1)){ |
+ pInode->eFileLock = SHARED_LOCK; |
+ } |
+ } |
+ if( rc==SQLITE_OK && eFileLock==NO_LOCK ){ |
+ |
+ /* Decrement the shared lock counter. Release the lock using an |
+ ** OS call only when all threads in this same process have released |
+ ** the lock. |
+ */ |
+ unsigned long long sharedLockByte = SHARED_FIRST+pInode->sharedByte; |
+ pInode->nShared--; |
+ if( pInode->nShared==0 ){ |
+ SimulateIOErrorBenign(1); |
+ SimulateIOError( h=(-1) ) |
+ SimulateIOErrorBenign(0); |
+ if( !skipShared ){ |
+ rc = afpSetLock(context->dbPath, pFile, sharedLockByte, 1, 0); |
+ } |
+ if( !rc ){ |
+ pInode->eFileLock = NO_LOCK; |
+ pFile->eFileLock = NO_LOCK; |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ pInode->nLock--; |
+ assert( pInode->nLock>=0 ); |
+ if( pInode->nLock==0 ){ |
+ closePendingFds(pFile); |
+ } |
+ } |
+ } |
+ |
+ unixLeaveMutex(); |
+ if( rc==SQLITE_OK ) pFile->eFileLock = eFileLock; |
+ return rc; |
+} |
+ |
+/* |
+** Close a file & cleanup AFP specific locking context |
+*/ |
+static int afpClose(sqlite3_file *id) { |
+ int rc = SQLITE_OK; |
+ unixFile *pFile = (unixFile*)id; |
+ assert( id!=0 ); |
+ afpUnlock(id, NO_LOCK); |
+ unixEnterMutex(); |
+ if( pFile->pInode && pFile->pInode->nLock ){ |
+ /* If there are outstanding locks, do not actually close the file just |
+ ** yet because that would clear those locks. Instead, add the file |
+ ** descriptor to pInode->aPending. It will be automatically closed when |
+ ** the last lock is cleared. |
+ */ |
+ setPendingFd(pFile); |
+ } |
+ releaseInodeInfo(pFile); |
+ sqlite3_free(pFile->lockingContext); |
+ rc = closeUnixFile(id); |
+ unixLeaveMutex(); |
+ return rc; |
+} |
+ |
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ |
+/* |
+** The code above is the AFP lock implementation. The code is specific |
+** to MacOSX and does not work on other unix platforms. No alternative |
+** is available. If you don't compile for a mac, then the "unix-afp" |
+** VFS is not available. |
+** |
+********************* End of the AFP lock implementation ********************** |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+*************************** Begin NFS Locking ********************************/ |
+ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+/* |
+ ** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+ ** must be either NO_LOCK or SHARED_LOCK. |
+ ** |
+ ** If the locking level of the file descriptor is already at or below |
+ ** the requested locking level, this routine is a no-op. |
+ */ |
+static int nfsUnlock(sqlite3_file *id, int eFileLock){ |
+ return posixUnlock(id, eFileLock, 1); |
+} |
+ |
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ |
+/* |
+** The code above is the NFS lock implementation. The code is specific |
+** to MacOSX and does not work on other unix platforms. No alternative |
+** is available. |
+** |
+********************* End of the NFS lock implementation ********************** |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+**************** Non-locking sqlite3_file methods ***************************** |
+** |
+** The next division contains implementations for all methods of the |
+** sqlite3_file object other than the locking methods. The locking |
+** methods were defined in divisions above (one locking method per |
+** division). Those methods that are common to all locking modes |
+** are gather together into this division. |
+*/ |
+ |
+/* |
+** Seek to the offset passed as the second argument, then read cnt |
+** bytes into pBuf. Return the number of bytes actually read. |
+** |
+** NB: If you define USE_PREAD or USE_PREAD64, then it might also |
+** be necessary to define _XOPEN_SOURCE to be 500. This varies from |
+** one system to another. Since SQLite does not define USE_PREAD |
+** in any form by default, we will not attempt to define _XOPEN_SOURCE. |
+** See tickets #2741 and #2681. |
+** |
+** To avoid stomping the errno value on a failed read the lastErrno value |
+** is set before returning. |
+*/ |
+static int seekAndRead(unixFile *id, sqlite3_int64 offset, void *pBuf, int cnt){ |
+ int got; |
+ int prior = 0; |
+#if (!defined(USE_PREAD) && !defined(USE_PREAD64)) |
+ i64 newOffset; |
+#endif |
+ TIMER_START; |
+ assert( cnt==(cnt&0x1ffff) ); |
+ assert( id->h>2 ); |
+ do{ |
+#if defined(USE_PREAD) |
+ got = osPread(id->h, pBuf, cnt, offset); |
+ SimulateIOError( got = -1 ); |
+#elif defined(USE_PREAD64) |
+ got = osPread64(id->h, pBuf, cnt, offset); |
+ SimulateIOError( got = -1 ); |
+#else |
+ newOffset = lseek(id->h, offset, SEEK_SET); |
+ SimulateIOError( newOffset = -1 ); |
+ if( newOffset<0 ){ |
+ storeLastErrno((unixFile*)id, errno); |
+ return -1; |
+ } |
+ got = osRead(id->h, pBuf, cnt); |
+#endif |
+ if( got==cnt ) break; |
+ if( got<0 ){ |
+ if( errno==EINTR ){ got = 1; continue; } |
+ prior = 0; |
+ storeLastErrno((unixFile*)id, errno); |
+ break; |
+ }else if( got>0 ){ |
+ cnt -= got; |
+ offset += got; |
+ prior += got; |
+ pBuf = (void*)(got + (char*)pBuf); |
+ } |
+ }while( got>0 ); |
+ TIMER_END; |
+ OSTRACE(("READ %-3d %5d %7lld %llu\n", |
+ id->h, got+prior, offset-prior, TIMER_ELAPSED)); |
+ return got+prior; |
+} |
+ |
+/* |
+** Read data from a file into a buffer. Return SQLITE_OK if all |
+** bytes were read successfully and SQLITE_IOERR if anything goes |
+** wrong. |
+*/ |
+static int unixRead( |
+ sqlite3_file *id, |
+ void *pBuf, |
+ int amt, |
+ sqlite3_int64 offset |
+){ |
+ unixFile *pFile = (unixFile *)id; |
+ int got; |
+ assert( id ); |
+ assert( offset>=0 ); |
+ assert( amt>0 ); |
+ |
+ /* If this is a database file (not a journal, master-journal or temp |
+ ** file), the bytes in the locking range should never be read or written. */ |
+#if 0 |
+ assert( pFile->pUnused==0 |
+ || offset>=PENDING_BYTE+512 |
+ || offset+amt<=PENDING_BYTE |
+ ); |
+#endif |
+ |
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 |
+ /* Deal with as much of this read request as possible by transfering |
+ ** data from the memory mapping using memcpy(). */ |
+ if( offset<pFile->mmapSize ){ |
+ if( offset+amt <= pFile->mmapSize ){ |
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); |
+ return SQLITE_OK; |
+ }else{ |
+ int nCopy = pFile->mmapSize - offset; |
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); |
+ pBuf = &((u8 *)pBuf)[nCopy]; |
+ amt -= nCopy; |
+ offset += nCopy; |
+ } |
+ } |
+#endif |
+ |
+ got = seekAndRead(pFile, offset, pBuf, amt); |
+ if( got==amt ){ |
+ return SQLITE_OK; |
+ }else if( got<0 ){ |
+ /* lastErrno set by seekAndRead */ |
+ return SQLITE_IOERR_READ; |
+ }else{ |
+ storeLastErrno(pFile, 0); /* not a system error */ |
+ /* Unread parts of the buffer must be zero-filled */ |
+ memset(&((char*)pBuf)[got], 0, amt-got); |
+ return SQLITE_IOERR_SHORT_READ; |
+ } |
+} |
+ |
+/* |
+** Attempt to seek the file-descriptor passed as the first argument to |
+** absolute offset iOff, then attempt to write nBuf bytes of data from |
+** pBuf to it. If an error occurs, return -1 and set *piErrno. Otherwise, |
+** return the actual number of bytes written (which may be less than |
+** nBuf). |
+*/ |
+static int seekAndWriteFd( |
+ int fd, /* File descriptor to write to */ |
+ i64 iOff, /* File offset to begin writing at */ |
+ const void *pBuf, /* Copy data from this buffer to the file */ |
+ int nBuf, /* Size of buffer pBuf in bytes */ |
+ int *piErrno /* OUT: Error number if error occurs */ |
+){ |
+ int rc = 0; /* Value returned by system call */ |
+ |
+ assert( nBuf==(nBuf&0x1ffff) ); |
+ assert( fd>2 ); |
+ assert( piErrno!=0 ); |
+ nBuf &= 0x1ffff; |
+ TIMER_START; |
+ |
+#if defined(USE_PREAD) |
+ do{ rc = (int)osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR ); |
+#elif defined(USE_PREAD64) |
+ do{ rc = (int)osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR); |
+#else |
+ do{ |
+ i64 iSeek = lseek(fd, iOff, SEEK_SET); |
+ SimulateIOError( iSeek = -1 ); |
+ if( iSeek<0 ){ |
+ rc = -1; |
+ break; |
+ } |
+ rc = osWrite(fd, pBuf, nBuf); |
+ }while( rc<0 && errno==EINTR ); |
+#endif |
+ |
+ TIMER_END; |
+ OSTRACE(("WRITE %-3d %5d %7lld %llu\n", fd, rc, iOff, TIMER_ELAPSED)); |
+ |
+ if( rc<0 ) *piErrno = errno; |
+ return rc; |
+} |
+ |
+ |
+/* |
+** Seek to the offset in id->offset then read cnt bytes into pBuf. |
+** Return the number of bytes actually read. Update the offset. |
+** |
+** To avoid stomping the errno value on a failed write the lastErrno value |
+** is set before returning. |
+*/ |
+static int seekAndWrite(unixFile *id, i64 offset, const void *pBuf, int cnt){ |
+ return seekAndWriteFd(id->h, offset, pBuf, cnt, &id->lastErrno); |
+} |
+ |
+ |
+/* |
+** Write data from a buffer into a file. Return SQLITE_OK on success |
+** or some other error code on failure. |
+*/ |
+static int unixWrite( |
+ sqlite3_file *id, |
+ const void *pBuf, |
+ int amt, |
+ sqlite3_int64 offset |
+){ |
+ unixFile *pFile = (unixFile*)id; |
+ int wrote = 0; |
+ assert( id ); |
+ assert( amt>0 ); |
+ |
+ /* If this is a database file (not a journal, master-journal or temp |
+ ** file), the bytes in the locking range should never be read or written. */ |
+#if 0 |
+ assert( pFile->pUnused==0 |
+ || offset>=PENDING_BYTE+512 |
+ || offset+amt<=PENDING_BYTE |
+ ); |
+#endif |
+ |
+#ifdef SQLITE_DEBUG |
+ /* If we are doing a normal write to a database file (as opposed to |
+ ** doing a hot-journal rollback or a write to some file other than a |
+ ** normal database file) then record the fact that the database |
+ ** has changed. If the transaction counter is modified, record that |
+ ** fact too. |
+ */ |
+ if( pFile->inNormalWrite ){ |
+ pFile->dbUpdate = 1; /* The database has been modified */ |
+ if( offset<=24 && offset+amt>=27 ){ |
+ int rc; |
+ char oldCntr[4]; |
+ SimulateIOErrorBenign(1); |
+ rc = seekAndRead(pFile, 24, oldCntr, 4); |
+ SimulateIOErrorBenign(0); |
+ if( rc!=4 || memcmp(oldCntr, &((char*)pBuf)[24-offset], 4)!=0 ){ |
+ pFile->transCntrChng = 1; /* The transaction counter has changed */ |
+ } |
+ } |
+ } |
+#endif |
+ |
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 |
+ /* Deal with as much of this write request as possible by transfering |
+ ** data from the memory mapping using memcpy(). */ |
+ if( offset<pFile->mmapSize ){ |
+ if( offset+amt <= pFile->mmapSize ){ |
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); |
+ return SQLITE_OK; |
+ }else{ |
+ int nCopy = pFile->mmapSize - offset; |
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); |
+ pBuf = &((u8 *)pBuf)[nCopy]; |
+ amt -= nCopy; |
+ offset += nCopy; |
+ } |
+ } |
+#endif |
+ |
+ while( (wrote = seekAndWrite(pFile, offset, pBuf, amt))<amt && wrote>0 ){ |
+ amt -= wrote; |
+ offset += wrote; |
+ pBuf = &((char*)pBuf)[wrote]; |
+ } |
+ SimulateIOError(( wrote=(-1), amt=1 )); |
+ SimulateDiskfullError(( wrote=0, amt=1 )); |
+ |
+ if( amt>wrote ){ |
+ if( wrote<0 && pFile->lastErrno!=ENOSPC ){ |
+ /* lastErrno set by seekAndWrite */ |
+ return SQLITE_IOERR_WRITE; |
+ }else{ |
+ storeLastErrno(pFile, 0); /* not a system error */ |
+ return SQLITE_FULL; |
+ } |
+ } |
+ |
+ return SQLITE_OK; |
+} |
+ |
+#ifdef SQLITE_TEST |
+/* |
+** Count the number of fullsyncs and normal syncs. This is used to test |
+** that syncs and fullsyncs are occurring at the right times. |
+*/ |
+SQLITE_API int sqlite3_sync_count = 0; |
+SQLITE_API int sqlite3_fullsync_count = 0; |
+#endif |
+ |
+/* |
+** We do not trust systems to provide a working fdatasync(). Some do. |
+** Others do no. To be safe, we will stick with the (slightly slower) |
+** fsync(). If you know that your system does support fdatasync() correctly, |
+** then simply compile with -Dfdatasync=fdatasync or -DHAVE_FDATASYNC |
+*/ |
+#if !defined(fdatasync) && !HAVE_FDATASYNC |
+# define fdatasync fsync |
+#endif |
+ |
+/* |
+** Define HAVE_FULLFSYNC to 0 or 1 depending on whether or not |
+** the F_FULLFSYNC macro is defined. F_FULLFSYNC is currently |
+** only available on Mac OS X. But that could change. |
+*/ |
+#ifdef F_FULLFSYNC |
+# define HAVE_FULLFSYNC 1 |
+#else |
+# define HAVE_FULLFSYNC 0 |
+#endif |
+ |
+ |
+/* |
+** The fsync() system call does not work as advertised on many |
+** unix systems. The following procedure is an attempt to make |
+** it work better. |
+** |
+** The SQLITE_NO_SYNC macro disables all fsync()s. This is useful |
+** for testing when we want to run through the test suite quickly. |
+** You are strongly advised *not* to deploy with SQLITE_NO_SYNC |
+** enabled, however, since with SQLITE_NO_SYNC enabled, an OS crash |
+** or power failure will likely corrupt the database file. |
+** |
+** SQLite sets the dataOnly flag if the size of the file is unchanged. |
+** The idea behind dataOnly is that it should only write the file content |
+** to disk, not the inode. We only set dataOnly if the file size is |
+** unchanged since the file size is part of the inode. However, |
+** Ted Ts'o tells us that fdatasync() will also write the inode if the |
+** file size has changed. The only real difference between fdatasync() |
+** and fsync(), Ted tells us, is that fdatasync() will not flush the |
+** inode if the mtime or owner or other inode attributes have changed. |
+** We only care about the file size, not the other file attributes, so |
+** as far as SQLite is concerned, an fdatasync() is always adequate. |
+** So, we always use fdatasync() if it is available, regardless of |
+** the value of the dataOnly flag. |
+*/ |
+static int full_fsync(int fd, int fullSync, int dataOnly){ |
+ int rc; |
+ |
+ /* The following "ifdef/elif/else/" block has the same structure as |
+ ** the one below. It is replicated here solely to avoid cluttering |
+ ** up the real code with the UNUSED_PARAMETER() macros. |
+ */ |
+#ifdef SQLITE_NO_SYNC |
+ UNUSED_PARAMETER(fd); |
+ UNUSED_PARAMETER(fullSync); |
+ UNUSED_PARAMETER(dataOnly); |
+#elif HAVE_FULLFSYNC |
+ UNUSED_PARAMETER(dataOnly); |
+#else |
+ UNUSED_PARAMETER(fullSync); |
+ UNUSED_PARAMETER(dataOnly); |
+#endif |
+ |
+ /* Record the number of times that we do a normal fsync() and |
+ ** FULLSYNC. This is used during testing to verify that this procedure |
+ ** gets called with the correct arguments. |
+ */ |
+#ifdef SQLITE_TEST |
+ if( fullSync ) sqlite3_fullsync_count++; |
+ sqlite3_sync_count++; |
+#endif |
+ |
+ /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a |
+ ** no-op. But go ahead and call fstat() to validate the file |
+ ** descriptor as we need a method to provoke a failure during |
+ ** coverate testing. |
+ */ |
+#ifdef SQLITE_NO_SYNC |
+ { |
+ struct stat buf; |
+ rc = osFstat(fd, &buf); |
+ } |
+#elif HAVE_FULLFSYNC |
+ if( fullSync ){ |
+ rc = osFcntl(fd, F_FULLFSYNC, 0); |
+ }else{ |
+ rc = 1; |
+ } |
+ /* If the FULLFSYNC failed, fall back to attempting an fsync(). |
+ ** It shouldn't be possible for fullfsync to fail on the local |
+ ** file system (on OSX), so failure indicates that FULLFSYNC |
+ ** isn't supported for this file system. So, attempt an fsync |
+ ** and (for now) ignore the overhead of a superfluous fcntl call. |
+ ** It'd be better to detect fullfsync support once and avoid |
+ ** the fcntl call every time sync is called. |
+ */ |
+ if( rc ) rc = fsync(fd); |
+ |
+#elif defined(__APPLE__) |
+ /* fdatasync() on HFS+ doesn't yet flush the file size if it changed correctly |
+ ** so currently we default to the macro that redefines fdatasync to fsync |
+ */ |
+ rc = fsync(fd); |
+#else |
+ rc = fdatasync(fd); |
+#if OS_VXWORKS |
+ if( rc==-1 && errno==ENOTSUP ){ |
+ rc = fsync(fd); |
+ } |
+#endif /* OS_VXWORKS */ |
+#endif /* ifdef SQLITE_NO_SYNC elif HAVE_FULLFSYNC */ |
+ |
+ if( OS_VXWORKS && rc!= -1 ){ |
+ rc = 0; |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Open a file descriptor to the directory containing file zFilename. |
+** If successful, *pFd is set to the opened file descriptor and |
+** SQLITE_OK is returned. If an error occurs, either SQLITE_NOMEM |
+** or SQLITE_CANTOPEN is returned and *pFd is set to an undefined |
+** value. |
+** |
+** The directory file descriptor is used for only one thing - to |
+** fsync() a directory to make sure file creation and deletion events |
+** are flushed to disk. Such fsyncs are not needed on newer |
+** journaling filesystems, but are required on older filesystems. |
+** |
+** This routine can be overridden using the xSetSysCall interface. |
+** The ability to override this routine was added in support of the |
+** chromium sandbox. Opening a directory is a security risk (we are |
+** told) so making it overrideable allows the chromium sandbox to |
+** replace this routine with a harmless no-op. To make this routine |
+** a no-op, replace it with a stub that returns SQLITE_OK but leaves |
+** *pFd set to a negative number. |
+** |
+** If SQLITE_OK is returned, the caller is responsible for closing |
+** the file descriptor *pFd using close(). |
+*/ |
+static int openDirectory(const char *zFilename, int *pFd){ |
+ int ii; |
+ int fd = -1; |
+ char zDirname[MAX_PATHNAME+1]; |
+ |
+ sqlite3_snprintf(MAX_PATHNAME, zDirname, "%s", zFilename); |
+ for(ii=(int)strlen(zDirname); ii>0 && zDirname[ii]!='/'; ii--); |
+ if( ii>0 ){ |
+ zDirname[ii] = '\0'; |
+ }else{ |
+ if( zDirname[0]!='/' ) zDirname[0] = '.'; |
+ zDirname[1] = 0; |
+ } |
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0); |
+ if( fd>=0 ){ |
+ OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname)); |
+ } |
+ *pFd = fd; |
+ if( fd>=0 ) return SQLITE_OK; |
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "openDirectory", zDirname); |
+} |
+ |
+/* |
+** Make sure all writes to a particular file are committed to disk. |
+** |
+** If dataOnly==0 then both the file itself and its metadata (file |
+** size, access time, etc) are synced. If dataOnly!=0 then only the |
+** file data is synced. |
+** |
+** Under Unix, also make sure that the directory entry for the file |
+** has been created by fsync-ing the directory that contains the file. |
+** If we do not do this and we encounter a power failure, the directory |
+** entry for the journal might not exist after we reboot. The next |
+** SQLite to access the file will not know that the journal exists (because |
+** the directory entry for the journal was never created) and the transaction |
+** will not roll back - possibly leading to database corruption. |
+*/ |
+static int unixSync(sqlite3_file *id, int flags){ |
+ int rc; |
+ unixFile *pFile = (unixFile*)id; |
+ |
+ int isDataOnly = (flags&SQLITE_SYNC_DATAONLY); |
+ int isFullsync = (flags&0x0F)==SQLITE_SYNC_FULL; |
+ |
+ /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ |
+ assert((flags&0x0F)==SQLITE_SYNC_NORMAL |
+ || (flags&0x0F)==SQLITE_SYNC_FULL |
+ ); |
+ |
+ /* Unix cannot, but some systems may return SQLITE_FULL from here. This |
+ ** line is to test that doing so does not cause any problems. |
+ */ |
+ SimulateDiskfullError( return SQLITE_FULL ); |
+ |
+ assert( pFile ); |
+ OSTRACE(("SYNC %-3d\n", pFile->h)); |
+ rc = full_fsync(pFile->h, isFullsync, isDataOnly); |
+ SimulateIOError( rc=1 ); |
+ if( rc ){ |
+ storeLastErrno(pFile, errno); |
+ return unixLogError(SQLITE_IOERR_FSYNC, "full_fsync", pFile->zPath); |
+ } |
+ |
+ /* Also fsync the directory containing the file if the DIRSYNC flag |
+ ** is set. This is a one-time occurrence. Many systems (examples: AIX) |
+ ** are unable to fsync a directory, so ignore errors on the fsync. |
+ */ |
+ if( pFile->ctrlFlags & UNIXFILE_DIRSYNC ){ |
+ int dirfd; |
+ OSTRACE(("DIRSYNC %s (have_fullfsync=%d fullsync=%d)\n", pFile->zPath, |
+ HAVE_FULLFSYNC, isFullsync)); |
+ rc = osOpenDirectory(pFile->zPath, &dirfd); |
+ if( rc==SQLITE_OK ){ |
+ full_fsync(dirfd, 0, 0); |
+ robust_close(pFile, dirfd, __LINE__); |
+ }else{ |
+ assert( rc==SQLITE_CANTOPEN ); |
+ rc = SQLITE_OK; |
+ } |
+ pFile->ctrlFlags &= ~UNIXFILE_DIRSYNC; |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Truncate an open file to a specified size |
+*/ |
+static int unixTruncate(sqlite3_file *id, i64 nByte){ |
+ unixFile *pFile = (unixFile *)id; |
+ int rc; |
+ assert( pFile ); |
+ SimulateIOError( return SQLITE_IOERR_TRUNCATE ); |
+ |
+ /* If the user has configured a chunk-size for this file, truncate the |
+ ** file so that it consists of an integer number of chunks (i.e. the |
+ ** actual file size after the operation may be larger than the requested |
+ ** size). |
+ */ |
+ if( pFile->szChunk>0 ){ |
+ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; |
+ } |
+ |
+ rc = robust_ftruncate(pFile->h, nByte); |
+ if( rc ){ |
+ storeLastErrno(pFile, errno); |
+ return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); |
+ }else{ |
+#ifdef SQLITE_DEBUG |
+ /* If we are doing a normal write to a database file (as opposed to |
+ ** doing a hot-journal rollback or a write to some file other than a |
+ ** normal database file) and we truncate the file to zero length, |
+ ** that effectively updates the change counter. This might happen |
+ ** when restoring a database using the backup API from a zero-length |
+ ** source. |
+ */ |
+ if( pFile->inNormalWrite && nByte==0 ){ |
+ pFile->transCntrChng = 1; |
+ } |
+#endif |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ /* If the file was just truncated to a size smaller than the currently |
+ ** mapped region, reduce the effective mapping size as well. SQLite will |
+ ** use read() and write() to access data beyond this point from now on. |
+ */ |
+ if( nByte<pFile->mmapSize ){ |
+ pFile->mmapSize = nByte; |
+ } |
+#endif |
+ |
+ return SQLITE_OK; |
+ } |
+} |
+ |
+/* |
+** Determine the current size of a file in bytes |
+*/ |
+static int unixFileSize(sqlite3_file *id, i64 *pSize){ |
+ int rc; |
+ struct stat buf; |
+ assert( id ); |
+ rc = osFstat(((unixFile*)id)->h, &buf); |
+ SimulateIOError( rc=1 ); |
+ if( rc!=0 ){ |
+ storeLastErrno((unixFile*)id, errno); |
+ return SQLITE_IOERR_FSTAT; |
+ } |
+ *pSize = buf.st_size; |
+ |
+ /* When opening a zero-size database, the findInodeInfo() procedure |
+ ** writes a single byte into that file in order to work around a bug |
+ ** in the OS-X msdos filesystem. In order to avoid problems with upper |
+ ** layers, we need to report this file size as zero even though it is |
+ ** really 1. Ticket #3260. |
+ */ |
+ if( *pSize==1 ) *pSize = 0; |
+ |
+ |
+ return SQLITE_OK; |
+} |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+/* |
+** Handler for proxy-locking file-control verbs. Defined below in the |
+** proxying locking division. |
+*/ |
+static int proxyFileControl(sqlite3_file*,int,void*); |
+#endif |
+ |
+/* |
+** This function is called to handle the SQLITE_FCNTL_SIZE_HINT |
+** file-control operation. Enlarge the database to nBytes in size |
+** (rounded up to the next chunk-size). If the database is already |
+** nBytes or larger, this routine is a no-op. |
+*/ |
+static int fcntlSizeHint(unixFile *pFile, i64 nByte){ |
+ if( pFile->szChunk>0 ){ |
+ i64 nSize; /* Required file size */ |
+ struct stat buf; /* Used to hold return values of fstat() */ |
+ |
+ if( osFstat(pFile->h, &buf) ){ |
+ return SQLITE_IOERR_FSTAT; |
+ } |
+ |
+ nSize = ((nByte+pFile->szChunk-1) / pFile->szChunk) * pFile->szChunk; |
+ if( nSize>(i64)buf.st_size ){ |
+ |
+#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE |
+ /* The code below is handling the return value of osFallocate() |
+ ** correctly. posix_fallocate() is defined to "returns zero on success, |
+ ** or an error number on failure". See the manpage for details. */ |
+ int err; |
+ do{ |
+ err = osFallocate(pFile->h, buf.st_size, nSize-buf.st_size); |
+ }while( err==EINTR ); |
+ if( err ) return SQLITE_IOERR_WRITE; |
+#else |
+ /* If the OS does not have posix_fallocate(), fake it. Write a |
+ ** single byte to the last byte in each block that falls entirely |
+ ** within the extended region. Then, if required, a single byte |
+ ** at offset (nSize-1), to set the size of the file correctly. |
+ ** This is a similar technique to that used by glibc on systems |
+ ** that do not have a real fallocate() call. |
+ */ |
+ int nBlk = buf.st_blksize; /* File-system block size */ |
+ int nWrite = 0; /* Number of bytes written by seekAndWrite */ |
+ i64 iWrite; /* Next offset to write to */ |
+ |
+ iWrite = (buf.st_size/nBlk)*nBlk + nBlk - 1; |
+ assert( iWrite>=buf.st_size ); |
+ assert( ((iWrite+1)%nBlk)==0 ); |
+ for(/*no-op*/; iWrite<nSize+nBlk-1; iWrite+=nBlk ){ |
+ if( iWrite>=nSize ) iWrite = nSize - 1; |
+ nWrite = seekAndWrite(pFile, iWrite, "", 1); |
+ if( nWrite!=1 ) return SQLITE_IOERR_WRITE; |
+ } |
+#endif |
+ } |
+ } |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ if( pFile->mmapSizeMax>0 && nByte>pFile->mmapSize ){ |
+ int rc; |
+ if( pFile->szChunk<=0 ){ |
+ if( robust_ftruncate(pFile->h, nByte) ){ |
+ storeLastErrno(pFile, errno); |
+ return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath); |
+ } |
+ } |
+ |
+ rc = unixMapfile(pFile, nByte); |
+ return rc; |
+ } |
+#endif |
+ |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** If *pArg is initially negative then this is a query. Set *pArg to |
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. |
+** |
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. |
+*/ |
+static void unixModeBit(unixFile *pFile, unsigned char mask, int *pArg){ |
+ if( *pArg<0 ){ |
+ *pArg = (pFile->ctrlFlags & mask)!=0; |
+ }else if( (*pArg)==0 ){ |
+ pFile->ctrlFlags &= ~mask; |
+ }else{ |
+ pFile->ctrlFlags |= mask; |
+ } |
+} |
+ |
+/* Forward declaration */ |
+static int unixGetTempname(int nBuf, char *zBuf); |
+ |
+/* |
+** Information and control of an open file handle. |
+*/ |
+static int unixFileControl(sqlite3_file *id, int op, void *pArg){ |
+ unixFile *pFile = (unixFile*)id; |
+ switch( op ){ |
+ case SQLITE_FCNTL_LOCKSTATE: { |
+ *(int*)pArg = pFile->eFileLock; |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_LAST_ERRNO: { |
+ *(int*)pArg = pFile->lastErrno; |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_CHUNK_SIZE: { |
+ pFile->szChunk = *(int *)pArg; |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_SIZE_HINT: { |
+ int rc; |
+ SimulateIOErrorBenign(1); |
+ rc = fcntlSizeHint(pFile, *(i64 *)pArg); |
+ SimulateIOErrorBenign(0); |
+ return rc; |
+ } |
+ case SQLITE_FCNTL_PERSIST_WAL: { |
+ unixModeBit(pFile, UNIXFILE_PERSIST_WAL, (int*)pArg); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { |
+ unixModeBit(pFile, UNIXFILE_PSOW, (int*)pArg); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_VFSNAME: { |
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_TEMPFILENAME: { |
+ char *zTFile = sqlite3_malloc64( pFile->pVfs->mxPathname ); |
+ if( zTFile ){ |
+ unixGetTempname(pFile->pVfs->mxPathname, zTFile); |
+ *(char**)pArg = zTFile; |
+ } |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_HAS_MOVED: { |
+ *(int*)pArg = fileHasMoved(pFile); |
+ return SQLITE_OK; |
+ } |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ case SQLITE_FCNTL_MMAP_SIZE: { |
+ i64 newLimit = *(i64*)pArg; |
+ int rc = SQLITE_OK; |
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){ |
+ newLimit = sqlite3GlobalConfig.mxMmap; |
+ } |
+ *(i64*)pArg = pFile->mmapSizeMax; |
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ |
+ pFile->mmapSizeMax = newLimit; |
+ if( pFile->mmapSize>0 ){ |
+ unixUnmapfile(pFile); |
+ rc = unixMapfile(pFile, -1); |
+ } |
+ } |
+ return rc; |
+ } |
+#endif |
+#ifdef SQLITE_DEBUG |
+ /* The pager calls this method to signal that it has done |
+ ** a rollback and that the database is therefore unchanged and |
+ ** it hence it is OK for the transaction change counter to be |
+ ** unchanged. |
+ */ |
+ case SQLITE_FCNTL_DB_UNCHANGED: { |
+ ((unixFile*)id)->dbUpdate = 0; |
+ return SQLITE_OK; |
+ } |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+ case SQLITE_FCNTL_SET_LOCKPROXYFILE: |
+ case SQLITE_FCNTL_GET_LOCKPROXYFILE: { |
+ return proxyFileControl(id,op,pArg); |
+ } |
+#endif /* SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) */ |
+ } |
+ return SQLITE_NOTFOUND; |
+} |
+ |
+/* |
+** Return the sector size in bytes of the underlying block device for |
+** the specified file. This is almost always 512 bytes, but may be |
+** larger for some devices. |
+** |
+** SQLite code assumes this function cannot fail. It also assumes that |
+** if two files are created in the same file-system directory (i.e. |
+** a database and its journal file) that the sector size will be the |
+** same for both. |
+*/ |
+#ifndef __QNXNTO__ |
+static int unixSectorSize(sqlite3_file *NotUsed){ |
+ UNUSED_PARAMETER(NotUsed); |
+ return SQLITE_DEFAULT_SECTOR_SIZE; |
+} |
+#endif |
+ |
+/* |
+** The following version of unixSectorSize() is optimized for QNX. |
+*/ |
+#ifdef __QNXNTO__ |
+#include <sys/dcmd_blk.h> |
+#include <sys/statvfs.h> |
+static int unixSectorSize(sqlite3_file *id){ |
+ unixFile *pFile = (unixFile*)id; |
+ if( pFile->sectorSize == 0 ){ |
+ struct statvfs fsInfo; |
+ |
+ /* Set defaults for non-supported filesystems */ |
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; |
+ pFile->deviceCharacteristics = 0; |
+ if( fstatvfs(pFile->h, &fsInfo) == -1 ) { |
+ return pFile->sectorSize; |
+ } |
+ |
+ if( !strcmp(fsInfo.f_basetype, "tmp") ) { |
+ pFile->sectorSize = fsInfo.f_bsize; |
+ pFile->deviceCharacteristics = |
+ SQLITE_IOCAP_ATOMIC4K | /* All ram filesystem writes are atomic */ |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until |
+ ** the write succeeds */ |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
+ ** so it is ordered */ |
+ 0; |
+ }else if( strstr(fsInfo.f_basetype, "etfs") ){ |
+ pFile->sectorSize = fsInfo.f_bsize; |
+ pFile->deviceCharacteristics = |
+ /* etfs cluster size writes are atomic */ |
+ (pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) | |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until |
+ ** the write succeeds */ |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
+ ** so it is ordered */ |
+ 0; |
+ }else if( !strcmp(fsInfo.f_basetype, "qnx6") ){ |
+ pFile->sectorSize = fsInfo.f_bsize; |
+ pFile->deviceCharacteristics = |
+ SQLITE_IOCAP_ATOMIC | /* All filesystem writes are atomic */ |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until |
+ ** the write succeeds */ |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
+ ** so it is ordered */ |
+ 0; |
+ }else if( !strcmp(fsInfo.f_basetype, "qnx4") ){ |
+ pFile->sectorSize = fsInfo.f_bsize; |
+ pFile->deviceCharacteristics = |
+ /* full bitset of atomics from max sector size and smaller */ |
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
+ ** so it is ordered */ |
+ 0; |
+ }else if( strstr(fsInfo.f_basetype, "dos") ){ |
+ pFile->sectorSize = fsInfo.f_bsize; |
+ pFile->deviceCharacteristics = |
+ /* full bitset of atomics from max sector size and smaller */ |
+ ((pFile->sectorSize / 512 * SQLITE_IOCAP_ATOMIC512) << 1) - 2 | |
+ SQLITE_IOCAP_SEQUENTIAL | /* The ram filesystem has no write behind |
+ ** so it is ordered */ |
+ 0; |
+ }else{ |
+ pFile->deviceCharacteristics = |
+ SQLITE_IOCAP_ATOMIC512 | /* blocks are atomic */ |
+ SQLITE_IOCAP_SAFE_APPEND | /* growing the file does not occur until |
+ ** the write succeeds */ |
+ 0; |
+ } |
+ } |
+ /* Last chance verification. If the sector size isn't a multiple of 512 |
+ ** then it isn't valid.*/ |
+ if( pFile->sectorSize % 512 != 0 ){ |
+ pFile->deviceCharacteristics = 0; |
+ pFile->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE; |
+ } |
+ return pFile->sectorSize; |
+} |
+#endif /* __QNXNTO__ */ |
+ |
+/* |
+** Return the device characteristics for the file. |
+** |
+** This VFS is set up to return SQLITE_IOCAP_POWERSAFE_OVERWRITE by default. |
+** However, that choice is controversial since technically the underlying |
+** file system does not always provide powersafe overwrites. (In other |
+** words, after a power-loss event, parts of the file that were never |
+** written might end up being altered.) However, non-PSOW behavior is very, |
+** very rare. And asserting PSOW makes a large reduction in the amount |
+** of required I/O for journaling, since a lot of padding is eliminated. |
+** Hence, while POWERSAFE_OVERWRITE is on by default, there is a file-control |
+** available to turn it off and URI query parameter available to turn it off. |
+*/ |
+static int unixDeviceCharacteristics(sqlite3_file *id){ |
+ unixFile *p = (unixFile*)id; |
+ int rc = 0; |
+#ifdef __QNXNTO__ |
+ if( p->sectorSize==0 ) unixSectorSize(id); |
+ rc = p->deviceCharacteristics; |
+#endif |
+ if( p->ctrlFlags & UNIXFILE_PSOW ){ |
+ rc |= SQLITE_IOCAP_POWERSAFE_OVERWRITE; |
+ } |
+ return rc; |
+} |
+ |
+#if !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
+ |
+/* |
+** Return the system page size. |
+** |
+** This function should not be called directly by other code in this file. |
+** Instead, it should be called via macro osGetpagesize(). |
+*/ |
+static int unixGetpagesize(void){ |
+#if OS_VXWORKS |
+ return 1024; |
+#elif defined(_BSD_SOURCE) |
+ return getpagesize(); |
+#else |
+ return (int)sysconf(_SC_PAGESIZE); |
+#endif |
+} |
+ |
+#endif /* !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 */ |
+ |
+#ifndef SQLITE_OMIT_WAL |
+ |
+/* |
+** Object used to represent an shared memory buffer. |
+** |
+** When multiple threads all reference the same wal-index, each thread |
+** has its own unixShm object, but they all point to a single instance |
+** of this unixShmNode object. In other words, each wal-index is opened |
+** only once per process. |
+** |
+** Each unixShmNode object is connected to a single unixInodeInfo object. |
+** We could coalesce this object into unixInodeInfo, but that would mean |
+** every open file that does not use shared memory (in other words, most |
+** open files) would have to carry around this extra information. So |
+** the unixInodeInfo object contains a pointer to this unixShmNode object |
+** and the unixShmNode object is created only when needed. |
+** |
+** unixMutexHeld() must be true when creating or destroying |
+** this object or while reading or writing the following fields: |
+** |
+** nRef |
+** |
+** The following fields are read-only after the object is created: |
+** |
+** fid |
+** zFilename |
+** |
+** Either unixShmNode.mutex must be held or unixShmNode.nRef==0 and |
+** unixMutexHeld() is true when reading or writing any other field |
+** in this structure. |
+*/ |
+struct unixShmNode { |
+ unixInodeInfo *pInode; /* unixInodeInfo that owns this SHM node */ |
+ sqlite3_mutex *mutex; /* Mutex to access this object */ |
+ char *zFilename; /* Name of the mmapped file */ |
+ int h; /* Open file descriptor */ |
+ int szRegion; /* Size of shared-memory regions */ |
+ u16 nRegion; /* Size of array apRegion */ |
+ u8 isReadonly; /* True if read-only */ |
+ char **apRegion; /* Array of mapped shared-memory regions */ |
+ int nRef; /* Number of unixShm objects pointing to this */ |
+ unixShm *pFirst; /* All unixShm objects pointing to this */ |
+#ifdef SQLITE_DEBUG |
+ u8 exclMask; /* Mask of exclusive locks held */ |
+ u8 sharedMask; /* Mask of shared locks held */ |
+ u8 nextShmId; /* Next available unixShm.id value */ |
+#endif |
+}; |
+ |
+/* |
+** Structure used internally by this VFS to record the state of an |
+** open shared memory connection. |
+** |
+** The following fields are initialized when this object is created and |
+** are read-only thereafter: |
+** |
+** unixShm.pFile |
+** unixShm.id |
+** |
+** All other fields are read/write. The unixShm.pFile->mutex must be held |
+** while accessing any read/write fields. |
+*/ |
+struct unixShm { |
+ unixShmNode *pShmNode; /* The underlying unixShmNode object */ |
+ unixShm *pNext; /* Next unixShm with the same unixShmNode */ |
+ u8 hasMutex; /* True if holding the unixShmNode mutex */ |
+ u8 id; /* Id of this connection within its unixShmNode */ |
+ u16 sharedMask; /* Mask of shared locks held */ |
+ u16 exclMask; /* Mask of exclusive locks held */ |
+}; |
+ |
+/* |
+** Constants used for locking |
+*/ |
+#define UNIX_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ |
+#define UNIX_SHM_DMS (UNIX_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ |
+ |
+/* |
+** Apply posix advisory locks for all bytes from ofst through ofst+n-1. |
+** |
+** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking |
+** otherwise. |
+*/ |
+static int unixShmSystemLock( |
+ unixFile *pFile, /* Open connection to the WAL file */ |
+ int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ |
+ int ofst, /* First byte of the locking range */ |
+ int n /* Number of bytes to lock */ |
+){ |
+ unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */ |
+ struct flock f; /* The posix advisory locking structure */ |
+ int rc = SQLITE_OK; /* Result code form fcntl() */ |
+ |
+ /* Access to the unixShmNode object is serialized by the caller */ |
+ pShmNode = pFile->pInode->pShmNode; |
+ assert( sqlite3_mutex_held(pShmNode->mutex) || pShmNode->nRef==0 ); |
+ |
+ /* Shared locks never span more than one byte */ |
+ assert( n==1 || lockType!=F_RDLCK ); |
+ |
+ /* Locks are within range */ |
+ assert( n>=1 && n<=SQLITE_SHM_NLOCK ); |
+ |
+ if( pShmNode->h>=0 ){ |
+ /* Initialize the locking parameters */ |
+ memset(&f, 0, sizeof(f)); |
+ f.l_type = lockType; |
+ f.l_whence = SEEK_SET; |
+ f.l_start = ofst; |
+ f.l_len = n; |
+ |
+ rc = osFcntl(pShmNode->h, F_SETLK, &f); |
+ rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; |
+ } |
+ |
+ /* Update the global lock state and do debug tracing */ |
+#ifdef SQLITE_DEBUG |
+ { u16 mask; |
+ OSTRACE(("SHM-LOCK ")); |
+ mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst); |
+ if( rc==SQLITE_OK ){ |
+ if( lockType==F_UNLCK ){ |
+ OSTRACE(("unlock %d ok", ofst)); |
+ pShmNode->exclMask &= ~mask; |
+ pShmNode->sharedMask &= ~mask; |
+ }else if( lockType==F_RDLCK ){ |
+ OSTRACE(("read-lock %d ok", ofst)); |
+ pShmNode->exclMask &= ~mask; |
+ pShmNode->sharedMask |= mask; |
+ }else{ |
+ assert( lockType==F_WRLCK ); |
+ OSTRACE(("write-lock %d ok", ofst)); |
+ pShmNode->exclMask |= mask; |
+ pShmNode->sharedMask &= ~mask; |
+ } |
+ }else{ |
+ if( lockType==F_UNLCK ){ |
+ OSTRACE(("unlock %d failed", ofst)); |
+ }else if( lockType==F_RDLCK ){ |
+ OSTRACE(("read-lock failed")); |
+ }else{ |
+ assert( lockType==F_WRLCK ); |
+ OSTRACE(("write-lock %d failed", ofst)); |
+ } |
+ } |
+ OSTRACE((" - afterwards %03x,%03x\n", |
+ pShmNode->sharedMask, pShmNode->exclMask)); |
+ } |
+#endif |
+ |
+ return rc; |
+} |
+ |
+/* |
+** Return the minimum number of 32KB shm regions that should be mapped at |
+** a time, assuming that each mapping must be an integer multiple of the |
+** current system page-size. |
+** |
+** Usually, this is 1. The exception seems to be systems that are configured |
+** to use 64KB pages - in this case each mapping must cover at least two |
+** shm regions. |
+*/ |
+static int unixShmRegionPerMap(void){ |
+ int shmsz = 32*1024; /* SHM region size */ |
+ int pgsz = osGetpagesize(); /* System page size */ |
+ assert( ((pgsz-1)&pgsz)==0 ); /* Page size must be a power of 2 */ |
+ if( pgsz<shmsz ) return 1; |
+ return pgsz/shmsz; |
+} |
+ |
+/* |
+** Purge the unixShmNodeList list of all entries with unixShmNode.nRef==0. |
+** |
+** This is not a VFS shared-memory method; it is a utility function called |
+** by VFS shared-memory methods. |
+*/ |
+static void unixShmPurge(unixFile *pFd){ |
+ unixShmNode *p = pFd->pInode->pShmNode; |
+ assert( unixMutexHeld() ); |
+ if( p && ALWAYS(p->nRef==0) ){ |
+ int nShmPerMap = unixShmRegionPerMap(); |
+ int i; |
+ assert( p->pInode==pFd->pInode ); |
+ sqlite3_mutex_free(p->mutex); |
+ for(i=0; i<p->nRegion; i+=nShmPerMap){ |
+ if( p->h>=0 ){ |
+ osMunmap(p->apRegion[i], p->szRegion); |
+ }else{ |
+ sqlite3_free(p->apRegion[i]); |
+ } |
+ } |
+ sqlite3_free(p->apRegion); |
+ if( p->h>=0 ){ |
+ robust_close(pFd, p->h, __LINE__); |
+ p->h = -1; |
+ } |
+ p->pInode->pShmNode = 0; |
+ sqlite3_free(p); |
+ } |
+} |
+ |
+/* |
+** Open a shared-memory area associated with open database file pDbFd. |
+** This particular implementation uses mmapped files. |
+** |
+** The file used to implement shared-memory is in the same directory |
+** as the open database file and has the same name as the open database |
+** file with the "-shm" suffix added. For example, if the database file |
+** is "/home/user1/config.db" then the file that is created and mmapped |
+** for shared memory will be called "/home/user1/config.db-shm". |
+** |
+** Another approach to is to use files in /dev/shm or /dev/tmp or an |
+** some other tmpfs mount. But if a file in a different directory |
+** from the database file is used, then differing access permissions |
+** or a chroot() might cause two different processes on the same |
+** database to end up using different files for shared memory - |
+** meaning that their memory would not really be shared - resulting |
+** in database corruption. Nevertheless, this tmpfs file usage |
+** can be enabled at compile-time using -DSQLITE_SHM_DIRECTORY="/dev/shm" |
+** or the equivalent. The use of the SQLITE_SHM_DIRECTORY compile-time |
+** option results in an incompatible build of SQLite; builds of SQLite |
+** that with differing SQLITE_SHM_DIRECTORY settings attempt to use the |
+** same database file at the same time, database corruption will likely |
+** result. The SQLITE_SHM_DIRECTORY compile-time option is considered |
+** "unsupported" and may go away in a future SQLite release. |
+** |
+** When opening a new shared-memory file, if no other instances of that |
+** file are currently open, in this process or in other processes, then |
+** the file must be truncated to zero length or have its header cleared. |
+** |
+** If the original database file (pDbFd) is using the "unix-excl" VFS |
+** that means that an exclusive lock is held on the database file and |
+** that no other processes are able to read or write the database. In |
+** that case, we do not really need shared memory. No shared memory |
+** file is created. The shared memory will be simulated with heap memory. |
+*/ |
+static int unixOpenSharedMemory(unixFile *pDbFd){ |
+ struct unixShm *p = 0; /* The connection to be opened */ |
+ struct unixShmNode *pShmNode; /* The underlying mmapped file */ |
+ int rc; /* Result code */ |
+ unixInodeInfo *pInode; /* The inode of fd */ |
+ char *zShmFilename; /* Name of the file used for SHM */ |
+ int nShmFilename; /* Size of the SHM filename in bytes */ |
+ |
+ /* Allocate space for the new unixShm object. */ |
+ p = sqlite3_malloc64( sizeof(*p) ); |
+ if( p==0 ) return SQLITE_NOMEM; |
+ memset(p, 0, sizeof(*p)); |
+ assert( pDbFd->pShm==0 ); |
+ |
+ /* Check to see if a unixShmNode object already exists. Reuse an existing |
+ ** one if present. Create a new one if necessary. |
+ */ |
+ unixEnterMutex(); |
+ pInode = pDbFd->pInode; |
+ pShmNode = pInode->pShmNode; |
+ if( pShmNode==0 ){ |
+ struct stat sStat; /* fstat() info for database file */ |
+#ifndef SQLITE_SHM_DIRECTORY |
+ const char *zBasePath = pDbFd->zPath; |
+#endif |
+ |
+ /* Call fstat() to figure out the permissions on the database file. If |
+ ** a new *-shm file is created, an attempt will be made to create it |
+ ** with the same permissions. |
+ */ |
+ if( osFstat(pDbFd->h, &sStat) ){ |
+ rc = SQLITE_IOERR_FSTAT; |
+ goto shm_open_err; |
+ } |
+ |
+#ifdef SQLITE_SHM_DIRECTORY |
+ nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31; |
+#else |
+ nShmFilename = 6 + (int)strlen(zBasePath); |
+#endif |
+ pShmNode = sqlite3_malloc64( sizeof(*pShmNode) + nShmFilename ); |
+ if( pShmNode==0 ){ |
+ rc = SQLITE_NOMEM; |
+ goto shm_open_err; |
+ } |
+ memset(pShmNode, 0, sizeof(*pShmNode)+nShmFilename); |
+ zShmFilename = pShmNode->zFilename = (char*)&pShmNode[1]; |
+#ifdef SQLITE_SHM_DIRECTORY |
+ sqlite3_snprintf(nShmFilename, zShmFilename, |
+ SQLITE_SHM_DIRECTORY "/sqlite-shm-%x-%x", |
+ (u32)sStat.st_ino, (u32)sStat.st_dev); |
+#else |
+ sqlite3_snprintf(nShmFilename, zShmFilename, "%s-shm", zBasePath); |
+ sqlite3FileSuffix3(pDbFd->zPath, zShmFilename); |
+#endif |
+ pShmNode->h = -1; |
+ pDbFd->pInode->pShmNode = pShmNode; |
+ pShmNode->pInode = pDbFd->pInode; |
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
+ if( pShmNode->mutex==0 ){ |
+ rc = SQLITE_NOMEM; |
+ goto shm_open_err; |
+ } |
+ |
+ if( pInode->bProcessLock==0 ){ |
+ int openFlags = O_RDWR | O_CREAT; |
+ if( sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ |
+ openFlags = O_RDONLY; |
+ pShmNode->isReadonly = 1; |
+ } |
+ pShmNode->h = robust_open(zShmFilename, openFlags, (sStat.st_mode&0777)); |
+ if( pShmNode->h<0 ){ |
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zShmFilename); |
+ goto shm_open_err; |
+ } |
+ |
+ /* If this process is running as root, make sure that the SHM file |
+ ** is owned by the same user that owns the original database. Otherwise, |
+ ** the original owner will not be able to connect. |
+ */ |
+ robustFchown(pShmNode->h, sStat.st_uid, sStat.st_gid); |
+ |
+ /* Check to see if another process is holding the dead-man switch. |
+ ** If not, truncate the file to zero length. |
+ */ |
+ rc = SQLITE_OK; |
+ if( unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1)==SQLITE_OK ){ |
+ if( robust_ftruncate(pShmNode->h, 0) ){ |
+ rc = unixLogError(SQLITE_IOERR_SHMOPEN, "ftruncate", zShmFilename); |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, UNIX_SHM_DMS, 1); |
+ } |
+ if( rc ) goto shm_open_err; |
+ } |
+ } |
+ |
+ /* Make the new connection a child of the unixShmNode */ |
+ p->pShmNode = pShmNode; |
+#ifdef SQLITE_DEBUG |
+ p->id = pShmNode->nextShmId++; |
+#endif |
+ pShmNode->nRef++; |
+ pDbFd->pShm = p; |
+ unixLeaveMutex(); |
+ |
+ /* The reference count on pShmNode has already been incremented under |
+ ** the cover of the unixEnterMutex() mutex and the pointer from the |
+ ** new (struct unixShm) object to the pShmNode has been set. All that is |
+ ** left to do is to link the new object into the linked list starting |
+ ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex |
+ ** mutex. |
+ */ |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ p->pNext = pShmNode->pFirst; |
+ pShmNode->pFirst = p; |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ return SQLITE_OK; |
+ |
+ /* Jump here on any error */ |
+shm_open_err: |
+ unixShmPurge(pDbFd); /* This call frees pShmNode if required */ |
+ sqlite3_free(p); |
+ unixLeaveMutex(); |
+ return rc; |
+} |
+ |
+/* |
+** This function is called to obtain a pointer to region iRegion of the |
+** shared-memory associated with the database file fd. Shared-memory regions |
+** are numbered starting from zero. Each shared-memory region is szRegion |
+** bytes in size. |
+** |
+** If an error occurs, an error code is returned and *pp is set to NULL. |
+** |
+** Otherwise, if the bExtend parameter is 0 and the requested shared-memory |
+** region has not been allocated (by any client, including one running in a |
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If |
+** bExtend is non-zero and the requested shared-memory region has not yet |
+** been allocated, it is allocated by this function. |
+** |
+** If the shared-memory region has already been allocated or is allocated by |
+** this call as described above, then it is mapped into this processes |
+** address space (if it is not already), *pp is set to point to the mapped |
+** memory and SQLITE_OK returned. |
+*/ |
+static int unixShmMap( |
+ sqlite3_file *fd, /* Handle open on database file */ |
+ int iRegion, /* Region to retrieve */ |
+ int szRegion, /* Size of regions */ |
+ int bExtend, /* True to extend file if necessary */ |
+ void volatile **pp /* OUT: Mapped memory */ |
+){ |
+ unixFile *pDbFd = (unixFile*)fd; |
+ unixShm *p; |
+ unixShmNode *pShmNode; |
+ int rc = SQLITE_OK; |
+ int nShmPerMap = unixShmRegionPerMap(); |
+ int nReqRegion; |
+ |
+ /* If the shared-memory file has not yet been opened, open it now. */ |
+ if( pDbFd->pShm==0 ){ |
+ rc = unixOpenSharedMemory(pDbFd); |
+ if( rc!=SQLITE_OK ) return rc; |
+ } |
+ |
+ p = pDbFd->pShm; |
+ pShmNode = p->pShmNode; |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); |
+ assert( pShmNode->pInode==pDbFd->pInode ); |
+ assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); |
+ assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); |
+ |
+ /* Minimum number of regions required to be mapped. */ |
+ nReqRegion = ((iRegion+nShmPerMap) / nShmPerMap) * nShmPerMap; |
+ |
+ if( pShmNode->nRegion<nReqRegion ){ |
+ char **apNew; /* New apRegion[] array */ |
+ int nByte = nReqRegion*szRegion; /* Minimum required file size */ |
+ struct stat sStat; /* Used by fstat() */ |
+ |
+ pShmNode->szRegion = szRegion; |
+ |
+ if( pShmNode->h>=0 ){ |
+ /* The requested region is not mapped into this processes address space. |
+ ** Check to see if it has been allocated (i.e. if the wal-index file is |
+ ** large enough to contain the requested region). |
+ */ |
+ if( osFstat(pShmNode->h, &sStat) ){ |
+ rc = SQLITE_IOERR_SHMSIZE; |
+ goto shmpage_out; |
+ } |
+ |
+ if( sStat.st_size<nByte ){ |
+ /* The requested memory region does not exist. If bExtend is set to |
+ ** false, exit early. *pp will be set to NULL and SQLITE_OK returned. |
+ */ |
+ if( !bExtend ){ |
+ goto shmpage_out; |
+ } |
+ |
+ /* Alternatively, if bExtend is true, extend the file. Do this by |
+ ** writing a single byte to the end of each (OS) page being |
+ ** allocated or extended. Technically, we need only write to the |
+ ** last page in order to extend the file. But writing to all new |
+ ** pages forces the OS to allocate them immediately, which reduces |
+ ** the chances of SIGBUS while accessing the mapped region later on. |
+ */ |
+ else{ |
+ static const int pgsz = 4096; |
+ int iPg; |
+ |
+ /* Write to the last byte of each newly allocated or extended page */ |
+ assert( (nByte % pgsz)==0 ); |
+ for(iPg=(sStat.st_size/pgsz); iPg<(nByte/pgsz); iPg++){ |
+ int x = 0; |
+ if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, &x)!=1 ){ |
+ const char *zFile = pShmNode->zFilename; |
+ rc = unixLogError(SQLITE_IOERR_SHMSIZE, "write", zFile); |
+ goto shmpage_out; |
+ } |
+ } |
+ } |
+ } |
+ } |
+ |
+ /* Map the requested memory region into this processes address space. */ |
+ apNew = (char **)sqlite3_realloc( |
+ pShmNode->apRegion, nReqRegion*sizeof(char *) |
+ ); |
+ if( !apNew ){ |
+ rc = SQLITE_IOERR_NOMEM; |
+ goto shmpage_out; |
+ } |
+ pShmNode->apRegion = apNew; |
+ while( pShmNode->nRegion<nReqRegion ){ |
+ int nMap = szRegion*nShmPerMap; |
+ int i; |
+ void *pMem; |
+ if( pShmNode->h>=0 ){ |
+ pMem = osMmap(0, nMap, |
+ pShmNode->isReadonly ? PROT_READ : PROT_READ|PROT_WRITE, |
+ MAP_SHARED, pShmNode->h, szRegion*(i64)pShmNode->nRegion |
+ ); |
+ if( pMem==MAP_FAILED ){ |
+ rc = unixLogError(SQLITE_IOERR_SHMMAP, "mmap", pShmNode->zFilename); |
+ goto shmpage_out; |
+ } |
+ }else{ |
+ pMem = sqlite3_malloc64(szRegion); |
+ if( pMem==0 ){ |
+ rc = SQLITE_NOMEM; |
+ goto shmpage_out; |
+ } |
+ memset(pMem, 0, szRegion); |
+ } |
+ |
+ for(i=0; i<nShmPerMap; i++){ |
+ pShmNode->apRegion[pShmNode->nRegion+i] = &((char*)pMem)[szRegion*i]; |
+ } |
+ pShmNode->nRegion += nShmPerMap; |
+ } |
+ } |
+ |
+shmpage_out: |
+ if( pShmNode->nRegion>iRegion ){ |
+ *pp = pShmNode->apRegion[iRegion]; |
+ }else{ |
+ *pp = 0; |
+ } |
+ if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ return rc; |
+} |
+ |
+/* |
+** Change the lock state for a shared-memory segment. |
+** |
+** Note that the relationship between SHAREd and EXCLUSIVE locks is a little |
+** different here than in posix. In xShmLock(), one can go from unlocked |
+** to shared and back or from unlocked to exclusive and back. But one may |
+** not go from shared to exclusive or from exclusive to shared. |
+*/ |
+static int unixShmLock( |
+ sqlite3_file *fd, /* Database file holding the shared memory */ |
+ int ofst, /* First lock to acquire or release */ |
+ int n, /* Number of locks to acquire or release */ |
+ int flags /* What to do with the lock */ |
+){ |
+ unixFile *pDbFd = (unixFile*)fd; /* Connection holding shared memory */ |
+ unixShm *p = pDbFd->pShm; /* The shared memory being locked */ |
+ unixShm *pX; /* For looping over all siblings */ |
+ unixShmNode *pShmNode = p->pShmNode; /* The underlying file iNode */ |
+ int rc = SQLITE_OK; /* Result code */ |
+ u16 mask; /* Mask of locks to take or release */ |
+ |
+ assert( pShmNode==pDbFd->pInode->pShmNode ); |
+ assert( pShmNode->pInode==pDbFd->pInode ); |
+ assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); |
+ assert( n>=1 ); |
+ assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) |
+ || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) |
+ || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
+ || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
+ assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
+ assert( pShmNode->h>=0 || pDbFd->pInode->bProcessLock==1 ); |
+ assert( pShmNode->h<0 || pDbFd->pInode->bProcessLock==0 ); |
+ |
+ mask = (1<<(ofst+n)) - (1<<ofst); |
+ assert( n>1 || mask==(1<<ofst) ); |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ if( flags & SQLITE_SHM_UNLOCK ){ |
+ u16 allMask = 0; /* Mask of locks held by siblings */ |
+ |
+ /* See if any siblings hold this same lock */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( pX==p ) continue; |
+ assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); |
+ allMask |= pX->sharedMask; |
+ } |
+ |
+ /* Unlock the system-level locks */ |
+ if( (mask & allMask)==0 ){ |
+ rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n); |
+ }else{ |
+ rc = SQLITE_OK; |
+ } |
+ |
+ /* Undo the local locks */ |
+ if( rc==SQLITE_OK ){ |
+ p->exclMask &= ~mask; |
+ p->sharedMask &= ~mask; |
+ } |
+ }else if( flags & SQLITE_SHM_SHARED ){ |
+ u16 allShared = 0; /* Union of locks held by connections other than "p" */ |
+ |
+ /* Find out which shared locks are already held by sibling connections. |
+ ** If any sibling already holds an exclusive lock, go ahead and return |
+ ** SQLITE_BUSY. |
+ */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( (pX->exclMask & mask)!=0 ){ |
+ rc = SQLITE_BUSY; |
+ break; |
+ } |
+ allShared |= pX->sharedMask; |
+ } |
+ |
+ /* Get shared locks at the system level, if necessary */ |
+ if( rc==SQLITE_OK ){ |
+ if( (allShared & mask)==0 ){ |
+ rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n); |
+ }else{ |
+ rc = SQLITE_OK; |
+ } |
+ } |
+ |
+ /* Get the local shared locks */ |
+ if( rc==SQLITE_OK ){ |
+ p->sharedMask |= mask; |
+ } |
+ }else{ |
+ /* Make sure no sibling connections hold locks that will block this |
+ ** lock. If any do, return SQLITE_BUSY right away. |
+ */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ |
+ rc = SQLITE_BUSY; |
+ break; |
+ } |
+ } |
+ |
+ /* Get the exclusive locks at the system level. Then if successful |
+ ** also mark the local connection as being locked. |
+ */ |
+ if( rc==SQLITE_OK ){ |
+ rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n); |
+ if( rc==SQLITE_OK ){ |
+ assert( (p->sharedMask & mask)==0 ); |
+ p->exclMask |= mask; |
+ } |
+ } |
+ } |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n", |
+ p->id, osGetpid(0), p->sharedMask, p->exclMask)); |
+ return rc; |
+} |
+ |
+/* |
+** Implement a memory barrier or memory fence on shared memory. |
+** |
+** All loads and stores begun before the barrier must complete before |
+** any load or store begun after the barrier. |
+*/ |
+static void unixShmBarrier( |
+ sqlite3_file *fd /* Database file holding the shared memory */ |
+){ |
+ UNUSED_PARAMETER(fd); |
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ |
+ unixEnterMutex(); /* Also mutex, for redundancy */ |
+ unixLeaveMutex(); |
+} |
+ |
+/* |
+** Close a connection to shared-memory. Delete the underlying |
+** storage if deleteFlag is true. |
+** |
+** If there is no shared memory associated with the connection then this |
+** routine is a harmless no-op. |
+*/ |
+static int unixShmUnmap( |
+ sqlite3_file *fd, /* The underlying database file */ |
+ int deleteFlag /* Delete shared-memory if true */ |
+){ |
+ unixShm *p; /* The connection to be closed */ |
+ unixShmNode *pShmNode; /* The underlying shared-memory file */ |
+ unixShm **pp; /* For looping over sibling connections */ |
+ unixFile *pDbFd; /* The underlying database file */ |
+ |
+ pDbFd = (unixFile*)fd; |
+ p = pDbFd->pShm; |
+ if( p==0 ) return SQLITE_OK; |
+ pShmNode = p->pShmNode; |
+ |
+ assert( pShmNode==pDbFd->pInode->pShmNode ); |
+ assert( pShmNode->pInode==pDbFd->pInode ); |
+ |
+ /* Remove connection p from the set of connections associated |
+ ** with pShmNode */ |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} |
+ *pp = p->pNext; |
+ |
+ /* Free the connection p */ |
+ sqlite3_free(p); |
+ pDbFd->pShm = 0; |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ |
+ /* If pShmNode->nRef has reached 0, then close the underlying |
+ ** shared-memory file, too */ |
+ unixEnterMutex(); |
+ assert( pShmNode->nRef>0 ); |
+ pShmNode->nRef--; |
+ if( pShmNode->nRef==0 ){ |
+ if( deleteFlag && pShmNode->h>=0 ){ |
+ osUnlink(pShmNode->zFilename); |
+ } |
+ unixShmPurge(pDbFd); |
+ } |
+ unixLeaveMutex(); |
+ |
+ return SQLITE_OK; |
+} |
+ |
+ |
+#else |
+# define unixShmMap 0 |
+# define unixShmLock 0 |
+# define unixShmBarrier 0 |
+# define unixShmUnmap 0 |
+#endif /* #ifndef SQLITE_OMIT_WAL */ |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+/* |
+** If it is currently memory mapped, unmap file pFd. |
+*/ |
+static void unixUnmapfile(unixFile *pFd){ |
+ assert( pFd->nFetchOut==0 ); |
+ if( pFd->pMapRegion ){ |
+ osMunmap(pFd->pMapRegion, pFd->mmapSizeActual); |
+ pFd->pMapRegion = 0; |
+ pFd->mmapSize = 0; |
+ pFd->mmapSizeActual = 0; |
+ } |
+} |
+ |
+/* |
+** Attempt to set the size of the memory mapping maintained by file |
+** descriptor pFd to nNew bytes. Any existing mapping is discarded. |
+** |
+** If successful, this function sets the following variables: |
+** |
+** unixFile.pMapRegion |
+** unixFile.mmapSize |
+** unixFile.mmapSizeActual |
+** |
+** If unsuccessful, an error message is logged via sqlite3_log() and |
+** the three variables above are zeroed. In this case SQLite should |
+** continue accessing the database using the xRead() and xWrite() |
+** methods. |
+*/ |
+static void unixRemapfile( |
+ unixFile *pFd, /* File descriptor object */ |
+ i64 nNew /* Required mapping size */ |
+){ |
+ const char *zErr = "mmap"; |
+ int h = pFd->h; /* File descriptor open on db file */ |
+ u8 *pOrig = (u8 *)pFd->pMapRegion; /* Pointer to current file mapping */ |
+ i64 nOrig = pFd->mmapSizeActual; /* Size of pOrig region in bytes */ |
+ u8 *pNew = 0; /* Location of new mapping */ |
+ int flags = PROT_READ; /* Flags to pass to mmap() */ |
+ |
+ assert( pFd->nFetchOut==0 ); |
+ assert( nNew>pFd->mmapSize ); |
+ assert( nNew<=pFd->mmapSizeMax ); |
+ assert( nNew>0 ); |
+ assert( pFd->mmapSizeActual>=pFd->mmapSize ); |
+ assert( MAP_FAILED!=0 ); |
+ |
+#ifdef SQLITE_MMAP_READWRITE |
+ if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE; |
+#endif |
+ |
+ if( pOrig ){ |
+#if HAVE_MREMAP |
+ i64 nReuse = pFd->mmapSize; |
+#else |
+ const int szSyspage = osGetpagesize(); |
+ i64 nReuse = (pFd->mmapSize & ~(szSyspage-1)); |
+#endif |
+ u8 *pReq = &pOrig[nReuse]; |
+ |
+ /* Unmap any pages of the existing mapping that cannot be reused. */ |
+ if( nReuse!=nOrig ){ |
+ osMunmap(pReq, nOrig-nReuse); |
+ } |
+ |
+#if HAVE_MREMAP |
+ pNew = osMremap(pOrig, nReuse, nNew, MREMAP_MAYMOVE); |
+ zErr = "mremap"; |
+#else |
+ pNew = osMmap(pReq, nNew-nReuse, flags, MAP_SHARED, h, nReuse); |
+ if( pNew!=MAP_FAILED ){ |
+ if( pNew!=pReq ){ |
+ osMunmap(pNew, nNew - nReuse); |
+ pNew = 0; |
+ }else{ |
+ pNew = pOrig; |
+ } |
+ } |
+#endif |
+ |
+ /* The attempt to extend the existing mapping failed. Free it. */ |
+ if( pNew==MAP_FAILED || pNew==0 ){ |
+ osMunmap(pOrig, nReuse); |
+ } |
+ } |
+ |
+ /* If pNew is still NULL, try to create an entirely new mapping. */ |
+ if( pNew==0 ){ |
+ pNew = osMmap(0, nNew, flags, MAP_SHARED, h, 0); |
+ } |
+ |
+ if( pNew==MAP_FAILED ){ |
+ pNew = 0; |
+ nNew = 0; |
+ unixLogError(SQLITE_OK, zErr, pFd->zPath); |
+ |
+ /* If the mmap() above failed, assume that all subsequent mmap() calls |
+ ** will probably fail too. Fall back to using xRead/xWrite exclusively |
+ ** in this case. */ |
+ pFd->mmapSizeMax = 0; |
+ } |
+ pFd->pMapRegion = (void *)pNew; |
+ pFd->mmapSize = pFd->mmapSizeActual = nNew; |
+} |
+ |
+/* |
+** Memory map or remap the file opened by file-descriptor pFd (if the file |
+** is already mapped, the existing mapping is replaced by the new). Or, if |
+** there already exists a mapping for this file, and there are still |
+** outstanding xFetch() references to it, this function is a no-op. |
+** |
+** If parameter nByte is non-negative, then it is the requested size of |
+** the mapping to create. Otherwise, if nByte is less than zero, then the |
+** requested size is the size of the file on disk. The actual size of the |
+** created mapping is either the requested size or the value configured |
+** using SQLITE_FCNTL_MMAP_LIMIT, whichever is smaller. |
+** |
+** SQLITE_OK is returned if no error occurs (even if the mapping is not |
+** recreated as a result of outstanding references) or an SQLite error |
+** code otherwise. |
+*/ |
+static int unixMapfile(unixFile *pFd, i64 nMap){ |
+ assert( nMap>=0 || pFd->nFetchOut==0 ); |
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); |
+ if( pFd->nFetchOut>0 ) return SQLITE_OK; |
+ |
+ if( nMap<0 ){ |
+ struct stat statbuf; /* Low-level file information */ |
+ if( osFstat(pFd->h, &statbuf) ){ |
+ return SQLITE_IOERR_FSTAT; |
+ } |
+ nMap = statbuf.st_size; |
+ } |
+ if( nMap>pFd->mmapSizeMax ){ |
+ nMap = pFd->mmapSizeMax; |
+ } |
+ |
+ assert( nMap>0 || (pFd->mmapSize==0 && pFd->pMapRegion==0) ); |
+ if( nMap!=pFd->mmapSize ){ |
+ unixRemapfile(pFd, nMap); |
+ } |
+ |
+ return SQLITE_OK; |
+} |
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */ |
+ |
+/* |
+** If possible, return a pointer to a mapping of file fd starting at offset |
+** iOff. The mapping must be valid for at least nAmt bytes. |
+** |
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. |
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. |
+** Finally, if an error does occur, return an SQLite error code. The final |
+** value of *pp is undefined in this case. |
+** |
+** If this function does return a pointer, the caller must eventually |
+** release the reference by calling unixUnfetch(). |
+*/ |
+static int unixFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ |
+#endif |
+ *pp = 0; |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ if( pFd->mmapSizeMax>0 ){ |
+ if( pFd->pMapRegion==0 ){ |
+ int rc = unixMapfile(pFd, -1); |
+ if( rc!=SQLITE_OK ) return rc; |
+ } |
+ if( pFd->mmapSize >= iOff+nAmt ){ |
+ *pp = &((u8 *)pFd->pMapRegion)[iOff]; |
+ pFd->nFetchOut++; |
+ } |
+ } |
+#endif |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** If the third argument is non-NULL, then this function releases a |
+** reference obtained by an earlier call to unixFetch(). The second |
+** argument passed to this function must be the same as the corresponding |
+** argument that was passed to the unixFetch() invocation. |
+** |
+** Or, if the third argument is NULL, then this function is being called |
+** to inform the VFS layer that, according to POSIX, any existing mapping |
+** may now be invalid and should be unmapped. |
+*/ |
+static int unixUnfetch(sqlite3_file *fd, i64 iOff, void *p){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ unixFile *pFd = (unixFile *)fd; /* The underlying database file */ |
+ UNUSED_PARAMETER(iOff); |
+ |
+ /* If p==0 (unmap the entire file) then there must be no outstanding |
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), |
+ ** then there must be at least one outstanding. */ |
+ assert( (p==0)==(pFd->nFetchOut==0) ); |
+ |
+ /* If p!=0, it must match the iOff value. */ |
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); |
+ |
+ if( p ){ |
+ pFd->nFetchOut--; |
+ }else{ |
+ unixUnmapfile(pFd); |
+ } |
+ |
+ assert( pFd->nFetchOut>=0 ); |
+#else |
+ UNUSED_PARAMETER(fd); |
+ UNUSED_PARAMETER(p); |
+ UNUSED_PARAMETER(iOff); |
+#endif |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Here ends the implementation of all sqlite3_file methods. |
+** |
+********************** End sqlite3_file Methods ******************************* |
+******************************************************************************/ |
+ |
+/* |
+** This division contains definitions of sqlite3_io_methods objects that |
+** implement various file locking strategies. It also contains definitions |
+** of "finder" functions. A finder-function is used to locate the appropriate |
+** sqlite3_io_methods object for a particular database file. The pAppData |
+** field of the sqlite3_vfs VFS objects are initialized to be pointers to |
+** the correct finder-function for that VFS. |
+** |
+** Most finder functions return a pointer to a fixed sqlite3_io_methods |
+** object. The only interesting finder-function is autolockIoFinder, which |
+** looks at the filesystem type and tries to guess the best locking |
+** strategy from that. |
+** |
+** For finder-function F, two objects are created: |
+** |
+** (1) The real finder-function named "FImpt()". |
+** |
+** (2) A constant pointer to this function named just "F". |
+** |
+** |
+** A pointer to the F pointer is used as the pAppData value for VFS |
+** objects. We have to do this instead of letting pAppData point |
+** directly at the finder-function since C90 rules prevent a void* |
+** from be cast into a function pointer. |
+** |
+** |
+** Each instance of this macro generates two objects: |
+** |
+** * A constant sqlite3_io_methods object call METHOD that has locking |
+** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. |
+** |
+** * An I/O method finder function called FINDER that returns a pointer |
+** to the METHOD object in the previous bullet. |
+*/ |
+#define IOMETHODS(FINDER,METHOD,VERSION,CLOSE,LOCK,UNLOCK,CKLOCK,SHMMAP) \ |
+static const sqlite3_io_methods METHOD = { \ |
+ VERSION, /* iVersion */ \ |
+ CLOSE, /* xClose */ \ |
+ unixRead, /* xRead */ \ |
+ unixWrite, /* xWrite */ \ |
+ unixTruncate, /* xTruncate */ \ |
+ unixSync, /* xSync */ \ |
+ unixFileSize, /* xFileSize */ \ |
+ LOCK, /* xLock */ \ |
+ UNLOCK, /* xUnlock */ \ |
+ CKLOCK, /* xCheckReservedLock */ \ |
+ unixFileControl, /* xFileControl */ \ |
+ unixSectorSize, /* xSectorSize */ \ |
+ unixDeviceCharacteristics, /* xDeviceCapabilities */ \ |
+ SHMMAP, /* xShmMap */ \ |
+ unixShmLock, /* xShmLock */ \ |
+ unixShmBarrier, /* xShmBarrier */ \ |
+ unixShmUnmap, /* xShmUnmap */ \ |
+ unixFetch, /* xFetch */ \ |
+ unixUnfetch, /* xUnfetch */ \ |
+}; \ |
+static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ |
+ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ |
+ return &METHOD; \ |
+} \ |
+static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ |
+ = FINDER##Impl; |
+ |
+/* |
+** Here are all of the sqlite3_io_methods objects for each of the |
+** locking strategies. Functions that return pointers to these methods |
+** are also created. |
+*/ |
+IOMETHODS( |
+ posixIoFinder, /* Finder function name */ |
+ posixIoMethods, /* sqlite3_io_methods object name */ |
+ 3, /* shared memory and mmap are enabled */ |
+ unixClose, /* xClose method */ |
+ unixLock, /* xLock method */ |
+ unixUnlock, /* xUnlock method */ |
+ unixCheckReservedLock, /* xCheckReservedLock method */ |
+ unixShmMap /* xShmMap method */ |
+) |
+IOMETHODS( |
+ nolockIoFinder, /* Finder function name */ |
+ nolockIoMethods, /* sqlite3_io_methods object name */ |
+ 3, /* shared memory is disabled */ |
+ nolockClose, /* xClose method */ |
+ nolockLock, /* xLock method */ |
+ nolockUnlock, /* xUnlock method */ |
+ nolockCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+IOMETHODS( |
+ dotlockIoFinder, /* Finder function name */ |
+ dotlockIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ dotlockClose, /* xClose method */ |
+ dotlockLock, /* xLock method */ |
+ dotlockUnlock, /* xUnlock method */ |
+ dotlockCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+IOMETHODS( |
+ flockIoFinder, /* Finder function name */ |
+ flockIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ flockClose, /* xClose method */ |
+ flockLock, /* xLock method */ |
+ flockUnlock, /* xUnlock method */ |
+ flockCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+#endif |
+ |
+#if OS_VXWORKS |
+IOMETHODS( |
+ semIoFinder, /* Finder function name */ |
+ semIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ semXClose, /* xClose method */ |
+ semXLock, /* xLock method */ |
+ semXUnlock, /* xUnlock method */ |
+ semXCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+#endif |
+ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+IOMETHODS( |
+ afpIoFinder, /* Finder function name */ |
+ afpIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ afpClose, /* xClose method */ |
+ afpLock, /* xLock method */ |
+ afpUnlock, /* xUnlock method */ |
+ afpCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+#endif |
+ |
+/* |
+** The proxy locking method is a "super-method" in the sense that it |
+** opens secondary file descriptors for the conch and lock files and |
+** it uses proxy, dot-file, AFP, and flock() locking methods on those |
+** secondary files. For this reason, the division that implements |
+** proxy locking is located much further down in the file. But we need |
+** to go ahead and define the sqlite3_io_methods and finder function |
+** for proxy locking here. So we forward declare the I/O methods. |
+*/ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+static int proxyClose(sqlite3_file*); |
+static int proxyLock(sqlite3_file*, int); |
+static int proxyUnlock(sqlite3_file*, int); |
+static int proxyCheckReservedLock(sqlite3_file*, int*); |
+IOMETHODS( |
+ proxyIoFinder, /* Finder function name */ |
+ proxyIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ proxyClose, /* xClose method */ |
+ proxyLock, /* xLock method */ |
+ proxyUnlock, /* xUnlock method */ |
+ proxyCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+#endif |
+ |
+/* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+IOMETHODS( |
+ nfsIoFinder, /* Finder function name */ |
+ nfsIoMethods, /* sqlite3_io_methods object name */ |
+ 1, /* shared memory is disabled */ |
+ unixClose, /* xClose method */ |
+ unixLock, /* xLock method */ |
+ nfsUnlock, /* xUnlock method */ |
+ unixCheckReservedLock, /* xCheckReservedLock method */ |
+ 0 /* xShmMap method */ |
+) |
+#endif |
+ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+/* |
+** This "finder" function attempts to determine the best locking strategy |
+** for the database file "filePath". It then returns the sqlite3_io_methods |
+** object that implements that strategy. |
+** |
+** This is for MacOSX only. |
+*/ |
+static const sqlite3_io_methods *autolockIoFinderImpl( |
+ const char *filePath, /* name of the database file */ |
+ unixFile *pNew /* open file object for the database file */ |
+){ |
+ static const struct Mapping { |
+ const char *zFilesystem; /* Filesystem type name */ |
+ const sqlite3_io_methods *pMethods; /* Appropriate locking method */ |
+ } aMap[] = { |
+ { "hfs", &posixIoMethods }, |
+ { "ufs", &posixIoMethods }, |
+ { "afpfs", &afpIoMethods }, |
+ { "smbfs", &afpIoMethods }, |
+ { "webdav", &nolockIoMethods }, |
+ { 0, 0 } |
+ }; |
+ int i; |
+ struct statfs fsInfo; |
+ struct flock lockInfo; |
+ |
+ if( !filePath ){ |
+ /* If filePath==NULL that means we are dealing with a transient file |
+ ** that does not need to be locked. */ |
+ return &nolockIoMethods; |
+ } |
+ if( statfs(filePath, &fsInfo) != -1 ){ |
+ if( fsInfo.f_flags & MNT_RDONLY ){ |
+ return &nolockIoMethods; |
+ } |
+ for(i=0; aMap[i].zFilesystem; i++){ |
+ if( strcmp(fsInfo.f_fstypename, aMap[i].zFilesystem)==0 ){ |
+ return aMap[i].pMethods; |
+ } |
+ } |
+ } |
+ |
+ /* Default case. Handles, amongst others, "nfs". |
+ ** Test byte-range lock using fcntl(). If the call succeeds, |
+ ** assume that the file-system supports POSIX style locks. |
+ */ |
+ lockInfo.l_len = 1; |
+ lockInfo.l_start = 0; |
+ lockInfo.l_whence = SEEK_SET; |
+ lockInfo.l_type = F_RDLCK; |
+ if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { |
+ if( strcmp(fsInfo.f_fstypename, "nfs")==0 ){ |
+ return &nfsIoMethods; |
+ } else { |
+ return &posixIoMethods; |
+ } |
+ }else{ |
+ return &dotlockIoMethods; |
+ } |
+} |
+static const sqlite3_io_methods |
+ *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; |
+ |
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ |
+ |
+#if OS_VXWORKS |
+/* |
+** This "finder" function for VxWorks checks to see if posix advisory |
+** locking works. If it does, then that is what is used. If it does not |
+** work, then fallback to named semaphore locking. |
+*/ |
+static const sqlite3_io_methods *vxworksIoFinderImpl( |
+ const char *filePath, /* name of the database file */ |
+ unixFile *pNew /* the open file object */ |
+){ |
+ struct flock lockInfo; |
+ |
+ if( !filePath ){ |
+ /* If filePath==NULL that means we are dealing with a transient file |
+ ** that does not need to be locked. */ |
+ return &nolockIoMethods; |
+ } |
+ |
+ /* Test if fcntl() is supported and use POSIX style locks. |
+ ** Otherwise fall back to the named semaphore method. |
+ */ |
+ lockInfo.l_len = 1; |
+ lockInfo.l_start = 0; |
+ lockInfo.l_whence = SEEK_SET; |
+ lockInfo.l_type = F_RDLCK; |
+ if( osFcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { |
+ return &posixIoMethods; |
+ }else{ |
+ return &semIoMethods; |
+ } |
+} |
+static const sqlite3_io_methods |
+ *(*const vxworksIoFinder)(const char*,unixFile*) = vxworksIoFinderImpl; |
+ |
+#endif /* OS_VXWORKS */ |
+ |
+/* |
+** An abstract type for a pointer to an IO method finder function: |
+*/ |
+typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); |
+ |
+ |
+/**************************************************************************** |
+**************************** sqlite3_vfs methods **************************** |
+** |
+** This division contains the implementation of methods on the |
+** sqlite3_vfs object. |
+*/ |
+ |
+/* |
+** Initialize the contents of the unixFile structure pointed to by pId. |
+*/ |
+static int fillInUnixFile( |
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */ |
+ int h, /* Open file descriptor of file being opened */ |
+ sqlite3_file *pId, /* Write to the unixFile structure here */ |
+ const char *zFilename, /* Name of the file being opened */ |
+ int ctrlFlags /* Zero or more UNIXFILE_* values */ |
+){ |
+ const sqlite3_io_methods *pLockingStyle; |
+ unixFile *pNew = (unixFile *)pId; |
+ int rc = SQLITE_OK; |
+ |
+ assert( pNew->pInode==NULL ); |
+ |
+ /* Usually the path zFilename should not be a relative pathname. The |
+ ** exception is when opening the proxy "conch" file in builds that |
+ ** include the special Apple locking styles. |
+ */ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+ assert( zFilename==0 || zFilename[0]=='/' |
+ || pVfs->pAppData==(void*)&autolockIoFinder ); |
+#else |
+ assert( zFilename==0 || zFilename[0]=='/' ); |
+#endif |
+ |
+ /* No locking occurs in temporary files */ |
+ assert( zFilename!=0 || (ctrlFlags & UNIXFILE_NOLOCK)!=0 ); |
+ |
+ OSTRACE(("OPEN %-3d %s\n", h, zFilename)); |
+ pNew->h = h; |
+ pNew->pVfs = pVfs; |
+ pNew->zPath = zFilename; |
+ pNew->ctrlFlags = (u8)ctrlFlags; |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap; |
+#endif |
+ if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), |
+ "psow", SQLITE_POWERSAFE_OVERWRITE) ){ |
+ pNew->ctrlFlags |= UNIXFILE_PSOW; |
+ } |
+ if( strcmp(pVfs->zName,"unix-excl")==0 ){ |
+ pNew->ctrlFlags |= UNIXFILE_EXCL; |
+ } |
+ |
+#if OS_VXWORKS |
+ pNew->pId = vxworksFindFileId(zFilename); |
+ if( pNew->pId==0 ){ |
+ ctrlFlags |= UNIXFILE_NOLOCK; |
+ rc = SQLITE_NOMEM; |
+ } |
+#endif |
+ |
+ if( ctrlFlags & UNIXFILE_NOLOCK ){ |
+ pLockingStyle = &nolockIoMethods; |
+ }else{ |
+ pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ /* Cache zFilename in the locking context (AFP and dotlock override) for |
+ ** proxyLock activation is possible (remote proxy is based on db name) |
+ ** zFilename remains valid until file is closed, to support */ |
+ pNew->lockingContext = (void*)zFilename; |
+#endif |
+ } |
+ |
+ if( pLockingStyle == &posixIoMethods |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+ || pLockingStyle == &nfsIoMethods |
+#endif |
+ ){ |
+ unixEnterMutex(); |
+ rc = findInodeInfo(pNew, &pNew->pInode); |
+ if( rc!=SQLITE_OK ){ |
+ /* If an error occurred in findInodeInfo(), close the file descriptor |
+ ** immediately, before releasing the mutex. findInodeInfo() may fail |
+ ** in two scenarios: |
+ ** |
+ ** (a) A call to fstat() failed. |
+ ** (b) A malloc failed. |
+ ** |
+ ** Scenario (b) may only occur if the process is holding no other |
+ ** file descriptors open on the same file. If there were other file |
+ ** descriptors on this file, then no malloc would be required by |
+ ** findInodeInfo(). If this is the case, it is quite safe to close |
+ ** handle h - as it is guaranteed that no posix locks will be released |
+ ** by doing so. |
+ ** |
+ ** If scenario (a) caused the error then things are not so safe. The |
+ ** implicit assumption here is that if fstat() fails, things are in |
+ ** such bad shape that dropping a lock or two doesn't matter much. |
+ */ |
+ robust_close(pNew, h, __LINE__); |
+ h = -1; |
+ } |
+ unixLeaveMutex(); |
+ } |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+ else if( pLockingStyle == &afpIoMethods ){ |
+ /* AFP locking uses the file path so it needs to be included in |
+ ** the afpLockingContext. |
+ */ |
+ afpLockingContext *pCtx; |
+ pNew->lockingContext = pCtx = sqlite3_malloc64( sizeof(*pCtx) ); |
+ if( pCtx==0 ){ |
+ rc = SQLITE_NOMEM; |
+ }else{ |
+ /* NB: zFilename exists and remains valid until the file is closed |
+ ** according to requirement F11141. So we do not need to make a |
+ ** copy of the filename. */ |
+ pCtx->dbPath = zFilename; |
+ pCtx->reserved = 0; |
+ srandomdev(); |
+ unixEnterMutex(); |
+ rc = findInodeInfo(pNew, &pNew->pInode); |
+ if( rc!=SQLITE_OK ){ |
+ sqlite3_free(pNew->lockingContext); |
+ robust_close(pNew, h, __LINE__); |
+ h = -1; |
+ } |
+ unixLeaveMutex(); |
+ } |
+ } |
+#endif |
+ |
+ else if( pLockingStyle == &dotlockIoMethods ){ |
+ /* Dotfile locking uses the file path so it needs to be included in |
+ ** the dotlockLockingContext |
+ */ |
+ char *zLockFile; |
+ int nFilename; |
+ assert( zFilename!=0 ); |
+ nFilename = (int)strlen(zFilename) + 6; |
+ zLockFile = (char *)sqlite3_malloc64(nFilename); |
+ if( zLockFile==0 ){ |
+ rc = SQLITE_NOMEM; |
+ }else{ |
+ sqlite3_snprintf(nFilename, zLockFile, "%s" DOTLOCK_SUFFIX, zFilename); |
+ } |
+ pNew->lockingContext = zLockFile; |
+ } |
+ |
+#if OS_VXWORKS |
+ else if( pLockingStyle == &semIoMethods ){ |
+ /* Named semaphore locking uses the file path so it needs to be |
+ ** included in the semLockingContext |
+ */ |
+ unixEnterMutex(); |
+ rc = findInodeInfo(pNew, &pNew->pInode); |
+ if( (rc==SQLITE_OK) && (pNew->pInode->pSem==NULL) ){ |
+ char *zSemName = pNew->pInode->aSemName; |
+ int n; |
+ sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", |
+ pNew->pId->zCanonicalName); |
+ for( n=1; zSemName[n]; n++ ) |
+ if( zSemName[n]=='/' ) zSemName[n] = '_'; |
+ pNew->pInode->pSem = sem_open(zSemName, O_CREAT, 0666, 1); |
+ if( pNew->pInode->pSem == SEM_FAILED ){ |
+ rc = SQLITE_NOMEM; |
+ pNew->pInode->aSemName[0] = '\0'; |
+ } |
+ } |
+ unixLeaveMutex(); |
+ } |
+#endif |
+ |
+ storeLastErrno(pNew, 0); |
+#if OS_VXWORKS |
+ if( rc!=SQLITE_OK ){ |
+ if( h>=0 ) robust_close(pNew, h, __LINE__); |
+ h = -1; |
+ osUnlink(zFilename); |
+ pNew->ctrlFlags |= UNIXFILE_DELETE; |
+ } |
+#endif |
+ if( rc!=SQLITE_OK ){ |
+ if( h>=0 ) robust_close(pNew, h, __LINE__); |
+ }else{ |
+ pNew->pMethod = pLockingStyle; |
+ OpenCounter(+1); |
+ verifyDbFile(pNew); |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Return the name of a directory in which to put temporary files. |
+** If no suitable temporary file directory can be found, return NULL. |
+*/ |
+static const char *unixTempFileDir(void){ |
+ static const char *azDirs[] = { |
+ 0, |
+ 0, |
+ "/var/tmp", |
+ "/usr/tmp", |
+ "/tmp", |
+ "." |
+ }; |
+ unsigned int i; |
+ struct stat buf; |
+ const char *zDir = sqlite3_temp_directory; |
+ |
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); |
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); |
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ |
+ if( zDir==0 ) continue; |
+ if( osStat(zDir, &buf) ) continue; |
+ if( !S_ISDIR(buf.st_mode) ) continue; |
+ if( osAccess(zDir, 07) ) continue; |
+ break; |
+ } |
+ return zDir; |
+} |
+ |
+/* |
+** Create a temporary file name in zBuf. zBuf must be allocated |
+** by the calling process and must be big enough to hold at least |
+** pVfs->mxPathname bytes. |
+*/ |
+static int unixGetTempname(int nBuf, char *zBuf){ |
+ const char *zDir; |
+ int iLimit = 0; |
+ |
+ /* It's odd to simulate an io-error here, but really this is just |
+ ** using the io-error infrastructure to test that SQLite handles this |
+ ** function failing. |
+ */ |
+ SimulateIOError( return SQLITE_IOERR ); |
+ |
+ zDir = unixTempFileDir(); |
+ do{ |
+ u64 r; |
+ sqlite3_randomness(sizeof(r), &r); |
+ assert( nBuf>2 ); |
+ zBuf[nBuf-2] = 0; |
+ sqlite3_snprintf(nBuf, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX"%llx%c", |
+ zDir, r, 0); |
+ if( zBuf[nBuf-2]!=0 || (iLimit++)>10 ) return SQLITE_ERROR; |
+ }while( osAccess(zBuf,0)==0 ); |
+ return SQLITE_OK; |
+} |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+/* |
+** Routine to transform a unixFile into a proxy-locking unixFile. |
+** Implementation in the proxy-lock division, but used by unixOpen() |
+** if SQLITE_PREFER_PROXY_LOCKING is defined. |
+*/ |
+static int proxyTransformUnixFile(unixFile*, const char*); |
+#endif |
+ |
+/* |
+** Search for an unused file descriptor that was opened on the database |
+** file (not a journal or master-journal file) identified by pathname |
+** zPath with SQLITE_OPEN_XXX flags matching those passed as the second |
+** argument to this function. |
+** |
+** Such a file descriptor may exist if a database connection was closed |
+** but the associated file descriptor could not be closed because some |
+** other file descriptor open on the same file is holding a file-lock. |
+** Refer to comments in the unixClose() function and the lengthy comment |
+** describing "Posix Advisory Locking" at the start of this file for |
+** further details. Also, ticket #4018. |
+** |
+** If a suitable file descriptor is found, then it is returned. If no |
+** such file descriptor is located, -1 is returned. |
+*/ |
+static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ |
+ UnixUnusedFd *pUnused = 0; |
+ |
+ /* Do not search for an unused file descriptor on vxworks. Not because |
+ ** vxworks would not benefit from the change (it might, we're not sure), |
+ ** but because no way to test it is currently available. It is better |
+ ** not to risk breaking vxworks support for the sake of such an obscure |
+ ** feature. */ |
+#if !OS_VXWORKS |
+ struct stat sStat; /* Results of stat() call */ |
+ |
+ /* A stat() call may fail for various reasons. If this happens, it is |
+ ** almost certain that an open() call on the same path will also fail. |
+ ** For this reason, if an error occurs in the stat() call here, it is |
+ ** ignored and -1 is returned. The caller will try to open a new file |
+ ** descriptor on the same path, fail, and return an error to SQLite. |
+ ** |
+ ** Even if a subsequent open() call does succeed, the consequences of |
+ ** not searching for a reusable file descriptor are not dire. */ |
+ if( 0==osStat(zPath, &sStat) ){ |
+ unixInodeInfo *pInode; |
+ |
+ unixEnterMutex(); |
+ pInode = inodeList; |
+ while( pInode && (pInode->fileId.dev!=sStat.st_dev |
+ || pInode->fileId.ino!=sStat.st_ino) ){ |
+ pInode = pInode->pNext; |
+ } |
+ if( pInode ){ |
+ UnixUnusedFd **pp; |
+ for(pp=&pInode->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); |
+ pUnused = *pp; |
+ if( pUnused ){ |
+ *pp = pUnused->pNext; |
+ } |
+ } |
+ unixLeaveMutex(); |
+ } |
+#endif /* if !OS_VXWORKS */ |
+ return pUnused; |
+} |
+ |
+/* |
+** This function is called by unixOpen() to determine the unix permissions |
+** to create new files with. If no error occurs, then SQLITE_OK is returned |
+** and a value suitable for passing as the third argument to open(2) is |
+** written to *pMode. If an IO error occurs, an SQLite error code is |
+** returned and the value of *pMode is not modified. |
+** |
+** In most cases, this routine sets *pMode to 0, which will become |
+** an indication to robust_open() to create the file using |
+** SQLITE_DEFAULT_FILE_PERMISSIONS adjusted by the umask. |
+** But if the file being opened is a WAL or regular journal file, then |
+** this function queries the file-system for the permissions on the |
+** corresponding database file and sets *pMode to this value. Whenever |
+** possible, WAL and journal files are created using the same permissions |
+** as the associated database file. |
+** |
+** If the SQLITE_ENABLE_8_3_NAMES option is enabled, then the |
+** original filename is unavailable. But 8_3_NAMES is only used for |
+** FAT filesystems and permissions do not matter there, so just use |
+** the default permissions. |
+*/ |
+static int findCreateFileMode( |
+ const char *zPath, /* Path of file (possibly) being created */ |
+ int flags, /* Flags passed as 4th argument to xOpen() */ |
+ mode_t *pMode, /* OUT: Permissions to open file with */ |
+ uid_t *pUid, /* OUT: uid to set on the file */ |
+ gid_t *pGid /* OUT: gid to set on the file */ |
+){ |
+ int rc = SQLITE_OK; /* Return Code */ |
+ *pMode = 0; |
+ *pUid = 0; |
+ *pGid = 0; |
+ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ |
+ char zDb[MAX_PATHNAME+1]; /* Database file path */ |
+ int nDb; /* Number of valid bytes in zDb */ |
+ struct stat sStat; /* Output of stat() on database file */ |
+ |
+ /* zPath is a path to a WAL or journal file. The following block derives |
+ ** the path to the associated database file from zPath. This block handles |
+ ** the following naming conventions: |
+ ** |
+ ** "<path to db>-journal" |
+ ** "<path to db>-wal" |
+ ** "<path to db>-journalNN" |
+ ** "<path to db>-walNN" |
+ ** |
+ ** where NN is a decimal number. The NN naming schemes are |
+ ** used by the test_multiplex.c module. |
+ */ |
+ nDb = sqlite3Strlen30(zPath) - 1; |
+ while( zPath[nDb]!='-' ){ |
+#ifndef SQLITE_ENABLE_8_3_NAMES |
+ /* In the normal case (8+3 filenames disabled) the journal filename |
+ ** is guaranteed to contain a '-' character. */ |
+ assert( nDb>0 ); |
+ assert( sqlite3Isalnum(zPath[nDb]) ); |
+#else |
+ /* If 8+3 names are possible, then the journal file might not contain |
+ ** a '-' character. So check for that case and return early. */ |
+ if( nDb==0 || zPath[nDb]=='.' ) return SQLITE_OK; |
+#endif |
+ nDb--; |
+ } |
+ memcpy(zDb, zPath, nDb); |
+ zDb[nDb] = '\0'; |
+ |
+ if( 0==osStat(zDb, &sStat) ){ |
+ *pMode = sStat.st_mode & 0777; |
+ *pUid = sStat.st_uid; |
+ *pGid = sStat.st_gid; |
+ }else{ |
+ rc = SQLITE_IOERR_FSTAT; |
+ } |
+ }else if( flags & SQLITE_OPEN_DELETEONCLOSE ){ |
+ *pMode = 0600; |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Initialize |unixFile| internals of |file| on behalf of chromiumOpen() in |
+** WebDatabase SQLiteFileSystemPosix.cpp. Function is a subset of unixOpen(), |
+** each duplicated piece is marked by "Duplicated in" comment in unixOpen(). |
+*/ |
+CHROMIUM_SQLITE_API |
+int chromium_sqlite3_fill_in_unix_sqlite3_file(sqlite3_vfs* pVfs, |
+ int fd, |
+ sqlite3_file* pFile, |
+ const char* zPath, |
+ int noLock, |
+ int flags) { |
+ unixFile *p = (unixFile *)pFile; |
+ const int eType = flags&0xFFFFFF00; /* Type of file to open */ |
+ const int ctrlFlags = (noLock ? UNIXFILE_NOLOCK : 0); |
+ int rc; |
+ |
+ memset(p, 0, sizeof(unixFile)); |
+ |
+ /* osStat() will not work in the sandbox, so findReusableFd() will always |
+ ** fail, so directly include the failure-case setup then initialize pUnused. |
+ */ |
+ if( eType==SQLITE_OPEN_MAIN_DB ){ |
+ p->pUnused = sqlite3_malloc(sizeof(*p->pUnused)); |
+ if (!p->pUnused) { |
+ return SQLITE_NOMEM; |
+ } |
+ p->pUnused->fd = fd; |
+ p->pUnused->flags = flags; |
+ } |
+ |
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); |
+ if( rc!=SQLITE_OK ){ |
+ sqlite3_free(p->pUnused); |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Open the file zPath. |
+** |
+** Previously, the SQLite OS layer used three functions in place of this |
+** one: |
+** |
+** sqlite3OsOpenReadWrite(); |
+** sqlite3OsOpenReadOnly(); |
+** sqlite3OsOpenExclusive(); |
+** |
+** These calls correspond to the following combinations of flags: |
+** |
+** ReadWrite() -> (READWRITE | CREATE) |
+** ReadOnly() -> (READONLY) |
+** OpenExclusive() -> (READWRITE | CREATE | EXCLUSIVE) |
+** |
+** The old OpenExclusive() accepted a boolean argument - "delFlag". If |
+** true, the file was configured to be automatically deleted when the |
+** file handle closed. To achieve the same effect using this new |
+** interface, add the DELETEONCLOSE flag to those specified above for |
+** OpenExclusive(). |
+*/ |
+static int unixOpen( |
+ sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ |
+ const char *zPath, /* Pathname of file to be opened */ |
+ sqlite3_file *pFile, /* The file descriptor to be filled in */ |
+ int flags, /* Input flags to control the opening */ |
+ int *pOutFlags /* Output flags returned to SQLite core */ |
+){ |
+ unixFile *p = (unixFile *)pFile; |
+ int fd = -1; /* File descriptor returned by open() */ |
+ int openFlags = 0; /* Flags to pass to open() */ |
+ int eType = flags&0xFFFFFF00; /* Type of file to open */ |
+ int noLock; /* True to omit locking primitives */ |
+ int rc = SQLITE_OK; /* Function Return Code */ |
+ int ctrlFlags = 0; /* UNIXFILE_* flags */ |
+ |
+ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
+ int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
+ int isCreate = (flags & SQLITE_OPEN_CREATE); |
+ int isReadonly = (flags & SQLITE_OPEN_READONLY); |
+ int isReadWrite = (flags & SQLITE_OPEN_READWRITE); |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ int isAutoProxy = (flags & SQLITE_OPEN_AUTOPROXY); |
+#endif |
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE |
+ struct statfs fsInfo; |
+#endif |
+ |
+ /* If creating a master or main-file journal, this function will open |
+ ** a file-descriptor on the directory too. The first time unixSync() |
+ ** is called the directory file descriptor will be fsync()ed and close()d. |
+ */ |
+ int syncDir = (isCreate && ( |
+ eType==SQLITE_OPEN_MASTER_JOURNAL |
+ || eType==SQLITE_OPEN_MAIN_JOURNAL |
+ || eType==SQLITE_OPEN_WAL |
+ )); |
+ |
+ /* If argument zPath is a NULL pointer, this function is required to open |
+ ** a temporary file. Use this buffer to store the file name in. |
+ */ |
+ char zTmpname[MAX_PATHNAME+2]; |
+ const char *zName = zPath; |
+ |
+ /* Check the following statements are true: |
+ ** |
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and |
+ ** (b) if CREATE is set, then READWRITE must also be set, and |
+ ** (c) if EXCLUSIVE is set, then CREATE must also be set. |
+ ** (d) if DELETEONCLOSE is set, then CREATE must also be set. |
+ */ |
+ assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); |
+ assert(isCreate==0 || isReadWrite); |
+ assert(isExclusive==0 || isCreate); |
+ assert(isDelete==0 || isCreate); |
+ |
+ /* The main DB, main journal, WAL file and master journal are never |
+ ** automatically deleted. Nor are they ever temporary files. */ |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); |
+ |
+ /* Assert that the upper layer has set one of the "file-type" flags. */ |
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB |
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL |
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL |
+ || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL |
+ ); |
+ |
+ /* Detect a pid change and reset the PRNG. There is a race condition |
+ ** here such that two or more threads all trying to open databases at |
+ ** the same instant might all reset the PRNG. But multiple resets |
+ ** are harmless. |
+ */ |
+ if( randomnessPid!=osGetpid(0) ){ |
+ randomnessPid = osGetpid(0); |
+ sqlite3_randomness(0,0); |
+ } |
+ |
+ /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ |
+ memset(p, 0, sizeof(unixFile)); |
+ |
+ if( eType==SQLITE_OPEN_MAIN_DB ){ |
+ UnixUnusedFd *pUnused; |
+ pUnused = findReusableFd(zName, flags); |
+ if( pUnused ){ |
+ fd = pUnused->fd; |
+ }else{ |
+ /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ |
+ pUnused = sqlite3_malloc64(sizeof(*pUnused)); |
+ if( !pUnused ){ |
+ return SQLITE_NOMEM; |
+ } |
+ } |
+ p->pUnused = pUnused; |
+ |
+ /* Database filenames are double-zero terminated if they are not |
+ ** URIs with parameters. Hence, they can always be passed into |
+ ** sqlite3_uri_parameter(). */ |
+ assert( (flags & SQLITE_OPEN_URI) || zName[strlen(zName)+1]==0 ); |
+ |
+ }else if( !zName ){ |
+ /* If zName is NULL, the upper layer is requesting a temp file. */ |
+ assert(isDelete && !syncDir); |
+ rc = unixGetTempname(pVfs->mxPathname, zTmpname); |
+ if( rc!=SQLITE_OK ){ |
+ return rc; |
+ } |
+ zName = zTmpname; |
+ |
+ /* Generated temporary filenames are always double-zero terminated |
+ ** for use by sqlite3_uri_parameter(). */ |
+ assert( zName[strlen(zName)+1]==0 ); |
+ } |
+ |
+ /* Determine the value of the flags parameter passed to POSIX function |
+ ** open(). These must be calculated even if open() is not called, as |
+ ** they may be stored as part of the file handle and used by the |
+ ** 'conch file' locking functions later on. */ |
+ if( isReadonly ) openFlags |= O_RDONLY; |
+ if( isReadWrite ) openFlags |= O_RDWR; |
+ if( isCreate ) openFlags |= O_CREAT; |
+ if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); |
+ openFlags |= (O_LARGEFILE|O_BINARY); |
+ |
+ if( fd<0 ){ |
+ mode_t openMode; /* Permissions to create file with */ |
+ uid_t uid; /* Userid for the file */ |
+ gid_t gid; /* Groupid for the file */ |
+ rc = findCreateFileMode(zName, flags, &openMode, &uid, &gid); |
+ if( rc!=SQLITE_OK ){ |
+ assert( !p->pUnused ); |
+ assert( eType==SQLITE_OPEN_WAL || eType==SQLITE_OPEN_MAIN_JOURNAL ); |
+ return rc; |
+ } |
+ fd = robust_open(zName, openFlags, openMode); |
+ OSTRACE(("OPENX %-3d %s 0%o\n", fd, zName, openFlags)); |
+ assert( !isExclusive || (openFlags & O_CREAT)!=0 ); |
+ if( fd<0 && errno!=EISDIR && isReadWrite ){ |
+ /* Failed to open the file for read/write access. Try read-only. */ |
+ flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); |
+ openFlags &= ~(O_RDWR|O_CREAT); |
+ flags |= SQLITE_OPEN_READONLY; |
+ openFlags |= O_RDONLY; |
+ isReadonly = 1; |
+ fd = robust_open(zName, openFlags, openMode); |
+ } |
+ if( fd<0 ){ |
+ rc = unixLogError(SQLITE_CANTOPEN_BKPT, "open", zName); |
+ goto open_finished; |
+ } |
+ |
+ /* If this process is running as root and if creating a new rollback |
+ ** journal or WAL file, set the ownership of the journal or WAL to be |
+ ** the same as the original database. |
+ */ |
+ if( flags & (SQLITE_OPEN_WAL|SQLITE_OPEN_MAIN_JOURNAL) ){ |
+ robustFchown(fd, uid, gid); |
+ } |
+ } |
+ assert( fd>=0 ); |
+ if( pOutFlags ){ |
+ *pOutFlags = flags; |
+ } |
+ |
+ if( p->pUnused ){ |
+ /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ |
+ p->pUnused->fd = fd; |
+ p->pUnused->flags = flags; |
+ } |
+ |
+ if( isDelete ){ |
+#if OS_VXWORKS |
+ zPath = zName; |
+#elif defined(SQLITE_UNLINK_AFTER_CLOSE) |
+ zPath = sqlite3_mprintf("%s", zName); |
+ if( zPath==0 ){ |
+ robust_close(p, fd, __LINE__); |
+ return SQLITE_NOMEM; |
+ } |
+#else |
+ osUnlink(zName); |
+#endif |
+ } |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ else{ |
+ p->openFlags = openFlags; |
+ } |
+#endif |
+ |
+ noLock = eType!=SQLITE_OPEN_MAIN_DB; |
+ |
+ |
+#if defined(__APPLE__) || SQLITE_ENABLE_LOCKING_STYLE |
+ if( fstatfs(fd, &fsInfo) == -1 ){ |
+ storeLastErrno(p, errno); |
+ robust_close(p, fd, __LINE__); |
+ return SQLITE_IOERR_ACCESS; |
+ } |
+ if (0 == strncmp("msdos", fsInfo.f_fstypename, 5)) { |
+ ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; |
+ } |
+ if (0 == strncmp("exfat", fsInfo.f_fstypename, 5)) { |
+ ((unixFile*)pFile)->fsFlags |= SQLITE_FSFLAGS_IS_MSDOS; |
+ } |
+#endif |
+ |
+ /* Set up appropriate ctrlFlags */ |
+ if( isDelete ) ctrlFlags |= UNIXFILE_DELETE; |
+ if( isReadonly ) ctrlFlags |= UNIXFILE_RDONLY; |
+ if( noLock ) ctrlFlags |= UNIXFILE_NOLOCK; |
+ if( syncDir ) ctrlFlags |= UNIXFILE_DIRSYNC; |
+ if( flags & SQLITE_OPEN_URI ) ctrlFlags |= UNIXFILE_URI; |
+ |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+#if SQLITE_PREFER_PROXY_LOCKING |
+ isAutoProxy = 1; |
+#endif |
+ if( isAutoProxy && (zPath!=NULL) && (!noLock) && pVfs->xOpen ){ |
+ char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); |
+ int useProxy = 0; |
+ |
+ /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means |
+ ** never use proxy, NULL means use proxy for non-local files only. */ |
+ if( envforce!=NULL ){ |
+ useProxy = atoi(envforce)>0; |
+ }else{ |
+ useProxy = !(fsInfo.f_flags&MNT_LOCAL); |
+ } |
+ if( useProxy ){ |
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); |
+ if( rc==SQLITE_OK ){ |
+ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); |
+ if( rc!=SQLITE_OK ){ |
+ /* Use unixClose to clean up the resources added in fillInUnixFile |
+ ** and clear all the structure's references. Specifically, |
+ ** pFile->pMethods will be NULL so sqlite3OsClose will be a no-op |
+ */ |
+ unixClose(pFile); |
+ return rc; |
+ } |
+ } |
+ goto open_finished; |
+ } |
+ } |
+#endif |
+ |
+ /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ |
+ rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags); |
+ |
+open_finished: |
+ if( rc!=SQLITE_OK ){ |
+ /* Duplicated in chromium_sqlite3_fill_in_unix_sqlite3_file(). */ |
+ sqlite3_free(p->pUnused); |
+ } |
+ return rc; |
+} |
+ |
+ |
+/* |
+** Delete the file at zPath. If the dirSync argument is true, fsync() |
+** the directory after deleting the file. |
+*/ |
+static int unixDelete( |
+ sqlite3_vfs *NotUsed, /* VFS containing this as the xDelete method */ |
+ const char *zPath, /* Name of file to be deleted */ |
+ int dirSync /* If true, fsync() directory after deleting file */ |
+){ |
+ int rc = SQLITE_OK; |
+ UNUSED_PARAMETER(NotUsed); |
+ SimulateIOError(return SQLITE_IOERR_DELETE); |
+ if( osUnlink(zPath)==(-1) ){ |
+ if( errno==ENOENT |
+#if OS_VXWORKS |
+ || osAccess(zPath,0)!=0 |
+#endif |
+ ){ |
+ rc = SQLITE_IOERR_DELETE_NOENT; |
+ }else{ |
+ rc = unixLogError(SQLITE_IOERR_DELETE, "unlink", zPath); |
+ } |
+ return rc; |
+ } |
+#ifndef SQLITE_DISABLE_DIRSYNC |
+ if( (dirSync & 1)!=0 ){ |
+ int fd; |
+ rc = osOpenDirectory(zPath, &fd); |
+ if( rc==SQLITE_OK ){ |
+#if OS_VXWORKS |
+ if( fsync(fd)==-1 ) |
+#else |
+ if( fsync(fd) ) |
+#endif |
+ { |
+ rc = unixLogError(SQLITE_IOERR_DIR_FSYNC, "fsync", zPath); |
+ } |
+ robust_close(0, fd, __LINE__); |
+ }else{ |
+ assert( rc==SQLITE_CANTOPEN ); |
+ rc = SQLITE_OK; |
+ } |
+ } |
+#endif |
+ return rc; |
+} |
+ |
+/* |
+** Test the existence of or access permissions of file zPath. The |
+** test performed depends on the value of flags: |
+** |
+** SQLITE_ACCESS_EXISTS: Return 1 if the file exists |
+** SQLITE_ACCESS_READWRITE: Return 1 if the file is read and writable. |
+** SQLITE_ACCESS_READONLY: Return 1 if the file is readable. |
+** |
+** Otherwise return 0. |
+*/ |
+static int unixAccess( |
+ sqlite3_vfs *NotUsed, /* The VFS containing this xAccess method */ |
+ const char *zPath, /* Path of the file to examine */ |
+ int flags, /* What do we want to learn about the zPath file? */ |
+ int *pResOut /* Write result boolean here */ |
+){ |
+ UNUSED_PARAMETER(NotUsed); |
+ SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
+ assert( pResOut!=0 ); |
+ |
+ /* The spec says there are three possible values for flags. But only |
+ ** two of them are actually used */ |
+ assert( flags==SQLITE_ACCESS_EXISTS || flags==SQLITE_ACCESS_READWRITE ); |
+ |
+ if( flags==SQLITE_ACCESS_EXISTS ){ |
+ struct stat buf; |
+ *pResOut = (0==osStat(zPath, &buf) && buf.st_size>0); |
+ }else{ |
+ *pResOut = osAccess(zPath, W_OK|R_OK)==0; |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+ |
+/* |
+** Turn a relative pathname into a full pathname. The relative path |
+** is stored as a nul-terminated string in the buffer pointed to by |
+** zPath. |
+** |
+** zOut points to a buffer of at least sqlite3_vfs.mxPathname bytes |
+** (in this case, MAX_PATHNAME bytes). The full-path is written to |
+** this buffer before returning. |
+*/ |
+static int unixFullPathname( |
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */ |
+ const char *zPath, /* Possibly relative input path */ |
+ int nOut, /* Size of output buffer in bytes */ |
+ char *zOut /* Output buffer */ |
+){ |
+ int nByte; |
+ |
+ /* It's odd to simulate an io-error here, but really this is just |
+ ** using the io-error infrastructure to test that SQLite handles this |
+ ** function failing. This function could fail if, for example, the |
+ ** current working directory has been unlinked. |
+ */ |
+ SimulateIOError( return SQLITE_ERROR ); |
+ |
+ assert( pVfs->mxPathname==MAX_PATHNAME ); |
+ UNUSED_PARAMETER(pVfs); |
+ |
+ /* Attempt to resolve the path as if it were a symbolic link. If it is |
+ ** a symbolic link, the resolved path is stored in buffer zOut[]. Or, if |
+ ** the identified file is not a symbolic link or does not exist, then |
+ ** zPath is copied directly into zOut. Either way, nByte is left set to |
+ ** the size of the string copied into zOut[] in bytes. */ |
+ nByte = osReadlink(zPath, zOut, nOut-1); |
+ if( nByte<0 ){ |
+ if( errno!=EINVAL && errno!=ENOENT ){ |
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "readlink", zPath); |
+ } |
+ sqlite3_snprintf(nOut, zOut, "%s", zPath); |
+ nByte = sqlite3Strlen30(zOut); |
+ }else{ |
+ zOut[nByte] = '\0'; |
+ } |
+ |
+ /* If buffer zOut[] now contains an absolute path there is nothing more |
+ ** to do. If it contains a relative path, do the following: |
+ ** |
+ ** * move the relative path string so that it is at the end of th |
+ ** zOut[] buffer. |
+ ** * Call getcwd() to read the path of the current working directory |
+ ** into the start of the zOut[] buffer. |
+ ** * Append a '/' character to the cwd string and move the |
+ ** relative path back within the buffer so that it immediately |
+ ** follows the '/'. |
+ ** |
+ ** This code is written so that if the combination of the CWD and relative |
+ ** path are larger than the allocated size of zOut[] the CWD is silently |
+ ** truncated to make it fit. This is Ok, as SQLite refuses to open any |
+ ** file for which this function returns a full path larger than (nOut-8) |
+ ** bytes in size. */ |
+ testcase( nByte==nOut-5 ); |
+ testcase( nByte==nOut-4 ); |
+ if( zOut[0]!='/' && nByte<nOut-4 ){ |
+ int nCwd; |
+ int nRem = nOut-nByte-1; |
+ memmove(&zOut[nRem], zOut, nByte+1); |
+ zOut[nRem-1] = '\0'; |
+ if( osGetcwd(zOut, nRem-1)==0 ){ |
+ return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath); |
+ } |
+ nCwd = sqlite3Strlen30(zOut); |
+ assert( nCwd<=nRem-1 ); |
+ zOut[nCwd] = '/'; |
+ memmove(&zOut[nCwd+1], &zOut[nRem], nByte+1); |
+ } |
+ |
+ return SQLITE_OK; |
+} |
+ |
+ |
+#ifndef SQLITE_OMIT_LOAD_EXTENSION |
+/* |
+** Interfaces for opening a shared library, finding entry points |
+** within the shared library, and closing the shared library. |
+*/ |
+#include <dlfcn.h> |
+static void *unixDlOpen(sqlite3_vfs *NotUsed, const char *zFilename){ |
+ UNUSED_PARAMETER(NotUsed); |
+ return dlopen(zFilename, RTLD_NOW | RTLD_GLOBAL); |
+} |
+ |
+/* |
+** SQLite calls this function immediately after a call to unixDlSym() or |
+** unixDlOpen() fails (returns a null pointer). If a more detailed error |
+** message is available, it is written to zBufOut. If no error message |
+** is available, zBufOut is left unmodified and SQLite uses a default |
+** error message. |
+*/ |
+static void unixDlError(sqlite3_vfs *NotUsed, int nBuf, char *zBufOut){ |
+ const char *zErr; |
+ UNUSED_PARAMETER(NotUsed); |
+ unixEnterMutex(); |
+ zErr = dlerror(); |
+ if( zErr ){ |
+ sqlite3_snprintf(nBuf, zBufOut, "%s", zErr); |
+ } |
+ unixLeaveMutex(); |
+} |
+static void (*unixDlSym(sqlite3_vfs *NotUsed, void *p, const char*zSym))(void){ |
+ /* |
+ ** GCC with -pedantic-errors says that C90 does not allow a void* to be |
+ ** cast into a pointer to a function. And yet the library dlsym() routine |
+ ** returns a void* which is really a pointer to a function. So how do we |
+ ** use dlsym() with -pedantic-errors? |
+ ** |
+ ** Variable x below is defined to be a pointer to a function taking |
+ ** parameters void* and const char* and returning a pointer to a function. |
+ ** We initialize x by assigning it a pointer to the dlsym() function. |
+ ** (That assignment requires a cast.) Then we call the function that |
+ ** x points to. |
+ ** |
+ ** This work-around is unlikely to work correctly on any system where |
+ ** you really cannot cast a function pointer into void*. But then, on the |
+ ** other hand, dlsym() will not work on such a system either, so we have |
+ ** not really lost anything. |
+ */ |
+ void (*(*x)(void*,const char*))(void); |
+ UNUSED_PARAMETER(NotUsed); |
+ x = (void(*(*)(void*,const char*))(void))dlsym; |
+ return (*x)(p, zSym); |
+} |
+static void unixDlClose(sqlite3_vfs *NotUsed, void *pHandle){ |
+ UNUSED_PARAMETER(NotUsed); |
+ dlclose(pHandle); |
+} |
+#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ |
+ #define unixDlOpen 0 |
+ #define unixDlError 0 |
+ #define unixDlSym 0 |
+ #define unixDlClose 0 |
+#endif |
+ |
+/* |
+** Write nBuf bytes of random data to the supplied buffer zBuf. |
+*/ |
+static int unixRandomness(sqlite3_vfs *NotUsed, int nBuf, char *zBuf){ |
+ UNUSED_PARAMETER(NotUsed); |
+ assert((size_t)nBuf>=(sizeof(time_t)+sizeof(int))); |
+ |
+ /* We have to initialize zBuf to prevent valgrind from reporting |
+ ** errors. The reports issued by valgrind are incorrect - we would |
+ ** prefer that the randomness be increased by making use of the |
+ ** uninitialized space in zBuf - but valgrind errors tend to worry |
+ ** some users. Rather than argue, it seems easier just to initialize |
+ ** the whole array and silence valgrind, even if that means less randomness |
+ ** in the random seed. |
+ ** |
+ ** When testing, initializing zBuf[] to zero is all we do. That means |
+ ** that we always use the same random number sequence. This makes the |
+ ** tests repeatable. |
+ */ |
+ memset(zBuf, 0, nBuf); |
+ randomnessPid = osGetpid(0); |
+#if !defined(SQLITE_TEST) && !defined(SQLITE_OMIT_RANDOMNESS) |
+ { |
+ int fd, got; |
+ fd = robust_open("/dev/urandom", O_RDONLY, 0); |
+ if( fd<0 ){ |
+ time_t t; |
+ time(&t); |
+ memcpy(zBuf, &t, sizeof(t)); |
+ memcpy(&zBuf[sizeof(t)], &randomnessPid, sizeof(randomnessPid)); |
+ assert( sizeof(t)+sizeof(randomnessPid)<=(size_t)nBuf ); |
+ nBuf = sizeof(t) + sizeof(randomnessPid); |
+ }else{ |
+ do{ got = osRead(fd, zBuf, nBuf); }while( got<0 && errno==EINTR ); |
+ robust_close(0, fd, __LINE__); |
+ } |
+ } |
+#endif |
+ return nBuf; |
+} |
+ |
+ |
+/* |
+** Sleep for a little while. Return the amount of time slept. |
+** The argument is the number of microseconds we want to sleep. |
+** The return value is the number of microseconds of sleep actually |
+** requested from the underlying operating system, a number which |
+** might be greater than or equal to the argument, but not less |
+** than the argument. |
+*/ |
+static int unixSleep(sqlite3_vfs *NotUsed, int microseconds){ |
+#if OS_VXWORKS |
+ struct timespec sp; |
+ |
+ sp.tv_sec = microseconds / 1000000; |
+ sp.tv_nsec = (microseconds % 1000000) * 1000; |
+ nanosleep(&sp, NULL); |
+ UNUSED_PARAMETER(NotUsed); |
+ return microseconds; |
+#elif defined(HAVE_USLEEP) && HAVE_USLEEP |
+ usleep(microseconds); |
+ UNUSED_PARAMETER(NotUsed); |
+ return microseconds; |
+#else |
+ int seconds = (microseconds+999999)/1000000; |
+ sleep(seconds); |
+ UNUSED_PARAMETER(NotUsed); |
+ return seconds*1000000; |
+#endif |
+} |
+ |
+/* |
+** The following variable, if set to a non-zero value, is interpreted as |
+** the number of seconds since 1970 and is used to set the result of |
+** sqlite3OsCurrentTime() during testing. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ |
+#endif |
+ |
+/* |
+** Find the current time (in Universal Coordinated Time). Write into *piNow |
+** the current time and date as a Julian Day number times 86_400_000. In |
+** other words, write into *piNow the number of milliseconds since the Julian |
+** epoch of noon in Greenwich on November 24, 4714 B.C according to the |
+** proleptic Gregorian calendar. |
+** |
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date |
+** cannot be found. |
+*/ |
+static int unixCurrentTimeInt64(sqlite3_vfs *NotUsed, sqlite3_int64 *piNow){ |
+ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; |
+ int rc = SQLITE_OK; |
+#if defined(NO_GETTOD) |
+ time_t t; |
+ time(&t); |
+ *piNow = ((sqlite3_int64)t)*1000 + unixEpoch; |
+#elif OS_VXWORKS |
+ struct timespec sNow; |
+ clock_gettime(CLOCK_REALTIME, &sNow); |
+ *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_nsec/1000000; |
+#else |
+ struct timeval sNow; |
+ (void)gettimeofday(&sNow, 0); /* Cannot fail given valid arguments */ |
+ *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000; |
+#endif |
+ |
+#ifdef SQLITE_TEST |
+ if( sqlite3_current_time ){ |
+ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; |
+ } |
+#endif |
+ UNUSED_PARAMETER(NotUsed); |
+ return rc; |
+} |
+ |
+#ifndef SQLITE_OMIT_DEPRECATED |
+/* |
+** Find the current time (in Universal Coordinated Time). Write the |
+** current time and date as a Julian Day number into *prNow and |
+** return 0. Return 1 if the time and date cannot be found. |
+*/ |
+static int unixCurrentTime(sqlite3_vfs *NotUsed, double *prNow){ |
+ sqlite3_int64 i = 0; |
+ int rc; |
+ UNUSED_PARAMETER(NotUsed); |
+ rc = unixCurrentTimeInt64(0, &i); |
+ *prNow = i/86400000.0; |
+ return rc; |
+} |
+#else |
+# define unixCurrentTime 0 |
+#endif |
+ |
+#ifndef SQLITE_OMIT_DEPRECATED |
+/* |
+** We added the xGetLastError() method with the intention of providing |
+** better low-level error messages when operating-system problems come up |
+** during SQLite operation. But so far, none of that has been implemented |
+** in the core. So this routine is never called. For now, it is merely |
+** a place-holder. |
+*/ |
+static int unixGetLastError(sqlite3_vfs *NotUsed, int NotUsed2, char *NotUsed3){ |
+ UNUSED_PARAMETER(NotUsed); |
+ UNUSED_PARAMETER(NotUsed2); |
+ UNUSED_PARAMETER(NotUsed3); |
+ return 0; |
+} |
+#else |
+# define unixGetLastError 0 |
+#endif |
+ |
+ |
+/* |
+************************ End of sqlite3_vfs methods *************************** |
+******************************************************************************/ |
+ |
+/****************************************************************************** |
+************************** Begin Proxy Locking ******************************** |
+** |
+** Proxy locking is a "uber-locking-method" in this sense: It uses the |
+** other locking methods on secondary lock files. Proxy locking is a |
+** meta-layer over top of the primitive locking implemented above. For |
+** this reason, the division that implements of proxy locking is deferred |
+** until late in the file (here) after all of the other I/O methods have |
+** been defined - so that the primitive locking methods are available |
+** as services to help with the implementation of proxy locking. |
+** |
+**** |
+** |
+** The default locking schemes in SQLite use byte-range locks on the |
+** database file to coordinate safe, concurrent access by multiple readers |
+** and writers [http://sqlite.org/lockingv3.html]. The five file locking |
+** states (UNLOCKED, PENDING, SHARED, RESERVED, EXCLUSIVE) are implemented |
+** as POSIX read & write locks over fixed set of locations (via fsctl), |
+** on AFP and SMB only exclusive byte-range locks are available via fsctl |
+** with _IOWR('z', 23, struct ByteRangeLockPB2) to track the same 5 states. |
+** To simulate a F_RDLCK on the shared range, on AFP a randomly selected |
+** address in the shared range is taken for a SHARED lock, the entire |
+** shared range is taken for an EXCLUSIVE lock): |
+** |
+** PENDING_BYTE 0x40000000 |
+** RESERVED_BYTE 0x40000001 |
+** SHARED_RANGE 0x40000002 -> 0x40000200 |
+** |
+** This works well on the local file system, but shows a nearly 100x |
+** slowdown in read performance on AFP because the AFP client disables |
+** the read cache when byte-range locks are present. Enabling the read |
+** cache exposes a cache coherency problem that is present on all OS X |
+** supported network file systems. NFS and AFP both observe the |
+** close-to-open semantics for ensuring cache coherency |
+** [http://nfs.sourceforge.net/#faq_a8], which does not effectively |
+** address the requirements for concurrent database access by multiple |
+** readers and writers |
+** [http://www.nabble.com/SQLite-on-NFS-cache-coherency-td15655701.html]. |
+** |
+** To address the performance and cache coherency issues, proxy file locking |
+** changes the way database access is controlled by limiting access to a |
+** single host at a time and moving file locks off of the database file |
+** and onto a proxy file on the local file system. |
+** |
+** |
+** Using proxy locks |
+** ----------------- |
+** |
+** C APIs |
+** |
+** sqlite3_file_control(db, dbname, SQLITE_FCNTL_SET_LOCKPROXYFILE, |
+** <proxy_path> | ":auto:"); |
+** sqlite3_file_control(db, dbname, SQLITE_FCNTL_GET_LOCKPROXYFILE, |
+** &<proxy_path>); |
+** |
+** |
+** SQL pragmas |
+** |
+** PRAGMA [database.]lock_proxy_file=<proxy_path> | :auto: |
+** PRAGMA [database.]lock_proxy_file |
+** |
+** Specifying ":auto:" means that if there is a conch file with a matching |
+** host ID in it, the proxy path in the conch file will be used, otherwise |
+** a proxy path based on the user's temp dir |
+** (via confstr(_CS_DARWIN_USER_TEMP_DIR,...)) will be used and the |
+** actual proxy file name is generated from the name and path of the |
+** database file. For example: |
+** |
+** For database path "/Users/me/foo.db" |
+** The lock path will be "<tmpdir>/sqliteplocks/_Users_me_foo.db:auto:") |
+** |
+** Once a lock proxy is configured for a database connection, it can not |
+** be removed, however it may be switched to a different proxy path via |
+** the above APIs (assuming the conch file is not being held by another |
+** connection or process). |
+** |
+** |
+** How proxy locking works |
+** ----------------------- |
+** |
+** Proxy file locking relies primarily on two new supporting files: |
+** |
+** * conch file to limit access to the database file to a single host |
+** at a time |
+** |
+** * proxy file to act as a proxy for the advisory locks normally |
+** taken on the database |
+** |
+** The conch file - to use a proxy file, sqlite must first "hold the conch" |
+** by taking an sqlite-style shared lock on the conch file, reading the |
+** contents and comparing the host's unique host ID (see below) and lock |
+** proxy path against the values stored in the conch. The conch file is |
+** stored in the same directory as the database file and the file name |
+** is patterned after the database file name as ".<databasename>-conch". |
+** If the conch file does not exist, or its contents do not match the |
+** host ID and/or proxy path, then the lock is escalated to an exclusive |
+** lock and the conch file contents is updated with the host ID and proxy |
+** path and the lock is downgraded to a shared lock again. If the conch |
+** is held by another process (with a shared lock), the exclusive lock |
+** will fail and SQLITE_BUSY is returned. |
+** |
+** The proxy file - a single-byte file used for all advisory file locks |
+** normally taken on the database file. This allows for safe sharing |
+** of the database file for multiple readers and writers on the same |
+** host (the conch ensures that they all use the same local lock file). |
+** |
+** Requesting the lock proxy does not immediately take the conch, it is |
+** only taken when the first request to lock database file is made. |
+** This matches the semantics of the traditional locking behavior, where |
+** opening a connection to a database file does not take a lock on it. |
+** The shared lock and an open file descriptor are maintained until |
+** the connection to the database is closed. |
+** |
+** The proxy file and the lock file are never deleted so they only need |
+** to be created the first time they are used. |
+** |
+** Configuration options |
+** --------------------- |
+** |
+** SQLITE_PREFER_PROXY_LOCKING |
+** |
+** Database files accessed on non-local file systems are |
+** automatically configured for proxy locking, lock files are |
+** named automatically using the same logic as |
+** PRAGMA lock_proxy_file=":auto:" |
+** |
+** SQLITE_PROXY_DEBUG |
+** |
+** Enables the logging of error messages during host id file |
+** retrieval and creation |
+** |
+** LOCKPROXYDIR |
+** |
+** Overrides the default directory used for lock proxy files that |
+** are named automatically via the ":auto:" setting |
+** |
+** SQLITE_DEFAULT_PROXYDIR_PERMISSIONS |
+** |
+** Permissions to use when creating a directory for storing the |
+** lock proxy files, only used when LOCKPROXYDIR is not set. |
+** |
+** |
+** As mentioned above, when compiled with SQLITE_PREFER_PROXY_LOCKING, |
+** setting the environment variable SQLITE_FORCE_PROXY_LOCKING to 1 will |
+** force proxy locking to be used for every database file opened, and 0 |
+** will force automatic proxy locking to be disabled for all database |
+** files (explicitly calling the SQLITE_FCNTL_SET_LOCKPROXYFILE pragma or |
+** sqlite_file_control API is not affected by SQLITE_FORCE_PROXY_LOCKING). |
+*/ |
+ |
+/* |
+** Proxy locking is only available on MacOSX |
+*/ |
+#if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE |
+ |
+/* |
+** The proxyLockingContext has the path and file structures for the remote |
+** and local proxy files in it |
+*/ |
+typedef struct proxyLockingContext proxyLockingContext; |
+struct proxyLockingContext { |
+ unixFile *conchFile; /* Open conch file */ |
+ char *conchFilePath; /* Name of the conch file */ |
+ unixFile *lockProxy; /* Open proxy lock file */ |
+ char *lockProxyPath; /* Name of the proxy lock file */ |
+ char *dbPath; /* Name of the open file */ |
+ int conchHeld; /* 1 if the conch is held, -1 if lockless */ |
+ int nFails; /* Number of conch taking failures */ |
+ void *oldLockingContext; /* Original lockingcontext to restore on close */ |
+ sqlite3_io_methods const *pOldMethod; /* Original I/O methods for close */ |
+}; |
+ |
+/* |
+** The proxy lock file path for the database at dbPath is written into lPath, |
+** which must point to valid, writable memory large enough for a maxLen length |
+** file path. |
+*/ |
+static int proxyGetLockPath(const char *dbPath, char *lPath, size_t maxLen){ |
+ int len; |
+ int dbLen; |
+ int i; |
+ |
+#ifdef LOCKPROXYDIR |
+ len = strlcpy(lPath, LOCKPROXYDIR, maxLen); |
+#else |
+# ifdef _CS_DARWIN_USER_TEMP_DIR |
+ { |
+ if( !confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen) ){ |
+ OSTRACE(("GETLOCKPATH failed %s errno=%d pid=%d\n", |
+ lPath, errno, osGetpid(0))); |
+ return SQLITE_IOERR_LOCK; |
+ } |
+ len = strlcat(lPath, "sqliteplocks", maxLen); |
+ } |
+# else |
+ len = strlcpy(lPath, "/tmp/", maxLen); |
+# endif |
+#endif |
+ |
+ if( lPath[len-1]!='/' ){ |
+ len = strlcat(lPath, "/", maxLen); |
+ } |
+ |
+ /* transform the db path to a unique cache name */ |
+ dbLen = (int)strlen(dbPath); |
+ for( i=0; i<dbLen && (i+len+7)<(int)maxLen; i++){ |
+ char c = dbPath[i]; |
+ lPath[i+len] = (c=='/')?'_':c; |
+ } |
+ lPath[i+len]='\0'; |
+ strlcat(lPath, ":auto:", maxLen); |
+ OSTRACE(("GETLOCKPATH proxy lock path=%s pid=%d\n", lPath, osGetpid(0))); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+ ** Creates the lock file and any missing directories in lockPath |
+ */ |
+static int proxyCreateLockPath(const char *lockPath){ |
+ int i, len; |
+ char buf[MAXPATHLEN]; |
+ int start = 0; |
+ |
+ assert(lockPath!=NULL); |
+ /* try to create all the intermediate directories */ |
+ len = (int)strlen(lockPath); |
+ buf[0] = lockPath[0]; |
+ for( i=1; i<len; i++ ){ |
+ if( lockPath[i] == '/' && (i - start > 0) ){ |
+ /* only mkdir if leaf dir != "." or "/" or ".." */ |
+ if( i-start>2 || (i-start==1 && buf[start] != '.' && buf[start] != '/') |
+ || (i-start==2 && buf[start] != '.' && buf[start+1] != '.') ){ |
+ buf[i]='\0'; |
+ if( osMkdir(buf, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ |
+ int err=errno; |
+ if( err!=EEXIST ) { |
+ OSTRACE(("CREATELOCKPATH FAILED creating %s, " |
+ "'%s' proxy lock path=%s pid=%d\n", |
+ buf, strerror(err), lockPath, osGetpid(0))); |
+ return err; |
+ } |
+ } |
+ } |
+ start=i+1; |
+ } |
+ buf[i] = lockPath[i]; |
+ } |
+ OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n",lockPath,osGetpid(0))); |
+ return 0; |
+} |
+ |
+/* |
+** Create a new VFS file descriptor (stored in memory obtained from |
+** sqlite3_malloc) and open the file named "path" in the file descriptor. |
+** |
+** The caller is responsible not only for closing the file descriptor |
+** but also for freeing the memory associated with the file descriptor. |
+*/ |
+static int proxyCreateUnixFile( |
+ const char *path, /* path for the new unixFile */ |
+ unixFile **ppFile, /* unixFile created and returned by ref */ |
+ int islockfile /* if non zero missing dirs will be created */ |
+) { |
+ int fd = -1; |
+ unixFile *pNew; |
+ int rc = SQLITE_OK; |
+ int openFlags = O_RDWR | O_CREAT; |
+ sqlite3_vfs dummyVfs; |
+ int terrno = 0; |
+ UnixUnusedFd *pUnused = NULL; |
+ |
+ /* 1. first try to open/create the file |
+ ** 2. if that fails, and this is a lock file (not-conch), try creating |
+ ** the parent directories and then try again. |
+ ** 3. if that fails, try to open the file read-only |
+ ** otherwise return BUSY (if lock file) or CANTOPEN for the conch file |
+ */ |
+ pUnused = findReusableFd(path, openFlags); |
+ if( pUnused ){ |
+ fd = pUnused->fd; |
+ }else{ |
+ pUnused = sqlite3_malloc64(sizeof(*pUnused)); |
+ if( !pUnused ){ |
+ return SQLITE_NOMEM; |
+ } |
+ } |
+ if( fd<0 ){ |
+ fd = robust_open(path, openFlags, 0); |
+ terrno = errno; |
+ if( fd<0 && errno==ENOENT && islockfile ){ |
+ if( proxyCreateLockPath(path) == SQLITE_OK ){ |
+ fd = robust_open(path, openFlags, 0); |
+ } |
+ } |
+ } |
+ if( fd<0 ){ |
+ openFlags = O_RDONLY; |
+ fd = robust_open(path, openFlags, 0); |
+ terrno = errno; |
+ } |
+ if( fd<0 ){ |
+ if( islockfile ){ |
+ return SQLITE_BUSY; |
+ } |
+ switch (terrno) { |
+ case EACCES: |
+ return SQLITE_PERM; |
+ case EIO: |
+ return SQLITE_IOERR_LOCK; /* even though it is the conch */ |
+ default: |
+ return SQLITE_CANTOPEN_BKPT; |
+ } |
+ } |
+ |
+ pNew = (unixFile *)sqlite3_malloc64(sizeof(*pNew)); |
+ if( pNew==NULL ){ |
+ rc = SQLITE_NOMEM; |
+ goto end_create_proxy; |
+ } |
+ memset(pNew, 0, sizeof(unixFile)); |
+ pNew->openFlags = openFlags; |
+ memset(&dummyVfs, 0, sizeof(dummyVfs)); |
+ dummyVfs.pAppData = (void*)&autolockIoFinder; |
+ dummyVfs.zName = "dummy"; |
+ pUnused->fd = fd; |
+ pUnused->flags = openFlags; |
+ pNew->pUnused = pUnused; |
+ |
+ rc = fillInUnixFile(&dummyVfs, fd, (sqlite3_file*)pNew, path, 0); |
+ if( rc==SQLITE_OK ){ |
+ *ppFile = pNew; |
+ return SQLITE_OK; |
+ } |
+end_create_proxy: |
+ robust_close(pNew, fd, __LINE__); |
+ sqlite3_free(pNew); |
+ sqlite3_free(pUnused); |
+ return rc; |
+} |
+ |
+#ifdef SQLITE_TEST |
+/* simulate multiple hosts by creating unique hostid file paths */ |
+SQLITE_API int sqlite3_hostid_num = 0; |
+#endif |
+ |
+#define PROXY_HOSTIDLEN 16 /* conch file host id length */ |
+ |
+#ifdef HAVE_GETHOSTUUID |
+/* Not always defined in the headers as it ought to be */ |
+extern int gethostuuid(uuid_t id, const struct timespec *wait); |
+#endif |
+ |
+/* get the host ID via gethostuuid(), pHostID must point to PROXY_HOSTIDLEN |
+** bytes of writable memory. |
+*/ |
+static int proxyGetHostID(unsigned char *pHostID, int *pError){ |
+ assert(PROXY_HOSTIDLEN == sizeof(uuid_t)); |
+ memset(pHostID, 0, PROXY_HOSTIDLEN); |
+#ifdef HAVE_GETHOSTUUID |
+ { |
+ struct timespec timeout = {1, 0}; /* 1 sec timeout */ |
+ if( gethostuuid(pHostID, &timeout) ){ |
+ int err = errno; |
+ if( pError ){ |
+ *pError = err; |
+ } |
+ return SQLITE_IOERR; |
+ } |
+ } |
+#else |
+ UNUSED_PARAMETER(pError); |
+#endif |
+#ifdef SQLITE_TEST |
+ /* simulate multiple hosts by creating unique hostid file paths */ |
+ if( sqlite3_hostid_num != 0){ |
+ pHostID[0] = (char)(pHostID[0] + (char)(sqlite3_hostid_num & 0xFF)); |
+ } |
+#endif |
+ |
+ return SQLITE_OK; |
+} |
+ |
+/* The conch file contains the header, host id and lock file path |
+ */ |
+#define PROXY_CONCHVERSION 2 /* 1-byte header, 16-byte host id, path */ |
+#define PROXY_HEADERLEN 1 /* conch file header length */ |
+#define PROXY_PATHINDEX (PROXY_HEADERLEN+PROXY_HOSTIDLEN) |
+#define PROXY_MAXCONCHLEN (PROXY_HEADERLEN+PROXY_HOSTIDLEN+MAXPATHLEN) |
+ |
+/* |
+** Takes an open conch file, copies the contents to a new path and then moves |
+** it back. The newly created file's file descriptor is assigned to the |
+** conch file structure and finally the original conch file descriptor is |
+** closed. Returns zero if successful. |
+*/ |
+static int proxyBreakConchLock(unixFile *pFile, uuid_t myHostID){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ unixFile *conchFile = pCtx->conchFile; |
+ char tPath[MAXPATHLEN]; |
+ char buf[PROXY_MAXCONCHLEN]; |
+ char *cPath = pCtx->conchFilePath; |
+ size_t readLen = 0; |
+ size_t pathLen = 0; |
+ char errmsg[64] = ""; |
+ int fd = -1; |
+ int rc = -1; |
+ UNUSED_PARAMETER(myHostID); |
+ |
+ /* create a new path by replace the trailing '-conch' with '-break' */ |
+ pathLen = strlcpy(tPath, cPath, MAXPATHLEN); |
+ if( pathLen>MAXPATHLEN || pathLen<6 || |
+ (strlcpy(&tPath[pathLen-5], "break", 6) != 5) ){ |
+ sqlite3_snprintf(sizeof(errmsg),errmsg,"path error (len %d)",(int)pathLen); |
+ goto end_breaklock; |
+ } |
+ /* read the conch content */ |
+ readLen = osPread(conchFile->h, buf, PROXY_MAXCONCHLEN, 0); |
+ if( readLen<PROXY_PATHINDEX ){ |
+ sqlite3_snprintf(sizeof(errmsg),errmsg,"read error (len %d)",(int)readLen); |
+ goto end_breaklock; |
+ } |
+ /* write it out to the temporary break file */ |
+ fd = robust_open(tPath, (O_RDWR|O_CREAT|O_EXCL), 0); |
+ if( fd<0 ){ |
+ sqlite3_snprintf(sizeof(errmsg), errmsg, "create failed (%d)", errno); |
+ goto end_breaklock; |
+ } |
+ if( osPwrite(fd, buf, readLen, 0) != (ssize_t)readLen ){ |
+ sqlite3_snprintf(sizeof(errmsg), errmsg, "write failed (%d)", errno); |
+ goto end_breaklock; |
+ } |
+ if( rename(tPath, cPath) ){ |
+ sqlite3_snprintf(sizeof(errmsg), errmsg, "rename failed (%d)", errno); |
+ goto end_breaklock; |
+ } |
+ rc = 0; |
+ fprintf(stderr, "broke stale lock on %s\n", cPath); |
+ robust_close(pFile, conchFile->h, __LINE__); |
+ conchFile->h = fd; |
+ conchFile->openFlags = O_RDWR | O_CREAT; |
+ |
+end_breaklock: |
+ if( rc ){ |
+ if( fd>=0 ){ |
+ osUnlink(tPath); |
+ robust_close(pFile, fd, __LINE__); |
+ } |
+ fprintf(stderr, "failed to break stale lock on %s, %s\n", cPath, errmsg); |
+ } |
+ return rc; |
+} |
+ |
+/* Take the requested lock on the conch file and break a stale lock if the |
+** host id matches. |
+*/ |
+static int proxyConchLock(unixFile *pFile, uuid_t myHostID, int lockType){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ unixFile *conchFile = pCtx->conchFile; |
+ int rc = SQLITE_OK; |
+ int nTries = 0; |
+ struct timespec conchModTime; |
+ |
+ memset(&conchModTime, 0, sizeof(conchModTime)); |
+ do { |
+ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); |
+ nTries ++; |
+ if( rc==SQLITE_BUSY ){ |
+ /* If the lock failed (busy): |
+ * 1st try: get the mod time of the conch, wait 0.5s and try again. |
+ * 2nd try: fail if the mod time changed or host id is different, wait |
+ * 10 sec and try again |
+ * 3rd try: break the lock unless the mod time has changed. |
+ */ |
+ struct stat buf; |
+ if( osFstat(conchFile->h, &buf) ){ |
+ storeLastErrno(pFile, errno); |
+ return SQLITE_IOERR_LOCK; |
+ } |
+ |
+ if( nTries==1 ){ |
+ conchModTime = buf.st_mtimespec; |
+ usleep(500000); /* wait 0.5 sec and try the lock again*/ |
+ continue; |
+ } |
+ |
+ assert( nTries>1 ); |
+ if( conchModTime.tv_sec != buf.st_mtimespec.tv_sec || |
+ conchModTime.tv_nsec != buf.st_mtimespec.tv_nsec ){ |
+ return SQLITE_BUSY; |
+ } |
+ |
+ if( nTries==2 ){ |
+ char tBuf[PROXY_MAXCONCHLEN]; |
+ int len = osPread(conchFile->h, tBuf, PROXY_MAXCONCHLEN, 0); |
+ if( len<0 ){ |
+ storeLastErrno(pFile, errno); |
+ return SQLITE_IOERR_LOCK; |
+ } |
+ if( len>PROXY_PATHINDEX && tBuf[0]==(char)PROXY_CONCHVERSION){ |
+ /* don't break the lock if the host id doesn't match */ |
+ if( 0!=memcmp(&tBuf[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN) ){ |
+ return SQLITE_BUSY; |
+ } |
+ }else{ |
+ /* don't break the lock on short read or a version mismatch */ |
+ return SQLITE_BUSY; |
+ } |
+ usleep(10000000); /* wait 10 sec and try the lock again */ |
+ continue; |
+ } |
+ |
+ assert( nTries==3 ); |
+ if( 0==proxyBreakConchLock(pFile, myHostID) ){ |
+ rc = SQLITE_OK; |
+ if( lockType==EXCLUSIVE_LOCK ){ |
+ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, SHARED_LOCK); |
+ } |
+ if( !rc ){ |
+ rc = conchFile->pMethod->xLock((sqlite3_file*)conchFile, lockType); |
+ } |
+ } |
+ } |
+ } while( rc==SQLITE_BUSY && nTries<3 ); |
+ |
+ return rc; |
+} |
+ |
+/* Takes the conch by taking a shared lock and read the contents conch, if |
+** lockPath is non-NULL, the host ID and lock file path must match. A NULL |
+** lockPath means that the lockPath in the conch file will be used if the |
+** host IDs match, or a new lock path will be generated automatically |
+** and written to the conch file. |
+*/ |
+static int proxyTakeConch(unixFile *pFile){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ |
+ if( pCtx->conchHeld!=0 ){ |
+ return SQLITE_OK; |
+ }else{ |
+ unixFile *conchFile = pCtx->conchFile; |
+ uuid_t myHostID; |
+ int pError = 0; |
+ char readBuf[PROXY_MAXCONCHLEN]; |
+ char lockPath[MAXPATHLEN]; |
+ char *tempLockPath = NULL; |
+ int rc = SQLITE_OK; |
+ int createConch = 0; |
+ int hostIdMatch = 0; |
+ int readLen = 0; |
+ int tryOldLockPath = 0; |
+ int forceNewLockPath = 0; |
+ |
+ OSTRACE(("TAKECONCH %d for %s pid=%d\n", conchFile->h, |
+ (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), |
+ osGetpid(0))); |
+ |
+ rc = proxyGetHostID(myHostID, &pError); |
+ if( (rc&0xff)==SQLITE_IOERR ){ |
+ storeLastErrno(pFile, pError); |
+ goto end_takeconch; |
+ } |
+ rc = proxyConchLock(pFile, myHostID, SHARED_LOCK); |
+ if( rc!=SQLITE_OK ){ |
+ goto end_takeconch; |
+ } |
+ /* read the existing conch file */ |
+ readLen = seekAndRead((unixFile*)conchFile, 0, readBuf, PROXY_MAXCONCHLEN); |
+ if( readLen<0 ){ |
+ /* I/O error: lastErrno set by seekAndRead */ |
+ storeLastErrno(pFile, conchFile->lastErrno); |
+ rc = SQLITE_IOERR_READ; |
+ goto end_takeconch; |
+ }else if( readLen<=(PROXY_HEADERLEN+PROXY_HOSTIDLEN) || |
+ readBuf[0]!=(char)PROXY_CONCHVERSION ){ |
+ /* a short read or version format mismatch means we need to create a new |
+ ** conch file. |
+ */ |
+ createConch = 1; |
+ } |
+ /* if the host id matches and the lock path already exists in the conch |
+ ** we'll try to use the path there, if we can't open that path, we'll |
+ ** retry with a new auto-generated path |
+ */ |
+ do { /* in case we need to try again for an :auto: named lock file */ |
+ |
+ if( !createConch && !forceNewLockPath ){ |
+ hostIdMatch = !memcmp(&readBuf[PROXY_HEADERLEN], myHostID, |
+ PROXY_HOSTIDLEN); |
+ /* if the conch has data compare the contents */ |
+ if( !pCtx->lockProxyPath ){ |
+ /* for auto-named local lock file, just check the host ID and we'll |
+ ** use the local lock file path that's already in there |
+ */ |
+ if( hostIdMatch ){ |
+ size_t pathLen = (readLen - PROXY_PATHINDEX); |
+ |
+ if( pathLen>=MAXPATHLEN ){ |
+ pathLen=MAXPATHLEN-1; |
+ } |
+ memcpy(lockPath, &readBuf[PROXY_PATHINDEX], pathLen); |
+ lockPath[pathLen] = 0; |
+ tempLockPath = lockPath; |
+ tryOldLockPath = 1; |
+ /* create a copy of the lock path if the conch is taken */ |
+ goto end_takeconch; |
+ } |
+ }else if( hostIdMatch |
+ && !strncmp(pCtx->lockProxyPath, &readBuf[PROXY_PATHINDEX], |
+ readLen-PROXY_PATHINDEX) |
+ ){ |
+ /* conch host and lock path match */ |
+ goto end_takeconch; |
+ } |
+ } |
+ |
+ /* if the conch isn't writable and doesn't match, we can't take it */ |
+ if( (conchFile->openFlags&O_RDWR) == 0 ){ |
+ rc = SQLITE_BUSY; |
+ goto end_takeconch; |
+ } |
+ |
+ /* either the conch didn't match or we need to create a new one */ |
+ if( !pCtx->lockProxyPath ){ |
+ proxyGetLockPath(pCtx->dbPath, lockPath, MAXPATHLEN); |
+ tempLockPath = lockPath; |
+ /* create a copy of the lock path _only_ if the conch is taken */ |
+ } |
+ |
+ /* update conch with host and path (this will fail if other process |
+ ** has a shared lock already), if the host id matches, use the big |
+ ** stick. |
+ */ |
+ futimes(conchFile->h, NULL); |
+ if( hostIdMatch && !createConch ){ |
+ if( conchFile->pInode && conchFile->pInode->nShared>1 ){ |
+ /* We are trying for an exclusive lock but another thread in this |
+ ** same process is still holding a shared lock. */ |
+ rc = SQLITE_BUSY; |
+ } else { |
+ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); |
+ } |
+ }else{ |
+ rc = proxyConchLock(pFile, myHostID, EXCLUSIVE_LOCK); |
+ } |
+ if( rc==SQLITE_OK ){ |
+ char writeBuffer[PROXY_MAXCONCHLEN]; |
+ int writeSize = 0; |
+ |
+ writeBuffer[0] = (char)PROXY_CONCHVERSION; |
+ memcpy(&writeBuffer[PROXY_HEADERLEN], myHostID, PROXY_HOSTIDLEN); |
+ if( pCtx->lockProxyPath!=NULL ){ |
+ strlcpy(&writeBuffer[PROXY_PATHINDEX], pCtx->lockProxyPath, |
+ MAXPATHLEN); |
+ }else{ |
+ strlcpy(&writeBuffer[PROXY_PATHINDEX], tempLockPath, MAXPATHLEN); |
+ } |
+ writeSize = PROXY_PATHINDEX + strlen(&writeBuffer[PROXY_PATHINDEX]); |
+ robust_ftruncate(conchFile->h, writeSize); |
+ rc = unixWrite((sqlite3_file *)conchFile, writeBuffer, writeSize, 0); |
+ fsync(conchFile->h); |
+ /* If we created a new conch file (not just updated the contents of a |
+ ** valid conch file), try to match the permissions of the database |
+ */ |
+ if( rc==SQLITE_OK && createConch ){ |
+ struct stat buf; |
+ int err = osFstat(pFile->h, &buf); |
+ if( err==0 ){ |
+ mode_t cmode = buf.st_mode&(S_IRUSR|S_IWUSR | S_IRGRP|S_IWGRP | |
+ S_IROTH|S_IWOTH); |
+ /* try to match the database file R/W permissions, ignore failure */ |
+#ifndef SQLITE_PROXY_DEBUG |
+ osFchmod(conchFile->h, cmode); |
+#else |
+ do{ |
+ rc = osFchmod(conchFile->h, cmode); |
+ }while( rc==(-1) && errno==EINTR ); |
+ if( rc!=0 ){ |
+ int code = errno; |
+ fprintf(stderr, "fchmod %o FAILED with %d %s\n", |
+ cmode, code, strerror(code)); |
+ } else { |
+ fprintf(stderr, "fchmod %o SUCCEDED\n",cmode); |
+ } |
+ }else{ |
+ int code = errno; |
+ fprintf(stderr, "STAT FAILED[%d] with %d %s\n", |
+ err, code, strerror(code)); |
+#endif |
+ } |
+ } |
+ } |
+ conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, SHARED_LOCK); |
+ |
+ end_takeconch: |
+ OSTRACE(("TRANSPROXY: CLOSE %d\n", pFile->h)); |
+ if( rc==SQLITE_OK && pFile->openFlags ){ |
+ int fd; |
+ if( pFile->h>=0 ){ |
+ robust_close(pFile, pFile->h, __LINE__); |
+ } |
+ pFile->h = -1; |
+ fd = robust_open(pCtx->dbPath, pFile->openFlags, 0); |
+ OSTRACE(("TRANSPROXY: OPEN %d\n", fd)); |
+ if( fd>=0 ){ |
+ pFile->h = fd; |
+ }else{ |
+ rc=SQLITE_CANTOPEN_BKPT; /* SQLITE_BUSY? proxyTakeConch called |
+ during locking */ |
+ } |
+ } |
+ if( rc==SQLITE_OK && !pCtx->lockProxy ){ |
+ char *path = tempLockPath ? tempLockPath : pCtx->lockProxyPath; |
+ rc = proxyCreateUnixFile(path, &pCtx->lockProxy, 1); |
+ if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM && tryOldLockPath ){ |
+ /* we couldn't create the proxy lock file with the old lock file path |
+ ** so try again via auto-naming |
+ */ |
+ forceNewLockPath = 1; |
+ tryOldLockPath = 0; |
+ continue; /* go back to the do {} while start point, try again */ |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ /* Need to make a copy of path if we extracted the value |
+ ** from the conch file or the path was allocated on the stack |
+ */ |
+ if( tempLockPath ){ |
+ pCtx->lockProxyPath = sqlite3DbStrDup(0, tempLockPath); |
+ if( !pCtx->lockProxyPath ){ |
+ rc = SQLITE_NOMEM; |
+ } |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ pCtx->conchHeld = 1; |
+ |
+ if( pCtx->lockProxy->pMethod == &afpIoMethods ){ |
+ afpLockingContext *afpCtx; |
+ afpCtx = (afpLockingContext *)pCtx->lockProxy->lockingContext; |
+ afpCtx->dbPath = pCtx->lockProxyPath; |
+ } |
+ } else { |
+ conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); |
+ } |
+ OSTRACE(("TAKECONCH %d %s\n", conchFile->h, |
+ rc==SQLITE_OK?"ok":"failed")); |
+ return rc; |
+ } while (1); /* in case we need to retry the :auto: lock file - |
+ ** we should never get here except via the 'continue' call. */ |
+ } |
+} |
+ |
+/* |
+** If pFile holds a lock on a conch file, then release that lock. |
+*/ |
+static int proxyReleaseConch(unixFile *pFile){ |
+ int rc = SQLITE_OK; /* Subroutine return code */ |
+ proxyLockingContext *pCtx; /* The locking context for the proxy lock */ |
+ unixFile *conchFile; /* Name of the conch file */ |
+ |
+ pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ conchFile = pCtx->conchFile; |
+ OSTRACE(("RELEASECONCH %d for %s pid=%d\n", conchFile->h, |
+ (pCtx->lockProxyPath ? pCtx->lockProxyPath : ":auto:"), |
+ osGetpid(0))); |
+ if( pCtx->conchHeld>0 ){ |
+ rc = conchFile->pMethod->xUnlock((sqlite3_file*)conchFile, NO_LOCK); |
+ } |
+ pCtx->conchHeld = 0; |
+ OSTRACE(("RELEASECONCH %d %s\n", conchFile->h, |
+ (rc==SQLITE_OK ? "ok" : "failed"))); |
+ return rc; |
+} |
+ |
+/* |
+** Given the name of a database file, compute the name of its conch file. |
+** Store the conch filename in memory obtained from sqlite3_malloc64(). |
+** Make *pConchPath point to the new name. Return SQLITE_OK on success |
+** or SQLITE_NOMEM if unable to obtain memory. |
+** |
+** The caller is responsible for ensuring that the allocated memory |
+** space is eventually freed. |
+** |
+** *pConchPath is set to NULL if a memory allocation error occurs. |
+*/ |
+static int proxyCreateConchPathname(char *dbPath, char **pConchPath){ |
+ int i; /* Loop counter */ |
+ int len = (int)strlen(dbPath); /* Length of database filename - dbPath */ |
+ char *conchPath; /* buffer in which to construct conch name */ |
+ |
+ /* Allocate space for the conch filename and initialize the name to |
+ ** the name of the original database file. */ |
+ *pConchPath = conchPath = (char *)sqlite3_malloc64(len + 8); |
+ if( conchPath==0 ){ |
+ return SQLITE_NOMEM; |
+ } |
+ memcpy(conchPath, dbPath, len+1); |
+ |
+ /* now insert a "." before the last / character */ |
+ for( i=(len-1); i>=0; i-- ){ |
+ if( conchPath[i]=='/' ){ |
+ i++; |
+ break; |
+ } |
+ } |
+ conchPath[i]='.'; |
+ while ( i<len ){ |
+ conchPath[i+1]=dbPath[i]; |
+ i++; |
+ } |
+ |
+ /* append the "-conch" suffix to the file */ |
+ memcpy(&conchPath[i+1], "-conch", 7); |
+ assert( (int)strlen(conchPath) == len+7 ); |
+ |
+ return SQLITE_OK; |
+} |
+ |
+ |
+/* Takes a fully configured proxy locking-style unix file and switches |
+** the local lock file path |
+*/ |
+static int switchLockProxyPath(unixFile *pFile, const char *path) { |
+ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; |
+ char *oldPath = pCtx->lockProxyPath; |
+ int rc = SQLITE_OK; |
+ |
+ if( pFile->eFileLock!=NO_LOCK ){ |
+ return SQLITE_BUSY; |
+ } |
+ |
+ /* nothing to do if the path is NULL, :auto: or matches the existing path */ |
+ if( !path || path[0]=='\0' || !strcmp(path, ":auto:") || |
+ (oldPath && !strncmp(oldPath, path, MAXPATHLEN)) ){ |
+ return SQLITE_OK; |
+ }else{ |
+ unixFile *lockProxy = pCtx->lockProxy; |
+ pCtx->lockProxy=NULL; |
+ pCtx->conchHeld = 0; |
+ if( lockProxy!=NULL ){ |
+ rc=lockProxy->pMethod->xClose((sqlite3_file *)lockProxy); |
+ if( rc ) return rc; |
+ sqlite3_free(lockProxy); |
+ } |
+ sqlite3_free(oldPath); |
+ pCtx->lockProxyPath = sqlite3DbStrDup(0, path); |
+ } |
+ |
+ return rc; |
+} |
+ |
+/* |
+** pFile is a file that has been opened by a prior xOpen call. dbPath |
+** is a string buffer at least MAXPATHLEN+1 characters in size. |
+** |
+** This routine find the filename associated with pFile and writes it |
+** int dbPath. |
+*/ |
+static int proxyGetDbPathForUnixFile(unixFile *pFile, char *dbPath){ |
+#if defined(__APPLE__) |
+ if( pFile->pMethod == &afpIoMethods ){ |
+ /* afp style keeps a reference to the db path in the filePath field |
+ ** of the struct */ |
+ assert( (int)strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); |
+ strlcpy(dbPath, ((afpLockingContext *)pFile->lockingContext)->dbPath, |
+ MAXPATHLEN); |
+ } else |
+#endif |
+ if( pFile->pMethod == &dotlockIoMethods ){ |
+ /* dot lock style uses the locking context to store the dot lock |
+ ** file path */ |
+ int len = strlen((char *)pFile->lockingContext) - strlen(DOTLOCK_SUFFIX); |
+ memcpy(dbPath, (char *)pFile->lockingContext, len + 1); |
+ }else{ |
+ /* all other styles use the locking context to store the db file path */ |
+ assert( strlen((char*)pFile->lockingContext)<=MAXPATHLEN ); |
+ strlcpy(dbPath, (char *)pFile->lockingContext, MAXPATHLEN); |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Takes an already filled in unix file and alters it so all file locking |
+** will be performed on the local proxy lock file. The following fields |
+** are preserved in the locking context so that they can be restored and |
+** the unix structure properly cleaned up at close time: |
+** ->lockingContext |
+** ->pMethod |
+*/ |
+static int proxyTransformUnixFile(unixFile *pFile, const char *path) { |
+ proxyLockingContext *pCtx; |
+ char dbPath[MAXPATHLEN+1]; /* Name of the database file */ |
+ char *lockPath=NULL; |
+ int rc = SQLITE_OK; |
+ |
+ if( pFile->eFileLock!=NO_LOCK ){ |
+ return SQLITE_BUSY; |
+ } |
+ proxyGetDbPathForUnixFile(pFile, dbPath); |
+ if( !path || path[0]=='\0' || !strcmp(path, ":auto:") ){ |
+ lockPath=NULL; |
+ }else{ |
+ lockPath=(char *)path; |
+ } |
+ |
+ OSTRACE(("TRANSPROXY %d for %s pid=%d\n", pFile->h, |
+ (lockPath ? lockPath : ":auto:"), osGetpid(0))); |
+ |
+ pCtx = sqlite3_malloc64( sizeof(*pCtx) ); |
+ if( pCtx==0 ){ |
+ return SQLITE_NOMEM; |
+ } |
+ memset(pCtx, 0, sizeof(*pCtx)); |
+ |
+ rc = proxyCreateConchPathname(dbPath, &pCtx->conchFilePath); |
+ if( rc==SQLITE_OK ){ |
+ rc = proxyCreateUnixFile(pCtx->conchFilePath, &pCtx->conchFile, 0); |
+ if( rc==SQLITE_CANTOPEN && ((pFile->openFlags&O_RDWR) == 0) ){ |
+ /* if (a) the open flags are not O_RDWR, (b) the conch isn't there, and |
+ ** (c) the file system is read-only, then enable no-locking access. |
+ ** Ugh, since O_RDONLY==0x0000 we test for !O_RDWR since unixOpen asserts |
+ ** that openFlags will have only one of O_RDONLY or O_RDWR. |
+ */ |
+ struct statfs fsInfo; |
+ struct stat conchInfo; |
+ int goLockless = 0; |
+ |
+ if( osStat(pCtx->conchFilePath, &conchInfo) == -1 ) { |
+ int err = errno; |
+ if( (err==ENOENT) && (statfs(dbPath, &fsInfo) != -1) ){ |
+ goLockless = (fsInfo.f_flags&MNT_RDONLY) == MNT_RDONLY; |
+ } |
+ } |
+ if( goLockless ){ |
+ pCtx->conchHeld = -1; /* read only FS/ lockless */ |
+ rc = SQLITE_OK; |
+ } |
+ } |
+ } |
+ if( rc==SQLITE_OK && lockPath ){ |
+ pCtx->lockProxyPath = sqlite3DbStrDup(0, lockPath); |
+ } |
+ |
+ if( rc==SQLITE_OK ){ |
+ pCtx->dbPath = sqlite3DbStrDup(0, dbPath); |
+ if( pCtx->dbPath==NULL ){ |
+ rc = SQLITE_NOMEM; |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ /* all memory is allocated, proxys are created and assigned, |
+ ** switch the locking context and pMethod then return. |
+ */ |
+ pCtx->oldLockingContext = pFile->lockingContext; |
+ pFile->lockingContext = pCtx; |
+ pCtx->pOldMethod = pFile->pMethod; |
+ pFile->pMethod = &proxyIoMethods; |
+ }else{ |
+ if( pCtx->conchFile ){ |
+ pCtx->conchFile->pMethod->xClose((sqlite3_file *)pCtx->conchFile); |
+ sqlite3_free(pCtx->conchFile); |
+ } |
+ sqlite3DbFree(0, pCtx->lockProxyPath); |
+ sqlite3_free(pCtx->conchFilePath); |
+ sqlite3_free(pCtx); |
+ } |
+ OSTRACE(("TRANSPROXY %d %s\n", pFile->h, |
+ (rc==SQLITE_OK ? "ok" : "failed"))); |
+ return rc; |
+} |
+ |
+ |
+/* |
+** This routine handles sqlite3_file_control() calls that are specific |
+** to proxy locking. |
+*/ |
+static int proxyFileControl(sqlite3_file *id, int op, void *pArg){ |
+ switch( op ){ |
+ case SQLITE_FCNTL_GET_LOCKPROXYFILE: { |
+ unixFile *pFile = (unixFile*)id; |
+ if( pFile->pMethod == &proxyIoMethods ){ |
+ proxyLockingContext *pCtx = (proxyLockingContext*)pFile->lockingContext; |
+ proxyTakeConch(pFile); |
+ if( pCtx->lockProxyPath ){ |
+ *(const char **)pArg = pCtx->lockProxyPath; |
+ }else{ |
+ *(const char **)pArg = ":auto: (not held)"; |
+ } |
+ } else { |
+ *(const char **)pArg = NULL; |
+ } |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_SET_LOCKPROXYFILE: { |
+ unixFile *pFile = (unixFile*)id; |
+ int rc = SQLITE_OK; |
+ int isProxyStyle = (pFile->pMethod == &proxyIoMethods); |
+ if( pArg==NULL || (const char *)pArg==0 ){ |
+ if( isProxyStyle ){ |
+ /* turn off proxy locking - not supported. If support is added for |
+ ** switching proxy locking mode off then it will need to fail if |
+ ** the journal mode is WAL mode. |
+ */ |
+ rc = SQLITE_ERROR /*SQLITE_PROTOCOL? SQLITE_MISUSE?*/; |
+ }else{ |
+ /* turn off proxy locking - already off - NOOP */ |
+ rc = SQLITE_OK; |
+ } |
+ }else{ |
+ const char *proxyPath = (const char *)pArg; |
+ if( isProxyStyle ){ |
+ proxyLockingContext *pCtx = |
+ (proxyLockingContext*)pFile->lockingContext; |
+ if( !strcmp(pArg, ":auto:") |
+ || (pCtx->lockProxyPath && |
+ !strncmp(pCtx->lockProxyPath, proxyPath, MAXPATHLEN)) |
+ ){ |
+ rc = SQLITE_OK; |
+ }else{ |
+ rc = switchLockProxyPath(pFile, proxyPath); |
+ } |
+ }else{ |
+ /* turn on proxy file locking */ |
+ rc = proxyTransformUnixFile(pFile, proxyPath); |
+ } |
+ } |
+ return rc; |
+ } |
+ default: { |
+ assert( 0 ); /* The call assures that only valid opcodes are sent */ |
+ } |
+ } |
+ /*NOTREACHED*/ |
+ return SQLITE_ERROR; |
+} |
+ |
+/* |
+** Within this division (the proxying locking implementation) the procedures |
+** above this point are all utilities. The lock-related methods of the |
+** proxy-locking sqlite3_io_method object follow. |
+*/ |
+ |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, set *pResOut |
+** to a non-zero value otherwise *pResOut is set to zero. The return value |
+** is set to SQLITE_OK unless an I/O error occurs during lock checking. |
+*/ |
+static int proxyCheckReservedLock(sqlite3_file *id, int *pResOut) { |
+ unixFile *pFile = (unixFile*)id; |
+ int rc = proxyTakeConch(pFile); |
+ if( rc==SQLITE_OK ){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ if( pCtx->conchHeld>0 ){ |
+ unixFile *proxy = pCtx->lockProxy; |
+ return proxy->pMethod->xCheckReservedLock((sqlite3_file*)proxy, pResOut); |
+ }else{ /* conchHeld < 0 is lockless */ |
+ pResOut=0; |
+ } |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter eFileLock - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** This routine will only increase a lock. Use the sqlite3OsUnlock() |
+** routine to lower a locking level. |
+*/ |
+static int proxyLock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ int rc = proxyTakeConch(pFile); |
+ if( rc==SQLITE_OK ){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ if( pCtx->conchHeld>0 ){ |
+ unixFile *proxy = pCtx->lockProxy; |
+ rc = proxy->pMethod->xLock((sqlite3_file*)proxy, eFileLock); |
+ pFile->eFileLock = proxy->eFileLock; |
+ }else{ |
+ /* conchHeld < 0 is lockless */ |
+ } |
+ } |
+ return rc; |
+} |
+ |
+ |
+/* |
+** Lower the locking level on file descriptor pFile to eFileLock. eFileLock |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+*/ |
+static int proxyUnlock(sqlite3_file *id, int eFileLock) { |
+ unixFile *pFile = (unixFile*)id; |
+ int rc = proxyTakeConch(pFile); |
+ if( rc==SQLITE_OK ){ |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ if( pCtx->conchHeld>0 ){ |
+ unixFile *proxy = pCtx->lockProxy; |
+ rc = proxy->pMethod->xUnlock((sqlite3_file*)proxy, eFileLock); |
+ pFile->eFileLock = proxy->eFileLock; |
+ }else{ |
+ /* conchHeld < 0 is lockless */ |
+ } |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Close a file that uses proxy locks. |
+*/ |
+static int proxyClose(sqlite3_file *id) { |
+ if( ALWAYS(id) ){ |
+ unixFile *pFile = (unixFile*)id; |
+ proxyLockingContext *pCtx = (proxyLockingContext *)pFile->lockingContext; |
+ unixFile *lockProxy = pCtx->lockProxy; |
+ unixFile *conchFile = pCtx->conchFile; |
+ int rc = SQLITE_OK; |
+ |
+ if( lockProxy ){ |
+ rc = lockProxy->pMethod->xUnlock((sqlite3_file*)lockProxy, NO_LOCK); |
+ if( rc ) return rc; |
+ rc = lockProxy->pMethod->xClose((sqlite3_file*)lockProxy); |
+ if( rc ) return rc; |
+ sqlite3_free(lockProxy); |
+ pCtx->lockProxy = 0; |
+ } |
+ if( conchFile ){ |
+ if( pCtx->conchHeld ){ |
+ rc = proxyReleaseConch(pFile); |
+ if( rc ) return rc; |
+ } |
+ rc = conchFile->pMethod->xClose((sqlite3_file*)conchFile); |
+ if( rc ) return rc; |
+ sqlite3_free(conchFile); |
+ } |
+ sqlite3DbFree(0, pCtx->lockProxyPath); |
+ sqlite3_free(pCtx->conchFilePath); |
+ sqlite3DbFree(0, pCtx->dbPath); |
+ /* restore the original locking context and pMethod then close it */ |
+ pFile->lockingContext = pCtx->oldLockingContext; |
+ pFile->pMethod = pCtx->pOldMethod; |
+ sqlite3_free(pCtx); |
+ return pFile->pMethod->xClose(id); |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+ |
+ |
+#endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ |
+/* |
+** The proxy locking style is intended for use with AFP filesystems. |
+** And since AFP is only supported on MacOSX, the proxy locking is also |
+** restricted to MacOSX. |
+** |
+** |
+******************* End of the proxy lock implementation ********************** |
+******************************************************************************/ |
+ |
+/* |
+** Initialize the operating system interface. |
+** |
+** This routine registers all VFS implementations for unix-like operating |
+** systems. This routine, and the sqlite3_os_end() routine that follows, |
+** should be the only routines in this file that are visible from other |
+** files. |
+** |
+** This routine is called once during SQLite initialization and by a |
+** single thread. The memory allocation and mutex subsystems have not |
+** necessarily been initialized when this routine is called, and so they |
+** should not be used. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ |
+ /* |
+ ** The following macro defines an initializer for an sqlite3_vfs object. |
+ ** The name of the VFS is NAME. The pAppData is a pointer to a pointer |
+ ** to the "finder" function. (pAppData is a pointer to a pointer because |
+ ** silly C90 rules prohibit a void* from being cast to a function pointer |
+ ** and so we have to go through the intermediate pointer to avoid problems |
+ ** when compiling with -pedantic-errors on GCC.) |
+ ** |
+ ** The FINDER parameter to this macro is the name of the pointer to the |
+ ** finder-function. The finder-function returns a pointer to the |
+ ** sqlite_io_methods object that implements the desired locking |
+ ** behaviors. See the division above that contains the IOMETHODS |
+ ** macro for addition information on finder-functions. |
+ ** |
+ ** Most finders simply return a pointer to a fixed sqlite3_io_methods |
+ ** object. But the "autolockIoFinder" available on MacOSX does a little |
+ ** more than that; it looks at the filesystem type that hosts the |
+ ** database file and tries to choose an locking method appropriate for |
+ ** that filesystem time. |
+ */ |
+ #define UNIXVFS(VFSNAME, FINDER) { \ |
+ 3, /* iVersion */ \ |
+ sizeof(unixFile), /* szOsFile */ \ |
+ MAX_PATHNAME, /* mxPathname */ \ |
+ 0, /* pNext */ \ |
+ VFSNAME, /* zName */ \ |
+ (void*)&FINDER, /* pAppData */ \ |
+ unixOpen, /* xOpen */ \ |
+ unixDelete, /* xDelete */ \ |
+ unixAccess, /* xAccess */ \ |
+ unixFullPathname, /* xFullPathname */ \ |
+ unixDlOpen, /* xDlOpen */ \ |
+ unixDlError, /* xDlError */ \ |
+ unixDlSym, /* xDlSym */ \ |
+ unixDlClose, /* xDlClose */ \ |
+ unixRandomness, /* xRandomness */ \ |
+ unixSleep, /* xSleep */ \ |
+ unixCurrentTime, /* xCurrentTime */ \ |
+ unixGetLastError, /* xGetLastError */ \ |
+ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ |
+ unixSetSystemCall, /* xSetSystemCall */ \ |
+ unixGetSystemCall, /* xGetSystemCall */ \ |
+ unixNextSystemCall, /* xNextSystemCall */ \ |
+ } |
+ |
+ /* |
+ ** All default VFSes for unix are contained in the following array. |
+ ** |
+ ** Note that the sqlite3_vfs.pNext field of the VFS object is modified |
+ ** by the SQLite core when the VFS is registered. So the following |
+ ** array cannot be const. |
+ */ |
+ static sqlite3_vfs aVfs[] = { |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+ UNIXVFS("unix", autolockIoFinder ), |
+#elif OS_VXWORKS |
+ UNIXVFS("unix", vxworksIoFinder ), |
+#else |
+ UNIXVFS("unix", posixIoFinder ), |
+#endif |
+ UNIXVFS("unix-none", nolockIoFinder ), |
+ UNIXVFS("unix-dotfile", dotlockIoFinder ), |
+ UNIXVFS("unix-excl", posixIoFinder ), |
+#if OS_VXWORKS |
+ UNIXVFS("unix-namedsem", semIoFinder ), |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE || OS_VXWORKS |
+ UNIXVFS("unix-posix", posixIoFinder ), |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE |
+ UNIXVFS("unix-flock", flockIoFinder ), |
+#endif |
+#if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) |
+ UNIXVFS("unix-afp", afpIoFinder ), |
+ UNIXVFS("unix-nfs", nfsIoFinder ), |
+ UNIXVFS("unix-proxy", proxyIoFinder ), |
+#endif |
+ }; |
+ unsigned int i; /* Loop counter */ |
+ |
+ /* Double-check that the aSyscall[] array has been constructed |
+ ** correctly. See ticket [bb3a86e890c8e96ab] */ |
+ assert( ArraySize(aSyscall)==27 ); |
+ |
+ /* Register all VFSes defined in the aVfs[] array */ |
+ for(i=0; i<(sizeof(aVfs)/sizeof(sqlite3_vfs)); i++){ |
+ sqlite3_vfs_register(&aVfs[i], i==0); |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Shutdown the operating system interface. |
+** |
+** Some operating systems might need to do some cleanup in this routine, |
+** to release dynamically allocated objects. But not on unix. |
+** This routine is a no-op for unix. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ |
+ return SQLITE_OK; |
+} |
+ |
+#endif /* SQLITE_OS_UNIX */ |
+ |
+/************** End of os_unix.c *********************************************/ |
+/************** Begin file os_win.c ******************************************/ |
+/* |
+** 2004 May 22 |
+** |
+** 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 code that is specific to Windows. |
+*/ |
+/* #include "sqliteInt.h" */ |
+#if SQLITE_OS_WIN /* This file is used for Windows only */ |
+ |
+/* |
+** Include code that is common to all os_*.c files |
+*/ |
+/************** Include os_common.h in the middle of os_win.c ****************/ |
+/************** Begin file os_common.h ***************************************/ |
+/* |
+** 2004 May 22 |
+** |
+** 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 macros and a little bit of code that is common to |
+** all of the platform-specific files (os_*.c) and is #included into those |
+** files. |
+** |
+** This file should be #included by the os_*.c files only. It is not a |
+** general purpose header file. |
+*/ |
+#ifndef _OS_COMMON_H_ |
+#define _OS_COMMON_H_ |
+ |
+/* |
+** At least two bugs have slipped in because we changed the MEMORY_DEBUG |
+** macro to SQLITE_DEBUG and some older makefiles have not yet made the |
+** switch. The following code should catch this problem at compile-time. |
+*/ |
+#ifdef MEMORY_DEBUG |
+# error "The MEMORY_DEBUG macro is obsolete. Use SQLITE_DEBUG instead." |
+#endif |
+ |
+/* |
+** Macros for performance tracing. Normally turned off. Only works |
+** on i486 hardware. |
+*/ |
+#ifdef SQLITE_PERFORMANCE_TRACE |
+ |
+/* |
+** hwtime.h contains inline assembler code for implementing |
+** high-performance timing routines. |
+*/ |
+/************** Include hwtime.h in the middle of os_common.h ****************/ |
+/************** Begin file hwtime.h ******************************************/ |
+/* |
+** 2008 May 27 |
+** |
+** 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 inline asm code for retrieving "high-performance" |
+** counters for x86 class CPUs. |
+*/ |
+#ifndef _HWTIME_H_ |
+#define _HWTIME_H_ |
+ |
+/* |
+** The following routine only works on pentium-class (or newer) processors. |
+** It uses the RDTSC opcode to read the cycle count value out of the |
+** processor and returns that value. This can be used for high-res |
+** profiling. |
+*/ |
+#if (defined(__GNUC__) || defined(_MSC_VER)) && \ |
+ (defined(i386) || defined(__i386__) || defined(_M_IX86)) |
+ |
+ #if defined(__GNUC__) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned int lo, hi; |
+ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); |
+ return (sqlite_uint64)hi << 32 | lo; |
+ } |
+ |
+ #elif defined(_MSC_VER) |
+ |
+ __declspec(naked) __inline sqlite_uint64 __cdecl sqlite3Hwtime(void){ |
+ __asm { |
+ rdtsc |
+ ret ; return value at EDX:EAX |
+ } |
+ } |
+ |
+ #endif |
+ |
+#elif (defined(__GNUC__) && defined(__x86_64__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long val; |
+ __asm__ __volatile__ ("rdtsc" : "=A" (val)); |
+ return val; |
+ } |
+ |
+#elif (defined(__GNUC__) && defined(__ppc__)) |
+ |
+ __inline__ sqlite_uint64 sqlite3Hwtime(void){ |
+ unsigned long long retval; |
+ unsigned long junk; |
+ __asm__ __volatile__ ("\n\ |
+ 1: mftbu %1\n\ |
+ mftb %L0\n\ |
+ mftbu %0\n\ |
+ cmpw %0,%1\n\ |
+ bne 1b" |
+ : "=r" (retval), "=r" (junk)); |
+ return retval; |
+ } |
+ |
+#else |
+ |
+ #error Need implementation of sqlite3Hwtime() for your platform. |
+ |
+ /* |
+ ** To compile without implementing sqlite3Hwtime() for your platform, |
+ ** you can remove the above #error and use the following |
+ ** stub function. You will lose timing support for many |
+ ** of the debugging and testing utilities, but it should at |
+ ** least compile and run. |
+ */ |
+SQLITE_PRIVATE sqlite_uint64 sqlite3Hwtime(void){ return ((sqlite_uint64)0); } |
+ |
+#endif |
+ |
+#endif /* !defined(_HWTIME_H_) */ |
+ |
+/************** End of hwtime.h **********************************************/ |
+/************** Continuing where we left off in os_common.h ******************/ |
+ |
+static sqlite_uint64 g_start; |
+static sqlite_uint64 g_elapsed; |
+#define TIMER_START g_start=sqlite3Hwtime() |
+#define TIMER_END g_elapsed=sqlite3Hwtime()-g_start |
+#define TIMER_ELAPSED g_elapsed |
+#else |
+#define TIMER_START |
+#define TIMER_END |
+#define TIMER_ELAPSED ((sqlite_uint64)0) |
+#endif |
+ |
+/* |
+** If we compile with the SQLITE_TEST macro set, then the following block |
+** of code will give us the ability to simulate a disk I/O error. This |
+** is used for testing the I/O recovery logic. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_io_error_hit = 0; /* Total number of I/O Errors */ |
+SQLITE_API int sqlite3_io_error_hardhit = 0; /* Number of non-benign errors */ |
+SQLITE_API int sqlite3_io_error_pending = 0; /* Count down to first I/O error */ |
+SQLITE_API int sqlite3_io_error_persist = 0; /* True if I/O errors persist */ |
+SQLITE_API int sqlite3_io_error_benign = 0; /* True if errors are benign */ |
+SQLITE_API int sqlite3_diskfull_pending = 0; |
+SQLITE_API int sqlite3_diskfull = 0; |
+#define SimulateIOErrorBenign(X) sqlite3_io_error_benign=(X) |
+#define SimulateIOError(CODE) \ |
+ if( (sqlite3_io_error_persist && sqlite3_io_error_hit) \ |
+ || sqlite3_io_error_pending-- == 1 ) \ |
+ { local_ioerr(); CODE; } |
+static void local_ioerr(){ |
+ IOTRACE(("IOERR\n")); |
+ sqlite3_io_error_hit++; |
+ if( !sqlite3_io_error_benign ) sqlite3_io_error_hardhit++; |
+} |
+#define SimulateDiskfullError(CODE) \ |
+ if( sqlite3_diskfull_pending ){ \ |
+ if( sqlite3_diskfull_pending == 1 ){ \ |
+ local_ioerr(); \ |
+ sqlite3_diskfull = 1; \ |
+ sqlite3_io_error_hit = 1; \ |
+ CODE; \ |
+ }else{ \ |
+ sqlite3_diskfull_pending--; \ |
+ } \ |
+ } |
+#else |
+#define SimulateIOErrorBenign(X) |
+#define SimulateIOError(A) |
+#define SimulateDiskfullError(A) |
+#endif |
+ |
+/* |
+** When testing, keep a count of the number of open files. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_open_file_count = 0; |
+#define OpenCounter(X) sqlite3_open_file_count+=(X) |
+#else |
+#define OpenCounter(X) |
+#endif |
+ |
+#endif /* !defined(_OS_COMMON_H_) */ |
+ |
+/************** End of os_common.h *******************************************/ |
+/************** Continuing where we left off in os_win.c *********************/ |
+ |
+/* |
+** Include the header file for the Windows VFS. |
+*/ |
+/* #include "os_win.h" */ |
+ |
+/* |
+** Compiling and using WAL mode requires several APIs that are only |
+** available in Windows platforms based on the NT kernel. |
+*/ |
+#if !SQLITE_OS_WINNT && !defined(SQLITE_OMIT_WAL) |
+# error "WAL mode requires support from the Windows NT kernel, compile\ |
+ with SQLITE_OMIT_WAL." |
+#endif |
+ |
+#if !SQLITE_OS_WINNT && SQLITE_MAX_MMAP_SIZE>0 |
+# error "Memory mapped files require support from the Windows NT kernel,\ |
+ compile with SQLITE_MAX_MMAP_SIZE=0." |
+#endif |
+ |
+/* |
+** Are most of the Win32 ANSI APIs available (i.e. with certain exceptions |
+** based on the sub-platform)? |
+*/ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(SQLITE_WIN32_NO_ANSI) |
+# define SQLITE_WIN32_HAS_ANSI |
+#endif |
+ |
+/* |
+** Are most of the Win32 Unicode APIs available (i.e. with certain exceptions |
+** based on the sub-platform)? |
+*/ |
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINNT || SQLITE_OS_WINRT) && \ |
+ !defined(SQLITE_WIN32_NO_WIDE) |
+# define SQLITE_WIN32_HAS_WIDE |
+#endif |
+ |
+/* |
+** Make sure at least one set of Win32 APIs is available. |
+*/ |
+#if !defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_WIN32_HAS_WIDE) |
+# error "At least one of SQLITE_WIN32_HAS_ANSI and SQLITE_WIN32_HAS_WIDE\ |
+ must be defined." |
+#endif |
+ |
+/* |
+** Define the required Windows SDK version constants if they are not |
+** already available. |
+*/ |
+#ifndef NTDDI_WIN8 |
+# define NTDDI_WIN8 0x06020000 |
+#endif |
+ |
+#ifndef NTDDI_WINBLUE |
+# define NTDDI_WINBLUE 0x06030000 |
+#endif |
+ |
+/* |
+** Check to see if the GetVersionEx[AW] functions are deprecated on the |
+** target system. GetVersionEx was first deprecated in Win8.1. |
+*/ |
+#ifndef SQLITE_WIN32_GETVERSIONEX |
+# if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WINBLUE |
+# define SQLITE_WIN32_GETVERSIONEX 0 /* GetVersionEx() is deprecated */ |
+# else |
+# define SQLITE_WIN32_GETVERSIONEX 1 /* GetVersionEx() is current */ |
+# endif |
+#endif |
+ |
+/* |
+** This constant should already be defined (in the "WinDef.h" SDK file). |
+*/ |
+#ifndef MAX_PATH |
+# define MAX_PATH (260) |
+#endif |
+ |
+/* |
+** Maximum pathname length (in chars) for Win32. This should normally be |
+** MAX_PATH. |
+*/ |
+#ifndef SQLITE_WIN32_MAX_PATH_CHARS |
+# define SQLITE_WIN32_MAX_PATH_CHARS (MAX_PATH) |
+#endif |
+ |
+/* |
+** This constant should already be defined (in the "WinNT.h" SDK file). |
+*/ |
+#ifndef UNICODE_STRING_MAX_CHARS |
+# define UNICODE_STRING_MAX_CHARS (32767) |
+#endif |
+ |
+/* |
+** Maximum pathname length (in chars) for WinNT. This should normally be |
+** UNICODE_STRING_MAX_CHARS. |
+*/ |
+#ifndef SQLITE_WINNT_MAX_PATH_CHARS |
+# define SQLITE_WINNT_MAX_PATH_CHARS (UNICODE_STRING_MAX_CHARS) |
+#endif |
+ |
+/* |
+** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in |
+** characters, so we allocate 4 bytes per character assuming worst-case of |
+** 4-bytes-per-character for UTF8. |
+*/ |
+#ifndef SQLITE_WIN32_MAX_PATH_BYTES |
+# define SQLITE_WIN32_MAX_PATH_BYTES (SQLITE_WIN32_MAX_PATH_CHARS*4) |
+#endif |
+ |
+/* |
+** Maximum pathname length (in bytes) for WinNT. This should normally be |
+** UNICODE_STRING_MAX_CHARS * sizeof(WCHAR). |
+*/ |
+#ifndef SQLITE_WINNT_MAX_PATH_BYTES |
+# define SQLITE_WINNT_MAX_PATH_BYTES \ |
+ (sizeof(WCHAR) * SQLITE_WINNT_MAX_PATH_CHARS) |
+#endif |
+ |
+/* |
+** Maximum error message length (in chars) for WinRT. |
+*/ |
+#ifndef SQLITE_WIN32_MAX_ERRMSG_CHARS |
+# define SQLITE_WIN32_MAX_ERRMSG_CHARS (1024) |
+#endif |
+ |
+/* |
+** Returns non-zero if the character should be treated as a directory |
+** separator. |
+*/ |
+#ifndef winIsDirSep |
+# define winIsDirSep(a) (((a) == '/') || ((a) == '\\')) |
+#endif |
+ |
+/* |
+** This macro is used when a local variable is set to a value that is |
+** [sometimes] not used by the code (e.g. via conditional compilation). |
+*/ |
+#ifndef UNUSED_VARIABLE_VALUE |
+# define UNUSED_VARIABLE_VALUE(x) (void)(x) |
+#endif |
+ |
+/* |
+** Returns the character that should be used as the directory separator. |
+*/ |
+#ifndef winGetDirSep |
+# define winGetDirSep() '\\' |
+#endif |
+ |
+/* |
+** Do we need to manually define the Win32 file mapping APIs for use with WAL |
+** mode or memory mapped files (e.g. these APIs are available in the Windows |
+** CE SDK; however, they are not present in the header file)? |
+*/ |
+#if SQLITE_WIN32_FILEMAPPING_API && \ |
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) |
+/* |
+** Two of the file mapping APIs are different under WinRT. Figure out which |
+** set we need. |
+*/ |
+#if SQLITE_OS_WINRT |
+WINBASEAPI HANDLE WINAPI CreateFileMappingFromApp(HANDLE, \ |
+ LPSECURITY_ATTRIBUTES, ULONG, ULONG64, LPCWSTR); |
+ |
+WINBASEAPI LPVOID WINAPI MapViewOfFileFromApp(HANDLE, ULONG, ULONG64, SIZE_T); |
+#else |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+WINBASEAPI HANDLE WINAPI CreateFileMappingA(HANDLE, LPSECURITY_ATTRIBUTES, \ |
+ DWORD, DWORD, DWORD, LPCSTR); |
+#endif /* defined(SQLITE_WIN32_HAS_ANSI) */ |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+WINBASEAPI HANDLE WINAPI CreateFileMappingW(HANDLE, LPSECURITY_ATTRIBUTES, \ |
+ DWORD, DWORD, DWORD, LPCWSTR); |
+#endif /* defined(SQLITE_WIN32_HAS_WIDE) */ |
+ |
+WINBASEAPI LPVOID WINAPI MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, SIZE_T); |
+#endif /* SQLITE_OS_WINRT */ |
+ |
+/* |
+** These file mapping APIs are common to both Win32 and WinRT. |
+*/ |
+ |
+WINBASEAPI BOOL WINAPI FlushViewOfFile(LPCVOID, SIZE_T); |
+WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID); |
+#endif /* SQLITE_WIN32_FILEMAPPING_API */ |
+ |
+/* |
+** Some Microsoft compilers lack this definition. |
+*/ |
+#ifndef INVALID_FILE_ATTRIBUTES |
+# define INVALID_FILE_ATTRIBUTES ((DWORD)-1) |
+#endif |
+ |
+#ifndef FILE_FLAG_MASK |
+# define FILE_FLAG_MASK (0xFF3C0000) |
+#endif |
+ |
+#ifndef FILE_ATTRIBUTE_MASK |
+# define FILE_ATTRIBUTE_MASK (0x0003FFF7) |
+#endif |
+ |
+#ifndef SQLITE_OMIT_WAL |
+/* Forward references to structures used for WAL */ |
+typedef struct winShm winShm; /* A connection to shared-memory */ |
+typedef struct winShmNode winShmNode; /* A region of shared-memory */ |
+#endif |
+ |
+/* |
+** WinCE lacks native support for file locking so we have to fake it |
+** with some code of our own. |
+*/ |
+#if SQLITE_OS_WINCE |
+typedef struct winceLock { |
+ int nReaders; /* Number of reader locks obtained */ |
+ BOOL bPending; /* Indicates a pending lock has been obtained */ |
+ BOOL bReserved; /* Indicates a reserved lock has been obtained */ |
+ BOOL bExclusive; /* Indicates an exclusive lock has been obtained */ |
+} winceLock; |
+#endif |
+ |
+/* |
+** The winFile structure is a subclass of sqlite3_file* specific to the win32 |
+** portability layer. |
+*/ |
+typedef struct winFile winFile; |
+struct winFile { |
+ const sqlite3_io_methods *pMethod; /*** Must be first ***/ |
+ sqlite3_vfs *pVfs; /* The VFS used to open this file */ |
+ HANDLE h; /* Handle for accessing the file */ |
+ u8 locktype; /* Type of lock currently held on this file */ |
+ short sharedLockByte; /* Randomly chosen byte used as a shared lock */ |
+ u8 ctrlFlags; /* Flags. See WINFILE_* below */ |
+ DWORD lastErrno; /* The Windows errno from the last I/O error */ |
+#ifndef SQLITE_OMIT_WAL |
+ winShm *pShm; /* Instance of shared memory on this file */ |
+#endif |
+ const char *zPath; /* Full pathname of this file */ |
+ int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */ |
+#if SQLITE_OS_WINCE |
+ LPWSTR zDeleteOnClose; /* Name of file to delete when closing */ |
+ HANDLE hMutex; /* Mutex used to control access to shared lock */ |
+ HANDLE hShared; /* Shared memory segment used for locking */ |
+ winceLock local; /* Locks obtained by this instance of winFile */ |
+ winceLock *shared; /* Global shared lock memory for the file */ |
+#endif |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ int nFetchOut; /* Number of outstanding xFetch references */ |
+ HANDLE hMap; /* Handle for accessing memory mapping */ |
+ void *pMapRegion; /* Area memory mapped */ |
+ sqlite3_int64 mmapSize; /* Usable size of mapped region */ |
+ sqlite3_int64 mmapSizeActual; /* Actual size of mapped region */ |
+ sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ |
+#endif |
+}; |
+ |
+/* |
+** Allowed values for winFile.ctrlFlags |
+*/ |
+#define WINFILE_RDONLY 0x02 /* Connection is read only */ |
+#define WINFILE_PERSIST_WAL 0x04 /* Persistent WAL mode */ |
+#define WINFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ |
+ |
+/* |
+ * The size of the buffer used by sqlite3_win32_write_debug(). |
+ */ |
+#ifndef SQLITE_WIN32_DBG_BUF_SIZE |
+# define SQLITE_WIN32_DBG_BUF_SIZE ((int)(4096-sizeof(DWORD))) |
+#endif |
+ |
+/* |
+ * The value used with sqlite3_win32_set_directory() to specify that |
+ * the data directory should be changed. |
+ */ |
+#ifndef SQLITE_WIN32_DATA_DIRECTORY_TYPE |
+# define SQLITE_WIN32_DATA_DIRECTORY_TYPE (1) |
+#endif |
+ |
+/* |
+ * The value used with sqlite3_win32_set_directory() to specify that |
+ * the temporary directory should be changed. |
+ */ |
+#ifndef SQLITE_WIN32_TEMP_DIRECTORY_TYPE |
+# define SQLITE_WIN32_TEMP_DIRECTORY_TYPE (2) |
+#endif |
+ |
+/* |
+ * If compiled with SQLITE_WIN32_MALLOC on Windows, we will use the |
+ * various Win32 API heap functions instead of our own. |
+ */ |
+#ifdef SQLITE_WIN32_MALLOC |
+ |
+/* |
+ * If this is non-zero, an isolated heap will be created by the native Win32 |
+ * allocator subsystem; otherwise, the default process heap will be used. This |
+ * setting has no effect when compiling for WinRT. By default, this is enabled |
+ * and an isolated heap will be created to store all allocated data. |
+ * |
+ ****************************************************************************** |
+ * WARNING: It is important to note that when this setting is non-zero and the |
+ * winMemShutdown function is called (e.g. by the sqlite3_shutdown |
+ * function), all data that was allocated using the isolated heap will |
+ * be freed immediately and any attempt to access any of that freed |
+ * data will almost certainly result in an immediate access violation. |
+ ****************************************************************************** |
+ */ |
+#ifndef SQLITE_WIN32_HEAP_CREATE |
+# define SQLITE_WIN32_HEAP_CREATE (TRUE) |
+#endif |
+ |
+/* |
+ * The initial size of the Win32-specific heap. This value may be zero. |
+ */ |
+#ifndef SQLITE_WIN32_HEAP_INIT_SIZE |
+# define SQLITE_WIN32_HEAP_INIT_SIZE ((SQLITE_DEFAULT_CACHE_SIZE) * \ |
+ (SQLITE_DEFAULT_PAGE_SIZE) + 4194304) |
+#endif |
+ |
+/* |
+ * The maximum size of the Win32-specific heap. This value may be zero. |
+ */ |
+#ifndef SQLITE_WIN32_HEAP_MAX_SIZE |
+# define SQLITE_WIN32_HEAP_MAX_SIZE (0) |
+#endif |
+ |
+/* |
+ * The extra flags to use in calls to the Win32 heap APIs. This value may be |
+ * zero for the default behavior. |
+ */ |
+#ifndef SQLITE_WIN32_HEAP_FLAGS |
+# define SQLITE_WIN32_HEAP_FLAGS (0) |
+#endif |
+ |
+ |
+/* |
+** The winMemData structure stores information required by the Win32-specific |
+** sqlite3_mem_methods implementation. |
+*/ |
+typedef struct winMemData winMemData; |
+struct winMemData { |
+#ifndef NDEBUG |
+ u32 magic1; /* Magic number to detect structure corruption. */ |
+#endif |
+ HANDLE hHeap; /* The handle to our heap. */ |
+ BOOL bOwned; /* Do we own the heap (i.e. destroy it on shutdown)? */ |
+#ifndef NDEBUG |
+ u32 magic2; /* Magic number to detect structure corruption. */ |
+#endif |
+}; |
+ |
+#ifndef NDEBUG |
+#define WINMEM_MAGIC1 0x42b2830b |
+#define WINMEM_MAGIC2 0xbd4d7cf4 |
+#endif |
+ |
+static struct winMemData win_mem_data = { |
+#ifndef NDEBUG |
+ WINMEM_MAGIC1, |
+#endif |
+ NULL, FALSE |
+#ifndef NDEBUG |
+ ,WINMEM_MAGIC2 |
+#endif |
+}; |
+ |
+#ifndef NDEBUG |
+#define winMemAssertMagic1() assert( win_mem_data.magic1==WINMEM_MAGIC1 ) |
+#define winMemAssertMagic2() assert( win_mem_data.magic2==WINMEM_MAGIC2 ) |
+#define winMemAssertMagic() winMemAssertMagic1(); winMemAssertMagic2(); |
+#else |
+#define winMemAssertMagic() |
+#endif |
+ |
+#define winMemGetDataPtr() &win_mem_data |
+#define winMemGetHeap() win_mem_data.hHeap |
+#define winMemGetOwned() win_mem_data.bOwned |
+ |
+static void *winMemMalloc(int nBytes); |
+static void winMemFree(void *pPrior); |
+static void *winMemRealloc(void *pPrior, int nBytes); |
+static int winMemSize(void *p); |
+static int winMemRoundup(int n); |
+static int winMemInit(void *pAppData); |
+static void winMemShutdown(void *pAppData); |
+ |
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void); |
+#endif /* SQLITE_WIN32_MALLOC */ |
+ |
+/* |
+** The following variable is (normally) set once and never changes |
+** thereafter. It records whether the operating system is Win9x |
+** or WinNT. |
+** |
+** 0: Operating system unknown. |
+** 1: Operating system is Win9x. |
+** 2: Operating system is WinNT. |
+** |
+** In order to facilitate testing on a WinNT system, the test fixture |
+** can manually set this value to 1 to emulate Win98 behavior. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; |
+#else |
+static LONG SQLITE_WIN32_VOLATILE sqlite3_os_type = 0; |
+#endif |
+ |
+#ifndef SYSCALL |
+# define SYSCALL sqlite3_syscall_ptr |
+#endif |
+ |
+/* |
+** This function is not available on Windows CE or WinRT. |
+ */ |
+ |
+#if SQLITE_OS_WINCE || SQLITE_OS_WINRT |
+# define osAreFileApisANSI() 1 |
+#endif |
+ |
+/* |
+** Many system calls are accessed through pointer-to-functions so that |
+** they may be overridden at runtime to facilitate fault injection during |
+** testing and sandboxing. The following array holds the names and pointers |
+** to all overrideable system calls. |
+*/ |
+static struct win_syscall { |
+ const char *zName; /* Name of the system call */ |
+ sqlite3_syscall_ptr pCurrent; /* Current value of the system call */ |
+ sqlite3_syscall_ptr pDefault; /* Default value */ |
+} aSyscall[] = { |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT |
+ { "AreFileApisANSI", (SYSCALL)AreFileApisANSI, 0 }, |
+#else |
+ { "AreFileApisANSI", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#ifndef osAreFileApisANSI |
+#define osAreFileApisANSI ((BOOL(WINAPI*)(VOID))aSyscall[0].pCurrent) |
+#endif |
+ |
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "CharLowerW", (SYSCALL)CharLowerW, 0 }, |
+#else |
+ { "CharLowerW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCharLowerW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[1].pCurrent) |
+ |
+#if SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "CharUpperW", (SYSCALL)CharUpperW, 0 }, |
+#else |
+ { "CharUpperW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCharUpperW ((LPWSTR(WINAPI*)(LPWSTR))aSyscall[2].pCurrent) |
+ |
+ { "CloseHandle", (SYSCALL)CloseHandle, 0 }, |
+ |
+#define osCloseHandle ((BOOL(WINAPI*)(HANDLE))aSyscall[3].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "CreateFileA", (SYSCALL)CreateFileA, 0 }, |
+#else |
+ { "CreateFileA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFileA ((HANDLE(WINAPI*)(LPCSTR,DWORD,DWORD, \ |
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[4].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "CreateFileW", (SYSCALL)CreateFileW, 0 }, |
+#else |
+ { "CreateFileW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFileW ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD, \ |
+ LPSECURITY_ATTRIBUTES,DWORD,DWORD,HANDLE))aSyscall[5].pCurrent) |
+ |
+#if (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_ANSI) && \ |
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) |
+ { "CreateFileMappingA", (SYSCALL)CreateFileMappingA, 0 }, |
+#else |
+ { "CreateFileMappingA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFileMappingA ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ |
+ DWORD,DWORD,DWORD,LPCSTR))aSyscall[6].pCurrent) |
+ |
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ |
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) |
+ { "CreateFileMappingW", (SYSCALL)CreateFileMappingW, 0 }, |
+#else |
+ { "CreateFileMappingW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFileMappingW ((HANDLE(WINAPI*)(HANDLE,LPSECURITY_ATTRIBUTES, \ |
+ DWORD,DWORD,DWORD,LPCWSTR))aSyscall[7].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "CreateMutexW", (SYSCALL)CreateMutexW, 0 }, |
+#else |
+ { "CreateMutexW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateMutexW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,BOOL, \ |
+ LPCWSTR))aSyscall[8].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "DeleteFileA", (SYSCALL)DeleteFileA, 0 }, |
+#else |
+ { "DeleteFileA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osDeleteFileA ((BOOL(WINAPI*)(LPCSTR))aSyscall[9].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ { "DeleteFileW", (SYSCALL)DeleteFileW, 0 }, |
+#else |
+ { "DeleteFileW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osDeleteFileW ((BOOL(WINAPI*)(LPCWSTR))aSyscall[10].pCurrent) |
+ |
+#if SQLITE_OS_WINCE |
+ { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 }, |
+#else |
+ { "FileTimeToLocalFileTime", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \ |
+ LPFILETIME))aSyscall[11].pCurrent) |
+ |
+#if SQLITE_OS_WINCE |
+ { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 }, |
+#else |
+ { "FileTimeToSystemTime", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \ |
+ LPSYSTEMTIME))aSyscall[12].pCurrent) |
+ |
+ { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 }, |
+ |
+#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "FormatMessageA", (SYSCALL)FormatMessageA, 0 }, |
+#else |
+ { "FormatMessageA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFormatMessageA ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPSTR, \ |
+ DWORD,va_list*))aSyscall[14].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ { "FormatMessageW", (SYSCALL)FormatMessageW, 0 }, |
+#else |
+ { "FormatMessageW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFormatMessageW ((DWORD(WINAPI*)(DWORD,LPCVOID,DWORD,DWORD,LPWSTR, \ |
+ DWORD,va_list*))aSyscall[15].pCurrent) |
+ |
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
+ { "FreeLibrary", (SYSCALL)FreeLibrary, 0 }, |
+#else |
+ { "FreeLibrary", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFreeLibrary ((BOOL(WINAPI*)(HMODULE))aSyscall[16].pCurrent) |
+ |
+ { "GetCurrentProcessId", (SYSCALL)GetCurrentProcessId, 0 }, |
+ |
+#define osGetCurrentProcessId ((DWORD(WINAPI*)(VOID))aSyscall[17].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) |
+ { "GetDiskFreeSpaceA", (SYSCALL)GetDiskFreeSpaceA, 0 }, |
+#else |
+ { "GetDiskFreeSpaceA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetDiskFreeSpaceA ((BOOL(WINAPI*)(LPCSTR,LPDWORD,LPDWORD,LPDWORD, \ |
+ LPDWORD))aSyscall[18].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "GetDiskFreeSpaceW", (SYSCALL)GetDiskFreeSpaceW, 0 }, |
+#else |
+ { "GetDiskFreeSpaceW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetDiskFreeSpaceW ((BOOL(WINAPI*)(LPCWSTR,LPDWORD,LPDWORD,LPDWORD, \ |
+ LPDWORD))aSyscall[19].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "GetFileAttributesA", (SYSCALL)GetFileAttributesA, 0 }, |
+#else |
+ { "GetFileAttributesA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFileAttributesA ((DWORD(WINAPI*)(LPCSTR))aSyscall[20].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "GetFileAttributesW", (SYSCALL)GetFileAttributesW, 0 }, |
+#else |
+ { "GetFileAttributesW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFileAttributesW ((DWORD(WINAPI*)(LPCWSTR))aSyscall[21].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ { "GetFileAttributesExW", (SYSCALL)GetFileAttributesExW, 0 }, |
+#else |
+ { "GetFileAttributesExW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFileAttributesExW ((BOOL(WINAPI*)(LPCWSTR,GET_FILEEX_INFO_LEVELS, \ |
+ LPVOID))aSyscall[22].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "GetFileSize", (SYSCALL)GetFileSize, 0 }, |
+#else |
+ { "GetFileSize", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFileSize ((DWORD(WINAPI*)(HANDLE,LPDWORD))aSyscall[23].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && defined(SQLITE_WIN32_HAS_ANSI) |
+ { "GetFullPathNameA", (SYSCALL)GetFullPathNameA, 0 }, |
+#else |
+ { "GetFullPathNameA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFullPathNameA ((DWORD(WINAPI*)(LPCSTR,DWORD,LPSTR, \ |
+ LPSTR*))aSyscall[24].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "GetFullPathNameW", (SYSCALL)GetFullPathNameW, 0 }, |
+#else |
+ { "GetFullPathNameW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ |
+ LPWSTR*))aSyscall[25].pCurrent) |
+ |
+ { "GetLastError", (SYSCALL)GetLastError, 0 }, |
+ |
+#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) |
+ |
+#if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
+#if SQLITE_OS_WINCE |
+ /* The GetProcAddressA() routine is only available on Windows CE. */ |
+ { "GetProcAddressA", (SYSCALL)GetProcAddressA, 0 }, |
+#else |
+ /* All other Windows platforms expect GetProcAddress() to take |
+ ** an ANSI string regardless of the _UNICODE setting */ |
+ { "GetProcAddressA", (SYSCALL)GetProcAddress, 0 }, |
+#endif |
+#else |
+ { "GetProcAddressA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetProcAddressA ((FARPROC(WINAPI*)(HMODULE, \ |
+ LPCSTR))aSyscall[27].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "GetSystemInfo", (SYSCALL)GetSystemInfo, 0 }, |
+#else |
+ { "GetSystemInfo", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetSystemInfo ((VOID(WINAPI*)(LPSYSTEM_INFO))aSyscall[28].pCurrent) |
+ |
+ { "GetSystemTime", (SYSCALL)GetSystemTime, 0 }, |
+ |
+#define osGetSystemTime ((VOID(WINAPI*)(LPSYSTEMTIME))aSyscall[29].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE |
+ { "GetSystemTimeAsFileTime", (SYSCALL)GetSystemTimeAsFileTime, 0 }, |
+#else |
+ { "GetSystemTimeAsFileTime", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetSystemTimeAsFileTime ((VOID(WINAPI*)( \ |
+ LPFILETIME))aSyscall[30].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "GetTempPathA", (SYSCALL)GetTempPathA, 0 }, |
+#else |
+ { "GetTempPathA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetTempPathA ((DWORD(WINAPI*)(DWORD,LPSTR))aSyscall[31].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) |
+ { "GetTempPathW", (SYSCALL)GetTempPathW, 0 }, |
+#else |
+ { "GetTempPathW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetTempPathW ((DWORD(WINAPI*)(DWORD,LPWSTR))aSyscall[32].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "GetTickCount", (SYSCALL)GetTickCount, 0 }, |
+#else |
+ { "GetTickCount", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetTickCount ((DWORD(WINAPI*)(VOID))aSyscall[33].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_GETVERSIONEX) && \ |
+ SQLITE_WIN32_GETVERSIONEX |
+ { "GetVersionExA", (SYSCALL)GetVersionExA, 0 }, |
+#else |
+ { "GetVersionExA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetVersionExA ((BOOL(WINAPI*)( \ |
+ LPOSVERSIONINFOA))aSyscall[34].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ |
+ defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX |
+ { "GetVersionExW", (SYSCALL)GetVersionExW, 0 }, |
+#else |
+ { "GetVersionExW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetVersionExW ((BOOL(WINAPI*)( \ |
+ LPOSVERSIONINFOW))aSyscall[35].pCurrent) |
+ |
+ { "HeapAlloc", (SYSCALL)HeapAlloc, 0 }, |
+ |
+#define osHeapAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD, \ |
+ SIZE_T))aSyscall[36].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "HeapCreate", (SYSCALL)HeapCreate, 0 }, |
+#else |
+ { "HeapCreate", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osHeapCreate ((HANDLE(WINAPI*)(DWORD,SIZE_T, \ |
+ SIZE_T))aSyscall[37].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "HeapDestroy", (SYSCALL)HeapDestroy, 0 }, |
+#else |
+ { "HeapDestroy", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osHeapDestroy ((BOOL(WINAPI*)(HANDLE))aSyscall[38].pCurrent) |
+ |
+ { "HeapFree", (SYSCALL)HeapFree, 0 }, |
+ |
+#define osHeapFree ((BOOL(WINAPI*)(HANDLE,DWORD,LPVOID))aSyscall[39].pCurrent) |
+ |
+ { "HeapReAlloc", (SYSCALL)HeapReAlloc, 0 }, |
+ |
+#define osHeapReAlloc ((LPVOID(WINAPI*)(HANDLE,DWORD,LPVOID, \ |
+ SIZE_T))aSyscall[40].pCurrent) |
+ |
+ { "HeapSize", (SYSCALL)HeapSize, 0 }, |
+ |
+#define osHeapSize ((SIZE_T(WINAPI*)(HANDLE,DWORD, \ |
+ LPCVOID))aSyscall[41].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "HeapValidate", (SYSCALL)HeapValidate, 0 }, |
+#else |
+ { "HeapValidate", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osHeapValidate ((BOOL(WINAPI*)(HANDLE,DWORD, \ |
+ LPCVOID))aSyscall[42].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT |
+ { "HeapCompact", (SYSCALL)HeapCompact, 0 }, |
+#else |
+ { "HeapCompact", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osHeapCompact ((UINT(WINAPI*)(HANDLE,DWORD))aSyscall[43].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
+ { "LoadLibraryA", (SYSCALL)LoadLibraryA, 0 }, |
+#else |
+ { "LoadLibraryA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osLoadLibraryA ((HMODULE(WINAPI*)(LPCSTR))aSyscall[44].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_HAS_WIDE) && \ |
+ !defined(SQLITE_OMIT_LOAD_EXTENSION) |
+ { "LoadLibraryW", (SYSCALL)LoadLibraryW, 0 }, |
+#else |
+ { "LoadLibraryW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osLoadLibraryW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[45].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "LocalFree", (SYSCALL)LocalFree, 0 }, |
+#else |
+ { "LocalFree", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[46].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT |
+ { "LockFile", (SYSCALL)LockFile, 0 }, |
+#else |
+ { "LockFile", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#ifndef osLockFile |
+#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ |
+ DWORD))aSyscall[47].pCurrent) |
+#endif |
+ |
+#if !SQLITE_OS_WINCE |
+ { "LockFileEx", (SYSCALL)LockFileEx, 0 }, |
+#else |
+ { "LockFileEx", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#ifndef osLockFileEx |
+#define osLockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD,DWORD, \ |
+ LPOVERLAPPED))aSyscall[48].pCurrent) |
+#endif |
+ |
+#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && \ |
+ (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0)) |
+ { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 }, |
+#else |
+ { "MapViewOfFile", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ |
+ SIZE_T))aSyscall[49].pCurrent) |
+ |
+ { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 }, |
+ |
+#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \ |
+ int))aSyscall[50].pCurrent) |
+ |
+ { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 }, |
+ |
+#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \ |
+ LARGE_INTEGER*))aSyscall[51].pCurrent) |
+ |
+ { "ReadFile", (SYSCALL)ReadFile, 0 }, |
+ |
+#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \ |
+ LPOVERLAPPED))aSyscall[52].pCurrent) |
+ |
+ { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 }, |
+ |
+#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[53].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "SetFilePointer", (SYSCALL)SetFilePointer, 0 }, |
+#else |
+ { "SetFilePointer", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \ |
+ DWORD))aSyscall[54].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "Sleep", (SYSCALL)Sleep, 0 }, |
+#else |
+ { "Sleep", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent) |
+ |
+ { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 }, |
+ |
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \ |
+ LPFILETIME))aSyscall[56].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT |
+ { "UnlockFile", (SYSCALL)UnlockFile, 0 }, |
+#else |
+ { "UnlockFile", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#ifndef osUnlockFile |
+#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ |
+ DWORD))aSyscall[57].pCurrent) |
+#endif |
+ |
+#if !SQLITE_OS_WINCE |
+ { "UnlockFileEx", (SYSCALL)UnlockFileEx, 0 }, |
+#else |
+ { "UnlockFileEx", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osUnlockFileEx ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \ |
+ LPOVERLAPPED))aSyscall[58].pCurrent) |
+ |
+#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0 |
+ { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 }, |
+#else |
+ { "UnmapViewOfFile", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[59].pCurrent) |
+ |
+ { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 }, |
+ |
+#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \ |
+ LPCSTR,LPBOOL))aSyscall[60].pCurrent) |
+ |
+ { "WriteFile", (SYSCALL)WriteFile, 0 }, |
+ |
+#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \ |
+ LPOVERLAPPED))aSyscall[61].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "CreateEventExW", (SYSCALL)CreateEventExW, 0 }, |
+#else |
+ { "CreateEventExW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ |
+ DWORD,DWORD))aSyscall[62].pCurrent) |
+ |
+#if !SQLITE_OS_WINRT |
+ { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
+#else |
+ { "WaitForSingleObject", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
+ DWORD))aSyscall[63].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE |
+ { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 }, |
+#else |
+ { "WaitForSingleObjectEx", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \ |
+ BOOL))aSyscall[64].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "SetFilePointerEx", (SYSCALL)SetFilePointerEx, 0 }, |
+#else |
+ { "SetFilePointerEx", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osSetFilePointerEx ((BOOL(WINAPI*)(HANDLE,LARGE_INTEGER, \ |
+ PLARGE_INTEGER,DWORD))aSyscall[65].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "GetFileInformationByHandleEx", (SYSCALL)GetFileInformationByHandleEx, 0 }, |
+#else |
+ { "GetFileInformationByHandleEx", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetFileInformationByHandleEx ((BOOL(WINAPI*)(HANDLE, \ |
+ FILE_INFO_BY_HANDLE_CLASS,LPVOID,DWORD))aSyscall[66].pCurrent) |
+ |
+#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) |
+ { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 }, |
+#else |
+ { "MapViewOfFileFromApp", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \ |
+ SIZE_T))aSyscall[67].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "CreateFile2", (SYSCALL)CreateFile2, 0 }, |
+#else |
+ { "CreateFile2", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFile2 ((HANDLE(WINAPI*)(LPCWSTR,DWORD,DWORD,DWORD, \ |
+ LPCREATEFILE2_EXTENDED_PARAMETERS))aSyscall[68].pCurrent) |
+ |
+#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_LOAD_EXTENSION) |
+ { "LoadPackagedLibrary", (SYSCALL)LoadPackagedLibrary, 0 }, |
+#else |
+ { "LoadPackagedLibrary", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osLoadPackagedLibrary ((HMODULE(WINAPI*)(LPCWSTR, \ |
+ DWORD))aSyscall[69].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "GetTickCount64", (SYSCALL)GetTickCount64, 0 }, |
+#else |
+ { "GetTickCount64", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[70].pCurrent) |
+ |
+#if SQLITE_OS_WINRT |
+ { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 }, |
+#else |
+ { "GetNativeSystemInfo", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osGetNativeSystemInfo ((VOID(WINAPI*)( \ |
+ LPSYSTEM_INFO))aSyscall[71].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 }, |
+#else |
+ { "OutputDebugStringA", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[72].pCurrent) |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 }, |
+#else |
+ { "OutputDebugStringW", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[73].pCurrent) |
+ |
+ { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 }, |
+ |
+#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[74].pCurrent) |
+ |
+#if SQLITE_OS_WINRT && (!defined(SQLITE_OMIT_WAL) || SQLITE_MAX_MMAP_SIZE>0) |
+ { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 }, |
+#else |
+ { "CreateFileMappingFromApp", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \ |
+ LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[75].pCurrent) |
+ |
+/* |
+** NOTE: On some sub-platforms, the InterlockedCompareExchange "function" |
+** is really just a macro that uses a compiler intrinsic (e.g. x64). |
+** So do not try to make this is into a redefinable interface. |
+*/ |
+#if defined(InterlockedCompareExchange) |
+ { "InterlockedCompareExchange", (SYSCALL)0, 0 }, |
+ |
+#define osInterlockedCompareExchange InterlockedCompareExchange |
+#else |
+ { "InterlockedCompareExchange", (SYSCALL)InterlockedCompareExchange, 0 }, |
+ |
+#define osInterlockedCompareExchange ((LONG(WINAPI*)(LONG \ |
+ SQLITE_WIN32_VOLATILE*, LONG,LONG))aSyscall[76].pCurrent) |
+#endif /* defined(InterlockedCompareExchange) */ |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID |
+ { "UuidCreate", (SYSCALL)UuidCreate, 0 }, |
+#else |
+ { "UuidCreate", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osUuidCreate ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[77].pCurrent) |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID |
+ { "UuidCreateSequential", (SYSCALL)UuidCreateSequential, 0 }, |
+#else |
+ { "UuidCreateSequential", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osUuidCreateSequential \ |
+ ((RPC_STATUS(RPC_ENTRY*)(UUID*))aSyscall[78].pCurrent) |
+ |
+#if !defined(SQLITE_NO_SYNC) && SQLITE_MAX_MMAP_SIZE>0 |
+ { "FlushViewOfFile", (SYSCALL)FlushViewOfFile, 0 }, |
+#else |
+ { "FlushViewOfFile", (SYSCALL)0, 0 }, |
+#endif |
+ |
+#define osFlushViewOfFile \ |
+ ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) |
+ |
+}; /* End of the overrideable system calls */ |
+ |
+/* |
+** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
+** "win32" VFSes. Return SQLITE_OK opon successfully updating the |
+** system call pointer, or SQLITE_NOTFOUND if there is no configurable |
+** system call named zName. |
+*/ |
+static int winSetSystemCall( |
+ sqlite3_vfs *pNotUsed, /* The VFS pointer. Not used */ |
+ const char *zName, /* Name of system call to override */ |
+ sqlite3_syscall_ptr pNewFunc /* Pointer to new system call value */ |
+){ |
+ unsigned int i; |
+ int rc = SQLITE_NOTFOUND; |
+ |
+ UNUSED_PARAMETER(pNotUsed); |
+ if( zName==0 ){ |
+ /* If no zName is given, restore all system calls to their default |
+ ** settings and return NULL |
+ */ |
+ rc = SQLITE_OK; |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( aSyscall[i].pDefault ){ |
+ aSyscall[i].pCurrent = aSyscall[i].pDefault; |
+ } |
+ } |
+ }else{ |
+ /* If zName is specified, operate on only the one system call |
+ ** specified. |
+ */ |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ){ |
+ if( aSyscall[i].pDefault==0 ){ |
+ aSyscall[i].pDefault = aSyscall[i].pCurrent; |
+ } |
+ rc = SQLITE_OK; |
+ if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault; |
+ aSyscall[i].pCurrent = pNewFunc; |
+ break; |
+ } |
+ } |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** Return the value of a system call. Return NULL if zName is not a |
+** recognized system call name. NULL is also returned if the system call |
+** is currently undefined. |
+*/ |
+static sqlite3_syscall_ptr winGetSystemCall( |
+ sqlite3_vfs *pNotUsed, |
+ const char *zName |
+){ |
+ unsigned int i; |
+ |
+ UNUSED_PARAMETER(pNotUsed); |
+ for(i=0; i<sizeof(aSyscall)/sizeof(aSyscall[0]); i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ) return aSyscall[i].pCurrent; |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** Return the name of the first system call after zName. If zName==NULL |
+** then return the name of the first system call. Return NULL if zName |
+** is the last system call or if zName is not the name of a valid |
+** system call. |
+*/ |
+static const char *winNextSystemCall(sqlite3_vfs *p, const char *zName){ |
+ int i = -1; |
+ |
+ UNUSED_PARAMETER(p); |
+ if( zName ){ |
+ for(i=0; i<ArraySize(aSyscall)-1; i++){ |
+ if( strcmp(zName, aSyscall[i].zName)==0 ) break; |
+ } |
+ } |
+ for(i++; i<ArraySize(aSyscall); i++){ |
+ if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName; |
+ } |
+ return 0; |
+} |
+ |
+#ifdef SQLITE_WIN32_MALLOC |
+/* |
+** If a Win32 native heap has been configured, this function will attempt to |
+** compact it. Upon success, SQLITE_OK will be returned. Upon failure, one |
+** of SQLITE_NOMEM, SQLITE_ERROR, or SQLITE_NOTFOUND will be returned. The |
+** "pnLargest" argument, if non-zero, will be used to return the size of the |
+** largest committed free block in the heap, in bytes. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_win32_compact_heap(LPUINT pnLargest){ |
+ int rc = SQLITE_OK; |
+ UINT nLargest = 0; |
+ HANDLE hHeap; |
+ |
+ winMemAssertMagic(); |
+ hHeap = winMemGetHeap(); |
+ assert( hHeap!=0 ); |
+ assert( hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); |
+#endif |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT |
+ if( (nLargest=osHeapCompact(hHeap, SQLITE_WIN32_HEAP_FLAGS))==0 ){ |
+ DWORD lastErrno = osGetLastError(); |
+ if( lastErrno==NO_ERROR ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapCompact (no space), heap=%p", |
+ (void*)hHeap); |
+ rc = SQLITE_NOMEM; |
+ }else{ |
+ sqlite3_log(SQLITE_ERROR, "failed to HeapCompact (%lu), heap=%p", |
+ osGetLastError(), (void*)hHeap); |
+ rc = SQLITE_ERROR; |
+ } |
+ } |
+#else |
+ sqlite3_log(SQLITE_NOTFOUND, "failed to HeapCompact, heap=%p", |
+ (void*)hHeap); |
+ rc = SQLITE_NOTFOUND; |
+#endif |
+ if( pnLargest ) *pnLargest = nLargest; |
+ return rc; |
+} |
+ |
+/* |
+** If a Win32 native heap has been configured, this function will attempt to |
+** destroy and recreate it. If the Win32 native heap is not isolated and/or |
+** the sqlite3_memory_used() function does not return zero, SQLITE_BUSY will |
+** be returned and no changes will be made to the Win32 native heap. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_win32_reset_heap(){ |
+ int rc; |
+ MUTEX_LOGIC( sqlite3_mutex *pMaster; ) /* The main static mutex */ |
+ MUTEX_LOGIC( sqlite3_mutex *pMem; ) /* The memsys static mutex */ |
+ MUTEX_LOGIC( pMaster = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MASTER); ) |
+ MUTEX_LOGIC( pMem = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_MEM); ) |
+ sqlite3_mutex_enter(pMaster); |
+ sqlite3_mutex_enter(pMem); |
+ winMemAssertMagic(); |
+ if( winMemGetHeap()!=NULL && winMemGetOwned() && sqlite3_memory_used()==0 ){ |
+ /* |
+ ** At this point, there should be no outstanding memory allocations on |
+ ** the heap. Also, since both the master and memsys locks are currently |
+ ** being held by us, no other function (i.e. from another thread) should |
+ ** be able to even access the heap. Attempt to destroy and recreate our |
+ ** isolated Win32 native heap now. |
+ */ |
+ assert( winMemGetHeap()!=NULL ); |
+ assert( winMemGetOwned() ); |
+ assert( sqlite3_memory_used()==0 ); |
+ winMemShutdown(winMemGetDataPtr()); |
+ assert( winMemGetHeap()==NULL ); |
+ assert( !winMemGetOwned() ); |
+ assert( sqlite3_memory_used()==0 ); |
+ rc = winMemInit(winMemGetDataPtr()); |
+ assert( rc!=SQLITE_OK || winMemGetHeap()!=NULL ); |
+ assert( rc!=SQLITE_OK || winMemGetOwned() ); |
+ assert( rc!=SQLITE_OK || sqlite3_memory_used()==0 ); |
+ }else{ |
+ /* |
+ ** The Win32 native heap cannot be modified because it may be in use. |
+ */ |
+ rc = SQLITE_BUSY; |
+ } |
+ sqlite3_mutex_leave(pMem); |
+ sqlite3_mutex_leave(pMaster); |
+ return rc; |
+} |
+#endif /* SQLITE_WIN32_MALLOC */ |
+ |
+/* |
+** This function outputs the specified (ANSI) string to the Win32 debugger |
+** (if available). |
+*/ |
+ |
+SQLITE_API void SQLITE_STDCALL sqlite3_win32_write_debug(const char *zBuf, int nBuf){ |
+ char zDbgBuf[SQLITE_WIN32_DBG_BUF_SIZE]; |
+ int nMin = MIN(nBuf, (SQLITE_WIN32_DBG_BUF_SIZE - 1)); /* may be negative. */ |
+ if( nMin<-1 ) nMin = -1; /* all negative values become -1. */ |
+ assert( nMin==-1 || nMin==0 || nMin<SQLITE_WIN32_DBG_BUF_SIZE ); |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ if( nMin>0 ){ |
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); |
+ memcpy(zDbgBuf, zBuf, nMin); |
+ osOutputDebugStringA(zDbgBuf); |
+ }else{ |
+ osOutputDebugStringA(zBuf); |
+ } |
+#elif defined(SQLITE_WIN32_HAS_WIDE) |
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); |
+ if ( osMultiByteToWideChar( |
+ osAreFileApisANSI() ? CP_ACP : CP_OEMCP, 0, zBuf, |
+ nMin, (LPWSTR)zDbgBuf, SQLITE_WIN32_DBG_BUF_SIZE/sizeof(WCHAR))<=0 ){ |
+ return; |
+ } |
+ osOutputDebugStringW((LPCWSTR)zDbgBuf); |
+#else |
+ if( nMin>0 ){ |
+ memset(zDbgBuf, 0, SQLITE_WIN32_DBG_BUF_SIZE); |
+ memcpy(zDbgBuf, zBuf, nMin); |
+ fprintf(stderr, "%s", zDbgBuf); |
+ }else{ |
+ fprintf(stderr, "%s", zBuf); |
+ } |
+#endif |
+} |
+ |
+/* |
+** The following routine suspends the current thread for at least ms |
+** milliseconds. This is equivalent to the Win32 Sleep() interface. |
+*/ |
+#if SQLITE_OS_WINRT |
+static HANDLE sleepObj = NULL; |
+#endif |
+ |
+SQLITE_API void SQLITE_STDCALL sqlite3_win32_sleep(DWORD milliseconds){ |
+#if SQLITE_OS_WINRT |
+ if ( sleepObj==NULL ){ |
+ sleepObj = osCreateEventExW(NULL, NULL, CREATE_EVENT_MANUAL_RESET, |
+ SYNCHRONIZE); |
+ } |
+ assert( sleepObj!=NULL ); |
+ osWaitForSingleObjectEx(sleepObj, milliseconds, FALSE); |
+#else |
+ osSleep(milliseconds); |
+#endif |
+} |
+ |
+#if SQLITE_MAX_WORKER_THREADS>0 && !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \ |
+ SQLITE_THREADSAFE>0 |
+SQLITE_PRIVATE DWORD sqlite3Win32Wait(HANDLE hObject){ |
+ DWORD rc; |
+ while( (rc = osWaitForSingleObjectEx(hObject, INFINITE, |
+ TRUE))==WAIT_IO_COMPLETION ){} |
+ return rc; |
+} |
+#endif |
+ |
+/* |
+** Return true (non-zero) if we are running under WinNT, Win2K, WinXP, |
+** or WinCE. Return false (zero) for Win95, Win98, or WinME. |
+** |
+** Here is an interesting observation: Win95, Win98, and WinME lack |
+** the LockFileEx() API. But we can still statically link against that |
+** API as long as we don't call it when running Win95/98/ME. A call to |
+** this routine is used to determine if the host is Win95/98/ME or |
+** WinNT/2K/XP so that we will know whether or not we can safely call |
+** the LockFileEx() API. |
+*/ |
+ |
+#if !defined(SQLITE_WIN32_GETVERSIONEX) || !SQLITE_WIN32_GETVERSIONEX |
+# define osIsNT() (1) |
+#elif SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI) |
+# define osIsNT() (1) |
+#elif !defined(SQLITE_WIN32_HAS_WIDE) |
+# define osIsNT() (0) |
+#else |
+# define osIsNT() ((sqlite3_os_type==2) || sqlite3_win32_is_nt()) |
+#endif |
+ |
+/* |
+** This function determines if the machine is running a version of Windows |
+** based on the NT kernel. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_win32_is_nt(void){ |
+#if SQLITE_OS_WINRT |
+ /* |
+ ** NOTE: The WinRT sub-platform is always assumed to be based on the NT |
+ ** kernel. |
+ */ |
+ return 1; |
+#elif defined(SQLITE_WIN32_GETVERSIONEX) && SQLITE_WIN32_GETVERSIONEX |
+ if( osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 ){ |
+#if defined(SQLITE_WIN32_HAS_ANSI) |
+ OSVERSIONINFOA sInfo; |
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo); |
+ osGetVersionExA(&sInfo); |
+ osInterlockedCompareExchange(&sqlite3_os_type, |
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); |
+#elif defined(SQLITE_WIN32_HAS_WIDE) |
+ OSVERSIONINFOW sInfo; |
+ sInfo.dwOSVersionInfoSize = sizeof(sInfo); |
+ osGetVersionExW(&sInfo); |
+ osInterlockedCompareExchange(&sqlite3_os_type, |
+ (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); |
+#endif |
+ } |
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
+#elif SQLITE_TEST |
+ return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
+#else |
+ /* |
+ ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are |
+ ** deprecated are always assumed to be based on the NT kernel. |
+ */ |
+ return 1; |
+#endif |
+} |
+ |
+#ifdef SQLITE_WIN32_MALLOC |
+/* |
+** Allocate nBytes of memory. |
+*/ |
+static void *winMemMalloc(int nBytes){ |
+ HANDLE hHeap; |
+ void *p; |
+ |
+ winMemAssertMagic(); |
+ hHeap = winMemGetHeap(); |
+ assert( hHeap!=0 ); |
+ assert( hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); |
+#endif |
+ assert( nBytes>=0 ); |
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); |
+ if( !p ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapAlloc %u bytes (%lu), heap=%p", |
+ nBytes, osGetLastError(), (void*)hHeap); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Free memory. |
+*/ |
+static void winMemFree(void *pPrior){ |
+ HANDLE hHeap; |
+ |
+ winMemAssertMagic(); |
+ hHeap = winMemGetHeap(); |
+ assert( hHeap!=0 ); |
+ assert( hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); |
+#endif |
+ if( !pPrior ) return; /* Passing NULL to HeapFree is undefined. */ |
+ if( !osHeapFree(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapFree block %p (%lu), heap=%p", |
+ pPrior, osGetLastError(), (void*)hHeap); |
+ } |
+} |
+ |
+/* |
+** Change the size of an existing memory allocation |
+*/ |
+static void *winMemRealloc(void *pPrior, int nBytes){ |
+ HANDLE hHeap; |
+ void *p; |
+ |
+ winMemAssertMagic(); |
+ hHeap = winMemGetHeap(); |
+ assert( hHeap!=0 ); |
+ assert( hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior) ); |
+#endif |
+ assert( nBytes>=0 ); |
+ if( !pPrior ){ |
+ p = osHeapAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, (SIZE_T)nBytes); |
+ }else{ |
+ p = osHeapReAlloc(hHeap, SQLITE_WIN32_HEAP_FLAGS, pPrior, (SIZE_T)nBytes); |
+ } |
+ if( !p ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to %s %u bytes (%lu), heap=%p", |
+ pPrior ? "HeapReAlloc" : "HeapAlloc", nBytes, osGetLastError(), |
+ (void*)hHeap); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Return the size of an outstanding allocation, in bytes. |
+*/ |
+static int winMemSize(void *p){ |
+ HANDLE hHeap; |
+ SIZE_T n; |
+ |
+ winMemAssertMagic(); |
+ hHeap = winMemGetHeap(); |
+ assert( hHeap!=0 ); |
+ assert( hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(hHeap, SQLITE_WIN32_HEAP_FLAGS, p) ); |
+#endif |
+ if( !p ) return 0; |
+ n = osHeapSize(hHeap, SQLITE_WIN32_HEAP_FLAGS, p); |
+ if( n==(SIZE_T)-1 ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapSize block %p (%lu), heap=%p", |
+ p, osGetLastError(), (void*)hHeap); |
+ return 0; |
+ } |
+ return (int)n; |
+} |
+ |
+/* |
+** Round up a request size to the next valid allocation size. |
+*/ |
+static int winMemRoundup(int n){ |
+ return n; |
+} |
+ |
+/* |
+** Initialize this module. |
+*/ |
+static int winMemInit(void *pAppData){ |
+ winMemData *pWinMemData = (winMemData *)pAppData; |
+ |
+ if( !pWinMemData ) return SQLITE_ERROR; |
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 ); |
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 ); |
+ |
+#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE |
+ if( !pWinMemData->hHeap ){ |
+ DWORD dwInitialSize = SQLITE_WIN32_HEAP_INIT_SIZE; |
+ DWORD dwMaximumSize = (DWORD)sqlite3GlobalConfig.nHeap; |
+ if( dwMaximumSize==0 ){ |
+ dwMaximumSize = SQLITE_WIN32_HEAP_MAX_SIZE; |
+ }else if( dwInitialSize>dwMaximumSize ){ |
+ dwInitialSize = dwMaximumSize; |
+ } |
+ pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS, |
+ dwInitialSize, dwMaximumSize); |
+ if( !pWinMemData->hHeap ){ |
+ sqlite3_log(SQLITE_NOMEM, |
+ "failed to HeapCreate (%lu), flags=%u, initSize=%lu, maxSize=%lu", |
+ osGetLastError(), SQLITE_WIN32_HEAP_FLAGS, dwInitialSize, |
+ dwMaximumSize); |
+ return SQLITE_NOMEM; |
+ } |
+ pWinMemData->bOwned = TRUE; |
+ assert( pWinMemData->bOwned ); |
+ } |
+#else |
+ pWinMemData->hHeap = osGetProcessHeap(); |
+ if( !pWinMemData->hHeap ){ |
+ sqlite3_log(SQLITE_NOMEM, |
+ "failed to GetProcessHeap (%lu)", osGetLastError()); |
+ return SQLITE_NOMEM; |
+ } |
+ pWinMemData->bOwned = FALSE; |
+ assert( !pWinMemData->bOwned ); |
+#endif |
+ assert( pWinMemData->hHeap!=0 ); |
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); |
+#endif |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Deinitialize this module. |
+*/ |
+static void winMemShutdown(void *pAppData){ |
+ winMemData *pWinMemData = (winMemData *)pAppData; |
+ |
+ if( !pWinMemData ) return; |
+ assert( pWinMemData->magic1==WINMEM_MAGIC1 ); |
+ assert( pWinMemData->magic2==WINMEM_MAGIC2 ); |
+ |
+ if( pWinMemData->hHeap ){ |
+ assert( pWinMemData->hHeap!=INVALID_HANDLE_VALUE ); |
+#if !SQLITE_OS_WINRT && defined(SQLITE_WIN32_MALLOC_VALIDATE) |
+ assert( osHeapValidate(pWinMemData->hHeap, SQLITE_WIN32_HEAP_FLAGS, NULL) ); |
+#endif |
+ if( pWinMemData->bOwned ){ |
+ if( !osHeapDestroy(pWinMemData->hHeap) ){ |
+ sqlite3_log(SQLITE_NOMEM, "failed to HeapDestroy (%lu), heap=%p", |
+ osGetLastError(), (void*)pWinMemData->hHeap); |
+ } |
+ pWinMemData->bOwned = FALSE; |
+ } |
+ pWinMemData->hHeap = NULL; |
+ } |
+} |
+ |
+/* |
+** Populate the low-level memory allocation function pointers in |
+** sqlite3GlobalConfig.m with pointers to the routines in this file. The |
+** arguments specify the block of memory to manage. |
+** |
+** This routine is only called by sqlite3_config(), and therefore |
+** is not required to be threadsafe (it is not). |
+*/ |
+SQLITE_PRIVATE const sqlite3_mem_methods *sqlite3MemGetWin32(void){ |
+ static const sqlite3_mem_methods winMemMethods = { |
+ winMemMalloc, |
+ winMemFree, |
+ winMemRealloc, |
+ winMemSize, |
+ winMemRoundup, |
+ winMemInit, |
+ winMemShutdown, |
+ &win_mem_data |
+ }; |
+ return &winMemMethods; |
+} |
+ |
+SQLITE_PRIVATE void sqlite3MemSetDefault(void){ |
+ sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32()); |
+} |
+#endif /* SQLITE_WIN32_MALLOC */ |
+ |
+/* |
+** Convert a UTF-8 string to Microsoft Unicode (UTF-16?). |
+** |
+** Space to hold the returned string is obtained from malloc. |
+*/ |
+static LPWSTR winUtf8ToUnicode(const char *zFilename){ |
+ int nChar; |
+ LPWSTR zWideFilename; |
+ |
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0); |
+ if( nChar==0 ){ |
+ return 0; |
+ } |
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(zWideFilename[0]) ); |
+ if( zWideFilename==0 ){ |
+ return 0; |
+ } |
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, zWideFilename, |
+ nChar); |
+ if( nChar==0 ){ |
+ sqlite3_free(zWideFilename); |
+ zWideFilename = 0; |
+ } |
+ return zWideFilename; |
+} |
+ |
+/* |
+** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is |
+** obtained from sqlite3_malloc(). |
+*/ |
+static char *winUnicodeToUtf8(LPCWSTR zWideFilename){ |
+ int nByte; |
+ char *zFilename; |
+ |
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, 0, 0, 0, 0); |
+ if( nByte == 0 ){ |
+ return 0; |
+ } |
+ zFilename = sqlite3MallocZero( nByte ); |
+ if( zFilename==0 ){ |
+ return 0; |
+ } |
+ nByte = osWideCharToMultiByte(CP_UTF8, 0, zWideFilename, -1, zFilename, nByte, |
+ 0, 0); |
+ if( nByte == 0 ){ |
+ sqlite3_free(zFilename); |
+ zFilename = 0; |
+ } |
+ return zFilename; |
+} |
+ |
+/* |
+** Convert an ANSI string to Microsoft Unicode, based on the |
+** current codepage settings for file apis. |
+** |
+** Space to hold the returned string is obtained |
+** from sqlite3_malloc. |
+*/ |
+static LPWSTR winMbcsToUnicode(const char *zFilename){ |
+ int nByte; |
+ LPWSTR zMbcsFilename; |
+ int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; |
+ |
+ nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, NULL, |
+ 0)*sizeof(WCHAR); |
+ if( nByte==0 ){ |
+ return 0; |
+ } |
+ zMbcsFilename = sqlite3MallocZero( nByte*sizeof(zMbcsFilename[0]) ); |
+ if( zMbcsFilename==0 ){ |
+ return 0; |
+ } |
+ nByte = osMultiByteToWideChar(codepage, 0, zFilename, -1, zMbcsFilename, |
+ nByte); |
+ if( nByte==0 ){ |
+ sqlite3_free(zMbcsFilename); |
+ zMbcsFilename = 0; |
+ } |
+ return zMbcsFilename; |
+} |
+ |
+/* |
+** Convert Microsoft Unicode to multi-byte character string, based on the |
+** user's ANSI codepage. |
+** |
+** Space to hold the returned string is obtained from |
+** sqlite3_malloc(). |
+*/ |
+static char *winUnicodeToMbcs(LPCWSTR zWideFilename){ |
+ int nByte; |
+ char *zFilename; |
+ int codepage = osAreFileApisANSI() ? CP_ACP : CP_OEMCP; |
+ |
+ nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, 0, 0, 0, 0); |
+ if( nByte == 0 ){ |
+ return 0; |
+ } |
+ zFilename = sqlite3MallocZero( nByte ); |
+ if( zFilename==0 ){ |
+ return 0; |
+ } |
+ nByte = osWideCharToMultiByte(codepage, 0, zWideFilename, -1, zFilename, |
+ nByte, 0, 0); |
+ if( nByte == 0 ){ |
+ sqlite3_free(zFilename); |
+ zFilename = 0; |
+ } |
+ return zFilename; |
+} |
+ |
+/* |
+** Convert multibyte character string to UTF-8. Space to hold the |
+** returned string is obtained from sqlite3_malloc(). |
+*/ |
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_mbcs_to_utf8(const char *zFilename){ |
+ char *zFilenameUtf8; |
+ LPWSTR zTmpWide; |
+ |
+ zTmpWide = winMbcsToUnicode(zFilename); |
+ if( zTmpWide==0 ){ |
+ return 0; |
+ } |
+ zFilenameUtf8 = winUnicodeToUtf8(zTmpWide); |
+ sqlite3_free(zTmpWide); |
+ return zFilenameUtf8; |
+} |
+ |
+/* |
+** Convert UTF-8 to multibyte character string. Space to hold the |
+** returned string is obtained from sqlite3_malloc(). |
+*/ |
+SQLITE_API char *SQLITE_STDCALL sqlite3_win32_utf8_to_mbcs(const char *zFilename){ |
+ char *zFilenameMbcs; |
+ LPWSTR zTmpWide; |
+ |
+ zTmpWide = winUtf8ToUnicode(zFilename); |
+ if( zTmpWide==0 ){ |
+ return 0; |
+ } |
+ zFilenameMbcs = winUnicodeToMbcs(zTmpWide); |
+ sqlite3_free(zTmpWide); |
+ return zFilenameMbcs; |
+} |
+ |
+/* |
+** This function sets the data directory or the temporary directory based on |
+** the provided arguments. The type argument must be 1 in order to set the |
+** data directory or 2 in order to set the temporary directory. The zValue |
+** argument is the name of the directory to use. The return value will be |
+** SQLITE_OK if successful. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_win32_set_directory(DWORD type, LPCWSTR zValue){ |
+ char **ppDirectory = 0; |
+#ifndef SQLITE_OMIT_AUTOINIT |
+ int rc = sqlite3_initialize(); |
+ if( rc ) return rc; |
+#endif |
+ if( type==SQLITE_WIN32_DATA_DIRECTORY_TYPE ){ |
+ ppDirectory = &sqlite3_data_directory; |
+ }else if( type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE ){ |
+ ppDirectory = &sqlite3_temp_directory; |
+ } |
+ assert( !ppDirectory || type==SQLITE_WIN32_DATA_DIRECTORY_TYPE |
+ || type==SQLITE_WIN32_TEMP_DIRECTORY_TYPE |
+ ); |
+ assert( !ppDirectory || sqlite3MemdebugHasType(*ppDirectory, MEMTYPE_HEAP) ); |
+ if( ppDirectory ){ |
+ char *zValueUtf8 = 0; |
+ if( zValue && zValue[0] ){ |
+ zValueUtf8 = winUnicodeToUtf8(zValue); |
+ if ( zValueUtf8==0 ){ |
+ return SQLITE_NOMEM; |
+ } |
+ } |
+ sqlite3_free(*ppDirectory); |
+ *ppDirectory = zValueUtf8; |
+ return SQLITE_OK; |
+ } |
+ return SQLITE_ERROR; |
+} |
+ |
+/* |
+** The return value of winGetLastErrorMsg |
+** is zero if the error message fits in the buffer, or non-zero |
+** otherwise (if the message was truncated). |
+*/ |
+static int winGetLastErrorMsg(DWORD lastErrno, int nBuf, char *zBuf){ |
+ /* FormatMessage returns 0 on failure. Otherwise it |
+ ** returns the number of TCHARs written to the output |
+ ** buffer, excluding the terminating null char. |
+ */ |
+ DWORD dwLen = 0; |
+ char *zOut = 0; |
+ |
+ if( osIsNT() ){ |
+#if SQLITE_OS_WINRT |
+ WCHAR zTempWide[SQLITE_WIN32_MAX_ERRMSG_CHARS+1]; |
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | |
+ FORMAT_MESSAGE_IGNORE_INSERTS, |
+ NULL, |
+ lastErrno, |
+ 0, |
+ zTempWide, |
+ SQLITE_WIN32_MAX_ERRMSG_CHARS, |
+ 0); |
+#else |
+ LPWSTR zTempWide = NULL; |
+ dwLen = osFormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
+ FORMAT_MESSAGE_FROM_SYSTEM | |
+ FORMAT_MESSAGE_IGNORE_INSERTS, |
+ NULL, |
+ lastErrno, |
+ 0, |
+ (LPWSTR) &zTempWide, |
+ 0, |
+ 0); |
+#endif |
+ if( dwLen > 0 ){ |
+ /* allocate a buffer and convert to UTF8 */ |
+ sqlite3BeginBenignMalloc(); |
+ zOut = winUnicodeToUtf8(zTempWide); |
+ sqlite3EndBenignMalloc(); |
+#if !SQLITE_OS_WINRT |
+ /* free the system buffer allocated by FormatMessage */ |
+ osLocalFree(zTempWide); |
+#endif |
+ } |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ char *zTemp = NULL; |
+ dwLen = osFormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | |
+ FORMAT_MESSAGE_FROM_SYSTEM | |
+ FORMAT_MESSAGE_IGNORE_INSERTS, |
+ NULL, |
+ lastErrno, |
+ 0, |
+ (LPSTR) &zTemp, |
+ 0, |
+ 0); |
+ if( dwLen > 0 ){ |
+ /* allocate a buffer and convert to UTF8 */ |
+ sqlite3BeginBenignMalloc(); |
+ zOut = sqlite3_win32_mbcs_to_utf8(zTemp); |
+ sqlite3EndBenignMalloc(); |
+ /* free the system buffer allocated by FormatMessage */ |
+ osLocalFree(zTemp); |
+ } |
+ } |
+#endif |
+ if( 0 == dwLen ){ |
+ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%lx (%lu)", lastErrno, lastErrno); |
+ }else{ |
+ /* copy a maximum of nBuf chars to output buffer */ |
+ sqlite3_snprintf(nBuf, zBuf, "%s", zOut); |
+ /* free the UTF8 buffer */ |
+ sqlite3_free(zOut); |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** |
+** This function - winLogErrorAtLine() - is only ever called via the macro |
+** winLogError(). |
+** |
+** This routine is invoked after an error occurs in an OS function. |
+** It logs a message using sqlite3_log() containing the current value of |
+** error code and, if possible, the human-readable equivalent from |
+** FormatMessage. |
+** |
+** The first argument passed to the macro should be the error code that |
+** will be returned to SQLite (e.g. SQLITE_IOERR_DELETE, SQLITE_CANTOPEN). |
+** The two subsequent arguments should be the name of the OS function that |
+** failed and the associated file-system path, if any. |
+*/ |
+#define winLogError(a,b,c,d) winLogErrorAtLine(a,b,c,d,__LINE__) |
+static int winLogErrorAtLine( |
+ int errcode, /* SQLite error code */ |
+ DWORD lastErrno, /* Win32 last error */ |
+ const char *zFunc, /* Name of OS function that failed */ |
+ const char *zPath, /* File path associated with error */ |
+ int iLine /* Source line number where error occurred */ |
+){ |
+ char zMsg[500]; /* Human readable error text */ |
+ int i; /* Loop counter */ |
+ |
+ zMsg[0] = 0; |
+ winGetLastErrorMsg(lastErrno, sizeof(zMsg), zMsg); |
+ assert( errcode!=SQLITE_OK ); |
+ if( zPath==0 ) zPath = ""; |
+ for(i=0; zMsg[i] && zMsg[i]!='\r' && zMsg[i]!='\n'; i++){} |
+ zMsg[i] = 0; |
+ sqlite3_log(errcode, |
+ "os_win.c:%d: (%lu) %s(%s) - %s", |
+ iLine, lastErrno, zFunc, zPath, zMsg |
+ ); |
+ |
+ return errcode; |
+} |
+ |
+/* |
+** The number of times that a ReadFile(), WriteFile(), and DeleteFile() |
+** will be retried following a locking error - probably caused by |
+** antivirus software. Also the initial delay before the first retry. |
+** The delay increases linearly with each retry. |
+*/ |
+#ifndef SQLITE_WIN32_IOERR_RETRY |
+# define SQLITE_WIN32_IOERR_RETRY 10 |
+#endif |
+#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY |
+# define SQLITE_WIN32_IOERR_RETRY_DELAY 25 |
+#endif |
+static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY; |
+static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY; |
+ |
+/* |
+** The "winIoerrCanRetry1" macro is used to determine if a particular I/O |
+** error code obtained via GetLastError() is eligible to be retried. It |
+** must accept the error code DWORD as its only argument and should return |
+** non-zero if the error code is transient in nature and the operation |
+** responsible for generating the original error might succeed upon being |
+** retried. The argument to this macro should be a variable. |
+** |
+** Additionally, a macro named "winIoerrCanRetry2" may be defined. If it |
+** is defined, it will be consulted only when the macro "winIoerrCanRetry1" |
+** returns zero. The "winIoerrCanRetry2" macro is completely optional and |
+** may be used to include additional error codes in the set that should |
+** result in the failing I/O operation being retried by the caller. If |
+** defined, the "winIoerrCanRetry2" macro must exhibit external semantics |
+** identical to those of the "winIoerrCanRetry1" macro. |
+*/ |
+#if !defined(winIoerrCanRetry1) |
+#define winIoerrCanRetry1(a) (((a)==ERROR_ACCESS_DENIED) || \ |
+ ((a)==ERROR_SHARING_VIOLATION) || \ |
+ ((a)==ERROR_LOCK_VIOLATION) || \ |
+ ((a)==ERROR_DEV_NOT_EXIST) || \ |
+ ((a)==ERROR_NETNAME_DELETED) || \ |
+ ((a)==ERROR_SEM_TIMEOUT) || \ |
+ ((a)==ERROR_NETWORK_UNREACHABLE)) |
+#endif |
+ |
+/* |
+** If a ReadFile() or WriteFile() error occurs, invoke this routine |
+** to see if it should be retried. Return TRUE to retry. Return FALSE |
+** to give up with an error. |
+*/ |
+static int winRetryIoerr(int *pnRetry, DWORD *pError){ |
+ DWORD e = osGetLastError(); |
+ if( *pnRetry>=winIoerrRetry ){ |
+ if( pError ){ |
+ *pError = e; |
+ } |
+ return 0; |
+ } |
+ if( winIoerrCanRetry1(e) ){ |
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); |
+ ++*pnRetry; |
+ return 1; |
+ } |
+#if defined(winIoerrCanRetry2) |
+ else if( winIoerrCanRetry2(e) ){ |
+ sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry)); |
+ ++*pnRetry; |
+ return 1; |
+ } |
+#endif |
+ if( pError ){ |
+ *pError = e; |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** Log a I/O error retry episode. |
+*/ |
+static void winLogIoerr(int nRetry, int lineno){ |
+ if( nRetry ){ |
+ sqlite3_log(SQLITE_NOTICE, |
+ "delayed %dms for lock/sharing conflict at line %d", |
+ winIoerrRetryDelay*nRetry*(nRetry+1)/2, lineno |
+ ); |
+ } |
+} |
+ |
+#if SQLITE_OS_WINCE |
+/************************************************************************* |
+** This section contains code for WinCE only. |
+*/ |
+#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API |
+/* |
+** The MSVC CRT on Windows CE may not have a localtime() function. So |
+** create a substitute. |
+*/ |
+/* #include <time.h> */ |
+struct tm *__cdecl localtime(const time_t *t) |
+{ |
+ static struct tm y; |
+ FILETIME uTm, lTm; |
+ SYSTEMTIME pTm; |
+ sqlite3_int64 t64; |
+ t64 = *t; |
+ t64 = (t64 + 11644473600)*10000000; |
+ uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); |
+ uTm.dwHighDateTime= (DWORD)(t64 >> 32); |
+ osFileTimeToLocalFileTime(&uTm,&lTm); |
+ osFileTimeToSystemTime(&lTm,&pTm); |
+ y.tm_year = pTm.wYear - 1900; |
+ y.tm_mon = pTm.wMonth - 1; |
+ y.tm_wday = pTm.wDayOfWeek; |
+ y.tm_mday = pTm.wDay; |
+ y.tm_hour = pTm.wHour; |
+ y.tm_min = pTm.wMinute; |
+ y.tm_sec = pTm.wSecond; |
+ return &y; |
+} |
+#endif |
+ |
+#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] |
+ |
+/* |
+** Acquire a lock on the handle h |
+*/ |
+static void winceMutexAcquire(HANDLE h){ |
+ DWORD dwErr; |
+ do { |
+ dwErr = osWaitForSingleObject(h, INFINITE); |
+ } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED); |
+} |
+/* |
+** Release a lock acquired by winceMutexAcquire() |
+*/ |
+#define winceMutexRelease(h) ReleaseMutex(h) |
+ |
+/* |
+** Create the mutex and shared memory used for locking in the file |
+** descriptor pFile |
+*/ |
+static int winceCreateLock(const char *zFilename, winFile *pFile){ |
+ LPWSTR zTok; |
+ LPWSTR zName; |
+ DWORD lastErrno; |
+ BOOL bLogged = FALSE; |
+ BOOL bInit = TRUE; |
+ |
+ zName = winUtf8ToUnicode(zFilename); |
+ if( zName==0 ){ |
+ /* out of memory */ |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ |
+ /* Initialize the local lockdata */ |
+ memset(&pFile->local, 0, sizeof(pFile->local)); |
+ |
+ /* Replace the backslashes from the filename and lowercase it |
+ ** to derive a mutex name. */ |
+ zTok = osCharLowerW(zName); |
+ for (;*zTok;zTok++){ |
+ if (*zTok == '\\') *zTok = '_'; |
+ } |
+ |
+ /* Create/open the named mutex */ |
+ pFile->hMutex = osCreateMutexW(NULL, FALSE, zName); |
+ if (!pFile->hMutex){ |
+ pFile->lastErrno = osGetLastError(); |
+ sqlite3_free(zName); |
+ return winLogError(SQLITE_IOERR, pFile->lastErrno, |
+ "winceCreateLock1", zFilename); |
+ } |
+ |
+ /* Acquire the mutex before continuing */ |
+ winceMutexAcquire(pFile->hMutex); |
+ |
+ /* Since the names of named mutexes, semaphores, file mappings etc are |
+ ** case-sensitive, take advantage of that by uppercasing the mutex name |
+ ** and using that as the shared filemapping name. |
+ */ |
+ osCharUpperW(zName); |
+ pFile->hShared = osCreateFileMappingW(INVALID_HANDLE_VALUE, NULL, |
+ PAGE_READWRITE, 0, sizeof(winceLock), |
+ zName); |
+ |
+ /* Set a flag that indicates we're the first to create the memory so it |
+ ** must be zero-initialized */ |
+ lastErrno = osGetLastError(); |
+ if (lastErrno == ERROR_ALREADY_EXISTS){ |
+ bInit = FALSE; |
+ } |
+ |
+ sqlite3_free(zName); |
+ |
+ /* If we succeeded in making the shared memory handle, map it. */ |
+ if( pFile->hShared ){ |
+ pFile->shared = (winceLock*)osMapViewOfFile(pFile->hShared, |
+ FILE_MAP_READ|FILE_MAP_WRITE, 0, 0, sizeof(winceLock)); |
+ /* If mapping failed, close the shared memory handle and erase it */ |
+ if( !pFile->shared ){ |
+ pFile->lastErrno = osGetLastError(); |
+ winLogError(SQLITE_IOERR, pFile->lastErrno, |
+ "winceCreateLock2", zFilename); |
+ bLogged = TRUE; |
+ osCloseHandle(pFile->hShared); |
+ pFile->hShared = NULL; |
+ } |
+ } |
+ |
+ /* If shared memory could not be created, then close the mutex and fail */ |
+ if( pFile->hShared==NULL ){ |
+ if( !bLogged ){ |
+ pFile->lastErrno = lastErrno; |
+ winLogError(SQLITE_IOERR, pFile->lastErrno, |
+ "winceCreateLock3", zFilename); |
+ bLogged = TRUE; |
+ } |
+ winceMutexRelease(pFile->hMutex); |
+ osCloseHandle(pFile->hMutex); |
+ pFile->hMutex = NULL; |
+ return SQLITE_IOERR; |
+ } |
+ |
+ /* Initialize the shared memory if we're supposed to */ |
+ if( bInit ){ |
+ memset(pFile->shared, 0, sizeof(winceLock)); |
+ } |
+ |
+ winceMutexRelease(pFile->hMutex); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Destroy the part of winFile that deals with wince locks |
+*/ |
+static void winceDestroyLock(winFile *pFile){ |
+ if (pFile->hMutex){ |
+ /* Acquire the mutex */ |
+ winceMutexAcquire(pFile->hMutex); |
+ |
+ /* The following blocks should probably assert in debug mode, but they |
+ are to cleanup in case any locks remained open */ |
+ if (pFile->local.nReaders){ |
+ pFile->shared->nReaders --; |
+ } |
+ if (pFile->local.bReserved){ |
+ pFile->shared->bReserved = FALSE; |
+ } |
+ if (pFile->local.bPending){ |
+ pFile->shared->bPending = FALSE; |
+ } |
+ if (pFile->local.bExclusive){ |
+ pFile->shared->bExclusive = FALSE; |
+ } |
+ |
+ /* De-reference and close our copy of the shared memory handle */ |
+ osUnmapViewOfFile(pFile->shared); |
+ osCloseHandle(pFile->hShared); |
+ |
+ /* Done with the mutex */ |
+ winceMutexRelease(pFile->hMutex); |
+ osCloseHandle(pFile->hMutex); |
+ pFile->hMutex = NULL; |
+ } |
+} |
+ |
+/* |
+** An implementation of the LockFile() API of Windows for CE |
+*/ |
+static BOOL winceLockFile( |
+ LPHANDLE phFile, |
+ DWORD dwFileOffsetLow, |
+ DWORD dwFileOffsetHigh, |
+ DWORD nNumberOfBytesToLockLow, |
+ DWORD nNumberOfBytesToLockHigh |
+){ |
+ winFile *pFile = HANDLE_TO_WINFILE(phFile); |
+ BOOL bReturn = FALSE; |
+ |
+ UNUSED_PARAMETER(dwFileOffsetHigh); |
+ UNUSED_PARAMETER(nNumberOfBytesToLockHigh); |
+ |
+ if (!pFile->hMutex) return TRUE; |
+ winceMutexAcquire(pFile->hMutex); |
+ |
+ /* Wanting an exclusive lock? */ |
+ if (dwFileOffsetLow == (DWORD)SHARED_FIRST |
+ && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ |
+ if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ |
+ pFile->shared->bExclusive = TRUE; |
+ pFile->local.bExclusive = TRUE; |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ /* Want a read-only lock? */ |
+ else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && |
+ nNumberOfBytesToLockLow == 1){ |
+ if (pFile->shared->bExclusive == 0){ |
+ pFile->local.nReaders ++; |
+ if (pFile->local.nReaders == 1){ |
+ pFile->shared->nReaders ++; |
+ } |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ /* Want a pending lock? */ |
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE |
+ && nNumberOfBytesToLockLow == 1){ |
+ /* If no pending lock has been acquired, then acquire it */ |
+ if (pFile->shared->bPending == 0) { |
+ pFile->shared->bPending = TRUE; |
+ pFile->local.bPending = TRUE; |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ /* Want a reserved lock? */ |
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE |
+ && nNumberOfBytesToLockLow == 1){ |
+ if (pFile->shared->bReserved == 0) { |
+ pFile->shared->bReserved = TRUE; |
+ pFile->local.bReserved = TRUE; |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ winceMutexRelease(pFile->hMutex); |
+ return bReturn; |
+} |
+ |
+/* |
+** An implementation of the UnlockFile API of Windows for CE |
+*/ |
+static BOOL winceUnlockFile( |
+ LPHANDLE phFile, |
+ DWORD dwFileOffsetLow, |
+ DWORD dwFileOffsetHigh, |
+ DWORD nNumberOfBytesToUnlockLow, |
+ DWORD nNumberOfBytesToUnlockHigh |
+){ |
+ winFile *pFile = HANDLE_TO_WINFILE(phFile); |
+ BOOL bReturn = FALSE; |
+ |
+ UNUSED_PARAMETER(dwFileOffsetHigh); |
+ UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); |
+ |
+ if (!pFile->hMutex) return TRUE; |
+ winceMutexAcquire(pFile->hMutex); |
+ |
+ /* Releasing a reader lock or an exclusive lock */ |
+ if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ |
+ /* Did we have an exclusive lock? */ |
+ if (pFile->local.bExclusive){ |
+ assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); |
+ pFile->local.bExclusive = FALSE; |
+ pFile->shared->bExclusive = FALSE; |
+ bReturn = TRUE; |
+ } |
+ |
+ /* Did we just have a reader lock? */ |
+ else if (pFile->local.nReaders){ |
+ assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE |
+ || nNumberOfBytesToUnlockLow == 1); |
+ pFile->local.nReaders --; |
+ if (pFile->local.nReaders == 0) |
+ { |
+ pFile->shared->nReaders --; |
+ } |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ /* Releasing a pending lock */ |
+ else if (dwFileOffsetLow == (DWORD)PENDING_BYTE |
+ && nNumberOfBytesToUnlockLow == 1){ |
+ if (pFile->local.bPending){ |
+ pFile->local.bPending = FALSE; |
+ pFile->shared->bPending = FALSE; |
+ bReturn = TRUE; |
+ } |
+ } |
+ /* Releasing a reserved lock */ |
+ else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE |
+ && nNumberOfBytesToUnlockLow == 1){ |
+ if (pFile->local.bReserved) { |
+ pFile->local.bReserved = FALSE; |
+ pFile->shared->bReserved = FALSE; |
+ bReturn = TRUE; |
+ } |
+ } |
+ |
+ winceMutexRelease(pFile->hMutex); |
+ return bReturn; |
+} |
+/* |
+** End of the special code for wince |
+*****************************************************************************/ |
+#endif /* SQLITE_OS_WINCE */ |
+ |
+/* |
+** Lock a file region. |
+*/ |
+static BOOL winLockFile( |
+ LPHANDLE phFile, |
+ DWORD flags, |
+ DWORD offsetLow, |
+ DWORD offsetHigh, |
+ DWORD numBytesLow, |
+ DWORD numBytesHigh |
+){ |
+#if SQLITE_OS_WINCE |
+ /* |
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
+ ** API LockFile. |
+ */ |
+ return winceLockFile(phFile, offsetLow, offsetHigh, |
+ numBytesLow, numBytesHigh); |
+#else |
+ if( osIsNT() ){ |
+ OVERLAPPED ovlp; |
+ memset(&ovlp, 0, sizeof(OVERLAPPED)); |
+ ovlp.Offset = offsetLow; |
+ ovlp.OffsetHigh = offsetHigh; |
+ return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp); |
+ }else{ |
+ return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
+ numBytesHigh); |
+ } |
+#endif |
+} |
+ |
+/* |
+** Unlock a file region. |
+ */ |
+static BOOL winUnlockFile( |
+ LPHANDLE phFile, |
+ DWORD offsetLow, |
+ DWORD offsetHigh, |
+ DWORD numBytesLow, |
+ DWORD numBytesHigh |
+){ |
+#if SQLITE_OS_WINCE |
+ /* |
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
+ ** API UnlockFile. |
+ */ |
+ return winceUnlockFile(phFile, offsetLow, offsetHigh, |
+ numBytesLow, numBytesHigh); |
+#else |
+ if( osIsNT() ){ |
+ OVERLAPPED ovlp; |
+ memset(&ovlp, 0, sizeof(OVERLAPPED)); |
+ ovlp.Offset = offsetLow; |
+ ovlp.OffsetHigh = offsetHigh; |
+ return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp); |
+ }else{ |
+ return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
+ numBytesHigh); |
+ } |
+#endif |
+} |
+ |
+/***************************************************************************** |
+** The next group of routines implement the I/O methods specified |
+** by the sqlite3_io_methods object. |
+******************************************************************************/ |
+ |
+/* |
+** Some Microsoft compilers lack this definition. |
+*/ |
+#ifndef INVALID_SET_FILE_POINTER |
+# define INVALID_SET_FILE_POINTER ((DWORD)-1) |
+#endif |
+ |
+/* |
+** Move the current position of the file handle passed as the first |
+** argument to offset iOffset within the file. If successful, return 0. |
+** Otherwise, set pFile->lastErrno and return non-zero. |
+*/ |
+static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ |
+#if !SQLITE_OS_WINRT |
+ LONG upperBits; /* Most sig. 32 bits of new offset */ |
+ LONG lowerBits; /* Least sig. 32 bits of new offset */ |
+ DWORD dwRet; /* Value returned by SetFilePointer() */ |
+ DWORD lastErrno; /* Value returned by GetLastError() */ |
+ |
+ OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); |
+ |
+ upperBits = (LONG)((iOffset>>32) & 0x7fffffff); |
+ lowerBits = (LONG)(iOffset & 0xffffffff); |
+ |
+ /* API oddity: If successful, SetFilePointer() returns a dword |
+ ** containing the lower 32-bits of the new file-offset. Or, if it fails, |
+ ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, |
+ ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine |
+ ** whether an error has actually occurred, it is also necessary to call |
+ ** GetLastError(). |
+ */ |
+ dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); |
+ |
+ if( (dwRet==INVALID_SET_FILE_POINTER |
+ && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ |
+ pFile->lastErrno = lastErrno; |
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, |
+ "winSeekFile", pFile->zPath); |
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); |
+ return 1; |
+ } |
+ |
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return 0; |
+#else |
+ /* |
+ ** Same as above, except that this implementation works for WinRT. |
+ */ |
+ |
+ LARGE_INTEGER x; /* The new offset */ |
+ BOOL bRet; /* Value returned by SetFilePointerEx() */ |
+ |
+ x.QuadPart = iOffset; |
+ bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); |
+ |
+ if(!bRet){ |
+ pFile->lastErrno = osGetLastError(); |
+ winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, |
+ "winSeekFile", pFile->zPath); |
+ OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); |
+ return 1; |
+ } |
+ |
+ OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return 0; |
+#endif |
+} |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+/* Forward references to VFS helper methods used for memory mapped files */ |
+static int winMapfile(winFile*, sqlite3_int64); |
+static int winUnmapfile(winFile*); |
+#endif |
+ |
+/* |
+** Close a file. |
+** |
+** It is reported that an attempt to close a handle might sometimes |
+** fail. This is a very unreasonable result, but Windows is notorious |
+** for being unreasonable so I do not doubt that it might happen. If |
+** the close fails, we pause for 100 milliseconds and try again. As |
+** many as MX_CLOSE_ATTEMPT attempts to close the handle are made before |
+** giving up and returning an error. |
+*/ |
+#define MX_CLOSE_ATTEMPT 3 |
+static int winClose(sqlite3_file *id){ |
+ int rc, cnt = 0; |
+ winFile *pFile = (winFile*)id; |
+ |
+ assert( id!=0 ); |
+#ifndef SQLITE_OMIT_WAL |
+ assert( pFile->pShm==0 ); |
+#endif |
+ assert( pFile->h!=NULL && pFile->h!=INVALID_HANDLE_VALUE ); |
+ OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ winUnmapfile(pFile); |
+#endif |
+ |
+ do{ |
+ rc = osCloseHandle(pFile->h); |
+ /* SimulateIOError( rc=0; cnt=MX_CLOSE_ATTEMPT; ); */ |
+ }while( rc==0 && ++cnt < MX_CLOSE_ATTEMPT && (sqlite3_win32_sleep(100), 1) ); |
+#if SQLITE_OS_WINCE |
+#define WINCE_DELETION_ATTEMPTS 3 |
+ winceDestroyLock(pFile); |
+ if( pFile->zDeleteOnClose ){ |
+ int cnt = 0; |
+ while( |
+ osDeleteFileW(pFile->zDeleteOnClose)==0 |
+ && osGetFileAttributesW(pFile->zDeleteOnClose)!=0xffffffff |
+ && cnt++ < WINCE_DELETION_ATTEMPTS |
+ ){ |
+ sqlite3_win32_sleep(100); /* Wait a little before trying again */ |
+ } |
+ sqlite3_free(pFile->zDeleteOnClose); |
+ } |
+#endif |
+ if( rc ){ |
+ pFile->h = NULL; |
+ } |
+ OpenCounter(-1); |
+ OSTRACE(("CLOSE pid=%lu, pFile=%p, file=%p, rc=%s\n", |
+ osGetCurrentProcessId(), pFile, pFile->h, rc ? "ok" : "failed")); |
+ return rc ? SQLITE_OK |
+ : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(), |
+ "winClose", pFile->zPath); |
+} |
+ |
+/* |
+** Read data from a file into a buffer. Return SQLITE_OK if all |
+** bytes were read successfully and SQLITE_IOERR if anything goes |
+** wrong. |
+*/ |
+static int winRead( |
+ sqlite3_file *id, /* File to read from */ |
+ void *pBuf, /* Write content into this buffer */ |
+ int amt, /* Number of bytes to read */ |
+ sqlite3_int64 offset /* Begin reading at this offset */ |
+){ |
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ OVERLAPPED overlapped; /* The offset for ReadFile. */ |
+#endif |
+ winFile *pFile = (winFile*)id; /* file handle */ |
+ DWORD nRead; /* Number of bytes actually read from file */ |
+ int nRetry = 0; /* Number of retrys */ |
+ |
+ assert( id!=0 ); |
+ assert( amt>0 ); |
+ assert( offset>=0 ); |
+ SimulateIOError(return SQLITE_IOERR_READ); |
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " |
+ "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, |
+ pFile->h, pBuf, amt, offset, pFile->locktype)); |
+ |
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 |
+ /* Deal with as much of this read request as possible by transfering |
+ ** data from the memory mapping using memcpy(). */ |
+ if( offset<pFile->mmapSize ){ |
+ if( offset+amt <= pFile->mmapSize ){ |
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], amt); |
+ OSTRACE(("READ-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+ }else{ |
+ int nCopy = (int)(pFile->mmapSize - offset); |
+ memcpy(pBuf, &((u8 *)(pFile->pMapRegion))[offset], nCopy); |
+ pBuf = &((u8 *)pBuf)[nCopy]; |
+ amt -= nCopy; |
+ offset += nCopy; |
+ } |
+ } |
+#endif |
+ |
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ if( winSeekFile(pFile, offset) ){ |
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_FULL; |
+ } |
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, 0) ){ |
+#else |
+ memset(&overlapped, 0, sizeof(OVERLAPPED)); |
+ overlapped.Offset = (LONG)(offset & 0xffffffff); |
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
+ while( !osReadFile(pFile->h, pBuf, amt, &nRead, &overlapped) && |
+ osGetLastError()!=ERROR_HANDLE_EOF ){ |
+#endif |
+ DWORD lastErrno; |
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue; |
+ pFile->lastErrno = lastErrno; |
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_READ\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return winLogError(SQLITE_IOERR_READ, pFile->lastErrno, |
+ "winRead", pFile->zPath); |
+ } |
+ winLogIoerr(nRetry, __LINE__); |
+ if( nRead<(DWORD)amt ){ |
+ /* Unread parts of the buffer must be zero-filled */ |
+ memset(&((char*)pBuf)[nRead], 0, amt-nRead); |
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_SHORT_READ\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_IOERR_SHORT_READ; |
+ } |
+ |
+ OSTRACE(("READ pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Write data from a buffer into a file. Return SQLITE_OK on success |
+** or some other error code on failure. |
+*/ |
+static int winWrite( |
+ sqlite3_file *id, /* File to write into */ |
+ const void *pBuf, /* The bytes to be written */ |
+ int amt, /* Number of bytes to write */ |
+ sqlite3_int64 offset /* Offset into the file to begin writing at */ |
+){ |
+ int rc = 0; /* True if error has occurred, else false */ |
+ winFile *pFile = (winFile*)id; /* File handle */ |
+ int nRetry = 0; /* Number of retries */ |
+ |
+ assert( amt>0 ); |
+ assert( pFile ); |
+ SimulateIOError(return SQLITE_IOERR_WRITE); |
+ SimulateDiskfullError(return SQLITE_FULL); |
+ |
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, buffer=%p, amount=%d, " |
+ "offset=%lld, lock=%d\n", osGetCurrentProcessId(), pFile, |
+ pFile->h, pBuf, amt, offset, pFile->locktype)); |
+ |
+#if defined(SQLITE_MMAP_READWRITE) && SQLITE_MAX_MMAP_SIZE>0 |
+ /* Deal with as much of this write request as possible by transfering |
+ ** data from the memory mapping using memcpy(). */ |
+ if( offset<pFile->mmapSize ){ |
+ if( offset+amt <= pFile->mmapSize ){ |
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, amt); |
+ OSTRACE(("WRITE-MMAP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+ }else{ |
+ int nCopy = (int)(pFile->mmapSize - offset); |
+ memcpy(&((u8 *)(pFile->pMapRegion))[offset], pBuf, nCopy); |
+ pBuf = &((u8 *)pBuf)[nCopy]; |
+ amt -= nCopy; |
+ offset += nCopy; |
+ } |
+ } |
+#endif |
+ |
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ rc = winSeekFile(pFile, offset); |
+ if( rc==0 ){ |
+#else |
+ { |
+#endif |
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ OVERLAPPED overlapped; /* The offset for WriteFile. */ |
+#endif |
+ u8 *aRem = (u8 *)pBuf; /* Data yet to be written */ |
+ int nRem = amt; /* Number of bytes yet to be written */ |
+ DWORD nWrite; /* Bytes written by each WriteFile() call */ |
+ DWORD lastErrno = NO_ERROR; /* Value returned by GetLastError() */ |
+ |
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ memset(&overlapped, 0, sizeof(OVERLAPPED)); |
+ overlapped.Offset = (LONG)(offset & 0xffffffff); |
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
+#endif |
+ |
+ while( nRem>0 ){ |
+#if SQLITE_OS_WINCE || defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, 0) ){ |
+#else |
+ if( !osWriteFile(pFile->h, aRem, nRem, &nWrite, &overlapped) ){ |
+#endif |
+ if( winRetryIoerr(&nRetry, &lastErrno) ) continue; |
+ break; |
+ } |
+ assert( nWrite==0 || nWrite<=(DWORD)nRem ); |
+ if( nWrite==0 || nWrite>(DWORD)nRem ){ |
+ lastErrno = osGetLastError(); |
+ break; |
+ } |
+#if !SQLITE_OS_WINCE && !defined(SQLITE_WIN32_NO_OVERLAPPED) |
+ offset += nWrite; |
+ overlapped.Offset = (LONG)(offset & 0xffffffff); |
+ overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff); |
+#endif |
+ aRem += nWrite; |
+ nRem -= nWrite; |
+ } |
+ if( nRem>0 ){ |
+ pFile->lastErrno = lastErrno; |
+ rc = 1; |
+ } |
+ } |
+ |
+ if( rc ){ |
+ if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL ) |
+ || ( pFile->lastErrno==ERROR_DISK_FULL )){ |
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_FULL\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return winLogError(SQLITE_FULL, pFile->lastErrno, |
+ "winWrite1", pFile->zPath); |
+ } |
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_WRITE\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno, |
+ "winWrite2", pFile->zPath); |
+ }else{ |
+ winLogIoerr(nRetry, __LINE__); |
+ } |
+ OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Truncate an open file to a specified size |
+*/ |
+static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ |
+ winFile *pFile = (winFile*)id; /* File handle object */ |
+ int rc = SQLITE_OK; /* Return code for this function */ |
+ DWORD lastErrno; |
+ |
+ assert( pFile ); |
+ SimulateIOError(return SQLITE_IOERR_TRUNCATE); |
+ OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, size=%lld, lock=%d\n", |
+ osGetCurrentProcessId(), pFile, pFile->h, nByte, pFile->locktype)); |
+ |
+ /* If the user has configured a chunk-size for this file, truncate the |
+ ** file so that it consists of an integer number of chunks (i.e. the |
+ ** actual file size after the operation may be larger than the requested |
+ ** size). |
+ */ |
+ if( pFile->szChunk>0 ){ |
+ nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk; |
+ } |
+ |
+ /* SetEndOfFile() returns non-zero when successful, or zero when it fails. */ |
+ if( winSeekFile(pFile, nByte) ){ |
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, |
+ "winTruncate1", pFile->zPath); |
+ }else if( 0==osSetEndOfFile(pFile->h) && |
+ ((lastErrno = osGetLastError())!=ERROR_USER_MAPPED_FILE) ){ |
+ pFile->lastErrno = lastErrno; |
+ rc = winLogError(SQLITE_IOERR_TRUNCATE, pFile->lastErrno, |
+ "winTruncate2", pFile->zPath); |
+ } |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ /* If the file was truncated to a size smaller than the currently |
+ ** mapped region, reduce the effective mapping size as well. SQLite will |
+ ** use read() and write() to access data beyond this point from now on. |
+ */ |
+ if( pFile->pMapRegion && nByte<pFile->mmapSize ){ |
+ pFile->mmapSize = nByte; |
+ } |
+#endif |
+ |
+ OSTRACE(("TRUNCATE pid=%lu, pFile=%p, file=%p, rc=%s\n", |
+ osGetCurrentProcessId(), pFile, pFile->h, sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+#ifdef SQLITE_TEST |
+/* |
+** Count the number of fullsyncs and normal syncs. This is used to test |
+** that syncs and fullsyncs are occuring at the right times. |
+*/ |
+SQLITE_API int sqlite3_sync_count = 0; |
+SQLITE_API int sqlite3_fullsync_count = 0; |
+#endif |
+ |
+/* |
+** Make sure all writes to a particular file are committed to disk. |
+*/ |
+static int winSync(sqlite3_file *id, int flags){ |
+#ifndef SQLITE_NO_SYNC |
+ /* |
+ ** Used only when SQLITE_NO_SYNC is not defined. |
+ */ |
+ BOOL rc; |
+#endif |
+#if !defined(NDEBUG) || !defined(SQLITE_NO_SYNC) || \ |
+ defined(SQLITE_HAVE_OS_TRACE) |
+ /* |
+ ** Used when SQLITE_NO_SYNC is not defined and by the assert() and/or |
+ ** OSTRACE() macros. |
+ */ |
+ winFile *pFile = (winFile*)id; |
+#else |
+ UNUSED_PARAMETER(id); |
+#endif |
+ |
+ assert( pFile ); |
+ /* Check that one of SQLITE_SYNC_NORMAL or FULL was passed */ |
+ assert((flags&0x0F)==SQLITE_SYNC_NORMAL |
+ || (flags&0x0F)==SQLITE_SYNC_FULL |
+ ); |
+ |
+ /* Unix cannot, but some systems may return SQLITE_FULL from here. This |
+ ** line is to test that doing so does not cause any problems. |
+ */ |
+ SimulateDiskfullError( return SQLITE_FULL ); |
+ |
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, flags=%x, lock=%d\n", |
+ osGetCurrentProcessId(), pFile, pFile->h, flags, |
+ pFile->locktype)); |
+ |
+#ifndef SQLITE_TEST |
+ UNUSED_PARAMETER(flags); |
+#else |
+ if( (flags&0x0F)==SQLITE_SYNC_FULL ){ |
+ sqlite3_fullsync_count++; |
+ } |
+ sqlite3_sync_count++; |
+#endif |
+ |
+ /* If we compiled with the SQLITE_NO_SYNC flag, then syncing is a |
+ ** no-op |
+ */ |
+#ifdef SQLITE_NO_SYNC |
+ OSTRACE(("SYNC-NOP pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+#else |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ if( pFile->pMapRegion ){ |
+ if( osFlushViewOfFile(pFile->pMapRegion, 0) ){ |
+ OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " |
+ "rc=SQLITE_OK\n", osGetCurrentProcessId(), |
+ pFile, pFile->pMapRegion)); |
+ }else{ |
+ pFile->lastErrno = osGetLastError(); |
+ OSTRACE(("SYNC-MMAP pid=%lu, pFile=%p, pMapRegion=%p, " |
+ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), |
+ pFile, pFile->pMapRegion)); |
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, |
+ "winSync1", pFile->zPath); |
+ } |
+ } |
+#endif |
+ rc = osFlushFileBuffers(pFile->h); |
+ SimulateIOError( rc=FALSE ); |
+ if( rc ){ |
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return SQLITE_OK; |
+ }else{ |
+ pFile->lastErrno = osGetLastError(); |
+ OSTRACE(("SYNC pid=%lu, pFile=%p, file=%p, rc=SQLITE_IOERR_FSYNC\n", |
+ osGetCurrentProcessId(), pFile, pFile->h)); |
+ return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno, |
+ "winSync2", pFile->zPath); |
+ } |
+#endif |
+} |
+ |
+/* |
+** Determine the current size of a file in bytes |
+*/ |
+static int winFileSize(sqlite3_file *id, sqlite3_int64 *pSize){ |
+ winFile *pFile = (winFile*)id; |
+ int rc = SQLITE_OK; |
+ |
+ assert( id!=0 ); |
+ assert( pSize!=0 ); |
+ SimulateIOError(return SQLITE_IOERR_FSTAT); |
+ OSTRACE(("SIZE file=%p, pSize=%p\n", pFile->h, pSize)); |
+ |
+#if SQLITE_OS_WINRT |
+ { |
+ FILE_STANDARD_INFO info; |
+ if( osGetFileInformationByHandleEx(pFile->h, FileStandardInfo, |
+ &info, sizeof(info)) ){ |
+ *pSize = info.EndOfFile.QuadPart; |
+ }else{ |
+ pFile->lastErrno = osGetLastError(); |
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, |
+ "winFileSize", pFile->zPath); |
+ } |
+ } |
+#else |
+ { |
+ DWORD upperBits; |
+ DWORD lowerBits; |
+ DWORD lastErrno; |
+ |
+ lowerBits = osGetFileSize(pFile->h, &upperBits); |
+ *pSize = (((sqlite3_int64)upperBits)<<32) + lowerBits; |
+ if( (lowerBits == INVALID_FILE_SIZE) |
+ && ((lastErrno = osGetLastError())!=NO_ERROR) ){ |
+ pFile->lastErrno = lastErrno; |
+ rc = winLogError(SQLITE_IOERR_FSTAT, pFile->lastErrno, |
+ "winFileSize", pFile->zPath); |
+ } |
+ } |
+#endif |
+ OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n", |
+ pFile->h, pSize, *pSize, sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+/* |
+** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems. |
+*/ |
+#ifndef LOCKFILE_FAIL_IMMEDIATELY |
+# define LOCKFILE_FAIL_IMMEDIATELY 1 |
+#endif |
+ |
+#ifndef LOCKFILE_EXCLUSIVE_LOCK |
+# define LOCKFILE_EXCLUSIVE_LOCK 2 |
+#endif |
+ |
+/* |
+** Historically, SQLite has used both the LockFile and LockFileEx functions. |
+** When the LockFile function was used, it was always expected to fail |
+** immediately if the lock could not be obtained. Also, it always expected to |
+** obtain an exclusive lock. These flags are used with the LockFileEx function |
+** and reflect those expectations; therefore, they should not be changed. |
+*/ |
+#ifndef SQLITE_LOCKFILE_FLAGS |
+# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \ |
+ LOCKFILE_EXCLUSIVE_LOCK) |
+#endif |
+ |
+/* |
+** Currently, SQLite never calls the LockFileEx function without wanting the |
+** call to fail immediately if the lock cannot be obtained. |
+*/ |
+#ifndef SQLITE_LOCKFILEEX_FLAGS |
+# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY) |
+#endif |
+ |
+/* |
+** Acquire a reader lock. |
+** Different API routines are called depending on whether or not this |
+** is Win9x or WinNT. |
+*/ |
+static int winGetReadLock(winFile *pFile){ |
+ int res; |
+ OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); |
+ if( osIsNT() ){ |
+#if SQLITE_OS_WINCE |
+ /* |
+ ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
+ ** API LockFileEx. |
+ */ |
+ res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); |
+#else |
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, |
+ SHARED_SIZE, 0); |
+#endif |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ int lk; |
+ sqlite3_randomness(sizeof(lk), &lk); |
+ pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); |
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, |
+ SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); |
+ } |
+#endif |
+ if( res == 0 ){ |
+ pFile->lastErrno = osGetLastError(); |
+ /* No need to log a failure to lock */ |
+ } |
+ OSTRACE(("READ-LOCK file=%p, result=%d\n", pFile->h, res)); |
+ return res; |
+} |
+ |
+/* |
+** Undo a readlock |
+*/ |
+static int winUnlockReadLock(winFile *pFile){ |
+ int res; |
+ DWORD lastErrno; |
+ OSTRACE(("READ-UNLOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); |
+ if( osIsNT() ){ |
+ res = winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ res = winUnlockFile(&pFile->h, SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); |
+ } |
+#endif |
+ if( res==0 && ((lastErrno = osGetLastError())!=ERROR_NOT_LOCKED) ){ |
+ pFile->lastErrno = lastErrno; |
+ winLogError(SQLITE_IOERR_UNLOCK, pFile->lastErrno, |
+ "winUnlockReadLock", pFile->zPath); |
+ } |
+ OSTRACE(("READ-UNLOCK file=%p, result=%d\n", pFile->h, res)); |
+ return res; |
+} |
+ |
+/* |
+** Lock the file with the lock specified by parameter locktype - one |
+** of the following: |
+** |
+** (1) SHARED_LOCK |
+** (2) RESERVED_LOCK |
+** (3) PENDING_LOCK |
+** (4) EXCLUSIVE_LOCK |
+** |
+** Sometimes when requesting one lock state, additional lock states |
+** are inserted in between. The locking might fail on one of the later |
+** transitions leaving the lock state different from what it started but |
+** still short of its goal. The following chart shows the allowed |
+** transitions and the inserted intermediate states: |
+** |
+** UNLOCKED -> SHARED |
+** SHARED -> RESERVED |
+** SHARED -> (PENDING) -> EXCLUSIVE |
+** RESERVED -> (PENDING) -> EXCLUSIVE |
+** PENDING -> EXCLUSIVE |
+** |
+** This routine will only increase a lock. The winUnlock() routine |
+** erases all locks at once and returns us immediately to locking level 0. |
+** It is not possible to lower the locking level one step at a time. You |
+** must go straight to locking level 0. |
+*/ |
+static int winLock(sqlite3_file *id, int locktype){ |
+ int rc = SQLITE_OK; /* Return code from subroutines */ |
+ int res = 1; /* Result of a Windows lock call */ |
+ int newLocktype; /* Set pFile->locktype to this value before exiting */ |
+ int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */ |
+ winFile *pFile = (winFile*)id; |
+ DWORD lastErrno = NO_ERROR; |
+ |
+ assert( id!=0 ); |
+ OSTRACE(("LOCK file=%p, oldLock=%d(%d), newLock=%d\n", |
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); |
+ |
+ /* If there is already a lock of this type or more restrictive on the |
+ ** OsFile, do nothing. Don't use the end_lock: exit path, as |
+ ** sqlite3OsEnterMutex() hasn't been called yet. |
+ */ |
+ if( pFile->locktype>=locktype ){ |
+ OSTRACE(("LOCK-HELD file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ |
+ /* Do not allow any kind of write-lock on a read-only database |
+ */ |
+ if( (pFile->ctrlFlags & WINFILE_RDONLY)!=0 && locktype>=RESERVED_LOCK ){ |
+ return SQLITE_IOERR_LOCK; |
+ } |
+ |
+ /* Make sure the locking sequence is correct |
+ */ |
+ assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); |
+ assert( locktype!=PENDING_LOCK ); |
+ assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); |
+ |
+ /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or |
+ ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of |
+ ** the PENDING_LOCK byte is temporary. |
+ */ |
+ newLocktype = pFile->locktype; |
+ if( (pFile->locktype==NO_LOCK) |
+ || ( (locktype==EXCLUSIVE_LOCK) |
+ && (pFile->locktype==RESERVED_LOCK)) |
+ ){ |
+ int cnt = 3; |
+ while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, |
+ PENDING_BYTE, 0, 1, 0))==0 ){ |
+ /* Try 3 times to get the pending lock. This is needed to work |
+ ** around problems caused by indexing and/or anti-virus software on |
+ ** Windows systems. |
+ ** If you are using this code as a model for alternative VFSes, do not |
+ ** copy this retry logic. It is a hack intended for Windows only. |
+ */ |
+ lastErrno = osGetLastError(); |
+ OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", |
+ pFile->h, cnt, res)); |
+ if( lastErrno==ERROR_INVALID_HANDLE ){ |
+ pFile->lastErrno = lastErrno; |
+ rc = SQLITE_IOERR_LOCK; |
+ OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", |
+ pFile->h, cnt, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+ if( cnt ) sqlite3_win32_sleep(1); |
+ } |
+ gotPendingLock = res; |
+ if( !res ){ |
+ lastErrno = osGetLastError(); |
+ } |
+ } |
+ |
+ /* Acquire a shared lock |
+ */ |
+ if( locktype==SHARED_LOCK && res ){ |
+ assert( pFile->locktype==NO_LOCK ); |
+ res = winGetReadLock(pFile); |
+ if( res ){ |
+ newLocktype = SHARED_LOCK; |
+ }else{ |
+ lastErrno = osGetLastError(); |
+ } |
+ } |
+ |
+ /* Acquire a RESERVED lock |
+ */ |
+ if( locktype==RESERVED_LOCK && res ){ |
+ assert( pFile->locktype==SHARED_LOCK ); |
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, RESERVED_BYTE, 0, 1, 0); |
+ if( res ){ |
+ newLocktype = RESERVED_LOCK; |
+ }else{ |
+ lastErrno = osGetLastError(); |
+ } |
+ } |
+ |
+ /* Acquire a PENDING lock |
+ */ |
+ if( locktype==EXCLUSIVE_LOCK && res ){ |
+ newLocktype = PENDING_LOCK; |
+ gotPendingLock = 0; |
+ } |
+ |
+ /* Acquire an EXCLUSIVE lock |
+ */ |
+ if( locktype==EXCLUSIVE_LOCK && res ){ |
+ assert( pFile->locktype>=SHARED_LOCK ); |
+ res = winUnlockReadLock(pFile); |
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, SHARED_FIRST, 0, |
+ SHARED_SIZE, 0); |
+ if( res ){ |
+ newLocktype = EXCLUSIVE_LOCK; |
+ }else{ |
+ lastErrno = osGetLastError(); |
+ winGetReadLock(pFile); |
+ } |
+ } |
+ |
+ /* If we are holding a PENDING lock that ought to be released, then |
+ ** release it now. |
+ */ |
+ if( gotPendingLock && locktype==SHARED_LOCK ){ |
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); |
+ } |
+ |
+ /* Update the state of the lock has held in the file descriptor then |
+ ** return the appropriate result code. |
+ */ |
+ if( res ){ |
+ rc = SQLITE_OK; |
+ }else{ |
+ pFile->lastErrno = lastErrno; |
+ rc = SQLITE_BUSY; |
+ OSTRACE(("LOCK-FAIL file=%p, wanted=%d, got=%d\n", |
+ pFile->h, locktype, newLocktype)); |
+ } |
+ pFile->locktype = (u8)newLocktype; |
+ OSTRACE(("LOCK file=%p, lock=%d, rc=%s\n", |
+ pFile->h, pFile->locktype, sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+/* |
+** This routine checks if there is a RESERVED lock held on the specified |
+** file by this or any other process. If such a lock is held, return |
+** non-zero, otherwise zero. |
+*/ |
+static int winCheckReservedLock(sqlite3_file *id, int *pResOut){ |
+ int res; |
+ winFile *pFile = (winFile*)id; |
+ |
+ SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); |
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut)); |
+ |
+ assert( id!=0 ); |
+ if( pFile->locktype>=RESERVED_LOCK ){ |
+ res = 1; |
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (local)\n", pFile->h, res)); |
+ }else{ |
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE,0,1,0); |
+ if( res ){ |
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); |
+ } |
+ res = !res; |
+ OSTRACE(("TEST-WR-LOCK file=%p, result=%d (remote)\n", pFile->h, res)); |
+ } |
+ *pResOut = res; |
+ OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
+ pFile->h, pResOut, *pResOut)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Lower the locking level on file descriptor id to locktype. locktype |
+** must be either NO_LOCK or SHARED_LOCK. |
+** |
+** If the locking level of the file descriptor is already at or below |
+** the requested locking level, this routine is a no-op. |
+** |
+** It is not possible for this routine to fail if the second argument |
+** is NO_LOCK. If the second argument is SHARED_LOCK then this routine |
+** might return SQLITE_IOERR; |
+*/ |
+static int winUnlock(sqlite3_file *id, int locktype){ |
+ int type; |
+ winFile *pFile = (winFile*)id; |
+ int rc = SQLITE_OK; |
+ assert( pFile!=0 ); |
+ assert( locktype<=SHARED_LOCK ); |
+ OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", |
+ pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); |
+ type = pFile->locktype; |
+ if( type>=EXCLUSIVE_LOCK ){ |
+ winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); |
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ |
+ /* This should never happen. We should always be able to |
+ ** reacquire the read lock */ |
+ rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), |
+ "winUnlock", pFile->zPath); |
+ } |
+ } |
+ if( type>=RESERVED_LOCK ){ |
+ winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0); |
+ } |
+ if( locktype==NO_LOCK && type>=SHARED_LOCK ){ |
+ winUnlockReadLock(pFile); |
+ } |
+ if( type>=PENDING_LOCK ){ |
+ winUnlockFile(&pFile->h, PENDING_BYTE, 0, 1, 0); |
+ } |
+ pFile->locktype = (u8)locktype; |
+ OSTRACE(("UNLOCK file=%p, lock=%d, rc=%s\n", |
+ pFile->h, pFile->locktype, sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+/* |
+** If *pArg is initially negative then this is a query. Set *pArg to |
+** 1 or 0 depending on whether or not bit mask of pFile->ctrlFlags is set. |
+** |
+** If *pArg is 0 or 1, then clear or set the mask bit of pFile->ctrlFlags. |
+*/ |
+static void winModeBit(winFile *pFile, unsigned char mask, int *pArg){ |
+ if( *pArg<0 ){ |
+ *pArg = (pFile->ctrlFlags & mask)!=0; |
+ }else if( (*pArg)==0 ){ |
+ pFile->ctrlFlags &= ~mask; |
+ }else{ |
+ pFile->ctrlFlags |= mask; |
+ } |
+} |
+ |
+/* Forward references to VFS helper methods used for temporary files */ |
+static int winGetTempname(sqlite3_vfs *, char **); |
+static int winIsDir(const void *); |
+static BOOL winIsDriveLetterAndColon(const char *); |
+ |
+/* |
+** Control and query of the open file handle. |
+*/ |
+static int winFileControl(sqlite3_file *id, int op, void *pArg){ |
+ winFile *pFile = (winFile*)id; |
+ OSTRACE(("FCNTL file=%p, op=%d, pArg=%p\n", pFile->h, op, pArg)); |
+ switch( op ){ |
+ case SQLITE_FCNTL_LOCKSTATE: { |
+ *(int*)pArg = pFile->locktype; |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_LAST_ERRNO: { |
+ *(int*)pArg = (int)pFile->lastErrno; |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_CHUNK_SIZE: { |
+ pFile->szChunk = *(int *)pArg; |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_SIZE_HINT: { |
+ if( pFile->szChunk>0 ){ |
+ sqlite3_int64 oldSz; |
+ int rc = winFileSize(id, &oldSz); |
+ if( rc==SQLITE_OK ){ |
+ sqlite3_int64 newSz = *(sqlite3_int64*)pArg; |
+ if( newSz>oldSz ){ |
+ SimulateIOErrorBenign(1); |
+ rc = winTruncate(id, newSz); |
+ SimulateIOErrorBenign(0); |
+ } |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_PERSIST_WAL: { |
+ winModeBit(pFile, WINFILE_PERSIST_WAL, (int*)pArg); |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_POWERSAFE_OVERWRITE: { |
+ winModeBit(pFile, WINFILE_PSOW, (int*)pArg); |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_VFSNAME: { |
+ *(char**)pArg = sqlite3_mprintf("%s", pFile->pVfs->zName); |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+ case SQLITE_FCNTL_WIN32_AV_RETRY: { |
+ int *a = (int*)pArg; |
+ if( a[0]>0 ){ |
+ winIoerrRetry = a[0]; |
+ }else{ |
+ a[0] = winIoerrRetry; |
+ } |
+ if( a[1]>0 ){ |
+ winIoerrRetryDelay = a[1]; |
+ }else{ |
+ a[1] = winIoerrRetryDelay; |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_OK\n", pFile->h)); |
+ return SQLITE_OK; |
+ } |
+#ifdef SQLITE_TEST |
+ case SQLITE_FCNTL_WIN32_SET_HANDLE: { |
+ LPHANDLE phFile = (LPHANDLE)pArg; |
+ HANDLE hOldFile = pFile->h; |
+ pFile->h = *phFile; |
+ *phFile = hOldFile; |
+ OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n", |
+ hOldFile, pFile->h)); |
+ return SQLITE_OK; |
+ } |
+#endif |
+ case SQLITE_FCNTL_TEMPFILENAME: { |
+ char *zTFile = 0; |
+ int rc = winGetTempname(pFile->pVfs, &zTFile); |
+ if( rc==SQLITE_OK ){ |
+ *(char**)pArg = zTFile; |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ case SQLITE_FCNTL_MMAP_SIZE: { |
+ i64 newLimit = *(i64*)pArg; |
+ int rc = SQLITE_OK; |
+ if( newLimit>sqlite3GlobalConfig.mxMmap ){ |
+ newLimit = sqlite3GlobalConfig.mxMmap; |
+ } |
+ *(i64*)pArg = pFile->mmapSizeMax; |
+ if( newLimit>=0 && newLimit!=pFile->mmapSizeMax && pFile->nFetchOut==0 ){ |
+ pFile->mmapSizeMax = newLimit; |
+ if( pFile->mmapSize>0 ){ |
+ winUnmapfile(pFile); |
+ rc = winMapfile(pFile, -1); |
+ } |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+#endif |
+ } |
+ OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); |
+ return SQLITE_NOTFOUND; |
+} |
+ |
+/* |
+** Return the sector size in bytes of the underlying block device for |
+** the specified file. This is almost always 512 bytes, but may be |
+** larger for some devices. |
+** |
+** SQLite code assumes this function cannot fail. It also assumes that |
+** if two files are created in the same file-system directory (i.e. |
+** a database and its journal file) that the sector size will be the |
+** same for both. |
+*/ |
+static int winSectorSize(sqlite3_file *id){ |
+ (void)id; |
+ return SQLITE_DEFAULT_SECTOR_SIZE; |
+} |
+ |
+/* |
+** Return a vector of device characteristics. |
+*/ |
+static int winDeviceCharacteristics(sqlite3_file *id){ |
+ winFile *p = (winFile*)id; |
+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | |
+ ((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0); |
+} |
+ |
+/* |
+** Windows will only let you create file view mappings |
+** on allocation size granularity boundaries. |
+** During sqlite3_os_init() we do a GetSystemInfo() |
+** to get the granularity size. |
+*/ |
+static SYSTEM_INFO winSysInfo; |
+ |
+#ifndef SQLITE_OMIT_WAL |
+ |
+/* |
+** Helper functions to obtain and relinquish the global mutex. The |
+** global mutex is used to protect the winLockInfo objects used by |
+** this file, all of which may be shared by multiple threads. |
+** |
+** Function winShmMutexHeld() is used to assert() that the global mutex |
+** is held when required. This function is only used as part of assert() |
+** statements. e.g. |
+** |
+** winShmEnterMutex() |
+** assert( winShmMutexHeld() ); |
+** winShmLeaveMutex() |
+*/ |
+static void winShmEnterMutex(void){ |
+ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+static void winShmLeaveMutex(void){ |
+ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+#ifndef NDEBUG |
+static int winShmMutexHeld(void) { |
+ return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1)); |
+} |
+#endif |
+ |
+/* |
+** Object used to represent a single file opened and mmapped to provide |
+** shared memory. When multiple threads all reference the same |
+** log-summary, each thread has its own winFile object, but they all |
+** point to a single instance of this object. In other words, each |
+** log-summary is opened only once per process. |
+** |
+** winShmMutexHeld() must be true when creating or destroying |
+** this object or while reading or writing the following fields: |
+** |
+** nRef |
+** pNext |
+** |
+** The following fields are read-only after the object is created: |
+** |
+** fid |
+** zFilename |
+** |
+** Either winShmNode.mutex must be held or winShmNode.nRef==0 and |
+** winShmMutexHeld() is true when reading or writing any other field |
+** in this structure. |
+** |
+*/ |
+struct winShmNode { |
+ sqlite3_mutex *mutex; /* Mutex to access this object */ |
+ char *zFilename; /* Name of the file */ |
+ winFile hFile; /* File handle from winOpen */ |
+ |
+ int szRegion; /* Size of shared-memory regions */ |
+ int nRegion; /* Size of array apRegion */ |
+ struct ShmRegion { |
+ HANDLE hMap; /* File handle from CreateFileMapping */ |
+ void *pMap; |
+ } *aRegion; |
+ DWORD lastErrno; /* The Windows errno from the last I/O error */ |
+ |
+ int nRef; /* Number of winShm objects pointing to this */ |
+ winShm *pFirst; /* All winShm objects pointing to this */ |
+ winShmNode *pNext; /* Next in list of all winShmNode objects */ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
+ u8 nextShmId; /* Next available winShm.id value */ |
+#endif |
+}; |
+ |
+/* |
+** A global array of all winShmNode objects. |
+** |
+** The winShmMutexHeld() must be true while reading or writing this list. |
+*/ |
+static winShmNode *winShmNodeList = 0; |
+ |
+/* |
+** Structure used internally by this VFS to record the state of an |
+** open shared memory connection. |
+** |
+** The following fields are initialized when this object is created and |
+** are read-only thereafter: |
+** |
+** winShm.pShmNode |
+** winShm.id |
+** |
+** All other fields are read/write. The winShm.pShmNode->mutex must be held |
+** while accessing any read/write fields. |
+*/ |
+struct winShm { |
+ winShmNode *pShmNode; /* The underlying winShmNode object */ |
+ winShm *pNext; /* Next winShm with the same winShmNode */ |
+ u8 hasMutex; /* True if holding the winShmNode mutex */ |
+ u16 sharedMask; /* Mask of shared locks held */ |
+ u16 exclMask; /* Mask of exclusive locks held */ |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
+ u8 id; /* Id of this connection with its winShmNode */ |
+#endif |
+}; |
+ |
+/* |
+** Constants used for locking |
+*/ |
+#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ |
+#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ |
+ |
+/* |
+** Apply advisory locks for all n bytes beginning at ofst. |
+*/ |
+#define _SHM_UNLCK 1 |
+#define _SHM_RDLCK 2 |
+#define _SHM_WRLCK 3 |
+static int winShmSystemLock( |
+ winShmNode *pFile, /* Apply locks to this open shared-memory segment */ |
+ int lockType, /* _SHM_UNLCK, _SHM_RDLCK, or _SHM_WRLCK */ |
+ int ofst, /* Offset to first byte to be locked/unlocked */ |
+ int nByte /* Number of bytes to lock or unlock */ |
+){ |
+ int rc = 0; /* Result code form Lock/UnlockFileEx() */ |
+ |
+ /* Access to the winShmNode object is serialized by the caller */ |
+ assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); |
+ |
+ OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", |
+ pFile->hFile.h, lockType, ofst, nByte)); |
+ |
+ /* Release/Acquire the system-level lock */ |
+ if( lockType==_SHM_UNLCK ){ |
+ rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); |
+ }else{ |
+ /* Initialize the locking parameters */ |
+ DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; |
+ if( lockType == _SHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; |
+ rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); |
+ } |
+ |
+ if( rc!= 0 ){ |
+ rc = SQLITE_OK; |
+ }else{ |
+ pFile->lastErrno = osGetLastError(); |
+ rc = SQLITE_BUSY; |
+ } |
+ |
+ OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", |
+ pFile->hFile.h, (lockType == _SHM_UNLCK) ? "winUnlockFile" : |
+ "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); |
+ |
+ return rc; |
+} |
+ |
+/* Forward references to VFS methods */ |
+static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); |
+static int winDelete(sqlite3_vfs *,const char*,int); |
+ |
+/* |
+** Purge the winShmNodeList list of all entries with winShmNode.nRef==0. |
+** |
+** This is not a VFS shared-memory method; it is a utility function called |
+** by VFS shared-memory methods. |
+*/ |
+static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){ |
+ winShmNode **pp; |
+ winShmNode *p; |
+ assert( winShmMutexHeld() ); |
+ OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n", |
+ osGetCurrentProcessId(), deleteFlag)); |
+ pp = &winShmNodeList; |
+ while( (p = *pp)!=0 ){ |
+ if( p->nRef==0 ){ |
+ int i; |
+ if( p->mutex ){ sqlite3_mutex_free(p->mutex); } |
+ for(i=0; i<p->nRegion; i++){ |
+ BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap); |
+ OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n", |
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); |
+ UNUSED_VARIABLE_VALUE(bRc); |
+ bRc = osCloseHandle(p->aRegion[i].hMap); |
+ OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", |
+ osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); |
+ UNUSED_VARIABLE_VALUE(bRc); |
+ } |
+ if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ |
+ SimulateIOErrorBenign(1); |
+ winClose((sqlite3_file *)&p->hFile); |
+ SimulateIOErrorBenign(0); |
+ } |
+ if( deleteFlag ){ |
+ SimulateIOErrorBenign(1); |
+ sqlite3BeginBenignMalloc(); |
+ winDelete(pVfs, p->zFilename, 0); |
+ sqlite3EndBenignMalloc(); |
+ SimulateIOErrorBenign(0); |
+ } |
+ *pp = p->pNext; |
+ sqlite3_free(p->aRegion); |
+ sqlite3_free(p); |
+ }else{ |
+ pp = &p->pNext; |
+ } |
+ } |
+} |
+ |
+/* |
+** Open the shared-memory area associated with database file pDbFd. |
+** |
+** When opening a new shared-memory file, if no other instances of that |
+** file are currently open, in this process or in other processes, then |
+** the file must be truncated to zero length or have its header cleared. |
+*/ |
+static int winOpenSharedMemory(winFile *pDbFd){ |
+ struct winShm *p; /* The connection to be opened */ |
+ struct winShmNode *pShmNode = 0; /* The underlying mmapped file */ |
+ int rc; /* Result code */ |
+ struct winShmNode *pNew; /* Newly allocated winShmNode */ |
+ int nName; /* Size of zName in bytes */ |
+ |
+ assert( pDbFd->pShm==0 ); /* Not previously opened */ |
+ |
+ /* Allocate space for the new sqlite3_shm object. Also speculatively |
+ ** allocate space for a new winShmNode and filename. |
+ */ |
+ p = sqlite3MallocZero( sizeof(*p) ); |
+ if( p==0 ) return SQLITE_IOERR_NOMEM; |
+ nName = sqlite3Strlen30(pDbFd->zPath); |
+ pNew = sqlite3MallocZero( sizeof(*pShmNode) + nName + 17 ); |
+ if( pNew==0 ){ |
+ sqlite3_free(p); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ pNew->zFilename = (char*)&pNew[1]; |
+ sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); |
+ sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); |
+ |
+ /* Look to see if there is an existing winShmNode that can be used. |
+ ** If no matching winShmNode currently exists, create a new one. |
+ */ |
+ winShmEnterMutex(); |
+ for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ |
+ /* TBD need to come up with better match here. Perhaps |
+ ** use FILE_ID_BOTH_DIR_INFO Structure. |
+ */ |
+ if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; |
+ } |
+ if( pShmNode ){ |
+ sqlite3_free(pNew); |
+ }else{ |
+ pShmNode = pNew; |
+ pNew = 0; |
+ ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; |
+ pShmNode->pNext = winShmNodeList; |
+ winShmNodeList = pShmNode; |
+ |
+ pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
+ if( pShmNode->mutex==0 ){ |
+ rc = SQLITE_IOERR_NOMEM; |
+ goto shm_open_err; |
+ } |
+ |
+ rc = winOpen(pDbFd->pVfs, |
+ pShmNode->zFilename, /* Name of the file (UTF-8) */ |
+ (sqlite3_file*)&pShmNode->hFile, /* File handle here */ |
+ SQLITE_OPEN_WAL | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
+ 0); |
+ if( SQLITE_OK!=rc ){ |
+ goto shm_open_err; |
+ } |
+ |
+ /* Check to see if another process is holding the dead-man switch. |
+ ** If not, truncate the file to zero length. |
+ */ |
+ if( winShmSystemLock(pShmNode, _SHM_WRLCK, WIN_SHM_DMS, 1)==SQLITE_OK ){ |
+ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, 0); |
+ if( rc!=SQLITE_OK ){ |
+ rc = winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), |
+ "winOpenShm", pDbFd->zPath); |
+ } |
+ } |
+ if( rc==SQLITE_OK ){ |
+ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); |
+ rc = winShmSystemLock(pShmNode, _SHM_RDLCK, WIN_SHM_DMS, 1); |
+ } |
+ if( rc ) goto shm_open_err; |
+ } |
+ |
+ /* Make the new connection a child of the winShmNode */ |
+ p->pShmNode = pShmNode; |
+#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
+ p->id = pShmNode->nextShmId++; |
+#endif |
+ pShmNode->nRef++; |
+ pDbFd->pShm = p; |
+ winShmLeaveMutex(); |
+ |
+ /* The reference count on pShmNode has already been incremented under |
+ ** the cover of the winShmEnterMutex() mutex and the pointer from the |
+ ** new (struct winShm) object to the pShmNode has been set. All that is |
+ ** left to do is to link the new object into the linked list starting |
+ ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex |
+ ** mutex. |
+ */ |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ p->pNext = pShmNode->pFirst; |
+ pShmNode->pFirst = p; |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ return SQLITE_OK; |
+ |
+ /* Jump here on any error */ |
+shm_open_err: |
+ winShmSystemLock(pShmNode, _SHM_UNLCK, WIN_SHM_DMS, 1); |
+ winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ |
+ sqlite3_free(p); |
+ sqlite3_free(pNew); |
+ winShmLeaveMutex(); |
+ return rc; |
+} |
+ |
+/* |
+** Close a connection to shared-memory. Delete the underlying |
+** storage if deleteFlag is true. |
+*/ |
+static int winShmUnmap( |
+ sqlite3_file *fd, /* Database holding shared memory */ |
+ int deleteFlag /* Delete after closing if true */ |
+){ |
+ winFile *pDbFd; /* Database holding shared-memory */ |
+ winShm *p; /* The connection to be closed */ |
+ winShmNode *pShmNode; /* The underlying shared-memory file */ |
+ winShm **pp; /* For looping over sibling connections */ |
+ |
+ pDbFd = (winFile*)fd; |
+ p = pDbFd->pShm; |
+ if( p==0 ) return SQLITE_OK; |
+ pShmNode = p->pShmNode; |
+ |
+ /* Remove connection p from the set of connections associated |
+ ** with pShmNode */ |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} |
+ *pp = p->pNext; |
+ |
+ /* Free the connection p */ |
+ sqlite3_free(p); |
+ pDbFd->pShm = 0; |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ |
+ /* If pShmNode->nRef has reached 0, then close the underlying |
+ ** shared-memory file, too */ |
+ winShmEnterMutex(); |
+ assert( pShmNode->nRef>0 ); |
+ pShmNode->nRef--; |
+ if( pShmNode->nRef==0 ){ |
+ winShmPurge(pDbFd->pVfs, deleteFlag); |
+ } |
+ winShmLeaveMutex(); |
+ |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Change the lock state for a shared-memory segment. |
+*/ |
+static int winShmLock( |
+ sqlite3_file *fd, /* Database file holding the shared memory */ |
+ int ofst, /* First lock to acquire or release */ |
+ int n, /* Number of locks to acquire or release */ |
+ int flags /* What to do with the lock */ |
+){ |
+ winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ |
+ winShm *p = pDbFd->pShm; /* The shared memory being locked */ |
+ winShm *pX; /* For looping over all siblings */ |
+ winShmNode *pShmNode = p->pShmNode; |
+ int rc = SQLITE_OK; /* Result code */ |
+ u16 mask; /* Mask of locks to take or release */ |
+ |
+ assert( ofst>=0 && ofst+n<=SQLITE_SHM_NLOCK ); |
+ assert( n>=1 ); |
+ assert( flags==(SQLITE_SHM_LOCK | SQLITE_SHM_SHARED) |
+ || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) |
+ || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
+ || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
+ assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
+ |
+ mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); |
+ assert( n>1 || mask==(1<<ofst) ); |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ if( flags & SQLITE_SHM_UNLOCK ){ |
+ u16 allMask = 0; /* Mask of locks held by siblings */ |
+ |
+ /* See if any siblings hold this same lock */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( pX==p ) continue; |
+ assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); |
+ allMask |= pX->sharedMask; |
+ } |
+ |
+ /* Unlock the system-level locks */ |
+ if( (mask & allMask)==0 ){ |
+ rc = winShmSystemLock(pShmNode, _SHM_UNLCK, ofst+WIN_SHM_BASE, n); |
+ }else{ |
+ rc = SQLITE_OK; |
+ } |
+ |
+ /* Undo the local locks */ |
+ if( rc==SQLITE_OK ){ |
+ p->exclMask &= ~mask; |
+ p->sharedMask &= ~mask; |
+ } |
+ }else if( flags & SQLITE_SHM_SHARED ){ |
+ u16 allShared = 0; /* Union of locks held by connections other than "p" */ |
+ |
+ /* Find out which shared locks are already held by sibling connections. |
+ ** If any sibling already holds an exclusive lock, go ahead and return |
+ ** SQLITE_BUSY. |
+ */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( (pX->exclMask & mask)!=0 ){ |
+ rc = SQLITE_BUSY; |
+ break; |
+ } |
+ allShared |= pX->sharedMask; |
+ } |
+ |
+ /* Get shared locks at the system level, if necessary */ |
+ if( rc==SQLITE_OK ){ |
+ if( (allShared & mask)==0 ){ |
+ rc = winShmSystemLock(pShmNode, _SHM_RDLCK, ofst+WIN_SHM_BASE, n); |
+ }else{ |
+ rc = SQLITE_OK; |
+ } |
+ } |
+ |
+ /* Get the local shared locks */ |
+ if( rc==SQLITE_OK ){ |
+ p->sharedMask |= mask; |
+ } |
+ }else{ |
+ /* Make sure no sibling connections hold locks that will block this |
+ ** lock. If any do, return SQLITE_BUSY right away. |
+ */ |
+ for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
+ if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ |
+ rc = SQLITE_BUSY; |
+ break; |
+ } |
+ } |
+ |
+ /* Get the exclusive locks at the system level. Then if successful |
+ ** also mark the local connection as being locked. |
+ */ |
+ if( rc==SQLITE_OK ){ |
+ rc = winShmSystemLock(pShmNode, _SHM_WRLCK, ofst+WIN_SHM_BASE, n); |
+ if( rc==SQLITE_OK ){ |
+ assert( (p->sharedMask & mask)==0 ); |
+ p->exclMask |= mask; |
+ } |
+ } |
+ } |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", |
+ osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, |
+ sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+/* |
+** Implement a memory barrier or memory fence on shared memory. |
+** |
+** All loads and stores begun before the barrier must complete before |
+** any load or store begun after the barrier. |
+*/ |
+static void winShmBarrier( |
+ sqlite3_file *fd /* Database holding the shared memory */ |
+){ |
+ UNUSED_PARAMETER(fd); |
+ sqlite3MemoryBarrier(); /* compiler-defined memory barrier */ |
+ winShmEnterMutex(); /* Also mutex, for redundancy */ |
+ winShmLeaveMutex(); |
+} |
+ |
+/* |
+** This function is called to obtain a pointer to region iRegion of the |
+** shared-memory associated with the database file fd. Shared-memory regions |
+** are numbered starting from zero. Each shared-memory region is szRegion |
+** bytes in size. |
+** |
+** If an error occurs, an error code is returned and *pp is set to NULL. |
+** |
+** Otherwise, if the isWrite parameter is 0 and the requested shared-memory |
+** region has not been allocated (by any client, including one running in a |
+** separate process), then *pp is set to NULL and SQLITE_OK returned. If |
+** isWrite is non-zero and the requested shared-memory region has not yet |
+** been allocated, it is allocated by this function. |
+** |
+** If the shared-memory region has already been allocated or is allocated by |
+** this call as described above, then it is mapped into this processes |
+** address space (if it is not already), *pp is set to point to the mapped |
+** memory and SQLITE_OK returned. |
+*/ |
+static int winShmMap( |
+ sqlite3_file *fd, /* Handle open on database file */ |
+ int iRegion, /* Region to retrieve */ |
+ int szRegion, /* Size of regions */ |
+ int isWrite, /* True to extend file if necessary */ |
+ void volatile **pp /* OUT: Mapped memory */ |
+){ |
+ winFile *pDbFd = (winFile*)fd; |
+ winShm *pShm = pDbFd->pShm; |
+ winShmNode *pShmNode; |
+ int rc = SQLITE_OK; |
+ |
+ if( !pShm ){ |
+ rc = winOpenSharedMemory(pDbFd); |
+ if( rc!=SQLITE_OK ) return rc; |
+ pShm = pDbFd->pShm; |
+ } |
+ pShmNode = pShm->pShmNode; |
+ |
+ sqlite3_mutex_enter(pShmNode->mutex); |
+ assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); |
+ |
+ if( pShmNode->nRegion<=iRegion ){ |
+ struct ShmRegion *apNew; /* New aRegion[] array */ |
+ int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ |
+ sqlite3_int64 sz; /* Current size of wal-index file */ |
+ |
+ pShmNode->szRegion = szRegion; |
+ |
+ /* The requested region is not mapped into this processes address space. |
+ ** Check to see if it has been allocated (i.e. if the wal-index file is |
+ ** large enough to contain the requested region). |
+ */ |
+ rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); |
+ if( rc!=SQLITE_OK ){ |
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), |
+ "winShmMap1", pDbFd->zPath); |
+ goto shmpage_out; |
+ } |
+ |
+ if( sz<nByte ){ |
+ /* The requested memory region does not exist. If isWrite is set to |
+ ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. |
+ ** |
+ ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate |
+ ** the requested memory region. |
+ */ |
+ if( !isWrite ) goto shmpage_out; |
+ rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); |
+ if( rc!=SQLITE_OK ){ |
+ rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), |
+ "winShmMap2", pDbFd->zPath); |
+ goto shmpage_out; |
+ } |
+ } |
+ |
+ /* Map the requested memory region into this processes address space. */ |
+ apNew = (struct ShmRegion *)sqlite3_realloc64( |
+ pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) |
+ ); |
+ if( !apNew ){ |
+ rc = SQLITE_IOERR_NOMEM; |
+ goto shmpage_out; |
+ } |
+ pShmNode->aRegion = apNew; |
+ |
+ while( pShmNode->nRegion<=iRegion ){ |
+ HANDLE hMap = NULL; /* file-mapping handle */ |
+ void *pMap = 0; /* Mapped memory region */ |
+ |
+#if SQLITE_OS_WINRT |
+ hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, |
+ NULL, PAGE_READWRITE, nByte, NULL |
+ ); |
+#elif defined(SQLITE_WIN32_HAS_WIDE) |
+ hMap = osCreateFileMappingW(pShmNode->hFile.h, |
+ NULL, PAGE_READWRITE, 0, nByte, NULL |
+ ); |
+#elif defined(SQLITE_WIN32_HAS_ANSI) |
+ hMap = osCreateFileMappingA(pShmNode->hFile.h, |
+ NULL, PAGE_READWRITE, 0, nByte, NULL |
+ ); |
+#endif |
+ OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", |
+ osGetCurrentProcessId(), pShmNode->nRegion, nByte, |
+ hMap ? "ok" : "failed")); |
+ if( hMap ){ |
+ int iOffset = pShmNode->nRegion*szRegion; |
+ int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; |
+#if SQLITE_OS_WINRT |
+ pMap = osMapViewOfFileFromApp(hMap, FILE_MAP_WRITE | FILE_MAP_READ, |
+ iOffset - iOffsetShift, szRegion + iOffsetShift |
+ ); |
+#else |
+ pMap = osMapViewOfFile(hMap, FILE_MAP_WRITE | FILE_MAP_READ, |
+ 0, iOffset - iOffsetShift, szRegion + iOffsetShift |
+ ); |
+#endif |
+ OSTRACE(("SHM-MAP-MAP pid=%lu, region=%d, offset=%d, size=%d, rc=%s\n", |
+ osGetCurrentProcessId(), pShmNode->nRegion, iOffset, |
+ szRegion, pMap ? "ok" : "failed")); |
+ } |
+ if( !pMap ){ |
+ pShmNode->lastErrno = osGetLastError(); |
+ rc = winLogError(SQLITE_IOERR_SHMMAP, pShmNode->lastErrno, |
+ "winShmMap3", pDbFd->zPath); |
+ if( hMap ) osCloseHandle(hMap); |
+ goto shmpage_out; |
+ } |
+ |
+ pShmNode->aRegion[pShmNode->nRegion].pMap = pMap; |
+ pShmNode->aRegion[pShmNode->nRegion].hMap = hMap; |
+ pShmNode->nRegion++; |
+ } |
+ } |
+ |
+shmpage_out: |
+ if( pShmNode->nRegion>iRegion ){ |
+ int iOffset = iRegion*szRegion; |
+ int iOffsetShift = iOffset % winSysInfo.dwAllocationGranularity; |
+ char *p = (char *)pShmNode->aRegion[iRegion].pMap; |
+ *pp = (void *)&p[iOffsetShift]; |
+ }else{ |
+ *pp = 0; |
+ } |
+ sqlite3_mutex_leave(pShmNode->mutex); |
+ return rc; |
+} |
+ |
+#else |
+# define winShmMap 0 |
+# define winShmLock 0 |
+# define winShmBarrier 0 |
+# define winShmUnmap 0 |
+#endif /* #ifndef SQLITE_OMIT_WAL */ |
+ |
+/* |
+** Cleans up the mapped region of the specified file, if any. |
+*/ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+static int winUnmapfile(winFile *pFile){ |
+ assert( pFile!=0 ); |
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, pMapRegion=%p, " |
+ "mmapSize=%lld, mmapSizeActual=%lld, mmapSizeMax=%lld\n", |
+ osGetCurrentProcessId(), pFile, pFile->hMap, pFile->pMapRegion, |
+ pFile->mmapSize, pFile->mmapSizeActual, pFile->mmapSizeMax)); |
+ if( pFile->pMapRegion ){ |
+ if( !osUnmapViewOfFile(pFile->pMapRegion) ){ |
+ pFile->lastErrno = osGetLastError(); |
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, pMapRegion=%p, " |
+ "rc=SQLITE_IOERR_MMAP\n", osGetCurrentProcessId(), pFile, |
+ pFile->pMapRegion)); |
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, |
+ "winUnmapfile1", pFile->zPath); |
+ } |
+ pFile->pMapRegion = 0; |
+ pFile->mmapSize = 0; |
+ pFile->mmapSizeActual = 0; |
+ } |
+ if( pFile->hMap!=NULL ){ |
+ if( !osCloseHandle(pFile->hMap) ){ |
+ pFile->lastErrno = osGetLastError(); |
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, hMap=%p, rc=SQLITE_IOERR_MMAP\n", |
+ osGetCurrentProcessId(), pFile, pFile->hMap)); |
+ return winLogError(SQLITE_IOERR_MMAP, pFile->lastErrno, |
+ "winUnmapfile2", pFile->zPath); |
+ } |
+ pFile->hMap = NULL; |
+ } |
+ OSTRACE(("UNMAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFile)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Memory map or remap the file opened by file-descriptor pFd (if the file |
+** is already mapped, the existing mapping is replaced by the new). Or, if |
+** there already exists a mapping for this file, and there are still |
+** outstanding xFetch() references to it, this function is a no-op. |
+** |
+** If parameter nByte is non-negative, then it is the requested size of |
+** the mapping to create. Otherwise, if nByte is less than zero, then the |
+** requested size is the size of the file on disk. The actual size of the |
+** created mapping is either the requested size or the value configured |
+** using SQLITE_FCNTL_MMAP_SIZE, whichever is smaller. |
+** |
+** SQLITE_OK is returned if no error occurs (even if the mapping is not |
+** recreated as a result of outstanding references) or an SQLite error |
+** code otherwise. |
+*/ |
+static int winMapfile(winFile *pFd, sqlite3_int64 nByte){ |
+ sqlite3_int64 nMap = nByte; |
+ int rc; |
+ |
+ assert( nMap>=0 || pFd->nFetchOut==0 ); |
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, size=%lld\n", |
+ osGetCurrentProcessId(), pFd, nByte)); |
+ |
+ if( pFd->nFetchOut>0 ) return SQLITE_OK; |
+ |
+ if( nMap<0 ){ |
+ rc = winFileSize((sqlite3_file*)pFd, &nMap); |
+ if( rc ){ |
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_IOERR_FSTAT\n", |
+ osGetCurrentProcessId(), pFd)); |
+ return SQLITE_IOERR_FSTAT; |
+ } |
+ } |
+ if( nMap>pFd->mmapSizeMax ){ |
+ nMap = pFd->mmapSizeMax; |
+ } |
+ nMap &= ~(sqlite3_int64)(winSysInfo.dwPageSize - 1); |
+ |
+ if( nMap==0 && pFd->mmapSize>0 ){ |
+ winUnmapfile(pFd); |
+ } |
+ if( nMap!=pFd->mmapSize ){ |
+ void *pNew = 0; |
+ DWORD protect = PAGE_READONLY; |
+ DWORD flags = FILE_MAP_READ; |
+ |
+ winUnmapfile(pFd); |
+#ifdef SQLITE_MMAP_READWRITE |
+ if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){ |
+ protect = PAGE_READWRITE; |
+ flags |= FILE_MAP_WRITE; |
+ } |
+#endif |
+#if SQLITE_OS_WINRT |
+ pFd->hMap = osCreateFileMappingFromApp(pFd->h, NULL, protect, nMap, NULL); |
+#elif defined(SQLITE_WIN32_HAS_WIDE) |
+ pFd->hMap = osCreateFileMappingW(pFd->h, NULL, protect, |
+ (DWORD)((nMap>>32) & 0xffffffff), |
+ (DWORD)(nMap & 0xffffffff), NULL); |
+#elif defined(SQLITE_WIN32_HAS_ANSI) |
+ pFd->hMap = osCreateFileMappingA(pFd->h, NULL, protect, |
+ (DWORD)((nMap>>32) & 0xffffffff), |
+ (DWORD)(nMap & 0xffffffff), NULL); |
+#endif |
+ if( pFd->hMap==NULL ){ |
+ pFd->lastErrno = osGetLastError(); |
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, |
+ "winMapfile1", pFd->zPath); |
+ /* Log the error, but continue normal operation using xRead/xWrite */ |
+ OSTRACE(("MAP-FILE-CREATE pid=%lu, pFile=%p, rc=%s\n", |
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); |
+ return SQLITE_OK; |
+ } |
+ assert( (nMap % winSysInfo.dwPageSize)==0 ); |
+ assert( sizeof(SIZE_T)==sizeof(sqlite3_int64) || nMap<=0xffffffff ); |
+#if SQLITE_OS_WINRT |
+ pNew = osMapViewOfFileFromApp(pFd->hMap, flags, 0, (SIZE_T)nMap); |
+#else |
+ pNew = osMapViewOfFile(pFd->hMap, flags, 0, 0, (SIZE_T)nMap); |
+#endif |
+ if( pNew==NULL ){ |
+ osCloseHandle(pFd->hMap); |
+ pFd->hMap = NULL; |
+ pFd->lastErrno = osGetLastError(); |
+ rc = winLogError(SQLITE_IOERR_MMAP, pFd->lastErrno, |
+ "winMapfile2", pFd->zPath); |
+ /* Log the error, but continue normal operation using xRead/xWrite */ |
+ OSTRACE(("MAP-FILE-MAP pid=%lu, pFile=%p, rc=%s\n", |
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); |
+ return SQLITE_OK; |
+ } |
+ pFd->pMapRegion = pNew; |
+ pFd->mmapSize = nMap; |
+ pFd->mmapSizeActual = nMap; |
+ } |
+ |
+ OSTRACE(("MAP-FILE pid=%lu, pFile=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), pFd)); |
+ return SQLITE_OK; |
+} |
+#endif /* SQLITE_MAX_MMAP_SIZE>0 */ |
+ |
+/* |
+** If possible, return a pointer to a mapping of file fd starting at offset |
+** iOff. The mapping must be valid for at least nAmt bytes. |
+** |
+** If such a pointer can be obtained, store it in *pp and return SQLITE_OK. |
+** Or, if one cannot but no error occurs, set *pp to 0 and return SQLITE_OK. |
+** Finally, if an error does occur, return an SQLite error code. The final |
+** value of *pp is undefined in this case. |
+** |
+** If this function does return a pointer, the caller must eventually |
+** release the reference by calling winUnfetch(). |
+*/ |
+static int winFetch(sqlite3_file *fd, i64 iOff, int nAmt, void **pp){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ winFile *pFd = (winFile*)fd; /* The underlying database file */ |
+#endif |
+ *pp = 0; |
+ |
+ OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n", |
+ osGetCurrentProcessId(), fd, iOff, nAmt, pp)); |
+ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ if( pFd->mmapSizeMax>0 ){ |
+ if( pFd->pMapRegion==0 ){ |
+ int rc = winMapfile(pFd, -1); |
+ if( rc!=SQLITE_OK ){ |
+ OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n", |
+ osGetCurrentProcessId(), pFd, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+ } |
+ if( pFd->mmapSize >= iOff+nAmt ){ |
+ *pp = &((u8 *)pFd->pMapRegion)[iOff]; |
+ pFd->nFetchOut++; |
+ } |
+ } |
+#endif |
+ |
+ OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), fd, pp, *pp)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** If the third argument is non-NULL, then this function releases a |
+** reference obtained by an earlier call to winFetch(). The second |
+** argument passed to this function must be the same as the corresponding |
+** argument that was passed to the winFetch() invocation. |
+** |
+** Or, if the third argument is NULL, then this function is being called |
+** to inform the VFS layer that, according to POSIX, any existing mapping |
+** may now be invalid and should be unmapped. |
+*/ |
+static int winUnfetch(sqlite3_file *fd, i64 iOff, void *p){ |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ winFile *pFd = (winFile*)fd; /* The underlying database file */ |
+ |
+ /* If p==0 (unmap the entire file) then there must be no outstanding |
+ ** xFetch references. Or, if p!=0 (meaning it is an xFetch reference), |
+ ** then there must be at least one outstanding. */ |
+ assert( (p==0)==(pFd->nFetchOut==0) ); |
+ |
+ /* If p!=0, it must match the iOff value. */ |
+ assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] ); |
+ |
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n", |
+ osGetCurrentProcessId(), pFd, iOff, p)); |
+ |
+ if( p ){ |
+ pFd->nFetchOut--; |
+ }else{ |
+ /* FIXME: If Windows truly always prevents truncating or deleting a |
+ ** file while a mapping is held, then the following winUnmapfile() call |
+ ** is unnecessary can be omitted - potentially improving |
+ ** performance. */ |
+ winUnmapfile(pFd); |
+ } |
+ |
+ assert( pFd->nFetchOut>=0 ); |
+#endif |
+ |
+ OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n", |
+ osGetCurrentProcessId(), fd)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Here ends the implementation of all sqlite3_file methods. |
+** |
+********************** End sqlite3_file Methods ******************************* |
+******************************************************************************/ |
+ |
+/* |
+** This vector defines all the methods that can operate on an |
+** sqlite3_file for win32. |
+*/ |
+static const sqlite3_io_methods winIoMethod = { |
+ 3, /* iVersion */ |
+ winClose, /* xClose */ |
+ winRead, /* xRead */ |
+ winWrite, /* xWrite */ |
+ winTruncate, /* xTruncate */ |
+ winSync, /* xSync */ |
+ winFileSize, /* xFileSize */ |
+ winLock, /* xLock */ |
+ winUnlock, /* xUnlock */ |
+ winCheckReservedLock, /* xCheckReservedLock */ |
+ winFileControl, /* xFileControl */ |
+ winSectorSize, /* xSectorSize */ |
+ winDeviceCharacteristics, /* xDeviceCharacteristics */ |
+ winShmMap, /* xShmMap */ |
+ winShmLock, /* xShmLock */ |
+ winShmBarrier, /* xShmBarrier */ |
+ winShmUnmap, /* xShmUnmap */ |
+ winFetch, /* xFetch */ |
+ winUnfetch /* xUnfetch */ |
+}; |
+ |
+/**************************************************************************** |
+**************************** sqlite3_vfs methods **************************** |
+** |
+** This division contains the implementation of methods on the |
+** sqlite3_vfs object. |
+*/ |
+ |
+#if defined(__CYGWIN__) |
+/* |
+** Convert a filename from whatever the underlying operating system |
+** supports for filenames into UTF-8. Space to hold the result is |
+** obtained from malloc and must be freed by the calling function. |
+*/ |
+static char *winConvertToUtf8Filename(const void *zFilename){ |
+ char *zConverted = 0; |
+ if( osIsNT() ){ |
+ zConverted = winUnicodeToUtf8(zFilename); |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ zConverted = sqlite3_win32_mbcs_to_utf8(zFilename); |
+ } |
+#endif |
+ /* caller will handle out of memory */ |
+ return zConverted; |
+} |
+#endif |
+ |
+/* |
+** Convert a UTF-8 filename into whatever form the underlying |
+** operating system wants filenames in. Space to hold the result |
+** is obtained from malloc and must be freed by the calling |
+** function. |
+*/ |
+static void *winConvertFromUtf8Filename(const char *zFilename){ |
+ void *zConverted = 0; |
+ if( osIsNT() ){ |
+ zConverted = winUtf8ToUnicode(zFilename); |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ zConverted = sqlite3_win32_utf8_to_mbcs(zFilename); |
+ } |
+#endif |
+ /* caller will handle out of memory */ |
+ return zConverted; |
+} |
+ |
+/* |
+** This function returns non-zero if the specified UTF-8 string buffer |
+** ends with a directory separator character or one was successfully |
+** added to it. |
+*/ |
+static int winMakeEndInDirSep(int nBuf, char *zBuf){ |
+ if( zBuf ){ |
+ int nLen = sqlite3Strlen30(zBuf); |
+ if( nLen>0 ){ |
+ if( winIsDirSep(zBuf[nLen-1]) ){ |
+ return 1; |
+ }else if( nLen+1<nBuf ){ |
+ zBuf[nLen] = winGetDirSep(); |
+ zBuf[nLen+1] = '\0'; |
+ return 1; |
+ } |
+ } |
+ } |
+ return 0; |
+} |
+ |
+/* |
+** Create a temporary file name and store the resulting pointer into pzBuf. |
+** The pointer returned in pzBuf must be freed via sqlite3_free(). |
+*/ |
+static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){ |
+ static char zChars[] = |
+ "abcdefghijklmnopqrstuvwxyz" |
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
+ "0123456789"; |
+ size_t i, j; |
+ int nPre = sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX); |
+ int nMax, nBuf, nDir, nLen; |
+ char *zBuf; |
+ |
+ /* It's odd to simulate an io-error here, but really this is just |
+ ** using the io-error infrastructure to test that SQLite handles this |
+ ** function failing. |
+ */ |
+ SimulateIOError( return SQLITE_IOERR ); |
+ |
+ /* Allocate a temporary buffer to store the fully qualified file |
+ ** name for the temporary file. If this fails, we cannot continue. |
+ */ |
+ nMax = pVfs->mxPathname; nBuf = nMax + 2; |
+ zBuf = sqlite3MallocZero( nBuf ); |
+ if( !zBuf ){ |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ |
+ /* Figure out the effective temporary directory. First, check if one |
+ ** has been explicitly set by the application; otherwise, use the one |
+ ** configured by the operating system. |
+ */ |
+ nDir = nMax - (nPre + 15); |
+ assert( nDir>0 ); |
+ if( sqlite3_temp_directory ){ |
+ int nDirLen = sqlite3Strlen30(sqlite3_temp_directory); |
+ if( nDirLen>0 ){ |
+ if( !winIsDirSep(sqlite3_temp_directory[nDirLen-1]) ){ |
+ nDirLen++; |
+ } |
+ if( nDirLen>nDir ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); |
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname1", 0); |
+ } |
+ sqlite3_snprintf(nMax, zBuf, "%s", sqlite3_temp_directory); |
+ } |
+ } |
+#if defined(__CYGWIN__) |
+ else{ |
+ static const char *azDirs[] = { |
+ 0, /* getenv("SQLITE_TMPDIR") */ |
+ 0, /* getenv("TMPDIR") */ |
+ 0, /* getenv("TMP") */ |
+ 0, /* getenv("TEMP") */ |
+ 0, /* getenv("USERPROFILE") */ |
+ "/var/tmp", |
+ "/usr/tmp", |
+ "/tmp", |
+ ".", |
+ 0 /* List terminator */ |
+ }; |
+ unsigned int i; |
+ const char *zDir = 0; |
+ |
+ if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR"); |
+ if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR"); |
+ if( !azDirs[2] ) azDirs[2] = getenv("TMP"); |
+ if( !azDirs[3] ) azDirs[3] = getenv("TEMP"); |
+ if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE"); |
+ for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){ |
+ void *zConverted; |
+ if( zDir==0 ) continue; |
+ /* If the path starts with a drive letter followed by the colon |
+ ** character, assume it is already a native Win32 path; otherwise, |
+ ** it must be converted to a native Win32 path via the Cygwin API |
+ ** prior to using it. |
+ */ |
+ if( winIsDriveLetterAndColon(zDir) ){ |
+ zConverted = winConvertFromUtf8Filename(zDir); |
+ if( !zConverted ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( winIsDir(zConverted) ){ |
+ sqlite3_snprintf(nMax, zBuf, "%s", zDir); |
+ sqlite3_free(zConverted); |
+ break; |
+ } |
+ sqlite3_free(zConverted); |
+ }else{ |
+ zConverted = sqlite3MallocZero( nMax+1 ); |
+ if( !zConverted ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( cygwin_conv_path( |
+ osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir, |
+ zConverted, nMax+1)<0 ){ |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n")); |
+ return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno, |
+ "winGetTempname2", zDir); |
+ } |
+ if( winIsDir(zConverted) ){ |
+ /* At this point, we know the candidate directory exists and should |
+ ** be used. However, we may need to convert the string containing |
+ ** its name into UTF-8 (i.e. if it is UTF-16 right now). |
+ */ |
+ char *zUtf8 = winConvertToUtf8Filename(zConverted); |
+ if( !zUtf8 ){ |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); |
+ sqlite3_free(zUtf8); |
+ sqlite3_free(zConverted); |
+ break; |
+ } |
+ sqlite3_free(zConverted); |
+ } |
+ } |
+ } |
+#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__) |
+ else if( osIsNT() ){ |
+ char *zMulti; |
+ LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) ); |
+ if( !zWidePath ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( osGetTempPathW(nMax, zWidePath)==0 ){ |
+ sqlite3_free(zWidePath); |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); |
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), |
+ "winGetTempname2", 0); |
+ } |
+ zMulti = winUnicodeToUtf8(zWidePath); |
+ if( zMulti ){ |
+ sqlite3_snprintf(nMax, zBuf, "%s", zMulti); |
+ sqlite3_free(zMulti); |
+ sqlite3_free(zWidePath); |
+ }else{ |
+ sqlite3_free(zWidePath); |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ char *zUtf8; |
+ char *zMbcsPath = sqlite3MallocZero( nMax ); |
+ if( !zMbcsPath ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( osGetTempPathA(nMax, zMbcsPath)==0 ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n")); |
+ return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(), |
+ "winGetTempname3", 0); |
+ } |
+ zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath); |
+ if( zUtf8 ){ |
+ sqlite3_snprintf(nMax, zBuf, "%s", zUtf8); |
+ sqlite3_free(zUtf8); |
+ }else{ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n")); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ } |
+#endif /* SQLITE_WIN32_HAS_ANSI */ |
+#endif /* !SQLITE_OS_WINRT */ |
+ |
+ /* |
+ ** Check to make sure the temporary directory ends with an appropriate |
+ ** separator. If it does not and there is not enough space left to add |
+ ** one, fail. |
+ */ |
+ if( !winMakeEndInDirSep(nDir+1, zBuf) ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); |
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname4", 0); |
+ } |
+ |
+ /* |
+ ** Check that the output buffer is large enough for the temporary file |
+ ** name in the following format: |
+ ** |
+ ** "<temporary_directory>/etilqs_XXXXXXXXXXXXXXX\0\0" |
+ ** |
+ ** If not, return SQLITE_ERROR. The number 17 is used here in order to |
+ ** account for the space used by the 15 character random suffix and the |
+ ** two trailing NUL characters. The final directory separator character |
+ ** has already added if it was not already present. |
+ */ |
+ nLen = sqlite3Strlen30(zBuf); |
+ if( (nLen + nPre + 17) > nBuf ){ |
+ sqlite3_free(zBuf); |
+ OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n")); |
+ return winLogError(SQLITE_ERROR, 0, "winGetTempname5", 0); |
+ } |
+ |
+ sqlite3_snprintf(nBuf-16-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX); |
+ |
+ j = sqlite3Strlen30(zBuf); |
+ sqlite3_randomness(15, &zBuf[j]); |
+ for(i=0; i<15; i++, j++){ |
+ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; |
+ } |
+ zBuf[j] = 0; |
+ zBuf[j+1] = 0; |
+ *pzBuf = zBuf; |
+ |
+ OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Return TRUE if the named file is really a directory. Return false if |
+** it is something other than a directory, or if there is any kind of memory |
+** allocation failure. |
+*/ |
+static int winIsDir(const void *zConverted){ |
+ DWORD attr; |
+ int rc = 0; |
+ DWORD lastErrno; |
+ |
+ if( osIsNT() ){ |
+ int cnt = 0; |
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData; |
+ memset(&sAttrData, 0, sizeof(sAttrData)); |
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, |
+ GetFileExInfoStandard, |
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} |
+ if( !rc ){ |
+ return 0; /* Invalid name? */ |
+ } |
+ attr = sAttrData.dwFileAttributes; |
+#if SQLITE_OS_WINCE==0 |
+ }else{ |
+ attr = osGetFileAttributesA((char*)zConverted); |
+#endif |
+ } |
+ return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY); |
+} |
+ |
+/* |
+** Open a file. |
+*/ |
+static int winOpen( |
+ sqlite3_vfs *pVfs, /* Used to get maximum path name length */ |
+ const char *zName, /* Name of the file (UTF-8) */ |
+ sqlite3_file *id, /* Write the SQLite file handle here */ |
+ int flags, /* Open mode flags */ |
+ int *pOutFlags /* Status return flags */ |
+){ |
+ HANDLE h; |
+ DWORD lastErrno = 0; |
+ DWORD dwDesiredAccess; |
+ DWORD dwShareMode; |
+ DWORD dwCreationDisposition; |
+ DWORD dwFlagsAndAttributes = 0; |
+#if SQLITE_OS_WINCE |
+ int isTemp = 0; |
+#endif |
+ winFile *pFile = (winFile*)id; |
+ void *zConverted; /* Filename in OS encoding */ |
+ const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */ |
+ int cnt = 0; |
+ |
+ /* If argument zPath is a NULL pointer, this function is required to open |
+ ** a temporary file. Use this buffer to store the file name in. |
+ */ |
+ char *zTmpname = 0; /* For temporary filename, if necessary. */ |
+ |
+ int rc = SQLITE_OK; /* Function Return Code */ |
+#if !defined(NDEBUG) || SQLITE_OS_WINCE |
+ int eType = flags&0xFFFFFF00; /* Type of file to open */ |
+#endif |
+ |
+ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); |
+ int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); |
+ int isCreate = (flags & SQLITE_OPEN_CREATE); |
+ int isReadonly = (flags & SQLITE_OPEN_READONLY); |
+ int isReadWrite = (flags & SQLITE_OPEN_READWRITE); |
+ |
+#ifndef NDEBUG |
+ int isOpenJournal = (isCreate && ( |
+ eType==SQLITE_OPEN_MASTER_JOURNAL |
+ || eType==SQLITE_OPEN_MAIN_JOURNAL |
+ || eType==SQLITE_OPEN_WAL |
+ )); |
+#endif |
+ |
+ OSTRACE(("OPEN name=%s, pFile=%p, flags=%x, pOutFlags=%p\n", |
+ zUtf8Name, id, flags, pOutFlags)); |
+ |
+ /* Check the following statements are true: |
+ ** |
+ ** (a) Exactly one of the READWRITE and READONLY flags must be set, and |
+ ** (b) if CREATE is set, then READWRITE must also be set, and |
+ ** (c) if EXCLUSIVE is set, then CREATE must also be set. |
+ ** (d) if DELETEONCLOSE is set, then CREATE must also be set. |
+ */ |
+ assert((isReadonly==0 || isReadWrite==0) && (isReadWrite || isReadonly)); |
+ assert(isCreate==0 || isReadWrite); |
+ assert(isExclusive==0 || isCreate); |
+ assert(isDelete==0 || isCreate); |
+ |
+ /* The main DB, main journal, WAL file and master journal are never |
+ ** automatically deleted. Nor are they ever temporary files. */ |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); |
+ assert( (!isDelete && zName) || eType!=SQLITE_OPEN_WAL ); |
+ |
+ /* Assert that the upper layer has set one of the "file-type" flags. */ |
+ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB |
+ || eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL |
+ || eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_MASTER_JOURNAL |
+ || eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL |
+ ); |
+ |
+ assert( pFile!=0 ); |
+ memset(pFile, 0, sizeof(winFile)); |
+ pFile->h = INVALID_HANDLE_VALUE; |
+ |
+#if SQLITE_OS_WINRT |
+ if( !zUtf8Name && !sqlite3_temp_directory ){ |
+ sqlite3_log(SQLITE_ERROR, |
+ "sqlite3_temp_directory variable should be set for WinRT"); |
+ } |
+#endif |
+ |
+ /* If the second argument to this function is NULL, generate a |
+ ** temporary file name to use |
+ */ |
+ if( !zUtf8Name ){ |
+ assert( isDelete && !isOpenJournal ); |
+ rc = winGetTempname(pVfs, &zTmpname); |
+ if( rc!=SQLITE_OK ){ |
+ OSTRACE(("OPEN name=%s, rc=%s", zUtf8Name, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+ zUtf8Name = zTmpname; |
+ } |
+ |
+ /* Database filenames are double-zero terminated if they are not |
+ ** URIs with parameters. Hence, they can always be passed into |
+ ** sqlite3_uri_parameter(). |
+ */ |
+ assert( (eType!=SQLITE_OPEN_MAIN_DB) || (flags & SQLITE_OPEN_URI) || |
+ zUtf8Name[sqlite3Strlen30(zUtf8Name)+1]==0 ); |
+ |
+ /* Convert the filename to the system encoding. */ |
+ zConverted = winConvertFromUtf8Filename(zUtf8Name); |
+ if( zConverted==0 ){ |
+ sqlite3_free(zTmpname); |
+ OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8Name)); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ |
+ if( winIsDir(zConverted) ){ |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zTmpname); |
+ OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name)); |
+ return SQLITE_CANTOPEN_ISDIR; |
+ } |
+ |
+ if( isReadWrite ){ |
+ dwDesiredAccess = GENERIC_READ | GENERIC_WRITE; |
+ }else{ |
+ dwDesiredAccess = GENERIC_READ; |
+ } |
+ |
+ /* SQLITE_OPEN_EXCLUSIVE is used to make sure that a new file is |
+ ** created. SQLite doesn't use it to indicate "exclusive access" |
+ ** as it is usually understood. |
+ */ |
+ if( isExclusive ){ |
+ /* Creates a new file, only if it does not already exist. */ |
+ /* If the file exists, it fails. */ |
+ dwCreationDisposition = CREATE_NEW; |
+ }else if( isCreate ){ |
+ /* Open existing file, or create if it doesn't exist */ |
+ dwCreationDisposition = OPEN_ALWAYS; |
+ }else{ |
+ /* Opens a file, only if it exists. */ |
+ dwCreationDisposition = OPEN_EXISTING; |
+ } |
+ |
+ dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; |
+ |
+ if( isDelete ){ |
+#if SQLITE_OS_WINCE |
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_HIDDEN; |
+ isTemp = 1; |
+#else |
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_TEMPORARY |
+ | FILE_ATTRIBUTE_HIDDEN |
+ | FILE_FLAG_DELETE_ON_CLOSE; |
+#endif |
+ }else{ |
+ dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; |
+ } |
+ /* Reports from the internet are that performance is always |
+ ** better if FILE_FLAG_RANDOM_ACCESS is used. Ticket #2699. */ |
+#if SQLITE_OS_WINCE |
+ dwFlagsAndAttributes |= FILE_FLAG_RANDOM_ACCESS; |
+#endif |
+ |
+ if( osIsNT() ){ |
+#if SQLITE_OS_WINRT |
+ CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; |
+ extendedParameters.dwSize = sizeof(CREATEFILE2_EXTENDED_PARAMETERS); |
+ extendedParameters.dwFileAttributes = |
+ dwFlagsAndAttributes & FILE_ATTRIBUTE_MASK; |
+ extendedParameters.dwFileFlags = dwFlagsAndAttributes & FILE_FLAG_MASK; |
+ extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; |
+ extendedParameters.lpSecurityAttributes = NULL; |
+ extendedParameters.hTemplateFile = NULL; |
+ while( (h = osCreateFile2((LPCWSTR)zConverted, |
+ dwDesiredAccess, |
+ dwShareMode, |
+ dwCreationDisposition, |
+ &extendedParameters))==INVALID_HANDLE_VALUE && |
+ winRetryIoerr(&cnt, &lastErrno) ){ |
+ /* Noop */ |
+ } |
+#else |
+ while( (h = osCreateFileW((LPCWSTR)zConverted, |
+ dwDesiredAccess, |
+ dwShareMode, NULL, |
+ dwCreationDisposition, |
+ dwFlagsAndAttributes, |
+ NULL))==INVALID_HANDLE_VALUE && |
+ winRetryIoerr(&cnt, &lastErrno) ){ |
+ /* Noop */ |
+ } |
+#endif |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ while( (h = osCreateFileA((LPCSTR)zConverted, |
+ dwDesiredAccess, |
+ dwShareMode, NULL, |
+ dwCreationDisposition, |
+ dwFlagsAndAttributes, |
+ NULL))==INVALID_HANDLE_VALUE && |
+ winRetryIoerr(&cnt, &lastErrno) ){ |
+ /* Noop */ |
+ } |
+ } |
+#endif |
+ winLogIoerr(cnt, __LINE__); |
+ |
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, rc=%s\n", h, zUtf8Name, |
+ dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); |
+ |
+ if( h==INVALID_HANDLE_VALUE ){ |
+ pFile->lastErrno = lastErrno; |
+ winLogError(SQLITE_CANTOPEN, pFile->lastErrno, "winOpen", zUtf8Name); |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zTmpname); |
+ if( isReadWrite && !isExclusive ){ |
+ return winOpen(pVfs, zName, id, |
+ ((flags|SQLITE_OPEN_READONLY) & |
+ ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), |
+ pOutFlags); |
+ }else{ |
+ return SQLITE_CANTOPEN_BKPT; |
+ } |
+ } |
+ |
+ if( pOutFlags ){ |
+ if( isReadWrite ){ |
+ *pOutFlags = SQLITE_OPEN_READWRITE; |
+ }else{ |
+ *pOutFlags = SQLITE_OPEN_READONLY; |
+ } |
+ } |
+ |
+ OSTRACE(("OPEN file=%p, name=%s, access=%lx, pOutFlags=%p, *pOutFlags=%d, " |
+ "rc=%s\n", h, zUtf8Name, dwDesiredAccess, pOutFlags, pOutFlags ? |
+ *pOutFlags : 0, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok")); |
+ |
+#if SQLITE_OS_WINCE |
+ if( isReadWrite && eType==SQLITE_OPEN_MAIN_DB |
+ && (rc = winceCreateLock(zName, pFile))!=SQLITE_OK |
+ ){ |
+ osCloseHandle(h); |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zTmpname); |
+ OSTRACE(("OPEN-CE-LOCK name=%s, rc=%s\n", zName, sqlite3ErrName(rc))); |
+ return rc; |
+ } |
+ if( isTemp ){ |
+ pFile->zDeleteOnClose = zConverted; |
+ }else |
+#endif |
+ { |
+ sqlite3_free(zConverted); |
+ } |
+ |
+ sqlite3_free(zTmpname); |
+ pFile->pMethod = &winIoMethod; |
+ pFile->pVfs = pVfs; |
+ pFile->h = h; |
+ if( isReadonly ){ |
+ pFile->ctrlFlags |= WINFILE_RDONLY; |
+ } |
+ if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){ |
+ pFile->ctrlFlags |= WINFILE_PSOW; |
+ } |
+ pFile->lastErrno = NO_ERROR; |
+ pFile->zPath = zName; |
+#if SQLITE_MAX_MMAP_SIZE>0 |
+ pFile->hMap = NULL; |
+ pFile->pMapRegion = 0; |
+ pFile->mmapSize = 0; |
+ pFile->mmapSizeActual = 0; |
+ pFile->mmapSizeMax = sqlite3GlobalConfig.szMmap; |
+#endif |
+ |
+ OpenCounter(+1); |
+ return rc; |
+} |
+ |
+/* |
+** Delete the named file. |
+** |
+** Note that Windows does not allow a file to be deleted if some other |
+** process has it open. Sometimes a virus scanner or indexing program |
+** will open a journal file shortly after it is created in order to do |
+** whatever it does. While this other process is holding the |
+** file open, we will be unable to delete it. To work around this |
+** problem, we delay 100 milliseconds and try to delete again. Up |
+** to MX_DELETION_ATTEMPTs deletion attempts are run before giving |
+** up and returning an error. |
+*/ |
+static int winDelete( |
+ sqlite3_vfs *pVfs, /* Not used on win32 */ |
+ const char *zFilename, /* Name of file to delete */ |
+ int syncDir /* Not used on win32 */ |
+){ |
+ int cnt = 0; |
+ int rc; |
+ DWORD attr; |
+ DWORD lastErrno = 0; |
+ void *zConverted; |
+ UNUSED_PARAMETER(pVfs); |
+ UNUSED_PARAMETER(syncDir); |
+ |
+ SimulateIOError(return SQLITE_IOERR_DELETE); |
+ OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir)); |
+ |
+ zConverted = winConvertFromUtf8Filename(zFilename); |
+ if( zConverted==0 ){ |
+ OSTRACE(("DELETE name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( osIsNT() ){ |
+ do { |
+#if SQLITE_OS_WINRT |
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData; |
+ memset(&sAttrData, 0, sizeof(sAttrData)); |
+ if ( osGetFileAttributesExW(zConverted, GetFileExInfoStandard, |
+ &sAttrData) ){ |
+ attr = sAttrData.dwFileAttributes; |
+ }else{ |
+ lastErrno = osGetLastError(); |
+ if( lastErrno==ERROR_FILE_NOT_FOUND |
+ || lastErrno==ERROR_PATH_NOT_FOUND ){ |
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ |
+ }else{ |
+ rc = SQLITE_ERROR; |
+ } |
+ break; |
+ } |
+#else |
+ attr = osGetFileAttributesW(zConverted); |
+#endif |
+ if ( attr==INVALID_FILE_ATTRIBUTES ){ |
+ lastErrno = osGetLastError(); |
+ if( lastErrno==ERROR_FILE_NOT_FOUND |
+ || lastErrno==ERROR_PATH_NOT_FOUND ){ |
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ |
+ }else{ |
+ rc = SQLITE_ERROR; |
+ } |
+ break; |
+ } |
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ |
+ rc = SQLITE_ERROR; /* Files only. */ |
+ break; |
+ } |
+ if ( osDeleteFileW(zConverted) ){ |
+ rc = SQLITE_OK; /* Deleted OK. */ |
+ break; |
+ } |
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){ |
+ rc = SQLITE_ERROR; /* No more retries. */ |
+ break; |
+ } |
+ } while(1); |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ do { |
+ attr = osGetFileAttributesA(zConverted); |
+ if ( attr==INVALID_FILE_ATTRIBUTES ){ |
+ lastErrno = osGetLastError(); |
+ if( lastErrno==ERROR_FILE_NOT_FOUND |
+ || lastErrno==ERROR_PATH_NOT_FOUND ){ |
+ rc = SQLITE_IOERR_DELETE_NOENT; /* Already gone? */ |
+ }else{ |
+ rc = SQLITE_ERROR; |
+ } |
+ break; |
+ } |
+ if ( attr&FILE_ATTRIBUTE_DIRECTORY ){ |
+ rc = SQLITE_ERROR; /* Files only. */ |
+ break; |
+ } |
+ if ( osDeleteFileA(zConverted) ){ |
+ rc = SQLITE_OK; /* Deleted OK. */ |
+ break; |
+ } |
+ if ( !winRetryIoerr(&cnt, &lastErrno) ){ |
+ rc = SQLITE_ERROR; /* No more retries. */ |
+ break; |
+ } |
+ } while(1); |
+ } |
+#endif |
+ if( rc && rc!=SQLITE_IOERR_DELETE_NOENT ){ |
+ rc = winLogError(SQLITE_IOERR_DELETE, lastErrno, "winDelete", zFilename); |
+ }else{ |
+ winLogIoerr(cnt, __LINE__); |
+ } |
+ sqlite3_free(zConverted); |
+ OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc))); |
+ return rc; |
+} |
+ |
+/* |
+** Check the existence and status of a file. |
+*/ |
+static int winAccess( |
+ sqlite3_vfs *pVfs, /* Not used on win32 */ |
+ const char *zFilename, /* Name of file to check */ |
+ int flags, /* Type of test to make on this file */ |
+ int *pResOut /* OUT: Result */ |
+){ |
+ DWORD attr; |
+ int rc = 0; |
+ DWORD lastErrno = 0; |
+ void *zConverted; |
+ UNUSED_PARAMETER(pVfs); |
+ |
+ SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
+ OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n", |
+ zFilename, flags, pResOut)); |
+ |
+ zConverted = winConvertFromUtf8Filename(zFilename); |
+ if( zConverted==0 ){ |
+ OSTRACE(("ACCESS name=%s, rc=SQLITE_IOERR_NOMEM\n", zFilename)); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( osIsNT() ){ |
+ int cnt = 0; |
+ WIN32_FILE_ATTRIBUTE_DATA sAttrData; |
+ memset(&sAttrData, 0, sizeof(sAttrData)); |
+ while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted, |
+ GetFileExInfoStandard, |
+ &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){} |
+ if( rc ){ |
+ /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file |
+ ** as if it does not exist. |
+ */ |
+ if( flags==SQLITE_ACCESS_EXISTS |
+ && sAttrData.nFileSizeHigh==0 |
+ && sAttrData.nFileSizeLow==0 ){ |
+ attr = INVALID_FILE_ATTRIBUTES; |
+ }else{ |
+ attr = sAttrData.dwFileAttributes; |
+ } |
+ }else{ |
+ winLogIoerr(cnt, __LINE__); |
+ if( lastErrno!=ERROR_FILE_NOT_FOUND && lastErrno!=ERROR_PATH_NOT_FOUND ){ |
+ sqlite3_free(zConverted); |
+ return winLogError(SQLITE_IOERR_ACCESS, lastErrno, "winAccess", |
+ zFilename); |
+ }else{ |
+ attr = INVALID_FILE_ATTRIBUTES; |
+ } |
+ } |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ attr = osGetFileAttributesA((char*)zConverted); |
+ } |
+#endif |
+ sqlite3_free(zConverted); |
+ switch( flags ){ |
+ case SQLITE_ACCESS_READ: |
+ case SQLITE_ACCESS_EXISTS: |
+ rc = attr!=INVALID_FILE_ATTRIBUTES; |
+ break; |
+ case SQLITE_ACCESS_READWRITE: |
+ rc = attr!=INVALID_FILE_ATTRIBUTES && |
+ (attr & FILE_ATTRIBUTE_READONLY)==0; |
+ break; |
+ default: |
+ assert(!"Invalid flags argument"); |
+ } |
+ *pResOut = rc; |
+ OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n", |
+ zFilename, pResOut, *pResOut)); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Returns non-zero if the specified path name starts with a drive letter |
+** followed by a colon character. |
+*/ |
+static BOOL winIsDriveLetterAndColon( |
+ const char *zPathname |
+){ |
+ return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' ); |
+} |
+ |
+/* |
+** Returns non-zero if the specified path name should be used verbatim. If |
+** non-zero is returned from this function, the calling function must simply |
+** use the provided path name verbatim -OR- resolve it into a full path name |
+** using the GetFullPathName Win32 API function (if available). |
+*/ |
+static BOOL winIsVerbatimPathname( |
+ const char *zPathname |
+){ |
+ /* |
+ ** If the path name starts with a forward slash or a backslash, it is either |
+ ** a legal UNC name, a volume relative path, or an absolute path name in the |
+ ** "Unix" format on Windows. There is no easy way to differentiate between |
+ ** the final two cases; therefore, we return the safer return value of TRUE |
+ ** so that callers of this function will simply use it verbatim. |
+ */ |
+ if ( winIsDirSep(zPathname[0]) ){ |
+ return TRUE; |
+ } |
+ |
+ /* |
+ ** If the path name starts with a letter and a colon it is either a volume |
+ ** relative path or an absolute path. Callers of this function must not |
+ ** attempt to treat it as a relative path name (i.e. they should simply use |
+ ** it verbatim). |
+ */ |
+ if ( winIsDriveLetterAndColon(zPathname) ){ |
+ return TRUE; |
+ } |
+ |
+ /* |
+ ** If we get to this point, the path name should almost certainly be a purely |
+ ** relative one (i.e. not a UNC name, not absolute, and not volume relative). |
+ */ |
+ return FALSE; |
+} |
+ |
+/* |
+** Turn a relative pathname into a full pathname. Write the full |
+** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname |
+** bytes in size. |
+*/ |
+static int winFullPathname( |
+ sqlite3_vfs *pVfs, /* Pointer to vfs object */ |
+ const char *zRelative, /* Possibly relative input path */ |
+ int nFull, /* Size of output buffer in bytes */ |
+ char *zFull /* Output buffer */ |
+){ |
+ |
+#if defined(__CYGWIN__) |
+ SimulateIOError( return SQLITE_ERROR ); |
+ UNUSED_PARAMETER(nFull); |
+ assert( nFull>=pVfs->mxPathname ); |
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ |
+ /* |
+ ** NOTE: We are dealing with a relative path name and the data |
+ ** directory has been set. Therefore, use it as the basis |
+ ** for converting the relative path name to an absolute |
+ ** one by prepending the data directory and a slash. |
+ */ |
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); |
+ if( !zOut ){ |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( cygwin_conv_path( |
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) | |
+ CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){ |
+ sqlite3_free(zOut); |
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, |
+ "winFullPathname1", zRelative); |
+ }else{ |
+ char *zUtf8 = winConvertToUtf8Filename(zOut); |
+ if( !zUtf8 ){ |
+ sqlite3_free(zOut); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", |
+ sqlite3_data_directory, winGetDirSep(), zUtf8); |
+ sqlite3_free(zUtf8); |
+ sqlite3_free(zOut); |
+ } |
+ }else{ |
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 ); |
+ if( !zOut ){ |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( cygwin_conv_path( |
+ (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A), |
+ zRelative, zOut, pVfs->mxPathname+1)<0 ){ |
+ sqlite3_free(zOut); |
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno, |
+ "winFullPathname2", zRelative); |
+ }else{ |
+ char *zUtf8 = winConvertToUtf8Filename(zOut); |
+ if( !zUtf8 ){ |
+ sqlite3_free(zOut); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8); |
+ sqlite3_free(zUtf8); |
+ sqlite3_free(zOut); |
+ } |
+ } |
+ return SQLITE_OK; |
+#endif |
+ |
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__) |
+ SimulateIOError( return SQLITE_ERROR ); |
+ /* WinCE has no concept of a relative pathname, or so I am told. */ |
+ /* WinRT has no way to convert a relative path to an absolute one. */ |
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ |
+ /* |
+ ** NOTE: We are dealing with a relative path name and the data |
+ ** directory has been set. Therefore, use it as the basis |
+ ** for converting the relative path name to an absolute |
+ ** one by prepending the data directory and a backslash. |
+ */ |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", |
+ sqlite3_data_directory, winGetDirSep(), zRelative); |
+ }else{ |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); |
+ } |
+ return SQLITE_OK; |
+#endif |
+ |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__) |
+ DWORD nByte; |
+ void *zConverted; |
+ char *zOut; |
+ |
+ /* If this path name begins with "/X:", where "X" is any alphabetic |
+ ** character, discard the initial "/" from the pathname. |
+ */ |
+ if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){ |
+ zRelative++; |
+ } |
+ |
+ /* It's odd to simulate an io-error here, but really this is just |
+ ** using the io-error infrastructure to test that SQLite handles this |
+ ** function failing. This function could fail if, for example, the |
+ ** current working directory has been unlinked. |
+ */ |
+ SimulateIOError( return SQLITE_ERROR ); |
+ if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){ |
+ /* |
+ ** NOTE: We are dealing with a relative path name and the data |
+ ** directory has been set. Therefore, use it as the basis |
+ ** for converting the relative path name to an absolute |
+ ** one by prepending the data directory and a backslash. |
+ */ |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s", |
+ sqlite3_data_directory, winGetDirSep(), zRelative); |
+ return SQLITE_OK; |
+ } |
+ zConverted = winConvertFromUtf8Filename(zRelative); |
+ if( zConverted==0 ){ |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ if( osIsNT() ){ |
+ LPWSTR zTemp; |
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, 0, 0, 0); |
+ if( nByte==0 ){ |
+ sqlite3_free(zConverted); |
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), |
+ "winFullPathname1", zRelative); |
+ } |
+ nByte += 3; |
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); |
+ if( zTemp==0 ){ |
+ sqlite3_free(zConverted); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0); |
+ if( nByte==0 ){ |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zTemp); |
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), |
+ "winFullPathname2", zRelative); |
+ } |
+ sqlite3_free(zConverted); |
+ zOut = winUnicodeToUtf8(zTemp); |
+ sqlite3_free(zTemp); |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ char *zTemp; |
+ nByte = osGetFullPathNameA((char*)zConverted, 0, 0, 0); |
+ if( nByte==0 ){ |
+ sqlite3_free(zConverted); |
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), |
+ "winFullPathname3", zRelative); |
+ } |
+ nByte += 3; |
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) ); |
+ if( zTemp==0 ){ |
+ sqlite3_free(zConverted); |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+ nByte = osGetFullPathNameA((char*)zConverted, nByte, zTemp, 0); |
+ if( nByte==0 ){ |
+ sqlite3_free(zConverted); |
+ sqlite3_free(zTemp); |
+ return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(), |
+ "winFullPathname4", zRelative); |
+ } |
+ sqlite3_free(zConverted); |
+ zOut = sqlite3_win32_mbcs_to_utf8(zTemp); |
+ sqlite3_free(zTemp); |
+ } |
+#endif |
+ if( zOut ){ |
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut); |
+ sqlite3_free(zOut); |
+ return SQLITE_OK; |
+ }else{ |
+ return SQLITE_IOERR_NOMEM; |
+ } |
+#endif |
+} |
+ |
+#ifndef SQLITE_OMIT_LOAD_EXTENSION |
+/* |
+** Interfaces for opening a shared library, finding entry points |
+** within the shared library, and closing the shared library. |
+*/ |
+static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){ |
+ HANDLE h; |
+#if defined(__CYGWIN__) |
+ int nFull = pVfs->mxPathname+1; |
+ char *zFull = sqlite3MallocZero( nFull ); |
+ void *zConverted = 0; |
+ if( zFull==0 ){ |
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); |
+ return 0; |
+ } |
+ if( winFullPathname(pVfs, zFilename, nFull, zFull)!=SQLITE_OK ){ |
+ sqlite3_free(zFull); |
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); |
+ return 0; |
+ } |
+ zConverted = winConvertFromUtf8Filename(zFull); |
+ sqlite3_free(zFull); |
+#else |
+ void *zConverted = winConvertFromUtf8Filename(zFilename); |
+ UNUSED_PARAMETER(pVfs); |
+#endif |
+ if( zConverted==0 ){ |
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0)); |
+ return 0; |
+ } |
+ if( osIsNT() ){ |
+#if SQLITE_OS_WINRT |
+ h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0); |
+#else |
+ h = osLoadLibraryW((LPCWSTR)zConverted); |
+#endif |
+ } |
+#ifdef SQLITE_WIN32_HAS_ANSI |
+ else{ |
+ h = osLoadLibraryA((char*)zConverted); |
+ } |
+#endif |
+ OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)h)); |
+ sqlite3_free(zConverted); |
+ return (void*)h; |
+} |
+static void winDlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ |
+ UNUSED_PARAMETER(pVfs); |
+ winGetLastErrorMsg(osGetLastError(), nBuf, zBufOut); |
+} |
+static void (*winDlSym(sqlite3_vfs *pVfs,void *pH,const char *zSym))(void){ |
+ FARPROC proc; |
+ UNUSED_PARAMETER(pVfs); |
+ proc = osGetProcAddressA((HANDLE)pH, zSym); |
+ OSTRACE(("DLSYM handle=%p, symbol=%s, address=%p\n", |
+ (void*)pH, zSym, (void*)proc)); |
+ return (void(*)(void))proc; |
+} |
+static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){ |
+ UNUSED_PARAMETER(pVfs); |
+ osFreeLibrary((HANDLE)pHandle); |
+ OSTRACE(("DLCLOSE handle=%p\n", (void*)pHandle)); |
+} |
+#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */ |
+ #define winDlOpen 0 |
+ #define winDlError 0 |
+ #define winDlSym 0 |
+ #define winDlClose 0 |
+#endif |
+ |
+ |
+/* |
+** Write up to nBuf bytes of randomness into zBuf. |
+*/ |
+static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ |
+ int n = 0; |
+ UNUSED_PARAMETER(pVfs); |
+#if defined(SQLITE_TEST) || defined(SQLITE_OMIT_RANDOMNESS) |
+ n = nBuf; |
+ memset(zBuf, 0, nBuf); |
+#else |
+ if( sizeof(SYSTEMTIME)<=nBuf-n ){ |
+ SYSTEMTIME x; |
+ osGetSystemTime(&x); |
+ memcpy(&zBuf[n], &x, sizeof(x)); |
+ n += sizeof(x); |
+ } |
+ if( sizeof(DWORD)<=nBuf-n ){ |
+ DWORD pid = osGetCurrentProcessId(); |
+ memcpy(&zBuf[n], &pid, sizeof(pid)); |
+ n += sizeof(pid); |
+ } |
+#if SQLITE_OS_WINRT |
+ if( sizeof(ULONGLONG)<=nBuf-n ){ |
+ ULONGLONG cnt = osGetTickCount64(); |
+ memcpy(&zBuf[n], &cnt, sizeof(cnt)); |
+ n += sizeof(cnt); |
+ } |
+#else |
+ if( sizeof(DWORD)<=nBuf-n ){ |
+ DWORD cnt = osGetTickCount(); |
+ memcpy(&zBuf[n], &cnt, sizeof(cnt)); |
+ n += sizeof(cnt); |
+ } |
+#endif |
+ if( sizeof(LARGE_INTEGER)<=nBuf-n ){ |
+ LARGE_INTEGER i; |
+ osQueryPerformanceCounter(&i); |
+ memcpy(&zBuf[n], &i, sizeof(i)); |
+ n += sizeof(i); |
+ } |
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && SQLITE_WIN32_USE_UUID |
+ if( sizeof(UUID)<=nBuf-n ){ |
+ UUID id; |
+ memset(&id, 0, sizeof(UUID)); |
+ osUuidCreate(&id); |
+ memcpy(&zBuf[n], &id, sizeof(UUID)); |
+ n += sizeof(UUID); |
+ } |
+ if( sizeof(UUID)<=nBuf-n ){ |
+ UUID id; |
+ memset(&id, 0, sizeof(UUID)); |
+ osUuidCreateSequential(&id); |
+ memcpy(&zBuf[n], &id, sizeof(UUID)); |
+ n += sizeof(UUID); |
+ } |
+#endif |
+#endif /* defined(SQLITE_TEST) || defined(SQLITE_ZERO_PRNG_SEED) */ |
+ return n; |
+} |
+ |
+ |
+/* |
+** Sleep for a little while. Return the amount of time slept. |
+*/ |
+static int winSleep(sqlite3_vfs *pVfs, int microsec){ |
+ sqlite3_win32_sleep((microsec+999)/1000); |
+ UNUSED_PARAMETER(pVfs); |
+ return ((microsec+999)/1000)*1000; |
+} |
+ |
+/* |
+** The following variable, if set to a non-zero value, is interpreted as |
+** the number of seconds since 1970 and is used to set the result of |
+** sqlite3OsCurrentTime() during testing. |
+*/ |
+#ifdef SQLITE_TEST |
+SQLITE_API int sqlite3_current_time = 0; /* Fake system time in seconds since 1970. */ |
+#endif |
+ |
+/* |
+** Find the current time (in Universal Coordinated Time). Write into *piNow |
+** the current time and date as a Julian Day number times 86_400_000. In |
+** other words, write into *piNow the number of milliseconds since the Julian |
+** epoch of noon in Greenwich on November 24, 4714 B.C according to the |
+** proleptic Gregorian calendar. |
+** |
+** On success, return SQLITE_OK. Return SQLITE_ERROR if the time and date |
+** cannot be found. |
+*/ |
+static int winCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){ |
+ /* FILETIME structure is a 64-bit value representing the number of |
+ 100-nanosecond intervals since January 1, 1601 (= JD 2305813.5). |
+ */ |
+ FILETIME ft; |
+ static const sqlite3_int64 winFiletimeEpoch = 23058135*(sqlite3_int64)8640000; |
+#ifdef SQLITE_TEST |
+ static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000; |
+#endif |
+ /* 2^32 - to avoid use of LL and warnings in gcc */ |
+ static const sqlite3_int64 max32BitValue = |
+ (sqlite3_int64)2000000000 + (sqlite3_int64)2000000000 + |
+ (sqlite3_int64)294967296; |
+ |
+#if SQLITE_OS_WINCE |
+ SYSTEMTIME time; |
+ osGetSystemTime(&time); |
+ /* if SystemTimeToFileTime() fails, it returns zero. */ |
+ if (!osSystemTimeToFileTime(&time,&ft)){ |
+ return SQLITE_ERROR; |
+ } |
+#else |
+ osGetSystemTimeAsFileTime( &ft ); |
+#endif |
+ |
+ *piNow = winFiletimeEpoch + |
+ ((((sqlite3_int64)ft.dwHighDateTime)*max32BitValue) + |
+ (sqlite3_int64)ft.dwLowDateTime)/(sqlite3_int64)10000; |
+ |
+#ifdef SQLITE_TEST |
+ if( sqlite3_current_time ){ |
+ *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch; |
+ } |
+#endif |
+ UNUSED_PARAMETER(pVfs); |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Find the current time (in Universal Coordinated Time). Write the |
+** current time and date as a Julian Day number into *prNow and |
+** return 0. Return 1 if the time and date cannot be found. |
+*/ |
+static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){ |
+ int rc; |
+ sqlite3_int64 i; |
+ rc = winCurrentTimeInt64(pVfs, &i); |
+ if( !rc ){ |
+ *prNow = i/86400000.0; |
+ } |
+ return rc; |
+} |
+ |
+/* |
+** The idea is that this function works like a combination of |
+** GetLastError() and FormatMessage() on Windows (or errno and |
+** strerror_r() on Unix). After an error is returned by an OS |
+** function, SQLite calls this function with zBuf pointing to |
+** a buffer of nBuf bytes. The OS layer should populate the |
+** buffer with a nul-terminated UTF-8 encoded error message |
+** describing the last IO error to have occurred within the calling |
+** thread. |
+** |
+** If the error message is too large for the supplied buffer, |
+** it should be truncated. The return value of xGetLastError |
+** is zero if the error message fits in the buffer, or non-zero |
+** otherwise (if the message was truncated). If non-zero is returned, |
+** then it is not necessary to include the nul-terminator character |
+** in the output buffer. |
+** |
+** Not supplying an error message will have no adverse effect |
+** on SQLite. It is fine to have an implementation that never |
+** returns an error message: |
+** |
+** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ |
+** assert(zBuf[0]=='\0'); |
+** return 0; |
+** } |
+** |
+** However if an error message is supplied, it will be incorporated |
+** by sqlite into the error message available to the user using |
+** sqlite3_errmsg(), possibly making IO errors easier to debug. |
+*/ |
+static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ |
+ UNUSED_PARAMETER(pVfs); |
+ return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf); |
+} |
+ |
+/* |
+** Initialize and deinitialize the operating system interface. |
+*/ |
+SQLITE_API int SQLITE_STDCALL sqlite3_os_init(void){ |
+ static sqlite3_vfs winVfs = { |
+ 3, /* iVersion */ |
+ sizeof(winFile), /* szOsFile */ |
+ SQLITE_WIN32_MAX_PATH_BYTES, /* mxPathname */ |
+ 0, /* pNext */ |
+ "win32", /* zName */ |
+ 0, /* pAppData */ |
+ winOpen, /* xOpen */ |
+ winDelete, /* xDelete */ |
+ winAccess, /* xAccess */ |
+ winFullPathname, /* xFullPathname */ |
+ winDlOpen, /* xDlOpen */ |
+ winDlError, /* xDlError */ |
+ winDlSym, /* xDlSym */ |
+ winDlClose, /* xDlClose */ |
+ winRandomness, /* xRandomness */ |
+ winSleep, /* xSleep */ |
+ winCurrentTime, /* xCurrentTime */ |
+ winGetLastError, /* xGetLastError */ |
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */ |
+ winSetSystemCall, /* xSetSystemCall */ |
+ winGetSystemCall, /* xGetSystemCall */ |
+ winNextSystemCall, /* xNextSystemCall */ |
+ }; |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ static sqlite3_vfs winLongPathVfs = { |
+ 3, /* iVersion */ |
+ sizeof(winFile), /* szOsFile */ |
+ SQLITE_WINNT_MAX_PATH_BYTES, /* mxPathname */ |
+ 0, /* pNext */ |
+ "win32-longpath", /* zName */ |
+ 0, /* pAppData */ |
+ winOpen, /* xOpen */ |
+ winDelete, /* xDelete */ |
+ winAccess, /* xAccess */ |
+ winFullPathname, /* xFullPathname */ |
+ winDlOpen, /* xDlOpen */ |
+ winDlError, /* xDlError */ |
+ winDlSym, /* xDlSym */ |
+ winDlClose, /* xDlClose */ |
+ winRandomness, /* xRandomness */ |
+ winSleep, /* xSleep */ |
+ winCurrentTime, /* xCurrentTime */ |
+ winGetLastError, /* xGetLastError */ |
+ winCurrentTimeInt64, /* xCurrentTimeInt64 */ |
+ winSetSystemCall, /* xSetSystemCall */ |
+ winGetSystemCall, /* xGetSystemCall */ |
+ winNextSystemCall, /* xNextSystemCall */ |
+ }; |
+#endif |
+ |
+ /* Double-check that the aSyscall[] array has been constructed |
+ ** correctly. See ticket [bb3a86e890c8e96ab] */ |
+ assert( ArraySize(aSyscall)==80 ); |
+ |
+ /* get memory map allocation granularity */ |
+ memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
+#if SQLITE_OS_WINRT |
+ osGetNativeSystemInfo(&winSysInfo); |
+#else |
+ osGetSystemInfo(&winSysInfo); |
+#endif |
+ assert( winSysInfo.dwAllocationGranularity>0 ); |
+ assert( winSysInfo.dwPageSize>0 ); |
+ |
+ sqlite3_vfs_register(&winVfs, 1); |
+ |
+#if defined(SQLITE_WIN32_HAS_WIDE) |
+ sqlite3_vfs_register(&winLongPathVfs, 0); |
+#endif |
+ |
+ return SQLITE_OK; |
+} |
+ |
+SQLITE_API int SQLITE_STDCALL sqlite3_os_end(void){ |
+#if SQLITE_OS_WINRT |
+ if( sleepObj!=NULL ){ |
+ osCloseHandle(sleepObj); |
+ sleepObj = NULL; |
+ } |
+#endif |
+ return SQLITE_OK; |
+} |
+ |
+CHROMIUM_SQLITE_API |
+void chromium_sqlite3_initialize_win_sqlite3_file(sqlite3_file* file, HANDLE handle) { |
+ winFile* winSQLite3File = (winFile*)file; |
+ memset(file, 0, sizeof(*file)); |
+ winSQLite3File->pMethod = &winIoMethod; |
+ winSQLite3File->h = handle; |
+} |
+ |
+#endif /* SQLITE_OS_WIN */ |
+ |
+/************** End of os_win.c **********************************************/ |
+/************** Begin file bitvec.c ******************************************/ |
+/* |
+** 2008 February 16 |
+** |
+** 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 implements an object that represents a fixed-length |
+** bitmap. Bits are numbered starting with 1. |
+** |
+** A bitmap is used to record which pages of a database file have been |
+** journalled during a transaction, or which pages have the "dont-write" |
+** property. Usually only a few pages are meet either condition. |
+** So the bitmap is usually sparse and has low cardinality. |
+** But sometimes (for example when during a DROP of a large table) most |
+** or all of the pages in a database can get journalled. In those cases, |
+** the bitmap becomes dense with high cardinality. The algorithm needs |
+** to handle both cases well. |
+** |
+** The size of the bitmap is fixed when the object is created. |
+** |
+** All bits are clear when the bitmap is created. Individual bits |
+** may be set or cleared one at a time. |
+** |
+** Test operations are about 100 times more common that set operations. |
+** Clear operations are exceedingly rare. There are usually between |
+** 5 and 500 set operations per Bitvec object, though the number of sets can |
+** sometimes grow into tens of thousands or larger. The size of the |
+** Bitvec object is the number of pages in the database file at the |
+** start of a transaction, and is thus usually less than a few thousand, |
+** but can be as large as 2 billion for a really big database. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+/* Size of the Bitvec structure in bytes. */ |
+#define BITVEC_SZ 512 |
+ |
+/* Round the union size down to the nearest pointer boundary, since that's how |
+** it will be aligned within the Bitvec struct. */ |
+#define BITVEC_USIZE \ |
+ (((BITVEC_SZ-(3*sizeof(u32)))/sizeof(Bitvec*))*sizeof(Bitvec*)) |
+ |
+/* Type of the array "element" for the bitmap representation. |
+** Should be a power of 2, and ideally, evenly divide into BITVEC_USIZE. |
+** Setting this to the "natural word" size of your CPU may improve |
+** performance. */ |
+#define BITVEC_TELEM u8 |
+/* Size, in bits, of the bitmap element. */ |
+#define BITVEC_SZELEM 8 |
+/* Number of elements in a bitmap array. */ |
+#define BITVEC_NELEM (BITVEC_USIZE/sizeof(BITVEC_TELEM)) |
+/* Number of bits in the bitmap array. */ |
+#define BITVEC_NBIT (BITVEC_NELEM*BITVEC_SZELEM) |
+ |
+/* Number of u32 values in hash table. */ |
+#define BITVEC_NINT (BITVEC_USIZE/sizeof(u32)) |
+/* Maximum number of entries in hash table before |
+** sub-dividing and re-hashing. */ |
+#define BITVEC_MXHASH (BITVEC_NINT/2) |
+/* Hashing function for the aHash representation. |
+** Empirical testing showed that the *37 multiplier |
+** (an arbitrary prime)in the hash function provided |
+** no fewer collisions than the no-op *1. */ |
+#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) |
+ |
+#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) |
+ |
+ |
+/* |
+** A bitmap is an instance of the following structure. |
+** |
+** This bitmap records the existence of zero or more bits |
+** with values between 1 and iSize, inclusive. |
+** |
+** There are three possible representations of the bitmap. |
+** If iSize<=BITVEC_NBIT, then Bitvec.u.aBitmap[] is a straight |
+** bitmap. The least significant bit is bit 1. |
+** |
+** If iSize>BITVEC_NBIT and iDivisor==0 then Bitvec.u.aHash[] is |
+** a hash table that will hold up to BITVEC_MXHASH distinct values. |
+** |
+** Otherwise, the value i is redirected into one of BITVEC_NPTR |
+** sub-bitmaps pointed to by Bitvec.u.apSub[]. Each subbitmap |
+** handles up to iDivisor separate values of i. apSub[0] holds |
+** values between 1 and iDivisor. apSub[1] holds values between |
+** iDivisor+1 and 2*iDivisor. apSub[N] holds values between |
+** N*iDivisor+1 and (N+1)*iDivisor. Each subbitmap is normalized |
+** to hold deal with values between 1 and iDivisor. |
+*/ |
+struct Bitvec { |
+ u32 iSize; /* Maximum bit index. Max iSize is 4,294,967,296. */ |
+ u32 nSet; /* Number of bits that are set - only valid for aHash |
+ ** element. Max is BITVEC_NINT. For BITVEC_SZ of 512, |
+ ** this would be 125. */ |
+ u32 iDivisor; /* Number of bits handled by each apSub[] entry. */ |
+ /* Should >=0 for apSub element. */ |
+ /* Max iDivisor is max(u32) / BITVEC_NPTR + 1. */ |
+ /* For a BITVEC_SZ of 512, this would be 34,359,739. */ |
+ union { |
+ BITVEC_TELEM aBitmap[BITVEC_NELEM]; /* Bitmap representation */ |
+ u32 aHash[BITVEC_NINT]; /* Hash table representation */ |
+ Bitvec *apSub[BITVEC_NPTR]; /* Recursive representation */ |
+ } u; |
+}; |
+ |
+/* |
+** Create a new bitmap object able to handle bits between 0 and iSize, |
+** inclusive. Return a pointer to the new object. Return NULL if |
+** malloc fails. |
+*/ |
+SQLITE_PRIVATE Bitvec *sqlite3BitvecCreate(u32 iSize){ |
+ Bitvec *p; |
+ assert( sizeof(*p)==BITVEC_SZ ); |
+ p = sqlite3MallocZero( sizeof(*p) ); |
+ if( p ){ |
+ p->iSize = iSize; |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Check to see if the i-th bit is set. Return true or false. |
+** If p is NULL (if the bitmap has not been created) or if |
+** i is out of range, then return false. |
+*/ |
+SQLITE_PRIVATE int sqlite3BitvecTestNotNull(Bitvec *p, u32 i){ |
+ assert( p!=0 ); |
+ i--; |
+ if( i>=p->iSize ) return 0; |
+ while( p->iDivisor ){ |
+ u32 bin = i/p->iDivisor; |
+ i = i%p->iDivisor; |
+ p = p->u.apSub[bin]; |
+ if (!p) { |
+ return 0; |
+ } |
+ } |
+ if( p->iSize<=BITVEC_NBIT ){ |
+ return (p->u.aBitmap[i/BITVEC_SZELEM] & (1<<(i&(BITVEC_SZELEM-1))))!=0; |
+ } else{ |
+ u32 h = BITVEC_HASH(i++); |
+ while( p->u.aHash[h] ){ |
+ if( p->u.aHash[h]==i ) return 1; |
+ h = (h+1) % BITVEC_NINT; |
+ } |
+ return 0; |
+ } |
+} |
+SQLITE_PRIVATE int sqlite3BitvecTest(Bitvec *p, u32 i){ |
+ return p!=0 && sqlite3BitvecTestNotNull(p,i); |
+} |
+ |
+/* |
+** Set the i-th bit. Return 0 on success and an error code if |
+** anything goes wrong. |
+** |
+** This routine might cause sub-bitmaps to be allocated. Failing |
+** to get the memory needed to hold the sub-bitmap is the only |
+** that can go wrong with an insert, assuming p and i are valid. |
+** |
+** The calling function must ensure that p is a valid Bitvec object |
+** and that the value for "i" is within range of the Bitvec object. |
+** Otherwise the behavior is undefined. |
+*/ |
+SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ |
+ u32 h; |
+ if( p==0 ) return SQLITE_OK; |
+ assert( i>0 ); |
+ assert( i<=p->iSize ); |
+ i--; |
+ while((p->iSize > BITVEC_NBIT) && p->iDivisor) { |
+ u32 bin = i/p->iDivisor; |
+ i = i%p->iDivisor; |
+ if( p->u.apSub[bin]==0 ){ |
+ p->u.apSub[bin] = sqlite3BitvecCreate( p->iDivisor ); |
+ if( p->u.apSub[bin]==0 ) return SQLITE_NOMEM; |
+ } |
+ p = p->u.apSub[bin]; |
+ } |
+ if( p->iSize<=BITVEC_NBIT ){ |
+ p->u.aBitmap[i/BITVEC_SZELEM] |= 1 << (i&(BITVEC_SZELEM-1)); |
+ return SQLITE_OK; |
+ } |
+ h = BITVEC_HASH(i++); |
+ /* if there wasn't a hash collision, and this doesn't */ |
+ /* completely fill the hash, then just add it without */ |
+ /* worring about sub-dividing and re-hashing. */ |
+ if( !p->u.aHash[h] ){ |
+ if (p->nSet<(BITVEC_NINT-1)) { |
+ goto bitvec_set_end; |
+ } else { |
+ goto bitvec_set_rehash; |
+ } |
+ } |
+ /* there was a collision, check to see if it's already */ |
+ /* in hash, if not, try to find a spot for it */ |
+ do { |
+ if( p->u.aHash[h]==i ) return SQLITE_OK; |
+ h++; |
+ if( h>=BITVEC_NINT ) h = 0; |
+ } while( p->u.aHash[h] ); |
+ /* we didn't find it in the hash. h points to the first */ |
+ /* available free spot. check to see if this is going to */ |
+ /* make our hash too "full". */ |
+bitvec_set_rehash: |
+ if( p->nSet>=BITVEC_MXHASH ){ |
+ unsigned int j; |
+ int rc; |
+ u32 *aiValues = sqlite3StackAllocRaw(0, sizeof(p->u.aHash)); |
+ if( aiValues==0 ){ |
+ return SQLITE_NOMEM; |
+ }else{ |
+ memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); |
+ memset(p->u.apSub, 0, sizeof(p->u.apSub)); |
+ p->iDivisor = (p->iSize + BITVEC_NPTR - 1)/BITVEC_NPTR; |
+ rc = sqlite3BitvecSet(p, i); |
+ for(j=0; j<BITVEC_NINT; j++){ |
+ if( aiValues[j] ) rc |= sqlite3BitvecSet(p, aiValues[j]); |
+ } |
+ sqlite3StackFree(0, aiValues); |
+ return rc; |
+ } |
+ } |
+bitvec_set_end: |
+ p->nSet++; |
+ p->u.aHash[h] = i; |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Clear the i-th bit. |
+** |
+** pBuf must be a pointer to at least BITVEC_SZ bytes of temporary storage |
+** that BitvecClear can use to rebuilt its hash table. |
+*/ |
+SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ |
+ if( p==0 ) return; |
+ assert( i>0 ); |
+ i--; |
+ while( p->iDivisor ){ |
+ u32 bin = i/p->iDivisor; |
+ i = i%p->iDivisor; |
+ p = p->u.apSub[bin]; |
+ if (!p) { |
+ return; |
+ } |
+ } |
+ if( p->iSize<=BITVEC_NBIT ){ |
+ p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); |
+ }else{ |
+ unsigned int j; |
+ u32 *aiValues = pBuf; |
+ memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); |
+ memset(p->u.aHash, 0, sizeof(p->u.aHash)); |
+ p->nSet = 0; |
+ for(j=0; j<BITVEC_NINT; j++){ |
+ if( aiValues[j] && aiValues[j]!=(i+1) ){ |
+ u32 h = BITVEC_HASH(aiValues[j]-1); |
+ p->nSet++; |
+ while( p->u.aHash[h] ){ |
+ h++; |
+ if( h>=BITVEC_NINT ) h = 0; |
+ } |
+ p->u.aHash[h] = aiValues[j]; |
+ } |
+ } |
+ } |
+} |
+ |
+/* |
+** Destroy a bitmap object. Reclaim all memory used. |
+*/ |
+SQLITE_PRIVATE void sqlite3BitvecDestroy(Bitvec *p){ |
+ if( p==0 ) return; |
+ if( p->iDivisor ){ |
+ unsigned int i; |
+ for(i=0; i<BITVEC_NPTR; i++){ |
+ sqlite3BitvecDestroy(p->u.apSub[i]); |
+ } |
+ } |
+ sqlite3_free(p); |
+} |
+ |
+/* |
+** Return the value of the iSize parameter specified when Bitvec *p |
+** was created. |
+*/ |
+SQLITE_PRIVATE u32 sqlite3BitvecSize(Bitvec *p){ |
+ return p->iSize; |
+} |
+ |
+#ifndef SQLITE_OMIT_BUILTIN_TEST |
+/* |
+** Let V[] be an array of unsigned characters sufficient to hold |
+** up to N bits. Let I be an integer between 0 and N. 0<=I<N. |
+** Then the following macros can be used to set, clear, or test |
+** individual bits within V. |
+*/ |
+#define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) |
+#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) |
+#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 |
+ |
+/* |
+** This routine runs an extensive test of the Bitvec code. |
+** |
+** The input is an array of integers that acts as a program |
+** to test the Bitvec. The integers are opcodes followed |
+** by 0, 1, or 3 operands, depending on the opcode. Another |
+** opcode follows immediately after the last operand. |
+** |
+** There are 6 opcodes numbered from 0 through 5. 0 is the |
+** "halt" opcode and causes the test to end. |
+** |
+** 0 Halt and return the number of errors |
+** 1 N S X Set N bits beginning with S and incrementing by X |
+** 2 N S X Clear N bits beginning with S and incrementing by X |
+** 3 N Set N randomly chosen bits |
+** 4 N Clear N randomly chosen bits |
+** 5 N S X Set N bits from S increment X in array only, not in bitvec |
+** |
+** The opcodes 1 through 4 perform set and clear operations are performed |
+** on both a Bitvec object and on a linear array of bits obtained from malloc. |
+** Opcode 5 works on the linear array only, not on the Bitvec. |
+** Opcode 5 is used to deliberately induce a fault in order to |
+** confirm that error detection works. |
+** |
+** At the conclusion of the test the linear array is compared |
+** against the Bitvec object. If there are any differences, |
+** an error is returned. If they are the same, zero is returned. |
+** |
+** If a memory allocation error occurs, return -1. |
+*/ |
+SQLITE_PRIVATE int sqlite3BitvecBuiltinTest(int sz, int *aOp){ |
+ Bitvec *pBitvec = 0; |
+ unsigned char *pV = 0; |
+ int rc = -1; |
+ int i, nx, pc, op; |
+ void *pTmpSpace; |
+ |
+ /* Allocate the Bitvec to be tested and a linear array of |
+ ** bits to act as the reference */ |
+ pBitvec = sqlite3BitvecCreate( sz ); |
+ pV = sqlite3MallocZero( (sz+7)/8 + 1 ); |
+ pTmpSpace = sqlite3_malloc64(BITVEC_SZ); |
+ if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; |
+ |
+ /* NULL pBitvec tests */ |
+ sqlite3BitvecSet(0, 1); |
+ sqlite3BitvecClear(0, 1, pTmpSpace); |
+ |
+ /* Run the program */ |
+ pc = 0; |
+ while( (op = aOp[pc])!=0 ){ |
+ switch( op ){ |
+ case 1: |
+ case 2: |
+ case 5: { |
+ nx = 4; |
+ i = aOp[pc+2] - 1; |
+ aOp[pc+2] += aOp[pc+3]; |
+ break; |
+ } |
+ case 3: |
+ case 4: |
+ default: { |
+ nx = 2; |
+ sqlite3_randomness(sizeof(i), &i); |
+ break; |
+ } |
+ } |
+ if( (--aOp[pc+1]) > 0 ) nx = 0; |
+ pc += nx; |
+ i = (i & 0x7fffffff)%sz; |
+ if( (op & 1)!=0 ){ |
+ SETBIT(pV, (i+1)); |
+ if( op!=5 ){ |
+ if( sqlite3BitvecSet(pBitvec, i+1) ) goto bitvec_end; |
+ } |
+ }else{ |
+ CLEARBIT(pV, (i+1)); |
+ sqlite3BitvecClear(pBitvec, i+1, pTmpSpace); |
+ } |
+ } |
+ |
+ /* Test to make sure the linear array exactly matches the |
+ ** Bitvec object. Start with the assumption that they do |
+ ** match (rc==0). Change rc to non-zero if a discrepancy |
+ ** is found. |
+ */ |
+ rc = sqlite3BitvecTest(0,0) + sqlite3BitvecTest(pBitvec, sz+1) |
+ + sqlite3BitvecTest(pBitvec, 0) |
+ + (sqlite3BitvecSize(pBitvec) - sz); |
+ for(i=1; i<=sz; i++){ |
+ if( (TESTBIT(pV,i))!=sqlite3BitvecTest(pBitvec,i) ){ |
+ rc = i; |
+ break; |
+ } |
+ } |
+ |
+ /* Free allocated structure */ |
+bitvec_end: |
+ sqlite3_free(pTmpSpace); |
+ sqlite3_free(pV); |
+ sqlite3BitvecDestroy(pBitvec); |
+ return rc; |
+} |
+#endif /* SQLITE_OMIT_BUILTIN_TEST */ |
+ |
+/************** End of bitvec.c **********************************************/ |
+/************** Begin file pcache.c ******************************************/ |
+/* |
+** 2008 August 05 |
+** |
+** 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 implements that page cache. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+/* |
+** A complete page cache is an instance of this structure. |
+*/ |
+struct PCache { |
+ PgHdr *pDirty, *pDirtyTail; /* List of dirty pages in LRU order */ |
+ PgHdr *pSynced; /* Last synced page in dirty page list */ |
+ int nRefSum; /* Sum of ref counts over all pages */ |
+ int szCache; /* Configured cache size */ |
+ int szSpill; /* Size before spilling occurs */ |
+ int szPage; /* Size of every page in this cache */ |
+ int szExtra; /* Size of extra space for each page */ |
+ u8 bPurgeable; /* True if pages are on backing store */ |
+ u8 eCreate; /* eCreate value for for xFetch() */ |
+ int (*xStress)(void*,PgHdr*); /* Call to try make a page clean */ |
+ void *pStress; /* Argument to xStress */ |
+ sqlite3_pcache *pCache; /* Pluggable cache module */ |
+}; |
+ |
+/********************************** Linked List Management ********************/ |
+ |
+/* Allowed values for second argument to pcacheManageDirtyList() */ |
+#define PCACHE_DIRTYLIST_REMOVE 1 /* Remove pPage from dirty list */ |
+#define PCACHE_DIRTYLIST_ADD 2 /* Add pPage to the dirty list */ |
+#define PCACHE_DIRTYLIST_FRONT 3 /* Move pPage to the front of the list */ |
+ |
+/* |
+** Manage pPage's participation on the dirty list. Bits of the addRemove |
+** argument determines what operation to do. The 0x01 bit means first |
+** remove pPage from the dirty list. The 0x02 means add pPage back to |
+** the dirty list. Doing both moves pPage to the front of the dirty list. |
+*/ |
+static void pcacheManageDirtyList(PgHdr *pPage, u8 addRemove){ |
+ PCache *p = pPage->pCache; |
+ |
+ if( addRemove & PCACHE_DIRTYLIST_REMOVE ){ |
+ assert( pPage->pDirtyNext || pPage==p->pDirtyTail ); |
+ assert( pPage->pDirtyPrev || pPage==p->pDirty ); |
+ |
+ /* Update the PCache1.pSynced variable if necessary. */ |
+ if( p->pSynced==pPage ){ |
+ PgHdr *pSynced = pPage->pDirtyPrev; |
+ while( pSynced && (pSynced->flags&PGHDR_NEED_SYNC) ){ |
+ pSynced = pSynced->pDirtyPrev; |
+ } |
+ p->pSynced = pSynced; |
+ } |
+ |
+ if( pPage->pDirtyNext ){ |
+ pPage->pDirtyNext->pDirtyPrev = pPage->pDirtyPrev; |
+ }else{ |
+ assert( pPage==p->pDirtyTail ); |
+ p->pDirtyTail = pPage->pDirtyPrev; |
+ } |
+ if( pPage->pDirtyPrev ){ |
+ pPage->pDirtyPrev->pDirtyNext = pPage->pDirtyNext; |
+ }else{ |
+ assert( pPage==p->pDirty ); |
+ p->pDirty = pPage->pDirtyNext; |
+ if( p->pDirty==0 && p->bPurgeable ){ |
+ assert( p->eCreate==1 ); |
+ p->eCreate = 2; |
+ } |
+ } |
+ pPage->pDirtyNext = 0; |
+ pPage->pDirtyPrev = 0; |
+ } |
+ if( addRemove & PCACHE_DIRTYLIST_ADD ){ |
+ assert( pPage->pDirtyNext==0 && pPage->pDirtyPrev==0 && p->pDirty!=pPage ); |
+ |
+ pPage->pDirtyNext = p->pDirty; |
+ if( pPage->pDirtyNext ){ |
+ assert( pPage->pDirtyNext->pDirtyPrev==0 ); |
+ pPage->pDirtyNext->pDirtyPrev = pPage; |
+ }else{ |
+ p->pDirtyTail = pPage; |
+ if( p->bPurgeable ){ |
+ assert( p->eCreate==2 ); |
+ p->eCreate = 1; |
+ } |
+ } |
+ p->pDirty = pPage; |
+ if( !p->pSynced && 0==(pPage->flags&PGHDR_NEED_SYNC) ){ |
+ p->pSynced = pPage; |
+ } |
+ } |
+} |
+ |
+/* |
+** Wrapper around the pluggable caches xUnpin method. If the cache is |
+** being used for an in-memory database, this function is a no-op. |
+*/ |
+static void pcacheUnpin(PgHdr *p){ |
+ if( p->pCache->bPurgeable ){ |
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 0); |
+ } |
+} |
+ |
+/* |
+** Compute the number of pages of cache requested. p->szCache is the |
+** cache size requested by the "PRAGMA cache_size" statement. |
+*/ |
+static int numberOfCachePages(PCache *p){ |
+ if( p->szCache>=0 ){ |
+ /* IMPLEMENTATION-OF: R-42059-47211 If the argument N is positive then the |
+ ** suggested cache size is set to N. */ |
+ return p->szCache; |
+ }else{ |
+ /* IMPLEMENTATION-OF: R-61436-13639 If the argument N is negative, then |
+ ** the number of cache pages is adjusted to use approximately abs(N*1024) |
+ ** bytes of memory. */ |
+ return (int)((-1024*(i64)p->szCache)/(p->szPage+p->szExtra)); |
+ } |
+} |
+ |
+/*************************************************** General Interfaces ****** |
+** |
+** Initialize and shutdown the page cache subsystem. Neither of these |
+** functions are threadsafe. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheInitialize(void){ |
+ if( sqlite3GlobalConfig.pcache2.xInit==0 ){ |
+ /* IMPLEMENTATION-OF: R-26801-64137 If the xInit() method is NULL, then the |
+ ** built-in default page cache is used instead of the application defined |
+ ** page cache. */ |
+ sqlite3PCacheSetDefault(); |
+ } |
+ return sqlite3GlobalConfig.pcache2.xInit(sqlite3GlobalConfig.pcache2.pArg); |
+} |
+SQLITE_PRIVATE void sqlite3PcacheShutdown(void){ |
+ if( sqlite3GlobalConfig.pcache2.xShutdown ){ |
+ /* IMPLEMENTATION-OF: R-26000-56589 The xShutdown() method may be NULL. */ |
+ sqlite3GlobalConfig.pcache2.xShutdown(sqlite3GlobalConfig.pcache2.pArg); |
+ } |
+} |
+ |
+/* |
+** Return the size in bytes of a PCache object. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheSize(void){ return sizeof(PCache); } |
+ |
+/* |
+** Create a new PCache object. Storage space to hold the object |
+** has already been allocated and is passed in as the p pointer. |
+** The caller discovers how much space needs to be allocated by |
+** calling sqlite3PcacheSize(). |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheOpen( |
+ int szPage, /* Size of every page */ |
+ int szExtra, /* Extra space associated with each page */ |
+ int bPurgeable, /* True if pages are on backing store */ |
+ int (*xStress)(void*,PgHdr*),/* Call to try to make pages clean */ |
+ void *pStress, /* Argument to xStress */ |
+ PCache *p /* Preallocated space for the PCache */ |
+){ |
+ memset(p, 0, sizeof(PCache)); |
+ p->szPage = 1; |
+ p->szExtra = szExtra; |
+ p->bPurgeable = bPurgeable; |
+ p->eCreate = 2; |
+ p->xStress = xStress; |
+ p->pStress = pStress; |
+ p->szCache = 100; |
+ p->szSpill = 1; |
+ return sqlite3PcacheSetPageSize(p, szPage); |
+} |
+ |
+/* |
+** Change the page size for PCache object. The caller must ensure that there |
+** are no outstanding page references when this function is called. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheSetPageSize(PCache *pCache, int szPage){ |
+ assert( pCache->nRefSum==0 && pCache->pDirty==0 ); |
+ if( pCache->szPage ){ |
+ sqlite3_pcache *pNew; |
+ pNew = sqlite3GlobalConfig.pcache2.xCreate( |
+ szPage, pCache->szExtra + ROUND8(sizeof(PgHdr)), |
+ pCache->bPurgeable |
+ ); |
+ if( pNew==0 ) return SQLITE_NOMEM; |
+ sqlite3GlobalConfig.pcache2.xCachesize(pNew, numberOfCachePages(pCache)); |
+ if( pCache->pCache ){ |
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); |
+ } |
+ pCache->pCache = pNew; |
+ pCache->szPage = szPage; |
+ } |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Try to obtain a page from the cache. |
+** |
+** This routine returns a pointer to an sqlite3_pcache_page object if |
+** such an object is already in cache, or if a new one is created. |
+** This routine returns a NULL pointer if the object was not in cache |
+** and could not be created. |
+** |
+** The createFlags should be 0 to check for existing pages and should |
+** be 3 (not 1, but 3) to try to create a new page. |
+** |
+** If the createFlag is 0, then NULL is always returned if the page |
+** is not already in the cache. If createFlag is 1, then a new page |
+** is created only if that can be done without spilling dirty pages |
+** and without exceeding the cache size limit. |
+** |
+** The caller needs to invoke sqlite3PcacheFetchFinish() to properly |
+** initialize the sqlite3_pcache_page object and convert it into a |
+** PgHdr object. The sqlite3PcacheFetch() and sqlite3PcacheFetchFinish() |
+** routines are split this way for performance reasons. When separated |
+** they can both (usually) operate without having to push values to |
+** the stack on entry and pop them back off on exit, which saves a |
+** lot of pushing and popping. |
+*/ |
+SQLITE_PRIVATE sqlite3_pcache_page *sqlite3PcacheFetch( |
+ PCache *pCache, /* Obtain the page from this cache */ |
+ Pgno pgno, /* Page number to obtain */ |
+ int createFlag /* If true, create page if it does not exist already */ |
+){ |
+ int eCreate; |
+ |
+ assert( pCache!=0 ); |
+ assert( pCache->pCache!=0 ); |
+ assert( createFlag==3 || createFlag==0 ); |
+ assert( pgno>0 ); |
+ |
+ /* eCreate defines what to do if the page does not exist. |
+ ** 0 Do not allocate a new page. (createFlag==0) |
+ ** 1 Allocate a new page if doing so is inexpensive. |
+ ** (createFlag==1 AND bPurgeable AND pDirty) |
+ ** 2 Allocate a new page even it doing so is difficult. |
+ ** (createFlag==1 AND !(bPurgeable AND pDirty) |
+ */ |
+ eCreate = createFlag & pCache->eCreate; |
+ assert( eCreate==0 || eCreate==1 || eCreate==2 ); |
+ assert( createFlag==0 || pCache->eCreate==eCreate ); |
+ assert( createFlag==0 || eCreate==1+(!pCache->bPurgeable||!pCache->pDirty) ); |
+ return sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, eCreate); |
+} |
+ |
+/* |
+** If the sqlite3PcacheFetch() routine is unable to allocate a new |
+** page because new clean pages are available for reuse and the cache |
+** size limit has been reached, then this routine can be invoked to |
+** try harder to allocate a page. This routine might invoke the stress |
+** callback to spill dirty pages to the journal. It will then try to |
+** allocate the new page and will only fail to allocate a new page on |
+** an OOM error. |
+** |
+** This routine should be invoked only after sqlite3PcacheFetch() fails. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheFetchStress( |
+ PCache *pCache, /* Obtain the page from this cache */ |
+ Pgno pgno, /* Page number to obtain */ |
+ sqlite3_pcache_page **ppPage /* Write result here */ |
+){ |
+ PgHdr *pPg; |
+ if( pCache->eCreate==2 ) return 0; |
+ |
+ if( sqlite3PcachePagecount(pCache)>pCache->szSpill ){ |
+ /* Find a dirty page to write-out and recycle. First try to find a |
+ ** page that does not require a journal-sync (one with PGHDR_NEED_SYNC |
+ ** cleared), but if that is not possible settle for any other |
+ ** unreferenced dirty page. |
+ */ |
+ for(pPg=pCache->pSynced; |
+ pPg && (pPg->nRef || (pPg->flags&PGHDR_NEED_SYNC)); |
+ pPg=pPg->pDirtyPrev |
+ ); |
+ pCache->pSynced = pPg; |
+ if( !pPg ){ |
+ for(pPg=pCache->pDirtyTail; pPg && pPg->nRef; pPg=pPg->pDirtyPrev); |
+ } |
+ if( pPg ){ |
+ int rc; |
+#ifdef SQLITE_LOG_CACHE_SPILL |
+ sqlite3_log(SQLITE_FULL, |
+ "spill page %d making room for %d - cache used: %d/%d", |
+ pPg->pgno, pgno, |
+ sqlite3GlobalConfig.pcache.xPagecount(pCache->pCache), |
+ numberOfCachePages(pCache)); |
+#endif |
+ rc = pCache->xStress(pCache->pStress, pPg); |
+ if( rc!=SQLITE_OK && rc!=SQLITE_BUSY ){ |
+ return rc; |
+ } |
+ } |
+ } |
+ *ppPage = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache, pgno, 2); |
+ return *ppPage==0 ? SQLITE_NOMEM : SQLITE_OK; |
+} |
+ |
+/* |
+** This is a helper routine for sqlite3PcacheFetchFinish() |
+** |
+** In the uncommon case where the page being fetched has not been |
+** initialized, this routine is invoked to do the initialization. |
+** This routine is broken out into a separate function since it |
+** requires extra stack manipulation that can be avoided in the common |
+** case. |
+*/ |
+static SQLITE_NOINLINE PgHdr *pcacheFetchFinishWithInit( |
+ PCache *pCache, /* Obtain the page from this cache */ |
+ Pgno pgno, /* Page number obtained */ |
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ |
+){ |
+ PgHdr *pPgHdr; |
+ assert( pPage!=0 ); |
+ pPgHdr = (PgHdr*)pPage->pExtra; |
+ assert( pPgHdr->pPage==0 ); |
+ memset(pPgHdr, 0, sizeof(PgHdr)); |
+ pPgHdr->pPage = pPage; |
+ pPgHdr->pData = pPage->pBuf; |
+ pPgHdr->pExtra = (void *)&pPgHdr[1]; |
+ memset(pPgHdr->pExtra, 0, pCache->szExtra); |
+ pPgHdr->pCache = pCache; |
+ pPgHdr->pgno = pgno; |
+ pPgHdr->flags = PGHDR_CLEAN; |
+ return sqlite3PcacheFetchFinish(pCache,pgno,pPage); |
+} |
+ |
+/* |
+** This routine converts the sqlite3_pcache_page object returned by |
+** sqlite3PcacheFetch() into an initialized PgHdr object. This routine |
+** must be called after sqlite3PcacheFetch() in order to get a usable |
+** result. |
+*/ |
+SQLITE_PRIVATE PgHdr *sqlite3PcacheFetchFinish( |
+ PCache *pCache, /* Obtain the page from this cache */ |
+ Pgno pgno, /* Page number obtained */ |
+ sqlite3_pcache_page *pPage /* Page obtained by prior PcacheFetch() call */ |
+){ |
+ PgHdr *pPgHdr; |
+ |
+ assert( pPage!=0 ); |
+ pPgHdr = (PgHdr *)pPage->pExtra; |
+ |
+ if( !pPgHdr->pPage ){ |
+ return pcacheFetchFinishWithInit(pCache, pgno, pPage); |
+ } |
+ pCache->nRefSum++; |
+ pPgHdr->nRef++; |
+ return pPgHdr; |
+} |
+ |
+/* |
+** Decrement the reference count on a page. If the page is clean and the |
+** reference count drops to 0, then it is made eligible for recycling. |
+*/ |
+SQLITE_PRIVATE void SQLITE_NOINLINE sqlite3PcacheRelease(PgHdr *p){ |
+ assert( p->nRef>0 ); |
+ p->pCache->nRefSum--; |
+ if( (--p->nRef)==0 ){ |
+ if( p->flags&PGHDR_CLEAN ){ |
+ pcacheUnpin(p); |
+ }else if( p->pDirtyPrev!=0 ){ |
+ /* Move the page to the head of the dirty list. */ |
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); |
+ } |
+ } |
+} |
+ |
+/* |
+** Increase the reference count of a supplied page by 1. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheRef(PgHdr *p){ |
+ assert(p->nRef>0); |
+ p->nRef++; |
+ p->pCache->nRefSum++; |
+} |
+ |
+/* |
+** Drop a page from the cache. There must be exactly one reference to the |
+** page. This function deletes that reference, so after it returns the |
+** page pointed to by p is invalid. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheDrop(PgHdr *p){ |
+ assert( p->nRef==1 ); |
+ if( p->flags&PGHDR_DIRTY ){ |
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); |
+ } |
+ p->pCache->nRefSum--; |
+ sqlite3GlobalConfig.pcache2.xUnpin(p->pCache->pCache, p->pPage, 1); |
+} |
+ |
+/* |
+** Make sure the page is marked as dirty. If it isn't dirty already, |
+** make it so. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheMakeDirty(PgHdr *p){ |
+ assert( p->nRef>0 ); |
+ if( p->flags & (PGHDR_CLEAN|PGHDR_DONT_WRITE) ){ |
+ p->flags &= ~PGHDR_DONT_WRITE; |
+ if( p->flags & PGHDR_CLEAN ){ |
+ p->flags ^= (PGHDR_DIRTY|PGHDR_CLEAN); |
+ assert( (p->flags & (PGHDR_DIRTY|PGHDR_CLEAN))==PGHDR_DIRTY ); |
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_ADD); |
+ } |
+ } |
+} |
+ |
+/* |
+** Make sure the page is marked as clean. If it isn't clean already, |
+** make it so. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheMakeClean(PgHdr *p){ |
+ if( (p->flags & PGHDR_DIRTY) ){ |
+ assert( (p->flags & PGHDR_CLEAN)==0 ); |
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_REMOVE); |
+ p->flags &= ~(PGHDR_DIRTY|PGHDR_NEED_SYNC|PGHDR_WRITEABLE); |
+ p->flags |= PGHDR_CLEAN; |
+ if( p->nRef==0 ){ |
+ pcacheUnpin(p); |
+ } |
+ } |
+} |
+ |
+/* |
+** Make every page in the cache clean. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheCleanAll(PCache *pCache){ |
+ PgHdr *p; |
+ while( (p = pCache->pDirty)!=0 ){ |
+ sqlite3PcacheMakeClean(p); |
+ } |
+} |
+ |
+/* |
+** Clear the PGHDR_NEED_SYNC flag from all dirty pages. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheClearSyncFlags(PCache *pCache){ |
+ PgHdr *p; |
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){ |
+ p->flags &= ~PGHDR_NEED_SYNC; |
+ } |
+ pCache->pSynced = pCache->pDirtyTail; |
+} |
+ |
+/* |
+** Change the page number of page p to newPgno. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheMove(PgHdr *p, Pgno newPgno){ |
+ PCache *pCache = p->pCache; |
+ assert( p->nRef>0 ); |
+ assert( newPgno>0 ); |
+ sqlite3GlobalConfig.pcache2.xRekey(pCache->pCache, p->pPage, p->pgno,newPgno); |
+ p->pgno = newPgno; |
+ if( (p->flags&PGHDR_DIRTY) && (p->flags&PGHDR_NEED_SYNC) ){ |
+ pcacheManageDirtyList(p, PCACHE_DIRTYLIST_FRONT); |
+ } |
+} |
+ |
+/* |
+** Drop every cache entry whose page number is greater than "pgno". The |
+** caller must ensure that there are no outstanding references to any pages |
+** other than page 1 with a page number greater than pgno. |
+** |
+** If there is a reference to page 1 and the pgno parameter passed to this |
+** function is 0, then the data area associated with page 1 is zeroed, but |
+** the page object is not dropped. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheTruncate(PCache *pCache, Pgno pgno){ |
+ if( pCache->pCache ){ |
+ PgHdr *p; |
+ PgHdr *pNext; |
+ for(p=pCache->pDirty; p; p=pNext){ |
+ pNext = p->pDirtyNext; |
+ /* This routine never gets call with a positive pgno except right |
+ ** after sqlite3PcacheCleanAll(). So if there are dirty pages, |
+ ** it must be that pgno==0. |
+ */ |
+ assert( p->pgno>0 ); |
+ if( ALWAYS(p->pgno>pgno) ){ |
+ assert( p->flags&PGHDR_DIRTY ); |
+ sqlite3PcacheMakeClean(p); |
+ } |
+ } |
+ if( pgno==0 && pCache->nRefSum ){ |
+ sqlite3_pcache_page *pPage1; |
+ pPage1 = sqlite3GlobalConfig.pcache2.xFetch(pCache->pCache,1,0); |
+ if( ALWAYS(pPage1) ){ /* Page 1 is always available in cache, because |
+ ** pCache->nRefSum>0 */ |
+ memset(pPage1->pBuf, 0, pCache->szPage); |
+ pgno = 1; |
+ } |
+ } |
+ sqlite3GlobalConfig.pcache2.xTruncate(pCache->pCache, pgno+1); |
+ } |
+} |
+ |
+/* |
+** Close a cache. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheClose(PCache *pCache){ |
+ assert( pCache->pCache!=0 ); |
+ sqlite3GlobalConfig.pcache2.xDestroy(pCache->pCache); |
+} |
+ |
+/* |
+** Discard the contents of the cache. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheClear(PCache *pCache){ |
+ sqlite3PcacheTruncate(pCache, 0); |
+} |
+ |
+/* |
+** Merge two lists of pages connected by pDirty and in pgno order. |
+** Do not both fixing the pDirtyPrev pointers. |
+*/ |
+static PgHdr *pcacheMergeDirtyList(PgHdr *pA, PgHdr *pB){ |
+ PgHdr result, *pTail; |
+ pTail = &result; |
+ while( pA && pB ){ |
+ if( pA->pgno<pB->pgno ){ |
+ pTail->pDirty = pA; |
+ pTail = pA; |
+ pA = pA->pDirty; |
+ }else{ |
+ pTail->pDirty = pB; |
+ pTail = pB; |
+ pB = pB->pDirty; |
+ } |
+ } |
+ if( pA ){ |
+ pTail->pDirty = pA; |
+ }else if( pB ){ |
+ pTail->pDirty = pB; |
+ }else{ |
+ pTail->pDirty = 0; |
+ } |
+ return result.pDirty; |
+} |
+ |
+/* |
+** Sort the list of pages in accending order by pgno. Pages are |
+** connected by pDirty pointers. The pDirtyPrev pointers are |
+** corrupted by this sort. |
+** |
+** Since there cannot be more than 2^31 distinct pages in a database, |
+** there cannot be more than 31 buckets required by the merge sorter. |
+** One extra bucket is added to catch overflow in case something |
+** ever changes to make the previous sentence incorrect. |
+*/ |
+#define N_SORT_BUCKET 32 |
+static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ |
+ PgHdr *a[N_SORT_BUCKET], *p; |
+ int i; |
+ memset(a, 0, sizeof(a)); |
+ while( pIn ){ |
+ p = pIn; |
+ pIn = p->pDirty; |
+ p->pDirty = 0; |
+ for(i=0; ALWAYS(i<N_SORT_BUCKET-1); i++){ |
+ if( a[i]==0 ){ |
+ a[i] = p; |
+ break; |
+ }else{ |
+ p = pcacheMergeDirtyList(a[i], p); |
+ a[i] = 0; |
+ } |
+ } |
+ if( NEVER(i==N_SORT_BUCKET-1) ){ |
+ /* To get here, there need to be 2^(N_SORT_BUCKET) elements in |
+ ** the input list. But that is impossible. |
+ */ |
+ a[i] = pcacheMergeDirtyList(a[i], p); |
+ } |
+ } |
+ p = a[0]; |
+ for(i=1; i<N_SORT_BUCKET; i++){ |
+ p = pcacheMergeDirtyList(p, a[i]); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Return a list of all dirty pages in the cache, sorted by page number. |
+*/ |
+SQLITE_PRIVATE PgHdr *sqlite3PcacheDirtyList(PCache *pCache){ |
+ PgHdr *p; |
+ for(p=pCache->pDirty; p; p=p->pDirtyNext){ |
+ p->pDirty = p->pDirtyNext; |
+ } |
+ return pcacheSortDirtyList(pCache->pDirty); |
+} |
+ |
+/* |
+** Return the total number of references to all pages held by the cache. |
+** |
+** This is not the total number of pages referenced, but the sum of the |
+** reference count for all pages. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheRefCount(PCache *pCache){ |
+ return pCache->nRefSum; |
+} |
+ |
+/* |
+** Return the number of references to the page supplied as an argument. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcachePageRefcount(PgHdr *p){ |
+ return p->nRef; |
+} |
+ |
+/* |
+** Return the total number of pages in the cache. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcachePagecount(PCache *pCache){ |
+ assert( pCache->pCache!=0 ); |
+ return sqlite3GlobalConfig.pcache2.xPagecount(pCache->pCache); |
+} |
+ |
+#ifdef SQLITE_TEST |
+/* |
+** Get the suggested cache-size value. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheGetCachesize(PCache *pCache){ |
+ return numberOfCachePages(pCache); |
+} |
+#endif |
+ |
+/* |
+** Set the suggested cache-size value. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheSetCachesize(PCache *pCache, int mxPage){ |
+ assert( pCache->pCache!=0 ); |
+ pCache->szCache = mxPage; |
+ sqlite3GlobalConfig.pcache2.xCachesize(pCache->pCache, |
+ numberOfCachePages(pCache)); |
+} |
+ |
+/* |
+** Set the suggested cache-spill value. Make no changes if if the |
+** argument is zero. Return the effective cache-spill size, which will |
+** be the larger of the szSpill and szCache. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheSetSpillsize(PCache *p, int mxPage){ |
+ int res; |
+ assert( p->pCache!=0 ); |
+ if( mxPage ){ |
+ if( mxPage<0 ){ |
+ mxPage = (int)((-1024*(i64)mxPage)/(p->szPage+p->szExtra)); |
+ } |
+ p->szSpill = mxPage; |
+ } |
+ res = numberOfCachePages(p); |
+ if( res<p->szSpill ) res = p->szSpill; |
+ return res; |
+} |
+ |
+/* |
+** Free up as much memory as possible from the page cache. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheShrink(PCache *pCache){ |
+ assert( pCache->pCache!=0 ); |
+ sqlite3GlobalConfig.pcache2.xShrink(pCache->pCache); |
+} |
+ |
+/* |
+** Return the size of the header added by this middleware layer |
+** in the page-cache hierarchy. |
+*/ |
+SQLITE_PRIVATE int sqlite3HeaderSizePcache(void){ return ROUND8(sizeof(PgHdr)); } |
+ |
+ |
+#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) |
+/* |
+** For all dirty pages currently in the cache, invoke the specified |
+** callback. This is only used if the SQLITE_CHECK_PAGES macro is |
+** defined. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheIterateDirty(PCache *pCache, void (*xIter)(PgHdr *)){ |
+ PgHdr *pDirty; |
+ for(pDirty=pCache->pDirty; pDirty; pDirty=pDirty->pDirtyNext){ |
+ xIter(pDirty); |
+ } |
+} |
+#endif |
+ |
+/************** End of pcache.c **********************************************/ |
+/************** Begin file pcache1.c *****************************************/ |
+/* |
+** 2008 November 05 |
+** |
+** 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 implements the default page cache implementation (the |
+** sqlite3_pcache interface). It also contains part of the implementation |
+** of the SQLITE_CONFIG_PAGECACHE and sqlite3_release_memory() features. |
+** If the default page cache implementation is overridden, then neither of |
+** these two features are available. |
+** |
+** A Page cache line looks like this: |
+** |
+** ------------------------------------------------------------- |
+** | database page content | PgHdr1 | MemPage | PgHdr | |
+** ------------------------------------------------------------- |
+** |
+** The database page content is up front (so that buffer overreads tend to |
+** flow harmlessly into the PgHdr1, MemPage, and PgHdr extensions). MemPage |
+** is the extension added by the btree.c module containing information such |
+** as the database page number and how that database page is used. PgHdr |
+** is added by the pcache.c layer and contains information used to keep track |
+** of which pages are "dirty". PgHdr1 is an extension added by this |
+** module (pcache1.c). The PgHdr1 header is a subclass of sqlite3_pcache_page. |
+** PgHdr1 contains information needed to look up a page by its page number. |
+** The superclass sqlite3_pcache_page.pBuf points to the start of the |
+** database page content and sqlite3_pcache_page.pExtra points to PgHdr. |
+** |
+** The size of the extension (MemPage+PgHdr+PgHdr1) can be determined at |
+** runtime using sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &size). The |
+** sizes of the extensions sum to 272 bytes on x64 for 3.8.10, but this |
+** size can vary according to architecture, compile-time options, and |
+** SQLite library version number. |
+** |
+** If SQLITE_PCACHE_SEPARATE_HEADER is defined, then the extension is obtained |
+** using a separate memory allocation from the database page content. This |
+** seeks to overcome the "clownshoe" problem (also called "internal |
+** fragmentation" in academic literature) of allocating a few bytes more |
+** than a power of two with the memory allocator rounding up to the next |
+** power of two, and leaving the rounded-up space unused. |
+** |
+** This module tracks pointers to PgHdr1 objects. Only pcache.c communicates |
+** with this module. Information is passed back and forth as PgHdr1 pointers. |
+** |
+** The pcache.c and pager.c modules deal pointers to PgHdr objects. |
+** The btree.c module deals with pointers to MemPage objects. |
+** |
+** SOURCE OF PAGE CACHE MEMORY: |
+** |
+** Memory for a page might come from any of three sources: |
+** |
+** (1) The general-purpose memory allocator - sqlite3Malloc() |
+** (2) Global page-cache memory provided using sqlite3_config() with |
+** SQLITE_CONFIG_PAGECACHE. |
+** (3) PCache-local bulk allocation. |
+** |
+** The third case is a chunk of heap memory (defaulting to 100 pages worth) |
+** that is allocated when the page cache is created. The size of the local |
+** bulk allocation can be adjusted using |
+** |
+** sqlite3_config(SQLITE_CONFIG_PAGECACHE, (void*)0, 0, N). |
+** |
+** If N is positive, then N pages worth of memory are allocated using a single |
+** sqlite3Malloc() call and that memory is used for the first N pages allocated. |
+** Or if N is negative, then -1024*N bytes of memory are allocated and used |
+** for as many pages as can be accomodated. |
+** |
+** Only one of (2) or (3) can be used. Once the memory available to (2) or |
+** (3) is exhausted, subsequent allocations fail over to the general-purpose |
+** memory allocator (1). |
+** |
+** Earlier versions of SQLite used only methods (1) and (2). But experiments |
+** show that method (3) with N==100 provides about a 5% performance boost for |
+** common workloads. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+typedef struct PCache1 PCache1; |
+typedef struct PgHdr1 PgHdr1; |
+typedef struct PgFreeslot PgFreeslot; |
+typedef struct PGroup PGroup; |
+ |
+/* |
+** Each cache entry is represented by an instance of the following |
+** structure. Unless SQLITE_PCACHE_SEPARATE_HEADER is defined, a buffer of |
+** PgHdr1.pCache->szPage bytes is allocated directly before this structure |
+** in memory. |
+*/ |
+struct PgHdr1 { |
+ sqlite3_pcache_page page; /* Base class. Must be first. pBuf & pExtra */ |
+ unsigned int iKey; /* Key value (page number) */ |
+ u8 isPinned; /* Page in use, not on the LRU list */ |
+ u8 isBulkLocal; /* This page from bulk local storage */ |
+ u8 isAnchor; /* This is the PGroup.lru element */ |
+ PgHdr1 *pNext; /* Next in hash table chain */ |
+ PCache1 *pCache; /* Cache that currently owns this page */ |
+ PgHdr1 *pLruNext; /* Next in LRU list of unpinned pages */ |
+ PgHdr1 *pLruPrev; /* Previous in LRU list of unpinned pages */ |
+}; |
+ |
+/* Each page cache (or PCache) belongs to a PGroup. A PGroup is a set |
+** of one or more PCaches that are able to recycle each other's unpinned |
+** pages when they are under memory pressure. A PGroup is an instance of |
+** the following object. |
+** |
+** This page cache implementation works in one of two modes: |
+** |
+** (1) Every PCache is the sole member of its own PGroup. There is |
+** one PGroup per PCache. |
+** |
+** (2) There is a single global PGroup that all PCaches are a member |
+** of. |
+** |
+** Mode 1 uses more memory (since PCache instances are not able to rob |
+** unused pages from other PCaches) but it also operates without a mutex, |
+** and is therefore often faster. Mode 2 requires a mutex in order to be |
+** threadsafe, but recycles pages more efficiently. |
+** |
+** For mode (1), PGroup.mutex is NULL. For mode (2) there is only a single |
+** PGroup which is the pcache1.grp global variable and its mutex is |
+** SQLITE_MUTEX_STATIC_LRU. |
+*/ |
+struct PGroup { |
+ sqlite3_mutex *mutex; /* MUTEX_STATIC_LRU or NULL */ |
+ unsigned int nMaxPage; /* Sum of nMax for purgeable caches */ |
+ unsigned int nMinPage; /* Sum of nMin for purgeable caches */ |
+ unsigned int mxPinned; /* nMaxpage + 10 - nMinPage */ |
+ unsigned int nCurrentPage; /* Number of purgeable pages allocated */ |
+ PgHdr1 lru; /* The beginning and end of the LRU list */ |
+}; |
+ |
+/* Each page cache is an instance of the following object. Every |
+** open database file (including each in-memory database and each |
+** temporary or transient database) has a single page cache which |
+** is an instance of this object. |
+** |
+** Pointers to structures of this type are cast and returned as |
+** opaque sqlite3_pcache* handles. |
+*/ |
+struct PCache1 { |
+ /* Cache configuration parameters. Page size (szPage) and the purgeable |
+ ** flag (bPurgeable) are set when the cache is created. nMax may be |
+ ** modified at any time by a call to the pcache1Cachesize() method. |
+ ** The PGroup mutex must be held when accessing nMax. |
+ */ |
+ PGroup *pGroup; /* PGroup this cache belongs to */ |
+ int szPage; /* Size of database content section */ |
+ int szExtra; /* sizeof(MemPage)+sizeof(PgHdr) */ |
+ int szAlloc; /* Total size of one pcache line */ |
+ int bPurgeable; /* True if cache is purgeable */ |
+ unsigned int nMin; /* Minimum number of pages reserved */ |
+ unsigned int nMax; /* Configured "cache_size" value */ |
+ unsigned int n90pct; /* nMax*9/10 */ |
+ unsigned int iMaxKey; /* Largest key seen since xTruncate() */ |
+ |
+ /* Hash table of all pages. The following variables may only be accessed |
+ ** when the accessor is holding the PGroup mutex. |
+ */ |
+ unsigned int nRecyclable; /* Number of pages in the LRU list */ |
+ unsigned int nPage; /* Total number of pages in apHash */ |
+ unsigned int nHash; /* Number of slots in apHash[] */ |
+ PgHdr1 **apHash; /* Hash table for fast lookup by key */ |
+ PgHdr1 *pFree; /* List of unused pcache-local pages */ |
+ void *pBulk; /* Bulk memory used by pcache-local */ |
+}; |
+ |
+/* |
+** Free slots in the allocator used to divide up the global page cache |
+** buffer provided using the SQLITE_CONFIG_PAGECACHE mechanism. |
+*/ |
+struct PgFreeslot { |
+ PgFreeslot *pNext; /* Next free slot */ |
+}; |
+ |
+/* |
+** Global data used by this cache. |
+*/ |
+static SQLITE_WSD struct PCacheGlobal { |
+ PGroup grp; /* The global PGroup for mode (2) */ |
+ |
+ /* Variables related to SQLITE_CONFIG_PAGECACHE settings. The |
+ ** szSlot, nSlot, pStart, pEnd, nReserve, and isInit values are all |
+ ** fixed at sqlite3_initialize() time and do not require mutex protection. |
+ ** The nFreeSlot and pFree values do require mutex protection. |
+ */ |
+ int isInit; /* True if initialized */ |
+ int separateCache; /* Use a new PGroup for each PCache */ |
+ int nInitPage; /* Initial bulk allocation size */ |
+ int szSlot; /* Size of each free slot */ |
+ int nSlot; /* The number of pcache slots */ |
+ int nReserve; /* Try to keep nFreeSlot above this */ |
+ void *pStart, *pEnd; /* Bounds of global page cache memory */ |
+ /* Above requires no mutex. Use mutex below for variable that follow. */ |
+ sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
+ PgFreeslot *pFree; /* Free page blocks */ |
+ int nFreeSlot; /* Number of unused pcache slots */ |
+ /* The following value requires a mutex to change. We skip the mutex on |
+ ** reading because (1) most platforms read a 32-bit integer atomically and |
+ ** (2) even if an incorrect value is read, no great harm is done since this |
+ ** is really just an optimization. */ |
+ int bUnderPressure; /* True if low on PAGECACHE memory */ |
+} pcache1_g; |
+ |
+/* |
+** All code in this file should access the global structure above via the |
+** alias "pcache1". This ensures that the WSD emulation is used when |
+** compiling for systems that do not support real WSD. |
+*/ |
+#define pcache1 (GLOBAL(struct PCacheGlobal, pcache1_g)) |
+ |
+/* |
+** Macros to enter and leave the PCache LRU mutex. |
+*/ |
+#if !defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) || SQLITE_THREADSAFE==0 |
+# define pcache1EnterMutex(X) assert((X)->mutex==0) |
+# define pcache1LeaveMutex(X) assert((X)->mutex==0) |
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 0 |
+#else |
+# define pcache1EnterMutex(X) sqlite3_mutex_enter((X)->mutex) |
+# define pcache1LeaveMutex(X) sqlite3_mutex_leave((X)->mutex) |
+# define PCACHE1_MIGHT_USE_GROUP_MUTEX 1 |
+#endif |
+ |
+/******************************************************************************/ |
+/******** Page Allocation/SQLITE_CONFIG_PCACHE Related Functions **************/ |
+ |
+ |
+/* |
+** This function is called during initialization if a static buffer is |
+** supplied to use for the page-cache by passing the SQLITE_CONFIG_PAGECACHE |
+** verb to sqlite3_config(). Parameter pBuf points to an allocation large |
+** enough to contain 'n' buffers of 'sz' bytes each. |
+** |
+** This routine is called from sqlite3_initialize() and so it is guaranteed |
+** to be serialized already. There is no need for further mutexing. |
+*/ |
+SQLITE_PRIVATE void sqlite3PCacheBufferSetup(void *pBuf, int sz, int n){ |
+ if( pcache1.isInit ){ |
+ PgFreeslot *p; |
+ if( pBuf==0 ) sz = n = 0; |
+ sz = ROUNDDOWN8(sz); |
+ pcache1.szSlot = sz; |
+ pcache1.nSlot = pcache1.nFreeSlot = n; |
+ pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
+ pcache1.pStart = pBuf; |
+ pcache1.pFree = 0; |
+ pcache1.bUnderPressure = 0; |
+ while( n-- ){ |
+ p = (PgFreeslot*)pBuf; |
+ p->pNext = pcache1.pFree; |
+ pcache1.pFree = p; |
+ pBuf = (void*)&((char*)pBuf)[sz]; |
+ } |
+ pcache1.pEnd = pBuf; |
+ } |
+} |
+ |
+/* |
+** Try to initialize the pCache->pFree and pCache->pBulk fields. Return |
+** true if pCache->pFree ends up containing one or more free pages. |
+*/ |
+static int pcache1InitBulk(PCache1 *pCache){ |
+ i64 szBulk; |
+ char *zBulk; |
+ if( pcache1.nInitPage==0 ) return 0; |
+ /* Do not bother with a bulk allocation if the cache size very small */ |
+ if( pCache->nMax<3 ) return 0; |
+ sqlite3BeginBenignMalloc(); |
+ if( pcache1.nInitPage>0 ){ |
+ szBulk = pCache->szAlloc * (i64)pcache1.nInitPage; |
+ }else{ |
+ szBulk = -1024 * (i64)pcache1.nInitPage; |
+ } |
+ if( szBulk > pCache->szAlloc*(i64)pCache->nMax ){ |
+ szBulk = pCache->szAlloc*pCache->nMax; |
+ } |
+ zBulk = pCache->pBulk = sqlite3Malloc( szBulk ); |
+ sqlite3EndBenignMalloc(); |
+ if( zBulk ){ |
+ int nBulk = sqlite3MallocSize(zBulk)/pCache->szAlloc; |
+ int i; |
+ for(i=0; i<nBulk; i++){ |
+ PgHdr1 *pX = (PgHdr1*)&zBulk[pCache->szPage]; |
+ pX->page.pBuf = zBulk; |
+ pX->page.pExtra = &pX[1]; |
+ pX->isBulkLocal = 1; |
+ pX->isAnchor = 0; |
+ pX->pNext = pCache->pFree; |
+ pCache->pFree = pX; |
+ zBulk += pCache->szAlloc; |
+ } |
+ } |
+ return pCache->pFree!=0; |
+} |
+ |
+/* |
+** Malloc function used within this file to allocate space from the buffer |
+** configured using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no |
+** such buffer exists or there is no space left in it, this function falls |
+** back to sqlite3Malloc(). |
+** |
+** Multiple threads can run this routine at the same time. Global variables |
+** in pcache1 need to be protected via mutex. |
+*/ |
+static void *pcache1Alloc(int nByte){ |
+ void *p = 0; |
+ assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); |
+ if( nByte<=pcache1.szSlot ){ |
+ sqlite3_mutex_enter(pcache1.mutex); |
+ p = (PgHdr1 *)pcache1.pFree; |
+ if( p ){ |
+ pcache1.pFree = pcache1.pFree->pNext; |
+ pcache1.nFreeSlot--; |
+ pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
+ assert( pcache1.nFreeSlot>=0 ); |
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
+ sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
+ } |
+ sqlite3_mutex_leave(pcache1.mutex); |
+ } |
+ if( p==0 ){ |
+ /* Memory is not available in the SQLITE_CONFIG_PAGECACHE pool. Get |
+ ** it from sqlite3Malloc instead. |
+ */ |
+ p = sqlite3Malloc(nByte); |
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS |
+ if( p ){ |
+ int sz = sqlite3MallocSize(p); |
+ sqlite3_mutex_enter(pcache1.mutex); |
+ sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
+ sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_OVERFLOW, sz); |
+ sqlite3_mutex_leave(pcache1.mutex); |
+ } |
+#endif |
+ sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Free an allocated buffer obtained from pcache1Alloc(). |
+*/ |
+static void pcache1Free(void *p){ |
+ int nFreed = 0; |
+ if( p==0 ) return; |
+ if( SQLITE_WITHIN(p, pcache1.pStart, pcache1.pEnd) ){ |
+ PgFreeslot *pSlot; |
+ sqlite3_mutex_enter(pcache1.mutex); |
+ sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
+ pSlot = (PgFreeslot*)p; |
+ pSlot->pNext = pcache1.pFree; |
+ pcache1.pFree = pSlot; |
+ pcache1.nFreeSlot++; |
+ pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
+ assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
+ sqlite3_mutex_leave(pcache1.mutex); |
+ }else{ |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
+#ifndef SQLITE_DISABLE_PAGECACHE_OVERFLOW_STATS |
+ nFreed = sqlite3MallocSize(p); |
+ sqlite3_mutex_enter(pcache1.mutex); |
+ sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_OVERFLOW, nFreed); |
+ sqlite3_mutex_leave(pcache1.mutex); |
+#endif |
+ sqlite3_free(p); |
+ } |
+} |
+ |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+/* |
+** Return the size of a pcache allocation |
+*/ |
+static int pcache1MemSize(void *p){ |
+ if( p>=pcache1.pStart && p<pcache1.pEnd ){ |
+ return pcache1.szSlot; |
+ }else{ |
+ int iSize; |
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
+ iSize = sqlite3MallocSize(p); |
+ sqlite3MemdebugSetType(p, MEMTYPE_PCACHE); |
+ return iSize; |
+ } |
+} |
+#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ |
+ |
+/* |
+** Allocate a new page object initially associated with cache pCache. |
+*/ |
+static PgHdr1 *pcache1AllocPage(PCache1 *pCache, int benignMalloc){ |
+ PgHdr1 *p = 0; |
+ void *pPg; |
+ |
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
+ if( pCache->pFree || (pCache->nPage==0 && pcache1InitBulk(pCache)) ){ |
+ p = pCache->pFree; |
+ pCache->pFree = p->pNext; |
+ p->pNext = 0; |
+ }else{ |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+ /* The group mutex must be released before pcache1Alloc() is called. This |
+ ** is because it might call sqlite3_release_memory(), which assumes that |
+ ** this mutex is not held. */ |
+ assert( pcache1.separateCache==0 ); |
+ assert( pCache->pGroup==&pcache1.grp ); |
+ pcache1LeaveMutex(pCache->pGroup); |
+#endif |
+ if( benignMalloc ){ sqlite3BeginBenignMalloc(); } |
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER |
+ pPg = pcache1Alloc(pCache->szPage); |
+ p = sqlite3Malloc(sizeof(PgHdr1) + pCache->szExtra); |
+ if( !pPg || !p ){ |
+ pcache1Free(pPg); |
+ sqlite3_free(p); |
+ pPg = 0; |
+ } |
+#else |
+ pPg = pcache1Alloc(pCache->szAlloc); |
+ p = (PgHdr1 *)&((u8 *)pPg)[pCache->szPage]; |
+#endif |
+ if( benignMalloc ){ sqlite3EndBenignMalloc(); } |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+ pcache1EnterMutex(pCache->pGroup); |
+#endif |
+ if( pPg==0 ) return 0; |
+ p->page.pBuf = pPg; |
+ p->page.pExtra = &p[1]; |
+ p->isBulkLocal = 0; |
+ p->isAnchor = 0; |
+ } |
+ if( pCache->bPurgeable ){ |
+ pCache->pGroup->nCurrentPage++; |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Free a page object allocated by pcache1AllocPage(). |
+*/ |
+static void pcache1FreePage(PgHdr1 *p){ |
+ PCache1 *pCache; |
+ assert( p!=0 ); |
+ pCache = p->pCache; |
+ assert( sqlite3_mutex_held(p->pCache->pGroup->mutex) ); |
+ if( p->isBulkLocal ){ |
+ p->pNext = pCache->pFree; |
+ pCache->pFree = p; |
+ }else{ |
+ pcache1Free(p->page.pBuf); |
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER |
+ sqlite3_free(p); |
+#endif |
+ } |
+ if( pCache->bPurgeable ){ |
+ pCache->pGroup->nCurrentPage--; |
+ } |
+} |
+ |
+/* |
+** Malloc function used by SQLite to obtain space from the buffer configured |
+** using sqlite3_config(SQLITE_CONFIG_PAGECACHE) option. If no such buffer |
+** exists, this function falls back to sqlite3Malloc(). |
+*/ |
+SQLITE_PRIVATE void *sqlite3PageMalloc(int sz){ |
+ return pcache1Alloc(sz); |
+} |
+ |
+/* |
+** Free an allocated buffer obtained from sqlite3PageMalloc(). |
+*/ |
+SQLITE_PRIVATE void sqlite3PageFree(void *p){ |
+ pcache1Free(p); |
+} |
+ |
+ |
+/* |
+** Return true if it desirable to avoid allocating a new page cache |
+** entry. |
+** |
+** If memory was allocated specifically to the page cache using |
+** SQLITE_CONFIG_PAGECACHE but that memory has all been used, then |
+** it is desirable to avoid allocating a new page cache entry because |
+** presumably SQLITE_CONFIG_PAGECACHE was suppose to be sufficient |
+** for all page cache needs and we should not need to spill the |
+** allocation onto the heap. |
+** |
+** Or, the heap is used for all page cache memory but the heap is |
+** under memory pressure, then again it is desirable to avoid |
+** allocating a new page cache entry in order to avoid stressing |
+** the heap even further. |
+*/ |
+static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
+ if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
+ return pcache1.bUnderPressure; |
+ }else{ |
+ return sqlite3HeapNearlyFull(); |
+ } |
+} |
+ |
+/******************************************************************************/ |
+/******** General Implementation Functions ************************************/ |
+ |
+/* |
+** This function is used to resize the hash table used by the cache passed |
+** as the first argument. |
+** |
+** The PCache mutex must be held when this function is called. |
+*/ |
+static void pcache1ResizeHash(PCache1 *p){ |
+ PgHdr1 **apNew; |
+ unsigned int nNew; |
+ unsigned int i; |
+ |
+ assert( sqlite3_mutex_held(p->pGroup->mutex) ); |
+ |
+ nNew = p->nHash*2; |
+ if( nNew<256 ){ |
+ nNew = 256; |
+ } |
+ |
+ pcache1LeaveMutex(p->pGroup); |
+ if( p->nHash ){ sqlite3BeginBenignMalloc(); } |
+ apNew = (PgHdr1 **)sqlite3MallocZero(sizeof(PgHdr1 *)*nNew); |
+ if( p->nHash ){ sqlite3EndBenignMalloc(); } |
+ pcache1EnterMutex(p->pGroup); |
+ if( apNew ){ |
+ for(i=0; i<p->nHash; i++){ |
+ PgHdr1 *pPage; |
+ PgHdr1 *pNext = p->apHash[i]; |
+ while( (pPage = pNext)!=0 ){ |
+ unsigned int h = pPage->iKey % nNew; |
+ pNext = pPage->pNext; |
+ pPage->pNext = apNew[h]; |
+ apNew[h] = pPage; |
+ } |
+ } |
+ sqlite3_free(p->apHash); |
+ p->apHash = apNew; |
+ p->nHash = nNew; |
+ } |
+} |
+ |
+/* |
+** This function is used internally to remove the page pPage from the |
+** PGroup LRU list, if is part of it. If pPage is not part of the PGroup |
+** LRU list, then this function is a no-op. |
+** |
+** The PGroup mutex must be held when this function is called. |
+*/ |
+static PgHdr1 *pcache1PinPage(PgHdr1 *pPage){ |
+ PCache1 *pCache; |
+ |
+ assert( pPage!=0 ); |
+ assert( pPage->isPinned==0 ); |
+ pCache = pPage->pCache; |
+ assert( pPage->pLruNext ); |
+ assert( pPage->pLruPrev ); |
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
+ pPage->pLruPrev->pLruNext = pPage->pLruNext; |
+ pPage->pLruNext->pLruPrev = pPage->pLruPrev; |
+ pPage->pLruNext = 0; |
+ pPage->pLruPrev = 0; |
+ pPage->isPinned = 1; |
+ assert( pPage->isAnchor==0 ); |
+ assert( pCache->pGroup->lru.isAnchor==1 ); |
+ pCache->nRecyclable--; |
+ return pPage; |
+} |
+ |
+ |
+/* |
+** Remove the page supplied as an argument from the hash table |
+** (PCache1.apHash structure) that it is currently stored in. |
+** Also free the page if freePage is true. |
+** |
+** The PGroup mutex must be held when this function is called. |
+*/ |
+static void pcache1RemoveFromHash(PgHdr1 *pPage, int freeFlag){ |
+ unsigned int h; |
+ PCache1 *pCache = pPage->pCache; |
+ PgHdr1 **pp; |
+ |
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
+ h = pPage->iKey % pCache->nHash; |
+ for(pp=&pCache->apHash[h]; (*pp)!=pPage; pp=&(*pp)->pNext); |
+ *pp = (*pp)->pNext; |
+ |
+ pCache->nPage--; |
+ if( freeFlag ) pcache1FreePage(pPage); |
+} |
+ |
+/* |
+** If there are currently more than nMaxPage pages allocated, try |
+** to recycle pages to reduce the number allocated to nMaxPage. |
+*/ |
+static void pcache1EnforceMaxPage(PCache1 *pCache){ |
+ PGroup *pGroup = pCache->pGroup; |
+ PgHdr1 *p; |
+ assert( sqlite3_mutex_held(pGroup->mutex) ); |
+ while( pGroup->nCurrentPage>pGroup->nMaxPage |
+ && (p=pGroup->lru.pLruPrev)->isAnchor==0 |
+ ){ |
+ assert( p->pCache->pGroup==pGroup ); |
+ assert( p->isPinned==0 ); |
+ pcache1PinPage(p); |
+ pcache1RemoveFromHash(p, 1); |
+ } |
+ if( pCache->nPage==0 && pCache->pBulk ){ |
+ sqlite3_free(pCache->pBulk); |
+ pCache->pBulk = pCache->pFree = 0; |
+ } |
+} |
+ |
+/* |
+** Discard all pages from cache pCache with a page number (key value) |
+** greater than or equal to iLimit. Any pinned pages that meet this |
+** criteria are unpinned before they are discarded. |
+** |
+** The PCache mutex must be held when this function is called. |
+*/ |
+static void pcache1TruncateUnsafe( |
+ PCache1 *pCache, /* The cache to truncate */ |
+ unsigned int iLimit /* Drop pages with this pgno or larger */ |
+){ |
+ TESTONLY( unsigned int nPage = 0; ) /* To assert pCache->nPage is correct */ |
+ unsigned int h; |
+ assert( sqlite3_mutex_held(pCache->pGroup->mutex) ); |
+ for(h=0; h<pCache->nHash; h++){ |
+ PgHdr1 **pp = &pCache->apHash[h]; |
+ PgHdr1 *pPage; |
+ while( (pPage = *pp)!=0 ){ |
+ if( pPage->iKey>=iLimit ){ |
+ pCache->nPage--; |
+ *pp = pPage->pNext; |
+ if( !pPage->isPinned ) pcache1PinPage(pPage); |
+ pcache1FreePage(pPage); |
+ }else{ |
+ pp = &pPage->pNext; |
+ TESTONLY( nPage++; ) |
+ } |
+ } |
+ } |
+ assert( pCache->nPage==nPage ); |
+} |
+ |
+/******************************************************************************/ |
+/******** sqlite3_pcache Methods **********************************************/ |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xInit method. |
+*/ |
+static int pcache1Init(void *NotUsed){ |
+ UNUSED_PARAMETER(NotUsed); |
+ assert( pcache1.isInit==0 ); |
+ memset(&pcache1, 0, sizeof(pcache1)); |
+ |
+ |
+ /* |
+ ** The pcache1.separateCache variable is true if each PCache has its own |
+ ** private PGroup (mode-1). pcache1.separateCache is false if the single |
+ ** PGroup in pcache1.grp is used for all page caches (mode-2). |
+ ** |
+ ** * Always use separate caches (mode-1) if SQLITE_SEPARATE_CACHE_POOLS |
+ ** |
+ ** * Always use a unified cache (mode-2) if ENABLE_MEMORY_MANAGEMENT |
+ ** |
+ ** * Use a unified cache in single-threaded applications that have |
+ ** configured a start-time buffer for use as page-cache memory using |
+ ** sqlite3_config(SQLITE_CONFIG_PAGECACHE, pBuf, sz, N) with non-NULL |
+ ** pBuf argument. |
+ ** |
+ ** * Otherwise use separate caches (mode-1) |
+ */ |
+#ifdef SQLITE_SEPARATE_CACHE_POOLS |
+ const int separateCache = 1; |
+#elif defined(SQLITE_ENABLE_MEMORY_MANAGEMENT) |
+ pcache1.separateCache = 0; |
+#elif SQLITE_THREADSAFE |
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0 |
+ || sqlite3GlobalConfig.bCoreMutex>0; |
+#else |
+ pcache1.separateCache = sqlite3GlobalConfig.pPage==0; |
+#endif |
+ |
+#if SQLITE_THREADSAFE |
+ if( sqlite3GlobalConfig.bCoreMutex ){ |
+ pcache1.grp.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_LRU); |
+ pcache1.mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_STATIC_PMEM); |
+ } |
+#endif |
+ if( pcache1.separateCache |
+ && sqlite3GlobalConfig.nPage!=0 |
+ && sqlite3GlobalConfig.pPage==0 |
+ ){ |
+ pcache1.nInitPage = sqlite3GlobalConfig.nPage; |
+ }else{ |
+ pcache1.nInitPage = 0; |
+ } |
+ pcache1.grp.mxPinned = 10; |
+ pcache1.isInit = 1; |
+ return SQLITE_OK; |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xShutdown method. |
+** Note that the static mutex allocated in xInit does |
+** not need to be freed. |
+*/ |
+static void pcache1Shutdown(void *NotUsed){ |
+ UNUSED_PARAMETER(NotUsed); |
+ assert( pcache1.isInit!=0 ); |
+ memset(&pcache1, 0, sizeof(pcache1)); |
+} |
+ |
+/* forward declaration */ |
+static void pcache1Destroy(sqlite3_pcache *p); |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xCreate method. |
+** |
+** Allocate a new cache. |
+*/ |
+static sqlite3_pcache *pcache1Create(int szPage, int szExtra, int bPurgeable){ |
+ PCache1 *pCache; /* The newly created page cache */ |
+ PGroup *pGroup; /* The group the new page cache will belong to */ |
+ int sz; /* Bytes of memory required to allocate the new cache */ |
+ |
+ assert( (szPage & (szPage-1))==0 && szPage>=512 && szPage<=65536 ); |
+ assert( szExtra < 300 ); |
+ |
+ sz = sizeof(PCache1) + sizeof(PGroup)*pcache1.separateCache; |
+ pCache = (PCache1 *)sqlite3MallocZero(sz); |
+ if( pCache ){ |
+ if( pcache1.separateCache ){ |
+ pGroup = (PGroup*)&pCache[1]; |
+ pGroup->mxPinned = 10; |
+ }else{ |
+ pGroup = &pcache1.grp; |
+ } |
+ if( pGroup->lru.isAnchor==0 ){ |
+ pGroup->lru.isAnchor = 1; |
+ pGroup->lru.pLruPrev = pGroup->lru.pLruNext = &pGroup->lru; |
+ } |
+ pCache->pGroup = pGroup; |
+ pCache->szPage = szPage; |
+ pCache->szExtra = szExtra; |
+ pCache->szAlloc = szPage + szExtra + ROUND8(sizeof(PgHdr1)); |
+ pCache->bPurgeable = (bPurgeable ? 1 : 0); |
+ pcache1EnterMutex(pGroup); |
+ pcache1ResizeHash(pCache); |
+ if( bPurgeable ){ |
+ pCache->nMin = 10; |
+ pGroup->nMinPage += pCache->nMin; |
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
+ } |
+ pcache1LeaveMutex(pGroup); |
+ if( pCache->nHash==0 ){ |
+ pcache1Destroy((sqlite3_pcache*)pCache); |
+ pCache = 0; |
+ } |
+ } |
+ return (sqlite3_pcache *)pCache; |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xCachesize method. |
+** |
+** Configure the cache_size limit for a cache. |
+*/ |
+static void pcache1Cachesize(sqlite3_pcache *p, int nMax){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ if( pCache->bPurgeable ){ |
+ PGroup *pGroup = pCache->pGroup; |
+ pcache1EnterMutex(pGroup); |
+ pGroup->nMaxPage += (nMax - pCache->nMax); |
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
+ pCache->nMax = nMax; |
+ pCache->n90pct = pCache->nMax*9/10; |
+ pcache1EnforceMaxPage(pCache); |
+ pcache1LeaveMutex(pGroup); |
+ } |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xShrink method. |
+** |
+** Free up as much memory as possible. |
+*/ |
+static void pcache1Shrink(sqlite3_pcache *p){ |
+ PCache1 *pCache = (PCache1*)p; |
+ if( pCache->bPurgeable ){ |
+ PGroup *pGroup = pCache->pGroup; |
+ int savedMaxPage; |
+ pcache1EnterMutex(pGroup); |
+ savedMaxPage = pGroup->nMaxPage; |
+ pGroup->nMaxPage = 0; |
+ pcache1EnforceMaxPage(pCache); |
+ pGroup->nMaxPage = savedMaxPage; |
+ pcache1LeaveMutex(pGroup); |
+ } |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xPagecount method. |
+*/ |
+static int pcache1Pagecount(sqlite3_pcache *p){ |
+ int n; |
+ PCache1 *pCache = (PCache1*)p; |
+ pcache1EnterMutex(pCache->pGroup); |
+ n = pCache->nPage; |
+ pcache1LeaveMutex(pCache->pGroup); |
+ return n; |
+} |
+ |
+ |
+/* |
+** Implement steps 3, 4, and 5 of the pcache1Fetch() algorithm described |
+** in the header of the pcache1Fetch() procedure. |
+** |
+** This steps are broken out into a separate procedure because they are |
+** usually not needed, and by avoiding the stack initialization required |
+** for these steps, the main pcache1Fetch() procedure can run faster. |
+*/ |
+static SQLITE_NOINLINE PgHdr1 *pcache1FetchStage2( |
+ PCache1 *pCache, |
+ unsigned int iKey, |
+ int createFlag |
+){ |
+ unsigned int nPinned; |
+ PGroup *pGroup = pCache->pGroup; |
+ PgHdr1 *pPage = 0; |
+ |
+ /* Step 3: Abort if createFlag is 1 but the cache is nearly full */ |
+ assert( pCache->nPage >= pCache->nRecyclable ); |
+ nPinned = pCache->nPage - pCache->nRecyclable; |
+ assert( pGroup->mxPinned == pGroup->nMaxPage + 10 - pGroup->nMinPage ); |
+ assert( pCache->n90pct == pCache->nMax*9/10 ); |
+ if( createFlag==1 && ( |
+ nPinned>=pGroup->mxPinned |
+ || nPinned>=pCache->n90pct |
+ || (pcache1UnderMemoryPressure(pCache) && pCache->nRecyclable<nPinned) |
+ )){ |
+ return 0; |
+ } |
+ |
+ if( pCache->nPage>=pCache->nHash ) pcache1ResizeHash(pCache); |
+ assert( pCache->nHash>0 && pCache->apHash ); |
+ |
+ /* Step 4. Try to recycle a page. */ |
+ if( pCache->bPurgeable |
+ && !pGroup->lru.pLruPrev->isAnchor |
+ && ((pCache->nPage+1>=pCache->nMax) || pcache1UnderMemoryPressure(pCache)) |
+ ){ |
+ PCache1 *pOther; |
+ pPage = pGroup->lru.pLruPrev; |
+ assert( pPage->isPinned==0 ); |
+ pcache1RemoveFromHash(pPage, 0); |
+ pcache1PinPage(pPage); |
+ pOther = pPage->pCache; |
+ if( pOther->szAlloc != pCache->szAlloc ){ |
+ pcache1FreePage(pPage); |
+ pPage = 0; |
+ }else{ |
+ pGroup->nCurrentPage -= (pOther->bPurgeable - pCache->bPurgeable); |
+ } |
+ } |
+ |
+ /* Step 5. If a usable page buffer has still not been found, |
+ ** attempt to allocate a new one. |
+ */ |
+ if( !pPage ){ |
+ pPage = pcache1AllocPage(pCache, createFlag==1); |
+ } |
+ |
+ if( pPage ){ |
+ unsigned int h = iKey % pCache->nHash; |
+ pCache->nPage++; |
+ pPage->iKey = iKey; |
+ pPage->pNext = pCache->apHash[h]; |
+ pPage->pCache = pCache; |
+ pPage->pLruPrev = 0; |
+ pPage->pLruNext = 0; |
+ pPage->isPinned = 1; |
+ *(void **)pPage->page.pExtra = 0; |
+ pCache->apHash[h] = pPage; |
+ if( iKey>pCache->iMaxKey ){ |
+ pCache->iMaxKey = iKey; |
+ } |
+ } |
+ return pPage; |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xFetch method. |
+** |
+** Fetch a page by key value. |
+** |
+** Whether or not a new page may be allocated by this function depends on |
+** the value of the createFlag argument. 0 means do not allocate a new |
+** page. 1 means allocate a new page if space is easily available. 2 |
+** means to try really hard to allocate a new page. |
+** |
+** For a non-purgeable cache (a cache used as the storage for an in-memory |
+** database) there is really no difference between createFlag 1 and 2. So |
+** the calling function (pcache.c) will never have a createFlag of 1 on |
+** a non-purgeable cache. |
+** |
+** There are three different approaches to obtaining space for a page, |
+** depending on the value of parameter createFlag (which may be 0, 1 or 2). |
+** |
+** 1. Regardless of the value of createFlag, the cache is searched for a |
+** copy of the requested page. If one is found, it is returned. |
+** |
+** 2. If createFlag==0 and the page is not already in the cache, NULL is |
+** returned. |
+** |
+** 3. If createFlag is 1, and the page is not already in the cache, then |
+** return NULL (do not allocate a new page) if any of the following |
+** conditions are true: |
+** |
+** (a) the number of pages pinned by the cache is greater than |
+** PCache1.nMax, or |
+** |
+** (b) the number of pages pinned by the cache is greater than |
+** the sum of nMax for all purgeable caches, less the sum of |
+** nMin for all other purgeable caches, or |
+** |
+** 4. If none of the first three conditions apply and the cache is marked |
+** as purgeable, and if one of the following is true: |
+** |
+** (a) The number of pages allocated for the cache is already |
+** PCache1.nMax, or |
+** |
+** (b) The number of pages allocated for all purgeable caches is |
+** already equal to or greater than the sum of nMax for all |
+** purgeable caches, |
+** |
+** (c) The system is under memory pressure and wants to avoid |
+** unnecessary pages cache entry allocations |
+** |
+** then attempt to recycle a page from the LRU list. If it is the right |
+** size, return the recycled buffer. Otherwise, free the buffer and |
+** proceed to step 5. |
+** |
+** 5. Otherwise, allocate and return a new page buffer. |
+** |
+** There are two versions of this routine. pcache1FetchWithMutex() is |
+** the general case. pcache1FetchNoMutex() is a faster implementation for |
+** the common case where pGroup->mutex is NULL. The pcache1Fetch() wrapper |
+** invokes the appropriate routine. |
+*/ |
+static PgHdr1 *pcache1FetchNoMutex( |
+ sqlite3_pcache *p, |
+ unsigned int iKey, |
+ int createFlag |
+){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ PgHdr1 *pPage = 0; |
+ |
+ /* Step 1: Search the hash table for an existing entry. */ |
+ pPage = pCache->apHash[iKey % pCache->nHash]; |
+ while( pPage && pPage->iKey!=iKey ){ pPage = pPage->pNext; } |
+ |
+ /* Step 2: If the page was found in the hash table, then return it. |
+ ** If the page was not in the hash table and createFlag is 0, abort. |
+ ** Otherwise (page not in hash and createFlag!=0) continue with |
+ ** subsequent steps to try to create the page. */ |
+ if( pPage ){ |
+ if( !pPage->isPinned ){ |
+ return pcache1PinPage(pPage); |
+ }else{ |
+ return pPage; |
+ } |
+ }else if( createFlag ){ |
+ /* Steps 3, 4, and 5 implemented by this subroutine */ |
+ return pcache1FetchStage2(pCache, iKey, createFlag); |
+ }else{ |
+ return 0; |
+ } |
+} |
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX |
+static PgHdr1 *pcache1FetchWithMutex( |
+ sqlite3_pcache *p, |
+ unsigned int iKey, |
+ int createFlag |
+){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ PgHdr1 *pPage; |
+ |
+ pcache1EnterMutex(pCache->pGroup); |
+ pPage = pcache1FetchNoMutex(p, iKey, createFlag); |
+ assert( pPage==0 || pCache->iMaxKey>=iKey ); |
+ pcache1LeaveMutex(pCache->pGroup); |
+ return pPage; |
+} |
+#endif |
+static sqlite3_pcache_page *pcache1Fetch( |
+ sqlite3_pcache *p, |
+ unsigned int iKey, |
+ int createFlag |
+){ |
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX || defined(SQLITE_DEBUG) |
+ PCache1 *pCache = (PCache1 *)p; |
+#endif |
+ |
+ assert( offsetof(PgHdr1,page)==0 ); |
+ assert( pCache->bPurgeable || createFlag!=1 ); |
+ assert( pCache->bPurgeable || pCache->nMin==0 ); |
+ assert( pCache->bPurgeable==0 || pCache->nMin==10 ); |
+ assert( pCache->nMin==0 || pCache->bPurgeable ); |
+ assert( pCache->nHash>0 ); |
+#if PCACHE1_MIGHT_USE_GROUP_MUTEX |
+ if( pCache->pGroup->mutex ){ |
+ return (sqlite3_pcache_page*)pcache1FetchWithMutex(p, iKey, createFlag); |
+ }else |
+#endif |
+ { |
+ return (sqlite3_pcache_page*)pcache1FetchNoMutex(p, iKey, createFlag); |
+ } |
+} |
+ |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xUnpin method. |
+** |
+** Mark a page as unpinned (eligible for asynchronous recycling). |
+*/ |
+static void pcache1Unpin( |
+ sqlite3_pcache *p, |
+ sqlite3_pcache_page *pPg, |
+ int reuseUnlikely |
+){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ PgHdr1 *pPage = (PgHdr1 *)pPg; |
+ PGroup *pGroup = pCache->pGroup; |
+ |
+ assert( pPage->pCache==pCache ); |
+ pcache1EnterMutex(pGroup); |
+ |
+ /* It is an error to call this function if the page is already |
+ ** part of the PGroup LRU list. |
+ */ |
+ assert( pPage->pLruPrev==0 && pPage->pLruNext==0 ); |
+ assert( pPage->isPinned==1 ); |
+ |
+ if( reuseUnlikely || pGroup->nCurrentPage>pGroup->nMaxPage ){ |
+ pcache1RemoveFromHash(pPage, 1); |
+ }else{ |
+ /* Add the page to the PGroup LRU list. */ |
+ PgHdr1 **ppFirst = &pGroup->lru.pLruNext; |
+ pPage->pLruPrev = &pGroup->lru; |
+ (pPage->pLruNext = *ppFirst)->pLruPrev = pPage; |
+ *ppFirst = pPage; |
+ pCache->nRecyclable++; |
+ pPage->isPinned = 0; |
+ } |
+ |
+ pcache1LeaveMutex(pCache->pGroup); |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xRekey method. |
+*/ |
+static void pcache1Rekey( |
+ sqlite3_pcache *p, |
+ sqlite3_pcache_page *pPg, |
+ unsigned int iOld, |
+ unsigned int iNew |
+){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ PgHdr1 *pPage = (PgHdr1 *)pPg; |
+ PgHdr1 **pp; |
+ unsigned int h; |
+ assert( pPage->iKey==iOld ); |
+ assert( pPage->pCache==pCache ); |
+ |
+ pcache1EnterMutex(pCache->pGroup); |
+ |
+ h = iOld%pCache->nHash; |
+ pp = &pCache->apHash[h]; |
+ while( (*pp)!=pPage ){ |
+ pp = &(*pp)->pNext; |
+ } |
+ *pp = pPage->pNext; |
+ |
+ h = iNew%pCache->nHash; |
+ pPage->iKey = iNew; |
+ pPage->pNext = pCache->apHash[h]; |
+ pCache->apHash[h] = pPage; |
+ if( iNew>pCache->iMaxKey ){ |
+ pCache->iMaxKey = iNew; |
+ } |
+ |
+ pcache1LeaveMutex(pCache->pGroup); |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xTruncate method. |
+** |
+** Discard all unpinned pages in the cache with a page number equal to |
+** or greater than parameter iLimit. Any pinned pages with a page number |
+** equal to or greater than iLimit are implicitly unpinned. |
+*/ |
+static void pcache1Truncate(sqlite3_pcache *p, unsigned int iLimit){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ pcache1EnterMutex(pCache->pGroup); |
+ if( iLimit<=pCache->iMaxKey ){ |
+ pcache1TruncateUnsafe(pCache, iLimit); |
+ pCache->iMaxKey = iLimit-1; |
+ } |
+ pcache1LeaveMutex(pCache->pGroup); |
+} |
+ |
+/* |
+** Implementation of the sqlite3_pcache.xDestroy method. |
+** |
+** Destroy a cache allocated using pcache1Create(). |
+*/ |
+static void pcache1Destroy(sqlite3_pcache *p){ |
+ PCache1 *pCache = (PCache1 *)p; |
+ PGroup *pGroup = pCache->pGroup; |
+ assert( pCache->bPurgeable || (pCache->nMax==0 && pCache->nMin==0) ); |
+ pcache1EnterMutex(pGroup); |
+ pcache1TruncateUnsafe(pCache, 0); |
+ assert( pGroup->nMaxPage >= pCache->nMax ); |
+ pGroup->nMaxPage -= pCache->nMax; |
+ assert( pGroup->nMinPage >= pCache->nMin ); |
+ pGroup->nMinPage -= pCache->nMin; |
+ pGroup->mxPinned = pGroup->nMaxPage + 10 - pGroup->nMinPage; |
+ pcache1EnforceMaxPage(pCache); |
+ pcache1LeaveMutex(pGroup); |
+ sqlite3_free(pCache->pBulk); |
+ sqlite3_free(pCache->apHash); |
+ sqlite3_free(pCache); |
+} |
+ |
+/* |
+** This function is called during initialization (sqlite3_initialize()) to |
+** install the default pluggable cache module, assuming the user has not |
+** already provided an alternative. |
+*/ |
+SQLITE_PRIVATE void sqlite3PCacheSetDefault(void){ |
+ static const sqlite3_pcache_methods2 defaultMethods = { |
+ 1, /* iVersion */ |
+ 0, /* pArg */ |
+ pcache1Init, /* xInit */ |
+ pcache1Shutdown, /* xShutdown */ |
+ pcache1Create, /* xCreate */ |
+ pcache1Cachesize, /* xCachesize */ |
+ pcache1Pagecount, /* xPagecount */ |
+ pcache1Fetch, /* xFetch */ |
+ pcache1Unpin, /* xUnpin */ |
+ pcache1Rekey, /* xRekey */ |
+ pcache1Truncate, /* xTruncate */ |
+ pcache1Destroy, /* xDestroy */ |
+ pcache1Shrink /* xShrink */ |
+ }; |
+ sqlite3_config(SQLITE_CONFIG_PCACHE2, &defaultMethods); |
+} |
+ |
+/* |
+** Return the size of the header on each page of this PCACHE implementation. |
+*/ |
+SQLITE_PRIVATE int sqlite3HeaderSizePcache1(void){ return ROUND8(sizeof(PgHdr1)); } |
+ |
+/* |
+** Return the global mutex used by this PCACHE implementation. The |
+** sqlite3_status() routine needs access to this mutex. |
+*/ |
+SQLITE_PRIVATE sqlite3_mutex *sqlite3Pcache1Mutex(void){ |
+ return pcache1.mutex; |
+} |
+ |
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT |
+/* |
+** This function is called to free superfluous dynamically allocated memory |
+** held by the pager system. Memory in use by any SQLite pager allocated |
+** by the current thread may be sqlite3_free()ed. |
+** |
+** nReq is the number of bytes of memory required. Once this much has |
+** been released, the function returns. The return value is the total number |
+** of bytes of memory released. |
+*/ |
+SQLITE_PRIVATE int sqlite3PcacheReleaseMemory(int nReq){ |
+ int nFree = 0; |
+ assert( sqlite3_mutex_notheld(pcache1.grp.mutex) ); |
+ assert( sqlite3_mutex_notheld(pcache1.mutex) ); |
+ if( sqlite3GlobalConfig.nPage==0 ){ |
+ PgHdr1 *p; |
+ pcache1EnterMutex(&pcache1.grp); |
+ while( (nReq<0 || nFree<nReq) |
+ && (p=pcache1.grp.lru.pLruPrev)!=0 |
+ && p->isAnchor==0 |
+ ){ |
+ nFree += pcache1MemSize(p->page.pBuf); |
+#ifdef SQLITE_PCACHE_SEPARATE_HEADER |
+ nFree += sqlite3MemSize(p); |
+#endif |
+ assert( p->isPinned==0 ); |
+ pcache1PinPage(p); |
+ pcache1RemoveFromHash(p, 1); |
+ } |
+ pcache1LeaveMutex(&pcache1.grp); |
+ } |
+ return nFree; |
+} |
+#endif /* SQLITE_ENABLE_MEMORY_MANAGEMENT */ |
+ |
+#ifdef SQLITE_TEST |
+/* |
+** This function is used by test procedures to inspect the internal state |
+** of the global cache. |
+*/ |
+SQLITE_PRIVATE void sqlite3PcacheStats( |
+ int *pnCurrent, /* OUT: Total number of pages cached */ |
+ int *pnMax, /* OUT: Global maximum cache size */ |
+ int *pnMin, /* OUT: Sum of PCache1.nMin for purgeable caches */ |
+ int *pnRecyclable /* OUT: Total number of pages available for recycling */ |
+){ |
+ PgHdr1 *p; |
+ int nRecyclable = 0; |
+ for(p=pcache1.grp.lru.pLruNext; p && !p->isAnchor; p=p->pLruNext){ |
+ assert( p->isPinned==0 ); |
+ nRecyclable++; |
+ } |
+ *pnCurrent = pcache1.grp.nCurrentPage; |
+ *pnMax = (int)pcache1.grp.nMaxPage; |
+ *pnMin = (int)pcache1.grp.nMinPage; |
+ *pnRecyclable = nRecyclable; |
+} |
+#endif |
+ |
+/************** End of pcache1.c *********************************************/ |
+/************** Begin file rowset.c ******************************************/ |
+/* |
+** 2008 December 3 |
+** |
+** 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 module implements an object we call a "RowSet". |
+** |
+** The RowSet object is a collection of rowids. Rowids |
+** are inserted into the RowSet in an arbitrary order. Inserts |
+** can be intermixed with tests to see if a given rowid has been |
+** previously inserted into the RowSet. |
+** |
+** After all inserts are finished, it is possible to extract the |
+** elements of the RowSet in sorted order. Once this extraction |
+** process has started, no new elements may be inserted. |
+** |
+** Hence, the primitive operations for a RowSet are: |
+** |
+** CREATE |
+** INSERT |
+** TEST |
+** SMALLEST |
+** DESTROY |
+** |
+** The CREATE and DESTROY primitives are the constructor and destructor, |
+** obviously. The INSERT primitive adds a new element to the RowSet. |
+** TEST checks to see if an element is already in the RowSet. SMALLEST |
+** extracts the least value from the RowSet. |
+** |
+** The INSERT primitive might allocate additional memory. Memory is |
+** allocated in chunks so most INSERTs do no allocation. There is an |
+** upper bound on the size of allocated memory. No memory is freed |
+** until DESTROY. |
+** |
+** The TEST primitive includes a "batch" number. The TEST primitive |
+** will only see elements that were inserted before the last change |
+** in the batch number. In other words, if an INSERT occurs between |
+** two TESTs where the TESTs have the same batch nubmer, then the |
+** value added by the INSERT will not be visible to the second TEST. |
+** The initial batch number is zero, so if the very first TEST contains |
+** a non-zero batch number, it will see all prior INSERTs. |
+** |
+** No INSERTs may occurs after a SMALLEST. An assertion will fail if |
+** that is attempted. |
+** |
+** The cost of an INSERT is roughly constant. (Sometimes new memory |
+** has to be allocated on an INSERT.) The cost of a TEST with a new |
+** batch number is O(NlogN) where N is the number of elements in the RowSet. |
+** The cost of a TEST using the same batch number is O(logN). The cost |
+** of the first SMALLEST is O(NlogN). Second and subsequent SMALLEST |
+** primitives are constant time. The cost of DESTROY is O(N). |
+** |
+** There is an added cost of O(N) when switching between TEST and |
+** SMALLEST primitives. |
+*/ |
+/* #include "sqliteInt.h" */ |
+ |
+ |
+/* |
+** Target size for allocation chunks. |
+*/ |
+#define ROWSET_ALLOCATION_SIZE 1024 |
+ |
+/* |
+** The number of rowset entries per allocation chunk. |
+*/ |
+#define ROWSET_ENTRY_PER_CHUNK \ |
+ ((ROWSET_ALLOCATION_SIZE-8)/sizeof(struct RowSetEntry)) |
+ |
+/* |
+** Each entry in a RowSet is an instance of the following object. |
+** |
+** This same object is reused to store a linked list of trees of RowSetEntry |
+** objects. In that alternative use, pRight points to the next entry |
+** in the list, pLeft points to the tree, and v is unused. The |
+** RowSet.pForest value points to the head of this forest list. |
+*/ |
+struct RowSetEntry { |
+ i64 v; /* ROWID value for this entry */ |
+ struct RowSetEntry *pRight; /* Right subtree (larger entries) or list */ |
+ struct RowSetEntry *pLeft; /* Left subtree (smaller entries) */ |
+}; |
+ |
+/* |
+** RowSetEntry objects are allocated in large chunks (instances of the |
+** following structure) to reduce memory allocation overhead. The |
+** chunks are kept on a linked list so that they can be deallocated |
+** when the RowSet is destroyed. |
+*/ |
+struct RowSetChunk { |
+ struct RowSetChunk *pNextChunk; /* Next chunk on list of them all */ |
+ struct RowSetEntry aEntry[ROWSET_ENTRY_PER_CHUNK]; /* Allocated entries */ |
+}; |
+ |
+/* |
+** A RowSet in an instance of the following structure. |
+** |
+** A typedef of this structure if found in sqliteInt.h. |
+*/ |
+struct RowSet { |
+ struct RowSetChunk *pChunk; /* List of all chunk allocations */ |
+ sqlite3 *db; /* The database connection */ |
+ struct RowSetEntry *pEntry; /* List of entries using pRight */ |
+ struct RowSetEntry *pLast; /* Last entry on the pEntry list */ |
+ struct RowSetEntry *pFresh; /* Source of new entry objects */ |
+ struct RowSetEntry *pForest; /* List of binary trees of entries */ |
+ u16 nFresh; /* Number of objects on pFresh */ |
+ u16 rsFlags; /* Various flags */ |
+ int iBatch; /* Current insert batch */ |
+}; |
+ |
+/* |
+** Allowed values for RowSet.rsFlags |
+*/ |
+#define ROWSET_SORTED 0x01 /* True if RowSet.pEntry is sorted */ |
+#define ROWSET_NEXT 0x02 /* True if sqlite3RowSetNext() has been called */ |
+ |
+/* |
+** Turn bulk memory into a RowSet object. N bytes of memory |
+** are available at pSpace. The db pointer is used as a memory context |
+** for any subsequent allocations that need to occur. |
+** Return a pointer to the new RowSet object. |
+** |
+** It must be the case that N is sufficient to make a Rowset. If not |
+** an assertion fault occurs. |
+** |
+** If N is larger than the minimum, use the surplus as an initial |
+** allocation of entries available to be filled. |
+*/ |
+SQLITE_PRIVATE RowSet *sqlite3RowSetInit(sqlite3 *db, void *pSpace, unsigned int N){ |
+ RowSet *p; |
+ assert( N >= ROUND8(sizeof(*p)) ); |
+ p = pSpace; |
+ p->pChunk = 0; |
+ p->db = db; |
+ p->pEntry = 0; |
+ p->pLast = 0; |
+ p->pForest = 0; |
+ p->pFresh = (struct RowSetEntry*)(ROUND8(sizeof(*p)) + (char*)p); |
+ p->nFresh = (u16)((N - ROUND8(sizeof(*p)))/sizeof(struct RowSetEntry)); |
+ p->rsFlags = ROWSET_SORTED; |
+ p->iBatch = 0; |
+ return p; |
+} |
+ |
+/* |
+** Deallocate all chunks from a RowSet. This frees all memory that |
+** the RowSet has allocated over its lifetime. This routine is |
+** the destructor for the RowSet. |
+*/ |
+SQLITE_PRIVATE void sqlite3RowSetClear(RowSet *p){ |
+ struct RowSetChunk *pChunk, *pNextChunk; |
+ for(pChunk=p->pChunk; pChunk; pChunk = pNextChunk){ |
+ pNextChunk = pChunk->pNextChunk; |
+ sqlite3DbFree(p->db, pChunk); |
+ } |
+ p->pChunk = 0; |
+ p->nFresh = 0; |
+ p->pEntry = 0; |
+ p->pLast = 0; |
+ p->pForest = 0; |
+ p->rsFlags = ROWSET_SORTED; |
+} |
+ |
+/* |
+** Allocate a new RowSetEntry object that is associated with the |
+** given RowSet. Return a pointer to the new and completely uninitialized |
+** objected. |
+** |
+** In an OOM situation, the RowSet.db->mallocFailed flag is set and this |
+** routine returns NULL. |
+*/ |
+static struct RowSetEntry *rowSetEntryAlloc(RowSet *p){ |
+ assert( p!=0 ); |
+ if( p->nFresh==0 ){ |
+ struct RowSetChunk *pNew; |
+ pNew = sqlite3DbMallocRaw(p->db, sizeof(*pNew)); |
+ if( pNew==0 ){ |
+ return 0; |
+ } |
+ pNew->pNextChunk = p->pChunk; |
+ p->pChunk = pNew; |
+ p->pFresh = pNew->aEntry; |
+ p->nFresh = ROWSET_ENTRY_PER_CHUNK; |
+ } |
+ p->nFresh--; |
+ return p->pFresh++; |
+} |
+ |
+/* |
+** Insert a new value into a RowSet. |
+** |
+** The mallocFailed flag of the database connection is set if a |
+** memory allocation fails. |
+*/ |
+SQLITE_PRIVATE void sqlite3RowSetInsert(RowSet *p, i64 rowid){ |
+ struct RowSetEntry *pEntry; /* The new entry */ |
+ struct RowSetEntry *pLast; /* The last prior entry */ |
+ |
+ /* This routine is never called after sqlite3RowSetNext() */ |
+ assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); |
+ |
+ pEntry = rowSetEntryAlloc(p); |
+ if( pEntry==0 ) return; |
+ pEntry->v = rowid; |
+ pEntry->pRight = 0; |
+ pLast = p->pLast; |
+ if( pLast ){ |
+ if( (p->rsFlags & ROWSET_SORTED)!=0 && rowid<=pLast->v ){ |
+ p->rsFlags &= ~ROWSET_SORTED; |
+ } |
+ pLast->pRight = pEntry; |
+ }else{ |
+ p->pEntry = pEntry; |
+ } |
+ p->pLast = pEntry; |
+} |
+ |
+/* |
+** Merge two lists of RowSetEntry objects. Remove duplicates. |
+** |
+** The input lists are connected via pRight pointers and are |
+** assumed to each already be in sorted order. |
+*/ |
+static struct RowSetEntry *rowSetEntryMerge( |
+ struct RowSetEntry *pA, /* First sorted list to be merged */ |
+ struct RowSetEntry *pB /* Second sorted list to be merged */ |
+){ |
+ struct RowSetEntry head; |
+ struct RowSetEntry *pTail; |
+ |
+ pTail = &head; |
+ while( pA && pB ){ |
+ assert( pA->pRight==0 || pA->v<=pA->pRight->v ); |
+ assert( pB->pRight==0 || pB->v<=pB->pRight->v ); |
+ if( pA->v<pB->v ){ |
+ pTail->pRight = pA; |
+ pA = pA->pRight; |
+ pTail = pTail->pRight; |
+ }else if( pB->v<pA->v ){ |
+ pTail->pRight = pB; |
+ pB = pB->pRight; |
+ pTail = pTail->pRight; |
+ }else{ |
+ pA = pA->pRight; |
+ } |
+ } |
+ if( pA ){ |
+ assert( pA->pRight==0 || pA->v<=pA->pRight->v ); |
+ pTail->pRight = pA; |
+ }else{ |
+ assert( pB==0 || pB->pRight==0 || pB->v<=pB->pRight->v ); |
+ pTail->pRight = pB; |
+ } |
+ return head.pRight; |
+} |
+ |
+/* |
+** Sort all elements on the list of RowSetEntry objects into order of |
+** increasing v. |
+*/ |
+static struct RowSetEntry *rowSetEntrySort(struct RowSetEntry *pIn){ |
+ unsigned int i; |
+ struct RowSetEntry *pNext, *aBucket[40]; |
+ |
+ memset(aBucket, 0, sizeof(aBucket)); |
+ while( pIn ){ |
+ pNext = pIn->pRight; |
+ pIn->pRight = 0; |
+ for(i=0; aBucket[i]; i++){ |
+ pIn = rowSetEntryMerge(aBucket[i], pIn); |
+ aBucket[i] = 0; |
+ } |
+ aBucket[i] = pIn; |
+ pIn = pNext; |
+ } |
+ pIn = 0; |
+ for(i=0; i<sizeof(aBucket)/sizeof(aBucket[0]); i++){ |
+ pIn = rowSetEntryMerge(pIn, aBucket[i]); |
+ } |
+ return pIn; |
+} |
+ |
+ |
+/* |
+** The input, pIn, is a binary tree (or subtree) of RowSetEntry objects. |
+** Convert this tree into a linked list connected by the pRight pointers |
+** and return pointers to the first and last elements of the new list. |
+*/ |
+static void rowSetTreeToList( |
+ struct RowSetEntry *pIn, /* Root of the input tree */ |
+ struct RowSetEntry **ppFirst, /* Write head of the output list here */ |
+ struct RowSetEntry **ppLast /* Write tail of the output list here */ |
+){ |
+ assert( pIn!=0 ); |
+ if( pIn->pLeft ){ |
+ struct RowSetEntry *p; |
+ rowSetTreeToList(pIn->pLeft, ppFirst, &p); |
+ p->pRight = pIn; |
+ }else{ |
+ *ppFirst = pIn; |
+ } |
+ if( pIn->pRight ){ |
+ rowSetTreeToList(pIn->pRight, &pIn->pRight, ppLast); |
+ }else{ |
+ *ppLast = pIn; |
+ } |
+ assert( (*ppLast)->pRight==0 ); |
+} |
+ |
+ |
+/* |
+** Convert a sorted list of elements (connected by pRight) into a binary |
+** tree with depth of iDepth. A depth of 1 means the tree contains a single |
+** node taken from the head of *ppList. A depth of 2 means a tree with |
+** three nodes. And so forth. |
+** |
+** Use as many entries from the input list as required and update the |
+** *ppList to point to the unused elements of the list. If the input |
+** list contains too few elements, then construct an incomplete tree |
+** and leave *ppList set to NULL. |
+** |
+** Return a pointer to the root of the constructed binary tree. |
+*/ |
+static struct RowSetEntry *rowSetNDeepTree( |
+ struct RowSetEntry **ppList, |
+ int iDepth |
+){ |
+ struct RowSetEntry *p; /* Root of the new tree */ |
+ struct RowSetEntry *pLeft; /* Left subtree */ |
+ if( *ppList==0 ){ |
+ return 0; |
+ } |
+ if( iDepth==1 ){ |
+ p = *ppList; |
+ *ppList = p->pRight; |
+ p->pLeft = p->pRight = 0; |
+ return p; |
+ } |
+ pLeft = rowSetNDeepTree(ppList, iDepth-1); |
+ p = *ppList; |
+ if( p==0 ){ |
+ return pLeft; |
+ } |
+ p->pLeft = pLeft; |
+ *ppList = p->pRight; |
+ p->pRight = rowSetNDeepTree(ppList, iDepth-1); |
+ return p; |
+} |
+ |
+/* |
+** Convert a sorted list of elements into a binary tree. Make the tree |
+** as deep as it needs to be in order to contain the entire list. |
+*/ |
+static struct RowSetEntry *rowSetListToTree(struct RowSetEntry *pList){ |
+ int iDepth; /* Depth of the tree so far */ |
+ struct RowSetEntry *p; /* Current tree root */ |
+ struct RowSetEntry *pLeft; /* Left subtree */ |
+ |
+ assert( pList!=0 ); |
+ p = pList; |
+ pList = p->pRight; |
+ p->pLeft = p->pRight = 0; |
+ for(iDepth=1; pList; iDepth++){ |
+ pLeft = p; |
+ p = pList; |
+ pList = p->pRight; |
+ p->pLeft = pLeft; |
+ p->pRight = rowSetNDeepTree(&pList, iDepth); |
+ } |
+ return p; |
+} |
+ |
+/* |
+** Take all the entries on p->pEntry and on the trees in p->pForest and |
+** sort them all together into one big ordered list on p->pEntry. |
+** |
+** This routine should only be called once in the life of a RowSet. |
+*/ |
+static void rowSetToList(RowSet *p){ |
+ |
+ /* This routine is called only once */ |
+ assert( p!=0 && (p->rsFlags & ROWSET_NEXT)==0 ); |
+ |
+ if( (p->rsFlags & ROWSET_SORTED)==0 ){ |
+ p->pEntry = rowSetEntrySort(p->pEntry); |
+ } |
+ |
+ /* While this module could theoretically support it, sqlite3RowSetNext() |
+ ** is never called after sqlite3RowSetText() for the same RowSet. So |
+ ** there is never a forest to deal with. Should this change, simply |
+ ** remove the assert() and the #if 0. */ |
+ assert( p->pForest==0 ); |
+#if 0 |
+ while( p->pForest ){ |
+ struct RowSetEntry *pTree = p->pForest->pLeft; |
+ if( pTree ){ |
+ struct RowSetEntry *pHead, *pTail; |
+ rowSetTreeToList(pTree, &pHead, &pTail); |
+ p->pEntry = rowSetEntryMerge(p->pEntry, pHead); |
+ } |
+ p->pForest = p->pForest->pRight; |
+ } |
+#endif |
+ p->rsFlags |= ROWSET_NEXT; /* Verify this routine is never called again */ |
+} |
+ |
+/* |
+** Extract the smallest element from the RowSet. |
+** Write the element into *pRowid. Return 1 on success. Return |
+** 0 if the RowSet is already empty. |
+** |
+** After this routine has been called, the sqlite3RowSetInsert() |
+** routine may not be called again. |
+*/ |
+SQLITE_PRIVATE int sqlite3RowSetNext(RowSet *p, i64 *pRowid){ |
+ assert( p!=0 ); |
+ |
+ /* Merge the forest into a single sorted list on first call */ |
+ if( (p->rsFlags & ROWSET_NEXT)==0 ) rowSetToList(p); |
+ |
+ /* Return the next entry on the list */ |
+ if( p->pEntry ){ |
+ *pRowid = p->pEntry->v; |
+ p->pEntry = p->pEntry->pRight; |
+ if( p->pEntry==0 ){ |
+ sqlite3RowSetClear(p); |
+ } |
+ return 1; |
+ }else{ |
+ return 0; |
+ } |
+} |
+ |
+/* |
+** Check to see if element iRowid was inserted into the rowset as |
+** part of any insert batch prior to iBatch. Return 1 or 0. |
+** |
+** If this is the first test of a new batch and if there exist entries |
+** on pRowSet->pEntry, then sort those entries into the forest at |
+** pRowSet->pForest so that they can be tested. |
+*/ |
+SQLITE_PRIVATE int sqlite3RowSetTest(RowSet *pRowSet, int iBatch, sqlite3_int64 iRowid){ |
+ struct RowSetEntry *p, *pTree; |
+ |
+ /* This routine is never called after sqlite3RowSetNext() */ |
+ assert( pRowSet!=0 && (pRowSet->rsFlags & ROWSET_NEXT)==0 ); |
+ |
+ /* Sort entries into the forest on the first test of a new batch |
+ */ |
+ if( iBatch!=pRowSet->iBatch ){ |
+ p = pRowSet->pEntry; |
+ if( p ){ |
+ struct RowSetEntry **ppPrevTree = &pRowSet->pForest; |
+ if( (pRowSet->rsFlags & ROWSET_SORTED)==0 ){ |
+ p = rowSetEntrySort(p); |
+ } |
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ |
+ ppPrevTree = &pTree->pRight; |
+ if( pTree->pLeft==0 ){ |
+ pTree->pLeft = rowSetListToTree(p); |
+ break; |
+ }else{ |
+ struct RowSetEntry *pAux, *pTail; |
+ rowSetTreeToList(pTree->pLeft, &pAux, &pTail); |
+ pTree->pLeft = 0; |
+ p = rowSetEntryMerge(pAux, p); |
+ } |
+ } |
+ if( pTree==0 ){ |
+ *ppPrevTree = pTree = rowSetEntryAlloc(pRowSet); |
+ if( pTree ){ |
+ pTree->v = 0; |
+ pTree->pRight = 0; |
+ pTree->pLeft = rowSetListToTree(p); |
+ } |
+ } |
+ pRowSet->pEntry = 0; |
+ pRowSet->pLast = 0; |
+ pRowSet->rsFlags |= ROWSET_SORTED; |
+ } |
+ pRowSet->iBatch = iBatch; |
+ } |
+ |
+ /* Test to see if the iRowid value appears anywhere in the forest. |
+ ** Return 1 if it does and 0 if not. |
+ */ |
+ for(pTree = pRowSet->pForest; pTree; pTree=pTree->pRight){ |
+ p = pTree->pLeft; |
+ while( p ){ |
+ if( p->v<iRowid ){ |
+ p = p->pRight; |
+ }else if( p->v>iRowid ){ |
+ p = p->pLeft; |
+ }else{ |
+ return 1; |
+ } |
+ } |
+ } |
+ return 0; |
+} |
+ |
+/************** End of rowset.c **********************************************/ |
+ |
+/* Chain include. */ |
+#include "sqlite3.02.c" |