|
|
@@ -18144,7 +18144,7 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
#endif /* !defined(SQLITE_MUTEX_OMIT) */
|
|
|
|
|
|
/************** End of mutex_noop.c ******************************************/
|
|
|
-/************** Begin file mutex_unix.c **************************************/
|
|
|
+/************** Begin file mutex_rtt.c ***************************************/
|
|
|
/*
|
|
|
** 2007 August 28
|
|
|
**
|
|
|
@@ -18156,19 +18156,19 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
** May you share freely, never taking more than you give.
|
|
|
**
|
|
|
*************************************************************************
|
|
|
-** This file contains the C functions that implement mutexes for pthreads
|
|
|
+** This file contains the C functions that implement mutexes for rtthread
|
|
|
*/
|
|
|
|
|
|
/*
|
|
|
** The code in this file is only used if we are compiling threadsafe
|
|
|
-** under unix with pthreads.
|
|
|
+** under rt-thread with rt_mutex.
|
|
|
**
|
|
|
-** Note that this implementation requires a version of pthreads that
|
|
|
+** Note that this implementation requires a version of rt-thread that
|
|
|
** supports recursive mutexes.
|
|
|
*/
|
|
|
-#ifdef SQLITE_MUTEX_PTHREADS
|
|
|
+#ifdef SQLITE_MUTEX_RTT
|
|
|
|
|
|
-#include <pthread.h>
|
|
|
+/* #include <rtthread.h> */
|
|
|
|
|
|
/*
|
|
|
** The sqlite3_mutex.id, sqlite3_mutex.nRef, and sqlite3_mutex.owner fields
|
|
|
@@ -18185,50 +18185,51 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
** Each recursive mutex is an instance of the following structure.
|
|
|
*/
|
|
|
struct sqlite3_mutex {
|
|
|
- pthread_mutex_t mutex; /* Mutex controlling the lock */
|
|
|
+ struct rt_mutex mutex; /* Mutex controlling the lock */
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
int id; /* Mutex type */
|
|
|
volatile int nRef; /* Number of entrances */
|
|
|
- volatile pthread_t owner; /* Thread that is within this mutex */
|
|
|
+ volatile rt_thread_t owner; /* Thread that is within this mutex */
|
|
|
int trace; /* True to trace changes */
|
|
|
#endif
|
|
|
};
|
|
|
+#define RTT_MUTEX_INITIALIZER { 0 }
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, 0, 0, (pthread_t)0, 0 }
|
|
|
+#define SQLITE3_MUTEX_INITIALIZER { RTT_MUTEX_INITIALIZER, 0, 0, (rt_thread_t)0, 0 }
|
|
|
#else
|
|
|
-#define SQLITE3_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER }
|
|
|
+#define SQLITE3_MUTEX_INITIALIZER { RTT_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
|
|
|
+** deliver incorrect results. In particular, if rtt_equal() is
|
|
|
** not an atomic operation, then these routines might delivery
|
|
|
-** incorrect results. On most platforms, pthread_equal() is a
|
|
|
+** incorrect results. On most platforms, rtt_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
|
|
|
+** On those platforms where rtt_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 rttMutexHeld(sqlite3_mutex *p){
|
|
|
+ return (p->nRef != 0 && p->owner == rt_thread_self());
|
|
|
}
|
|
|
-static int pthreadMutexNotheld(sqlite3_mutex *p){
|
|
|
- return p->nRef==0 || pthread_equal(p->owner, pthread_self())==0;
|
|
|
+static int rttMutexNotheld(sqlite3_mutex *p){
|
|
|
+ return (p->nRef == 0 || p->owner != rt_thread_self());
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
** Initialize and deinitialize the mutex subsystem.
|
|
|
*/
|
|
|
-static int pthreadMutexInit(void){ return SQLITE_OK; }
|
|
|
-static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
|
|
+static int rttMutexInit(void){ return SQLITE_OK; }
|
|
|
+static int rttMutexEnd(void){ return SQLITE_OK; }
|
|
|
|
|
|
/*
|
|
|
** The sqlite3_mutex_alloc() routine allocates a new
|
|
|
@@ -18268,11 +18269,11 @@ static int pthreadMutexEnd(void){ return SQLITE_OK; }
|
|
|
**
|
|
|
** 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
|
|
|
+** 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 *rttMutexAlloc(int iType){
|
|
|
static sqlite3_mutex staticMutexes[] = {
|
|
|
SQLITE3_MUTEX_INITIALIZER,
|
|
|
SQLITE3_MUTEX_INITIALIZER,
|
|
|
@@ -18286,18 +18287,8 @@ static sqlite3_mutex *pthreadMutexAlloc(int 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
|
|
|
+ rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
p->id = iType;
|
|
|
#endif
|
|
|
@@ -18310,7 +18301,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
p->id = iType;
|
|
|
#endif
|
|
|
- pthread_mutex_init(&p->mutex, 0);
|
|
|
+ rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
@@ -18321,6 +18312,7 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
p->id = iType;
|
|
|
#endif
|
|
|
+ rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
@@ -18333,10 +18325,10 @@ static sqlite3_mutex *pthreadMutexAlloc(int iType){
|
|
|
** allocated mutex. SQLite is careful to deallocate every
|
|
|
** mutex that it allocates.
|
|
|
*/
|
|
|
-static void pthreadMutexFree(sqlite3_mutex *p){
|
|
|
+static void rttMutexFree(sqlite3_mutex *p){
|
|
|
assert( p->nRef==0 );
|
|
|
assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
|
|
- pthread_mutex_destroy(&p->mutex);
|
|
|
+ rt_mutex_detach(&p->mutex);
|
|
|
sqlite3_free(p);
|
|
|
}
|
|
|
|
|
|
@@ -18351,26 +18343,26 @@ static void pthreadMutexFree(sqlite3_mutex *p){
|
|
|
** 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) );
|
|
|
+static void rttMutexEnter(sqlite3_mutex *p){
|
|
|
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || rttMutexNotheld(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()
|
|
|
+ ** our own. This implementation assumes that rtt_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
|
|
|
+ ** 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) ){
|
|
|
+ rt_thread_t self = rt_thread_self();
|
|
|
+ if( p->nRef>0 && (p->owner == self) ){
|
|
|
p->nRef++;
|
|
|
}else{
|
|
|
- pthread_mutex_lock(&p->mutex);
|
|
|
+ rt_mutex_take(&p->mutex, RT_WAITING_FOREVER);
|
|
|
assert( p->nRef==0 );
|
|
|
p->owner = self;
|
|
|
p->nRef = 1;
|
|
|
@@ -18379,41 +18371,41 @@ static void pthreadMutexEnter(sqlite3_mutex *p){
|
|
|
#else
|
|
|
/* Use the built-in recursive mutexes if they are available.
|
|
|
*/
|
|
|
- pthread_mutex_lock(&p->mutex);
|
|
|
+ rt_mutex_take(&p->mutex, RT_WAITING_FOREVER);
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
assert( p->nRef>0 || p->owner==0 );
|
|
|
- p->owner = pthread_self();
|
|
|
+ p->owner = rt_thread_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);
|
|
|
+ rt_kprintf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
}
|
|
|
#endif
|
|
|
}
|
|
|
-static int pthreadMutexTry(sqlite3_mutex *p){
|
|
|
+static int rttMutexTry(sqlite3_mutex *p){
|
|
|
int rc;
|
|
|
- assert( p->id==SQLITE_MUTEX_RECURSIVE || pthreadMutexNotheld(p) );
|
|
|
+ assert( p->id==SQLITE_MUTEX_RECURSIVE || rttMutexNotheld(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()
|
|
|
+ ** our own. This implementation assumes that rtt_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
|
|
|
+ ** 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) ){
|
|
|
+ rt_thread_t self = rt_thread_self();
|
|
|
+ if( p->nRef>0 && (p->owner == self) ){
|
|
|
p->nRef++;
|
|
|
rc = SQLITE_OK;
|
|
|
- }else if( pthread_mutex_trylock(&p->mutex)==0 ){
|
|
|
+ }else if( rt_mutex_take(&p->mutex, RT_WAITING_NO)==RT_EOK ){
|
|
|
assert( p->nRef==0 );
|
|
|
p->owner = self;
|
|
|
p->nRef = 1;
|
|
|
@@ -18425,9 +18417,9 @@ static int pthreadMutexTry(sqlite3_mutex *p){
|
|
|
#else
|
|
|
/* Use the built-in recursive mutexes if they are available.
|
|
|
*/
|
|
|
- if( pthread_mutex_trylock(&p->mutex)==0 ){
|
|
|
+ if( rt_mutex_take(&p->mutex, RT_WAITING_NO)==RT_EOK ){
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
- p->owner = pthread_self();
|
|
|
+ p->owner = rt_thread_self();
|
|
|
p->nRef++;
|
|
|
#endif
|
|
|
rc = SQLITE_OK;
|
|
|
@@ -18438,7 +18430,7 @@ static int pthreadMutexTry(sqlite3_mutex *p){
|
|
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
|
if( rc==SQLITE_OK && p->trace ){
|
|
|
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+ rt_kprintf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
}
|
|
|
#endif
|
|
|
return rc;
|
|
|
@@ -18450,8 +18442,8 @@ static int pthreadMutexTry(sqlite3_mutex *p){
|
|
|
** 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) );
|
|
|
+static void rttMutexLeave(sqlite3_mutex *p){
|
|
|
+ assert( rttMutexHeld(p) );
|
|
|
#if SQLITE_MUTEX_NREF
|
|
|
p->nRef--;
|
|
|
if( p->nRef==0 ) p->owner = 0;
|
|
|
@@ -18460,31 +18452,31 @@ static void pthreadMutexLeave(sqlite3_mutex *p){
|
|
|
|
|
|
#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
|
|
if( p->nRef==0 ){
|
|
|
- pthread_mutex_unlock(&p->mutex);
|
|
|
+ rt_mutex_release(&p->mutex);
|
|
|
}
|
|
|
#else
|
|
|
- pthread_mutex_unlock(&p->mutex);
|
|
|
+ rt_mutex_release(&p->mutex);
|
|
|
#endif
|
|
|
|
|
|
#ifdef SQLITE_DEBUG
|
|
|
if( p->trace ){
|
|
|
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+ rt_kprintf("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,
|
|
|
+ rttMutexInit,
|
|
|
+ rttMutexEnd,
|
|
|
+ rttMutexAlloc,
|
|
|
+ rttMutexFree,
|
|
|
+ rttMutexEnter,
|
|
|
+ rttMutexTry,
|
|
|
+ rttMutexLeave,
|
|
|
#ifdef SQLITE_DEBUG
|
|
|
- pthreadMutexHeld,
|
|
|
- pthreadMutexNotheld
|
|
|
+ rttMutexHeld,
|
|
|
+ rttMutexNotheld
|
|
|
#else
|
|
|
0,
|
|
|
0
|
|
|
@@ -18494,12 +18486,13 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
return &sMutex;
|
|
|
}
|
|
|
|
|
|
-#endif /* SQLITE_MUTEX_PTHREADS */
|
|
|
+#endif /* SQLITE_MUTEX_RTT */
|
|
|
+
|
|
|
|
|
|
-/************** End of mutex_unix.c ******************************************/
|
|
|
-/************** Begin file mutex_w32.c ***************************************/
|
|
|
+/************** End of mutex_rtt.c *******************************************/
|
|
|
+/************** Begin file malloc.c ******************************************/
|
|
|
/*
|
|
|
-** 2007 August 14
|
|
|
+** 2001 September 15
|
|
|
**
|
|
|
** The author disclaims copyright to this source code. In place of
|
|
|
** a legal notice, here is a blessing:
|
|
|
@@ -18509,17912 +18502,4135 @@ SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
** May you share freely, never taking more than you give.
|
|
|
**
|
|
|
*************************************************************************
|
|
|
-** This file contains the C functions that implement mutexes for win32
|
|
|
+**
|
|
|
+** Memory allocation functions used throughout sqlite.
|
|
|
*/
|
|
|
+/* #include <stdarg.h> */
|
|
|
|
|
|
/*
|
|
|
-** The code in this file is only used if we are compiling multithreaded
|
|
|
-** on a win32 system.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#ifdef SQLITE_MUTEX_W32
|
|
|
+SQLITE_API int 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
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** Each recursive mutex is an instance of the following structure.
|
|
|
+** An instance of the following object records the location of
|
|
|
+** each unused scratch buffer.
|
|
|
*/
|
|
|
-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 */
|
|
|
- int trace; /* True to trace changes */
|
|
|
-#endif
|
|
|
-};
|
|
|
-#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
|
|
|
+typedef struct ScratchFreeslot {
|
|
|
+ struct ScratchFreeslot *pNext; /* Next unused scratch buffer */
|
|
|
+} ScratchFreeslot;
|
|
|
|
|
|
/*
|
|
|
-** 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 win 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.
|
|
|
-**
|
|
|
-** mutexIsNT() is only used for the TryEnterCriticalSection() API call,
|
|
|
-** which is only available if your application was compiled with
|
|
|
-** _WIN32_WINNT defined to a value >= 0x0400. Currently, the only
|
|
|
-** call to TryEnterCriticalSection() is #ifdef'ed out, so #ifdef
|
|
|
-** this out as well.
|
|
|
+** State information local to the memory allocation subsystem.
|
|
|
*/
|
|
|
-#if 0
|
|
|
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT
|
|
|
-# define mutexIsNT() (1)
|
|
|
-#else
|
|
|
- static int mutexIsNT(void){
|
|
|
- static int osType = 0;
|
|
|
- if( osType==0 ){
|
|
|
- OSVERSIONINFO sInfo;
|
|
|
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
|
|
- GetVersionEx(&sInfo);
|
|
|
- osType = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
|
|
|
- }
|
|
|
- return osType==2;
|
|
|
- }
|
|
|
-#endif /* SQLITE_OS_WINCE || SQLITE_OS_WINRT */
|
|
|
-#endif
|
|
|
+static SQLITE_WSD struct Mem0Global {
|
|
|
+ sqlite3_mutex *mutex; /* Mutex to serialize access */
|
|
|
+
|
|
|
+ /*
|
|
|
+ ** The alarm callback and its arguments. The mem0.mutex lock will
|
|
|
+ ** be held while the callback is running. Recursive calls into
|
|
|
+ ** the memory subsystem are allowed, but no new callbacks will be
|
|
|
+ ** issued.
|
|
|
+ */
|
|
|
+ sqlite3_int64 alarmThreshold;
|
|
|
+ void (*alarmCallback)(void*, sqlite3_int64,int);
|
|
|
+ void *alarmArg;
|
|
|
+
|
|
|
+ /*
|
|
|
+ ** 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, 0, 0 };
|
|
|
+
|
|
|
+#define mem0 GLOBAL(struct Mem0Global, mem0)
|
|
|
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
/*
|
|
|
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
|
|
|
-** intended for use only inside assert() statements.
|
|
|
+** This routine runs when the memory allocator sees that the
|
|
|
+** total memory allocation is about to exceed the soft heap
|
|
|
+** limit.
|
|
|
*/
|
|
|
-static int winMutexHeld(sqlite3_mutex *p){
|
|
|
- return p->nRef!=0 && p->owner==GetCurrentThreadId();
|
|
|
+static void softHeapLimitEnforcer(
|
|
|
+ void *NotUsed,
|
|
|
+ sqlite3_int64 NotUsed2,
|
|
|
+ int allocSize
|
|
|
+){
|
|
|
+ UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
|
+ sqlite3_release_memory(allocSize);
|
|
|
}
|
|
|
-static int winMutexNotheld2(sqlite3_mutex *p, DWORD tid){
|
|
|
- return p->nRef==0 || p->owner!=tid;
|
|
|
+
|
|
|
+/*
|
|
|
+** Change the alarm callback
|
|
|
+*/
|
|
|
+static int sqlite3MemoryAlarm(
|
|
|
+ void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
|
|
+ void *pArg,
|
|
|
+ sqlite3_int64 iThreshold
|
|
|
+){
|
|
|
+ int nUsed;
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ mem0.alarmCallback = xCallback;
|
|
|
+ mem0.alarmArg = pArg;
|
|
|
+ mem0.alarmThreshold = iThreshold;
|
|
|
+ nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
|
+ mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ return SQLITE_OK;
|
|
|
}
|
|
|
-static int winMutexNotheld(sqlite3_mutex *p){
|
|
|
- DWORD tid = GetCurrentThreadId();
|
|
|
- return winMutexNotheld2(p, tid);
|
|
|
+
|
|
|
+#ifndef SQLITE_OMIT_DEPRECATED
|
|
|
+/*
|
|
|
+** Deprecated external interface. Internal/core SQLite code
|
|
|
+** should call sqlite3MemoryAlarm.
|
|
|
+*/
|
|
|
+SQLITE_API int sqlite3_memory_alarm(
|
|
|
+ void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
|
|
+ void *pArg,
|
|
|
+ sqlite3_int64 iThreshold
|
|
|
+){
|
|
|
+ return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
|
|
|
}
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
/*
|
|
|
-** Initialize and deinitialize the mutex subsystem.
|
|
|
-*/
|
|
|
-static sqlite3_mutex winMutex_staticMutexes[6] = {
|
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
|
- SQLITE3_MUTEX_INITIALIZER,
|
|
|
- SQLITE3_MUTEX_INITIALIZER
|
|
|
-};
|
|
|
-static int winMutex_isInit = 0;
|
|
|
-/* As winMutexInit() and winMutexEnd() are called as part
|
|
|
-** of the sqlite3_initialize and sqlite3_shutdown()
|
|
|
-** processing, the "interlocked" magic is probably not
|
|
|
-** strictly necessary.
|
|
|
+** Set the soft heap-size limit for the library. Passing a zero or
|
|
|
+** negative value indicates no limit.
|
|
|
*/
|
|
|
-static LONG winMutex_lock = 0;
|
|
|
-
|
|
|
-SQLITE_API void 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);
|
|
|
+SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
|
|
+ sqlite3_int64 priorLimit;
|
|
|
+ sqlite3_int64 excess;
|
|
|
+#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
+ int rc = sqlite3_initialize();
|
|
|
+ if( rc ) return -1;
|
|
|
#endif
|
|
|
- }
|
|
|
- winMutex_isInit = 1;
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ priorLimit = mem0.alarmThreshold;
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ if( n<0 ) return priorLimit;
|
|
|
+ if( n>0 ){
|
|
|
+ sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
|
|
|
}else{
|
|
|
- /* Someone else is in the process of initing the static mutexes */
|
|
|
- while( !winMutex_isInit ){
|
|
|
- sqlite3_win32_sleep(1);
|
|
|
- }
|
|
|
+ sqlite3MemoryAlarm(0, 0, 0);
|
|
|
}
|
|
|
- return SQLITE_OK;
|
|
|
+ excess = sqlite3_memory_used() - n;
|
|
|
+ if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
|
|
+ return priorLimit;
|
|
|
+}
|
|
|
+SQLITE_API void sqlite3_soft_heap_limit(int n){
|
|
|
+ if( n<0 ) n = 0;
|
|
|
+ sqlite3_soft_heap_limit64(n);
|
|
|
}
|
|
|
|
|
|
-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;
|
|
|
+/*
|
|
|
+** Initialize the memory allocation subsystem.
|
|
|
+*/
|
|
|
+SQLITE_PRIVATE int sqlite3MallocInit(void){
|
|
|
+ if( sqlite3GlobalConfig.m.xMalloc==0 ){
|
|
|
+ sqlite3MemSetDefault();
|
|
|
+ }
|
|
|
+ memset(&mem0, 0, sizeof(mem0));
|
|
|
+ if( sqlite3GlobalConfig.bCoreMutex ){
|
|
|
+ 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<1 ){
|
|
|
+ sqlite3GlobalConfig.pPage = 0;
|
|
|
+ sqlite3GlobalConfig.szPage = 0;
|
|
|
+ sqlite3GlobalConfig.nPage = 0;
|
|
|
}
|
|
|
- return SQLITE_OK;
|
|
|
+ return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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_MEM2
|
|
|
-** <li> SQLITE_MUTEX_STATIC_PRNG
|
|
|
-** <li> SQLITE_MUTEX_STATIC_LRU
|
|
|
-** <li> SQLITE_MUTEX_STATIC_PMEM
|
|
|
-** </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.
|
|
|
+** 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().
|
|
|
*/
|
|
|
-static sqlite3_mutex *winMutexAlloc(int iType){
|
|
|
- sqlite3_mutex *p;
|
|
|
+SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
|
|
|
+ return mem0.nearlyFull;
|
|
|
+}
|
|
|
|
|
|
- switch( iType ){
|
|
|
- case SQLITE_MUTEX_FAST:
|
|
|
- case SQLITE_MUTEX_RECURSIVE: {
|
|
|
- p = sqlite3MallocZero( sizeof(*p) );
|
|
|
- if( p ){
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- p->id = iType;
|
|
|
-#endif
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- InitializeCriticalSectionEx(&p->mutex, 0, 0);
|
|
|
-#else
|
|
|
- InitializeCriticalSection(&p->mutex);
|
|
|
-#endif
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- default: {
|
|
|
- assert( winMutex_isInit==1 );
|
|
|
- assert( iType-2 >= 0 );
|
|
|
- assert( iType-2 < ArraySize(winMutex_staticMutexes) );
|
|
|
- p = &winMutex_staticMutexes[iType-2];
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- p->id = iType;
|
|
|
-#endif
|
|
|
- break;
|
|
|
- }
|
|
|
+/*
|
|
|
+** Deinitialize the memory allocation subsystem.
|
|
|
+*/
|
|
|
+SQLITE_PRIVATE void sqlite3MallocEnd(void){
|
|
|
+ if( sqlite3GlobalConfig.m.xShutdown ){
|
|
|
+ sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData);
|
|
|
}
|
|
|
- return p;
|
|
|
+ memset(&mem0, 0, sizeof(mem0));
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+** Return the amount of memory currently checked out.
|
|
|
+*/
|
|
|
+SQLITE_API sqlite3_int64 sqlite3_memory_used(void){
|
|
|
+ int n, mx;
|
|
|
+ sqlite3_int64 res;
|
|
|
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
|
|
|
+ res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */
|
|
|
+ return res;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** This routine deallocates a previously
|
|
|
-** allocated mutex. SQLite is careful to deallocate every
|
|
|
-** mutex that it allocates.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-static void winMutexFree(sqlite3_mutex *p){
|
|
|
- assert( p );
|
|
|
- assert( p->nRef==0 && p->owner==0 );
|
|
|
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
|
|
- DeleteCriticalSection(&p->mutex);
|
|
|
- sqlite3_free(p);
|
|
|
+SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
|
|
+ int n, mx;
|
|
|
+ sqlite3_int64 res;
|
|
|
+ sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag);
|
|
|
+ res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */
|
|
|
+ return res;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Trigger the alarm
|
|
|
*/
|
|
|
-static void winMutexEnter(sqlite3_mutex *p){
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- DWORD tid = GetCurrentThreadId();
|
|
|
- assert( p->id==SQLITE_MUTEX_RECURSIVE || winMutexNotheld2(p, tid) );
|
|
|
-#endif
|
|
|
- EnterCriticalSection(&p->mutex);
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- assert( p->nRef>0 || p->owner==0 );
|
|
|
- p->owner = tid;
|
|
|
- p->nRef++;
|
|
|
- if( p->trace ){
|
|
|
- printf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
- }
|
|
|
-#endif
|
|
|
+static void sqlite3MallocAlarm(int nByte){
|
|
|
+ void (*xCallback)(void*,sqlite3_int64,int);
|
|
|
+ sqlite3_int64 nowUsed;
|
|
|
+ void *pArg;
|
|
|
+ if( mem0.alarmCallback==0 ) return;
|
|
|
+ xCallback = mem0.alarmCallback;
|
|
|
+ nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
|
+ pArg = mem0.alarmArg;
|
|
|
+ mem0.alarmCallback = 0;
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ xCallback(pArg, nowUsed, nByte);
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ mem0.alarmCallback = xCallback;
|
|
|
+ mem0.alarmArg = pArg;
|
|
|
}
|
|
|
-static int winMutexTry(sqlite3_mutex *p){
|
|
|
-#ifndef NDEBUG
|
|
|
- DWORD tid = GetCurrentThreadId();
|
|
|
-#endif
|
|
|
- int rc = SQLITE_BUSY;
|
|
|
- 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 0
|
|
|
- if( mutexIsNT() && TryEnterCriticalSection(&p->mutex) ){
|
|
|
- p->owner = tid;
|
|
|
- p->nRef++;
|
|
|
- rc = SQLITE_OK;
|
|
|
+
|
|
|
+/*
|
|
|
+** 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);
|
|
|
+ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
|
|
|
+ if( mem0.alarmCallback!=0 ){
|
|
|
+ int nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
|
+ if( nUsed >= mem0.alarmThreshold - nFull ){
|
|
|
+ mem0.nearlyFull = 1;
|
|
|
+ sqlite3MallocAlarm(nFull);
|
|
|
+ }else{
|
|
|
+ mem0.nearlyFull = 0;
|
|
|
+ }
|
|
|
}
|
|
|
-#else
|
|
|
- UNUSED_PARAMETER(p);
|
|
|
-#endif
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- if( rc==SQLITE_OK && p->trace ){
|
|
|
- printf("try mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+ p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
|
|
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
|
+ if( p==0 && mem0.alarmCallback ){
|
|
|
+ sqlite3MallocAlarm(nFull);
|
|
|
+ p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
|
|
}
|
|
|
#endif
|
|
|
- return rc;
|
|
|
+ if( p ){
|
|
|
+ nFull = sqlite3MallocSize(p);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1);
|
|
|
+ }
|
|
|
+ *pp = p;
|
|
|
+ return nFull;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Allocate memory. This routine is like sqlite3_malloc() except that it
|
|
|
+** assumes the memory subsystem has already been initialized.
|
|
|
*/
|
|
|
-static void winMutexLeave(sqlite3_mutex *p){
|
|
|
-#ifndef NDEBUG
|
|
|
- DWORD tid = GetCurrentThreadId();
|
|
|
- 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
|
|
|
- LeaveCriticalSection(&p->mutex);
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- if( p->trace ){
|
|
|
- printf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+SQLITE_PRIVATE void *sqlite3Malloc(int n){
|
|
|
+ void *p;
|
|
|
+ if( n<=0 /* IMP: R-65312-04917 */
|
|
|
+ || 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(n, &p);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ }else{
|
|
|
+ p = sqlite3GlobalConfig.m.xMalloc(n);
|
|
|
}
|
|
|
-#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;
|
|
|
+ assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
|
|
|
+ return p;
|
|
|
}
|
|
|
-#endif /* SQLITE_MUTEX_W32 */
|
|
|
|
|
|
-/************** End of mutex_w32.c *******************************************/
|
|
|
-/************** Begin file mutex_rtt.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 rtthread
|
|
|
+** 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 *sqlite3_malloc(int n){
|
|
|
+#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
+ if( sqlite3_initialize() ) return 0;
|
|
|
+#endif
|
|
|
+ return sqlite3Malloc(n);
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** The code in this file is only used if we are compiling threadsafe
|
|
|
-** under rt-thread with rt_mutex.
|
|
|
-**
|
|
|
-** Note that this implementation requires a version of rt-thread that
|
|
|
-** supports recursive mutexes.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#ifdef SQLITE_MUTEX_RTT
|
|
|
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
|
+static int scratchAllocOut = 0;
|
|
|
+#endif
|
|
|
|
|
|
-/* #include <rtthread.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.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#if defined(SQLITE_DEBUG) || defined(SQLITE_HOMEGROWN_RECURSIVE_MUTEX)
|
|
|
-# define SQLITE_MUTEX_NREF 1
|
|
|
-#else
|
|
|
-# define SQLITE_MUTEX_NREF 0
|
|
|
+SQLITE_PRIVATE void *sqlite3ScratchMalloc(int n){
|
|
|
+ void *p;
|
|
|
+ assert( n>0 );
|
|
|
+
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
|
|
+ p = mem0.pScratchFree;
|
|
|
+ mem0.pScratchFree = mem0.pScratchFree->pNext;
|
|
|
+ mem0.nScratchFree--;
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
|
|
|
+ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ }else{
|
|
|
+ if( sqlite3GlobalConfig.bMemstat ){
|
|
|
+ sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
|
|
+ n = mallocWithAlarm(n, &p);
|
|
|
+ if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ }else{
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ p = sqlite3GlobalConfig.m.xMalloc(n);
|
|
|
+ }
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
|
|
+ }
|
|
|
+ assert( sqlite3_mutex_notheld(mem0.mutex) );
|
|
|
+
|
|
|
+
|
|
|
+#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
|
+ /* Verify that no more than two scratch allocations per thread
|
|
|
+ ** are 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 );
|
|
|
+ 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( p>=sqlite3GlobalConfig.pScratch && p<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 );
|
|
|
+ sqlite3StatusAdd(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, ~MEMTYPE_SCRATCH) );
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
|
+ if( sqlite3GlobalConfig.bMemstat ){
|
|
|
+ int iSize = sqlite3MallocSize(p);
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
|
|
|
+ sqlite3GlobalConfig.m.xFree(p);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ }else{
|
|
|
+ sqlite3GlobalConfig.m.xFree(p);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
-** Each recursive mutex is an instance of the following structure.
|
|
|
+** TRUE if p is a lookaside memory allocation from db
|
|
|
*/
|
|
|
-struct sqlite3_mutex {
|
|
|
- struct rt_mutex mutex; /* Mutex controlling the lock */
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- int id; /* Mutex type */
|
|
|
- volatile int nRef; /* Number of entrances */
|
|
|
- volatile rt_thread_t owner; /* Thread that is within this mutex */
|
|
|
- int trace; /* True to trace changes */
|
|
|
-#endif
|
|
|
-};
|
|
|
-#define RTT_MUTEX_INITIALIZER { 0 }
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
-#define SQLITE3_MUTEX_INITIALIZER { RTT_MUTEX_INITIALIZER, 0, 0, (rt_thread_t)0, 0 }
|
|
|
+#ifndef SQLITE_OMIT_LOOKASIDE
|
|
|
+static int isLookaside(sqlite3 *db, void *p){
|
|
|
+ return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
|
|
|
+}
|
|
|
#else
|
|
|
-#define SQLITE3_MUTEX_INITIALIZER { RTT_MUTEX_INITIALIZER }
|
|
|
+#define isLookaside(A,B) 0
|
|
|
#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 rtt_equal() is
|
|
|
-** not an atomic operation, then these routines might delivery
|
|
|
-** incorrect results. On most platforms, rtt_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 rtt_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.
|
|
|
+** Return the size of a memory allocation previously obtained from
|
|
|
+** sqlite3Malloc() or sqlite3_malloc().
|
|
|
*/
|
|
|
-#if !defined(NDEBUG) || defined(SQLITE_DEBUG)
|
|
|
-static int rttMutexHeld(sqlite3_mutex *p){
|
|
|
- return (p->nRef != 0 && p->owner == rt_thread_self());
|
|
|
+SQLITE_PRIVATE int sqlite3MallocSize(void *p){
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
|
|
+ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
|
|
|
+ return sqlite3GlobalConfig.m.xSize(p);
|
|
|
}
|
|
|
-static int rttMutexNotheld(sqlite3_mutex *p){
|
|
|
- return (p->nRef == 0 || p->owner != rt_thread_self());
|
|
|
+SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
|
|
+ assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
|
|
+ if( db && isLookaside(db, p) ){
|
|
|
+ return db->lookaside.sz;
|
|
|
+ }else{
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
|
|
|
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
|
|
|
+ return sqlite3GlobalConfig.m.xSize(p);
|
|
|
+ }
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
/*
|
|
|
-** Initialize and deinitialize the mutex subsystem.
|
|
|
+** Free memory previously obtained from sqlite3Malloc().
|
|
|
*/
|
|
|
-static int rttMutexInit(void){ return SQLITE_OK; }
|
|
|
-static int rttMutexEnd(void){ return SQLITE_OK; }
|
|
|
+SQLITE_API void sqlite3_free(void *p){
|
|
|
+ if( p==0 ) return; /* IMP: R-49053-54554 */
|
|
|
+ assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
|
|
+ if( sqlite3GlobalConfig.bMemstat ){
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
|
|
|
+ sqlite3GlobalConfig.m.xFree(p);
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ }else{
|
|
|
+ sqlite3GlobalConfig.m.xFree(p);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** 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_MEM2
|
|
|
-** <li> SQLITE_MUTEX_STATIC_PRNG
|
|
|
-** <li> SQLITE_MUTEX_STATIC_LRU
|
|
|
-** <li> SQLITE_MUTEX_STATIC_PMEM
|
|
|
-** </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.
|
|
|
+** Free memory that might be associated with a particular database
|
|
|
+** connection.
|
|
|
*/
|
|
|
-static sqlite3_mutex *rttMutexAlloc(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 *p;
|
|
|
- switch( iType ){
|
|
|
- case SQLITE_MUTEX_RECURSIVE: {
|
|
|
- p = sqlite3MallocZero( sizeof(*p) );
|
|
|
- if( p ){
|
|
|
- /* Use a recursive mutex if it is available */
|
|
|
- rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- p->id = iType;
|
|
|
-#endif
|
|
|
- }
|
|
|
- break;
|
|
|
+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 ){
|
|
|
+ *db->pnBytesFreed += sqlite3DbMallocSize(db, p);
|
|
|
+ return;
|
|
|
}
|
|
|
- case SQLITE_MUTEX_FAST: {
|
|
|
- p = sqlite3MallocZero( sizeof(*p) );
|
|
|
- if( p ){
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- p->id = iType;
|
|
|
+ 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
|
|
|
- rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- default: {
|
|
|
- assert( iType-2 >= 0 );
|
|
|
- assert( iType-2 < ArraySize(staticMutexes) );
|
|
|
- p = &staticMutexes[iType-2];
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- p->id = iType;
|
|
|
-#endif
|
|
|
- rt_mutex_init(&p->mutex, "sqlmtx", RT_IPC_FLAG_PRIO);
|
|
|
- break;
|
|
|
+ pBuf->pNext = db->lookaside.pFree;
|
|
|
+ db->lookaside.pFree = pBuf;
|
|
|
+ db->lookaside.nOut--;
|
|
|
+ return;
|
|
|
}
|
|
|
}
|
|
|
- return p;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** This routine deallocates a previously
|
|
|
-** allocated mutex. SQLite is careful to deallocate every
|
|
|
-** mutex that it allocates.
|
|
|
-*/
|
|
|
-static void rttMutexFree(sqlite3_mutex *p){
|
|
|
- assert( p->nRef==0 );
|
|
|
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
|
|
|
- rt_mutex_delete(&p->mutex);
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
|
|
|
+ assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
|
sqlite3_free(p);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Change the size of an existing memory allocation
|
|
|
*/
|
|
|
-static void rttMutexEnter(sqlite3_mutex *p){
|
|
|
- assert( p->id==SQLITE_MUTEX_RECURSIVE || rttMutexNotheld(p) );
|
|
|
-
|
|
|
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
|
|
- /* If recursive mutexes are not available, then we have to grow
|
|
|
- ** our own. This implementation assumes that rtt_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.
|
|
|
- */
|
|
|
- {
|
|
|
- rt_thread_t self = rt_thread_self();
|
|
|
- if( p->nRef>0 && (p->owner == self) ){
|
|
|
- p->nRef++;
|
|
|
- }else{
|
|
|
- rt_mutex_take(&p->mutex, RT_WAITING_FOREVER);
|
|
|
- assert( p->nRef==0 );
|
|
|
- p->owner = self;
|
|
|
- p->nRef = 1;
|
|
|
- }
|
|
|
+SQLITE_PRIVATE void *sqlite3Realloc(void *pOld, int nBytes){
|
|
|
+ int nOld, nNew, nDiff;
|
|
|
+ void *pNew;
|
|
|
+ if( pOld==0 ){
|
|
|
+ return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
|
|
|
}
|
|
|
-#else
|
|
|
- /* Use the built-in recursive mutexes if they are available.
|
|
|
- */
|
|
|
- rt_mutex_take(&p->mutex, RT_WAITING_FOREVER);
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- assert( p->nRef>0 || p->owner==0 );
|
|
|
- p->owner = rt_thread_self();
|
|
|
- p->nRef++;
|
|
|
-#endif
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- if( p->trace ){
|
|
|
- rt_kprintf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+ if( nBytes<=0 ){
|
|
|
+ sqlite3_free(pOld); /* IMP: R-31593-10574 */
|
|
|
+ return 0;
|
|
|
}
|
|
|
-#endif
|
|
|
-}
|
|
|
-static int rttMutexTry(sqlite3_mutex *p){
|
|
|
- int rc;
|
|
|
- assert( p->id==SQLITE_MUTEX_RECURSIVE || rttMutexNotheld(p) );
|
|
|
-
|
|
|
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
|
|
- /* If recursive mutexes are not available, then we have to grow
|
|
|
- ** our own. This implementation assumes that rtt_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.
|
|
|
- */
|
|
|
- {
|
|
|
- rt_thread_t self = rt_thread_self();
|
|
|
- if( p->nRef>0 && (p->owner == self) ){
|
|
|
- p->nRef++;
|
|
|
- rc = SQLITE_OK;
|
|
|
- }else if( rt_mutex_take(&p->mutex, RT_WAITING_NO)==RT_EOK ){
|
|
|
- assert( p->nRef==0 );
|
|
|
- p->owner = self;
|
|
|
- p->nRef = 1;
|
|
|
- rc = SQLITE_OK;
|
|
|
- }else{
|
|
|
- rc = SQLITE_BUSY;
|
|
|
- }
|
|
|
+ if( nBytes>=0x7fffff00 ){
|
|
|
+ /* The 0x7ffff00 limit term is explained in comments on sqlite3Malloc() */
|
|
|
+ return 0;
|
|
|
}
|
|
|
-#else
|
|
|
- /* Use the built-in recursive mutexes if they are available.
|
|
|
- */
|
|
|
- if( rt_mutex_take(&p->mutex, RT_WAITING_NO)==RT_EOK ){
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- p->owner = rt_thread_self();
|
|
|
- p->nRef++;
|
|
|
-#endif
|
|
|
- rc = SQLITE_OK;
|
|
|
+ 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(nBytes);
|
|
|
+ if( nOld==nNew ){
|
|
|
+ pNew = pOld;
|
|
|
+ }else if( sqlite3GlobalConfig.bMemstat ){
|
|
|
+ sqlite3_mutex_enter(mem0.mutex);
|
|
|
+ sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
|
|
|
+ nDiff = nNew - nOld;
|
|
|
+ if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
|
|
+ mem0.alarmThreshold-nDiff ){
|
|
|
+ sqlite3MallocAlarm(nDiff);
|
|
|
+ }
|
|
|
+ assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
|
|
|
+ assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
|
|
|
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
|
+ if( pNew==0 && mem0.alarmCallback ){
|
|
|
+ sqlite3MallocAlarm(nBytes);
|
|
|
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
|
+ }
|
|
|
+ if( pNew ){
|
|
|
+ nNew = sqlite3MallocSize(pNew);
|
|
|
+ sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
|
|
|
+ }
|
|
|
+ sqlite3_mutex_leave(mem0.mutex);
|
|
|
}else{
|
|
|
- rc = SQLITE_BUSY;
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- if( rc==SQLITE_OK && p->trace ){
|
|
|
- rt_kprintf("enter mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+ pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
|
}
|
|
|
-#endif
|
|
|
- return rc;
|
|
|
+ assert( EIGHT_BYTE_ALIGNMENT(pNew) ); /* IMP: R-04675-44850 */
|
|
|
+ return pNew;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** The public interface to sqlite3Realloc. Make sure that the memory
|
|
|
+** subsystem is initialized prior to invoking sqliteRealloc.
|
|
|
*/
|
|
|
-static void rttMutexLeave(sqlite3_mutex *p){
|
|
|
- assert( rttMutexHeld(p) );
|
|
|
-#if SQLITE_MUTEX_NREF
|
|
|
- p->nRef--;
|
|
|
- if( p->nRef==0 ) p->owner = 0;
|
|
|
+SQLITE_API void *sqlite3_realloc(void *pOld, int n){
|
|
|
+#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
+ if( sqlite3_initialize() ) return 0;
|
|
|
#endif
|
|
|
- assert( p->nRef==0 || p->id==SQLITE_MUTEX_RECURSIVE );
|
|
|
+ return sqlite3Realloc(pOld, n);
|
|
|
+}
|
|
|
|
|
|
-#ifdef SQLITE_HOMEGROWN_RECURSIVE_MUTEX
|
|
|
- if( p->nRef==0 ){
|
|
|
- rt_mutex_release(&p->mutex);
|
|
|
- }
|
|
|
-#else
|
|
|
- rt_mutex_release(&p->mutex);
|
|
|
-#endif
|
|
|
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- if( p->trace ){
|
|
|
- rt_kprintf("leave mutex %p (%d) with nRef=%d\n", p, p->trace, p->nRef);
|
|
|
+/*
|
|
|
+** Allocate and zero memory.
|
|
|
+*/
|
|
|
+SQLITE_PRIVATE void *sqlite3MallocZero(int n){
|
|
|
+ void *p = sqlite3Malloc(n);
|
|
|
+ if( p ){
|
|
|
+ memset(p, 0, n);
|
|
|
}
|
|
|
-#endif
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
|
|
|
- static const sqlite3_mutex_methods sMutex = {
|
|
|
- rttMutexInit,
|
|
|
- rttMutexEnd,
|
|
|
- rttMutexAlloc,
|
|
|
- rttMutexFree,
|
|
|
- rttMutexEnter,
|
|
|
- rttMutexTry,
|
|
|
- rttMutexLeave,
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- rttMutexHeld,
|
|
|
- rttMutexNotheld
|
|
|
-#else
|
|
|
- 0,
|
|
|
- 0
|
|
|
-#endif
|
|
|
- };
|
|
|
-
|
|
|
- return &sMutex;
|
|
|
+/*
|
|
|
+** Allocate and zero memory. If the allocation fails, make
|
|
|
+** the mallocFailed flag in the connection pointer.
|
|
|
+*/
|
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocZero(sqlite3 *db, int n){
|
|
|
+ void *p = sqlite3DbMallocRaw(db, n);
|
|
|
+ if( p ){
|
|
|
+ memset(p, 0, n);
|
|
|
+ }
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
-#endif /* SQLITE_MUTEX_RTT */
|
|
|
-
|
|
|
-
|
|
|
-/************** End of mutex_rtt.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:
|
|
|
+** Allocate and zero memory. If the allocation fails, make
|
|
|
+** the mallocFailed flag in the connection pointer.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** 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;
|
|
|
**
|
|
|
-** Memory allocation functions used throughout sqlite.
|
|
|
+** In other words, if a subsequent malloc (ex: "b") worked, it is assumed
|
|
|
+** that all prior mallocs (ex: "a") worked too.
|
|
|
*/
|
|
|
-/* #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 sqlite3_release_memory(int n){
|
|
|
-#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
|
|
|
- return sqlite3PcacheReleaseMemory(n);
|
|
|
+SQLITE_PRIVATE void *sqlite3DbMallocRaw(sqlite3 *db, int 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
|
|
|
- /* 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;
|
|
|
+ if( db && db->mallocFailed ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
#endif
|
|
|
+ p = sqlite3Malloc(n);
|
|
|
+ if( !p && db ){
|
|
|
+ db->mallocFailed = 1;
|
|
|
+ }
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_DB |
|
|
|
+ ((db && db->lookaside.bEnabled) ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
|
|
|
+ return p;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Resize the block of memory pointed to by p to n bytes. If the
|
|
|
+** resize fails, set the mallocFailed flag in the connection object.
|
|
|
*/
|
|
|
-static SQLITE_WSD struct Mem0Global {
|
|
|
- sqlite3_mutex *mutex; /* Mutex to serialize access */
|
|
|
-
|
|
|
- /*
|
|
|
- ** The alarm callback and its arguments. The mem0.mutex lock will
|
|
|
- ** be held while the callback is running. Recursive calls into
|
|
|
- ** the memory subsystem are allowed, but no new callbacks will be
|
|
|
- ** issued.
|
|
|
- */
|
|
|
- sqlite3_int64 alarmThreshold;
|
|
|
- void (*alarmCallback)(void*, sqlite3_int64,int);
|
|
|
- void *alarmArg;
|
|
|
-
|
|
|
- /*
|
|
|
- ** 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, 0, 0 };
|
|
|
-
|
|
|
-#define mem0 GLOBAL(struct Mem0Global, mem0)
|
|
|
+SQLITE_PRIVATE void *sqlite3DbRealloc(sqlite3 *db, void *p, int 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_DB) );
|
|
|
+ assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
|
+ pNew = sqlite3_realloc(p, n);
|
|
|
+ if( !pNew ){
|
|
|
+ sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP);
|
|
|
+ db->mallocFailed = 1;
|
|
|
+ }
|
|
|
+ sqlite3MemdebugSetType(pNew, MEMTYPE_DB |
|
|
|
+ (db->lookaside.bEnabled ? MEMTYPE_LOOKASIDE : MEMTYPE_HEAP));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return pNew;
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** This routine runs when the memory allocator sees that the
|
|
|
-** total memory allocation is about to exceed the soft heap
|
|
|
-** limit.
|
|
|
+** Attempt to reallocate p. If the reallocation fails, then free p
|
|
|
+** and set the mallocFailed flag in the database connection.
|
|
|
*/
|
|
|
-static void softHeapLimitEnforcer(
|
|
|
- void *NotUsed,
|
|
|
- sqlite3_int64 NotUsed2,
|
|
|
- int allocSize
|
|
|
-){
|
|
|
- UNUSED_PARAMETER2(NotUsed, NotUsed2);
|
|
|
- sqlite3_release_memory(allocSize);
|
|
|
+SQLITE_PRIVATE void *sqlite3DbReallocOrFree(sqlite3 *db, void *p, int n){
|
|
|
+ void *pNew;
|
|
|
+ pNew = sqlite3DbRealloc(db, p, n);
|
|
|
+ if( !pNew ){
|
|
|
+ sqlite3DbFree(db, p);
|
|
|
+ }
|
|
|
+ return pNew;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Change the alarm callback
|
|
|
+** 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.
|
|
|
*/
|
|
|
-static int sqlite3MemoryAlarm(
|
|
|
- void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
|
|
- void *pArg,
|
|
|
- sqlite3_int64 iThreshold
|
|
|
-){
|
|
|
- int nUsed;
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- mem0.alarmCallback = xCallback;
|
|
|
- mem0.alarmArg = pArg;
|
|
|
- mem0.alarmThreshold = iThreshold;
|
|
|
- nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
|
- mem0.nearlyFull = (iThreshold>0 && iThreshold<=nUsed);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- return SQLITE_OK;
|
|
|
+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, int n){
|
|
|
+ char *zNew;
|
|
|
+ if( z==0 ){
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ assert( (n&0x7fffffff)==n );
|
|
|
+ zNew = sqlite3DbMallocRaw(db, n+1);
|
|
|
+ if( zNew ){
|
|
|
+ memcpy(zNew, z, n);
|
|
|
+ zNew[n] = 0;
|
|
|
+ }
|
|
|
+ return zNew;
|
|
|
}
|
|
|
|
|
|
-#ifndef SQLITE_OMIT_DEPRECATED
|
|
|
/*
|
|
|
-** Deprecated external interface. Internal/core SQLite code
|
|
|
-** should call sqlite3MemoryAlarm.
|
|
|
+** Create a string from the zFromat argument and the va_list that follows.
|
|
|
+** Store the string in memory obtained from sqliteMalloc() and make *pz
|
|
|
+** point to that string.
|
|
|
*/
|
|
|
-SQLITE_API int sqlite3_memory_alarm(
|
|
|
- void(*xCallback)(void *pArg, sqlite3_int64 used,int N),
|
|
|
- void *pArg,
|
|
|
- sqlite3_int64 iThreshold
|
|
|
-){
|
|
|
- return sqlite3MemoryAlarm(xCallback, pArg, iThreshold);
|
|
|
+SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
|
|
|
+ va_list ap;
|
|
|
+ char *z;
|
|
|
+
|
|
|
+ va_start(ap, zFormat);
|
|
|
+ z = sqlite3VMPrintf(db, zFormat, ap);
|
|
|
+ va_end(ap);
|
|
|
+ sqlite3DbFree(db, *pz);
|
|
|
+ *pz = z;
|
|
|
}
|
|
|
-#endif
|
|
|
+
|
|
|
|
|
|
/*
|
|
|
-** Set the soft heap-size limit for the library. Passing a zero or
|
|
|
-** negative value indicates no limit.
|
|
|
+** 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 the first argument, db, is not NULL and a malloc() error has occurred,
|
|
|
+** then the connection error-code (the value returned by sqlite3_errcode())
|
|
|
+** is set to SQLITE_NOMEM.
|
|
|
*/
|
|
|
-SQLITE_API sqlite3_int64 sqlite3_soft_heap_limit64(sqlite3_int64 n){
|
|
|
- sqlite3_int64 priorLimit;
|
|
|
- sqlite3_int64 excess;
|
|
|
-#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
- int rc = sqlite3_initialize();
|
|
|
- if( rc ) return -1;
|
|
|
-#endif
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- priorLimit = mem0.alarmThreshold;
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- if( n<0 ) return priorLimit;
|
|
|
- if( n>0 ){
|
|
|
- sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, n);
|
|
|
- }else{
|
|
|
- sqlite3MemoryAlarm(0, 0, 0);
|
|
|
+SQLITE_PRIVATE int sqlite3ApiExit(sqlite3* db, int rc){
|
|
|
+ /* If the db handle is not NULL, then we 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 || sqlite3_mutex_held(db->mutex) );
|
|
|
+ if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){
|
|
|
+ sqlite3Error(db, SQLITE_NOMEM, 0);
|
|
|
+ db->mallocFailed = 0;
|
|
|
+ rc = SQLITE_NOMEM;
|
|
|
}
|
|
|
- excess = sqlite3_memory_used() - n;
|
|
|
- if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
|
|
|
- return priorLimit;
|
|
|
-}
|
|
|
-SQLITE_API void sqlite3_soft_heap_limit(int n){
|
|
|
- if( n<0 ) n = 0;
|
|
|
- sqlite3_soft_heap_limit64(n);
|
|
|
+ return rc & (db ? db->errMask : 0xff);
|
|
|
}
|
|
|
|
|
|
+/************** End of malloc.c **********************************************/
|
|
|
+/************** Begin file printf.c ******************************************/
|
|
|
/*
|
|
|
-** Initialize the memory allocation subsystem.
|
|
|
+** The "printf" code that follows dates from the 1980's. It is in
|
|
|
+** the public domain. The original comments are included here for
|
|
|
+** completeness. They are very out-of-date but might be useful as
|
|
|
+** an historical reference. Most of the "enhancements" have been backed
|
|
|
+** out so that the functionality is now the same as standard printf().
|
|
|
+**
|
|
|
+**************************************************************************
|
|
|
+**
|
|
|
+** 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
|
|
|
+** SQLlite.
|
|
|
*/
|
|
|
-SQLITE_PRIVATE int sqlite3MallocInit(void){
|
|
|
- if( sqlite3GlobalConfig.m.xMalloc==0 ){
|
|
|
- sqlite3MemSetDefault();
|
|
|
- }
|
|
|
- memset(&mem0, 0, sizeof(mem0));
|
|
|
- if( sqlite3GlobalConfig.bCoreMutex ){
|
|
|
- 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<1 ){
|
|
|
- sqlite3GlobalConfig.pPage = 0;
|
|
|
- sqlite3GlobalConfig.szPage = 0;
|
|
|
- sqlite3GlobalConfig.nPage = 0;
|
|
|
- }
|
|
|
- return sqlite3GlobalConfig.m.xInit(sqlite3GlobalConfig.m.pAppData);
|
|
|
-}
|
|
|
|
|
|
/*
|
|
|
-** 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().
|
|
|
+** Conversion types fall into various categories as defined by the
|
|
|
+** following enumeration.
|
|
|
*/
|
|
|
-SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
|
|
|
- return mem0.nearlyFull;
|
|
|
-}
|
|
|
+#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 */
|
|
|
+
|
|
|
|
|
|
/*
|
|
|
-** Deinitialize the memory allocation subsystem.
|
|
|
+** An "etByte" is an 8-bit unsigned value.
|
|
|
*/
|
|
|
-SQLITE_PRIVATE void sqlite3MallocEnd(void){
|
|
|
- if( sqlite3GlobalConfig.m.xShutdown ){
|
|
|
- sqlite3GlobalConfig.m.xShutdown(sqlite3GlobalConfig.m.pAppData);
|
|
|
- }
|
|
|
- memset(&mem0, 0, sizeof(mem0));
|
|
|
-}
|
|
|
+typedef unsigned char etByte;
|
|
|
|
|
|
/*
|
|
|
-** Return the amount of memory currently checked out.
|
|
|
+** Each builtin conversion character (ex: the 'd' in "%d") is described
|
|
|
+** by an instance of the following structure
|
|
|
*/
|
|
|
-SQLITE_API sqlite3_int64 sqlite3_memory_used(void){
|
|
|
- int n, mx;
|
|
|
- sqlite3_int64 res;
|
|
|
- sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, 0);
|
|
|
- res = (sqlite3_int64)n; /* Work around bug in Borland C. Ticket #3216 */
|
|
|
- return res;
|
|
|
-}
|
|
|
+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;
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Allowed values for et_info.flags
|
|
|
*/
|
|
|
-SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag){
|
|
|
- int n, mx;
|
|
|
- sqlite3_int64 res;
|
|
|
- sqlite3_status(SQLITE_STATUS_MEMORY_USED, &n, &mx, resetFlag);
|
|
|
- res = (sqlite3_int64)mx; /* Work around bug in Borland C. Ticket #3216 */
|
|
|
- return res;
|
|
|
-}
|
|
|
+#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 */
|
|
|
+
|
|
|
|
|
|
/*
|
|
|
-** Trigger the alarm
|
|
|
+** The following table is searched linearly, so it is good to put the
|
|
|
+** most frequently used conversion types first.
|
|
|
*/
|
|
|
-static void sqlite3MallocAlarm(int nByte){
|
|
|
- void (*xCallback)(void*,sqlite3_int64,int);
|
|
|
- sqlite3_int64 nowUsed;
|
|
|
- void *pArg;
|
|
|
- if( mem0.alarmCallback==0 ) return;
|
|
|
- xCallback = mem0.alarmCallback;
|
|
|
- nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
|
|
|
- pArg = mem0.alarmArg;
|
|
|
- mem0.alarmCallback = 0;
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- xCallback(pArg, nowUsed, nByte);
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- mem0.alarmCallback = xCallback;
|
|
|
- mem0.alarmArg = pArg;
|
|
|
+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 */
|
|
|
|
|
|
/*
|
|
|
-** Do a memory allocation with statistics and alarms. Assume the
|
|
|
-** lock is already held.
|
|
|
+** Append N space characters to the given string buffer.
|
|
|
*/
|
|
|
-static int mallocWithAlarm(int n, void **pp){
|
|
|
- int nFull;
|
|
|
- void *p;
|
|
|
- assert( sqlite3_mutex_held(mem0.mutex) );
|
|
|
- nFull = sqlite3GlobalConfig.m.xRoundup(n);
|
|
|
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, n);
|
|
|
- if( mem0.alarmCallback!=0 ){
|
|
|
- int 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.alarmCallback ){
|
|
|
- sqlite3MallocAlarm(nFull);
|
|
|
- p = sqlite3GlobalConfig.m.xMalloc(nFull);
|
|
|
+SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
|
|
+ static const char zSpaces[] = " ";
|
|
|
+ while( N>=(int)sizeof(zSpaces)-1 ){
|
|
|
+ sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
|
|
+ N -= sizeof(zSpaces)-1;
|
|
|
}
|
|
|
-#endif
|
|
|
- if( p ){
|
|
|
- nFull = sqlite3MallocSize(p);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nFull);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, 1);
|
|
|
+ if( N>0 ){
|
|
|
+ sqlite3StrAccumAppend(pAccum, zSpaces, N);
|
|
|
}
|
|
|
- *pp = p;
|
|
|
- return nFull;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Allocate memory. This routine is like sqlite3_malloc() except that it
|
|
|
-** assumes the memory subsystem has already been initialized.
|
|
|
+** On machines with a small stack size, you can redefine the
|
|
|
+** SQLITE_PRINT_BUF_SIZE to be something smaller, if desired.
|
|
|
*/
|
|
|
-SQLITE_PRIVATE void *sqlite3Malloc(int n){
|
|
|
- void *p;
|
|
|
- if( n<=0 /* IMP: R-65312-04917 */
|
|
|
- || 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(n, &p);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- }else{
|
|
|
- p = sqlite3GlobalConfig.m.xMalloc(n);
|
|
|
- }
|
|
|
- assert( EIGHT_BYTE_ALIGNMENT(p) ); /* IMP: R-04675-44850 */
|
|
|
- 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 *sqlite3_malloc(int n){
|
|
|
-#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
- if( sqlite3_initialize() ) return 0;
|
|
|
+#ifndef SQLITE_PRINT_BUF_SIZE
|
|
|
+# define SQLITE_PRINT_BUF_SIZE 70
|
|
|
#endif
|
|
|
- return sqlite3Malloc(n);
|
|
|
-}
|
|
|
+#define etBUFSIZE SQLITE_PRINT_BUF_SIZE /* Size of the output buffer */
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Render a string given by "fmt" into the StrAccum object.
|
|
|
*/
|
|
|
-#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
|
-static int scratchAllocOut = 0;
|
|
|
+SQLITE_PRIVATE void sqlite3VXPrintf(
|
|
|
+ StrAccum *pAccum, /* Accumulate results here */
|
|
|
+ int useExtended, /* Allow extended %-conversions */
|
|
|
+ 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 */
|
|
|
+ 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; /* 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
|
|
|
+ char buf[etBUFSIZE]; /* Conversion buffer */
|
|
|
|
|
|
-
|
|
|
-/*
|
|
|
-** 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);
|
|
|
- if( mem0.nScratchFree && sqlite3GlobalConfig.szScratch>=n ){
|
|
|
- p = mem0.pScratchFree;
|
|
|
- mem0.pScratchFree = mem0.pScratchFree->pNext;
|
|
|
- mem0.nScratchFree--;
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, 1);
|
|
|
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- }else{
|
|
|
- if( sqlite3GlobalConfig.bMemstat ){
|
|
|
- sqlite3StatusSet(SQLITE_STATUS_SCRATCH_SIZE, n);
|
|
|
- n = mallocWithAlarm(n, &p);
|
|
|
- if( p ) sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, n);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ bufpt = 0;
|
|
|
+ for(; (c=(*fmt))!=0; ++fmt){
|
|
|
+ if( c!='%' ){
|
|
|
+ int amt;
|
|
|
+ bufpt = (char *)fmt;
|
|
|
+ amt = 1;
|
|
|
+ while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
|
|
+ sqlite3StrAccumAppend(pAccum, bufpt, amt);
|
|
|
+ if( c==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 */
|
|
|
+ width = 0;
|
|
|
+ if( c=='*' ){
|
|
|
+ width = va_arg(ap,int);
|
|
|
+ if( width<0 ){
|
|
|
+ flag_leftjustify = 1;
|
|
|
+ width = -width;
|
|
|
+ }
|
|
|
+ c = *++fmt;
|
|
|
}else{
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- p = sqlite3GlobalConfig.m.xMalloc(n);
|
|
|
+ while( c>='0' && c<='9' ){
|
|
|
+ width = width*10 + c - '0';
|
|
|
+ c = *++fmt;
|
|
|
+ }
|
|
|
}
|
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_SCRATCH);
|
|
|
- }
|
|
|
- assert( sqlite3_mutex_notheld(mem0.mutex) );
|
|
|
-
|
|
|
-
|
|
|
-#if SQLITE_THREADSAFE==0 && !defined(NDEBUG)
|
|
|
- /* Verify that no more than two scratch allocations per thread
|
|
|
- ** are 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 );
|
|
|
- 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( p>=sqlite3GlobalConfig.pScratch && p<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 );
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_USED, -1);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ /* Get the precision */
|
|
|
+ if( c=='.' ){
|
|
|
+ precision = 0;
|
|
|
+ c = *++fmt;
|
|
|
+ if( c=='*' ){
|
|
|
+ precision = va_arg(ap,int);
|
|
|
+ if( precision<0 ) precision = -precision;
|
|
|
+ c = *++fmt;
|
|
|
+ }else{
|
|
|
+ while( c>='0' && c<='9' ){
|
|
|
+ precision = precision*10 + c - '0';
|
|
|
+ c = *++fmt;
|
|
|
+ }
|
|
|
+ }
|
|
|
}else{
|
|
|
- /* Release memory back to the heap */
|
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_SCRATCH) );
|
|
|
- assert( sqlite3MemdebugNoType(p, ~MEMTYPE_SCRATCH) );
|
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
|
- if( sqlite3GlobalConfig.bMemstat ){
|
|
|
- int iSize = sqlite3MallocSize(p);
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_SCRATCH_OVERFLOW, -iSize);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -iSize);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
|
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
+ precision = -1;
|
|
|
+ }
|
|
|
+ /* Get the conversion type modifier */
|
|
|
+ if( c=='l' ){
|
|
|
+ flag_long = 1;
|
|
|
+ c = *++fmt;
|
|
|
+ if( c=='l' ){
|
|
|
+ flag_longlong = 1;
|
|
|
+ c = *++fmt;
|
|
|
}else{
|
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
|
+ flag_longlong = 0;
|
|
|
}
|
|
|
+ }else{
|
|
|
+ flag_long = flag_longlong = 0;
|
|
|
}
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** TRUE if p is a lookaside memory allocation from db
|
|
|
-*/
|
|
|
-#ifndef SQLITE_OMIT_LOOKASIDE
|
|
|
-static int isLookaside(sqlite3 *db, void *p){
|
|
|
- return p && p>=db->lookaside.pStart && p<db->lookaside.pEnd;
|
|
|
-}
|
|
|
-#else
|
|
|
-#define isLookaside(A,B) 0
|
|
|
-#endif
|
|
|
+ /* 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( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
|
|
+ xtype = infop->type;
|
|
|
+ }else{
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ zExtra = 0;
|
|
|
|
|
|
-/*
|
|
|
-** 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) );
|
|
|
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
|
|
|
- return sqlite3GlobalConfig.m.xSize(p);
|
|
|
-}
|
|
|
-SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){
|
|
|
- assert( db==0 || sqlite3_mutex_held(db->mutex) );
|
|
|
- if( db && isLookaside(db, p) ){
|
|
|
- return db->lookaside.sz;
|
|
|
- }else{
|
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_DB) );
|
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
|
|
|
- assert( db!=0 || sqlite3MemdebugNoType(p, MEMTYPE_LOOKASIDE) );
|
|
|
- return sqlite3GlobalConfig.m.xSize(p);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Free memory previously obtained from sqlite3Malloc().
|
|
|
-*/
|
|
|
-SQLITE_API void sqlite3_free(void *p){
|
|
|
- if( p==0 ) return; /* IMP: R-49053-54554 */
|
|
|
- assert( sqlite3MemdebugNoType(p, MEMTYPE_DB) );
|
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_HEAP) );
|
|
|
- if( sqlite3GlobalConfig.bMemstat ){
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, -sqlite3MallocSize(p));
|
|
|
- sqlite3StatusAdd(SQLITE_STATUS_MALLOC_COUNT, -1);
|
|
|
- sqlite3GlobalConfig.m.xFree(p);
|
|
|
- sqlite3_mutex_leave(mem0.mutex);
|
|
|
- }else{
|
|
|
- sqlite3GlobalConfig.m.xFree(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 ){
|
|
|
- *db->pnBytesFreed += sqlite3DbMallocSize(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_DB) );
|
|
|
- assert( sqlite3MemdebugHasType(p, 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, int nBytes){
|
|
|
- int nOld, nNew, nDiff;
|
|
|
- void *pNew;
|
|
|
- if( pOld==0 ){
|
|
|
- return sqlite3Malloc(nBytes); /* IMP: R-28354-25769 */
|
|
|
- }
|
|
|
- if( nBytes<=0 ){
|
|
|
- sqlite3_free(pOld); /* IMP: R-31593-10574 */
|
|
|
- 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(nBytes);
|
|
|
- if( nOld==nNew ){
|
|
|
- pNew = pOld;
|
|
|
- }else if( sqlite3GlobalConfig.bMemstat ){
|
|
|
- sqlite3_mutex_enter(mem0.mutex);
|
|
|
- sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes);
|
|
|
- nDiff = nNew - nOld;
|
|
|
- if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
|
|
|
- mem0.alarmThreshold-nDiff ){
|
|
|
- sqlite3MallocAlarm(nDiff);
|
|
|
- }
|
|
|
- assert( sqlite3MemdebugHasType(pOld, MEMTYPE_HEAP) );
|
|
|
- assert( sqlite3MemdebugNoType(pOld, ~MEMTYPE_HEAP) );
|
|
|
- pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
|
- if( pNew==0 && mem0.alarmCallback ){
|
|
|
- sqlite3MallocAlarm(nBytes);
|
|
|
- pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
|
|
|
- }
|
|
|
- if( pNew ){
|
|
|
- nNew = sqlite3MallocSize(pNew);
|
|
|
- sqlite3StatusAdd(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-04675-44850 */
|
|
|
- return pNew;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** The public interface to sqlite3Realloc. Make sure that the memory
|
|
|
-** subsystem is initialized prior to invoking sqliteRealloc.
|
|
|
-*/
|
|
|
-SQLITE_API void *sqlite3_realloc(void *pOld, int n){
|
|
|
-#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
- if( sqlite3_initialize() ) return 0;
|
|
|
-#endif
|
|
|
- return sqlite3Realloc(pOld, n);
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** Allocate and zero memory.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void *sqlite3MallocZero(int n){
|
|
|
- void *p = sqlite3Malloc(n);
|
|
|
- if( p ){
|
|
|
- memset(p, 0, 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, int n){
|
|
|
- void *p = sqlite3DbMallocRaw(db, n);
|
|
|
- if( p ){
|
|
|
- memset(p, 0, 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, int 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, MEMTYPE_DB |
|
|
|
- ((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, int 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_DB) );
|
|
|
- assert( sqlite3MemdebugHasType(p, MEMTYPE_LOOKASIDE|MEMTYPE_HEAP) );
|
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
|
|
|
- pNew = sqlite3_realloc(p, n);
|
|
|
- if( !pNew ){
|
|
|
- sqlite3MemdebugSetType(p, MEMTYPE_DB|MEMTYPE_HEAP);
|
|
|
- db->mallocFailed = 1;
|
|
|
- }
|
|
|
- sqlite3MemdebugSetType(pNew, MEMTYPE_DB |
|
|
|
- (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, int 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, int n){
|
|
|
- char *zNew;
|
|
|
- if( z==0 ){
|
|
|
- return 0;
|
|
|
- }
|
|
|
- assert( (n&0x7fffffff)==n );
|
|
|
- zNew = sqlite3DbMallocRaw(db, n+1);
|
|
|
- if( zNew ){
|
|
|
- memcpy(zNew, z, n);
|
|
|
- zNew[n] = 0;
|
|
|
- }
|
|
|
- return zNew;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Create a string from the zFromat argument and the va_list that follows.
|
|
|
-** Store the string in memory obtained from sqliteMalloc() and make *pz
|
|
|
-** point to that string.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3SetString(char **pz, sqlite3 *db, const char *zFormat, ...){
|
|
|
- va_list ap;
|
|
|
- char *z;
|
|
|
-
|
|
|
- va_start(ap, zFormat);
|
|
|
- z = sqlite3VMPrintf(db, zFormat, ap);
|
|
|
- va_end(ap);
|
|
|
- sqlite3DbFree(db, *pz);
|
|
|
- *pz = z;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 the first argument, db, is not NULL and a malloc() error has 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 is not NULL, then we 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 || sqlite3_mutex_held(db->mutex) );
|
|
|
- if( db && (db->mallocFailed || rc==SQLITE_IOERR_NOMEM) ){
|
|
|
- sqlite3Error(db, SQLITE_NOMEM, 0);
|
|
|
- db->mallocFailed = 0;
|
|
|
- rc = SQLITE_NOMEM;
|
|
|
- }
|
|
|
- return rc & (db ? db->errMask : 0xff);
|
|
|
-}
|
|
|
-
|
|
|
-/************** End of malloc.c **********************************************/
|
|
|
-/************** Begin file printf.c ******************************************/
|
|
|
-/*
|
|
|
-** The "printf" code that follows dates from the 1980's. It is in
|
|
|
-** the public domain. The original comments are included here for
|
|
|
-** completeness. They are very out-of-date but might be useful as
|
|
|
-** an historical reference. Most of the "enhancements" have been backed
|
|
|
-** out so that the functionality is now the same as standard printf().
|
|
|
-**
|
|
|
-**************************************************************************
|
|
|
-**
|
|
|
-** 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
|
|
|
-** SQLlite.
|
|
|
-*/
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
-
|
|
|
-/*
|
|
|
-** Append N space characters to the given string buffer.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *pAccum, int N){
|
|
|
- static const char zSpaces[] = " ";
|
|
|
- while( N>=(int)sizeof(zSpaces)-1 ){
|
|
|
- sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
|
|
|
- N -= sizeof(zSpaces)-1;
|
|
|
- }
|
|
|
- if( N>0 ){
|
|
|
- sqlite3StrAccumAppend(pAccum, zSpaces, N);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
- int useExtended, /* Allow extended %-conversions */
|
|
|
- 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 */
|
|
|
- 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; /* 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
|
|
|
- char buf[etBUFSIZE]; /* Conversion buffer */
|
|
|
-
|
|
|
- bufpt = 0;
|
|
|
- for(; (c=(*fmt))!=0; ++fmt){
|
|
|
- if( c!='%' ){
|
|
|
- int amt;
|
|
|
- bufpt = (char *)fmt;
|
|
|
- amt = 1;
|
|
|
- while( (c=(*++fmt))!='%' && c!=0 ) amt++;
|
|
|
- sqlite3StrAccumAppend(pAccum, bufpt, amt);
|
|
|
- if( c==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 */
|
|
|
- width = 0;
|
|
|
- if( c=='*' ){
|
|
|
- width = va_arg(ap,int);
|
|
|
- if( width<0 ){
|
|
|
- flag_leftjustify = 1;
|
|
|
- width = -width;
|
|
|
- }
|
|
|
- c = *++fmt;
|
|
|
- }else{
|
|
|
- while( c>='0' && c<='9' ){
|
|
|
- width = width*10 + c - '0';
|
|
|
- c = *++fmt;
|
|
|
- }
|
|
|
- }
|
|
|
- /* Get the precision */
|
|
|
- if( c=='.' ){
|
|
|
- precision = 0;
|
|
|
- c = *++fmt;
|
|
|
- if( c=='*' ){
|
|
|
- precision = va_arg(ap,int);
|
|
|
- if( precision<0 ) precision = -precision;
|
|
|
- c = *++fmt;
|
|
|
- }else{
|
|
|
- while( c>='0' && c<='9' ){
|
|
|
- precision = precision*10 + c - '0';
|
|
|
- c = *++fmt;
|
|
|
- }
|
|
|
- }
|
|
|
- }else{
|
|
|
- precision = -1;
|
|
|
- }
|
|
|
- /* 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( useExtended || (infop->flags & FLAG_INTERN)==0 ){
|
|
|
- xtype = infop->type;
|
|
|
- }else{
|
|
|
- return;
|
|
|
- }
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- zExtra = 0;
|
|
|
-
|
|
|
- /*
|
|
|
- ** 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( 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( 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 ){
|
|
|
- pAccum->accError = 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];
|
|
|
- }
|
|
|
- {
|
|
|
- register const char *cset; /* Use registers for speed */
|
|
|
- register int base;
|
|
|
- cset = &aDigits[infop->charset];
|
|
|
- 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:
|
|
|
- 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--;
|
|
|
- for(idx=precision, 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>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; }
|
|
|
- while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; }
|
|
|
- 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 ){
|
|
|
- if( prefix=='-' ){
|
|
|
- bufpt = "-Inf";
|
|
|
- }else if( prefix=='+' ){
|
|
|
- bufpt = "+Inf";
|
|
|
- }else{
|
|
|
- bufpt = "Inf";
|
|
|
- }
|
|
|
- length = sqlite3Strlen30(bufpt);
|
|
|
- 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)+precision+width > etBUFSIZE - 15 ){
|
|
|
- bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
|
|
|
- if( bufpt==0 ){
|
|
|
- pAccum->accError = 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:
|
|
|
- *(va_arg(ap,int*)) = pAccum->nChar;
|
|
|
- length = width = 0;
|
|
|
- break;
|
|
|
- case etPERCENT:
|
|
|
- buf[0] = '%';
|
|
|
- bufpt = buf;
|
|
|
- length = 1;
|
|
|
- break;
|
|
|
- case etCHARX:
|
|
|
- c = va_arg(ap,int);
|
|
|
- buf[0] = (char)c;
|
|
|
- if( precision>=0 ){
|
|
|
- for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
|
|
|
- length = precision;
|
|
|
- }else{
|
|
|
- length =1;
|
|
|
- }
|
|
|
- bufpt = buf;
|
|
|
- break;
|
|
|
- case etSTRING:
|
|
|
- case etDYNSTRING:
|
|
|
- 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:
|
|
|
- case etSQLESCAPE2:
|
|
|
- case etSQLESCAPE3: {
|
|
|
- int i, j, k, n, isnull;
|
|
|
- int needQuote;
|
|
|
- char ch;
|
|
|
- char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
|
|
- char *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 + 1 + needQuote*2;
|
|
|
- if( n>etBUFSIZE ){
|
|
|
- bufpt = zExtra = sqlite3Malloc( n );
|
|
|
- if( bufpt==0 ){
|
|
|
- pAccum->accError = 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*);
|
|
|
- if( pToken ){
|
|
|
- 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( k>=0 && k<pSrc->nSrc );
|
|
|
- if( pItem->zDatabase ){
|
|
|
- sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
|
|
|
- sqlite3StrAccumAppend(pAccum, ".", 1);
|
|
|
- }
|
|
|
- sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
|
|
|
- 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.
|
|
|
- */
|
|
|
- if( !flag_leftjustify ){
|
|
|
- register int nspace;
|
|
|
- nspace = width-length;
|
|
|
- if( nspace>0 ){
|
|
|
- sqlite3AppendSpace(pAccum, nspace);
|
|
|
- }
|
|
|
- }
|
|
|
- if( length>0 ){
|
|
|
- sqlite3StrAccumAppend(pAccum, bufpt, length);
|
|
|
- }
|
|
|
- if( flag_leftjustify ){
|
|
|
- register int nspace;
|
|
|
- nspace = width-length;
|
|
|
- if( nspace>0 ){
|
|
|
- sqlite3AppendSpace(pAccum, nspace);
|
|
|
- }
|
|
|
- }
|
|
|
- sqlite3_free(zExtra);
|
|
|
- }/* End for loop over the format string */
|
|
|
-} /* End of function */
|
|
|
-
|
|
|
-/*
|
|
|
-** Append N bytes of text from z to the StrAccum object.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
|
|
- assert( z!=0 || N==0 );
|
|
|
- if( p->accError ){
|
|
|
- testcase(p->accError==STRACCUM_TOOBIG);
|
|
|
- testcase(p->accError==STRACCUM_NOMEM);
|
|
|
- return;
|
|
|
- }
|
|
|
- assert( p->zText!=0 || p->nChar==0 );
|
|
|
- if( N<=0 ){
|
|
|
- if( N==0 || z[0]==0 ) return;
|
|
|
- N = sqlite3Strlen30(z);
|
|
|
- }
|
|
|
- if( p->nChar+N >= p->nAlloc ){
|
|
|
- char *zNew;
|
|
|
- if( !p->useMalloc ){
|
|
|
- p->accError = STRACCUM_TOOBIG;
|
|
|
- N = p->nAlloc - p->nChar - 1;
|
|
|
- if( N<=0 ){
|
|
|
- return;
|
|
|
- }
|
|
|
- }else{
|
|
|
- char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
|
|
- i64 szNew = p->nChar;
|
|
|
- szNew += N + 1;
|
|
|
- if( szNew > p->mxAlloc ){
|
|
|
- sqlite3StrAccumReset(p);
|
|
|
- p->accError = STRACCUM_TOOBIG;
|
|
|
- return;
|
|
|
- }else{
|
|
|
- p->nAlloc = (int)szNew;
|
|
|
- }
|
|
|
- if( p->useMalloc==1 ){
|
|
|
- zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
|
|
- }else{
|
|
|
- zNew = sqlite3_realloc(zOld, p->nAlloc);
|
|
|
- }
|
|
|
- if( zNew ){
|
|
|
- if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
|
|
- p->zText = zNew;
|
|
|
- }else{
|
|
|
- p->accError = STRACCUM_NOMEM;
|
|
|
- sqlite3StrAccumReset(p);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- assert( p->zText );
|
|
|
- memcpy(&p->zText[p->nChar], z, N);
|
|
|
- p->nChar += N;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 ){
|
|
|
- p->zText[p->nChar] = 0;
|
|
|
- if( p->useMalloc && p->zText==p->zBase ){
|
|
|
- if( p->useMalloc==1 ){
|
|
|
- p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
|
|
- }else{
|
|
|
- p->zText = sqlite3_malloc(p->nChar+1);
|
|
|
- }
|
|
|
- if( p->zText ){
|
|
|
- memcpy(p->zText, p->zBase, p->nChar+1);
|
|
|
- }else{
|
|
|
- p->accError = STRACCUM_NOMEM;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return p->zText;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Reset an StrAccum string. Reclaim all malloced memory.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
|
|
|
- if( p->zText!=p->zBase ){
|
|
|
- if( p->useMalloc==1 ){
|
|
|
- sqlite3DbFree(p->db, p->zText);
|
|
|
- }else{
|
|
|
- sqlite3_free(p->zText);
|
|
|
- }
|
|
|
- }
|
|
|
- p->zText = 0;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Initialize a string accumulator
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){
|
|
|
- p->zText = p->zBase = zBase;
|
|
|
- p->db = 0;
|
|
|
- p->nChar = 0;
|
|
|
- p->nAlloc = n;
|
|
|
- p->mxAlloc = mx;
|
|
|
- p->useMalloc = 1;
|
|
|
- p->accError = 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, zBase, sizeof(zBase),
|
|
|
- db->aLimit[SQLITE_LIMIT_LENGTH]);
|
|
|
- acc.db = db;
|
|
|
- sqlite3VXPrintf(&acc, 1, 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;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
|
|
|
-** the string and before returnning. This routine is intended to be used
|
|
|
-** to modify an existing string. For example:
|
|
|
-**
|
|
|
-** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
|
|
|
-**
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
|
|
|
- va_list ap;
|
|
|
- char *z;
|
|
|
- va_start(ap, zFormat);
|
|
|
- z = sqlite3VMPrintf(db, zFormat, ap);
|
|
|
- va_end(ap);
|
|
|
- sqlite3DbFree(db, zStr);
|
|
|
- return z;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Print into memory obtained from sqlite3_malloc(). Omit the internal
|
|
|
-** %-conversion extensions.
|
|
|
-*/
|
|
|
-SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
|
|
- char *z;
|
|
|
- char zBase[SQLITE_PRINT_BUF_SIZE];
|
|
|
- StrAccum acc;
|
|
|
-#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
- if( sqlite3_initialize() ) return 0;
|
|
|
-#endif
|
|
|
- sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
|
|
- acc.useMalloc = 2;
|
|
|
- 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 *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 *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
|
|
- StrAccum acc;
|
|
|
- if( n<=0 ) return zBuf;
|
|
|
- sqlite3StrAccumInit(&acc, zBuf, n, 0);
|
|
|
- acc.useMalloc = 0;
|
|
|
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
- return sqlite3StrAccumFinish(&acc);
|
|
|
-}
|
|
|
-SQLITE_API char *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.
|
|
|
-*/
|
|
|
-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, zMsg, sizeof(zMsg), 0);
|
|
|
- acc.useMalloc = 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 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)
|
|
|
-/*
|
|
|
-** 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, zBuf, sizeof(zBuf), 0);
|
|
|
- acc.useMalloc = 0;
|
|
|
- va_start(ap,zFormat);
|
|
|
- sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
- va_end(ap);
|
|
|
- sqlite3StrAccumFinish(&acc);
|
|
|
- fprintf(stdout,"%s", zBuf);
|
|
|
- fflush(stdout);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifndef SQLITE_OMIT_TRACE
|
|
|
-/*
|
|
|
-** variable-argument wrapper around sqlite3VXPrintf().
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
|
|
- va_list ap;
|
|
|
- va_start(ap,zFormat);
|
|
|
- sqlite3VXPrintf(p, 1, zFormat, ap);
|
|
|
- va_end(ap);
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-/************** End of printf.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.
|
|
|
-*/
|
|
|
-
|
|
|
-
|
|
|
-/* 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 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 = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
|
|
- sqlite3_mutex_enter(mutex);
|
|
|
-#endif
|
|
|
-
|
|
|
- /* 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;
|
|
|
- }
|
|
|
-
|
|
|
- while( N-- ){
|
|
|
- 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];
|
|
|
- }
|
|
|
- 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)
|
|
|
- );
|
|
|
-}
|
|
|
-SQLITE_PRIVATE void sqlite3PrngResetState(void){
|
|
|
- GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
|
|
|
-}
|
|
|
-#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
|
|
-
|
|
|
-/************** End of random.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 <assert.h> */
|
|
|
-
|
|
|
-#ifndef SQLITE_AMALGAMATION
|
|
|
-/*
|
|
|
-** The following constant value is used by the SQLITE_BIGENDIAN and
|
|
|
-** SQLITE_LITTLEENDIAN macros.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE const int sqlite3one = 1;
|
|
|
-#endif /* SQLITE_AMALGAMATION */
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 an infinite number of different UTF8 encodings
|
|
|
-** for unicode values 0x80 and greater. It do 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 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 );
|
|
|
-
|
|
|
- sqlite3VdbeMemRelease(pMem);
|
|
|
- pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
|
|
|
- pMem->enc = desiredEnc;
|
|
|
- pMem->flags |= (MEM_Term|MEM_Dyn);
|
|
|
- pMem->z = (char*)zOut;
|
|
|
- pMem->zMalloc = 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.flags & MEM_Dyn)!=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 <stdarg.h> */
|
|
|
-#ifdef 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
|
|
|
-
|
|
|
-#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 !defined(SQLITE_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 defined(SQLITE_HAVE_ISNAN) */
|
|
|
- rc = isnan(x);
|
|
|
-#endif /* SQLITE_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){
|
|
|
- const char *z2 = z;
|
|
|
- if( z==0 ) return 0;
|
|
|
- while( *z2 ){ z2++; }
|
|
|
- return 0x3fffffff & (int)(z2 - z);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
|
|
|
- if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
|
|
|
- db->errCode = err_code;
|
|
|
- if( zFormat ){
|
|
|
- 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);
|
|
|
- }else{
|
|
|
- sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 whilst
|
|
|
-** 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().
|
|
|
-** Function sqlite3Error() 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 identifers. 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 sqlite3_stricmp(const char *zLeft, const char *zRight){
|
|
|
- register unsigned char *a, *b;
|
|
|
- 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 sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
|
|
- register unsigned char *a, *b;
|
|
|
- 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.
|
|
|
-**
|
|
|
-** 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 9223372036854665808, return 2. This special
|
|
|
-** case is broken out because while 9223372036854665808 cannot be a
|
|
|
-** signed 64-bit integer, its negative -9223372036854665808 can be.
|
|
|
-**
|
|
|
-** If zNum is too big for a 64-bit integer and is not
|
|
|
-** 9223372036854665808 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 = SMALLEST_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 );
|
|
|
- assert( (*pNum)==SMALLEST_INT64 );
|
|
|
- return neg ? 0 : 2;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** If zNum represents an integer that will fit in 32-bits, then set
|
|
|
-** *pValue to that integer and return true. Otherwise return false.
|
|
|
-**
|
|
|
-** 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++;
|
|
|
- }
|
|
|
- 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.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE int sqlite3PutVarint(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;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** This routine is a faster version of sqlite3PutVarint() that only
|
|
|
-** works for 32-bit positive integers and which is optimized for
|
|
|
-** the common case of small integers. A MACRO version, putVarint32,
|
|
|
-** 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 int sqlite3PutVarint32(unsigned char *p, u32 v){
|
|
|
-#ifndef putVarint32
|
|
|
- if( (v & ~0x7f)==0 ){
|
|
|
- p[0] = v;
|
|
|
- return 1;
|
|
|
- }
|
|
|
-#endif
|
|
|
- if( (v & ~0x3fff)==0 ){
|
|
|
- p[0] = (u8)((v>>7) | 0x80);
|
|
|
- p[1] = (u8)(v & 0x7f);
|
|
|
- return 2;
|
|
|
- }
|
|
|
- return sqlite3PutVarint(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 in calc'ing 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 = 0;
|
|
|
- do{
|
|
|
- i++;
|
|
|
- v >>= 7;
|
|
|
- }while( v!=0 && ALWAYS(i<9) );
|
|
|
- return i;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** Read or write a four-byte big-endian integer value.
|
|
|
-*/
|
|
|
-SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
|
|
|
- return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
|
|
-}
|
|
|
-SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
|
|
|
- p[0] = (u8)(v>>24);
|
|
|
- p[1] = (u8)(v>>16);
|
|
|
- p[2] = (u8)(v>>8);
|
|
|
- p[3] = (u8)v;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
- *pA += iB;
|
|
|
- }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*iB1 != 0 ) return 1;
|
|
|
- assert( iA1*iB0==0 || iA0*iB1==0 );
|
|
|
- r = iA1*iB0 + iA0*iB1;
|
|
|
- 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 a
|
|
|
-** good approximatation 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 (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 <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, int nKey){
|
|
|
- int h = 0;
|
|
|
- assert( nKey>=0 );
|
|
|
- while( nKey > 0 ){
|
|
|
- h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
|
|
- nKey--;
|
|
|
- }
|
|
|
- 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, elem->nKey) % 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 has
|
|
|
-** already been computed and is passed as the 4th parameter.
|
|
|
-*/
|
|
|
-static HashElem *findElementGivenHash(
|
|
|
- const Hash *pH, /* The pH to be searched */
|
|
|
- const char *pKey, /* The key we are searching for */
|
|
|
- int nKey, /* Bytes in key (not counting zero terminator) */
|
|
|
- unsigned int h /* The hash for this key. */
|
|
|
-){
|
|
|
- HashElem *elem; /* Used to loop thru the element list */
|
|
|
- int count; /* Number of elements left to test */
|
|
|
-
|
|
|
- if( pH->ht ){
|
|
|
- struct _ht *pEntry = &pH->ht[h];
|
|
|
- elem = pEntry->chain;
|
|
|
- count = pEntry->count;
|
|
|
- }else{
|
|
|
- elem = pH->first;
|
|
|
- count = pH->count;
|
|
|
- }
|
|
|
- while( count-- && ALWAYS(elem) ){
|
|
|
- if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==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,nKey. 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, int nKey){
|
|
|
- HashElem *elem; /* The element that matches key */
|
|
|
- unsigned int h; /* A hash on key */
|
|
|
-
|
|
|
- assert( pH!=0 );
|
|
|
- assert( pKey!=0 );
|
|
|
- assert( nKey>=0 );
|
|
|
- if( pH->ht ){
|
|
|
- h = strHash(pKey, nKey) % pH->htsize;
|
|
|
- }else{
|
|
|
- h = 0;
|
|
|
- }
|
|
|
- elem = findElementGivenHash(pH, pKey, nKey, h);
|
|
|
- return elem ? elem->data : 0;
|
|
|
-}
|
|
|
-
|
|
|
-/* Insert an element into the hash table pH. The key is pKey,nKey
|
|
|
-** 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, int nKey, 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 );
|
|
|
- assert( nKey>=0 );
|
|
|
- if( pH->htsize ){
|
|
|
- h = strHash(pKey, nKey) % pH->htsize;
|
|
|
- }else{
|
|
|
- h = 0;
|
|
|
- }
|
|
|
- elem = findElementGivenHash(pH,pKey,nKey,h);
|
|
|
- if( elem ){
|
|
|
- void *old_data = elem->data;
|
|
|
- if( data==0 ){
|
|
|
- removeElementGivenHash(pH,elem,h);
|
|
|
- }else{
|
|
|
- elem->data = data;
|
|
|
- elem->pKey = pKey;
|
|
|
- assert(nKey==elem->nKey);
|
|
|
- }
|
|
|
- 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->nKey = nKey;
|
|
|
- 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, nKey) % pH->htsize;
|
|
|
- }
|
|
|
- }
|
|
|
- if( pH->ht ){
|
|
|
- insertElement(pH, &pH->ht[h], new_elem);
|
|
|
- }else{
|
|
|
- insertElement(pH, 0, new_elem);
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/************** End of hash.c ************************************************/
|
|
|
-/************** Begin file opcodes.c *****************************************/
|
|
|
-/* Automatically generated. Do not edit */
|
|
|
-/* See the mkopcodec.awk script for details. */
|
|
|
-#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
|
|
|
-SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
|
|
|
- static const char *const azName[] = { "?",
|
|
|
- /* 1 */ "Function",
|
|
|
- /* 2 */ "Savepoint",
|
|
|
- /* 3 */ "AutoCommit",
|
|
|
- /* 4 */ "Transaction",
|
|
|
- /* 5 */ "SorterNext",
|
|
|
- /* 6 */ "Prev",
|
|
|
- /* 7 */ "Next",
|
|
|
- /* 8 */ "AggStep",
|
|
|
- /* 9 */ "Checkpoint",
|
|
|
- /* 10 */ "JournalMode",
|
|
|
- /* 11 */ "Vacuum",
|
|
|
- /* 12 */ "VFilter",
|
|
|
- /* 13 */ "VUpdate",
|
|
|
- /* 14 */ "Goto",
|
|
|
- /* 15 */ "Gosub",
|
|
|
- /* 16 */ "Return",
|
|
|
- /* 17 */ "Yield",
|
|
|
- /* 18 */ "HaltIfNull",
|
|
|
- /* 19 */ "Not",
|
|
|
- /* 20 */ "Halt",
|
|
|
- /* 21 */ "Integer",
|
|
|
- /* 22 */ "Int64",
|
|
|
- /* 23 */ "String",
|
|
|
- /* 24 */ "Null",
|
|
|
- /* 25 */ "Blob",
|
|
|
- /* 26 */ "Variable",
|
|
|
- /* 27 */ "Move",
|
|
|
- /* 28 */ "Copy",
|
|
|
- /* 29 */ "SCopy",
|
|
|
- /* 30 */ "ResultRow",
|
|
|
- /* 31 */ "CollSeq",
|
|
|
- /* 32 */ "AddImm",
|
|
|
- /* 33 */ "MustBeInt",
|
|
|
- /* 34 */ "RealAffinity",
|
|
|
- /* 35 */ "Permutation",
|
|
|
- /* 36 */ "Compare",
|
|
|
- /* 37 */ "Jump",
|
|
|
- /* 38 */ "Once",
|
|
|
- /* 39 */ "If",
|
|
|
- /* 40 */ "IfNot",
|
|
|
- /* 41 */ "Column",
|
|
|
- /* 42 */ "Affinity",
|
|
|
- /* 43 */ "MakeRecord",
|
|
|
- /* 44 */ "Count",
|
|
|
- /* 45 */ "ReadCookie",
|
|
|
- /* 46 */ "SetCookie",
|
|
|
- /* 47 */ "VerifyCookie",
|
|
|
- /* 48 */ "OpenRead",
|
|
|
- /* 49 */ "OpenWrite",
|
|
|
- /* 50 */ "OpenAutoindex",
|
|
|
- /* 51 */ "OpenEphemeral",
|
|
|
- /* 52 */ "SorterOpen",
|
|
|
- /* 53 */ "OpenPseudo",
|
|
|
- /* 54 */ "Close",
|
|
|
- /* 55 */ "SeekLt",
|
|
|
- /* 56 */ "SeekLe",
|
|
|
- /* 57 */ "SeekGe",
|
|
|
- /* 58 */ "SeekGt",
|
|
|
- /* 59 */ "Seek",
|
|
|
- /* 60 */ "NotFound",
|
|
|
- /* 61 */ "Found",
|
|
|
- /* 62 */ "IsUnique",
|
|
|
- /* 63 */ "NotExists",
|
|
|
- /* 64 */ "Sequence",
|
|
|
- /* 65 */ "NewRowid",
|
|
|
- /* 66 */ "Insert",
|
|
|
- /* 67 */ "InsertInt",
|
|
|
- /* 68 */ "Or",
|
|
|
- /* 69 */ "And",
|
|
|
- /* 70 */ "Delete",
|
|
|
- /* 71 */ "ResetCount",
|
|
|
- /* 72 */ "SorterCompare",
|
|
|
- /* 73 */ "IsNull",
|
|
|
- /* 74 */ "NotNull",
|
|
|
- /* 75 */ "Ne",
|
|
|
- /* 76 */ "Eq",
|
|
|
- /* 77 */ "Gt",
|
|
|
- /* 78 */ "Le",
|
|
|
- /* 79 */ "Lt",
|
|
|
- /* 80 */ "Ge",
|
|
|
- /* 81 */ "SorterData",
|
|
|
- /* 82 */ "BitAnd",
|
|
|
- /* 83 */ "BitOr",
|
|
|
- /* 84 */ "ShiftLeft",
|
|
|
- /* 85 */ "ShiftRight",
|
|
|
- /* 86 */ "Add",
|
|
|
- /* 87 */ "Subtract",
|
|
|
- /* 88 */ "Multiply",
|
|
|
- /* 89 */ "Divide",
|
|
|
- /* 90 */ "Remainder",
|
|
|
- /* 91 */ "Concat",
|
|
|
- /* 92 */ "RowKey",
|
|
|
- /* 93 */ "BitNot",
|
|
|
- /* 94 */ "String8",
|
|
|
- /* 95 */ "RowData",
|
|
|
- /* 96 */ "Rowid",
|
|
|
- /* 97 */ "NullRow",
|
|
|
- /* 98 */ "Last",
|
|
|
- /* 99 */ "SorterSort",
|
|
|
- /* 100 */ "Sort",
|
|
|
- /* 101 */ "Rewind",
|
|
|
- /* 102 */ "SorterInsert",
|
|
|
- /* 103 */ "IdxInsert",
|
|
|
- /* 104 */ "IdxDelete",
|
|
|
- /* 105 */ "IdxRowid",
|
|
|
- /* 106 */ "IdxLT",
|
|
|
- /* 107 */ "IdxGE",
|
|
|
- /* 108 */ "Destroy",
|
|
|
- /* 109 */ "Clear",
|
|
|
- /* 110 */ "CreateIndex",
|
|
|
- /* 111 */ "CreateTable",
|
|
|
- /* 112 */ "ParseSchema",
|
|
|
- /* 113 */ "LoadAnalysis",
|
|
|
- /* 114 */ "DropTable",
|
|
|
- /* 115 */ "DropIndex",
|
|
|
- /* 116 */ "DropTrigger",
|
|
|
- /* 117 */ "IntegrityCk",
|
|
|
- /* 118 */ "RowSetAdd",
|
|
|
- /* 119 */ "RowSetRead",
|
|
|
- /* 120 */ "RowSetTest",
|
|
|
- /* 121 */ "Program",
|
|
|
- /* 122 */ "Param",
|
|
|
- /* 123 */ "FkCounter",
|
|
|
- /* 124 */ "FkIfZero",
|
|
|
- /* 125 */ "MemMax",
|
|
|
- /* 126 */ "IfPos",
|
|
|
- /* 127 */ "IfNeg",
|
|
|
- /* 128 */ "IfZero",
|
|
|
- /* 129 */ "AggFinal",
|
|
|
- /* 130 */ "Real",
|
|
|
- /* 131 */ "IncrVacuum",
|
|
|
- /* 132 */ "Expire",
|
|
|
- /* 133 */ "TableLock",
|
|
|
- /* 134 */ "VBegin",
|
|
|
- /* 135 */ "VCreate",
|
|
|
- /* 136 */ "VDestroy",
|
|
|
- /* 137 */ "VOpen",
|
|
|
- /* 138 */ "VColumn",
|
|
|
- /* 139 */ "VNext",
|
|
|
- /* 140 */ "VRename",
|
|
|
- /* 141 */ "ToText",
|
|
|
- /* 142 */ "ToBlob",
|
|
|
- /* 143 */ "ToNumeric",
|
|
|
- /* 144 */ "ToInt",
|
|
|
- /* 145 */ "ToReal",
|
|
|
- /* 146 */ "Pagecount",
|
|
|
- /* 147 */ "MaxPgcnt",
|
|
|
- /* 148 */ "Trace",
|
|
|
- /* 149 */ "Noop",
|
|
|
- /* 150 */ "Explain",
|
|
|
- };
|
|
|
- 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().
|
|
|
-*/
|
|
|
-#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
|
|
|
-
|
|
|
-/*
|
|
|
-** Define the OS_VXWORKS pre-processor macro to 1 if building on
|
|
|
-** vxworks, or 0 otherwise.
|
|
|
-*/
|
|
|
-#ifndef OS_VXWORKS
|
|
|
-# if defined(__RTP__) || defined(_WRS_KERNEL)
|
|
|
-# define OS_VXWORKS 1
|
|
|
-# else
|
|
|
-# define OS_VXWORKS 0
|
|
|
-# endif
|
|
|
-#endif
|
|
|
-
|
|
|
-/*
|
|
|
-** These #defines should enable >2GB file support on Posix if the
|
|
|
-** underlying operating system supports it. If the OS lacks
|
|
|
-** large file support, these should be no-ops.
|
|
|
-**
|
|
|
-** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch
|
|
|
-** on the compiler command line. This is necessary if you are compiling
|
|
|
-** on a recent machine (ex: RedHat 7.2) but you want your code to work
|
|
|
-** on an older machine (ex: RedHat 6.0). If you compile on RedHat 7.2
|
|
|
-** without this option, LFS is enable. But LFS does not exist in the kernel
|
|
|
-** in RedHat 6.0, so the code won't work. Hence, for maximum binary
|
|
|
-** portability you should omit LFS.
|
|
|
-**
|
|
|
-** The previous paragraph was written in 2005. (This paragraph is written
|
|
|
-** on 2008-11-28.) These days, all Linux kernels support large files, so
|
|
|
-** you should probably leave LFS enabled. But some embedded platforms might
|
|
|
-** lack LFS in which case the SQLITE_DISABLE_LFS macro might still be useful.
|
|
|
-*/
|
|
|
-#ifndef SQLITE_DISABLE_LFS
|
|
|
-# define _LARGE_FILE 1
|
|
|
-# ifndef _FILE_OFFSET_BITS
|
|
|
-# define _FILE_OFFSET_BITS 64
|
|
|
-# endif
|
|
|
-# define _LARGEFILE_SOURCE 1
|
|
|
-#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>
|
|
|
-# if OS_VXWORKS
|
|
|
-# include <semaphore.h>
|
|
|
-# include <limits.h>
|
|
|
-# else
|
|
|
-# include <sys/file.h>
|
|
|
-# include <sys/param.h>
|
|
|
-# endif
|
|
|
-#endif /* SQLITE_ENABLE_LOCKING_STYLE */
|
|
|
-
|
|
|
-#if defined(__APPLE__) || (SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS)
|
|
|
-# 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
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-};
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
-#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-
|
|
|
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
|
|
-# ifndef SQLITE_DEBUG_OS_TRACE
|
|
|
-# define SQLITE_DEBUG_OS_TRACE 0
|
|
|
-# endif
|
|
|
- int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
|
|
|
-# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
|
|
|
-#else
|
|
|
-# define OSTRACE(X)
|
|
|
-#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
|
|
|
-
|
|
|
-/*
|
|
|
-** 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);
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 posixFchown(int fd, uid_t uid, gid_t gid){
|
|
|
- return geteuid() ? 0 : fchown(fd,uid,gid);
|
|
|
-}
|
|
|
-
|
|
|
-/* Forward reference */
|
|
|
-static int openDirectory(const char*, int*);
|
|
|
-
|
|
|
-/*
|
|
|
-** 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)posixFchown, 0 },
|
|
|
-#define osFchown ((int(*)(int,uid_t,gid_t))aSyscall[20].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[21].pCurrent)
|
|
|
-
|
|
|
- { "munmap", (sqlite3_syscall_ptr)munmap, 0 },
|
|
|
-#define osMunmap ((void*(*)(void*,size_t))aSyscall[22].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[23].pCurrent)
|
|
|
-#endif
|
|
|
-
|
|
|
-}; /* End of the overrideable system calls */
|
|
|
-
|
|
|
-/*
|
|
|
-** 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_MASTER));
|
|
|
-}
|
|
|
-static void unixLeaveMutex(void){
|
|
|
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
|
-}
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
-static int unixMutexHeld(void) {
|
|
|
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
|
-}
|
|
|
-#endif
|
|
|
-
|
|
|
-
|
|
|
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
|
|
-/*
|
|
|
-** Helper function for printing out trace information from debugging
|
|
|
-** binaries. This returns the string represetation 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
|
|
|
-*/
|
|
|
-static int robust_ftruncate(int h, sqlite3_int64 sz){
|
|
|
- int rc;
|
|
|
- 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) {
|
|
|
- switch (posixError) {
|
|
|
-#if 0
|
|
|
- /* At one point this code was not commented out. In theory, this branch
|
|
|
- ** should never be hit, as this function should only be called after
|
|
|
- ** a locking-related function (i.e. fcntl()) has returned non-zero with
|
|
|
- ** the value of errno as the first argument. Since a system call has failed,
|
|
|
- ** errno should be non-zero.
|
|
|
- **
|
|
|
- ** Despite this, if errno really is zero, we still don't want to return
|
|
|
- ** SQLITE_OK. The system call failed, and *some* SQLite error should be
|
|
|
- ** propagated back to the caller. Commenting this branch out means errno==0
|
|
|
- ** will be handled by the "default:" case below.
|
|
|
- */
|
|
|
- case 0:
|
|
|
- return SQLITE_OK;
|
|
|
-#endif
|
|
|
-
|
|
|
- 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 EACCES:
|
|
|
- /* EACCES is like EAGAIN during locking operations, but not any other time*/
|
|
|
- if( (sqliteIOErr == SQLITE_IOERR_LOCK) ||
|
|
|
- (sqliteIOErr == SQLITE_IOERR_UNLOCK) ||
|
|
|
- (sqliteIOErr == SQLITE_IOERR_RDLOCK) ||
|
|
|
- (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK) ){
|
|
|
- return SQLITE_BUSY;
|
|
|
- }
|
|
|
- /* else fall through */
|
|
|
- case EPERM:
|
|
|
- return SQLITE_PERM;
|
|
|
-
|
|
|
- /* EDEADLK is only possible if a call to fcntl(F_SETLKW) is made. And
|
|
|
- ** this module never makes such a call. And the code in SQLite itself
|
|
|
- ** asserts that SQLITE_IOERR_BLOCKED is never returned. For these reasons
|
|
|
- ** this case is also commented out. If the system does set errno to EDEADLK,
|
|
|
- ** the default SQLITE_IOERR_XXX code will be returned. */
|
|
|
-#if 0
|
|
|
- case EDEADLK:
|
|
|
- return SQLITE_IOERR_BLOCKED;
|
|
|
-#endif
|
|
|
-
|
|
|
-#if EOPNOTSUPP!=ENOTSUP
|
|
|
- case EOPNOTSUPP:
|
|
|
- /* something went terribly awry, unless during file system support
|
|
|
- * introspection, in which it actually means what it says */
|
|
|
-#endif
|
|
|
-#ifdef ENOTSUP
|
|
|
- case ENOTSUP:
|
|
|
- /* invalid fd, unless during file system support introspection, in which
|
|
|
- * it actually means what it says */
|
|
|
-#endif
|
|
|
- case EIO:
|
|
|
- case EBADF:
|
|
|
- case EINVAL:
|
|
|
- case ENOTCONN:
|
|
|
- case ENODEV:
|
|
|
- case ENXIO:
|
|
|
- case ENOENT:
|
|
|
-#ifdef ESTALE /* ESTALE is not defined on Interix systems */
|
|
|
- case ESTALE:
|
|
|
-#endif
|
|
|
- case ENOSYS:
|
|
|
- /* these should force the client to close the file and reconnect */
|
|
|
-
|
|
|
- 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_malloc( 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 - unixLogError_x(), 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);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 ){
|
|
|
- pFile->lastErrno = errno;
|
|
|
-#ifdef EOVERFLOW
|
|
|
- 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 ){
|
|
|
- pFile->lastErrno = errno;
|
|
|
- return SQLITE_IOERR;
|
|
|
- }
|
|
|
- rc = osFstat(fd, &statbuf);
|
|
|
- if( rc!=0 ){
|
|
|
- pFile->lastErrno = 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_malloc( 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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
- if( pFile->ctrlFlags & UNIXFILE_WARNED ){
|
|
|
- /* One or more of the following warnings have already been issued. Do not
|
|
|
- ** repeat them so as not to clutter the error log */
|
|
|
- return;
|
|
|
- }
|
|
|
- rc = osFstat(pFile->h, &buf);
|
|
|
- if( rc!=0 ){
|
|
|
- sqlite3_log(SQLITE_WARNING, "cannot fstat db file %s", pFile->zPath);
|
|
|
- pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
|
- return;
|
|
|
- }
|
|
|
- if( buf.st_nlink==0 && (pFile->ctrlFlags & UNIXFILE_DELETE)==0 ){
|
|
|
- sqlite3_log(SQLITE_WARNING, "file unlinked while open: %s", pFile->zPath);
|
|
|
- pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
|
- return;
|
|
|
- }
|
|
|
- if( buf.st_nlink>1 ){
|
|
|
- sqlite3_log(SQLITE_WARNING, "multiple links to file: %s", pFile->zPath);
|
|
|
- pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
|
- return;
|
|
|
- }
|
|
|
- if( pFile->pInode!=0
|
|
|
- && ((rc = osStat(pFile->zPath, &buf))!=0
|
|
|
- || buf.st_ino!=pFile->pInode->fileId.ino)
|
|
|
- ){
|
|
|
- sqlite3_log(SQLITE_WARNING, "file renamed while open: %s", pFile->zPath);
|
|
|
- pFile->ctrlFlags |= UNIXFILE_WARNED;
|
|
|
- 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 );
|
|
|
- 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;
|
|
|
- pFile->lastErrno = 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)!=0 || pInode->bProcessLock)
|
|
|
- && ((pFile->ctrlFlags & UNIXFILE_RDONLY)==0)
|
|
|
- ){
|
|
|
- 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 , getpid()));
|
|
|
-
|
|
|
- /* 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 ){
|
|
|
- pFile->lastErrno = 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 ){
|
|
|
- pFile->lastErrno = 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 ){
|
|
|
- pFile->lastErrno = 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,
|
|
|
- getpid()));
|
|
|
-
|
|
|
- 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;
|
|
|
- if( IS_LOCK_ERROR(rc) ){
|
|
|
- pFile->lastErrno = 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) ){
|
|
|
- pFile->lastErrno = 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;
|
|
|
- if( IS_LOCK_ERROR(rc) ){
|
|
|
- pFile->lastErrno = 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;
|
|
|
- pFile->lastErrno = 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;
|
|
|
- pFile->lastErrno = 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;
|
|
|
- pFile->lastErrno = 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){
|
|
|
- assert( eFileLock==SHARED_LOCK || ((unixFile *)id)->nFetchOut==0 );
|
|
|
- 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
|
|
|
- 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 );
|
|
|
-
|
|
|
- /* Check if a thread in this process holds such a lock */
|
|
|
- if( pFile->eFileLock>SHARED_LOCK ){
|
|
|
- /* Either this connection or some other connection in the same process
|
|
|
- ** holds a lock on the file. No need to check further. */
|
|
|
- reserved = 1;
|
|
|
- }else{
|
|
|
- /* The lock is held if and only if the lockfile exists */
|
|
|
- const char *zLockFile = (const char*)pFile->lockingContext;
|
|
|
- reserved = osAccess(zLockFile, 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( IS_LOCK_ERROR(rc) ){
|
|
|
- pFile->lastErrno = 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, getpid()));
|
|
|
- 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 && errno==ENOTDIR ) rc = osUnlink(zLockFile);
|
|
|
- if( rc<0 ){
|
|
|
- int tErrno = errno;
|
|
|
- rc = 0;
|
|
|
- if( ENOENT != tErrno ){
|
|
|
- rc = SQLITE_IOERR_UNLOCK;
|
|
|
- }
|
|
|
- if( IS_LOCK_ERROR(rc) ){
|
|
|
- pFile->lastErrno = 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) {
|
|
|
- int rc = SQLITE_OK;
|
|
|
- if( id ){
|
|
|
- unixFile *pFile = (unixFile*)id;
|
|
|
- dotlockUnlock(id, NO_LOCK);
|
|
|
- sqlite3_free(pFile->lockingContext);
|
|
|
- rc = closeUnixFile(id);
|
|
|
- }
|
|
|
- return rc;
|
|
|
-}
|
|
|
-/****************** 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 or if
|
|
|
-** compiling for VXWORKS.
|
|
|
-*/
|
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
- if( IS_LOCK_ERROR(lrc) ){
|
|
|
- pFile->lastErrno = 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) ){
|
|
|
- pFile->lastErrno = 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) ){
|
|
|
- pFile->lastErrno = 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, getpid()));
|
|
|
- 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) {
|
|
|
- int rc = SQLITE_OK;
|
|
|
- if( id ){
|
|
|
- flockUnlock(id, NO_LOCK);
|
|
|
- rc = closeUnixFile(id);
|
|
|
- }
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-#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 semCheckReservedLock(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;
|
|
|
- struct stat statBuf;
|
|
|
-
|
|
|
- if( sem_trywait(pSem)==-1 ){
|
|
|
- int tErrno = errno;
|
|
|
- if( EAGAIN != tErrno ){
|
|
|
- rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK);
|
|
|
- pFile->lastErrno = 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 semLock(sqlite3_file *id, int eFileLock) {
|
|
|
- unixFile *pFile = (unixFile*)id;
|
|
|
- int fd;
|
|
|
- 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 semUnlock(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, getpid()));
|
|
|
- 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) ){
|
|
|
- pFile->lastErrno = tErrno;
|
|
|
- }
|
|
|
- return rc;
|
|
|
- }
|
|
|
- pFile->eFileLock = NO_LOCK;
|
|
|
- return SQLITE_OK;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
- ** Close a file.
|
|
|
- */
|
|
|
-static int semClose(sqlite3_file *id) {
|
|
|
- if( id ){
|
|
|
- unixFile *pFile = (unixFile*)id;
|
|
|
- semUnlock(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) ){
|
|
|
- pFile->lastErrno = 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 , getpid()));
|
|
|
-
|
|
|
- /* 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) ) {
|
|
|
- pFile->lastErrno = 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,
|
|
|
- getpid()));
|
|
|
-
|
|
|
- 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;
|
|
|
- if( id ){
|
|
|
- unixFile *pFile = (unixFile*)id;
|
|
|
- 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
|
|
|
-** any 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 );
|
|
|
- cnt &= 0x1ffff;
|
|
|
- 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-- );
|
|
|
- if( newOffset!=offset ){
|
|
|
- if( newOffset == -1 ){
|
|
|
- ((unixFile*)id)->lastErrno = errno;
|
|
|
- }else{
|
|
|
- ((unixFile*)id)->lastErrno = 0;
|
|
|
- }
|
|
|
- return -1;
|
|
|
- }
|
|
|
- got = osRead(id->h, pBuf, cnt);
|
|
|
-#endif
|
|
|
- if( got==cnt ) break;
|
|
|
- if( got<0 ){
|
|
|
- if( errno==EINTR ){ got = 1; continue; }
|
|
|
- prior = 0;
|
|
|
- ((unixFile*)id)->lastErrno = 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 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{
|
|
|
- pFile->lastErrno = 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 );
|
|
|
- nBuf &= 0x1ffff;
|
|
|
- TIMER_START;
|
|
|
-
|
|
|
-#if defined(USE_PREAD)
|
|
|
- do{ rc = osPwrite(fd, pBuf, nBuf, iOff); }while( rc<0 && errno==EINTR );
|
|
|
-#elif defined(USE_PREAD64)
|
|
|
- do{ rc = osPwrite64(fd, pBuf, nBuf, iOff);}while( rc<0 && errno==EINTR);
|
|
|
-#else
|
|
|
- do{
|
|
|
- i64 iSeek = lseek(fd, iOff, SEEK_SET);
|
|
|
- SimulateIOError( iSeek-- );
|
|
|
-
|
|
|
- if( iSeek!=iOff ){
|
|
|
- if( piErrno ) *piErrno = (iSeek==-1 ? errno : 0);
|
|
|
- return -1;
|
|
|
- }
|
|
|
- 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 ) *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 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( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
|
|
|
- amt -= wrote;
|
|
|
- offset += wrote;
|
|
|
- pBuf = &((char*)pBuf)[wrote];
|
|
|
- }
|
|
|
- SimulateIOError(( wrote=(-1), amt=1 ));
|
|
|
- SimulateDiskfullError(( wrote=0, amt=1 ));
|
|
|
-
|
|
|
- if( amt>0 ){
|
|
|
- if( wrote<0 && pFile->lastErrno!=ENOSPC ){
|
|
|
- /* lastErrno set by seekAndWrite */
|
|
|
- return SQLITE_IOERR_WRITE;
|
|
|
- }else{
|
|
|
- pFile->lastErrno = 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
|
|
|
-*/
|
|
|
-#if !defined(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
|
|
|
- */
|
|
|
-#ifdef SQLITE_NO_SYNC
|
|
|
- rc = SQLITE_OK;
|
|
|
-#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>1 && zDirname[ii]!='/'; ii--);
|
|
|
- if( ii>0 ){
|
|
|
- zDirname[ii] = '\0';
|
|
|
- fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
|
|
|
- if( fd>=0 ){
|
|
|
- OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
|
|
|
- }
|
|
|
- }
|
|
|
- *pFd = fd;
|
|
|
- return (fd>=0?SQLITE_OK:unixLogError(SQLITE_CANTOPEN_BKPT, "open", 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 ){
|
|
|
- pFile->lastErrno = 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 && dirfd>=0 ){
|
|
|
- full_fsync(dirfd, 0, 0);
|
|
|
- robust_close(pFile, dirfd, __LINE__);
|
|
|
- }else if( 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, (off_t)nByte);
|
|
|
- if( rc ){
|
|
|
- pFile->lastErrno = 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 ){
|
|
|
- ((unixFile*)id)->lastErrno = 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. First use
|
|
|
- ** ftruncate() to set the file size, then write a single byte to
|
|
|
- ** the last byte in each block within the extended region. This
|
|
|
- ** is the same technique used by glibc to implement posix_fallocate()
|
|
|
- ** on systems that do not have a real fallocate() system call.
|
|
|
- */
|
|
|
- int nBlk = buf.st_blksize; /* File-system block size */
|
|
|
- i64 iWrite; /* Next offset to write to */
|
|
|
-
|
|
|
- if( robust_ftruncate(pFile->h, nSize) ){
|
|
|
- pFile->lastErrno = errno;
|
|
|
- return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
|
|
- }
|
|
|
- iWrite = ((buf.st_size + 2*nBlk - 1)/nBlk)*nBlk-1;
|
|
|
- while( iWrite<nSize ){
|
|
|
- int nWrite = seekAndWrite(pFile, iWrite, "", 1);
|
|
|
- if( nWrite!=1 ) return SQLITE_IOERR_WRITE;
|
|
|
- iWrite += nBlk;
|
|
|
- }
|
|
|
-#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) ){
|
|
|
- pFile->lastErrno = errno;
|
|
|
- return unixLogError(SQLITE_IOERR_TRUNCATE, "ftruncate", pFile->zPath);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- rc = unixMapfile(pFile, nByte);
|
|
|
- return rc;
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
- return SQLITE_OK;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** If *pArg is inititially 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_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_malloc( pFile->pVfs->mxPathname );
|
|
|
- if( zTFile ){
|
|
|
- unixGetTempname(pFile->pVfs->mxPathname, zTFile);
|
|
|
- *(char**)pArg = zTFile;
|
|
|
- }
|
|
|
- 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_SET_LOCKPROXYFILE:
|
|
|
- case SQLITE_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 contraversial 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;
|
|
|
-}
|
|
|
-
|
|
|
-#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(
|
|
|
- unixShmNode *pShmNode, /* Apply locks to this open shared-memory segment */
|
|
|
- int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */
|
|
|
- int ofst, /* First byte of the locking range */
|
|
|
- int n /* Number of bytes to lock */
|
|
|
-){
|
|
|
- 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 */
|
|
|
- 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 = (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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 && p->nRef==0 ){
|
|
|
- int i;
|
|
|
- assert( p->pInode==pFd->pInode );
|
|
|
- sqlite3_mutex_free(p->mutex);
|
|
|
- for(i=0; i<p->nRegion; i++){
|
|
|
- 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_malloc( 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 */
|
|
|
-
|
|
|
- /* 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) && pInode->bProcessLock==0 ){
|
|
|
- rc = SQLITE_IOERR_FSTAT;
|
|
|
- goto shm_open_err;
|
|
|
- }
|
|
|
-
|
|
|
-#ifdef SQLITE_SHM_DIRECTORY
|
|
|
- nShmFilename = sizeof(SQLITE_SHM_DIRECTORY) + 31;
|
|
|
-#else
|
|
|
- nShmFilename = 6 + (int)strlen(pDbFd->zPath);
|
|
|
-#endif
|
|
|
- pShmNode = sqlite3_malloc( 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", pDbFd->zPath);
|
|
|
- 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.
|
|
|
- */
|
|
|
- osFchown(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(pShmNode, 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(pShmNode, 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;
|
|
|
-
|
|
|
- /* 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 );
|
|
|
-
|
|
|
- if( pShmNode->nRegion<=iRegion ){
|
|
|
- char **apNew; /* New apRegion[] array */
|
|
|
- int nByte = (iRegion+1)*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++){
|
|
|
- if( seekAndWriteFd(pShmNode->h, iPg*pgsz + pgsz-1, "", 1, 0)!=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, (iRegion+1)*sizeof(char *)
|
|
|
- );
|
|
|
- if( !apNew ){
|
|
|
- rc = SQLITE_IOERR_NOMEM;
|
|
|
- goto shmpage_out;
|
|
|
- }
|
|
|
- pShmNode->apRegion = apNew;
|
|
|
- while(pShmNode->nRegion<=iRegion){
|
|
|
- void *pMem;
|
|
|
- if( pShmNode->h>=0 ){
|
|
|
- pMem = osMmap(0, szRegion,
|
|
|
- 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_malloc(szRegion);
|
|
|
- if( pMem==0 ){
|
|
|
- rc = SQLITE_NOMEM;
|
|
|
- goto shmpage_out;
|
|
|
- }
|
|
|
- memset(pMem, 0, szRegion);
|
|
|
- }
|
|
|
- pShmNode->apRegion[pShmNode->nRegion] = pMem;
|
|
|
- pShmNode->nRegion++;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-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(pShmNode, 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(pShmNode, 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(pShmNode, 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, getpid(), 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);
|
|
|
- unixEnterMutex();
|
|
|
- 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;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** Return the system page size.
|
|
|
-*/
|
|
|
-static int unixGetPagesize(void){
|
|
|
-#if HAVE_MREMAP
|
|
|
- return 512;
|
|
|
-#elif defined(_BSD_SOURCE)
|
|
|
- return getpagesize();
|
|
|
-#else
|
|
|
- return (int)sysconf(_SC_PAGESIZE);
|
|
|
-#endif
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 );
|
|
|
-
|
|
|
- if( (pFd->ctrlFlags & UNIXFILE_RDONLY)==0 ) flags |= PROT_WRITE;
|
|
|
-
|
|
|
- if( pOrig ){
|
|
|
- const int szSyspage = unixGetPagesize();
|
|
|
- i64 nReuse = (pFd->mmapSize & ~(szSyspage-1));
|
|
|
- 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 nByte){
|
|
|
- i64 nMap = nByte;
|
|
|
- int rc;
|
|
|
-
|
|
|
- assert( nMap>=0 || pFd->nFetchOut==0 );
|
|
|
- if( pFd->nFetchOut>0 ) return SQLITE_OK;
|
|
|
-
|
|
|
- if( nMap<0 ){
|
|
|
- struct stat statbuf; /* Low-level file information */
|
|
|
- rc = osFstat(pFd->h, &statbuf);
|
|
|
- if( rc!=SQLITE_OK ){
|
|
|
- return SQLITE_IOERR_FSTAT;
|
|
|
- }
|
|
|
- nMap = statbuf.st_size;
|
|
|
- }
|
|
|
- if( nMap>pFd->mmapSizeMax ){
|
|
|
- nMap = pFd->mmapSizeMax;
|
|
|
- }
|
|
|
-
|
|
|
- if( nMap!=pFd->mmapSize ){
|
|
|
- if( nMap>0 ){
|
|
|
- unixRemapfile(pFd, nMap);
|
|
|
- }else{
|
|
|
- unixUnmapfile(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 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){
|
|
|
- unixFile *pFd = (unixFile *)fd; /* The underlying database file */
|
|
|
- UNUSED_PARAMETER(iOff);
|
|
|
-
|
|
|
-#if SQLITE_MAX_MMAP_SIZE>0
|
|
|
- /* 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 );
|
|
|
-#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-funtion 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) \
|
|
|
-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 */ \
|
|
|
- unixShmMap, /* 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 */
|
|
|
-)
|
|
|
-IOMETHODS(
|
|
|
- nolockIoFinder, /* Finder function name */
|
|
|
- nolockIoMethods, /* sqlite3_io_methods object name */
|
|
|
- 1, /* shared memory is disabled */
|
|
|
- nolockClose, /* xClose method */
|
|
|
- nolockLock, /* xLock method */
|
|
|
- nolockUnlock, /* xUnlock method */
|
|
|
- nolockCheckReservedLock /* xCheckReservedLock 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 */
|
|
|
-)
|
|
|
-
|
|
|
-#if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS
|
|
|
-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 */
|
|
|
-)
|
|
|
-#endif
|
|
|
-
|
|
|
-#if OS_VXWORKS
|
|
|
-IOMETHODS(
|
|
|
- semIoFinder, /* Finder function name */
|
|
|
- semIoMethods, /* sqlite3_io_methods object name */
|
|
|
- 1, /* shared memory is disabled */
|
|
|
- semClose, /* xClose method */
|
|
|
- semLock, /* xLock method */
|
|
|
- semUnlock, /* xUnlock method */
|
|
|
- semCheckReservedLock /* xCheckReservedLock 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 */
|
|
|
-)
|
|
|
-#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 */
|
|
|
-)
|
|
|
-#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 */
|
|
|
-)
|
|
|
-#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 && 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 VXWorks only.
|
|
|
-*/
|
|
|
-static const sqlite3_io_methods *autolockIoFinderImpl(
|
|
|
- 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 autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl;
|
|
|
-
|
|
|
-#endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */
|
|
|
-
|
|
|
-/*
|
|
|
-** An abstract type for a pointer to a 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_malloc( 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_malloc(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
|
|
|
-
|
|
|
- pNew->lastErrno = 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,
|
|
|
- 0,
|
|
|
- "/var/tmp",
|
|
|
- "/usr/tmp",
|
|
|
- "/tmp",
|
|
|
- 0 /* List terminator */
|
|
|
- };
|
|
|
- unsigned int i;
|
|
|
- struct stat buf;
|
|
|
- const char *zDir = 0;
|
|
|
-
|
|
|
- azDirs[0] = sqlite3_temp_directory;
|
|
|
- if( !azDirs[1] ) azDirs[1] = getenv("SQLITE_TMPDIR");
|
|
|
- if( !azDirs[2] ) azDirs[2] = 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){
|
|
|
- static const unsigned char zChars[] =
|
|
|
- "abcdefghijklmnopqrstuvwxyz"
|
|
|
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
|
- "0123456789";
|
|
|
- unsigned int i, j;
|
|
|
- const char *zDir;
|
|
|
-
|
|
|
- /* 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();
|
|
|
- if( zDir==0 ) zDir = ".";
|
|
|
-
|
|
|
- /* Check that the output buffer is large enough for the temporary file
|
|
|
- ** name. If it is not, return SQLITE_ERROR.
|
|
|
- */
|
|
|
- if( (strlen(zDir) + strlen(SQLITE_TEMP_FILE_PREFIX) + 18) >= (size_t)nBuf ){
|
|
|
- return SQLITE_ERROR;
|
|
|
- }
|
|
|
-
|
|
|
- do{
|
|
|
- sqlite3_snprintf(nBuf-18, zBuf, "%s/"SQLITE_TEMP_FILE_PREFIX, zDir);
|
|
|
- j = (int)strlen(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;
|
|
|
- }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 resusable 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 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;
|
|
|
-#ifdef SQLITE_ENABLE_8_3_NAMES
|
|
|
- while( nDb>0 && sqlite3Isalnum(zPath[nDb]) ) nDb--;
|
|
|
- if( nDb==0 || zPath[nDb]!='-' ) return SQLITE_OK;
|
|
|
-#else
|
|
|
- while( zPath[nDb]!='-' ){
|
|
|
- assert( nDb>0 );
|
|
|
- assert( zPath[nDb]!='\n' );
|
|
|
- nDb--;
|
|
|
- }
|
|
|
-#endif
|
|
|
- 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;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
- );
|
|
|
-
|
|
|
- memset(p, 0, sizeof(unixFile));
|
|
|
-
|
|
|
- if( eType==SQLITE_OPEN_MAIN_DB ){
|
|
|
- UnixUnusedFd *pUnused;
|
|
|
- pUnused = findReusableFd(zName, flags);
|
|
|
- if( pUnused ){
|
|
|
- fd = pUnused->fd;
|
|
|
- }else{
|
|
|
- pUnused = sqlite3_malloc(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(MAX_PATHNAME+2, 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));
|
|
|
- if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
|
|
|
- /* 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) ){
|
|
|
- osFchown(fd, uid, gid);
|
|
|
- }
|
|
|
- }
|
|
|
- assert( fd>=0 );
|
|
|
- if( pOutFlags ){
|
|
|
- *pOutFlags = flags;
|
|
|
- }
|
|
|
-
|
|
|
- if( p->pUnused ){
|
|
|
- p->pUnused->fd = fd;
|
|
|
- p->pUnused->flags = flags;
|
|
|
- }
|
|
|
-
|
|
|
- if( isDelete ){
|
|
|
-#if OS_VXWORKS
|
|
|
- zPath = zName;
|
|
|
-#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 ){
|
|
|
- ((unixFile*)pFile)->lastErrno = 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;
|
|
|
- }
|
|
|
-#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{
|
|
|
- if( statfs(zPath, &fsInfo) == -1 ){
|
|
|
- /* In theory, the close(fd) call is sub-optimal. If the file opened
|
|
|
- ** with fd is a database file, and there are other connections open
|
|
|
- ** on that file that are currently holding advisory locks on it,
|
|
|
- ** then the call to close() will cancel those locks. In practice,
|
|
|
- ** we're assuming that statfs() doesn't fail very often. At least
|
|
|
- ** not while other file descriptors opened by the same process on
|
|
|
- ** the same file are working. */
|
|
|
- p->lastErrno = errno;
|
|
|
- robust_close(p, fd, __LINE__);
|
|
|
- rc = SQLITE_IOERR_ACCESS;
|
|
|
- goto open_finished;
|
|
|
- }
|
|
|
- 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
|
|
|
-
|
|
|
- rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
|
|
|
-
|
|
|
-open_finished:
|
|
|
- if( rc!=SQLITE_OK ){
|
|
|
- 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 ){
|
|
|
- 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 if( 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 */
|
|
|
-){
|
|
|
- int amode = 0;
|
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
|
- SimulateIOError( return SQLITE_IOERR_ACCESS; );
|
|
|
- switch( flags ){
|
|
|
- case SQLITE_ACCESS_EXISTS:
|
|
|
- amode = F_OK;
|
|
|
- break;
|
|
|
- case SQLITE_ACCESS_READWRITE:
|
|
|
- amode = W_OK|R_OK;
|
|
|
- break;
|
|
|
- case SQLITE_ACCESS_READ:
|
|
|
- amode = R_OK;
|
|
|
- break;
|
|
|
-
|
|
|
- default:
|
|
|
- assert(!"Invalid flags argument");
|
|
|
- }
|
|
|
- *pResOut = (osAccess(zPath, amode)==0);
|
|
|
- if( flags==SQLITE_ACCESS_EXISTS && *pResOut ){
|
|
|
- struct stat buf;
|
|
|
- if( 0==osStat(zPath, &buf) && buf.st_size==0 ){
|
|
|
- *pResOut = 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 */
|
|
|
-){
|
|
|
-
|
|
|
- /* 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);
|
|
|
-
|
|
|
- zOut[nOut-1] = '\0';
|
|
|
- if( zPath[0]=='/' ){
|
|
|
- sqlite3_snprintf(nOut, zOut, "%s", zPath);
|
|
|
- }else{
|
|
|
- int nCwd;
|
|
|
- if( osGetcwd(zOut, nOut-1)==0 ){
|
|
|
- return unixLogError(SQLITE_CANTOPEN_BKPT, "getcwd", zPath);
|
|
|
- }
|
|
|
- nCwd = (int)strlen(zOut);
|
|
|
- sqlite3_snprintf(nOut-nCwd, &zOut[nCwd], "/%s", zPath);
|
|
|
- }
|
|
|
- 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);
|
|
|
-#if !defined(SQLITE_TEST)
|
|
|
- {
|
|
|
- int pid, fd, got;
|
|
|
- fd = robust_open("/dev/urandom", O_RDONLY, 0);
|
|
|
- if( fd<0 ){
|
|
|
- time_t t;
|
|
|
- time(&t);
|
|
|
- memcpy(zBuf, &t, sizeof(t));
|
|
|
- pid = getpid();
|
|
|
- memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid));
|
|
|
- assert( sizeof(t)+sizeof(pid)<=(size_t)nBuf );
|
|
|
- nBuf = sizeof(t) + sizeof(pid);
|
|
|
- }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;
|
|
|
- if( gettimeofday(&sNow, 0)==0 ){
|
|
|
- *piNow = unixEpoch + 1000*(sqlite3_int64)sNow.tv_sec + sNow.tv_usec/1000;
|
|
|
- }else{
|
|
|
- rc = SQLITE_ERROR;
|
|
|
- }
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifdef SQLITE_TEST
|
|
|
- if( sqlite3_current_time ){
|
|
|
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
|
|
|
- }
|
|
|
-#endif
|
|
|
- UNUSED_PARAMETER(NotUsed);
|
|
|
- return rc;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/*
|
|
|
-************************ 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_SET_LOCKPROXYFILE,
|
|
|
-** <proxy_path> | ":auto:");
|
|
|
-** sqlite3_file_control(db, dbname, SQLITE_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 it's 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 (explicity calling the SQLITE_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 */
|
|
|
- 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, getpid()));
|
|
|
- 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, getpid()));
|
|
|
- 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, getpid()));
|
|
|
- return err;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- start=i+1;
|
|
|
- }
|
|
|
- buf[i] = lockPath[i];
|
|
|
- }
|
|
|
- OSTRACE(("CREATELOCKPATH proxy lock path=%s pid=%d\n", lockPath, getpid()));
|
|
|
- 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_malloc(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_malloc(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 */
|
|
|
-
|
|
|
-/* Not always defined in the headers as it ought to be */
|
|
|
-extern int gethostuuid(uuid_t id, const struct timespec *wait);
|
|
|
-
|
|
|
-/* 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);
|
|
|
-#if defined(__MAX_OS_X_VERSION_MIN_REQUIRED)\
|
|
|
- && __MAC_OS_X_VERSION_MIN_REQUIRED<1050
|
|
|
- {
|
|
|
- static const 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) ){
|
|
|
- pFile->lastErrno = 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 ){
|
|
|
- pFile->lastErrno = 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:"), getpid()));
|
|
|
-
|
|
|
- rc = proxyGetHostID(myHostID, &pError);
|
|
|
- if( (rc&0xff)==SQLITE_IOERR ){
|
|
|
- pFile->lastErrno = 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 */
|
|
|
- pFile->lastErrno = 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 = conchFile->pMethod->xLock((sqlite3_file*)conchFile, 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:"),
|
|
|
- getpid()));
|
|
|
- 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_malloc().
|
|
|
-** 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_malloc(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:"), getpid()));
|
|
|
-
|
|
|
- pCtx = sqlite3_malloc( 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_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_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 */
|
|
|
- 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( 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 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 && (OS_VXWORKS || defined(__APPLE__))
|
|
|
- UNIXVFS("unix", autolockIoFinder ),
|
|
|
-#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
|
|
|
- UNIXVFS("unix-posix", posixIoFinder ),
|
|
|
-#if !OS_VXWORKS
|
|
|
- UNIXVFS("unix-flock", flockIoFinder ),
|
|
|
-#endif
|
|
|
-#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)==24 );
|
|
|
-
|
|
|
- /* 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 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.
|
|
|
-*/
|
|
|
-#if SQLITE_OS_WIN /* This file is used for Windows only */
|
|
|
-
|
|
|
-#ifdef __CYGWIN__
|
|
|
-# include <sys/cygwin.h>
|
|
|
-# include <errno.h> /* amalgamator: keep */
|
|
|
-#endif
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-
|
|
|
-#if defined(SQLITE_TEST) && defined(SQLITE_DEBUG)
|
|
|
-# ifndef SQLITE_DEBUG_OS_TRACE
|
|
|
-# define SQLITE_DEBUG_OS_TRACE 0
|
|
|
-# endif
|
|
|
- int sqlite3OSTrace = SQLITE_DEBUG_OS_TRACE;
|
|
|
-# define OSTRACE(X) if( sqlite3OSTrace ) sqlite3DebugPrintf X
|
|
|
-#else
|
|
|
-# define OSTRACE(X)
|
|
|
-#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 *********************/
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-
|
|
|
-/*
|
|
|
-** 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
|
|
|
-
|
|
|
-/*
|
|
|
-** Maximum pathname length (in chars) for WinNT. This should normally be
|
|
|
-** 32767.
|
|
|
-*/
|
|
|
-#ifndef SQLITE_WINNT_MAX_PATH_CHARS
|
|
|
-# define SQLITE_WINNT_MAX_PATH_CHARS (32767)
|
|
|
-#endif
|
|
|
-
|
|
|
-/*
|
|
|
-** Maximum pathname length (in bytes) for Win32. The MAX_PATH macro is in
|
|
|
-** characters, so we allocate 3 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
|
|
|
-** 32767 * 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 string that should be used as the directory separator.
|
|
|
-*/
|
|
|
-#ifndef winGetDirDep
|
|
|
-# ifdef __CYGWIN__
|
|
|
-# define winGetDirDep() "/"
|
|
|
-# else
|
|
|
-# define winGetDirDep() "\\"
|
|
|
-# endif
|
|
|
-#endif
|
|
|
-
|
|
|
-/*
|
|
|
-** Do we need to manually define the Win32 file mapping APIs for use with WAL
|
|
|
-** mode (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)
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
-
|
|
|
-/*
|
|
|
-** This file mapping API is common to both Win32 and WinRT.
|
|
|
-*/
|
|
|
-WINBASEAPI BOOL WINAPI UnmapViewOfFile(LPCVOID);
|
|
|
-#endif /* SQLITE_WIN32_FILEMAPPING_API && !defined(SQLITE_OMIT_WAL) */
|
|
|
-
|
|
|
-/*
|
|
|
-** 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 magic; /* 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
|
|
|
-#define WINMEM_MAGIC 0x42b2830b
|
|
|
-#endif
|
|
|
-
|
|
|
-static struct winMemData win_mem_data = {
|
|
|
-#ifndef NDEBUG
|
|
|
- WINMEM_MAGIC,
|
|
|
-#endif
|
|
|
- NULL, FALSE
|
|
|
-};
|
|
|
-
|
|
|
-#ifndef NDEBUG
|
|
|
-#define winMemAssertMagic() assert( win_mem_data.magic==WINMEM_MAGIC )
|
|
|
-#else
|
|
|
-#define winMemAssertMagic()
|
|
|
-#endif
|
|
|
-
|
|
|
-#define winMemGetHeap() win_mem_data.hHeap
|
|
|
-
|
|
|
-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 int sqlite3_os_type = 0;
|
|
|
-#elif !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && \
|
|
|
- defined(SQLITE_WIN32_HAS_ANSI) && defined(SQLITE_WIN32_HAS_WIDE)
|
|
|
-static int 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))
|
|
|
- { "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))
|
|
|
- { "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)
|
|
|
- { "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)
|
|
|
- { "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 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[43].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[44].pCurrent)
|
|
|
-
|
|
|
-#if !SQLITE_OS_WINRT
|
|
|
- { "LocalFree", (SYSCALL)LocalFree, 0 },
|
|
|
-#else
|
|
|
- { "LocalFree", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osLocalFree ((HLOCAL(WINAPI*)(HLOCAL))aSyscall[45].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[46].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[47].pCurrent)
|
|
|
-#endif
|
|
|
-
|
|
|
-#if SQLITE_OS_WINCE || (!SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL))
|
|
|
- { "MapViewOfFile", (SYSCALL)MapViewOfFile, 0 },
|
|
|
-#else
|
|
|
- { "MapViewOfFile", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osMapViewOfFile ((LPVOID(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
|
|
|
- SIZE_T))aSyscall[48].pCurrent)
|
|
|
-
|
|
|
- { "MultiByteToWideChar", (SYSCALL)MultiByteToWideChar, 0 },
|
|
|
-
|
|
|
-#define osMultiByteToWideChar ((int(WINAPI*)(UINT,DWORD,LPCSTR,int,LPWSTR, \
|
|
|
- int))aSyscall[49].pCurrent)
|
|
|
-
|
|
|
- { "QueryPerformanceCounter", (SYSCALL)QueryPerformanceCounter, 0 },
|
|
|
-
|
|
|
-#define osQueryPerformanceCounter ((BOOL(WINAPI*)( \
|
|
|
- LARGE_INTEGER*))aSyscall[50].pCurrent)
|
|
|
-
|
|
|
- { "ReadFile", (SYSCALL)ReadFile, 0 },
|
|
|
-
|
|
|
-#define osReadFile ((BOOL(WINAPI*)(HANDLE,LPVOID,DWORD,LPDWORD, \
|
|
|
- LPOVERLAPPED))aSyscall[51].pCurrent)
|
|
|
-
|
|
|
- { "SetEndOfFile", (SYSCALL)SetEndOfFile, 0 },
|
|
|
-
|
|
|
-#define osSetEndOfFile ((BOOL(WINAPI*)(HANDLE))aSyscall[52].pCurrent)
|
|
|
-
|
|
|
-#if !SQLITE_OS_WINRT
|
|
|
- { "SetFilePointer", (SYSCALL)SetFilePointer, 0 },
|
|
|
-#else
|
|
|
- { "SetFilePointer", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osSetFilePointer ((DWORD(WINAPI*)(HANDLE,LONG,PLONG, \
|
|
|
- DWORD))aSyscall[53].pCurrent)
|
|
|
-
|
|
|
-#if !SQLITE_OS_WINRT
|
|
|
- { "Sleep", (SYSCALL)Sleep, 0 },
|
|
|
-#else
|
|
|
- { "Sleep", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[54].pCurrent)
|
|
|
-
|
|
|
- { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
|
|
|
-
|
|
|
-#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
|
|
|
- LPFILETIME))aSyscall[55].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[56].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[57].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINCE || !defined(SQLITE_OMIT_WAL)
|
|
|
- { "UnmapViewOfFile", (SYSCALL)UnmapViewOfFile, 0 },
|
|
|
-#else
|
|
|
- { "UnmapViewOfFile", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osUnmapViewOfFile ((BOOL(WINAPI*)(LPCVOID))aSyscall[58].pCurrent)
|
|
|
-
|
|
|
- { "WideCharToMultiByte", (SYSCALL)WideCharToMultiByte, 0 },
|
|
|
-
|
|
|
-#define osWideCharToMultiByte ((int(WINAPI*)(UINT,DWORD,LPCWSTR,int,LPSTR,int, \
|
|
|
- LPCSTR,LPBOOL))aSyscall[59].pCurrent)
|
|
|
-
|
|
|
- { "WriteFile", (SYSCALL)WriteFile, 0 },
|
|
|
-
|
|
|
-#define osWriteFile ((BOOL(WINAPI*)(HANDLE,LPCVOID,DWORD,LPDWORD, \
|
|
|
- LPOVERLAPPED))aSyscall[60].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[61].pCurrent)
|
|
|
-
|
|
|
-#if !SQLITE_OS_WINRT
|
|
|
- { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
|
|
|
-#else
|
|
|
- { "WaitForSingleObject", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
|
|
|
- DWORD))aSyscall[62].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- { "WaitForSingleObjectEx", (SYSCALL)WaitForSingleObjectEx, 0 },
|
|
|
-#else
|
|
|
- { "WaitForSingleObjectEx", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osWaitForSingleObjectEx ((DWORD(WINAPI*)(HANDLE,DWORD, \
|
|
|
- BOOL))aSyscall[63].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[64].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[65].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
|
|
|
- { "MapViewOfFileFromApp", (SYSCALL)MapViewOfFileFromApp, 0 },
|
|
|
-#else
|
|
|
- { "MapViewOfFileFromApp", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osMapViewOfFileFromApp ((LPVOID(WINAPI*)(HANDLE,ULONG,ULONG64, \
|
|
|
- SIZE_T))aSyscall[66].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[67].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[68].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- { "GetTickCount64", (SYSCALL)GetTickCount64, 0 },
|
|
|
-#else
|
|
|
- { "GetTickCount64", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osGetTickCount64 ((ULONGLONG(WINAPI*)(VOID))aSyscall[69].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- { "GetNativeSystemInfo", (SYSCALL)GetNativeSystemInfo, 0 },
|
|
|
-#else
|
|
|
- { "GetNativeSystemInfo", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osGetNativeSystemInfo ((VOID(WINAPI*)( \
|
|
|
- LPSYSTEM_INFO))aSyscall[70].pCurrent)
|
|
|
-
|
|
|
-#if defined(SQLITE_WIN32_HAS_ANSI)
|
|
|
- { "OutputDebugStringA", (SYSCALL)OutputDebugStringA, 0 },
|
|
|
-#else
|
|
|
- { "OutputDebugStringA", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osOutputDebugStringA ((VOID(WINAPI*)(LPCSTR))aSyscall[71].pCurrent)
|
|
|
-
|
|
|
-#if defined(SQLITE_WIN32_HAS_WIDE)
|
|
|
- { "OutputDebugStringW", (SYSCALL)OutputDebugStringW, 0 },
|
|
|
-#else
|
|
|
- { "OutputDebugStringW", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osOutputDebugStringW ((VOID(WINAPI*)(LPCWSTR))aSyscall[72].pCurrent)
|
|
|
-
|
|
|
- { "GetProcessHeap", (SYSCALL)GetProcessHeap, 0 },
|
|
|
-
|
|
|
-#define osGetProcessHeap ((HANDLE(WINAPI*)(VOID))aSyscall[73].pCurrent)
|
|
|
-
|
|
|
-#if SQLITE_OS_WINRT && !defined(SQLITE_OMIT_WAL)
|
|
|
- { "CreateFileMappingFromApp", (SYSCALL)CreateFileMappingFromApp, 0 },
|
|
|
+ /*
|
|
|
+ ** 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( 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( 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 ){
|
|
|
+ pAccum->accError = 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];
|
|
|
+ }
|
|
|
+ {
|
|
|
+ register const char *cset; /* Use registers for speed */
|
|
|
+ register int base;
|
|
|
+ cset = &aDigits[infop->charset];
|
|
|
+ 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:
|
|
|
+ realvalue = va_arg(ap,double);
|
|
|
+#ifdef SQLITE_OMIT_FLOATING_POINT
|
|
|
+ length = 0;
|
|
|
#else
|
|
|
- { "CreateFileMappingFromApp", (SYSCALL)0, 0 },
|
|
|
-#endif
|
|
|
-
|
|
|
-#define osCreateFileMappingFromApp ((HANDLE(WINAPI*)(HANDLE, \
|
|
|
- LPSECURITY_ATTRIBUTES,ULONG,ULONG64,LPCWSTR))aSyscall[74].pCurrent)
|
|
|
-
|
|
|
-}; /* End of the overrideable system calls */
|
|
|
+ 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--;
|
|
|
+ for(idx=precision, 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>=1e64*scale && exp<=350 ){ scale *= 1e64; exp+=64; }
|
|
|
+ while( realvalue>=1e8*scale && exp<=350 ){ scale *= 1e8; exp+=8; }
|
|
|
+ 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 ){
|
|
|
+ if( prefix=='-' ){
|
|
|
+ bufpt = "-Inf";
|
|
|
+ }else if( prefix=='+' ){
|
|
|
+ bufpt = "+Inf";
|
|
|
+ }else{
|
|
|
+ bufpt = "Inf";
|
|
|
+ }
|
|
|
+ length = sqlite3Strlen30(bufpt);
|
|
|
+ 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)+precision+width > etBUFSIZE - 15 ){
|
|
|
+ bufpt = zExtra = sqlite3Malloc( MAX(e2,0)+precision+width+15 );
|
|
|
+ if( bufpt==0 ){
|
|
|
+ pAccum->accError = 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;
|
|
|
|
|
|
-/*
|
|
|
-** 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;
|
|
|
+ /* 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;
|
|
|
|
|
|
- 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;
|
|
|
+ /* 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:
|
|
|
+ *(va_arg(ap,int*)) = pAccum->nChar;
|
|
|
+ length = width = 0;
|
|
|
+ break;
|
|
|
+ case etPERCENT:
|
|
|
+ buf[0] = '%';
|
|
|
+ bufpt = buf;
|
|
|
+ length = 1;
|
|
|
+ break;
|
|
|
+ case etCHARX:
|
|
|
+ c = va_arg(ap,int);
|
|
|
+ buf[0] = (char)c;
|
|
|
+ if( precision>=0 ){
|
|
|
+ for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
|
|
|
+ length = precision;
|
|
|
+ }else{
|
|
|
+ length =1;
|
|
|
+ }
|
|
|
+ bufpt = buf;
|
|
|
+ break;
|
|
|
+ case etSTRING:
|
|
|
+ case etDYNSTRING:
|
|
|
+ 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:
|
|
|
+ case etSQLESCAPE2:
|
|
|
+ case etSQLESCAPE3: {
|
|
|
+ int i, j, k, n, isnull;
|
|
|
+ int needQuote;
|
|
|
+ char ch;
|
|
|
+ char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
|
|
|
+ char *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 + 1 + needQuote*2;
|
|
|
+ if( n>etBUFSIZE ){
|
|
|
+ bufpt = zExtra = sqlite3Malloc( n );
|
|
|
+ if( bufpt==0 ){
|
|
|
+ pAccum->accError = 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;
|
|
|
}
|
|
|
- }
|
|
|
- }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;
|
|
|
+ case etTOKEN: {
|
|
|
+ Token *pToken = va_arg(ap, Token*);
|
|
|
+ if( pToken ){
|
|
|
+ sqlite3StrAccumAppend(pAccum, (const char*)pToken->z, pToken->n);
|
|
|
}
|
|
|
- rc = SQLITE_OK;
|
|
|
- if( pNewFunc==0 ) pNewFunc = aSyscall[i].pDefault;
|
|
|
- aSyscall[i].pCurrent = pNewFunc;
|
|
|
+ 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( k>=0 && k<pSrc->nSrc );
|
|
|
+ if( pItem->zDatabase ){
|
|
|
+ sqlite3StrAccumAppend(pAccum, pItem->zDatabase, -1);
|
|
|
+ sqlite3StrAccumAppend(pAccum, ".", 1);
|
|
|
+ }
|
|
|
+ sqlite3StrAccumAppend(pAccum, pItem->zName, -1);
|
|
|
+ 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.
|
|
|
+ */
|
|
|
+ if( !flag_leftjustify ){
|
|
|
+ register int nspace;
|
|
|
+ nspace = width-length;
|
|
|
+ if( nspace>0 ){
|
|
|
+ sqlite3AppendSpace(pAccum, nspace);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- 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;
|
|
|
+ if( length>0 ){
|
|
|
+ sqlite3StrAccumAppend(pAccum, bufpt, length);
|
|
|
}
|
|
|
- }
|
|
|
- for(i++; i<ArraySize(aSyscall); i++){
|
|
|
- if( aSyscall[i].pCurrent!=0 ) return aSyscall[i].zName;
|
|
|
- }
|
|
|
- return 0;
|
|
|
-}
|
|
|
+ if( flag_leftjustify ){
|
|
|
+ register int nspace;
|
|
|
+ nspace = width-length;
|
|
|
+ if( nspace>0 ){
|
|
|
+ sqlite3AppendSpace(pAccum, nspace);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ sqlite3_free(zExtra);
|
|
|
+ }/* End for loop over the format string */
|
|
|
+} /* End of function */
|
|
|
|
|
|
/*
|
|
|
-** This function outputs the specified (ANSI) string to the Win32 debugger
|
|
|
-** (if available).
|
|
|
+** Append N bytes of text from z to the StrAccum object.
|
|
|
*/
|
|
|
-
|
|
|
-SQLITE_API void 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 ){
|
|
|
+SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum *p, const char *z, int N){
|
|
|
+ assert( z!=0 || N==0 );
|
|
|
+ if( p->accError ){
|
|
|
+ testcase(p->accError==STRACCUM_TOOBIG);
|
|
|
+ testcase(p->accError==STRACCUM_NOMEM);
|
|
|
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 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
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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.
|
|
|
-*/
|
|
|
-#ifndef NTDDI_WIN8
|
|
|
-# define NTDDI_WIN8 0x06020000
|
|
|
-#endif
|
|
|
-
|
|
|
-#if SQLITE_OS_WINCE || SQLITE_OS_WINRT || !defined(SQLITE_WIN32_HAS_ANSI)
|
|
|
-# define osIsNT() (1)
|
|
|
-#elif !defined(SQLITE_WIN32_HAS_WIDE)
|
|
|
-# define osIsNT() (0)
|
|
|
-#else
|
|
|
- static int osIsNT(void){
|
|
|
- if( sqlite3_os_type==0 ){
|
|
|
-#if defined(NTDDI_VERSION) && NTDDI_VERSION >= NTDDI_WIN8
|
|
|
- OSVERSIONINFOW sInfo;
|
|
|
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
|
|
- osGetVersionExW(&sInfo);
|
|
|
-#else
|
|
|
- OSVERSIONINFOA sInfo;
|
|
|
- sInfo.dwOSVersionInfoSize = sizeof(sInfo);
|
|
|
- osGetVersionExA(&sInfo);
|
|
|
-#endif
|
|
|
- sqlite3_os_type = sInfo.dwPlatformId==VER_PLATFORM_WIN32_NT ? 2 : 1;
|
|
|
- }
|
|
|
- return sqlite3_os_type==2;
|
|
|
- }
|
|
|
-#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);
|
|
|
+ assert( p->zText!=0 || p->nChar==0 );
|
|
|
+ if( N<=0 ){
|
|
|
+ if( N==0 || z[0]==0 ) return;
|
|
|
+ N = sqlite3Strlen30(z);
|
|
|
}
|
|
|
- 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);
|
|
|
+ if( p->nChar+N >= p->nAlloc ){
|
|
|
+ char *zNew;
|
|
|
+ if( !p->useMalloc ){
|
|
|
+ p->accError = STRACCUM_TOOBIG;
|
|
|
+ N = p->nAlloc - p->nChar - 1;
|
|
|
+ if( N<=0 ){
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }else{
|
|
|
+ char *zOld = (p->zText==p->zBase ? 0 : p->zText);
|
|
|
+ i64 szNew = p->nChar;
|
|
|
+ szNew += N + 1;
|
|
|
+ if( szNew > p->mxAlloc ){
|
|
|
+ sqlite3StrAccumReset(p);
|
|
|
+ p->accError = STRACCUM_TOOBIG;
|
|
|
+ return;
|
|
|
+ }else{
|
|
|
+ p->nAlloc = (int)szNew;
|
|
|
+ }
|
|
|
+ if( p->useMalloc==1 ){
|
|
|
+ zNew = sqlite3DbRealloc(p->db, zOld, p->nAlloc);
|
|
|
+ }else{
|
|
|
+ zNew = sqlite3_realloc(zOld, p->nAlloc);
|
|
|
+ }
|
|
|
+ if( zNew ){
|
|
|
+ if( zOld==0 && p->nChar>0 ) memcpy(zNew, p->zText, p->nChar);
|
|
|
+ p->zText = zNew;
|
|
|
+ }else{
|
|
|
+ p->accError = STRACCUM_NOMEM;
|
|
|
+ sqlite3StrAccumReset(p);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ assert( p->zText );
|
|
|
+ memcpy(&p->zText[p->nChar], z, N);
|
|
|
+ p->nChar += N;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Change the size of an existing memory allocation
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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);
|
|
|
+SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum *p){
|
|
|
+ if( p->zText ){
|
|
|
+ p->zText[p->nChar] = 0;
|
|
|
+ if( p->useMalloc && p->zText==p->zBase ){
|
|
|
+ if( p->useMalloc==1 ){
|
|
|
+ p->zText = sqlite3DbMallocRaw(p->db, p->nChar+1 );
|
|
|
+ }else{
|
|
|
+ p->zText = sqlite3_malloc(p->nChar+1);
|
|
|
+ }
|
|
|
+ if( p->zText ){
|
|
|
+ memcpy(p->zText, p->zBase, p->nChar+1);
|
|
|
+ }else{
|
|
|
+ p->accError = STRACCUM_NOMEM;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
- return p;
|
|
|
+ return p->zText;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Return the size of an outstanding allocation, in bytes.
|
|
|
+** Reset an StrAccum string. Reclaim all malloced memory.
|
|
|
*/
|
|
|
-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, NULL) );
|
|
|
-#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;
|
|
|
+SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum *p){
|
|
|
+ if( p->zText!=p->zBase ){
|
|
|
+ if( p->useMalloc==1 ){
|
|
|
+ sqlite3DbFree(p->db, p->zText);
|
|
|
+ }else{
|
|
|
+ sqlite3_free(p->zText);
|
|
|
+ }
|
|
|
}
|
|
|
- return (int)n;
|
|
|
+ p->zText = 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Round up a request size to the next valid allocation size.
|
|
|
+** Initialize a string accumulator
|
|
|
*/
|
|
|
-static int winMemRoundup(int n){
|
|
|
- return n;
|
|
|
+SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum *p, char *zBase, int n, int mx){
|
|
|
+ p->zText = p->zBase = zBase;
|
|
|
+ p->db = 0;
|
|
|
+ p->nChar = 0;
|
|
|
+ p->nAlloc = n;
|
|
|
+ p->mxAlloc = mx;
|
|
|
+ p->useMalloc = 1;
|
|
|
+ p->accError = 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Initialize this module.
|
|
|
+** Print into memory obtained from sqliteMalloc(). Use the internal
|
|
|
+** %-conversion extensions.
|
|
|
*/
|
|
|
-static int winMemInit(void *pAppData){
|
|
|
- winMemData *pWinMemData = (winMemData *)pAppData;
|
|
|
-
|
|
|
- if( !pWinMemData ) return SQLITE_ERROR;
|
|
|
- assert( pWinMemData->magic==WINMEM_MAGIC );
|
|
|
-
|
|
|
-#if !SQLITE_OS_WINRT && SQLITE_WIN32_HEAP_CREATE
|
|
|
- if( !pWinMemData->hHeap ){
|
|
|
- pWinMemData->hHeap = osHeapCreate(SQLITE_WIN32_HEAP_FLAGS,
|
|
|
- SQLITE_WIN32_HEAP_INIT_SIZE,
|
|
|
- SQLITE_WIN32_HEAP_MAX_SIZE);
|
|
|
- if( !pWinMemData->hHeap ){
|
|
|
- sqlite3_log(SQLITE_NOMEM,
|
|
|
- "failed to HeapCreate (%lu), flags=%u, initSize=%u, maxSize=%u",
|
|
|
- osGetLastError(), SQLITE_WIN32_HEAP_FLAGS,
|
|
|
- SQLITE_WIN32_HEAP_INIT_SIZE, SQLITE_WIN32_HEAP_MAX_SIZE);
|
|
|
- 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;
|
|
|
+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, zBase, sizeof(zBase),
|
|
|
+ db->aLimit[SQLITE_LIMIT_LENGTH]);
|
|
|
+ acc.db = db;
|
|
|
+ sqlite3VXPrintf(&acc, 1, zFormat, ap);
|
|
|
+ z = sqlite3StrAccumFinish(&acc);
|
|
|
+ if( acc.accError==STRACCUM_NOMEM ){
|
|
|
+ db->mallocFailed = 1;
|
|
|
}
|
|
|
- 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;
|
|
|
+ return z;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Deinitialize this module.
|
|
|
+** Print into memory obtained from sqliteMalloc(). Use the internal
|
|
|
+** %-conversion extensions.
|
|
|
*/
|
|
|
-static void winMemShutdown(void *pAppData){
|
|
|
- winMemData *pWinMemData = (winMemData *)pAppData;
|
|
|
-
|
|
|
- if( !pWinMemData ) return;
|
|
|
- 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;
|
|
|
- }
|
|
|
+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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Like sqlite3MPrintf(), but call sqlite3DbFree() on zStr after formatting
|
|
|
+** the string and before returnning. This routine is intended to be used
|
|
|
+** to modify an existing string. For example:
|
|
|
**
|
|
|
-** 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?).
|
|
|
+** x = sqlite3MPrintf(db, x, "prefix %s suffix", x);
|
|
|
**
|
|
|
-** 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;
|
|
|
+SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3 *db, char *zStr, const char *zFormat, ...){
|
|
|
+ va_list ap;
|
|
|
+ char *z;
|
|
|
+ va_start(ap, zFormat);
|
|
|
+ z = sqlite3VMPrintf(db, zFormat, ap);
|
|
|
+ va_end(ap);
|
|
|
+ sqlite3DbFree(db, zStr);
|
|
|
+ return z;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Convert Microsoft Unicode to UTF-8. Space to hold the returned string is
|
|
|
-** obtained from sqlite3_malloc().
|
|
|
+** Print into memory obtained from sqlite3_malloc(). Omit the internal
|
|
|
+** %-conversion extensions.
|
|
|
*/
|
|
|
-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;
|
|
|
+SQLITE_API char *sqlite3_vmprintf(const char *zFormat, va_list ap){
|
|
|
+ char *z;
|
|
|
+ char zBase[SQLITE_PRINT_BUF_SIZE];
|
|
|
+ StrAccum acc;
|
|
|
+#ifndef SQLITE_OMIT_AUTOINIT
|
|
|
+ if( sqlite3_initialize() ) return 0;
|
|
|
+#endif
|
|
|
+ sqlite3StrAccumInit(&acc, zBase, sizeof(zBase), SQLITE_MAX_LENGTH);
|
|
|
+ acc.useMalloc = 2;
|
|
|
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
+ z = sqlite3StrAccumFinish(&acc);
|
|
|
+ return z;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Print into memory obtained from sqlite3_malloc()(). Omit the internal
|
|
|
+** %-conversion extensions.
|
|
|
*/
|
|
|
-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;
|
|
|
+SQLITE_API char *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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Convert Microsoft Unicode to multi-byte character string, based on the
|
|
|
-** user's ANSI codepage.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** Space to hold the returned string is obtained from
|
|
|
-** sqlite3_malloc().
|
|
|
+** sqlite3_vsnprintf() is the varargs version.
|
|
|
*/
|
|
|
-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;
|
|
|
+SQLITE_API char *sqlite3_vsnprintf(int n, char *zBuf, const char *zFormat, va_list ap){
|
|
|
+ StrAccum acc;
|
|
|
+ if( n<=0 ) return zBuf;
|
|
|
+ sqlite3StrAccumInit(&acc, zBuf, n, 0);
|
|
|
+ acc.useMalloc = 0;
|
|
|
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
+ return sqlite3StrAccumFinish(&acc);
|
|
|
+}
|
|
|
+SQLITE_API char *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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Convert multibyte character string to UTF-8. Space to hold the
|
|
|
-** returned string is obtained from sqlite3_malloc().
|
|
|
+** 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.
|
|
|
*/
|
|
|
-SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zFilename){
|
|
|
- char *zFilenameUtf8;
|
|
|
- LPWSTR zTmpWide;
|
|
|
+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 */
|
|
|
|
|
|
- zTmpWide = winMbcsToUnicode(zFilename);
|
|
|
- if( zTmpWide==0 ){
|
|
|
- return 0;
|
|
|
- }
|
|
|
- zFilenameUtf8 = winUnicodeToUtf8(zTmpWide);
|
|
|
- sqlite3_free(zTmpWide);
|
|
|
- return zFilenameUtf8;
|
|
|
+ sqlite3StrAccumInit(&acc, zMsg, sizeof(zMsg), 0);
|
|
|
+ acc.useMalloc = 0;
|
|
|
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
+ sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
|
|
|
+ sqlite3StrAccumFinish(&acc));
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Convert UTF-8 to multibyte character string. Space to hold the
|
|
|
-** returned string is obtained from sqlite3_malloc().
|
|
|
+** Format and write a message to the log if logging is enabled.
|
|
|
*/
|
|
|
-SQLITE_API char *sqlite3_win32_utf8_to_mbcs(const char *zFilename){
|
|
|
- char *zFilenameMbcs;
|
|
|
- LPWSTR zTmpWide;
|
|
|
-
|
|
|
- zTmpWide = winUtf8ToUnicode(zFilename);
|
|
|
- if( zTmpWide==0 ){
|
|
|
- return 0;
|
|
|
+SQLITE_API void 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);
|
|
|
}
|
|
|
- zFilenameMbcs = winUnicodeToMbcs(zTmpWide);
|
|
|
- sqlite3_free(zTmpWide);
|
|
|
- return zFilenameMbcs;
|
|
|
}
|
|
|
|
|
|
+#if defined(SQLITE_DEBUG)
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** 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_API int 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;
|
|
|
+SQLITE_PRIVATE void sqlite3DebugPrintf(const char *zFormat, ...){
|
|
|
+ va_list ap;
|
|
|
+ StrAccum acc;
|
|
|
+ char zBuf[500];
|
|
|
+ sqlite3StrAccumInit(&acc, zBuf, sizeof(zBuf), 0);
|
|
|
+ acc.useMalloc = 0;
|
|
|
+ va_start(ap,zFormat);
|
|
|
+ sqlite3VXPrintf(&acc, 0, zFormat, ap);
|
|
|
+ va_end(ap);
|
|
|
+ sqlite3StrAccumFinish(&acc);
|
|
|
+ fprintf(stdout,"%s", zBuf);
|
|
|
+ fflush(stdout);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
+#ifndef SQLITE_OMIT_TRACE
|
|
|
/*
|
|
|
-** The return value of winGetLastErrorMsg
|
|
|
-** is zero if the error message fits in the buffer, or non-zero
|
|
|
-** otherwise (if the message was truncated).
|
|
|
+** variable-argument wrapper around sqlite3VXPrintf().
|
|
|
*/
|
|
|
-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;
|
|
|
+SQLITE_PRIVATE void sqlite3XPrintf(StrAccum *p, const char *zFormat, ...){
|
|
|
+ va_list ap;
|
|
|
+ va_start(ap,zFormat);
|
|
|
+ sqlite3VXPrintf(p, 1, zFormat, ap);
|
|
|
+ va_end(ap);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
+/************** End of printf.c **********************************************/
|
|
|
+/************** Begin file random.c ******************************************/
|
|
|
/*
|
|
|
+** 2001 September 15
|
|
|
**
|
|
|
-** This function - winLogErrorAtLine() - is only ever called via the macro
|
|
|
-** winLogError().
|
|
|
+** The author disclaims copyright to this source code. In place of
|
|
|
+** a legal notice, here is a blessing:
|
|
|
**
|
|
|
-** 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.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+*************************************************************************
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#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;
|
|
|
-}
|
|
|
+/* 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;
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Return N random bytes.
|
|
|
*/
|
|
|
-#ifndef SQLITE_WIN32_IOERR_RETRY
|
|
|
-# define SQLITE_WIN32_IOERR_RETRY 10
|
|
|
+SQLITE_API void 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
|
|
|
-#ifndef SQLITE_WIN32_IOERR_RETRY_DELAY
|
|
|
-# define SQLITE_WIN32_IOERR_RETRY_DELAY 25
|
|
|
+
|
|
|
+#if SQLITE_THREADSAFE
|
|
|
+ sqlite3_mutex *mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_PRNG);
|
|
|
+ sqlite3_mutex_enter(mutex);
|
|
|
#endif
|
|
|
-static int winIoerrRetry = SQLITE_WIN32_IOERR_RETRY;
|
|
|
-static int winIoerrRetryDelay = SQLITE_WIN32_IOERR_RETRY_DELAY;
|
|
|
|
|
|
-/*
|
|
|
-** 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;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
- return 0;
|
|
|
- }
|
|
|
- if( e==ERROR_ACCESS_DENIED ||
|
|
|
- e==ERROR_LOCK_VIOLATION ||
|
|
|
- e==ERROR_SHARING_VIOLATION ){
|
|
|
- sqlite3_win32_sleep(winIoerrRetryDelay*(1+*pnRetry));
|
|
|
- ++*pnRetry;
|
|
|
- return 1;
|
|
|
- }
|
|
|
- if( pError ){
|
|
|
- *pError = e;
|
|
|
+ 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;
|
|
|
}
|
|
|
- return 0;
|
|
|
-}
|
|
|
|
|
|
-/*
|
|
|
-** Log a I/O error retry episode.
|
|
|
-*/
|
|
|
-static void winLogIoerr(int nRetry){
|
|
|
- if( nRetry ){
|
|
|
- sqlite3_log(SQLITE_IOERR,
|
|
|
- "delayed %dms for lock/sharing conflict",
|
|
|
- winIoerrRetryDelay*nRetry*(nRetry+1)/2
|
|
|
- );
|
|
|
+ while( N-- ){
|
|
|
+ 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];
|
|
|
}
|
|
|
+ sqlite3_mutex_leave(mutex);
|
|
|
}
|
|
|
|
|
|
-#if SQLITE_OS_WINCE
|
|
|
-/*************************************************************************
|
|
|
-** This section contains code for WinCE only.
|
|
|
-*/
|
|
|
-#if !defined(SQLITE_MSVC_LOCALTIME_API) || !SQLITE_MSVC_LOCALTIME_API
|
|
|
+#ifndef SQLITE_OMIT_BUILTIN_TEST
|
|
|
/*
|
|
|
-** The MSVC CRT on Windows CE may not have a localtime() function. So
|
|
|
-** create a substitute.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-/* #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;
|
|
|
+static SQLITE_WSD struct sqlite3PrngType sqlite3SavedPrng;
|
|
|
+SQLITE_PRIVATE void sqlite3PrngSaveState(void){
|
|
|
+ memcpy(
|
|
|
+ &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
|
|
+ &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
|
|
+ sizeof(sqlite3Prng)
|
|
|
+ );
|
|
|
}
|
|
|
-#endif
|
|
|
-
|
|
|
-#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)]
|
|
|
+SQLITE_PRIVATE void sqlite3PrngRestoreState(void){
|
|
|
+ memcpy(
|
|
|
+ &GLOBAL(struct sqlite3PrngType, sqlite3Prng),
|
|
|
+ &GLOBAL(struct sqlite3PrngType, sqlite3SavedPrng),
|
|
|
+ sizeof(sqlite3Prng)
|
|
|
+ );
|
|
|
+}
|
|
|
+SQLITE_PRIVATE void sqlite3PrngResetState(void){
|
|
|
+ GLOBAL(struct sqlite3PrngType, sqlite3Prng).isInit = 0;
|
|
|
+}
|
|
|
+#endif /* SQLITE_OMIT_BUILTIN_TEST */
|
|
|
|
|
|
+/************** End of random.c **********************************************/
|
|
|
+/************** Begin file utf.c *********************************************/
|
|
|
/*
|
|
|
-** Acquire a lock on the handle h
|
|
|
+** 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
|
|
|
+**
|
|
|
*/
|
|
|
-static void winceMutexAcquire(HANDLE h){
|
|
|
- DWORD dwErr;
|
|
|
- do {
|
|
|
- dwErr = osWaitForSingleObject(h, INFINITE);
|
|
|
- } while (dwErr != WAIT_OBJECT_0 && dwErr != WAIT_ABANDONED);
|
|
|
-}
|
|
|
+/* #include <assert.h> */
|
|
|
+
|
|
|
+#ifndef SQLITE_AMALGAMATION
|
|
|
/*
|
|
|
-** Release a lock acquired by winceMutexAcquire()
|
|
|
+** The following constant value is used by the SQLITE_BIGENDIAN and
|
|
|
+** SQLITE_LITTLEENDIAN macros.
|
|
|
*/
|
|
|
-#define winceMutexRelease(h) ReleaseMutex(h)
|
|
|
+SQLITE_PRIVATE const int sqlite3one = 1;
|
|
|
+#endif /* SQLITE_AMALGAMATION */
|
|
|
|
|
|
/*
|
|
|
-** Create the mutex and shared memory used for locking in the file
|
|
|
-** descriptor pFile
|
|
|
+** This lookup table is used to help decode the first byte of
|
|
|
+** a multi-byte UTF8 character.
|
|
|
*/
|
|
|
-static int winceCreateLock(const char *zFilename, winFile *pFile){
|
|
|
- LPWSTR zTok;
|
|
|
- LPWSTR zName;
|
|
|
- DWORD lastErrno;
|
|
|
- BOOL bLogged = FALSE;
|
|
|
- BOOL bInit = TRUE;
|
|
|
+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,
|
|
|
+};
|
|
|
|
|
|
- zName = winUtf8ToUnicode(zFilename);
|
|
|
- if( zName==0 ){
|
|
|
- /* out of memory */
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
- }
|
|
|
|
|
|
- /* Initialize the local lockdata */
|
|
|
- memset(&pFile->local, 0, sizeof(pFile->local));
|
|
|
+#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); \
|
|
|
+ } \
|
|
|
+}
|
|
|
|
|
|
- /* Replace the backslashes from the filename and lowercase it
|
|
|
- ** to derive a mutex name. */
|
|
|
- zTok = osCharLowerW(zName);
|
|
|
- for (;*zTok;zTok++){
|
|
|
- if (*zTok == '\\') *zTok = '_';
|
|
|
- }
|
|
|
+#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)); \
|
|
|
+ } \
|
|
|
+}
|
|
|
|
|
|
- /* 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);
|
|
|
- }
|
|
|
+#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); \
|
|
|
+ } \
|
|
|
+}
|
|
|
|
|
|
- /* 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));
|
|
|
- }
|
|
|
+#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); \
|
|
|
+ } \
|
|
|
+}
|
|
|
|
|
|
- winceMutexRelease(pFile->hMutex);
|
|
|
- return SQLITE_OK;
|
|
|
+#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); \
|
|
|
+ } \
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Destroy the part of winFile that deals with wince locks
|
|
|
+** 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 an infinite number of different UTF8 encodings
|
|
|
+** for unicode values 0x80 and greater. It do not change over-length
|
|
|
+** encodings to 0xfffd as some systems recommend.
|
|
|
*/
|
|
|
-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;
|
|
|
+#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; } \
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** An implementation of the LockFile() API of Windows for CE
|
|
|
-*/
|
|
|
-static BOOL winceLockFile(
|
|
|
- LPHANDLE phFile,
|
|
|
- DWORD dwFileOffsetLow,
|
|
|
- DWORD dwFileOffsetHigh,
|
|
|
- DWORD nNumberOfBytesToLockLow,
|
|
|
- DWORD nNumberOfBytesToLockHigh
|
|
|
+SQLITE_PRIVATE u32 sqlite3Utf8Read(
|
|
|
+ const unsigned char **pz /* Pointer to string from which to read char */
|
|
|
){
|
|
|
- winFile *pFile = HANDLE_TO_WINFILE(phFile);
|
|
|
- BOOL bReturn = FALSE;
|
|
|
-
|
|
|
- UNUSED_PARAMETER(dwFileOffsetHigh);
|
|
|
- UNUSED_PARAMETER(nNumberOfBytesToLockHigh);
|
|
|
-
|
|
|
- if (!pFile->hMutex) return TRUE;
|
|
|
- winceMutexAcquire(pFile->hMutex);
|
|
|
+ unsigned int c;
|
|
|
|
|
|
- /* 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;
|
|
|
+ /* 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;
|
|
|
+}
|
|
|
|
|
|
- /* 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;
|
|
|
-}
|
|
|
+/*
|
|
|
+** 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
|
|
|
/*
|
|
|
-** An implementation of the UnlockFile API of Windows for CE
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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);
|
|
|
+SQLITE_PRIVATE 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;
|
|
|
|
|
|
- /* 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;
|
|
|
- }
|
|
|
+ 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 );
|
|
|
|
|
|
- /* 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;
|
|
|
- }
|
|
|
+#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
|
|
|
+ {
|
|
|
+ char zBuf[100];
|
|
|
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
|
|
+ fprintf(stderr, "INPUT: %s\n", zBuf);
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
- /* 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;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
- }
|
|
|
- /* 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;
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- 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);
|
|
|
+ /* 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{
|
|
|
- return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
|
|
|
- numBytesHigh);
|
|
|
+ /* 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;
|
|
|
}
|
|
|
-#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.
|
|
|
+ /* 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().
|
|
|
*/
|
|
|
- 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);
|
|
|
+ zIn = (u8*)pMem->z;
|
|
|
+ zTerm = &zIn[pMem->n];
|
|
|
+ zOut = sqlite3DbMallocRaw(pMem->db, len);
|
|
|
+ if( !zOut ){
|
|
|
+ return SQLITE_NOMEM;
|
|
|
}
|
|
|
-#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);
|
|
|
+ z = zOut;
|
|
|
|
|
|
- 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;
|
|
|
+ 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 );
|
|
|
|
|
|
- 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);
|
|
|
+ sqlite3VdbeMemRelease(pMem);
|
|
|
+ pMem->flags &= ~(MEM_Static|MEM_Dyn|MEM_Ephem);
|
|
|
+ pMem->enc = desiredEnc;
|
|
|
+ pMem->flags |= (MEM_Term|MEM_Dyn);
|
|
|
+ pMem->z = (char*)zOut;
|
|
|
+ pMem->zMalloc = pMem->z;
|
|
|
|
|
|
- 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;
|
|
|
+translate_out:
|
|
|
+#if defined(TRANSLATE_TRACE) && defined(SQLITE_DEBUG)
|
|
|
+ {
|
|
|
+ char zBuf[100];
|
|
|
+ sqlite3VdbeMemPrettyPrint(pMem, zBuf);
|
|
|
+ fprintf(stderr, "OUTPUT: %s\n", zBuf);
|
|
|
}
|
|
|
-
|
|
|
- OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h));
|
|
|
- return 0;
|
|
|
#endif
|
|
|
+ return SQLITE_OK;
|
|
|
}
|
|
|
|
|
|
-#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.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** The allocation (static, dynamic etc.) and encoding of the Mem may be
|
|
|
+** changed by this function.
|
|
|
*/
|
|
|
-#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 file=%p\n", pFile->h));
|
|
|
-
|
|
|
-#if SQLITE_MAX_MMAP_SIZE>0
|
|
|
- winUnmapfile(pFile);
|
|
|
-#endif
|
|
|
+SQLITE_PRIVATE int sqlite3VdbeMemHandleBom(Mem *pMem){
|
|
|
+ int rc = SQLITE_OK;
|
|
|
+ u8 bom = 0;
|
|
|
|
|
|
- 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 */
|
|
|
+ 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;
|
|
|
}
|
|
|
- sqlite3_free(pFile->zDeleteOnClose);
|
|
|
}
|
|
|
-#endif
|
|
|
- if( rc ){
|
|
|
- pFile->h = NULL;
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
}
|
|
|
- OpenCounter(-1);
|
|
|
- OSTRACE(("CLOSE file=%p, rc=%s\n", pFile->h, rc ? "ok" : "failed"));
|
|
|
- return rc ? SQLITE_OK
|
|
|
- : winLogError(SQLITE_IOERR_CLOSE, osGetLastError(),
|
|
|
- "winClose", pFile->zPath);
|
|
|
+ return rc;
|
|
|
}
|
|
|
+#endif /* SQLITE_OMIT_UTF16 */
|
|
|
|
|
|
/*
|
|
|
-** Read data from a file into a buffer. Return SQLITE_OK if all
|
|
|
-** bytes were read successfully and SQLITE_IOERR if anything goes
|
|
|
-** wrong.
|
|
|
+** 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).
|
|
|
*/
|
|
|
-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
|
|
|
- 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 file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n",
|
|
|
- pFile->h, pBuf, amt, offset, pFile->locktype));
|
|
|
-
|
|
|
-#if 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 file=%p, rc=SQLITE_OK\n", 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
|
|
|
- if( winSeekFile(pFile, offset) ){
|
|
|
- OSTRACE(("READ file=%p, rc=SQLITE_FULL\n", pFile->h));
|
|
|
- return SQLITE_FULL;
|
|
|
+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);
|
|
|
}
|
|
|
- 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 file=%p, rc=SQLITE_IOERR_READ\n", pFile->h));
|
|
|
- return winLogError(SQLITE_IOERR_READ, pFile->lastErrno,
|
|
|
- "winRead", pFile->zPath);
|
|
|
- }
|
|
|
- winLogIoerr(nRetry);
|
|
|
- if( nRead<(DWORD)amt ){
|
|
|
- /* Unread parts of the buffer must be zero-filled */
|
|
|
- memset(&((char*)pBuf)[nRead], 0, amt-nRead);
|
|
|
- OSTRACE(("READ file=%p, rc=SQLITE_IOERR_SHORT_READ\n", pFile->h));
|
|
|
- return SQLITE_IOERR_SHORT_READ;
|
|
|
+ assert( z<=zTerm );
|
|
|
+ while( *z!=0 && z<zTerm ){
|
|
|
+ SQLITE_SKIP_UTF8(z);
|
|
|
+ r++;
|
|
|
}
|
|
|
-
|
|
|
- OSTRACE(("READ file=%p, rc=SQLITE_OK\n", pFile->h));
|
|
|
- return SQLITE_OK;
|
|
|
+ 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)
|
|
|
/*
|
|
|
-** Write data from a buffer into a file. Return SQLITE_OK on success
|
|
|
-** or some other error code on failure.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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 file=%p, buffer=%p, amount=%d, offset=%lld, lock=%d\n",
|
|
|
- pFile->h, pBuf, amt, offset, pFile->locktype));
|
|
|
+SQLITE_PRIVATE int sqlite3Utf8To8(unsigned char *zIn){
|
|
|
+ unsigned char *zOut = zIn;
|
|
|
+ unsigned char *zStart = zIn;
|
|
|
+ u32 c;
|
|
|
|
|
|
-#if 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 file=%p, rc=SQLITE_OK\n", 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;
|
|
|
+ while( zIn[0] && zOut<=zIn ){
|
|
|
+ c = sqlite3Utf8Read((const u8**)&zIn);
|
|
|
+ if( c!=0xfffd ){
|
|
|
+ WRITE_UTF8(zOut, c);
|
|
|
}
|
|
|
}
|
|
|
+ *zOut = 0;
|
|
|
+ return (int)(zOut - zStart);
|
|
|
+}
|
|
|
#endif
|
|
|
|
|
|
-#if SQLITE_OS_WINCE
|
|
|
- rc = winSeekFile(pFile, offset);
|
|
|
- if( rc==0 ){
|
|
|
-#else
|
|
|
- {
|
|
|
-#endif
|
|
|
-#if !SQLITE_OS_WINCE
|
|
|
- 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
|
|
|
- memset(&overlapped, 0, sizeof(OVERLAPPED));
|
|
|
- overlapped.Offset = (LONG)(offset & 0xffffffff);
|
|
|
- overlapped.OffsetHigh = (LONG)((offset>>32) & 0x7fffffff);
|
|
|
-#endif
|
|
|
-
|
|
|
- while( nRem>0 ){
|
|
|
-#if SQLITE_OS_WINCE
|
|
|
- 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
|
|
|
- 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;
|
|
|
- }
|
|
|
+#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.flags & MEM_Dyn)!=0 || db->mallocFailed );
|
|
|
+ assert( m.z || db->mallocFailed );
|
|
|
+ return m.z;
|
|
|
+}
|
|
|
|
|
|
- if( rc ){
|
|
|
- if( ( pFile->lastErrno==ERROR_HANDLE_DISK_FULL )
|
|
|
- || ( pFile->lastErrno==ERROR_DISK_FULL )){
|
|
|
- OSTRACE(("WRITE file=%p, rc=SQLITE_FULL\n", pFile->h));
|
|
|
- return winLogError(SQLITE_FULL, pFile->lastErrno,
|
|
|
- "winWrite1", pFile->zPath);
|
|
|
- }
|
|
|
- OSTRACE(("WRITE file=%p, rc=SQLITE_IOERR_WRITE\n", pFile->h));
|
|
|
- return winLogError(SQLITE_IOERR_WRITE, pFile->lastErrno,
|
|
|
- "winWrite2", pFile->zPath);
|
|
|
+/*
|
|
|
+** 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{
|
|
|
- winLogIoerr(nRetry);
|
|
|
+ while( n<nChar ){
|
|
|
+ READ_UTF16LE(z, 1, c);
|
|
|
+ n++;
|
|
|
+ }
|
|
|
}
|
|
|
- OSTRACE(("WRITE file=%p, rc=SQLITE_OK\n", pFile->h));
|
|
|
- return SQLITE_OK;
|
|
|
+ return (int)(z-(unsigned char const *)zIn);
|
|
|
}
|
|
|
|
|
|
+#if defined(SQLITE_TEST)
|
|
|
/*
|
|
|
-** Truncate an open file to a specified size
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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 file=%p, size=%lld, lock=%d\n",
|
|
|
- pFile->h, nByte, pFile->locktype));
|
|
|
+SQLITE_PRIVATE void sqlite3UtfSelfTest(void){
|
|
|
+ unsigned int i, t;
|
|
|
+ unsigned char zBuf[20];
|
|
|
+ unsigned char *z;
|
|
|
+ int n;
|
|
|
+ unsigned int c;
|
|
|
|
|
|
- /* 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;
|
|
|
+ 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 );
|
|
|
}
|
|
|
-
|
|
|
- /* 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);
|
|
|
+ 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 );
|
|
|
}
|
|
|
-
|
|
|
-#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;
|
|
|
+ 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
|
|
|
-
|
|
|
- OSTRACE(("TRUNCATE file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
|
|
- return rc;
|
|
|
}
|
|
|
+#endif /* SQLITE_TEST */
|
|
|
+#endif /* SQLITE_OMIT_UTF16 */
|
|
|
|
|
|
-#ifdef SQLITE_TEST
|
|
|
+/************** End of utf.c *************************************************/
|
|
|
+/************** Begin file util.c ********************************************/
|
|
|
/*
|
|
|
-** Count the number of fullsyncs and normal syncs. This is used to test
|
|
|
-** that syncs and fullsyncs are occuring at the right times.
|
|
|
+** 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.
|
|
|
+**
|
|
|
*/
|
|
|
-SQLITE_API int sqlite3_sync_count = 0;
|
|
|
-SQLITE_API int sqlite3_fullsync_count = 0;
|
|
|
+/* #include <stdarg.h> */
|
|
|
+#ifdef SQLITE_HAVE_ISNAN
|
|
|
+# include <math.h>
|
|
|
#endif
|
|
|
|
|
|
/*
|
|
|
-** Make sure all writes to a particular file are committed to disk.
|
|
|
+** Routine needed to support the testcase() macro.
|
|
|
*/
|
|
|
-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_TEST) && defined(SQLITE_DEBUG))
|
|
|
- /*
|
|
|
- ** 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 file=%p, flags=%x, lock=%d\n",
|
|
|
- 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 file=%p, rc=SQLITE_OK\n", pFile->h));
|
|
|
- return SQLITE_OK;
|
|
|
-#else
|
|
|
- rc = osFlushFileBuffers(pFile->h);
|
|
|
- SimulateIOError( rc=FALSE );
|
|
|
- if( rc ){
|
|
|
- OSTRACE(("SYNC file=%p, rc=SQLITE_OK\n", pFile->h));
|
|
|
- return SQLITE_OK;
|
|
|
- }else{
|
|
|
- pFile->lastErrno = osGetLastError();
|
|
|
- OSTRACE(("SYNC file=%p, rc=SQLITE_IOERR_FSYNC\n", pFile->h));
|
|
|
- return winLogError(SQLITE_IOERR_FSYNC, pFile->lastErrno,
|
|
|
- "winSync", pFile->zPath);
|
|
|
- }
|
|
|
-#endif
|
|
|
+#ifdef SQLITE_COVERAGE_TEST
|
|
|
+SQLITE_PRIVATE void sqlite3Coverage(int x){
|
|
|
+ static unsigned dummy = 0;
|
|
|
+ dummy += (unsigned)x;
|
|
|
}
|
|
|
+#endif
|
|
|
|
|
|
+#ifndef SQLITE_OMIT_FLOATING_POINT
|
|
|
/*
|
|
|
-** Determine the current size of a file in bytes
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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);
|
|
|
- }
|
|
|
- }
|
|
|
+SQLITE_PRIVATE int sqlite3IsNaN(double x){
|
|
|
+ int rc; /* The value return */
|
|
|
+#if !defined(SQLITE_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
|
|
|
- OSTRACE(("SIZE file=%p, pSize=%p, *pSize=%lld, rc=%s\n",
|
|
|
- pFile->h, pSize, *pSize, sqlite3ErrName(rc)));
|
|
|
+ volatile double y = x;
|
|
|
+ volatile double z = y;
|
|
|
+ rc = (y!=z);
|
|
|
+#else /* if defined(SQLITE_HAVE_ISNAN) */
|
|
|
+ rc = isnan(x);
|
|
|
+#endif /* SQLITE_HAVE_ISNAN */
|
|
|
+ testcase( rc );
|
|
|
return rc;
|
|
|
}
|
|
|
+#endif /* SQLITE_OMIT_FLOATING_POINT */
|
|
|
|
|
|
/*
|
|
|
-** LOCKFILE_FAIL_IMMEDIATELY is undefined on some Windows systems.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#ifndef LOCKFILE_FAIL_IMMEDIATELY
|
|
|
-# define LOCKFILE_FAIL_IMMEDIATELY 1
|
|
|
-#endif
|
|
|
-
|
|
|
-#ifndef LOCKFILE_EXCLUSIVE_LOCK
|
|
|
-# define LOCKFILE_EXCLUSIVE_LOCK 2
|
|
|
-#endif
|
|
|
+SQLITE_PRIVATE int sqlite3Strlen30(const char *z){
|
|
|
+ const char *z2 = z;
|
|
|
+ if( z==0 ) return 0;
|
|
|
+ while( *z2 ){ z2++; }
|
|
|
+ return 0x3fffffff & (int)(z2 - z);
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#ifndef SQLITE_LOCKFILE_FLAGS
|
|
|
-# define SQLITE_LOCKFILE_FLAGS (LOCKFILE_FAIL_IMMEDIATELY | \
|
|
|
- LOCKFILE_EXCLUSIVE_LOCK)
|
|
|
-#endif
|
|
|
+SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code, const char *zFormat, ...){
|
|
|
+ if( db && (db->pErr || (db->pErr = sqlite3ValueNew(db))!=0) ){
|
|
|
+ db->errCode = err_code;
|
|
|
+ if( zFormat ){
|
|
|
+ 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);
|
|
|
+ }else{
|
|
|
+ sqlite3ValueSetStr(db->pErr, 0, 0, SQLITE_UTF8, SQLITE_STATIC);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** Currently, SQLite never calls the LockFileEx function without wanting the
|
|
|
-** call to fail immediately if the lock cannot be obtained.
|
|
|
+** 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 whilst
|
|
|
+** 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().
|
|
|
+** Function sqlite3Error() should be used during statement execution
|
|
|
+** (sqlite3_step() etc.).
|
|
|
*/
|
|
|
-#ifndef SQLITE_LOCKFILEEX_FLAGS
|
|
|
-# define SQLITE_LOCKFILEEX_FLAGS (LOCKFILE_FAIL_IMMEDIATELY)
|
|
|
-#endif
|
|
|
+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;
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
/*
|
|
|
-** Acquire a reader lock.
|
|
|
-** Different API routines are called depending on whether or not this
|
|
|
-** is Win9x or WinNT.
|
|
|
+** 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 identifers. For example: "[a-b-c]" becomes
|
|
|
+** "a-b-c".
|
|
|
*/
|
|
|
-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);
|
|
|
+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;
|
|
|
}
|
|
|
-#endif
|
|
|
- if( res == 0 ){
|
|
|
- pFile->lastErrno = osGetLastError();
|
|
|
- /* No need to log a failure to lock */
|
|
|
+ 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];
|
|
|
+ }
|
|
|
}
|
|
|
- OSTRACE(("READ-LOCK file=%p, rc=%s\n", pFile->h, sqlite3ErrName(res)));
|
|
|
- return res;
|
|
|
+ z[j] = 0;
|
|
|
+ return j;
|
|
|
}
|
|
|
|
|
|
+/* Convenient short-hand */
|
|
|
+#define UpperToLower sqlite3UpperToLower
|
|
|
+
|
|
|
/*
|
|
|
-** Undo a readlock
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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, rc=%s\n", pFile->h, sqlite3ErrName(res)));
|
|
|
- return res;
|
|
|
+SQLITE_API int sqlite3_stricmp(const char *zLeft, const char *zRight){
|
|
|
+ register unsigned char *a, *b;
|
|
|
+ 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 sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){
|
|
|
+ register unsigned char *a, *b;
|
|
|
+ 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];
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Lock the file with the lock specified by parameter locktype - one
|
|
|
-** of the following:
|
|
|
+** The string z[] is an text representation of a real number.
|
|
|
+** Convert this string to a double and write it into *pResult.
|
|
|
**
|
|
|
-** (1) SHARED_LOCK
|
|
|
-** (2) RESERVED_LOCK
|
|
|
-** (3) PENDING_LOCK
|
|
|
-** (4) EXCLUSIVE_LOCK
|
|
|
+** The string z[] is length bytes in length (bytes, not characters) and
|
|
|
+** uses the encoding enc. The string is not necessarily zero-terminated.
|
|
|
**
|
|
|
-** 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:
|
|
|
+** 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:
|
|
|
**
|
|
|
-** UNLOCKED -> SHARED
|
|
|
-** SHARED -> RESERVED
|
|
|
-** SHARED -> (PENDING) -> EXCLUSIVE
|
|
|
-** RESERVED -> (PENDING) -> EXCLUSIVE
|
|
|
-** PENDING -> EXCLUSIVE
|
|
|
+** [+-]digits[E[+-]digits]
|
|
|
+** [+-]digits.[digits][E[+-]digits]
|
|
|
+** [+-].digits[E[+-]digits]
|
|
|
**
|
|
|
-** 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;
|
|
|
- }
|
|
|
+** 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;
|
|
|
|
|
|
- /* 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 );
|
|
|
+ assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
|
|
|
+ *pResult = 0.0; /* Default return value, in case of an error */
|
|
|
|
|
|
- /* 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.
|
|
|
- */
|
|
|
- OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, rc=%s\n",
|
|
|
- pFile->h, cnt, sqlite3ErrName(res)));
|
|
|
- if( cnt ) sqlite3_win32_sleep(1);
|
|
|
- }
|
|
|
- gotPendingLock = res;
|
|
|
- if( !res ){
|
|
|
- lastErrno = osGetLastError();
|
|
|
- }
|
|
|
+ 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);
|
|
|
}
|
|
|
|
|
|
- /* 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();
|
|
|
- }
|
|
|
- }
|
|
|
+ /* skip leading spaces */
|
|
|
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
|
|
+ if( z>=zEnd ) return 0;
|
|
|
|
|
|
- /* 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();
|
|
|
- }
|
|
|
+ /* get sign of significand */
|
|
|
+ if( *z=='-' ){
|
|
|
+ sign = -1;
|
|
|
+ z+=incr;
|
|
|
+ }else if( *z=='+' ){
|
|
|
+ z+=incr;
|
|
|
}
|
|
|
|
|
|
- /* Acquire a PENDING lock
|
|
|
- */
|
|
|
- if( locktype==EXCLUSIVE_LOCK && res ){
|
|
|
- newLocktype = PENDING_LOCK;
|
|
|
- gotPendingLock = 0;
|
|
|
- }
|
|
|
+ /* skip leading zeroes */
|
|
|
+ while( z<zEnd && z[0]=='0' ) z+=incr, nDigits++;
|
|
|
|
|
|
- /* 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);
|
|
|
- }
|
|
|
+ /* copy max significant digits to significand */
|
|
|
+ while( z<zEnd && sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){
|
|
|
+ s = s*10 + (*z - '0');
|
|
|
+ z+=incr, nDigits++;
|
|
|
}
|
|
|
|
|
|
- /* 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);
|
|
|
- }
|
|
|
+ /* 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;
|
|
|
|
|
|
- /* 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));
|
|
|
+ /* 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++;
|
|
|
}
|
|
|
- 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 rc;
|
|
|
- winFile *pFile = (winFile*)id;
|
|
|
-
|
|
|
- SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; );
|
|
|
- OSTRACE(("TEST-WR-LOCK file=%p, pResOut=%p\n", pFile->h, pResOut));
|
|
|
+ if( z>=zEnd ) goto do_atof_calc;
|
|
|
|
|
|
- assert( id!=0 );
|
|
|
- if( pFile->locktype>=RESERVED_LOCK ){
|
|
|
- rc = 1;
|
|
|
- OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (local)\n", pFile->h, rc));
|
|
|
- }else{
|
|
|
- rc = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS,RESERVED_BYTE, 0, 1, 0);
|
|
|
- if( rc ){
|
|
|
- winUnlockFile(&pFile->h, RESERVED_BYTE, 0, 1, 0);
|
|
|
+ /* 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;
|
|
|
}
|
|
|
- rc = !rc;
|
|
|
- OSTRACE(("TEST-WR-LOCK file=%p, rc=%d (remote)\n", pFile->h, rc));
|
|
|
}
|
|
|
- *pResOut = rc;
|
|
|
- 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 inititially 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;
|
|
|
+ /* skip trailing spaces */
|
|
|
+ if( nDigits && eValid ){
|
|
|
+ while( z<zEnd && sqlite3Isspace(*z) ) z+=incr;
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-/* 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 *);
|
|
|
+do_atof_calc:
|
|
|
+ /* adjust exponent by d, and update sign */
|
|
|
+ e = (e*esign) + d;
|
|
|
+ if( e<0 ) {
|
|
|
+ esign = -1;
|
|
|
+ e *= -1;
|
|
|
+ } else {
|
|
|
+ esign = 1;
|
|
|
+ }
|
|
|
|
|
|
-/*
|
|
|
-** 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;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
- 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);
|
|
|
- }
|
|
|
+
|
|
|
+ /* 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 */
|
|
|
}
|
|
|
- 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("win32");
|
|
|
- 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;
|
|
|
- }
|
|
|
- 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);
|
|
|
+ /* 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;
|
|
|
}
|
|
|
}
|
|
|
- OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
|
|
- return rc;
|
|
|
+ } else {
|
|
|
+ result = (double)s;
|
|
|
}
|
|
|
-#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;
|
|
|
-}
|
|
|
+ /* store the result */
|
|
|
+ *pResult = result;
|
|
|
|
|
|
-/*
|
|
|
-** 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);
|
|
|
+ /* 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 */
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-** 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.
|
|
|
-*/
|
|
|
-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.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** 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,
|
|
|
**
|
|
|
-** winShmEnterMutex()
|
|
|
-** assert( winShmMutexHeld() );
|
|
|
-** winShmLeaveMutex()
|
|
|
+** compare2pow63("9223372036854775800", 1)
|
|
|
+**
|
|
|
+** will return -8.
|
|
|
*/
|
|
|
-static void winShmEnterMutex(void){
|
|
|
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
|
-}
|
|
|
-static void winShmLeaveMutex(void){
|
|
|
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
|
-}
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
-static int winShmMutexHeld(void) {
|
|
|
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
|
|
|
+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;
|
|
|
}
|
|
|
-#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 */
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- u8 nextShmId; /* Next available winShm.id value */
|
|
|
-#endif
|
|
|
-};
|
|
|
|
|
|
/*
|
|
|
-** A global array of all winShmNode objects.
|
|
|
+** Convert zNum to a 64-bit signed integer.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** If the zNum value is representable as a 64-bit twos-complement
|
|
|
+** integer, then write that value into *pNum and return 0.
|
|
|
**
|
|
|
-** The following fields are initialized when this object is created and
|
|
|
-** are read-only thereafter:
|
|
|
+** If zNum is exactly 9223372036854665808, return 2. This special
|
|
|
+** case is broken out because while 9223372036854665808 cannot be a
|
|
|
+** signed 64-bit integer, its negative -9223372036854665808 can be.
|
|
|
**
|
|
|
-** winShm.pShmNode
|
|
|
-** winShm.id
|
|
|
+** If zNum is too big for a 64-bit integer and is not
|
|
|
+** 9223372036854665808 or if zNum contains any non-numeric text,
|
|
|
+** then return 1.
|
|
|
**
|
|
|
-** 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 */
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- 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.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#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;
|
|
|
+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{
|
|
|
- pFile->lastErrno = osGetLastError();
|
|
|
- rc = SQLITE_BUSY;
|
|
|
+ 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);
|
|
|
}
|
|
|
-
|
|
|
- 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;
|
|
|
+ while( zNum<zEnd && sqlite3Isspace(*zNum) ) zNum+=incr;
|
|
|
+ if( zNum<zEnd ){
|
|
|
+ if( *zNum=='-' ){
|
|
|
+ neg = 1;
|
|
|
+ zNum+=incr;
|
|
|
+ }else if( *zNum=='+' ){
|
|
|
+ zNum+=incr;
|
|
|
}
|
|
|
}
|
|
|
-}
|
|
|
-
|
|
|
-/*
|
|
|
-** 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;
|
|
|
+ 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';
|
|
|
}
|
|
|
- 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( u>LARGEST_INT64 ){
|
|
|
+ *pNum = SMALLEST_INT64;
|
|
|
+ }else if( neg ){
|
|
|
+ *pNum = -(i64)u;
|
|
|
+ }else{
|
|
|
+ *pNum = (i64)u;
|
|
|
}
|
|
|
- if( pShmNode ){
|
|
|
- sqlite3_free(pNew);
|
|
|
+ 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{
|
|
|
- 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);
|
|
|
+ /* 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 );
|
|
|
+ assert( (*pNum)==SMALLEST_INT64 );
|
|
|
+ return neg ? 0 : 2;
|
|
|
}
|
|
|
- if( rc ) goto shm_open_err;
|
|
|
- }
|
|
|
-
|
|
|
- /* Make the new connection a child of the winShmNode */
|
|
|
- p->pShmNode = pShmNode;
|
|
|
-#ifdef SQLITE_DEBUG
|
|
|
- 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.
|
|
|
+** If zNum represents an integer that will fit in 32-bits, then set
|
|
|
+** *pValue to that integer and return true. Otherwise return false.
|
|
|
+**
|
|
|
+** Any non-numeric characters that following zNum are ignored.
|
|
|
+** This is different from sqlite3Atoi64() which requires the
|
|
|
+** input number to be zero-terminated.
|
|
|
*/
|
|
|
-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);
|
|
|
+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++;
|
|
|
+ }
|
|
|
+ while( zNum[0]=='0' ) zNum++;
|
|
|
+ for(i=0; i<11 && (c = zNum[i] - '0')>=0 && c<=9; i++){
|
|
|
+ v = v*10 + c;
|
|
|
}
|
|
|
- winShmLeaveMutex();
|
|
|
|
|
|
- return SQLITE_OK;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Change the lock state for a shared-memory segment.
|
|
|
+** Return a 32-bit integer value extracted from a string. If the
|
|
|
+** string is not an integer, just return 0.
|
|
|
*/
|
|
|
-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;
|
|
|
+SQLITE_PRIVATE int sqlite3Atoi(const char *z){
|
|
|
+ int x = 0;
|
|
|
+ if( z ) sqlite3GetInt32(z, &x);
|
|
|
+ return x;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Implement a memory barrier or memory fence on shared memory.
|
|
|
+** 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
|
|
|
**
|
|
|
-** All loads and stores begun before the barrier must complete before
|
|
|
-** any load or store begun after the barrier.
|
|
|
+** 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
|
|
|
*/
|
|
|
-static void winShmBarrier(
|
|
|
- sqlite3_file *fd /* Database holding the shared memory */
|
|
|
-){
|
|
|
- UNUSED_PARAMETER(fd);
|
|
|
- /* MemoryBarrier(); // does not work -- do not know why not */
|
|
|
- winShmEnterMutex();
|
|
|
- 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.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** 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 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 *p = pDbFd->pShm;
|
|
|
- winShmNode *pShmNode;
|
|
|
- int rc = SQLITE_OK;
|
|
|
-
|
|
|
- if( !p ){
|
|
|
- rc = winOpenSharedMemory(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 );
|
|
|
-
|
|
|
- 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_realloc(
|
|
|
- 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++;
|
|
|
+SQLITE_PRIVATE int sqlite3PutVarint(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];
|
|
|
}
|
|
|
-
|
|
|
-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;
|
|
|
+ return n;
|
|
|
}
|
|
|
|
|
|
-#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.
|
|
|
+** This routine is a faster version of sqlite3PutVarint() that only
|
|
|
+** works for 32-bit positive integers and which is optimized for
|
|
|
+** the common case of small integers. A MACRO version, putVarint32,
|
|
|
+** 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.
|
|
|
*/
|
|
|
-#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;
|
|
|
+SQLITE_PRIVATE int sqlite3PutVarint32(unsigned char *p, u32 v){
|
|
|
+#ifndef putVarint32
|
|
|
+ if( (v & ~0x7f)==0 ){
|
|
|
+ p[0] = v;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ if( (v & ~0x3fff)==0 ){
|
|
|
+ p[0] = (u8)((v>>7) | 0x80);
|
|
|
+ p[1] = (u8)(v & 0x7f);
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
+ return sqlite3PutVarint(p, v);
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** SLOT_2_0 A mask for (0x7f<<14) | 0x7f
|
|
|
**
|
|
|
-** 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.
|
|
|
+** SLOT_4_2_0 A mask for (0x7f<<28) | SLOT_2_0
|
|
|
*/
|
|
|
-static int winMapfile(winFile *pFd, sqlite3_int64 nByte){
|
|
|
- sqlite3_int64 nMap = nByte;
|
|
|
- int rc;
|
|
|
+#define SLOT_2_0 0x001fc07f
|
|
|
+#define SLOT_4_2_0 0xf01fc07f
|
|
|
|
|
|
- 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;
|
|
|
+/*
|
|
|
+** 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;
|
|
|
|
|
|
- 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);
|
|
|
- if( (pFd->ctrlFlags & WINFILE_RDONLY)==0 ){
|
|
|
- protect = PAGE_READWRITE;
|
|
|
- flags |= FILE_MAP_WRITE;
|
|
|
- }
|
|
|
-#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;
|
|
|
+ a = *p;
|
|
|
+ /* a: p0 (unmasked) */
|
|
|
+ if (!(a&0x80))
|
|
|
+ {
|
|
|
+ *v = a;
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
- 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;
|
|
|
+ p++;
|
|
|
+ b = *p;
|
|
|
+ /* b: p1 (unmasked) */
|
|
|
+ if (!(b&0x80))
|
|
|
+ {
|
|
|
+ a &= 0x7f;
|
|
|
+ a = a<<7;
|
|
|
+ a |= b;
|
|
|
+ *v = a;
|
|
|
+ return 2;
|
|
|
+ }
|
|
|
|
|
|
- OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
|
|
|
- osGetCurrentProcessId(), fd, iOff, nAmt, pp));
|
|
|
+ /* Verify that constants are precomputed correctly */
|
|
|
+ assert( SLOT_2_0 == ((0x7f<<14) | (0x7f)) );
|
|
|
+ assert( SLOT_4_2_0 == ((0xfU<<28) | (0x7f<<14) | (0x7f)) );
|
|
|
|
|
|
-#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++;
|
|
|
- }
|
|
|
+ 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;
|
|
|
}
|
|
|
-#endif
|
|
|
|
|
|
- OSTRACE(("FETCH pid=%lu, pFile=%p, pp=%p, *pp=%p, rc=SQLITE_OK\n",
|
|
|
- osGetCurrentProcessId(), fd, pp, *pp));
|
|
|
- return SQLITE_OK;
|
|
|
-}
|
|
|
+ /* 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;
|
|
|
+ }
|
|
|
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
+ /* 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) */
|
|
|
|
|
|
- /* 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) );
|
|
|
+ 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 in calc'ing 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;
|
|
|
+ }
|
|
|
|
|
|
- /* If p!=0, it must match the iOff value. */
|
|
|
- assert( p==0 || p==&((u8 *)pFd->pMapRegion)[iOff] );
|
|
|
+ /* 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) */
|
|
|
|
|
|
- OSTRACE(("UNFETCH pid=%lu, pFile=%p, offset=%lld, p=%p\n",
|
|
|
- osGetCurrentProcessId(), pFd, iOff, p));
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- 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 can be omitted - potentially improving
|
|
|
- ** performance. */
|
|
|
- winUnmapfile(pFd);
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- assert( pFd->nFetchOut>=0 );
|
|
|
-#endif
|
|
|
+ /* 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;
|
|
|
+ }
|
|
|
|
|
|
- OSTRACE(("UNFETCH pid=%lu, pFile=%p, rc=SQLITE_OK\n",
|
|
|
- osGetCurrentProcessId(), fd));
|
|
|
- return SQLITE_OK;
|
|
|
-}
|
|
|
+ p++;
|
|
|
+ a = a<<15;
|
|
|
+ a |= *p;
|
|
|
+ /* a: p4<<29 | p6<<15 | p8 (unmasked) */
|
|
|
|
|
|
-/*
|
|
|
-** Here ends the implementation of all sqlite3_file methods.
|
|
|
-**
|
|
|
-********************** End sqlite3_file Methods *******************************
|
|
|
-******************************************************************************/
|
|
|
+ /* moved CSE2 up */
|
|
|
+ /* a &= (0x7f<<29)|(0x7f<<15)|(0xff); */
|
|
|
+ b &= SLOT_2_0;
|
|
|
+ b = b<<8;
|
|
|
+ a |= b;
|
|
|
|
|
|
-/*
|
|
|
-** 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 */
|
|
|
-};
|
|
|
+ s = s<<4;
|
|
|
+ b = p[-4];
|
|
|
+ b &= 0x7f;
|
|
|
+ b = b>>3;
|
|
|
+ s |= b;
|
|
|
|
|
|
-/****************************************************************************
|
|
|
-**************************** sqlite3_vfs methods ****************************
|
|
|
-**
|
|
|
-** This division contains the implementation of methods on the
|
|
|
-** sqlite3_vfs object.
|
|
|
-*/
|
|
|
+ *v = ((u64)s)<<32 | a;
|
|
|
|
|
|
-#if 0
|
|
|
-/*
|
|
|
-** 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;
|
|
|
+ return 9;
|
|
|
}
|
|
|
-#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.
|
|
|
+** 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.
|
|
|
*/
|
|
|
-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;
|
|
|
-}
|
|
|
+SQLITE_PRIVATE u8 sqlite3GetVarint32(const unsigned char *p, u32 *v){
|
|
|
+ u32 a,b;
|
|
|
|
|
|
-/*
|
|
|
-** This function returns non-zero if the specified UTF-8 string buffer
|
|
|
-** ends with a directory separator character.
|
|
|
-*/
|
|
|
-static int winEndsInDirSep(char *zBuf){
|
|
|
- if( zBuf ){
|
|
|
- int nLen = sqlite3Strlen30(zBuf);
|
|
|
- return nLen>0 && winIsDirSep(zBuf[nLen-1]);
|
|
|
+ /* 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;
|
|
|
}
|
|
|
- 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 nBuf, 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 );
|
|
|
+#endif
|
|
|
|
|
|
- /* Allocate a temporary buffer to store the fully qualified file
|
|
|
- ** name for the temporary file. If this fails, we cannot continue.
|
|
|
- */
|
|
|
- nBuf = pVfs->mxPathname;
|
|
|
- zBuf = sqlite3MallocZero( nBuf+2 );
|
|
|
- if( !zBuf ){
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
+ /* 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;
|
|
|
}
|
|
|
|
|
|
- /* 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.
|
|
|
- */
|
|
|
- assert( nBuf>30 );
|
|
|
- if( sqlite3_temp_directory ){
|
|
|
- sqlite3_snprintf(nBuf-30, zBuf, "%s%s", sqlite3_temp_directory,
|
|
|
- winEndsInDirSep(sqlite3_temp_directory) ? "" :
|
|
|
- winGetDirDep());
|
|
|
- }
|
|
|
-#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 prior 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(nBuf-30, zBuf, "%s", zDir);
|
|
|
- sqlite3_free(zConverted);
|
|
|
- break;
|
|
|
- }
|
|
|
- sqlite3_free(zConverted);
|
|
|
- }else{
|
|
|
- zConverted = sqlite3MallocZero( nBuf+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, nBuf+1)<0 ){
|
|
|
- sqlite3_free(zConverted);
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
|
|
|
- return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
|
|
|
- "winGetTempname1", 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).
|
|
|
- */
|
|
|
- if( osIsNT() ){
|
|
|
- char *zUtf8 = winUnicodeToUtf8(zConverted);
|
|
|
- if( !zUtf8 ){
|
|
|
- sqlite3_free(zConverted);
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
- }
|
|
|
- sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
|
|
|
- sqlite3_free(zUtf8);
|
|
|
- sqlite3_free(zConverted);
|
|
|
- break;
|
|
|
- }else{
|
|
|
- sqlite3_snprintf(nBuf-30, zBuf, "%s", zConverted);
|
|
|
- sqlite3_free(zConverted);
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
- sqlite3_free(zConverted);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
|
|
|
- else if( osIsNT() ){
|
|
|
- char *zMulti;
|
|
|
- LPWSTR zWidePath = sqlite3MallocZero( nBuf*sizeof(WCHAR) );
|
|
|
- if( !zWidePath ){
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
- }
|
|
|
- if( osGetTempPathW(nBuf, zWidePath)==0 ){
|
|
|
- sqlite3_free(zWidePath);
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
|
|
- return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
|
|
|
- "winGetTempname1", 0);
|
|
|
- }
|
|
|
- zMulti = winUnicodeToUtf8(zWidePath);
|
|
|
- if( zMulti ){
|
|
|
- sqlite3_snprintf(nBuf-30, 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;
|
|
|
- }
|
|
|
+ /* 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;
|
|
|
}
|
|
|
-#ifdef SQLITE_WIN32_HAS_ANSI
|
|
|
- else{
|
|
|
- char *zUtf8;
|
|
|
- char *zMbcsPath = sqlite3MallocZero( nBuf );
|
|
|
- if( !zMbcsPath ){
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
- }
|
|
|
- if( osGetTempPathA(nBuf, zMbcsPath)==0 ){
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_GETTEMPPATH\n"));
|
|
|
- return winLogError(SQLITE_IOERR_GETTEMPPATH, osGetLastError(),
|
|
|
- "winGetTempname2", 0);
|
|
|
- }
|
|
|
- zUtf8 = sqlite3_win32_mbcs_to_utf8(zMbcsPath);
|
|
|
- if( zUtf8 ){
|
|
|
- sqlite3_snprintf(nBuf-30, zBuf, "%s", zUtf8);
|
|
|
- sqlite3_free(zUtf8);
|
|
|
+
|
|
|
+ /* 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{
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
|
|
|
- return SQLITE_IOERR_NOMEM;
|
|
|
+ *v = (u32)v64;
|
|
|
}
|
|
|
+ return n;
|
|
|
}
|
|
|
-#endif /* SQLITE_WIN32_HAS_ANSI */
|
|
|
-#endif /* !SQLITE_OS_WINRT */
|
|
|
|
|
|
- /* Check that the output buffer is large enough for the temporary file
|
|
|
- ** name. If it is not, return SQLITE_ERROR.
|
|
|
+#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.
|
|
|
*/
|
|
|
- nLen = sqlite3Strlen30(zBuf);
|
|
|
+ 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;
|
|
|
+ }
|
|
|
|
|
|
- if( (nLen + sqlite3Strlen30(SQLITE_TEMP_FILE_PREFIX) + 18) >= nBuf ){
|
|
|
- sqlite3_free(zBuf);
|
|
|
- OSTRACE(("TEMP-FILENAME rc=SQLITE_ERROR\n"));
|
|
|
- return winLogError(SQLITE_ERROR, 0, "winGetTempname3", 0);
|
|
|
+ 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;
|
|
|
}
|
|
|
|
|
|
- sqlite3_snprintf(nBuf-18-nLen, zBuf+nLen, SQLITE_TEMP_FILE_PREFIX);
|
|
|
+ /* 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;
|
|
|
|
|
|
- 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) ];
|
|
|
+ p -= 4;
|
|
|
+ n = sqlite3GetVarint(p, &v64);
|
|
|
+ assert( n>5 && n<=9 );
|
|
|
+ *v = (u32)v64;
|
|
|
+ return n;
|
|
|
}
|
|
|
- zBuf[j] = 0;
|
|
|
- zBuf[j+1] = 0;
|
|
|
- *pzBuf = zBuf;
|
|
|
-
|
|
|
- OSTRACE(("TEMP-FILENAME name=%s, rc=SQLITE_OK\n", zBuf));
|
|
|
- return SQLITE_OK;
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Return the number of bytes that will be needed to store the given
|
|
|
+** 64-bit integer.
|
|
|
*/
|
|
|
-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);
|
|
|
+SQLITE_PRIVATE int sqlite3VarintLen(u64 v){
|
|
|
+ int i = 0;
|
|
|
+ do{
|
|
|
+ i++;
|
|
|
+ v >>= 7;
|
|
|
+ }while( v!=0 && ALWAYS(i<9) );
|
|
|
+ return i;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/*
|
|
|
-** Open a file.
|
|
|
+** Read or write a four-byte big-endian integer value.
|
|
|
*/
|
|
|
-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;
|
|
|
- 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. */
|
|
|
+SQLITE_PRIVATE u32 sqlite3Get4byte(const u8 *p){
|
|
|
+ return (p[0]<<24) | (p[1]<<16) | (p[2]<<8) | p[3];
|
|
|
+}
|
|
|
+SQLITE_PRIVATE void sqlite3Put4byte(unsigned char *p, u32 v){
|
|
|
+ p[0] = (u8)(v>>24);
|
|
|
+ p[1] = (u8)(v>>16);
|
|
|
+ p[2] = (u8)(v>>8);
|
|
|
+ p[3] = (u8)v;
|
|
|
+}
|
|
|
|
|
|
- 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
|
|
|
- ));
|
|
|
+/*
|
|
|
+** 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
|
|
|
-
|
|
|
- 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");
|
|
|
- }
|
|
|
+#ifdef SQLITE_EBCDIC
|
|
|
+ h += 9*(1&~(h>>4));
|
|
|
#endif
|
|
|
+ return (u8)(h & 0xf);
|
|
|
+}
|
|
|
|
|
|
- /* 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;
|
|
|
+#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]);
|
|
|
}
|
|
|
- zUtf8Name = zTmpname;
|
|
|
+ zBlob[i/2] = 0;
|
|
|
}
|
|
|
+ return zBlob;
|
|
|
+}
|
|
|
+#endif /* !SQLITE_OMIT_BLOB_LITERAL || SQLITE_HAS_CODEC */
|
|
|
|
|
|
- /* 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 );
|
|
|
+/*
|
|
|
+** 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
|
|
|
+ );
|
|
|
+}
|
|
|
|
|
|
- /* 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;
|
|
|
+/*
|
|
|
+** 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;
|
|
|
}
|
|
|
-
|
|
|
- if( winIsDir(zConverted) ){
|
|
|
- sqlite3_free(zConverted);
|
|
|
- sqlite3_free(zTmpname);
|
|
|
- OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8Name));
|
|
|
- return SQLITE_CANTOPEN_ISDIR;
|
|
|
+ magic = db->magic;
|
|
|
+ if( magic!=SQLITE_MAGIC_OPEN ){
|
|
|
+ if( sqlite3SafetyCheckSickOrOk(db) ){
|
|
|
+ testcase( sqlite3GlobalConfig.xLog!=0 );
|
|
|
+ logBadConnection("unopened");
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }else{
|
|
|
+ return 1;
|
|
|
}
|
|
|
-
|
|
|
- if( isReadWrite ){
|
|
|
- dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
|
+}
|
|
|
+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{
|
|
|
- dwDesiredAccess = GENERIC_READ;
|
|
|
+ return 1;
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- /* 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;
|
|
|
+/*
|
|
|
+** 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;
|
|
|
+ *pA += iB;
|
|
|
}else{
|
|
|
- /* Opens a file, only if it exists. */
|
|
|
- dwCreationDisposition = OPEN_EXISTING;
|
|
|
+ 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;
|
|
|
}
|
|
|
-
|
|
|
- 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
|
|
|
+ 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{
|
|
|
- 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);
|
|
|
-
|
|
|
- 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;
|
|
|
- }
|
|
|
+ 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;
|
|
|
|
|
|
- if( pOutFlags ){
|
|
|
- if( isReadWrite ){
|
|
|
- *pOutFlags = SQLITE_OPEN_READWRITE;
|
|
|
- }else{
|
|
|
- *pOutFlags = SQLITE_OPEN_READONLY;
|
|
|
- }
|
|
|
- }
|
|
|
+ iA1 = iA/TWOPOWER32;
|
|
|
+ iA0 = iA % TWOPOWER32;
|
|
|
+ iB1 = iB/TWOPOWER32;
|
|
|
+ iB0 = iB % TWOPOWER32;
|
|
|
+ if( iA1*iB1 != 0 ) return 1;
|
|
|
+ assert( iA1*iB0==0 || iA0*iB1==0 );
|
|
|
+ r = iA1*iB0 + iA0*iB1;
|
|
|
+ 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;
|
|
|
+}
|
|
|
|
|
|
- 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"));
|
|
|
+/*
|
|
|
+** 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;
|
|
|
+}
|
|
|
|
|
|
-#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
|
|
|
+#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
|
|
|
{
|
|
|
- sqlite3_free(zConverted);
|
|
|
+ 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
|
|
|
|
|
|
- sqlite3_free(zTmpname);
|
|
|
- pFile->pMethod = &winIoMethod;
|
|
|
- pFile->pVfs = pVfs;
|
|
|
- pFile->h = h;
|
|
|
- if( isReadonly ){
|
|
|
- pFile->ctrlFlags |= WINFILE_RDONLY;
|
|
|
+/*
|
|
|
+** 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];
|
|
|
}
|
|
|
- if( sqlite3_uri_boolean(zName, "psow", SQLITE_POWERSAFE_OVERWRITE) ){
|
|
|
- pFile->ctrlFlags |= WINFILE_PSOW;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+** Convert an integer into a LogEst. In other words, compute a
|
|
|
+** good approximatation 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; }
|
|
|
}
|
|
|
- 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
|
|
|
+ return a[x&7] + y - 10;
|
|
|
+}
|
|
|
|
|
|
- OpenCounter(+1);
|
|
|
- return rc;
|
|
|
+#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 */
|
|
|
|
|
|
/*
|
|
|
-** 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.
|
|
|
+** Convert a LogEst into an integer.
|
|
|
*/
|
|
|
-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;
|
|
|
- void *zConverted;
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
- UNUSED_PARAMETER(syncDir);
|
|
|
+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 (n+8)<<(x-3);
|
|
|
+ return (n+8)>>(3-x);
|
|
|
+}
|
|
|
|
|
|
- SimulateIOError(return SQLITE_IOERR_DELETE);
|
|
|
- OSTRACE(("DELETE name=%s, syncDir=%d\n", zFilename, syncDir));
|
|
|
+/************** 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 <assert.h> */
|
|
|
|
|
|
- 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);
|
|
|
- }
|
|
|
- sqlite3_free(zConverted);
|
|
|
- OSTRACE(("DELETE name=%s, rc=%s\n", zFilename, sqlite3ErrName(rc)));
|
|
|
- return rc;
|
|
|
+/* 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;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-** Check the existence and status of a file.
|
|
|
+/* 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.
|
|
|
*/
|
|
|
-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;
|
|
|
- void *zConverted;
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
+SQLITE_PRIVATE void sqlite3HashClear(Hash *pH){
|
|
|
+ HashElem *elem; /* For looping over all elements of the table */
|
|
|
|
|
|
- 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);
|
|
|
- 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");
|
|
|
+ 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;
|
|
|
}
|
|
|
- *pResOut = rc;
|
|
|
- OSTRACE(("ACCESS name=%s, pResOut=%p, *pResOut=%d, rc=SQLITE_OK\n",
|
|
|
- zFilename, pResOut, *pResOut));
|
|
|
- return SQLITE_OK;
|
|
|
+ pH->count = 0;
|
|
|
}
|
|
|
|
|
|
/*
|
|
|
-** Returns non-zero if the specified path name starts with a drive letter
|
|
|
-** followed by a colon character.
|
|
|
+** The hashing function.
|
|
|
*/
|
|
|
-static BOOL winIsDriveLetterAndColon(
|
|
|
- const char *zPathname
|
|
|
-){
|
|
|
- return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
|
|
|
+static unsigned int strHash(const char *z, int nKey){
|
|
|
+ int h = 0;
|
|
|
+ assert( nKey>=0 );
|
|
|
+ while( nKey > 0 ){
|
|
|
+ h = (h<<3) ^ h ^ sqlite3UpperToLower[(unsigned char)*z++];
|
|
|
+ nKey--;
|
|
|
+ }
|
|
|
+ return h;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-** 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).
|
|
|
+
|
|
|
+/* Link pNew element into the hash table pH. If pEntry!=0 then also
|
|
|
+** insert pNew into the pEntry hash bucket.
|
|
|
*/
|
|
|
-static BOOL winIsVerbatimPathname(
|
|
|
- const char *zPathname
|
|
|
+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 */
|
|
|
){
|
|
|
- /*
|
|
|
- ** 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;
|
|
|
+ HashElem *pHead; /* First element already in pEntry */
|
|
|
+ if( pEntry ){
|
|
|
+ pHead = pEntry->count ? pEntry->chain : 0;
|
|
|
+ pEntry->count++;
|
|
|
+ pEntry->chain = pNew;
|
|
|
+ }else{
|
|
|
+ pHead = 0;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- ** 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( 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;
|
|
|
}
|
|
|
-
|
|
|
- /*
|
|
|
- ** 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.
|
|
|
+
|
|
|
+/* 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 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(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);
|
|
|
- }
|
|
|
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%s%s",
|
|
|
- sqlite3_data_directory, winGetDirDep(), zOut);
|
|
|
- sqlite3_free(zOut);
|
|
|
- }else{
|
|
|
- if( cygwin_conv_path(CCP_POSIX_TO_WIN_A, zRelative, zFull, nFull)<0 ){
|
|
|
- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
|
|
|
- "winFullPathname2", zRelative);
|
|
|
- }
|
|
|
- }
|
|
|
- return SQLITE_OK;
|
|
|
-#endif
|
|
|
+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_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%s%s",
|
|
|
- sqlite3_data_directory, winGetDirDep(), zRelative);
|
|
|
- }else{
|
|
|
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
|
|
|
+#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);
|
|
|
}
|
|
|
- return SQLITE_OK;
|
|
|
+ if( new_size==pH->htsize ) return 0;
|
|
|
#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.
|
|
|
+ /* 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).
|
|
|
*/
|
|
|
- if( zRelative[0]=='/' && winIsDriveLetterAndColon(zRelative+1) ){
|
|
|
- zRelative++;
|
|
|
- }
|
|
|
+ sqlite3BeginBenignMalloc();
|
|
|
+ new_ht = (struct _ht *)sqlite3Malloc( new_size*sizeof(struct _ht) );
|
|
|
+ sqlite3EndBenignMalloc();
|
|
|
|
|
|
- /* 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%s%s",
|
|
|
- sqlite3_data_directory, winGetDirDep(), 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;
|
|
|
+ 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, elem->nKey) % new_size;
|
|
|
+ next_elem = elem->next;
|
|
|
+ insertElement(pH, &new_ht[h], elem);
|
|
|
}
|
|
|
-#endif
|
|
|
+ return 1;
|
|
|
}
|
|
|
|
|
|
-#ifndef SQLITE_OMIT_LOAD_EXTENSION
|
|
|
-/*
|
|
|
-** Interfaces for opening a shared library, finding entry points
|
|
|
-** within the shared library, and closing the shared library.
|
|
|
-*/
|
|
|
-/*
|
|
|
-** Interfaces for opening a shared library, finding entry points
|
|
|
-** within the shared library, and closing the shared library.
|
|
|
+/* This function (for internal use only) locates an element in an
|
|
|
+** hash table that matches the given key. The hash for this key has
|
|
|
+** already been computed and is passed as the 4th parameter.
|
|
|
*/
|
|
|
-static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
|
|
|
- HANDLE h;
|
|
|
- void *zConverted = winConvertFromUtf8Filename(zFilename);
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
- if( zConverted==0 ){
|
|
|
- return 0;
|
|
|
- }
|
|
|
- if( osIsNT() ){
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- h = osLoadPackagedLibrary((LPCWSTR)zConverted, 0);
|
|
|
-#else
|
|
|
- h = osLoadLibraryW((LPCWSTR)zConverted);
|
|
|
-#endif
|
|
|
+static HashElem *findElementGivenHash(
|
|
|
+ const Hash *pH, /* The pH to be searched */
|
|
|
+ const char *pKey, /* The key we are searching for */
|
|
|
+ int nKey, /* Bytes in key (not counting zero terminator) */
|
|
|
+ unsigned int h /* The hash for this key. */
|
|
|
+){
|
|
|
+ HashElem *elem; /* Used to loop thru the element list */
|
|
|
+ int count; /* Number of elements left to test */
|
|
|
+
|
|
|
+ if( pH->ht ){
|
|
|
+ struct _ht *pEntry = &pH->ht[h];
|
|
|
+ elem = pEntry->chain;
|
|
|
+ count = pEntry->count;
|
|
|
+ }else{
|
|
|
+ elem = pH->first;
|
|
|
+ count = pH->count;
|
|
|
}
|
|
|
-#ifdef SQLITE_WIN32_HAS_ANSI
|
|
|
- else{
|
|
|
- h = osLoadLibraryA((char*)zConverted);
|
|
|
+ while( count-- && ALWAYS(elem) ){
|
|
|
+ if( elem->nKey==nKey && sqlite3StrNICmp(elem->pKey,pKey,nKey)==0 ){
|
|
|
+ return elem;
|
|
|
+ }
|
|
|
+ elem = elem->next;
|
|
|
}
|
|
|
-#endif
|
|
|
- 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){
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
- return (void(*)(void))osGetProcAddressA((HANDLE)pH, zSym);
|
|
|
-}
|
|
|
-static void winDlClose(sqlite3_vfs *pVfs, void *pHandle){
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
- osFreeLibrary((HANDLE)pHandle);
|
|
|
+ return 0;
|
|
|
}
|
|
|
-#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.
|
|
|
+/* Remove a single entry from the hash table given a pointer to that
|
|
|
+** element and a hash on the element's key.
|
|
|
*/
|
|
|
-static int winRandomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
|
|
- int n = 0;
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
-#if defined(SQLITE_TEST)
|
|
|
- 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);
|
|
|
+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;
|
|
|
}
|
|
|
-#endif
|
|
|
- if( sizeof(LARGE_INTEGER)<=nBuf-n ){
|
|
|
- LARGE_INTEGER i;
|
|
|
- osQueryPerformanceCounter(&i);
|
|
|
- memcpy(&zBuf[n], &i, sizeof(i));
|
|
|
- n += sizeof(i);
|
|
|
+ if( elem->next ){
|
|
|
+ elem->next->prev = elem->prev;
|
|
|
}
|
|
|
-#endif
|
|
|
- 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;
|
|
|
+ if( pH->ht ){
|
|
|
+ pEntry = &pH->ht[h];
|
|
|
+ if( pEntry->chain==elem ){
|
|
|
+ pEntry->chain = elem->next;
|
|
|
+ }
|
|
|
+ pEntry->count--;
|
|
|
+ assert( pEntry->count>=0 );
|
|
|
}
|
|
|
-#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;
|
|
|
+ sqlite3_free( elem );
|
|
|
+ pH->count--;
|
|
|
+ if( pH->count==0 ){
|
|
|
+ assert( pH->first==0 );
|
|
|
+ assert( pH->count==0 );
|
|
|
+ sqlite3HashClear(pH);
|
|
|
}
|
|
|
-#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.
|
|
|
+/* Attempt to locate an element of the hash table pH with a key
|
|
|
+** that matches pKey,nKey. Return the data for this element if it is
|
|
|
+** found, or NULL if there is no match.
|
|
|
*/
|
|
|
-static int winCurrentTime(sqlite3_vfs *pVfs, double *prNow){
|
|
|
- int rc;
|
|
|
- sqlite3_int64 i;
|
|
|
- rc = winCurrentTimeInt64(pVfs, &i);
|
|
|
- if( !rc ){
|
|
|
- *prNow = i/86400000.0;
|
|
|
+SQLITE_PRIVATE void *sqlite3HashFind(const Hash *pH, const char *pKey, int nKey){
|
|
|
+ HashElem *elem; /* The element that matches key */
|
|
|
+ unsigned int h; /* A hash on key */
|
|
|
+
|
|
|
+ assert( pH!=0 );
|
|
|
+ assert( pKey!=0 );
|
|
|
+ assert( nKey>=0 );
|
|
|
+ if( pH->ht ){
|
|
|
+ h = strHash(pKey, nKey) % pH->htsize;
|
|
|
+ }else{
|
|
|
+ h = 0;
|
|
|
}
|
|
|
- return rc;
|
|
|
+ elem = findElementGivenHash(pH, pKey, nKey, h);
|
|
|
+ return elem ? elem->data : 0;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-** 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.
|
|
|
+/* Insert an element into the hash table pH. The key is pKey,nKey
|
|
|
+** and the data is "data".
|
|
|
**
|
|
|
-** 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:
|
|
|
+** If no element exists with a matching key, then a new
|
|
|
+** element is created and NULL is returned.
|
|
|
**
|
|
|
-** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
|
|
-** assert(zBuf[0]=='\0');
|
|
|
-** return 0;
|
|
|
-** }
|
|
|
+** 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.
|
|
|
**
|
|
|
-** 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.
|
|
|
+** If the "data" parameter to this function is NULL, then the
|
|
|
+** element corresponding to "key" is removed from the hash table.
|
|
|
*/
|
|
|
-static int winGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
|
|
|
- UNUSED_PARAMETER(pVfs);
|
|
|
- return winGetLastErrorMsg(osGetLastError(), nBuf, zBuf);
|
|
|
+SQLITE_PRIVATE void *sqlite3HashInsert(Hash *pH, const char *pKey, int nKey, 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 );
|
|
|
+ assert( nKey>=0 );
|
|
|
+ if( pH->htsize ){
|
|
|
+ h = strHash(pKey, nKey) % pH->htsize;
|
|
|
+ }else{
|
|
|
+ h = 0;
|
|
|
+ }
|
|
|
+ elem = findElementGivenHash(pH,pKey,nKey,h);
|
|
|
+ if( elem ){
|
|
|
+ void *old_data = elem->data;
|
|
|
+ if( data==0 ){
|
|
|
+ removeElementGivenHash(pH,elem,h);
|
|
|
+ }else{
|
|
|
+ elem->data = data;
|
|
|
+ elem->pKey = pKey;
|
|
|
+ assert(nKey==elem->nKey);
|
|
|
+ }
|
|
|
+ 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->nKey = nKey;
|
|
|
+ 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, nKey) % pH->htsize;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if( pH->ht ){
|
|
|
+ insertElement(pH, &pH->ht[h], new_elem);
|
|
|
+ }else{
|
|
|
+ insertElement(pH, 0, new_elem);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
-/*
|
|
|
-** Initialize and deinitialize the operating system interface.
|
|
|
-*/
|
|
|
-SQLITE_API int 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 */
|
|
|
+/************** End of hash.c ************************************************/
|
|
|
+/************** Begin file opcodes.c *****************************************/
|
|
|
+/* Automatically generated. Do not edit */
|
|
|
+/* See the mkopcodec.awk script for details. */
|
|
|
+#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
|
|
|
+SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){
|
|
|
+ static const char *const azName[] = { "?",
|
|
|
+ /* 1 */ "Function",
|
|
|
+ /* 2 */ "Savepoint",
|
|
|
+ /* 3 */ "AutoCommit",
|
|
|
+ /* 4 */ "Transaction",
|
|
|
+ /* 5 */ "SorterNext",
|
|
|
+ /* 6 */ "Prev",
|
|
|
+ /* 7 */ "Next",
|
|
|
+ /* 8 */ "AggStep",
|
|
|
+ /* 9 */ "Checkpoint",
|
|
|
+ /* 10 */ "JournalMode",
|
|
|
+ /* 11 */ "Vacuum",
|
|
|
+ /* 12 */ "VFilter",
|
|
|
+ /* 13 */ "VUpdate",
|
|
|
+ /* 14 */ "Goto",
|
|
|
+ /* 15 */ "Gosub",
|
|
|
+ /* 16 */ "Return",
|
|
|
+ /* 17 */ "Yield",
|
|
|
+ /* 18 */ "HaltIfNull",
|
|
|
+ /* 19 */ "Not",
|
|
|
+ /* 20 */ "Halt",
|
|
|
+ /* 21 */ "Integer",
|
|
|
+ /* 22 */ "Int64",
|
|
|
+ /* 23 */ "String",
|
|
|
+ /* 24 */ "Null",
|
|
|
+ /* 25 */ "Blob",
|
|
|
+ /* 26 */ "Variable",
|
|
|
+ /* 27 */ "Move",
|
|
|
+ /* 28 */ "Copy",
|
|
|
+ /* 29 */ "SCopy",
|
|
|
+ /* 30 */ "ResultRow",
|
|
|
+ /* 31 */ "CollSeq",
|
|
|
+ /* 32 */ "AddImm",
|
|
|
+ /* 33 */ "MustBeInt",
|
|
|
+ /* 34 */ "RealAffinity",
|
|
|
+ /* 35 */ "Permutation",
|
|
|
+ /* 36 */ "Compare",
|
|
|
+ /* 37 */ "Jump",
|
|
|
+ /* 38 */ "Once",
|
|
|
+ /* 39 */ "If",
|
|
|
+ /* 40 */ "IfNot",
|
|
|
+ /* 41 */ "Column",
|
|
|
+ /* 42 */ "Affinity",
|
|
|
+ /* 43 */ "MakeRecord",
|
|
|
+ /* 44 */ "Count",
|
|
|
+ /* 45 */ "ReadCookie",
|
|
|
+ /* 46 */ "SetCookie",
|
|
|
+ /* 47 */ "VerifyCookie",
|
|
|
+ /* 48 */ "OpenRead",
|
|
|
+ /* 49 */ "OpenWrite",
|
|
|
+ /* 50 */ "OpenAutoindex",
|
|
|
+ /* 51 */ "OpenEphemeral",
|
|
|
+ /* 52 */ "SorterOpen",
|
|
|
+ /* 53 */ "OpenPseudo",
|
|
|
+ /* 54 */ "Close",
|
|
|
+ /* 55 */ "SeekLt",
|
|
|
+ /* 56 */ "SeekLe",
|
|
|
+ /* 57 */ "SeekGe",
|
|
|
+ /* 58 */ "SeekGt",
|
|
|
+ /* 59 */ "Seek",
|
|
|
+ /* 60 */ "NotFound",
|
|
|
+ /* 61 */ "Found",
|
|
|
+ /* 62 */ "IsUnique",
|
|
|
+ /* 63 */ "NotExists",
|
|
|
+ /* 64 */ "Sequence",
|
|
|
+ /* 65 */ "NewRowid",
|
|
|
+ /* 66 */ "Insert",
|
|
|
+ /* 67 */ "InsertInt",
|
|
|
+ /* 68 */ "Or",
|
|
|
+ /* 69 */ "And",
|
|
|
+ /* 70 */ "Delete",
|
|
|
+ /* 71 */ "ResetCount",
|
|
|
+ /* 72 */ "SorterCompare",
|
|
|
+ /* 73 */ "IsNull",
|
|
|
+ /* 74 */ "NotNull",
|
|
|
+ /* 75 */ "Ne",
|
|
|
+ /* 76 */ "Eq",
|
|
|
+ /* 77 */ "Gt",
|
|
|
+ /* 78 */ "Le",
|
|
|
+ /* 79 */ "Lt",
|
|
|
+ /* 80 */ "Ge",
|
|
|
+ /* 81 */ "SorterData",
|
|
|
+ /* 82 */ "BitAnd",
|
|
|
+ /* 83 */ "BitOr",
|
|
|
+ /* 84 */ "ShiftLeft",
|
|
|
+ /* 85 */ "ShiftRight",
|
|
|
+ /* 86 */ "Add",
|
|
|
+ /* 87 */ "Subtract",
|
|
|
+ /* 88 */ "Multiply",
|
|
|
+ /* 89 */ "Divide",
|
|
|
+ /* 90 */ "Remainder",
|
|
|
+ /* 91 */ "Concat",
|
|
|
+ /* 92 */ "RowKey",
|
|
|
+ /* 93 */ "BitNot",
|
|
|
+ /* 94 */ "String8",
|
|
|
+ /* 95 */ "RowData",
|
|
|
+ /* 96 */ "Rowid",
|
|
|
+ /* 97 */ "NullRow",
|
|
|
+ /* 98 */ "Last",
|
|
|
+ /* 99 */ "SorterSort",
|
|
|
+ /* 100 */ "Sort",
|
|
|
+ /* 101 */ "Rewind",
|
|
|
+ /* 102 */ "SorterInsert",
|
|
|
+ /* 103 */ "IdxInsert",
|
|
|
+ /* 104 */ "IdxDelete",
|
|
|
+ /* 105 */ "IdxRowid",
|
|
|
+ /* 106 */ "IdxLT",
|
|
|
+ /* 107 */ "IdxGE",
|
|
|
+ /* 108 */ "Destroy",
|
|
|
+ /* 109 */ "Clear",
|
|
|
+ /* 110 */ "CreateIndex",
|
|
|
+ /* 111 */ "CreateTable",
|
|
|
+ /* 112 */ "ParseSchema",
|
|
|
+ /* 113 */ "LoadAnalysis",
|
|
|
+ /* 114 */ "DropTable",
|
|
|
+ /* 115 */ "DropIndex",
|
|
|
+ /* 116 */ "DropTrigger",
|
|
|
+ /* 117 */ "IntegrityCk",
|
|
|
+ /* 118 */ "RowSetAdd",
|
|
|
+ /* 119 */ "RowSetRead",
|
|
|
+ /* 120 */ "RowSetTest",
|
|
|
+ /* 121 */ "Program",
|
|
|
+ /* 122 */ "Param",
|
|
|
+ /* 123 */ "FkCounter",
|
|
|
+ /* 124 */ "FkIfZero",
|
|
|
+ /* 125 */ "MemMax",
|
|
|
+ /* 126 */ "IfPos",
|
|
|
+ /* 127 */ "IfNeg",
|
|
|
+ /* 128 */ "IfZero",
|
|
|
+ /* 129 */ "AggFinal",
|
|
|
+ /* 130 */ "Real",
|
|
|
+ /* 131 */ "IncrVacuum",
|
|
|
+ /* 132 */ "Expire",
|
|
|
+ /* 133 */ "TableLock",
|
|
|
+ /* 134 */ "VBegin",
|
|
|
+ /* 135 */ "VCreate",
|
|
|
+ /* 136 */ "VDestroy",
|
|
|
+ /* 137 */ "VOpen",
|
|
|
+ /* 138 */ "VColumn",
|
|
|
+ /* 139 */ "VNext",
|
|
|
+ /* 140 */ "VRename",
|
|
|
+ /* 141 */ "ToText",
|
|
|
+ /* 142 */ "ToBlob",
|
|
|
+ /* 143 */ "ToNumeric",
|
|
|
+ /* 144 */ "ToInt",
|
|
|
+ /* 145 */ "ToReal",
|
|
|
+ /* 146 */ "Pagecount",
|
|
|
+ /* 147 */ "MaxPgcnt",
|
|
|
+ /* 148 */ "Trace",
|
|
|
+ /* 149 */ "Noop",
|
|
|
+ /* 150 */ "Explain",
|
|
|
};
|
|
|
-#endif
|
|
|
-
|
|
|
- /* Double-check that the aSyscall[] array has been constructed
|
|
|
- ** correctly. See ticket [bb3a86e890c8e96ab] */
|
|
|
- assert( ArraySize(aSyscall)==75 );
|
|
|
-
|
|
|
- /* 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;
|
|
|
+ return azName[i];
|
|
|
}
|
|
|
-
|
|
|
-SQLITE_API int sqlite3_os_end(void){
|
|
|
-#if SQLITE_OS_WINRT
|
|
|
- if( sleepObj!=NULL ){
|
|
|
- osCloseHandle(sleepObj);
|
|
|
- sleepObj = NULL;
|
|
|
- }
|
|
|
#endif
|
|
|
- return SQLITE_OK;
|
|
|
-}
|
|
|
|
|
|
-#endif /* SQLITE_OS_WIN */
|
|
|
-
|
|
|
-/************** End of os_win.c **********************************************/
|
|
|
+/************** End of opcodes.c *********************************************/
|
|
|
/************** Begin file os_rtt.c ******************************************/
|
|
|
/*
|
|
|
** 2004 May 22
|