Index: third_party/sqlite/src/src/os_os2.c |
diff --git a/third_party/sqlite/src/src/os_os2.c b/third_party/sqlite/src/src/os_os2.c |
index 07e9e6dbae66ba215580589b0aa6df503356e69c..487ac3c3c8130b24d93cfc8c131c5ea8772514aa 100644 |
--- a/third_party/sqlite/src/src/os_os2.c |
+++ b/third_party/sqlite/src/src/os_os2.c |
@@ -11,8 +11,6 @@ |
****************************************************************************** |
** |
** This file contains code that is specific to OS/2. |
-** |
-** $Id: os_os2.c,v 1.63 2008/12/10 19:26:24 drh Exp $ |
*/ |
#include "sqliteInt.h" |
@@ -57,20 +55,35 @@ |
*/ |
#include "os_common.h" |
+/* 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. |
*/ |
-typedef struct os2File os2File; |
struct os2File { |
const sqlite3_io_methods *pMethod; /* Always the first entry */ |
HFILE h; /* Handle for accessing the file */ |
- char* pathToDel; /* Name of file to delete on close, NULL if not */ |
- unsigned char locktype; /* Type of lock currently held on this 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. |
@@ -80,21 +93,24 @@ struct os2File { |
** Close a file. |
*/ |
static int os2Close( sqlite3_file *id ){ |
- APIRET rc = NO_ERROR; |
- os2File *pFile; |
- if( id && (pFile = (os2File*)id) != 0 ){ |
- OSTRACE2( "CLOSE %d\n", pFile->h ); |
- rc = DosClose( pFile->h ); |
- pFile->locktype = NO_LOCK; |
- if( pFile->pathToDel != NULL ){ |
- rc = DosForceDelete( (PSZ)pFile->pathToDel ); |
- free( pFile->pathToDel ); |
- pFile->pathToDel = NULL; |
- } |
- id = 0; |
- OpenCounter( -1 ); |
- } |
+ 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; |
} |
@@ -114,7 +130,7 @@ static int os2Read( |
os2File *pFile = (os2File*)id; |
assert( id!=0 ); |
SimulateIOError( return SQLITE_IOERR_READ ); |
- OSTRACE3( "READ %d lock=%d\n", pFile->h, pFile->locktype ); |
+ OSTRACE(( "READ %d lock=%d\n", pFile->h, pFile->locktype )); |
if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ |
return SQLITE_IOERR; |
} |
@@ -147,7 +163,7 @@ static int os2Write( |
assert( id!=0 ); |
SimulateIOError( return SQLITE_IOERR_WRITE ); |
SimulateDiskfullError( return SQLITE_FULL ); |
- OSTRACE3( "WRITE %d lock=%d\n", pFile->h, pFile->locktype ); |
+ OSTRACE(( "WRITE %d lock=%d\n", pFile->h, pFile->locktype )); |
if( DosSetFilePtr(pFile->h, offset, FILE_BEGIN, &fileLocation) != NO_ERROR ){ |
return SQLITE_IOERR; |
} |
@@ -167,10 +183,21 @@ static int os2Write( |
** Truncate an open file to a specified size |
*/ |
static int os2Truncate( sqlite3_file *id, i64 nByte ){ |
- APIRET rc = NO_ERROR; |
+ APIRET rc; |
os2File *pFile = (os2File*)id; |
- OSTRACE3( "TRUNCATE %d %lld\n", pFile->h, nByte ); |
+ 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; |
} |
@@ -189,7 +216,7 @@ int sqlite3_fullsync_count = 0; |
*/ |
static int os2Sync( sqlite3_file *id, int flags ){ |
os2File *pFile = (os2File*)id; |
- OSTRACE3( "SYNC %d lock=%d\n", pFile->h, pFile->locktype ); |
+ OSTRACE(( "SYNC %d lock=%d\n", pFile->h, pFile->locktype )); |
#ifdef SQLITE_TEST |
if( flags & SQLITE_SYNC_FULL){ |
sqlite3_fullsync_count++; |
@@ -239,7 +266,7 @@ static int getReadLock( os2File *pFile ){ |
UnlockArea.lOffset = 0L; |
UnlockArea.lRange = 0L; |
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); |
- OSTRACE3( "GETREADLOCK %d res=%d\n", pFile->h, res ); |
+ OSTRACE(( "GETREADLOCK %d res=%d\n", pFile->h, res )); |
return res; |
} |
@@ -257,7 +284,7 @@ static int unlockReadLock( os2File *id ){ |
UnlockArea.lOffset = SHARED_FIRST; |
UnlockArea.lRange = SHARED_SIZE; |
res = DosSetFileLocks( id->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 1L ); |
- OSTRACE3( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res ); |
+ OSTRACE(( "UNLOCK-READLOCK file handle=%d res=%d?\n", id->h, res )); |
return res; |
} |
@@ -298,14 +325,14 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
memset(&LockArea, 0, sizeof(LockArea)); |
memset(&UnlockArea, 0, sizeof(UnlockArea)); |
assert( pFile!=0 ); |
- OSTRACE4( "LOCK %d %d was %d\n", pFile->h, locktype, pFile->locktype ); |
+ 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 ){ |
- OSTRACE3( "LOCK %d %d ok (already held)\n", pFile->h, locktype ); |
+ OSTRACE(( "LOCK %d %d ok (already held)\n", pFile->h, locktype )); |
return SQLITE_OK; |
} |
@@ -332,7 +359,7 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, 100L, 0L ); |
if( res == NO_ERROR ){ |
gotPendingLock = 1; |
- OSTRACE3( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res ); |
+ OSTRACE(( "LOCK %d pending lock boolean set. res=%d\n", pFile->h, res )); |
} |
} |
@@ -344,7 +371,7 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( res == NO_ERROR ){ |
newLocktype = SHARED_LOCK; |
} |
- OSTRACE3( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res ); |
+ OSTRACE(( "LOCK %d acquire shared lock. res=%d\n", pFile->h, res )); |
} |
/* Acquire a RESERVED lock |
@@ -359,7 +386,7 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( res == NO_ERROR ){ |
newLocktype = RESERVED_LOCK; |
} |
- OSTRACE3( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res ); |
+ OSTRACE(( "LOCK %d acquire reserved lock. res=%d\n", pFile->h, res )); |
} |
/* Acquire a PENDING lock |
@@ -367,7 +394,8 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ |
newLocktype = PENDING_LOCK; |
gotPendingLock = 0; |
- OSTRACE2( "LOCK %d acquire pending lock. pending lock boolean unset.\n", pFile->h ); |
+ OSTRACE(( "LOCK %d acquire pending lock. pending lock boolean unset.\n", |
+ pFile->h )); |
} |
/* Acquire an EXCLUSIVE lock |
@@ -375,7 +403,7 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( locktype==EXCLUSIVE_LOCK && res == NO_ERROR ){ |
assert( pFile->locktype>=SHARED_LOCK ); |
res = unlockReadLock(pFile); |
- OSTRACE2( "unreadlock = %d\n", res ); |
+ OSTRACE(( "unreadlock = %d\n", res )); |
LockArea.lOffset = SHARED_FIRST; |
LockArea.lRange = SHARED_SIZE; |
UnlockArea.lOffset = 0L; |
@@ -384,10 +412,10 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( res == NO_ERROR ){ |
newLocktype = EXCLUSIVE_LOCK; |
}else{ |
- OSTRACE2( "OS/2 error-code = %d\n", res ); |
+ OSTRACE(( "OS/2 error-code = %d\n", res )); |
getReadLock(pFile); |
} |
- OSTRACE3( "LOCK %d acquire exclusive lock. res=%d\n", pFile->h, res ); |
+ 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 |
@@ -400,7 +428,7 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
UnlockArea.lOffset = PENDING_BYTE; |
UnlockArea.lRange = 1L; |
r = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "LOCK %d unlocking pending/is shared. r=%d\n", pFile->h, r ); |
+ 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 |
@@ -409,12 +437,12 @@ static int os2Lock( sqlite3_file *id, int locktype ){ |
if( res == NO_ERROR ){ |
rc = SQLITE_OK; |
}else{ |
- OSTRACE4( "LOCK FAILED %d trying for %d but got %d\n", pFile->h, |
- locktype, newLocktype ); |
+ OSTRACE(( "LOCK FAILED %d trying for %d but got %d\n", pFile->h, |
+ locktype, newLocktype )); |
rc = SQLITE_BUSY; |
} |
pFile->locktype = newLocktype; |
- OSTRACE3( "LOCK %d now %d\n", pFile->h, pFile->locktype ); |
+ OSTRACE(( "LOCK %d now %d\n", pFile->h, pFile->locktype )); |
return rc; |
} |
@@ -429,7 +457,7 @@ static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){ |
assert( pFile!=0 ); |
if( pFile->locktype>=RESERVED_LOCK ){ |
r = 1; |
- OSTRACE3( "TEST WR-LOCK %d %d (local)\n", pFile->h, r ); |
+ OSTRACE(( "TEST WR-LOCK %d %d (local)\n", pFile->h, r )); |
}else{ |
FILELOCK LockArea, |
UnlockArea; |
@@ -441,7 +469,7 @@ static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){ |
UnlockArea.lOffset = 0L; |
UnlockArea.lRange = 0L; |
rc = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "TEST WR-LOCK %d lock reserved byte rc=%d\n", pFile->h, rc ); |
+ 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; |
@@ -449,10 +477,10 @@ static int os2CheckReservedLock( sqlite3_file *id, int *pOut ){ |
UnlockArea.lOffset = RESERVED_BYTE; |
UnlockArea.lRange = 1L; |
rcu = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu ); |
+ OSTRACE(( "TEST WR-LOCK %d unlock reserved byte r=%d\n", pFile->h, rcu )); |
} |
r = !(rc == NO_ERROR); |
- OSTRACE3( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r ); |
+ OSTRACE(( "TEST WR-LOCK %d %d (remote)\n", pFile->h, r )); |
} |
*pOut = r; |
return SQLITE_OK; |
@@ -480,7 +508,7 @@ static int os2Unlock( sqlite3_file *id, int locktype ){ |
memset(&UnlockArea, 0, sizeof(UnlockArea)); |
assert( pFile!=0 ); |
assert( locktype<=SHARED_LOCK ); |
- OSTRACE4( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype ); |
+ OSTRACE(( "UNLOCK %d to %d was %d\n", pFile->h, locktype, pFile->locktype )); |
type = pFile->locktype; |
if( type>=EXCLUSIVE_LOCK ){ |
LockArea.lOffset = 0L; |
@@ -488,11 +516,11 @@ static int os2Unlock( sqlite3_file *id, int locktype ){ |
UnlockArea.lOffset = SHARED_FIRST; |
UnlockArea.lRange = SHARED_SIZE; |
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "UNLOCK %d exclusive lock res=%d\n", pFile->h, res ); |
+ 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 */ |
- OSTRACE3( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype ); |
+ OSTRACE(( "UNLOCK %d to %d getReadLock() failed\n", pFile->h, locktype )); |
rc = SQLITE_IOERR_UNLOCK; |
} |
} |
@@ -502,11 +530,12 @@ static int os2Unlock( sqlite3_file *id, int locktype ){ |
UnlockArea.lOffset = RESERVED_BYTE; |
UnlockArea.lRange = 1L; |
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "UNLOCK %d reserved res=%d\n", pFile->h, res ); |
+ OSTRACE(( "UNLOCK %d reserved res=%d\n", pFile->h, res )); |
} |
if( locktype==NO_LOCK && type>=SHARED_LOCK ){ |
res = unlockReadLock(pFile); |
- OSTRACE5( "UNLOCK %d is %d want %d res=%d\n", pFile->h, type, locktype, res ); |
+ OSTRACE(( "UNLOCK %d is %d want %d res=%d\n", |
+ pFile->h, type, locktype, res )); |
} |
if( type>=PENDING_LOCK ){ |
LockArea.lOffset = 0L; |
@@ -514,10 +543,10 @@ static int os2Unlock( sqlite3_file *id, int locktype ){ |
UnlockArea.lOffset = PENDING_BYTE; |
UnlockArea.lRange = 1L; |
res = DosSetFileLocks( pFile->h, &UnlockArea, &LockArea, LOCK_TIMEOUT, 0L ); |
- OSTRACE3( "UNLOCK %d pending res=%d\n", pFile->h, res ); |
+ OSTRACE(( "UNLOCK %d pending res=%d\n", pFile->h, res )); |
} |
pFile->locktype = locktype; |
- OSTRACE3( "UNLOCK %d now %d\n", pFile->h, pFile->locktype ); |
+ OSTRACE(( "UNLOCK %d now %d\n", pFile->h, pFile->locktype )); |
return rc; |
} |
@@ -528,11 +557,26 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ |
switch( op ){ |
case SQLITE_FCNTL_LOCKSTATE: { |
*(int*)pArg = ((os2File*)id)->locktype; |
- OSTRACE3( "FCNTL_LOCKSTATE %d lock=%d\n", ((os2File*)id)->h, ((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_ERROR; |
+ return SQLITE_NOTFOUND; |
} |
/* |
@@ -546,6 +590,7 @@ static int os2FileControl(sqlite3_file *id, int op, void *pArg){ |
** same for both. |
*/ |
static int os2SectorSize(sqlite3_file *id){ |
+ UNUSED_PARAMETER(id); |
return SQLITE_DEFAULT_SECTOR_SIZE; |
} |
@@ -553,7 +598,8 @@ static int os2SectorSize(sqlite3_file *id){ |
** Return a vector of device characteristics. |
*/ |
static int os2DeviceCharacteristics(sqlite3_file *id){ |
- return 0; |
+ UNUSED_PARAMETER(id); |
+ return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN; |
} |
@@ -640,26 +686,682 @@ char *convertCpPathToUtf8( const char *in ){ |
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<<ofst)); |
+ assert( n>1 || mask==(1<<ofst) ); |
+ |
+ |
+ sqlite3_mutex_enter(pShmNode->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 = { |
- 1, /* iVersion */ |
- os2Close, |
- os2Read, |
- os2Write, |
- os2Truncate, |
- os2Sync, |
- os2FileSize, |
- os2Lock, |
- os2Unlock, |
- os2CheckReservedLock, |
- os2FileControl, |
- os2SectorSize, |
- os2DeviceCharacteristics |
+ 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. |
** |
@@ -671,51 +1373,58 @@ static const sqlite3_io_methods os2IoMethod = { |
** hold at pVfs->mxPathname characters. |
*/ |
static int getTempname(int nBuf, char *zBuf ){ |
- static const unsigned char zChars[] = |
+ static const char zChars[] = |
"abcdefghijklmnopqrstuvwxyz" |
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
"0123456789"; |
int i, j; |
- char zTempPathBuf[3]; |
- PSZ zTempPath = (PSZ)&zTempPathBuf; |
- if( sqlite3_temp_directory ){ |
- zTempPath = sqlite3_temp_directory; |
- }else{ |
- if( DosScanEnv( (PSZ)"TEMP", &zTempPath ) ){ |
- if( DosScanEnv( (PSZ)"TMP", &zTempPath ) ){ |
- if( DosScanEnv( (PSZ)"TMPDIR", &zTempPath ) ){ |
- ULONG ulDriveNum = 0, ulDriveMap = 0; |
- DosQueryCurrentDisk( &ulDriveNum, &ulDriveMap ); |
- sprintf( (char*)zTempPath, "%c:", (char)( 'A' + ulDriveNum - 1 ) ); |
- } |
- } |
- } |
+ 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] == ' ' ) ){ |
+ while( j > 0 && ( zTempPath[j-1] == '\\' || zTempPath[j-1] == '/' || |
+ zTempPath[j-1] == ' ' ) ){ |
j--; |
} |
zTempPath[j] = '\0'; |
- if( !sqlite3_temp_directory ){ |
- char *zTempPathUTF = convertCpPathToUtf8( zTempPath ); |
- sqlite3_snprintf( nBuf-30, zBuf, |
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPathUTF ); |
- free( zTempPathUTF ); |
- }else{ |
- sqlite3_snprintf( nBuf-30, zBuf, |
- "%s\\"SQLITE_TEMP_FILE_PREFIX, zTempPath ); |
- } |
- j = sqlite3Strlen30( zBuf ); |
+ |
+ /* 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] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; |
+ zBuf[j] = zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; |
} |
zBuf[j] = 0; |
- OSTRACE2( "TEMP FILENAME: %s\n", zBuf ); |
+ |
+ OSTRACE(( "TEMP FILENAME: %s\n", zBuf )); |
return SQLITE_OK; |
} |
@@ -734,8 +1443,8 @@ static int os2FullPathname( |
char *zRelativeCp = convertUtf8PathToCp( zRelative ); |
char zFullCp[CCHMAXPATH] = "\0"; |
char *zFullUTF; |
- APIRET rc = DosQueryPathInfo( zRelativeCp, FIL_QUERYFULLNAME, zFullCp, |
- CCHMAXPATH ); |
+ APIRET rc = DosQueryPathInfo( (PSZ)zRelativeCp, FIL_QUERYFULLNAME, |
+ zFullCp, CCHMAXPATH ); |
free( zRelativeCp ); |
zFullUTF = convertCpPathToUtf8( zFullCp ); |
sqlite3_snprintf( nFull, zFull, zFullUTF ); |
@@ -749,99 +1458,127 @@ static int os2FullPathname( |
*/ |
static int os2Open( |
sqlite3_vfs *pVfs, /* Not used */ |
- const char *zName, /* Name of the file */ |
+ 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 ulFileAttribute = FILE_NORMAL; |
ULONG ulOpenFlags = 0; |
ULONG ulOpenMode = 0; |
+ ULONG ulAction = 0; |
+ ULONG rc; |
os2File *pFile = (os2File*)id; |
- APIRET rc = NO_ERROR; |
- ULONG ulAction; |
+ const char *zUtf8Name = zName; |
char *zNameCp; |
- char zTmpname[CCHMAXPATH+1]; /* Buffer to hold name of temp file */ |
+ 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( !zName ){ |
- int rc = getTempname(CCHMAXPATH+1, zTmpname); |
+ if( !zUtf8Name ){ |
+ assert(isDelete && !isOpenJournal); |
+ rc = getTempname(CCHMAXPATH, zTmpname); |
if( rc!=SQLITE_OK ){ |
return rc; |
} |
- zName = zTmpname; |
+ zUtf8Name = zTmpname; |
} |
- |
- memset( pFile, 0, sizeof(*pFile) ); |
- |
- OSTRACE2( "OPEN want %d\n", flags ); |
- |
- if( flags & SQLITE_OPEN_READWRITE ){ |
+ if( isReadWrite ){ |
ulOpenMode |= OPEN_ACCESS_READWRITE; |
- OSTRACE1( "OPEN read/write\n" ); |
}else{ |
ulOpenMode |= OPEN_ACCESS_READONLY; |
- OSTRACE1( "OPEN read only\n" ); |
- } |
- |
- if( flags & SQLITE_OPEN_CREATE ){ |
- ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_CREATE_IF_NEW; |
- OSTRACE1( "OPEN open new/create\n" ); |
- }else{ |
- ulOpenFlags |= OPEN_ACTION_OPEN_IF_EXISTS | OPEN_ACTION_FAIL_IF_NEW; |
- OSTRACE1( "OPEN open existing\n" ); |
} |
- if( flags & SQLITE_OPEN_MAIN_DB ){ |
- ulOpenMode |= OPEN_SHARE_DENYNONE; |
- OSTRACE1( "OPEN share read/write\n" ); |
- }else{ |
- ulOpenMode |= OPEN_SHARE_DENYWRITE; |
- OSTRACE1( "OPEN share read only\n" ); |
- } |
+ /* 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; |
- if( flags & SQLITE_OPEN_DELETEONCLOSE ){ |
- char pathUtf8[CCHMAXPATH]; |
-#ifdef NDEBUG /* when debugging we want to make sure it is deleted */ |
- ulFileAttribute = FILE_HIDDEN; |
-#endif |
- os2FullPathname( pVfs, zName, CCHMAXPATH, pathUtf8 ); |
- pFile->pathToDel = convertUtf8PathToCp( pathUtf8 ); |
- OSTRACE1( "OPEN hidden/delete on close file attributes\n" ); |
+ /* 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{ |
- pFile->pathToDel = NULL; |
- OSTRACE1( "OPEN normal file attribute\n" ); |
+ /* Opens a file, only if it exists. */ |
+ ulOpenFlags |= OPEN_ACTION_FAIL_IF_NEW | OPEN_ACTION_OPEN_IF_EXISTS; |
} |
- /* always open in random access mode for possibly better speed */ |
- ulOpenMode |= OPEN_FLAGS_RANDOM; |
- ulOpenMode |= OPEN_FLAGS_FAIL_ON_ERROR; |
- ulOpenMode |= OPEN_FLAGS_NOINHERIT; |
- |
- zNameCp = convertUtf8PathToCp( zName ); |
+ zNameCp = convertUtf8PathToCp( zUtf8Name ); |
rc = DosOpen( (PSZ)zNameCp, |
&h, |
&ulAction, |
0L, |
- ulFileAttribute, |
+ FILE_NORMAL, |
ulOpenFlags, |
ulOpenMode, |
(PEAOP2)NULL ); |
free( zNameCp ); |
+ |
if( rc != NO_ERROR ){ |
- OSTRACE7( "OPEN Invalid handle rc=%d: zName=%s, ulAction=%#lx, ulAttr=%#lx, ulFlags=%#lx, ulMode=%#lx\n", |
- rc, zName, ulAction, ulFileAttribute, ulOpenFlags, ulOpenMode ); |
- if( pFile->pathToDel ) |
- free( pFile->pathToDel ); |
- pFile->pathToDel = NULL; |
- if( flags & SQLITE_OPEN_READWRITE ){ |
- OSTRACE2( "OPEN %d Invalid handle\n", ((flags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE) ); |
+ 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_READWRITE), |
+ ((flags|SQLITE_OPEN_READONLY)&~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)), |
pOutFlags ); |
}else{ |
return SQLITE_CANTOPEN; |
@@ -849,13 +1586,17 @@ static int os2Open( |
} |
if( pOutFlags ){ |
- *pOutFlags = flags & SQLITE_OPEN_READWRITE ? SQLITE_OPEN_READWRITE : SQLITE_OPEN_READONLY; |
+ *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); |
- OSTRACE3( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags ); |
+ OSTRACE(( "OPEN %d pOutFlags=%d\n", pFile->h, pOutFlags )); |
return SQLITE_OK; |
} |
@@ -867,13 +1608,16 @@ static int os2Delete( |
const char *zFilename, /* Name of file to delete */ |
int syncDir /* Not used on os2 */ |
){ |
- APIRET rc = NO_ERROR; |
- char *zFilenameCp = convertUtf8PathToCp( zFilename ); |
+ APIRET rc; |
+ char *zFilenameCp; |
SimulateIOError( return SQLITE_IOERR_DELETE ); |
+ zFilenameCp = convertUtf8PathToCp( zFilename ); |
rc = DosDelete( (PSZ)zFilenameCp ); |
free( zFilenameCp ); |
- OSTRACE2( "DELETE \"%s\"\n", zFilename ); |
- return rc == NO_ERROR ? SQLITE_OK : SQLITE_IOERR_DELETE; |
+ OSTRACE(( "DELETE \"%s\"\n", zFilename )); |
+ return (rc == NO_ERROR || |
+ rc == ERROR_FILE_NOT_FOUND || |
+ rc == ERROR_PATH_NOT_FOUND ) ? SQLITE_OK : SQLITE_IOERR_DELETE; |
} |
/* |
@@ -885,30 +1629,42 @@ static int os2Access( |
int flags, /* Type of test to make on this file */ |
int *pOut /* Write results here */ |
){ |
+ APIRET rc; |
FILESTATUS3 fsts3ConfigInfo; |
- APIRET rc = NO_ERROR; |
- char *zFilenameCp = convertUtf8PathToCp( zFilename ); |
+ char *zFilenameCp; |
- memset( &fsts3ConfigInfo, 0, sizeof(fsts3ConfigInfo) ); |
+ UNUSED_PARAMETER(pVfs); |
+ SimulateIOError( return SQLITE_IOERR_ACCESS; ); |
+ |
+ zFilenameCp = convertUtf8PathToCp( zFilename ); |
rc = DosQueryPathInfo( (PSZ)zFilenameCp, FIL_STANDARD, |
&fsts3ConfigInfo, sizeof(FILESTATUS3) ); |
free( zFilenameCp ); |
- OSTRACE4( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", |
- fsts3ConfigInfo.attrFile, flags, rc ); |
+ OSTRACE(( "ACCESS fsts3ConfigInfo.attrFile=%d flags=%d rc=%d\n", |
+ fsts3ConfigInfo.attrFile, flags, rc )); |
+ |
switch( flags ){ |
- case SQLITE_ACCESS_READ: |
case SQLITE_ACCESS_EXISTS: |
- rc = (rc == NO_ERROR); |
- OSTRACE3( "ACCESS %s access of read and exists rc=%d\n", zFilename, rc ); |
+ /* 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: |
- rc = (rc == NO_ERROR) && ( (fsts3ConfigInfo.attrFile & FILE_READONLY) == 0 ); |
- OSTRACE3( "ACCESS %s access of read/write rc=%d\n", zFilename, rc ); |
+ if( fsts3ConfigInfo.attrFile & FILE_READONLY ) |
+ rc = ERROR_ACCESS_DENIED; |
break; |
default: |
+ rc = ERROR_FILE_NOT_FOUND; |
assert( !"Invalid flags argument" ); |
} |
- *pOut = rc; |
+ |
+ *pOut = (rc == NO_ERROR); |
+ OSTRACE(( "ACCESS %s flags %d: rc=%d\n", zFilename, flags, *pOut )); |
+ |
return SQLITE_OK; |
} |
@@ -923,11 +1679,10 @@ static int os2Access( |
** within the shared library, and closing the shared library. |
*/ |
static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ |
- UCHAR loadErr[256]; |
HMODULE hmod; |
APIRET rc; |
char *zFilenameCp = convertUtf8PathToCp(zFilename); |
- rc = DosLoadModule((PSZ)loadErr, sizeof(loadErr), zFilenameCp, &hmod); |
+ rc = DosLoadModule(NULL, 0, (PSZ)zFilenameCp, &hmod); |
free(zFilenameCp); |
return rc != NO_ERROR ? 0 : (void*)hmod; |
} |
@@ -938,19 +1693,19 @@ static void *os2DlOpen(sqlite3_vfs *pVfs, const char *zFilename){ |
static void os2DlError(sqlite3_vfs *pVfs, int nBuf, char *zBufOut){ |
/* no-op */ |
} |
-static void *os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol){ |
+static void (*os2DlSym(sqlite3_vfs *pVfs, void *pHandle, const char *zSymbol))(void){ |
PFN pfn; |
APIRET rc; |
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, zSymbol, &pfn); |
+ 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, 255); |
- rc = DosQueryProcAddr((HMODULE)pHandle, 0L, _zSymbol, &pfn); |
+ strncat(_zSymbol, zSymbol, 254); |
+ rc = DosQueryProcAddr((HMODULE)pHandle, 0L, (PSZ)_zSymbol, &pfn); |
} |
- return rc != NO_ERROR ? 0 : (void*)pfn; |
+ return rc != NO_ERROR ? 0 : (void(*)(void))pfn; |
} |
static void os2DlClose(sqlite3_vfs *pVfs, void *pHandle){ |
DosFreeModule((HMODULE)pHandle); |
@@ -972,54 +1727,39 @@ static int os2Randomness(sqlite3_vfs *pVfs, int nBuf, char *zBuf ){ |
n = nBuf; |
memset(zBuf, 0, nBuf); |
#else |
- int sizeofULong = sizeof(ULONG); |
- if( (int)sizeof(DATETIME) <= nBuf - n ){ |
- DATETIME x; |
- DosGetDateTime(&x); |
- memcpy(&zBuf[n], &x, sizeof(x)); |
- n += sizeof(x); |
+ 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; |
} |
- if( sizeofULong <= nBuf - n ){ |
- PPIB ppib; |
- DosGetInfoBlocks(NULL, &ppib); |
- memcpy(&zBuf[n], &ppib->pib_ulpid, sizeofULong); |
- n += sizeofULong; |
+ /* 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); |
} |
- if( sizeofULong <= nBuf - n ){ |
- PTIB ptib; |
- DosGetInfoBlocks(&ptib, NULL); |
- memcpy(&zBuf[n], &ptib->tib_ptib2->tib2_ultid, sizeofULong); |
- n += sizeofULong; |
- } |
- |
- /* if we still haven't filled the buffer yet the following will */ |
- /* grab everything once instead of making several calls for a single item */ |
- if( sizeofULong <= nBuf - n ){ |
- ULONG ulSysInfo[QSV_MAX]; |
- DosQuerySysInfo(1L, QSV_MAX, ulSysInfo, sizeofULong * QSV_MAX); |
- |
- memcpy(&zBuf[n], &ulSysInfo[QSV_MS_COUNT - 1], sizeofULong); |
- n += sizeofULong; |
- |
- if( sizeofULong <= nBuf - n ){ |
- memcpy(&zBuf[n], &ulSysInfo[QSV_TIMER_INTERVAL - 1], sizeofULong); |
- n += sizeofULong; |
- } |
- if( sizeofULong <= nBuf - n ){ |
- memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_LOW - 1], sizeofULong); |
- n += sizeofULong; |
- } |
- if( sizeofULong <= nBuf - n ){ |
- memcpy(&zBuf[n], &ulSysInfo[QSV_TIME_HIGH - 1], sizeofULong); |
- n += sizeofULong; |
- } |
- if( sizeofULong <= nBuf - n ){ |
- memcpy(&zBuf[n], &ulSysInfo[QSV_TOTAVAILMEM - 1], sizeofULong); |
- n += sizeofULong; |
- } |
- } |
+ /* 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; |
@@ -1047,46 +1787,98 @@ int sqlite3_current_time = 0; |
#endif |
/* |
-** 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. |
+** 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. |
*/ |
-int os2CurrentTime( sqlite3_vfs *pVfs, double *prNow ){ |
- double now; |
- SHORT minute; /* needs to be able to cope with negative timezone offset */ |
- USHORT second, hour, |
- day, month, year; |
+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 ); |
- second = (USHORT)dt.seconds; |
- minute = (SHORT)dt.minutes + dt.timezone; |
- hour = (USHORT)dt.hours; |
- day = (USHORT)dt.day; |
- month = (USHORT)dt.month; |
- year = (USHORT)dt.year; |
+ |
+ 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 */ |
- now = day - 32076 + |
+ ** 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; |
- /* Add the fractional hours, mins and seconds */ |
- now += (hour + 12.0)/24.0; |
- now += minute/1440.0; |
- now += second/86400.0; |
- *prNow = now; |
+ /* 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 ){ |
- *prNow = sqlite3_current_time/86400.0 + 2440587.5; |
+ *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; |
} |
@@ -1095,7 +1887,7 @@ static int os2GetLastError(sqlite3_vfs *pVfs, int nBuf, char *zBuf){ |
*/ |
int sqlite3_os_init(void){ |
static sqlite3_vfs os2Vfs = { |
- 1, /* iVersion */ |
+ 3, /* iVersion */ |
sizeof(os2File), /* szOsFile */ |
CCHMAXPATH, /* mxPathname */ |
0, /* pNext */ |
@@ -1113,10 +1905,15 @@ int sqlite3_os_init(void){ |
os2Randomness, /* xRandomness */ |
os2Sleep, /* xSleep */ |
os2CurrentTime, /* xCurrentTime */ |
- os2GetLastError /* xGetLastError */ |
+ os2GetLastError, /* xGetLastError */ |
+ os2CurrentTimeInt64, /* xCurrentTimeInt64 */ |
+ 0, /* xSetSystemCall */ |
+ 0, /* xGetSystemCall */ |
+ 0 /* xNextSystemCall */ |
}; |
sqlite3_vfs_register(&os2Vfs, 1); |
initUconvObjects(); |
+/* sqlite3OSTrace = 1; */ |
return SQLITE_OK; |
} |
int sqlite3_os_end(void){ |