@@ -1,8 +1,8 @@
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
-** version 3.7.13. By combining all the individual C code files into this
+** version 3.7.14. By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit. This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately. Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
** translation unit.
@@ -387,10 +387,11 @@
** Exactly one of the following macros must be defined in order to
** specify which memory allocation subsystem to use.
**
** SQLITE_SYSTEM_MALLOC // Use normal system malloc()
** SQLITE_WIN32_MALLOC // Use Win32 native heap API
+** SQLITE_ZERO_MALLOC // Use a stub allocator that always fails
** SQLITE_MEMDEBUG // Debugging version of system malloc()
**
** On Windows, if the SQLITE_WIN32_MALLOC_VALIDATE macro is defined and the
** assert() macro is enabled, each call into the Win32 native heap subsystem
** will cause HeapValidate to be called. If heap validation should fail, an
@@ -400,15 +401,23 @@
** pared it down to just these three.)
**
** If none of the above are defined, then set SQLITE_SYSTEM_MALLOC as
** the default.
*/
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)>1
-# error "At most one of the following compile-time configuration options\
- is allows: SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG"
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)>1
+# error "Two or more of the following compile-time configuration options\
+ are defined but at most one is allowed:\
+ SQLITE_SYSTEM_MALLOC, SQLITE_WIN32_MALLOC, SQLITE_MEMDEBUG,\
+ SQLITE_ZERO_MALLOC"
#endif
-#if defined(SQLITE_SYSTEM_MALLOC)+defined(SQLITE_WIN32_MALLOC)+defined(SQLITE_MEMDEBUG)==0
+#if defined(SQLITE_SYSTEM_MALLOC) \
+ + defined(SQLITE_WIN32_MALLOC) \
+ + defined(SQLITE_ZERO_MALLOC) \
+ + defined(SQLITE_MEMDEBUG)==0
# define SQLITE_SYSTEM_MALLOC 1
#endif
/*
** If SQLITE_MALLOC_SOFT_LIMIT is not zero, then try to keep the
@@ -662,13 +671,13 @@
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
-#define SQLITE_VERSION "3.7.13"
-#define SQLITE_VERSION_NUMBER 3007013
-#define SQLITE_SOURCE_ID "2012-06-07 07:24:04 506008f000ba4af0b35da023b8c52f7a3f5033bd"
+#define SQLITE_VERSION "3.7.14"
+#define SQLITE_VERSION_NUMBER 3007014
+#define SQLITE_SOURCE_ID "2012-06-21 17:21:52 d5e6880279210ca63e2d5e7f6d009f30566f1242"
/*
** CAPI3REF: Run-Time Library Version Numbers
** KEYWORDS: sqlite3_version, sqlite3_sourceid
**
@@ -774,11 +783,12 @@
**
** Each open SQLite database is represented by a pointer to an instance of
** the opaque structure named "sqlite3". It is useful to think of an sqlite3
** pointer as an object. The [sqlite3_open()], [sqlite3_open16()], and
** [sqlite3_open_v2()] interfaces are its constructors, and [sqlite3_close()]
-** is its destructor. There are many other interfaces (such as
+** and [sqlite3_close_v2()] are its destructors. There are many other
+** interfaces (such as
** [sqlite3_prepare_v2()], [sqlite3_create_function()], and
** [sqlite3_busy_timeout()] to name but three) that are methods on an
** sqlite3 object.
*/
typedef struct sqlite3 sqlite3;
@@ -821,32 +831,50 @@
#endif
/*
** CAPI3REF: Closing A Database Connection
**
-** ^The sqlite3_close() routine is the destructor for the [sqlite3] object.
-** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is
-** successfully destroyed and all associated resources are deallocated.
+** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
+** for the [sqlite3] object.
+** ^Calls to sqlite3_close() and sqlite3_close_v2() return SQLITE_OK if
+** the [sqlite3] object is successfully destroyed and all associated
+** resources are deallocated.
**
-** Applications must [sqlite3_finalize | finalize] all [prepared statements]
-** and [sqlite3_blob_close | close] all [BLOB handles] associated with
-** the [sqlite3] object prior to attempting to close the object. ^If
+** ^If the database connection is associated with unfinalized prepared
+** statements or unfinished sqlite3_backup objects then sqlite3_close()
+** will leave the database connection open and return [SQLITE_BUSY].
+** ^If sqlite3_close_v2() is called with unfinalized prepared statements
+** and unfinished sqlite3_backups, then the database connection becomes
+** an unusable "zombie" which will automatically be deallocated when the
+** last prepared statement is finalized or the last sqlite3_backup is
+** finished. The sqlite3_close_v2() interface is intended for use with
+** host languages that are garbage collected, and where the order in which
+** destructors are called is arbitrary.
+**
+** Applications should [sqlite3_finalize | finalize] all [prepared statements],
+** [sqlite3_blob_close | close] all [BLOB handles], and
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
+** with the [sqlite3] object prior to attempting to close the object. ^If
** sqlite3_close() is called on a [database connection] that still has
-** outstanding [prepared statements] or [BLOB handles], then it returns
-** SQLITE_BUSY.
+** outstanding [prepared statements], [BLOB handles], and/or
+** [sqlite3_backup] objects then it returns SQLITE_OK but the deallocation
+** of resources is deferred until all [prepared statements], [BLOB handles],
+** and [sqlite3_backup] objects are also destroyed.
**
-** ^If [sqlite3_close()] is invoked while a transaction is open,
+** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
-** The C parameter to [sqlite3_close(C)] must be either a NULL
+** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
+** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
** from [sqlite3_open()], [sqlite3_open16()], or
** [sqlite3_open_v2()], and not previously closed.
-** ^Calling sqlite3_close() with a NULL pointer argument is a
-** harmless no-op.
+** ^Calling sqlite3_close() or sqlite3_close_v2() with a NULL pointer
+** argument is a harmless no-op.
*/
-SQLITE_API int sqlite3_close(sqlite3 *);
+SQLITE_API int sqlite3_close(sqlite3*);
+SQLITE_API int sqlite3_close_v2(sqlite3*);
/*
** The type for a callback function.
** This is legacy and deprecated. It is included for historical
** compatibility and is not documented.
@@ -5013,15 +5041,16 @@
** CAPI3REF: Name Of The Folder Holding Database Files
**
** ^(If this global variable is made to point to a string which is
** the name of a folder (a.k.a. directory), then all database files
** specified with a relative pathname and created or accessed by
-** SQLite when using a built-in [sqlite3_vfs | VFS] will be assumed
+** SQLite when using a built-in windows [sqlite3_vfs | VFS] will be assumed
** to be relative to that directory.)^ ^If this variable is a NULL
** pointer, then SQLite assumes that all database files specified
** with a relative pathname are relative to the current directory
-** for the process.
+** for the process. Only the windows VFS makes use of this global
+** variable; it is ignored by the unix VFS.
**
** Changing the value of this variable while a database connection is
** open can result in a corrupt database.
**
** It is not safe to read or modify this variable in more than one
@@ -6048,21 +6077,20 @@
** of these mutex routines. An appropriate implementation
** is selected automatically at compile-time. ^(The following
** implementations are available in the SQLite core:
**
**
-** - SQLITE_MUTEX_OS2
**
- SQLITE_MUTEX_PTHREADS
**
- SQLITE_MUTEX_W32
**
- SQLITE_MUTEX_NOOP
**
)^
**
** ^The SQLITE_MUTEX_NOOP implementation is a set of routines
** that does no real locking and is appropriate for use in
-** a single-threaded application. ^The SQLITE_MUTEX_OS2,
-** SQLITE_MUTEX_PTHREADS, and SQLITE_MUTEX_W32 implementations
-** are appropriate for use on OS/2, Unix, and Windows.
+** a single-threaded application. ^The SQLITE_MUTEX_PTHREADS and
+** SQLITE_MUTEX_W32 implementations are appropriate for use on Unix
+** and Windows.
**
** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor
** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex
** implementation is included with the library. In this case the
** application must supply a custom mutex implementation using the
@@ -9287,22 +9315,20 @@
#define _SQLITE_OS_H_
/*
** Figure out if we are dealing with Unix, Windows, or some other
** operating system. After the following block of preprocess macros,
-** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, SQLITE_OS_OS2, and SQLITE_OS_OTHER
+** all of SQLITE_OS_UNIX, SQLITE_OS_WIN, and SQLITE_OS_OTHER
** will defined to either 1 or 0. One of the four will be 1. The other
** three will be 0.
*/
#if defined(SQLITE_OS_OTHER)
# if SQLITE_OS_OTHER==1
# undef SQLITE_OS_UNIX
# define SQLITE_OS_UNIX 0
# undef SQLITE_OS_WIN
# define SQLITE_OS_WIN 0
-# undef SQLITE_OS_OS2
-# define SQLITE_OS_OS2 0
# else
# undef SQLITE_OS_OTHER
# endif
#endif
#if !defined(SQLITE_OS_UNIX) && !defined(SQLITE_OS_OTHER)
@@ -9309,23 +9335,16 @@
# define SQLITE_OS_OTHER 0
# ifndef SQLITE_OS_WIN
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__)
# define SQLITE_OS_WIN 1
# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
-# elif defined(__EMX__) || defined(_OS2) || defined(OS2) || defined(_OS2_) || defined(__OS2__)
-# define SQLITE_OS_WIN 0
-# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 1
# else
# define SQLITE_OS_WIN 0
# define SQLITE_OS_UNIX 1
-# define SQLITE_OS_OS2 0
# endif
# else
# define SQLITE_OS_UNIX 0
-# define SQLITE_OS_OS2 0
# endif
#else
# ifndef SQLITE_OS_WIN
# define SQLITE_OS_WIN 0
# endif
@@ -9333,25 +9352,10 @@
#if SQLITE_OS_WIN
# include
#endif
-#if SQLITE_OS_OS2
-# if (__GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 3) && defined(OS2_HIGH_MEMORY)
-# include /* has to be included before os2.h for linking to work */
-# endif
-# define INCL_DOSDATETIME
-# define INCL_DOSFILEMGR
-# define INCL_DOSERRORS
-# define INCL_DOSMISC
-# define INCL_DOSPROCESS
-# define INCL_DOSMODULEMGR
-# define INCL_DOSSEMAPHORES
-# include
-# include
-#endif
-
/*
** Determine if we are dealing with Windows NT.
**
** We ought to be able to determine if we are compiling for win98 or winNT
** using the _WIN32_WINNT macro as follows:
@@ -9617,23 +9621,19 @@
** start-time.
**
** SQLITE_MUTEX_PTHREADS For multi-threaded applications on Unix.
**
** SQLITE_MUTEX_W32 For multi-threaded applications on Win32.
-**
-** SQLITE_MUTEX_OS2 For multi-threaded applications on OS/2.
*/
#if !SQLITE_THREADSAFE
# define SQLITE_MUTEX_OMIT
#endif
#if SQLITE_THREADSAFE && !defined(SQLITE_MUTEX_NOOP)
# if SQLITE_OS_UNIX
# define SQLITE_MUTEX_PTHREADS
# elif SQLITE_OS_WIN
# define SQLITE_MUTEX_W32
-# elif SQLITE_OS_OS2
-# define SQLITE_MUTEX_OS2
# else
# define SQLITE_MUTEX_NOOP
# endif
#endif
@@ -9950,10 +9950,11 @@
#define SQLITE_MAGIC_OPEN 0xa029a697 /* Database is open */
#define SQLITE_MAGIC_CLOSED 0x9f3c2d33 /* Database is closed */
#define SQLITE_MAGIC_SICK 0x4b771290 /* Error and awaiting close */
#define SQLITE_MAGIC_BUSY 0xf03b7906 /* Database currently in use */
#define SQLITE_MAGIC_ERROR 0xb5357930 /* An SQLITE_MISUSE error occurred */
+#define SQLITE_MAGIC_ZOMBIE 0x64cffc7f /* Close with last statement close */
/*
** Each SQL function is defined by an instance of the following
** structure. A pointer to this structure is stored in the sqlite.aFunc
** hash table. When multiple functions have the same name, the hash table
@@ -11811,10 +11812,11 @@
SQLITE_PRIVATE void sqlite3BeginTransaction(Parse*, int);
SQLITE_PRIVATE void sqlite3CommitTransaction(Parse*);
SQLITE_PRIVATE void sqlite3RollbackTransaction(Parse*);
SQLITE_PRIVATE void sqlite3Savepoint(Parse*, int, Token*);
SQLITE_PRIVATE void sqlite3CloseSavepoints(sqlite3 *);
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3*);
SQLITE_PRIVATE int sqlite3ExprIsConstant(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*);
SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*);
SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*);
@@ -17716,286 +17718,10 @@
}
#endif /* defined(SQLITE_MUTEX_NOOP) */
#endif /* !defined(SQLITE_MUTEX_OMIT) */
/************** End of mutex_noop.c ******************************************/
-/************** Begin file mutex_os2.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 OS/2
-*/
-
-/*
-** The code in this file is only used if SQLITE_MUTEX_OS2 is defined.
-** See the mutex.h file for details.
-*/
-#ifdef SQLITE_MUTEX_OS2
-
-/********************** OS/2 Mutex Implementation **********************
-**
-** This implementation of mutexes is built using the OS/2 API.
-*/
-
-/*
-** The mutex object
-** Each recursive mutex is an instance of the following structure.
-*/
-struct sqlite3_mutex {
- HMTX mutex; /* Mutex controlling the lock */
- int id; /* Mutex type */
-#ifdef SQLITE_DEBUG
- int trace; /* True to trace changes */
-#endif
-};
-
-#ifdef SQLITE_DEBUG
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0, 0 }
-#else
-#define SQLITE3_MUTEX_INITIALIZER { 0, 0 }
-#endif
-
-/*
-** Initialize and deinitialize the mutex subsystem.
-*/
-static int os2MutexInit(void){ return SQLITE_OK; }
-static int os2MutexEnd(void){ return SQLITE_OK; }
-
-/*
-** The sqlite3_mutex_alloc() routine allocates a new
-** mutex and returns a pointer to it. If it returns NULL
-** that means that a mutex could not be allocated.
-** SQLite will unwind its stack and return an error. The argument
-** to sqlite3_mutex_alloc() is one of these integer constants:
-**
-**
-** - SQLITE_MUTEX_FAST
-**
- SQLITE_MUTEX_RECURSIVE
-**
- SQLITE_MUTEX_STATIC_MASTER
-**
- SQLITE_MUTEX_STATIC_MEM
-**
- SQLITE_MUTEX_STATIC_MEM2
-**
- SQLITE_MUTEX_STATIC_PRNG
-**
- SQLITE_MUTEX_STATIC_LRU
-**
- SQLITE_MUTEX_STATIC_LRU2
-**
-**
-** The first two constants cause sqlite3_mutex_alloc() to create
-** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE
-** is used but not necessarily so when SQLITE_MUTEX_FAST is used.
-** The mutex implementation does not need to make a distinction
-** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does
-** not want to. But SQLite will only request a recursive mutex in
-** cases where it really needs one. If a faster non-recursive mutex
-** implementation is available on the host platform, the mutex subsystem
-** might return such a mutex in response to SQLITE_MUTEX_FAST.
-**
-** The other allowed parameters to sqlite3_mutex_alloc() each return
-** a pointer to a static preexisting mutex. Six static mutexes are
-** used by the current version of SQLite. Future versions of SQLite
-** may add additional static mutexes. Static mutexes are for internal
-** use by SQLite only. Applications that use SQLite mutexes should
-** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or
-** SQLITE_MUTEX_RECURSIVE.
-**
-** Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST
-** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc()
-** returns a different mutex on every call. But for the static
-** mutex types, the same mutex is returned on every call that has
-** the same type number.
-*/
-static sqlite3_mutex *os2MutexAlloc(int iType){
- sqlite3_mutex *p = NULL;
- switch( iType ){
- case SQLITE_MUTEX_FAST:
- case SQLITE_MUTEX_RECURSIVE: {
- p = sqlite3MallocZero( sizeof(*p) );
- if( p ){
- p->id = iType;
- if( DosCreateMutexSem( 0, &p->mutex, 0, FALSE ) != NO_ERROR ){
- sqlite3_free( p );
- p = NULL;
- }
- }
- break;
- }
- default: {
- static volatile int isInit = 0;
- static sqlite3_mutex staticMutexes[6] = {
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- SQLITE3_MUTEX_INITIALIZER,
- };
- if ( !isInit ){
- APIRET rc;
- PTIB ptib;
- PPIB ppib;
- HMTX mutex;
- char name[32];
- DosGetInfoBlocks( &ptib, &ppib );
- sqlite3_snprintf( sizeof(name), name, "\\SEM32\\SQLITE%04x",
- ppib->pib_ulpid );
- while( !isInit ){
- mutex = 0;
- rc = DosCreateMutexSem( name, &mutex, 0, FALSE);
- if( rc == NO_ERROR ){
- unsigned int i;
- if( !isInit ){
- for( i = 0; i < sizeof(staticMutexes)/sizeof(staticMutexes[0]); i++ ){
- DosCreateMutexSem( 0, &staticMutexes[i].mutex, 0, FALSE );
- }
- isInit = 1;
- }
- DosCloseMutexSem( mutex );
- }else if( rc == ERROR_DUPLICATE_NAME ){
- DosSleep( 1 );
- }else{
- return p;
- }
- }
- }
- assert( iType-2 >= 0 );
- assert( iType-2 < sizeof(staticMutexes)/sizeof(staticMutexes[0]) );
- p = &staticMutexes[iType-2];
- p->id = iType;
- break;
- }
- }
- return p;
-}
-
-
-/*
-** This routine deallocates a previously allocated mutex.
-** SQLite is careful to deallocate every mutex that it allocates.
-*/
-static void os2MutexFree(sqlite3_mutex *p){
-#ifdef SQLITE_DEBUG
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- assert( ulCount==0 );
- assert( p->id==SQLITE_MUTEX_FAST || p->id==SQLITE_MUTEX_RECURSIVE );
-#endif
- DosCloseMutexSem( p->mutex );
- sqlite3_free( p );
-}
-
-#ifdef SQLITE_DEBUG
-/*
-** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are
-** intended for use inside assert() statements.
-*/
-static int os2MutexHeld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 || ( ulCount>1 && p->id!=SQLITE_MUTEX_RECURSIVE ) )
- return 0;
- DosGetInfoBlocks(&ptib, NULL);
- return tid==ptib->tib_ptib2->tib2_ultid;
-}
-static int os2MutexNotheld(sqlite3_mutex *p){
- TID tid;
- PID pid;
- ULONG ulCount;
- PTIB ptib;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- if( ulCount==0 )
- return 1;
- DosGetInfoBlocks(&ptib, NULL);
- return tid!=ptib->tib_ptib2->tib2_ultid;
-}
-static void os2MutexTrace(sqlite3_mutex *p, char *pAction){
- TID tid;
- PID pid;
- ULONG ulCount;
- DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount);
- printf("%s mutex %p (%d) with nRef=%ld\n", pAction, (void*)p, p->trace, ulCount);
-}
-#endif
-
-/*
-** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt
-** to enter a mutex. If another thread is already within the mutex,
-** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
-** SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK
-** upon successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can
-** be entered multiple times by the same thread. In such cases the,
-** mutex must be exited an equal number of times before another thread
-** can enter. If the same thread tries to enter any other kind of mutex
-** more than once, the behavior is undefined.
-*/
-static void os2MutexEnter(sqlite3_mutex *p){
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- DosRequestMutexSem(p->mutex, SEM_INDEFINITE_WAIT);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "enter");
-#endif
-}
-static int os2MutexTry(sqlite3_mutex *p){
- int rc = SQLITE_BUSY;
- assert( p->id==SQLITE_MUTEX_RECURSIVE || os2MutexNotheld(p) );
- if( DosRequestMutexSem(p->mutex, SEM_IMMEDIATE_RETURN) == NO_ERROR ) {
- rc = SQLITE_OK;
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "try");
-#endif
- }
- return rc;
-}
-
-/*
-** The sqlite3_mutex_leave() routine exits a mutex that was
-** previously entered by the same thread. The behavior
-** is undefined if the mutex is not currently entered or
-** is not currently allocated. SQLite will never do either.
-*/
-static void os2MutexLeave(sqlite3_mutex *p){
- assert( os2MutexHeld(p) );
- DosReleaseMutexSem(p->mutex);
-#ifdef SQLITE_DEBUG
- if( p->trace ) os2MutexTrace(p, "leave");
-#endif
-}
-
-SQLITE_PRIVATE sqlite3_mutex_methods const *sqlite3DefaultMutex(void){
- static const sqlite3_mutex_methods sMutex = {
- os2MutexInit,
- os2MutexEnd,
- os2MutexAlloc,
- os2MutexFree,
- os2MutexEnter,
- os2MutexTry,
- os2MutexLeave,
-#ifdef SQLITE_DEBUG
- os2MutexHeld,
- os2MutexNotheld
-#else
- 0,
- 0
-#endif
- };
-
- return &sMutex;
-}
-#endif /* SQLITE_MUTEX_OS2 */
-
-/************** End of mutex_os2.c *******************************************/
/************** Begin file mutex_unix.c **************************************/
/*
** 2007 August 28
**
** The author disclaims copyright to this source code. In place of
@@ -18456,11 +18182,11 @@
** processing, the "interlocked" magic is probably not
** strictly necessary.
*/
static long winMutex_lock = 0;
-SQLITE_API extern void sqlite3_win32_sleep(DWORD milliseconds); /* os_win.c */
+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;
@@ -19599,11 +19325,12 @@
** always returned.
*/
static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
int digit;
LONGDOUBLE_TYPE d;
- if( (*cnt)++ >= 16 ) return '0';
+ if( (*cnt)<=0 ) return '0';
+ (*cnt)--;
digit = (int)*val;
d = digit;
digit += '0';
*val = (*val - d)*10.0;
return (char)digit;
@@ -19903,13 +19630,16 @@
bufpt = "NaN";
length = 3;
break;
}
if( realvalue>0.0 ){
- while( realvalue>=1e32 && exp<=350 ){ realvalue *= 1e-32; exp+=32; }
- while( realvalue>=1e8 && exp<=350 ){ realvalue *= 1e-8; exp+=8; }
- while( realvalue>=10.0 && exp<=350 ){ realvalue *= 0.1; exp++; }
+ 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";
@@ -19938,11 +19668,11 @@
}else{
precision = precision - exp;
xtype = etFLOAT;
}
}else{
- flag_rtz = 0;
+ flag_rtz = flag_altform2;
}
if( xtype==etEXP ){
e2 = 0;
}else{
e2 = exp;
@@ -19953,11 +19683,11 @@
pAccum->mallocFailed = 1;
return;
}
}
zOut = bufpt;
- nsd = 0;
+ 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;
}
@@ -21526,11 +21256,11 @@
s = sign<0 ? -s : s;
/* if exponent, scale significand as appropriate
** and store in result. */
if( e ){
- double scale = 1.0;
+ 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;
@@ -22781,2144 +22511,10 @@
return azName[i];
}
#endif
/************** End of opcodes.c *********************************************/
-/************** Begin file os_os2.c ******************************************/
-/*
-** 2006 Feb 14
-**
-** The author disclaims copyright to this source code. In place of
-** a legal notice, here is a blessing:
-**
-** May you do good and not evil.
-** May you find forgiveness for yourself and forgive others.
-** May you share freely, never taking more than you give.
-**
-******************************************************************************
-**
-** This file contains code that is specific to OS/2.
-*/
-
-
-#if SQLITE_OS_OS2
-
-/*
-** A Note About Memory Allocation:
-**
-** This driver uses malloc()/free() directly rather than going through
-** the SQLite-wrappers sqlite3_malloc()/sqlite3_free(). Those wrappers
-** are designed for use on embedded systems where memory is scarce and
-** malloc failures happen frequently. OS/2 does not typically run on
-** embedded systems, and when it does the developers normally have bigger
-** problems to worry about than running out of memory. So there is not
-** a compelling need to use the wrappers.
-**
-** But there is a good reason to not use the wrappers. If we use the
-** wrappers then we will get simulated malloc() failures within this
-** driver. And that causes all kinds of problems for our tests. We
-** could enhance SQLite to deal with simulated malloc failures within
-** the OS driver, but the code to deal with those failure would not
-** be exercised on Linux (which does not need to malloc() in the driver)
-** and so we would have difficulty writing coverage tests for that
-** code. Better to leave the code out, we think.
-**
-** The point of this discussion is as follows: When creating a new
-** OS layer for an embedded system, if you use this file as an example,
-** avoid the use of malloc()/free(). Those routines work ok on OS/2
-** desktops but not so well in embedded systems.
-*/
-
-/*
-** Macros used to determine whether or not to use threads.
-*/
-#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE
-# define SQLITE_OS2_THREADS 1
-#endif
-
-/*
-** Include code that is common to all os_*.c files
-*/
-/************** Include os_common.h in the middle of os_os2.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_os2.c *********************/
-
-/* Forward references */
-typedef struct os2File os2File; /* The file structure */
-typedef struct os2ShmNode os2ShmNode; /* A shared descritive memory node */
-typedef struct os2ShmLink os2ShmLink; /* A connection to shared-memory */
-
-/*
-** The os2File structure is subclass of sqlite3_file specific for the OS/2
-** protability layer.
-*/
-struct os2File {
- const sqlite3_io_methods *pMethod; /* Always the first entry */
- HFILE h; /* Handle for accessing the file */
- int flags; /* Flags provided to os2Open() */
- int locktype; /* Type of lock currently held on this file */
- int szChunk; /* Chunk size configured by FCNTL_CHUNK_SIZE */
- char *zFullPathCp; /* Full path name of this file */
- os2ShmLink *pShmLink; /* Instance of shared memory on this file */
-};
-
-#define LOCK_TIMEOUT 10L /* the default locking timeout */
-
-/*
-** Missing from some versions of the OS/2 toolkit -
-** used to allocate from high memory if possible
-*/
-#ifndef OBJ_ANY
-# define OBJ_ANY 0x00000400
-#endif
-
-/*****************************************************************************
-** The next group of routines implement the I/O methods specified
-** by the sqlite3_io_methods object.
-******************************************************************************/
-
-/*
-** Close a file.
-*/
-static int os2Close( sqlite3_file *id ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
-
- assert( id!=0 );
- OSTRACE(( "CLOSE %d (%s)\n", pFile->h, pFile->zFullPathCp ));
-
- rc = DosClose( pFile->h );
-
- if( pFile->flags & SQLITE_OPEN_DELETEONCLOSE )
- DosForceDelete( (PSZ)pFile->zFullPathCp );
-
- free( pFile->zFullPathCp );
- pFile->zFullPathCp = NULL;
- pFile->locktype = NO_LOCK;
- pFile->h = (HFILE)-1;
- pFile->flags = 0;
-
- OpenCounter( -1 );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-/*
-** 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 os2Read(
- 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 */
-){
- ULONG fileLocation = 0L;
- ULONG got;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_READ );
- OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- if( DosRead( pFile->h, pBuf, amt, &got ) != NO_ERROR ){
- return SQLITE_IOERR_READ;
- }
- if( got == (ULONG)amt )
- return SQLITE_OK;
- else {
- /* Unread portions of the input buffer must be zero-filled */
- memset(&((char*)pBuf)[got], 0, amt-got);
- return SQLITE_IOERR_SHORT_READ;
- }
-}
-
-/*
-** Write data from a buffer into a file. Return SQLITE_OK on success
-** or some other error code on failure.
-*/
-static int os2Write(
- 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 */
-){
- ULONG fileLocation = 0L;
- APIRET rc = NO_ERROR;
- ULONG wrote;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_WRITE );
- SimulateDiskfullError( return SQLITE_FULL );
- OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype ));
- if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){
- return SQLITE_IOERR;
- }
- assert( amt>0 );
- while( amt > 0 &&
- ( rc = DosWrite( pFile->h, (PVOID)pBuf, amt, &wrote ) ) == NO_ERROR &&
- wrote > 0
- ){
- amt -= wrote;
- pBuf = &((char*)pBuf)[wrote];
- }
-
- return ( rc != NO_ERROR || amt > (int)wrote ) ? SQLITE_FULL : SQLITE_OK;
-}
-
-/*
-** Truncate an open file to a specified size
-*/
-static int os2Truncate( sqlite3_file *id, i64 nByte ){
- APIRET rc;
- os2File *pFile = (os2File*)id;
- assert( id!=0 );
- OSTRACE(( "TRUNCATE %d %lld\n", pFile->h, nByte ));
- 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 ){
- nByte = ((nByte + pFile->szChunk - 1)/pFile->szChunk) * pFile->szChunk;
- }
-
- rc = DosSetFileSize( pFile->h, nByte );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_TRUNCATE;
-}
-
-#ifdef SQLITE_TEST
-/*
-** Count the number of fullsyncs and normal syncs. This is used to test
-** that syncs and fullsyncs are occuring at the right times.
-*/
-SQLITE_API int sqlite3_sync_count = 0;
-SQLITE_API int sqlite3_fullsync_count = 0;
-#endif
-
-/*
-** Make sure all writes to a particular file are committed to disk.
-*/
-static int os2Sync( sqlite3_file *id, int flags ){
- os2File *pFile = (os2File*)id;
- OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype ));
-#ifdef SQLITE_TEST
- if( flags & 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
- UNUSED_PARAMETER(pFile);
- return SQLITE_OK;
-#else
- return DosResetBuffer( pFile->h ) == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-#endif
-}
-
-/*
-** Determine the current size of a file in bytes
-*/
-static int os2FileSize( sqlite3_file *id, sqlite3_int64 *pSize ){
- APIRET rc = NO_ERROR;
- FILESTATUS3 fsts3FileInfo;
- memset(&fsts3FileInfo, 0, sizeof(fsts3FileInfo));
- assert( id!=0 );
- SimulateIOError( return SQLITE_IOERR_FSTAT );
- rc = DosQueryFileInfo( ((os2File*)id)->h, FIL_STANDARD, &fsts3FileInfo, sizeof(FILESTATUS3) );
- if( rc == NO_ERROR ){
- *pSize = fsts3FileInfo.cbFile;
- return SQLITE_OK;
- }else{
- return SQLITE_IOERR_FSTAT;
- }
-}
-
-/*
-** Acquire a reader lock.
-*/
-static int getReadLock( os2File *pFile ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res ));
- return res;
-}
-
-/*
-** Undo a readlock
-*/
-static int unlockReadLock( os2File *id ){
- FILELOCK LockArea,
- UnlockArea;
- APIRET res;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L );
- OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res ));
- return res;
-}
-
-/*
-** Lock the file with the lock specified by parameter locktype - one
-** of the following:
-**
-** (1) SHARED_LOCK
-** (2) RESERVED_LOCK
-** (3) PENDING_LOCK
-** (4) EXCLUSIVE_LOCK
-**
-** Sometimes when requesting one lock state, additional lock states
-** are inserted in between. The locking might fail on one of the later
-** transitions leaving the lock state different from what it started but
-** still short of its goal. The following chart shows the allowed
-** transitions and the inserted intermediate states:
-**
-** UNLOCKED -> SHARED
-** SHARED -> RESERVED
-** SHARED -> (PENDING) -> EXCLUSIVE
-** RESERVED -> (PENDING) -> EXCLUSIVE
-** PENDING -> EXCLUSIVE
-**
-** This routine will only increase a lock. The os2Unlock() 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 os2Lock( sqlite3_file *id, int locktype ){
- int rc = SQLITE_OK; /* Return code from subroutines */
- APIRET res = NO_ERROR; /* Result of an OS/2 lock call */
- int newLocktype; /* Set pFile->locktype to this value before exiting */
- int gotPendingLock = 0;/* True if we acquired a PENDING lock this time */
- FILELOCK LockArea,
- UnlockArea;
- os2File *pFile = (os2File*)id;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- OSTRACE(( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype ));
-
- /* If there is already a lock of this type or more restrictive on the
- ** os2File, do nothing. Don't use the end_lock: exit path, as
- ** sqlite3_mutex_enter() hasn't been called yet.
- */
- if( pFile->locktype>=locktype ){
- OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype ));
- return SQLITE_OK;
- }
-
- /* Make sure the locking sequence is correct
- */
- assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
- assert( locktype!=PENDING_LOCK );
- assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
-
- /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or
- ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
- ** the PENDING_LOCK byte is temporary.
- */
- newLocktype = pFile->locktype;
- if( pFile->locktype==NO_LOCK
- || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK)
- ){
- LockArea.lOffset = PENDING_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
-
- /* wait longer than LOCK_TIMEOUT here not to have to try multiple times */
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L );
- if( res == NO_ERROR ){
- gotPendingLock = 1;
- OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res ));
- }
- }
-
- /* Acquire a shared lock
- */
- if( locktype==SHARED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==NO_LOCK );
- res = getReadLock(pFile);
- if( res == NO_ERROR ){
- newLocktype = SHARED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a RESERVED lock
- */
- if( locktype==RESERVED_LOCK && res == NO_ERROR ){
- assert( pFile->locktype==SHARED_LOCK );
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = RESERVED_LOCK;
- }
- OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res ));
- }
-
- /* Acquire a PENDING lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- newLocktype = PENDING_LOCK;
- gotPendingLock = 0;
- OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n",
- pFile->h ));
- }
-
- /* Acquire an EXCLUSIVE lock
- */
- if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){
- assert( pFile->locktype>=SHARED_LOCK );
- res = unlockReadLock(pFile);
- OSTRACE(( "unreadlock = %d\n", res ));
- LockArea.lOffset = SHARED_FIRST;
- LockArea.lRange = SHARED_SIZE;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- if( res == NO_ERROR ){
- newLocktype = EXCLUSIVE_LOCK;
- }else{
- OSTRACE(( "OS/2 error-code = %d\n", res ));
- getReadLock(pFile);
- }
- OSTRACE(( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res ));
- }
-
- /* If we are holding a PENDING lock that ought to be released, then
- ** release it now.
- */
- if( gotPendingLock && locktype==SHARED_LOCK ){
- int r;
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r ));
- }
-
- /* Update the state of the lock has held in the file descriptor then
- ** return the appropriate result code.
- */
- if( res == NO_ERROR ){
- rc = SQLITE_OK;
- }else{
- OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h,
- locktype, newLocktype ));
- rc = SQLITE_BUSY;
- }
- pFile->locktype = newLocktype;
- OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype ));
- 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 os2CheckReservedLock( sqlite3_file *id, int *pOut ){
- int r = 0;
- os2File *pFile = (os2File*)id;
- assert( pFile!=0 );
- if( pFile->locktype>=RESERVED_LOCK ){
- r = 1;
- OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r ));
- }else{
- FILELOCK LockArea,
- UnlockArea;
- APIRET rc = NO_ERROR;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- LockArea.lOffset = RESERVED_BYTE;
- LockArea.lRange = 1L;
- UnlockArea.lOffset = 0L;
- UnlockArea.lRange = 0L;
- rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc ));
- if( rc == NO_ERROR ){
- APIRET rcu = NO_ERROR; /* return code for unlocking */
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu ));
- }
- r = !(rc == NO_ERROR);
- OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r ));
- }
- *pOut = r;
- 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 os2Unlock( sqlite3_file *id, int locktype ){
- int type;
- os2File *pFile = (os2File*)id;
- APIRET rc = SQLITE_OK;
- APIRET res = NO_ERROR;
- FILELOCK LockArea,
- UnlockArea;
- memset(&LockArea, 0, sizeof(LockArea));
- memset(&UnlockArea, 0, sizeof(UnlockArea));
- assert( pFile!=0 );
- assert( locktype<=SHARED_LOCK );
- OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype ));
- type = pFile->locktype;
- if( type>=EXCLUSIVE_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = SHARED_FIRST;
- UnlockArea.lRange = SHARED_SIZE;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res ));
- if( locktype==SHARED_LOCK && getReadLock(pFile) != NO_ERROR ){
- /* This should never happen. We should always be able to
- ** reacquire the read lock */
- OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype ));
- rc = SQLITE_IOERR_UNLOCK;
- }
- }
- if( type>=RESERVED_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = RESERVED_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res ));
- }
- if( locktype==NO_LOCK && type>=SHARED_LOCK ){
- res = unlockReadLock(pFile);
- OSTRACE(( "UNLOCK %d is %d want %d res=%d\n",
- pFile->h, type, locktype, res ));
- }
- if( type>=PENDING_LOCK ){
- LockArea.lOffset = 0L;
- LockArea.lRange = 0L;
- UnlockArea.lOffset = PENDING_BYTE;
- UnlockArea.lRange = 1L;
- res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L );
- OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res ));
- }
- pFile->locktype = locktype;
- OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype ));
- return rc;
-}
-
-/*
-** Control and query of the open file handle.
-*/
-static int os2FileControl(sqlite3_file *id, int op, void *pArg){
- switch( op ){
- case SQLITE_FCNTL_LOCKSTATE: {
- *(int*)pArg = ((os2File*)id)->locktype;
- OSTRACE(( "FCNTL_LOCKSTATE %d lock=%d\n",
- ((os2File*)id)->h, ((os2File*)id)->locktype ));
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_CHUNK_SIZE: {
- ((os2File*)id)->szChunk = *(int*)pArg;
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SIZE_HINT: {
- sqlite3_int64 sz = *(sqlite3_int64*)pArg;
- SimulateIOErrorBenign(1);
- os2Truncate(id, sz);
- SimulateIOErrorBenign(0);
- return SQLITE_OK;
- }
- case SQLITE_FCNTL_SYNC_OMITTED: {
- return SQLITE_OK;
- }
- }
- 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 os2SectorSize(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_DEFAULT_SECTOR_SIZE;
-}
-
-/*
-** Return a vector of device characteristics.
-*/
-static int os2DeviceCharacteristics(sqlite3_file *id){
- UNUSED_PARAMETER(id);
- return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
-}
-
-
-/*
-** Character set conversion objects used by conversion routines.
-*/
-static UconvObject ucUtf8 = NULL; /* convert between UTF-8 and UCS-2 */
-static UconvObject uclCp = NULL; /* convert between local codepage and UCS-2 */
-
-/*
-** Helper function to initialize the conversion objects from and to UTF-8.
-*/
-static void initUconvObjects( void ){
- if( UniCreateUconvObject( UTF_8, &ucUtf8 ) != ULS_SUCCESS )
- ucUtf8 = NULL;
- if ( UniCreateUconvObject( (UniChar *)L"@path=yes", &uclCp ) != ULS_SUCCESS )
- uclCp = NULL;
-}
-
-/*
-** Helper function to free the conversion objects from and to UTF-8.
-*/
-static void freeUconvObjects( void ){
- if ( ucUtf8 )
- UniFreeUconvObject( ucUtf8 );
- if ( uclCp )
- UniFreeUconvObject( uclCp );
- ucUtf8 = NULL;
- uclCp = NULL;
-}
-
-/*
-** Helper function to convert UTF-8 filenames to local OS/2 codepage.
-** The two-step process: first convert the incoming UTF-8 string
-** into UCS-2 and then from UCS-2 to the current codepage.
-** The returned char pointer has to be freed.
-*/
-static char *convertUtf8PathToCp( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- if( UniStrToUcs( ucUtf8, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* conversion for current codepage which can be used for paths */
- UniStrFromUcs( uclCp, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-/*
-** Helper function to convert filenames from local codepage to UTF-8.
-** The two-step process: first convert the incoming codepage-specific
-** string into UCS-2 and then from UCS-2 to the codepage of UTF-8.
-** The returned char pointer has to be freed.
-**
-** This function is non-static to be able to use this in shell.c and
-** similar applications that take command line arguments.
-*/
-char *convertCpPathToUtf8( const char *in ){
- UniChar tempPath[CCHMAXPATH];
- char *out = (char *)calloc( CCHMAXPATH, 1 );
-
- if( !out )
- return NULL;
-
- if( !ucUtf8 || !uclCp )
- initUconvObjects();
-
- /* conversion for current codepage which can be used for paths */
- if( UniStrToUcs( uclCp, tempPath, (char *)in, CCHMAXPATH ) != ULS_SUCCESS )
- return out; /* if conversion fails, return the empty string */
-
- /* determine string for the conversion of UTF-8 which is CP1208 */
- UniStrFromUcs( ucUtf8, out, tempPath, CCHMAXPATH );
-
- return out;
-}
-
-
-#ifndef SQLITE_OMIT_WAL
-
-/*
-** Use main database file for interprocess locking. If un-defined
-** a separate file is created for this purpose. The file will be
-** used only to set file locks. There will be no data written to it.
-*/
-#define SQLITE_OS2_NO_WAL_LOCK_FILE
-
-#if 0
-static void _ERR_TRACE( const char *fmt, ... ) {
- va_list ap;
- va_start(ap, fmt);
- vfprintf(stderr, fmt, ap);
- fflush(stderr);
-}
-#define ERR_TRACE(rc, msg) \
- if( (rc) != SQLITE_OK ) _ERR_TRACE msg;
-#else
-#define ERR_TRACE(rc, msg)
-#endif
-
-/*
-** Helper functions to obtain and relinquish the global mutex. The
-** global mutex is used to protect os2ShmNodeList.
-**
-** Function os2ShmMutexHeld() is used to assert() that the global mutex
-** is held when required. This function is only used as part of assert()
-** statements. e.g.
-**
-** os2ShmEnterMutex()
-** assert( os2ShmMutexHeld() );
-** os2ShmLeaveMutex()
-*/
-static void os2ShmEnterMutex(void){
- sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-static void os2ShmLeaveMutex(void){
- sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-#ifdef SQLITE_DEBUG
-static int os2ShmMutexHeld(void) {
- return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER));
-}
-int GetCurrentProcessId(void) {
- PPIB pib;
- DosGetInfoBlocks(NULL, &pib);
- return (int)pib->pib_ulpid;
-}
-#endif
-
-/*
-** Object used to represent a the shared memory area for a single log file.
-** When multiple threads all reference the same log-summary, each thread has
-** its own os2File object, but they all point to a single instance of this
-** object. In other words, each log-summary is opened only once per process.
-**
-** os2ShmMutexHeld() 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:
-**
-** szRegion
-** hLockFile
-** shmBaseName
-**
-** Either os2ShmNode.mutex must be held or os2ShmNode.nRef==0 and
-** os2ShmMutexHeld() is true when reading or writing any other field
-** in this structure.
-**
-*/
-struct os2ShmNode {
- sqlite3_mutex *mutex; /* Mutex to access this object */
- os2ShmNode *pNext; /* Next in list of all os2ShmNode objects */
-
- int szRegion; /* Size of shared-memory regions */
-
- int nRegion; /* Size of array apRegion */
- void **apRegion; /* Array of pointers to shared-memory regions */
-
- int nRef; /* Number of os2ShmLink objects pointing to this */
- os2ShmLink *pFirst; /* First os2ShmLink object pointing to this */
-
- HFILE hLockFile; /* File used for inter-process memory locking */
- char shmBaseName[1]; /* Name of the memory object !!! must last !!! */
-};
-
-
-/*
-** 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:
-**
-** os2Shm.pShmNode
-** os2Shm.id
-**
-** All other fields are read/write. The os2Shm.pShmNode->mutex must be held
-** while accessing any read/write fields.
-*/
-struct os2ShmLink {
- os2ShmNode *pShmNode; /* The underlying os2ShmNode object */
- os2ShmLink *pNext; /* Next os2Shm with the same os2ShmNode */
- u32 sharedMask; /* Mask of shared locks held */
- u32 exclMask; /* Mask of exclusive locks held */
-#ifdef SQLITE_DEBUG
- u8 id; /* Id of this connection with its os2ShmNode */
-#endif
-};
-
-
-/*
-** A global list of all os2ShmNode objects.
-**
-** The os2ShmMutexHeld() must be true while reading or writing this list.
-*/
-static os2ShmNode *os2ShmNodeList = NULL;
-
-/*
-** Constants used for locking
-*/
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
-#define OS2_SHM_BASE (PENDING_BYTE + 0x10000) /* first lock byte */
-#else
-#define OS2_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
-#endif
-
-#define OS2_SHM_DMS (OS2_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
-
-/*
-** Apply advisory locks for all n bytes beginning at ofst.
-*/
-#define _SHM_UNLCK 1 /* no lock */
-#define _SHM_RDLCK 2 /* shared lock, no wait */
-#define _SHM_WRLCK 3 /* exlusive lock, no wait */
-#define _SHM_WRLCK_WAIT 4 /* exclusive lock, wait */
-static int os2ShmSystemLock(
- os2ShmNode *pNode, /* Apply locks to this open shared-memory segment */
- int lockType, /* _SHM_UNLCK, _SHM_RDLCK, _SHM_WRLCK or _SHM_WRLCK_WAIT */
- int ofst, /* Offset to first byte to be locked/unlocked */
- int nByte /* Number of bytes to lock or unlock */
-){
- APIRET rc;
- FILELOCK area;
- ULONG mode, timeout;
-
- /* Access to the os2ShmNode object is serialized by the caller */
- assert( sqlite3_mutex_held(pNode->mutex) || pNode->nRef==0 );
-
- mode = 1; /* shared lock */
- timeout = 0; /* no wait */
- area.lOffset = ofst;
- area.lRange = nByte;
-
- switch( lockType ) {
- case _SHM_WRLCK_WAIT:
- timeout = (ULONG)-1; /* wait forever */
- case _SHM_WRLCK:
- mode = 0; /* exclusive lock */
- case _SHM_RDLCK:
- rc = DosSetFileLocks(pNode->hLockFile,
- NULL, &area, timeout, mode);
- break;
- /* case _SHM_UNLCK: */
- default:
- rc = DosSetFileLocks(pNode->hLockFile,
- &area, NULL, 0, 0);
- break;
- }
-
- OSTRACE(("SHM-LOCK %d %s %s 0x%08lx\n",
- pNode->hLockFile,
- rc==SQLITE_OK ? "ok" : "failed",
- lockType==_SHM_UNLCK ? "Unlock" : "Lock",
- rc));
-
- ERR_TRACE(rc, ("os2ShmSystemLock: %d %s\n", rc, pNode->shmBaseName))
-
- return ( rc == 0 ) ? SQLITE_OK : SQLITE_BUSY;
-}
-
-/*
-** Find an os2ShmNode in global list or allocate a new one, if not found.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static int os2OpenSharedMemory( os2File *fd, int szRegion ) {
- os2ShmLink *pLink;
- os2ShmNode *pNode;
- int cbShmName, rc = SQLITE_OK;
- char shmName[CCHMAXPATH + 30];
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- ULONG action;
-#endif
-
- /* We need some additional space at the end to append the region number */
- cbShmName = sprintf(shmName, "\\SHAREMEM\\%s", fd->zFullPathCp );
- if( cbShmName >= CCHMAXPATH-8 )
- return SQLITE_IOERR_SHMOPEN;
-
- /* Replace colon in file name to form a valid shared memory name */
- shmName[10+1] = '!';
-
- /* Allocate link object (we free it later in case of failure) */
- pLink = sqlite3_malloc( sizeof(*pLink) );
- if( !pLink )
- return SQLITE_NOMEM;
-
- /* Access node list */
- os2ShmEnterMutex();
-
- /* Find node by it's shared memory base name */
- for( pNode = os2ShmNodeList;
- pNode && stricmp(shmName, pNode->shmBaseName) != 0;
- pNode = pNode->pNext ) ;
-
- /* Not found: allocate a new node */
- if( !pNode ) {
- pNode = sqlite3_malloc( sizeof(*pNode) + cbShmName );
- if( pNode ) {
- memset(pNode, 0, sizeof(*pNode) );
- pNode->szRegion = szRegion;
- pNode->hLockFile = (HFILE)-1;
- strcpy(pNode->shmBaseName, shmName);
-
-#ifdef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( DosDupHandle(fd->h, &pNode->hLockFile) != 0 ) {
-#else
- sprintf(shmName, "%s-lck", fd->zFullPathCp);
- if( DosOpen((PSZ)shmName, &pNode->hLockFile, &action, 0, FILE_NORMAL,
- OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW,
- OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR,
- NULL) != 0 ) {
-#endif
- sqlite3_free(pNode);
- rc = SQLITE_IOERR;
- } else {
- pNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
- if( !pNode->mutex ) {
- sqlite3_free(pNode);
- rc = SQLITE_NOMEM;
- }
- }
- } else {
- rc = SQLITE_NOMEM;
- }
-
- if( rc == SQLITE_OK ) {
- pNode->pNext = os2ShmNodeList;
- os2ShmNodeList = pNode;
- } else {
- pNode = NULL;
- }
- } else if( pNode->szRegion != szRegion ) {
- rc = SQLITE_IOERR_SHMSIZE;
- pNode = NULL;
- }
-
- if( pNode ) {
- sqlite3_mutex_enter(pNode->mutex);
-
- memset(pLink, 0, sizeof(*pLink));
-
- pLink->pShmNode = pNode;
- pLink->pNext = pNode->pFirst;
- pNode->pFirst = pLink;
- pNode->nRef++;
-
- fd->pShmLink = pLink;
-
- sqlite3_mutex_leave(pNode->mutex);
-
- } else {
- /* Error occured. Free our link object. */
- sqlite3_free(pLink);
- }
-
- os2ShmLeaveMutex();
-
- ERR_TRACE(rc, ("os2OpenSharedMemory: %d %s\n", rc, fd->zFullPathCp))
-
- return rc;
-}
-
-/*
-** Purge the os2ShmNodeList list of all entries with nRef==0.
-**
-** This is not a VFS shared-memory method; it is a utility function called
-** by VFS shared-memory methods.
-*/
-static void os2PurgeShmNodes( int deleteFlag ) {
- os2ShmNode *pNode;
- os2ShmNode **ppNode;
-
- os2ShmEnterMutex();
-
- ppNode = &os2ShmNodeList;
-
- while( *ppNode ) {
- pNode = *ppNode;
-
- if( pNode->nRef == 0 ) {
- *ppNode = pNode->pNext;
-
- if( pNode->apRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- while( pNode->nRegion-- ) {
-#ifdef SQLITE_DEBUG
- int rc =
-#endif
- DosFreeMem(pNode->apRegion[pNode->nRegion]);
-
- OSTRACE(("SHM-PURGE pid-%d unmap region=%d %s\n",
- (int)GetCurrentProcessId(), pNode->nRegion,
- rc == 0 ? "ok" : "failed"));
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- sqlite3_free(pNode->apRegion);
- }
-
- DosClose(pNode->hLockFile);
-
-#ifndef SQLITE_OS2_NO_WAL_LOCK_FILE
- if( deleteFlag ) {
- char fileName[CCHMAXPATH];
- /* Skip "\\SHAREMEM\\" */
- sprintf(fileName, "%s-lck", pNode->shmBaseName + 10);
- /* restore colon */
- fileName[1] = ':';
-
- DosForceDelete(fileName);
- }
-#endif
-
- sqlite3_mutex_free(pNode->mutex);
-
- sqlite3_free(pNode);
-
- } else {
- ppNode = &pNode->pNext;
- }
- }
-
- os2ShmLeaveMutex();
-}
-
-/*
-** This function is called to obtain a pointer to region iRegion of the
-** shared-memory associated with the database file id. 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 os2ShmMap(
- sqlite3_file *id, /* Handle open on database file */
- int iRegion, /* Region to retrieve */
- int szRegion, /* Size of regions */
- int bExtend, /* True to extend block if necessary */
- void volatile **pp /* OUT: Mapped memory */
-){
- PVOID pvTemp;
- void **apRegion;
- os2ShmNode *pNode;
- int n, rc = SQLITE_OK;
- char shmName[CCHMAXPATH];
- os2File *pFile = (os2File*)id;
-
- *pp = NULL;
-
- if( !pFile->pShmLink )
- rc = os2OpenSharedMemory( pFile, szRegion );
-
- if( rc == SQLITE_OK ) {
- pNode = pFile->pShmLink->pShmNode ;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- assert( szRegion==pNode->szRegion );
-
- /* Unmapped region ? */
- if( iRegion >= pNode->nRegion ) {
- /* Prevent other processes from resizing the shared memory */
- os2ShmSystemLock(pNode, _SHM_WRLCK_WAIT, OS2_SHM_DMS, 1);
-
- apRegion = sqlite3_realloc(
- pNode->apRegion, (iRegion + 1) * sizeof(apRegion[0]));
-
- if( apRegion ) {
- pNode->apRegion = apRegion;
-
- while( pNode->nRegion <= iRegion ) {
- sprintf(shmName, "%s-%u",
- pNode->shmBaseName, pNode->nRegion);
-
- if( DosGetNamedSharedMem(&pvTemp, (PSZ)shmName,
- PAG_READ | PAG_WRITE) != NO_ERROR ) {
- if( !bExtend )
- break;
-
- if( DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_ANY) != NO_ERROR &&
- DosAllocSharedMem(&pvTemp, (PSZ)shmName, szRegion,
- PAG_READ | PAG_WRITE | PAG_COMMIT) != NO_ERROR ) {
- rc = SQLITE_NOMEM;
- break;
- }
- }
-
- apRegion[pNode->nRegion++] = pvTemp;
- }
-
- /* zero out remaining entries */
- for( n = pNode->nRegion; n <= iRegion; n++ )
- pNode->apRegion[n] = NULL;
-
- /* Return this region (maybe zero) */
- *pp = pNode->apRegion[iRegion];
- } else {
- rc = SQLITE_NOMEM;
- }
-
- /* Allow other processes to resize the shared memory */
- os2ShmSystemLock(pNode, _SHM_UNLCK, OS2_SHM_DMS, 1);
-
- } else {
- /* Region has been mapped previously */
- *pp = pNode->apRegion[iRegion];
- }
-
- sqlite3_mutex_leave(pNode->mutex);
- }
-
- ERR_TRACE(rc, ("os2ShmMap: %s iRgn = %d, szRgn = %d, bExt = %d : %d\n",
- pFile->zFullPathCp, iRegion, szRegion, bExtend, rc))
-
- return rc;
-}
-
-/*
-** 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 os2ShmUnmap(
- sqlite3_file *id, /* The underlying database file */
- int deleteFlag /* Delete shared-memory if true */
-){
- os2File *pFile = (os2File*)id;
- os2ShmLink *pLink = pFile->pShmLink;
-
- if( pLink ) {
- int nRef = -1;
- os2ShmLink **ppLink;
- os2ShmNode *pNode = pLink->pShmNode;
-
- sqlite3_mutex_enter(pNode->mutex);
-
- for( ppLink = &pNode->pFirst;
- *ppLink && *ppLink != pLink;
- ppLink = &(*ppLink)->pNext ) ;
-
- assert(*ppLink);
-
- if( *ppLink ) {
- *ppLink = pLink->pNext;
- nRef = --pNode->nRef;
- } else {
- ERR_TRACE(1, ("os2ShmUnmap: link not found ! %s\n",
- pNode->shmBaseName))
- }
-
- pFile->pShmLink = NULL;
- sqlite3_free(pLink);
-
- sqlite3_mutex_leave(pNode->mutex);
-
- if( nRef == 0 )
- os2PurgeShmNodes( deleteFlag );
- }
-
- return SQLITE_OK;
-}
-
-/*
-** 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 os2ShmLock(
- sqlite3_file *id, /* 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 */
-){
- u32 mask; /* Mask of locks to take or release */
- int rc = SQLITE_OK; /* Result code */
- os2File *pFile = (os2File*)id;
- os2ShmLink *p = pFile->pShmLink; /* The shared memory being locked */
- os2ShmLink *pX; /* For looping over all siblings */
- os2ShmNode *pShmNode = p->pShmNode; /* Our node */
-
- 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 = (u32)((1U<<(ofst+n)) - (1U<1 || mask==(1<mutex);
-
- if( flags & SQLITE_SHM_UNLOCK ){
- u32 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 = os2ShmSystemLock(pShmNode, _SHM_UNLCK, ofst+OS2_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 ){
- u32 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 = os2ShmSystemLock(pShmNode, _SHM_RDLCK, ofst+OS2_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 = os2ShmSystemLock(pShmNode, _SHM_WRLCK, ofst+OS2_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 %s\n",
- p->id, (int)GetCurrentProcessId(), p->sharedMask, p->exclMask,
- rc ? "failed" : "ok"));
-
- ERR_TRACE(rc, ("os2ShmLock: ofst = %d, n = %d, flags = 0x%x -> %d \n",
- ofst, n, flags, rc))
-
- return rc;
-}
-
-/*
-** Implement a memory barrier or memory fence on shared memory.
-**
-** All loads and stores begun before the barrier must complete before
-** any load or store begun after the barrier.
-*/
-static void os2ShmBarrier(
- sqlite3_file *id /* Database file holding the shared memory */
-){
- UNUSED_PARAMETER(id);
- os2ShmEnterMutex();
- os2ShmLeaveMutex();
-}
-
-#else
-# define os2ShmMap 0
-# define os2ShmLock 0
-# define os2ShmBarrier 0
-# define os2ShmUnmap 0
-#endif /* #ifndef SQLITE_OMIT_WAL */
-
-
-/*
-** This vector defines all the methods that can operate on an
-** sqlite3_file for os2.
-*/
-static const sqlite3_io_methods os2IoMethod = {
- 2, /* iVersion */
- os2Close, /* xClose */
- os2Read, /* xRead */
- os2Write, /* xWrite */
- os2Truncate, /* xTruncate */
- os2Sync, /* xSync */
- os2FileSize, /* xFileSize */
- os2Lock, /* xLock */
- os2Unlock, /* xUnlock */
- os2CheckReservedLock, /* xCheckReservedLock */
- os2FileControl, /* xFileControl */
- os2SectorSize, /* xSectorSize */
- os2DeviceCharacteristics, /* xDeviceCharacteristics */
- os2ShmMap, /* xShmMap */
- os2ShmLock, /* xShmLock */
- os2ShmBarrier, /* xShmBarrier */
- os2ShmUnmap /* xShmUnmap */
-};
-
-
-/***************************************************************************
-** Here ends the I/O methods that form the sqlite3_io_methods object.
-**
-** The next block of code implements the VFS methods.
-****************************************************************************/
-
-/*
-** Create a temporary file name in zBuf. zBuf must be big enough to
-** hold at pVfs->mxPathname characters.
-*/
-static int getTempname(int nBuf, char *zBuf ){
- static const char zChars[] =
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "0123456789";
- int i, j;
- PSZ zTempPathCp;
- char zTempPath[CCHMAXPATH];
- ULONG ulDriveNum, ulDriveMap;
-
- /* 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 );
-
- if( sqlite3_temp_directory ) {
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", sqlite3_temp_directory);
- } else if( DosScanEnv( (PSZ)"TEMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMP", &zTempPathCp ) == NO_ERROR ||
- DosScanEnv( (PSZ)"TMPDIR", &zTempPathCp ) == NO_ERROR ) {
- char *zTempPathUTF = convertCpPathToUtf8( (char *)zTempPathCp );
- sqlite3_snprintf(CCHMAXPATH-30, zTempPath, "%s", zTempPathUTF);
- free( zTempPathUTF );
- } else if( DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ) == NO_ERROR ) {
- zTempPath[0] = (char)('A' + ulDriveNum - 1);
- zTempPath[1] = ':';
- zTempPath[2] = '\0';
- } else {
- zTempPath[0] = '\0';
- }
-
- /* Strip off a trailing slashes or backslashes, otherwise we would get *
- * multiple (back)slashes which causes DosOpen() to fail. *
- * Trailing spaces are not allowed, either. */
- j = sqlite3Strlen30(zTempPath);
- while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' ||
- zTempPath[j-1] == ' ' ) ){
- j--;
- }
- zTempPath[j] = '\0';
-
- /* We use 20 bytes to randomize the name */
- sqlite3_snprintf(nBuf-22, zBuf,
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath);
- j = sqlite3Strlen30(zBuf);
- sqlite3_randomness( 20, &zBuf[j] );
- for( i = 0; i < 20; i++, j++ ){
- zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ];
- }
- zBuf[j] = 0;
-
- OSTRACE(( "TEMP FILENAME: %s\n", zBuf ));
- return SQLITE_OK;
-}
-
-
-/*
-** Turn a relative pathname into a full pathname. Write the full
-** pathname into zFull[]. zFull[] will be at least pVfs->mxPathname
-** bytes in size.
-*/
-static int os2FullPathname(
- 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 */
-){
- char *zRelativeCp = convertUtf8PathToCp( zRelative );
- char zFullCp[CCHMAXPATH] = "\0";
- char *zFullUTF;
- APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME,
- zFullCp, CCHMAXPATH );
- free( zRelativeCp );
- zFullUTF = convertCpPathToUtf8( zFullCp );
- sqlite3_snprintf( nFull, zFull, zFullUTF );
- free( zFullUTF );
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR;
-}
-
-
-/*
-** Open a file.
-*/
-static int os2Open(
- sqlite3_vfs *pVfs, /* Not used */
- 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 */
-){
- HFILE h;
- ULONG ulOpenFlags = 0;
- ULONG ulOpenMode = 0;
- ULONG ulAction = 0;
- ULONG rc;
- os2File *pFile = (os2File*)id;
- const char *zUtf8Name = zName;
- char *zNameCp;
- char zTmpname[CCHMAXPATH];
-
- int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
- int isCreate = (flags & SQLITE_OPEN_CREATE);
- int isReadWrite = (flags & SQLITE_OPEN_READWRITE);
-#ifndef NDEBUG
- int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
- int isReadonly = (flags & SQLITE_OPEN_READONLY);
- int eType = (flags & 0xFFFFFF00);
- int isOpenJournal = (isCreate && (
- eType==SQLITE_OPEN_MASTER_JOURNAL
- || eType==SQLITE_OPEN_MAIN_JOURNAL
- || eType==SQLITE_OPEN_WAL
- ));
-#endif
-
- UNUSED_PARAMETER(pVfs);
- assert( id!=0 );
-
- /* 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( pFile, 0, sizeof(*pFile) );
- pFile->h = (HFILE)-1;
-
- /* If the second argument to this function is NULL, generate a
- ** temporary file name to use
- */
- if( !zUtf8Name ){
- assert(isDelete && !isOpenJournal);
- rc = getTempname(CCHMAXPATH, zTmpname);
- if( rc!=SQLITE_OK ){
- return rc;
- }
- zUtf8Name = zTmpname;
- }
-
- if( isReadWrite ){
- ulOpenMode |= OPEN_ACCESS_READWRITE;
- }else{
- ulOpenMode |= OPEN_ACCESS_READONLY;
- }
-
- /* Open in random access mode for possibly better speed. Allow full
- ** sharing because file locks will provide exclusive access when needed.
- ** The handle should not be inherited by child processes and we don't
- ** want popups from the critical error handler.
- */
- ulOpenMode |= OPEN_FLAGS_RANDOM | OPEN_SHARE_DENYNONE |
- OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_FAIL_ON_ERROR;
-
- /* 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. */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_FAIL_IF_EXISTS;
- }else if( isCreate ){
- /* Open existing file, or create if it doesn't exist */
- ulOpenFlags |= OPEN_ACTION_CREATE_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }else{
- /* Opens a file, only if it exists. */
- ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS;
- }
-
- zNameCp = convertUtf8PathToCp( zUtf8Name );
- rc = DosOpen( (PSZ)zNameCp,
- &h,
- &ulAction,
- 0L,
- FILE_NORMAL,
- ulOpenFlags,
- ulOpenMode,
- (PEAOP2)NULL );
- free( zNameCp );
-
- if( rc != NO_ERROR ){
- OSTRACE(( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulFlags=%#lx, ulMode=%#lx\n",
- rc, zUtf8Name, ulAction, ulOpenFlags, ulOpenMode ));
-
- if( isReadWrite ){
- return os2Open( pVfs, zName, id,
- ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
- pOutFlags );
- }else{
- return SQLITE_CANTOPEN;
- }
- }
-
- if( pOutFlags ){
- *pOutFlags = isReadWrite ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY;
- }
-
- os2FullPathname( pVfs, zUtf8Name, sizeof( zTmpname ), zTmpname );
- pFile->zFullPathCp = convertUtf8PathToCp( zTmpname );
- pFile->pMethod = &os2IoMethod;
- pFile->flags = flags;
- pFile->h = h;
-
- OpenCounter(+1);
- OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags ));
- return SQLITE_OK;
-}
-
-/*
-** Delete the named file.
-*/
-static int os2Delete(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to delete */
- int syncDir /* Not used on os2 */
-){
- APIRET rc;
- char *zFilenameCp;
- SimulateIOError( return SQLITE_IOERR_DELETE );
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosDelete( (PSZ)zFilenameCp );
- free( zFilenameCp );
- OSTRACE(( "DELETE \"%s\"\n", zFilename ));
- return (rc == NO_ERROR ||
- rc == ERROR_FILE_NOT_FOUND ||
- rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE;
-}
-
-/*
-** Check the existance and status of a file.
-*/
-static int os2Access(
- sqlite3_vfs *pVfs, /* Not used on os2 */
- const char *zFilename, /* Name of file to check */
- int flags, /* Type of test to make on this file */
- int *pOut /* Write results here */
-){
- APIRET rc;
- FILESTATUS3 fsts3ConfigInfo;
- char *zFilenameCp;
-
- UNUSED_PARAMETER(pVfs);
- SimulateIOError( return SQLITE_IOERR_ACCESS; );
-
- zFilenameCp = convertUtf8PathToCp( zFilename );
- rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD,
- &fsts3ConfigInfo, sizeof(FILESTATUS3) );
- free( zFilenameCp );
- OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n",
- fsts3ConfigInfo.attrFile, flags, rc ));
-
- switch( flags ){
- case SQLITE_ACCESS_EXISTS:
- /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
- ** as if it does not exist.
- */
- if( fsts3ConfigInfo.cbFile == 0 )
- rc = ERROR_FILE_NOT_FOUND;
- break;
- case SQLITE_ACCESS_READ:
- break;
- case SQLITE_ACCESS_READWRITE:
- if( fsts3ConfigInfo.attrFile & FILE_READONLY )
- rc = ERROR_ACCESS_DENIED;
- break;
- default:
- rc = ERROR_FILE_NOT_FOUND;
- assert( !"Invalid flags argument" );
- }
-
- *pOut = (rc == NO_ERROR);
- OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut ));
-
- 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.
-*/
-/*
-** Interfaces for opening a shared library, finding entry points
-** within the shared library, and closing the shared library.
-*/
-static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){
- HMODULE hmod;
- APIRET rc;
- char *zFilenameCp = convertUtf8PathToCp(zFilename);
- rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod);
- free(zFilenameCp);
- return rc != NO_ERROR ? 0 : (void*)hmod;
-}
-/*
-** A no-op since the error code is returned on the DosLoadModule call.
-** os2Dlopen returns zero if DosLoadModule is not successful.
-*/
-static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){
-/* no-op */
-}
-static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){
- PFN pfn;
- APIRET rc;
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)zSymbol, &pfn);
- if( rc != NO_ERROR ){
- /* if the symbol itself was not found, search again for the same
- * symbol with an extra underscore, that might be needed depending
- * on the calling convention */
- char _zSymbol[256] = "_";
- strncat(_zSymbol, zSymbol, 254);
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn);
- }
- return rc != NO_ERROR ? 0 : (void(*)(void))pfn;
-}
-static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){
- DosFreeModule((HMODULE)pHandle);
-}
-#else /* if SQLITE_OMIT_LOAD_EXTENSION is defined: */
- #define os2DlOpen 0
- #define os2DlError 0
- #define os2DlSym 0
- #define os2DlClose 0
-#endif
-
-
-/*
-** Write up to nBuf bytes of randomness into zBuf.
-*/
-static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){
- int n = 0;
-#if defined(SQLITE_TEST)
- n = nBuf;
- memset(zBuf, 0, nBuf);
-#else
- int i;
- PPIB ppib;
- PTIB ptib;
- DATETIME dt;
- static unsigned c = 0;
- /* Ordered by variation probability */
- static ULONG svIdx[6] = { QSV_MS_COUNT, QSV_TIME_LOW,
- QSV_MAXPRMEM, QSV_MAXSHMEM,
- QSV_TOTAVAILMEM, QSV_TOTRESMEM };
-
- /* 8 bytes; timezone and weekday don't increase the randomness much */
- if( (int)sizeof(dt)-3 <= nBuf - n ){
- c += 0x0100;
- DosGetDateTime(&dt);
- dt.year = (USHORT)((dt.year - 1900) | c);
- memcpy(&zBuf[n], &dt, sizeof(dt)-3);
- n += sizeof(dt)-3;
- }
-
- /* 4 bytes; PIDs and TIDs are 16 bit internally, so combine them */
- if( (int)sizeof(ULONG) <= nBuf - n ){
- DosGetInfoBlocks(&ptib, &ppib);
- *(PULONG)&zBuf[n] = MAKELONG(ppib->pib_ulpid,
- ptib->tib_ptib2->tib2_ultid);
- n += sizeof(ULONG);
- }
-
- /* Up to 6 * 4 bytes; variables depend on the system state */
- for( i = 0; i < 6 && (int)sizeof(ULONG) <= nBuf - n; i++ ){
- DosQuerySysInfo(svIdx[i], svIdx[i],
- (PULONG)&zBuf[n], sizeof(ULONG));
- n += sizeof(ULONG);
- }
-#endif
-
- return n;
-}
-
-/*
-** 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 os2Sleep( sqlite3_vfs *pVfs, int microsec ){
- DosSleep( (microsec/1000) );
- return microsec;
-}
-
-/*
-** The following variable, if set to a non-zero value, becomes the result
-** returned from sqlite3OsCurrentTime(). This is used for testing.
-*/
-#ifdef SQLITE_TEST
-SQLITE_API int sqlite3_current_time = 0;
-#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 0. Return 1 if the time and date cannot be found.
-*/
-static int os2CurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *piNow){
-#ifdef SQLITE_TEST
- static const sqlite3_int64 unixEpoch = 24405875*(sqlite3_int64)8640000;
-#endif
- int year, month, datepart, timepart;
-
- DATETIME dt;
- DosGetDateTime( &dt );
-
- year = dt.year;
- month = dt.month;
-
- /* Calculations from http://www.astro.keele.ac.uk/~rno/Astronomy/hjd.html
- ** http://www.astro.keele.ac.uk/~rno/Astronomy/hjd-0.1.c
- ** Calculate the Julian days
- */
- datepart = (int)dt.day - 32076 +
- 1461*(year + 4800 + (month - 14)/12)/4 +
- 367*(month - 2 - (month - 14)/12*12)/12 -
- 3*((year + 4900 + (month - 14)/12)/100)/4;
-
- /* Time in milliseconds, hours to noon added */
- timepart = 12*3600*1000 + dt.hundredths*10 + dt.seconds*1000 +
- ((int)dt.minutes + dt.timezone)*60*1000 + dt.hours*3600*1000;
-
- *piNow = (sqlite3_int64)datepart*86400*1000 + timepart;
-
-#ifdef SQLITE_TEST
- if( sqlite3_current_time ){
- *piNow = 1000*(sqlite3_int64)sqlite3_current_time + unixEpoch;
- }
-#endif
-
- UNUSED_PARAMETER(pVfs);
- return 0;
-}
-
-/*
-** 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 os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){
- int rc;
- sqlite3_int64 i;
- rc = os2CurrentTimeInt64(pVfs, &i);
- if( !rc ){
- *prNow = i/86400000.0;
- }
- return rc;
-}
-
-/*
-** The idea is that this function works like a combination of
-** GetLastError() and FormatMessage() on windows (or errno and
-** strerror_r() on unix). After an error is returned by an OS
-** function, SQLite calls this function with zBuf pointing to
-** a buffer of nBuf bytes. The OS layer should populate the
-** buffer with a nul-terminated UTF-8 encoded error message
-** describing the last IO error to have occurred within the calling
-** thread.
-**
-** If the error message is too large for the supplied buffer,
-** it should be truncated. The return value of xGetLastError
-** is zero if the error message fits in the buffer, or non-zero
-** otherwise (if the message was truncated). If non-zero is returned,
-** then it is not necessary to include the nul-terminator character
-** in the output buffer.
-**
-** Not supplying an error message will have no adverse effect
-** on SQLite. It is fine to have an implementation that never
-** returns an error message:
-**
-** int xGetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
-** assert(zBuf[0]=='\0');
-** return 0;
-** }
-**
-** However if an error message is supplied, it will be incorporated
-** by sqlite into the error message available to the user using
-** sqlite3_errmsg(), possibly making IO errors easier to debug.
-*/
-static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){
- assert(zBuf[0]=='\0');
- return 0;
-}
-
-/*
-** Initialize and deinitialize the operating system interface.
-*/
-SQLITE_API int sqlite3_os_init(void){
- static sqlite3_vfs os2Vfs = {
- 3, /* iVersion */
- sizeof(os2File), /* szOsFile */
- CCHMAXPATH, /* mxPathname */
- 0, /* pNext */
- "os2", /* zName */
- 0, /* pAppData */
-
- os2Open, /* xOpen */
- os2Delete, /* xDelete */
- os2Access, /* xAccess */
- os2FullPathname, /* xFullPathname */
- os2DlOpen, /* xDlOpen */
- os2DlError, /* xDlError */
- os2DlSym, /* xDlSym */
- os2DlClose, /* xDlClose */
- os2Randomness, /* xRandomness */
- os2Sleep, /* xSleep */
- os2CurrentTime, /* xCurrentTime */
- os2GetLastError, /* xGetLastError */
- os2CurrentTimeInt64, /* xCurrentTimeInt64 */
- 0, /* xSetSystemCall */
- 0, /* xGetSystemCall */
- 0 /* xNextSystemCall */
- };
- sqlite3_vfs_register(&os2Vfs, 1);
- initUconvObjects();
-/* sqlite3OSTrace = 1; */
- return SQLITE_OK;
-}
-SQLITE_API int sqlite3_os_end(void){
- freeUconvObjects();
- return SQLITE_OK;
-}
-
-#endif /* SQLITE_OS_OS2 */
-
-/************** End of os_os2.c **********************************************/
/************** Begin file os_unix.c *****************************************/
/*
** 2004 May 22
**
** The author disclaims copyright to this source code. In place of
@@ -59054,18 +56650,18 @@
/*
** Release all resources associated with an sqlite3_backup* handle.
*/
SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p){
sqlite3_backup **pp; /* Ptr to head of pagers backup list */
- MUTEX_LOGIC( sqlite3_mutex *mutex; ) /* Mutex to protect source database */
+ sqlite3 *pSrcDb; /* Source database connection */
int rc; /* Value to return */
/* Enter the mutexes */
if( p==0 ) return SQLITE_OK;
- sqlite3_mutex_enter(p->pSrcDb->mutex);
+ pSrcDb = p->pSrcDb;
+ sqlite3_mutex_enter(pSrcDb->mutex);
sqlite3BtreeEnter(p->pSrc);
- MUTEX_LOGIC( mutex = p->pSrcDb->mutex; )
if( p->pDestDb ){
sqlite3_mutex_enter(p->pDestDb->mutex);
}
/* Detach this backup from the source pager. */
@@ -59087,20 +56683,20 @@
rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
sqlite3Error(p->pDestDb, rc, 0);
/* Exit the mutexes and free the backup context structure. */
if( p->pDestDb ){
- sqlite3_mutex_leave(p->pDestDb->mutex);
+ sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
}
sqlite3BtreeLeave(p->pSrc);
if( p->pDestDb ){
/* EVIDENCE-OF: R-64852-21591 The sqlite3_backup object is created by a
** call to sqlite3_backup_init() and is destroyed by a call to
** sqlite3_backup_finish(). */
sqlite3_free(p);
}
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(pSrcDb);
return rc;
}
/*
** Return the number of pages still to be backed up as of the most recent
@@ -62860,10 +60456,11 @@
SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){
sqlite3 *db;
if( NEVER(p==0) ) return;
db = p->db;
+ assert( sqlite3_mutex_held(db->mutex) );
if( p->pPrev ){
p->pPrev->pNext = p->pNext;
}else{
assert( db->pVdbe==p );
db->pVdbe = p->pNext;
@@ -63699,21 +61296,15 @@
** pointer is a harmless no-op. */
rc = SQLITE_OK;
}else{
Vdbe *v = (Vdbe*)pStmt;
sqlite3 *db = v->db;
-#if SQLITE_THREADSAFE
- sqlite3_mutex *mutex;
-#endif
if( vdbeSafety(v) ) return SQLITE_MISUSE_BKPT;
-#if SQLITE_THREADSAFE
- mutex = v->db->mutex;
-#endif
- sqlite3_mutex_enter(mutex);
+ sqlite3_mutex_enter(db->mutex);
rc = sqlite3VdbeFinalize(v);
rc = sqlite3ApiExit(db, rc);
- sqlite3_mutex_leave(mutex);
+ sqlite3LeaveMutexAndCloseZombie(db);
}
return rc;
}
/*
@@ -70017,11 +67608,10 @@
assert( u.bl.pC->isTable || pOp->opcode!=OP_RowData );
assert( u.bl.pC->isIndex || pOp->opcode==OP_RowData );
assert( u.bl.pC!=0 );
assert( u.bl.pC->nullRow==0 );
assert( u.bl.pC->pseudoTableReg==0 );
- assert( !u.bl.pC->isSorter );
assert( u.bl.pC->pCursor!=0 );
u.bl.pCrsr = u.bl.pC->pCursor;
assert( sqlite3BtreeCursorIsValid(u.bl.pCrsr) );
/* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or
@@ -88019,12 +85609,23 @@
*/
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
assert( argc==1 );
UNUSED_PARAMETER(argc);
switch( sqlite3_value_type(argv[0]) ){
- case SQLITE_INTEGER:
case SQLITE_FLOAT: {
+ double r1, r2;
+ char zBuf[50];
+ r1 = sqlite3_value_double(argv[0]);
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.15g", r1);
+ sqlite3AtoF(zBuf, &r2, 20, SQLITE_UTF8);
+ if( r1!=r2 ){
+ sqlite3_snprintf(sizeof(zBuf), zBuf, "%!.20e", r1);
+ }
+ sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
+ break;
+ }
+ case SQLITE_INTEGER: {
sqlite3_result_value(context, argv[0]);
break;
}
case SQLITE_BLOB: {
char *zText = 0;
@@ -114347,18 +111948,30 @@
sqlite3BtreeLeaveAll(db);
#else
UNUSED_PARAMETER(db);
#endif
}
+
+/*
+** Return TRUE if database connection db has unfinalized prepared
+** statements or unfinished sqlite3_backup objects.
+*/
+static int connectionIsBusy(sqlite3 *db){
+ int j;
+ assert( sqlite3_mutex_held(db->mutex) );
+ if( db->pVdbe ) return 1;
+ for(j=0; jnDb; j++){
+ Btree *pBt = db->aDb[j].pBt;
+ if( pBt && sqlite3BtreeIsInBackup(pBt) ) return 1;
+ }
+ return 0;
+}
/*
** Close an existing SQLite database
*/
-SQLITE_API int sqlite3_close(sqlite3 *db){
- HashElem *i; /* Hash table iterator */
- int j;
-
+static int sqlite3Close(sqlite3 *db, int forceZombie){
if( !db ){
return SQLITE_OK;
}
if( !sqlite3SafetyCheckSickOrOk(db) ){
return SQLITE_MISUSE_BKPT;
@@ -114375,28 +111988,66 @@
** SQL statements below, as the v-table implementation may be storing
** some prepared statements internally.
*/
sqlite3VtabRollback(db);
- /* If there are any outstanding VMs, return SQLITE_BUSY. */
- if( db->pVdbe ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinalised statements");
+ /* Legacy behavior (sqlite3_close() behavior) is to return
+ ** SQLITE_BUSY if the connection can not be closed immediately.
+ */
+ if( !forceZombie && connectionIsBusy(db) ){
+ sqlite3Error(db, SQLITE_BUSY, "unable to close due to unfinalized "
+ "statements or unfinished backups");
sqlite3_mutex_leave(db->mutex);
return SQLITE_BUSY;
}
- assert( sqlite3SafetyCheckSickOrOk(db) );
-
- for(j=0; jnDb; j++){
- Btree *pBt = db->aDb[j].pBt;
- if( pBt && sqlite3BtreeIsInBackup(pBt) ){
- sqlite3Error(db, SQLITE_BUSY,
- "unable to close due to unfinished backup operation");
- sqlite3_mutex_leave(db->mutex);
- return SQLITE_BUSY;
- }
- }
+
+ /* Convert the connection into a zombie and then close it.
+ */
+ db->magic = SQLITE_MAGIC_ZOMBIE;
+ sqlite3LeaveMutexAndCloseZombie(db);
+ return SQLITE_OK;
+}
+
+/*
+** Two variations on the public interface for closing a database
+** connection. The sqlite3_close() version returns SQLITE_BUSY and
+** leaves the connection option if there are unfinalized prepared
+** statements or unfinished sqlite3_backups. The sqlite3_close_v2()
+** version forces the connection to become a zombie if there are
+** unclosed resources, and arranges for deallocation when the last
+** prepare statement or sqlite3_backup closes.
+*/
+SQLITE_API int sqlite3_close(sqlite3 *db){ return sqlite3Close(db,0); }
+SQLITE_API int sqlite3_close_v2(sqlite3 *db){ return sqlite3Close(db,1); }
+
+
+/*
+** Close the mutex on database connection db.
+**
+** Furthermore, if database connection db is a zombie (meaning that there
+** has been a prior call to sqlite3_close(db) or sqlite3_close_v2(db)) and
+** every sqlite3_stmt has now been finalized and every sqlite3_backup has
+** finished, then free all resources.
+*/
+SQLITE_PRIVATE void sqlite3LeaveMutexAndCloseZombie(sqlite3 *db){
+ HashElem *i; /* Hash table iterator */
+ int j;
+
+ /* If there are outstanding sqlite3_stmt or sqlite3_backup objects
+ ** or if the connection has not yet been closed by sqlite3_close_v2(),
+ ** then just leave the mutex and return.
+ */
+ if( db->magic!=SQLITE_MAGIC_ZOMBIE || connectionIsBusy(db) ){
+ sqlite3_mutex_leave(db->mutex);
+ return;
+ }
+
+ /* If we reach this point, it means that the database connection has
+ ** closed all sqlite3_stmt and sqlite3_backup objects and has been
+ ** pased to sqlite3_close (meaning that it is a zombie). Therefore,
+ ** go ahead and free all resources.
+ */
/* Free any outstanding Savepoint structures. */
sqlite3CloseSavepoints(db);
/* Close all database connections */
@@ -114481,11 +112132,10 @@
assert( db->lookaside.nOut==0 ); /* Fails on a lookaside memory leak */
if( db->lookaside.bMalloced ){
sqlite3_free(db->lookaside.pStart);
}
sqlite3_free(db);
- return SQLITE_OK;
}
/*
** Rollback all database files. If tripCode is not SQLITE_OK, then
** any open cursors are invalidated ("tripped" - as in "tripping a circuit
@@ -115648,13 +113298,11 @@
if( nOpt==4 && memcmp("mode", zOpt, 4)==0 ){
static struct OpenMode aOpenMode[] = {
{ "ro", SQLITE_OPEN_READONLY },
{ "rw", SQLITE_OPEN_READWRITE },
{ "rwc", SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE },
- { "memory",
- SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
- | SQLITE_OPEN_MEMORY },
+ { "memory", SQLITE_OPEN_MEMORY },
{ 0, 0 }
};
mask = SQLITE_OPEN_READONLY | SQLITE_OPEN_READWRITE
| SQLITE_OPEN_CREATE | SQLITE_OPEN_MEMORY;
@@ -125936,14 +123584,13 @@
/* #include */
/*
** Implementation of a special SQL scalar function for testing tokenizers
** designed to be used in concert with the Tcl testing framework. This
-** function must be called with two arguments:
+** function must be called with two or more arguments:
**
-** SELECT (, );
-** SELECT (, );
+** SELECT (, ..., );
**
** where is the name passed as the second argument
** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer')
** concatenated with the string '_test' (e.g. 'fts3_tokenizer_test').
**
@@ -125976,31 +123623,31 @@
const char *zName;
int nName;
const char *zInput;
int nInput;
- const char *zArg = 0;
+ const char *azArg[64];
const char *zToken;
int nToken;
int iStart;
int iEnd;
int iPos;
+ int i;
Tcl_Obj *pRet;
- assert( argc==2 || argc==3 );
+ if( argc<2 ){
+ sqlite3_result_error(context, "insufficient arguments", -1);
+ return;
+ }
nName = sqlite3_value_bytes(argv[0]);
zName = (const char *)sqlite3_value_text(argv[0]);
nInput = sqlite3_value_bytes(argv[argc-1]);
zInput = (const char *)sqlite3_value_text(argv[argc-1]);
- if( argc==3 ){
- zArg = (const char *)sqlite3_value_text(argv[1]);
- }
-
pHash = (Fts3Hash *)sqlite3_user_data(context);
p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1);
if( !p ){
char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName);
@@ -126010,11 +123657,15 @@
}
pRet = Tcl_NewObj();
Tcl_IncrRefCount(pRet);
- if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){
+ for(i=1; ixCreate(argc-2, azArg, &pTokenizer) ){
zErr = "error in xCreate()";
goto finish;
}
pTokenizer->pModule = p;
if( sqlite3Fts3OpenTokenizer(pTokenizer, 0, zInput, nInput, &pCsr) ){
@@ -126194,14 +123845,11 @@
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0);
}
#ifdef SQLITE_TEST
if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0);
- }
- if( SQLITE_OK==rc ){
- rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0);
+ rc = sqlite3_create_function(db, zTest, -1, any, p, testFunc, 0, 0);
}
if( SQLITE_OK==rc ){
rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0);
}
#endif
@@ -126588,11 +124236,12 @@
** fts3SegReaderFirstDocid()
** fts3SegReaderNextDocid()
*/
struct Fts3SegReader {
int iIdx; /* Index within level, or 0x7FFFFFFF for PT */
- int bLookup; /* True for a lookup only */
+ u8 bLookup; /* True for a lookup only */
+ u8 rootOnly; /* True for a root-only reader */
sqlite3_int64 iStartBlock; /* Rowid of first leaf block to traverse */
sqlite3_int64 iLeafEndBlock; /* Rowid of final leaf block to traverse */
sqlite3_int64 iEndBlock; /* Rowid of final block in segment (or 0) */
sqlite3_int64 iCurrentBlock; /* Current leaf block (or 0) */
@@ -126622,11 +124271,11 @@
int nOffsetList; /* For descending pending seg-readers only */
sqlite3_int64 iDocid;
};
#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0)
-#define fts3SegReaderIsRootOnly(p) ((p)->aNode==(char *)&(p)[1])
+#define fts3SegReaderIsRootOnly(p) ((p)->rootOnly!=0)
/*
** An instance of this structure is used to create a segment b-tree in the
** database. The internal details of this type are only accessed by the
** following functions:
@@ -128033,18 +125682,19 @@
if( !pReader ){
return SQLITE_NOMEM;
}
memset(pReader, 0, sizeof(Fts3SegReader));
pReader->iIdx = iAge;
- pReader->bLookup = bLookup;
+ pReader->bLookup = bLookup!=0;
pReader->iStartBlock = iStartLeaf;
pReader->iLeafEndBlock = iEndLeaf;
pReader->iEndBlock = iEndBlock;
if( nExtra ){
/* The entire segment is stored in the root node. */
pReader->aNode = (char *)&pReader[1];
+ pReader->rootOnly = 1;
pReader->nNode = nRoot;
memcpy(pReader->aNode, zRoot, nRoot);
memset(&pReader->aNode[nRoot], 0, FTS3_NODE_PADDING);
}else{
pReader->iCurrentBlock = iStartLeaf-1;
@@ -133438,10 +131088,12 @@
typedef struct unicode_cursor unicode_cursor;
struct unicode_tokenizer {
sqlite3_tokenizer base;
int bRemoveDiacritic;
+ int nException;
+ int *aiException;
};
struct unicode_cursor {
sqlite3_tokenizer_cursor base;
const unsigned char *aInput; /* Input text being tokenized */
@@ -133450,10 +131102,125 @@
int iToken; /* Index of next token to be returned */
char *zToken; /* storage for current token */
int nAlloc; /* space allocated at zToken */
};
+
+/*
+** Destroy a tokenizer allocated by unicodeCreate().
+*/
+static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){
+ if( pTokenizer ){
+ unicode_tokenizer *p = (unicode_tokenizer *)pTokenizer;
+ sqlite3_free(p->aiException);
+ sqlite3_free(p);
+ }
+ return SQLITE_OK;
+}
+
+/*
+** As part of a tokenchars= or separators= option, the CREATE VIRTUAL TABLE
+** statement has specified that the tokenizer for this table shall consider
+** all characters in string zIn/nIn to be separators (if bAlnum==0) or
+** token characters (if bAlnum==1).
+**
+** For each codepoint in the zIn/nIn string, this function checks if the
+** sqlite3FtsUnicodeIsalnum() function already returns the desired result.
+** If so, no action is taken. Otherwise, the codepoint is added to the
+** unicode_tokenizer.aiException[] array. For the purposes of tokenization,
+** the return value of sqlite3FtsUnicodeIsalnum() is inverted for all
+** codepoints in the aiException[] array.
+**
+** If a standalone diacritic mark (one that sqlite3FtsUnicodeIsdiacritic()
+** identifies as a diacritic) occurs in the zIn/nIn string it is ignored.
+** It is not possible to change the behaviour of the tokenizer with respect
+** to these codepoints.
+*/
+static int unicodeAddExceptions(
+ unicode_tokenizer *p, /* Tokenizer to add exceptions to */
+ int bAlnum, /* Replace Isalnum() return value with this */
+ const char *zIn, /* Array of characters to make exceptions */
+ int nIn /* Length of z in bytes */
+){
+ const unsigned char *z = (const unsigned char *)zIn;
+ const unsigned char *zTerm = &z[nIn];
+ int iCode;
+ int nEntry = 0;
+
+ assert( bAlnum==0 || bAlnum==1 );
+
+ while( zaiException, (p->nException+nEntry)*sizeof(int));
+ if( aNew==0 ) return SQLITE_NOMEM;
+ nNew = p->nException;
+
+ z = (const unsigned char *)zIn;
+ while( zi; j--) aNew[j] = aNew[j-1];
+ aNew[i] = iCode;
+ nNew++;
+ }
+ }
+ p->aiException = aNew;
+ p->nException = nNew;
+ }
+
+ return SQLITE_OK;
+}
+
+/*
+** Return true if the p->aiException[] array contains the value iCode.
+*/
+static int unicodeIsException(unicode_tokenizer *p, int iCode){
+ if( p->nException>0 ){
+ int *a = p->aiException;
+ int iLo = 0;
+ int iHi = p->nException-1;
+
+ while( iHi>=iLo ){
+ int iTest = (iHi + iLo) / 2;
+ if( iCode==a[iTest] ){
+ return 1;
+ }else if( iCode>a[iTest] ){
+ iLo = iTest+1;
+ }else{
+ iHi = iTest-1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+** Return true if, for the purposes of tokenization, codepoint iCode is
+** considered a token character (not a separator).
+*/
+static int unicodeIsAlnum(unicode_tokenizer *p, int iCode){
+ assert( (sqlite3FtsUnicodeIsalnum(iCode) & 0xFFFFFFFE)==0 );
+ return sqlite3FtsUnicodeIsalnum(iCode) ^ unicodeIsException(p, iCode);
+}
+
/*
** Create a new tokenizer instance.
*/
static int unicodeCreate(
int nArg, /* Size of array argv[] */
@@ -133460,43 +131227,45 @@
const char * const *azArg, /* Tokenizer creation arguments */
sqlite3_tokenizer **pp /* OUT: New tokenizer handle */
){
unicode_tokenizer *pNew; /* New tokenizer object */
int i;
+ int rc = SQLITE_OK;
+
pNew = (unicode_tokenizer *) sqlite3_malloc(sizeof(unicode_tokenizer));
- if( pNew==NULL ){
- return SQLITE_NOMEM;
- }
+ if( pNew==NULL ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(unicode_tokenizer));
pNew->bRemoveDiacritic = 1;
- for(i=0; ibRemoveDiacritic = 1;
}
else if( n==19 && memcmp("remove_diacritics=0", z, 19)==0 ){
pNew->bRemoveDiacritic = 0;
}
+ else if( n>=11 && memcmp("tokenchars=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 1, &z[11], n-11);
+ }
+ else if( n>=11 && memcmp("separators=", z, 11)==0 ){
+ rc = unicodeAddExceptions(pNew, 0, &z[11], n-11);
+ }
else{
/* Unrecognized argument */
- return SQLITE_ERROR;
+ rc = SQLITE_ERROR;
}
}
- *pp = &pNew->base;
- return SQLITE_OK;
-}
-
-/*
-** Destroy a tokenizer allocated by unicodeCreate().
-*/
-static int unicodeDestroy(sqlite3_tokenizer *pTokenizer){
- sqlite3_free(pTokenizer);
- return SQLITE_OK;
+ if( rc!=SQLITE_OK ){
+ unicodeDestroy((sqlite3_tokenizer *)pNew);
+ pNew = 0;
+ }
+ *pp = (sqlite3_tokenizer *)pNew;
+ return rc;
}
/*
** Prepare to begin tokenizing a particular string. The input
** string to be tokenized is pInput[0..nBytes-1]. A cursor
@@ -133545,18 +131314,19 @@
/*
** Extract the next token from a tokenization cursor. The cursor must
** have been opened by a prior call to simpleOpen().
*/
static int unicodeNext(
- sqlite3_tokenizer_cursor *p, /* Cursor returned by simpleOpen */
+ sqlite3_tokenizer_cursor *pC, /* Cursor returned by simpleOpen */
const char **paToken, /* OUT: Token text */
int *pnToken, /* OUT: Number of bytes at *paToken */
int *piStart, /* OUT: Starting offset of token */
int *piEnd, /* OUT: Ending offset of token */
int *piPos /* OUT: Position integer of token */
){
- unicode_cursor *pCsr = (unicode_cursor *)p;
+ unicode_cursor *pCsr = (unicode_cursor *)pC;
+ unicode_tokenizer *p = ((unicode_tokenizer *)pCsr->base.pTokenizer);
int iCode;
char *zOut;
const unsigned char *z = &pCsr->aInput[pCsr->iOff];
const unsigned char *zStart = z;
const unsigned char *zEnd;
@@ -133565,11 +131335,11 @@
/* Scan past any delimiter characters before the start of the next token.
** Return SQLITE_DONE early if this takes us all the way to the end of
** the input. */
while( z=zTerm ) return SQLITE_DONE;
zOut = pCsr->zToken;
@@ -133585,21 +131355,19 @@
pCsr->nAlloc += 64;
}
/* Write the folded case of the last character read to the output */
zEnd = z;
- iOut = sqlite3FtsUnicodeFold(iCode,
- ((unicode_tokenizer *)pCsr->base.pTokenizer)->bRemoveDiacritic
- );
+ iOut = sqlite3FtsUnicodeFold(iCode, p->bRemoveDiacritic);
if( iOut ){
WRITE_UTF8(zOut, iOut);
}
/* If the cursor is not at EOF, read the next character */
if( z>=zTerm ) break;
READ_UTF8(z, zTerm, iCode);
- }while( sqlite3FtsUnicodeIsalnum(iCode)
+ }while( unicodeIsAlnum(p, iCode)
|| sqlite3FtsUnicodeIsdiacritic(iCode)
);
/* Set the output variables and return. */
pCsr->iOff = (z - pCsr->aInput);
@@ -133779,11 +131547,11 @@
iHi = iTest-1;
}
}
assert( aEntry[0]=aEntry[iRes] );
- return (c >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
+ return (((unsigned int)c) >= ((aEntry[iRes]>>10) + (aEntry[iRes]&0x3FF)));
}
return 1;
}
@@ -138059,11 +135827,11 @@
return SQLITE_DONE;
}
while( iStartaChar, iWhite, pCsr->nChar, c);
+ U16_NEXT(pCsr->aChar, iWhite, pCsr->nChar, c);
if( u_isspace(c) ){
iStart = iWhite;
}else{
break;
}