Index: doc/vfs-shm.txt ================================================================== --- doc/vfs-shm.txt +++ doc/vfs-shm.txt @@ -76,18 +76,28 @@ assertions on the behavior of the SQLite core which might be verified during testing using an instrumented lock manager. (5) No part of the wal-index will be read without holding either some kind of SHM lock or an EXCLUSIVE lock on the original database. + The original database is the file named in the 2nd parameter to + the xShmOpen method. + (6) A holder of a READ_FULL will never read any page of the database file that is contained anywhere in the wal-index. + (7) No part of the wal-index other than the header will be written nor - will the size of the wal-index grow without holding a WRITE. + will the size of the wal-index grow without holding a WRITE or + an EXCLUSIVE on the original database file. + (8) The wal-index header will not be written without holding one of - WRITE, CHECKPOINT, or RECOVER. -(9) A CHECKPOINT or RECOVER must be held in order to reset the last valid - frame counter in the header of the wal-index back to zero. + WRITE, CHECKPOINT, or RECOVER on the wal-index or an EXCLUSIVE on + the original database files. + +(9) A CHECKPOINT or RECOVER must be held on the wal-index, or an + EXCLUSIVE on the original database file, in order to reset the + last valid frame counter in the header of the wal-index back to zero. + (10) A WRITE can only increase the last valid frame pointer in the header. The SQLite core will only ever send requests for UNLOCK, READ, WRITE, CHECKPOINT, or RECOVER to the lock manager. The SQLite core will never request a READ_FULL or PENDING lock though the lock manager may deliver Index: src/os.c ================================================================== --- src/os.c +++ src/os.c @@ -96,10 +96,28 @@ return (xSectorSize ? xSectorSize(id) : SQLITE_DEFAULT_SECTOR_SIZE); } int sqlite3OsDeviceCharacteristics(sqlite3_file *id){ return id->pMethods->xDeviceCharacteristics(id); } +int sqlite3OsShmOpen(sqlite3_file *id){ + return id->pMethods->xShmOpen(id); +} +int sqlite3OsShmSize(sqlite3_file *id, int reqSize, int *pNewSize){ + return id->pMethods->xShmSize(id, reqSize, pNewSize); +} +int sqlite3OsShmGet(sqlite3_file *id, int reqSize, int *pSize, void **pp){ + return id->pMethods->xShmGet(id, reqSize, pSize, pp); +} +int sqlite3OsShmRelease(sqlite3_file *id){ + return id->pMethods->xShmRelease(id); +} +int sqlite3OsShmLock(sqlite3_file *id, int desiredLock, int *pGotLock){ + return id->pMethods->xShmLock(id, desiredLock, pGotLock); +} +int sqlite3OsShmClose(sqlite3_file *id, int deleteFlag){ + return id->pMethods->xShmClose(id, deleteFlag); +} /* ** The next group of routines are convenience wrappers around the ** VFS methods. */ Index: src/os.h ================================================================== --- src/os.h +++ src/os.h @@ -241,10 +241,16 @@ int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut); int sqlite3OsFileControl(sqlite3_file*,int,void*); #define SQLITE_FCNTL_DB_UNCHANGED 0xca093fa0 int sqlite3OsSectorSize(sqlite3_file *id); int sqlite3OsDeviceCharacteristics(sqlite3_file *id); +int sqlite3OsShmOpen(sqlite3_file *id); +int sqlite3OsShmSize(sqlite3_file *id, int, int*); +int sqlite3OsShmGet(sqlite3_file *id, int, int*, void**); +int sqlite3OsShmRelease(sqlite3_file *id); +int sqlite3OsShmLock(sqlite3_file *id, int, int*); +int sqlite3OsShmClose(sqlite3_file *id, int); /* ** Functions for accessing sqlite3_vfs methods */ int sqlite3OsOpen(sqlite3_vfs *, const char *, sqlite3_file*, int, int *); Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -173,10 +173,13 @@ ** Only set the lastErrno if the error code is a real error and not ** a normal expected return code of SQLITE_BUSY or SQLITE_OK */ #define IS_LOCK_ERROR(x) ((x != SQLITE_OK) && (x != SQLITE_BUSY)) +/* Forward reference */ +typedef struct unixShm unixShm; +typedef struct unixShmFile unixShmFile; /* ** Sometimes, after a file handle is closed by SQLite, the file descriptor ** cannot be closed immediately. In these cases, instances of the following ** structure are used to store the file descriptor while waiting for an @@ -203,10 +206,12 @@ unsigned char locktype; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from the last I/O error */ void *lockingContext; /* Locking style specific state */ UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ int fileFlags; /* Miscellanous flags */ + const char *zPath; /* Name of the file */ + unixShm *pShm; /* Shared memory segment information */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ @@ -3398,10 +3403,821 @@ static int unixDeviceCharacteristics(sqlite3_file *NotUsed){ UNUSED_PARAMETER(NotUsed); return 0; } +#ifndef SQLITE_OMIT_WAL + + +/* +** Object used to represent a single file opened and mmapped to provide +** shared memory. When multiple threads all reference the same +** log-summary, each thread has its own unixFile object, but they all +** point to a single instance of this object. In other words, each +** log-summary is opened only once per process. +** +** unixMutexHeld() must be true when creating or destroying +** this object or while reading or writing the following fields: +** +** nRef +** pNext +** +** The following fields are read-only after the object is created: +** +** fid +** zFilename +** +** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and +** unixMutexHeld() is true when reading or writing any other field +** in this structure. +** +** To avoid deadlocks, mutex and mutexBuf are always released in the +** reverse order that they are acquired. mutexBuf is always acquired +** first and released last. This invariant is check by asserting +** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or +** released. +*/ +struct unixShmFile { + struct unixFileId fid; /* Unique file identifier */ + sqlite3_mutex *mutex; /* Mutex to access this object */ + sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ + char *zFilename; /* Name of the mmapped file */ + int h; /* Open file descriptor */ + int szMap; /* Size of the mapping of file into memory */ + char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ + int nRef; /* Number of unixShm objects pointing to this */ + unixShm *pFirst; /* All unixShm objects pointing to this */ + unixShmFile *pNext; /* Next in list of all unixShmFile objects */ +#ifdef SQLITE_DEBUG + u8 exclMask; /* Mask of exclusive locks held */ + u8 sharedMask; /* Mask of shared locks held */ + u8 nextShmId; /* Next available unixShm.id value */ +#endif +}; + +/* +** A global array of all unixShmFile objects. +** +** The unixMutexHeld() must be true while reading or writing this list. +*/ +static unixShmFile *unixShmFileList = 0; + +/* +** Structure used internally by this VFS to record the state of an +** open shared memory connection. +** +** unixShm.pFile->mutex must be held while reading or writing the +** unixShm.pNext and unixShm.locks[] elements. +** +** The unixShm.pFile element is initialized when the object is created +** and is read-only thereafter. +*/ +struct unixShm { + unixShmFile *pFile; /* The underlying unixShmFile object */ + unixShm *pNext; /* Next unixShm with the same unixShmFile */ + u8 lockState; /* Current lock state */ + u8 hasMutex; /* True if holding the unixShmFile mutex */ + u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ + u8 sharedMask; /* Mask of shared locks held */ + u8 exclMask; /* Mask of exclusive locks held */ +#ifdef SQLITE_DEBUG + u8 id; /* Id of this connection with its unixShmFile */ +#endif +}; + +/* +** Size increment by which shared memory grows +*/ +#define SQLITE_UNIX_SHM_INCR 4096 + +/* +** Constants used for locking +*/ +#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */ +#define UNIX_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ +#define UNIX_SHM_A 0x10 /* Mask for region locks... */ +#define UNIX_SHM_B 0x20 +#define UNIX_SHM_C 0x40 +#define UNIX_SHM_D 0x80 + +#ifdef SQLITE_DEBUG +/* +** Return a pointer to a nul-terminated string in static memory that +** describes a locking mask. The string is of the form "MSABCD" with +** each character representing a lock. "M" for MUTEX, "S" for DMS, +** and "A" through "D" for the region locks. If a lock is held, the +** letter is shown. If the lock is not held, the letter is converted +** to ".". +** +** This routine is for debugging purposes only and does not appear +** in a production build. +*/ +static const char *unixShmLockString(u8 mask){ + static char zBuf[48]; + static int iBuf = 0; + char *z; + + z = &zBuf[iBuf]; + iBuf += 8; + if( iBuf>=sizeof(zBuf) ) iBuf = 0; + + z[0] = (mask & UNIX_SHM_DMS) ? 'S' : '.'; + z[1] = (mask & UNIX_SHM_A) ? 'A' : '.'; + z[2] = (mask & UNIX_SHM_B) ? 'B' : '.'; + z[3] = (mask & UNIX_SHM_C) ? 'C' : '.'; + z[4] = (mask & UNIX_SHM_D) ? 'D' : '.'; + z[5] = 0; + return z; +} +#endif /* SQLITE_DEBUG */ + +/* +** Apply posix advisory locks for all bytes identified in lockMask. +** +** lockMask might contain multiple bits but all bits are guaranteed +** to be contiguous. +** +** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking +** otherwise. +*/ +static int unixShmSystemLock( + unixShmFile *pFile, /* Apply locks to this open shared-memory segment */ + int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ + u8 lockMask /* Which bytes to lock or unlock */ +){ + struct flock f; /* The posix advisory locking structure */ + int lockOp; /* The opcode for fcntl() */ + int i; /* Offset into the locking byte range */ + int rc; /* Result code form fcntl() */ + u8 mask; /* Mask of bits in lockMask */ + + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); + + /* Initialize the locking parameters */ + memset(&f, 0, sizeof(f)); + f.l_type = lockType; + f.l_whence = SEEK_SET; + if( lockMask==UNIX_SHM_C && lockType!=F_UNLCK ){ + lockOp = F_SETLKW; + OSTRACE(("SHM-LOCK requesting blocking lock\n")); + }else{ + lockOp = F_SETLK; + } + + /* Find the first bit in lockMask that is set */ + for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} + assert( mask!=0 ); + f.l_start = i+UNIX_SHM_BASE; + f.l_len = 1; + + /* Extend the locking range for each additional bit that is set */ + mask <<= 1; + while( mask!=0 && (lockMask & mask)!=0 ){ + f.l_len++; + mask <<= 1; + } + + /* Verify that all bits set in lockMask are contiguous */ + assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); + + /* Acquire the system-level lock */ + rc = fcntl(pFile->h, lockOp, &f); + rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; + + /* Update the global lock state and do debug tracing */ +#ifdef SQLITE_DEBUG + OSTRACE(("SHM-LOCK ")); + if( rc==SQLITE_OK ){ + if( lockType==F_UNLCK ){ + OSTRACE(("unlock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask &= ~lockMask; + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock ok")); + pFile->exclMask &= ~lockMask; + pFile->sharedMask |= lockMask; + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock ok")); + pFile->exclMask |= lockMask; + pFile->sharedMask &= ~lockMask; + } + }else{ + if( lockType==F_UNLCK ){ + OSTRACE(("unlock failed")); + }else if( lockType==F_RDLCK ){ + OSTRACE(("read-lock failed")); + }else{ + assert( lockType==F_WRLCK ); + OSTRACE(("write-lock failed")); + } + } + OSTRACE((" - change requested %s - afterwards %s:%s\n", + unixShmLockString(lockMask), + unixShmLockString(pFile->sharedMask), + unixShmLockString(pFile->exclMask))); +#endif + + return rc; +} + +/* +** For connection p, unlock all of the locks identified by the unlockMask +** parameter. +*/ +static int unixShmUnlock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to be unlocked */ + u8 unlockMask /* Mask of locks to be unlocked */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allMask; /* Union of locks held by connections other than "p" */ + + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); + + /* Compute locks held by sibling connections */ + allMask = 0; + for(pX=pFile->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( (unlockMask & allMask)!=unlockMask ){ + rc = unixShmSystemLock(pFile, F_UNLCK, unlockMask & ~allMask); + }else{ + rc = SQLITE_OK; + } + + /* Undo the local locks */ + if( rc==SQLITE_OK ){ + p->exclMask &= ~unlockMask; + p->sharedMask &= ~unlockMask; + } + return rc; +} + +/* +** Get reader locks for connection p on all locks in the readMask parameter. +*/ +static int unixShmSharedLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the shared locks */ + u8 readMask /* Mask of shared locks to be acquired */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + u8 allShared; /* Union of locks held by connections other than "p" */ + + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); + + /* 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. + */ + allShared = 0; + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; + allShared |= pX->sharedMask; + } + + /* Get shared locks at the system level, if necessary */ + if( (~allShared) & readMask ){ + rc = unixShmSystemLock(pFile, F_RDLCK, readMask); + }else{ + rc = SQLITE_OK; + } + + /* Get the local shared locks */ + if( rc==SQLITE_OK ){ + p->sharedMask |= readMask; + } + return rc; +} + +/* +** For connection p, get an exclusive lock on all locks identified in +** the writeMask parameter. +*/ +static int unixShmExclusiveLock( + unixShmFile *pFile, /* The underlying shared-memory file */ + unixShm *p, /* The connection to get the exclusive locks */ + u8 writeMask /* Mask of exclusive locks to be acquired */ +){ + int rc; /* Result code */ + unixShm *pX; /* For looping over all sibling connections */ + + /* Access to the unixShmFile object is serialized by the caller */ + assert( sqlite3_mutex_held(pFile->mutex) ); + + /* Make sure no sibling connections hold locks that will block this + ** lock. If any do, return SQLITE_BUSY right away. + */ + for(pX=pFile->pFirst; pX; pX=pX->pNext){ + if( pX==p ) continue; + if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; + if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; + } + + /* Get the exclusive locks at the system level. Then if successful + ** also mark the local connection as being locked. + */ + rc = unixShmSystemLock(pFile, F_WRLCK, writeMask); + if( rc==SQLITE_OK ){ + p->sharedMask &= ~writeMask; + p->exclMask |= writeMask; + } + return rc; +} + +/* +** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0. +** +** This is not a VFS shared-memory method; it is a utility function called +** by VFS shared-memory methods. +*/ +static void unixShmPurge(void){ + unixShmFile **pp; + unixShmFile *p; + assert( unixMutexHeld() ); + pp = &unixShmFileList; + while( (p = *pp)!=0 ){ + if( p->nRef==0 ){ + if( p->mutex ) sqlite3_mutex_free(p->mutex); + if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); + if( p->h>=0 ) close(p->h); + *pp = p->pNext; + sqlite3_free(p); + }else{ + pp = &p->pNext; + } + } +} + +/* +** Open a shared-memory area. This particular implementation uses +** mmapped files. +** +** zName is a filename used to identify the shared-memory area. The +** implementation does not (and perhaps should not) use this name +** directly, but rather use it as a template for finding an appropriate +** name for the shared-memory storage. In this implementation, the +** string "-index" is appended to zName and used as the name of the +** mmapped file. +** +** When opening a new shared-memory file, if no other instances of that +** file are currently open, in this process or in other processes, then +** the file must be truncated to zero length or have its header cleared. +*/ +static int unixShmOpen( + sqlite3_file *fd /* The file descriptor of the associated database */ +){ + struct unixShm *p = 0; /* The connection to be opened */ + struct unixShmFile *pFile = 0; /* The underlying mmapped file */ + int rc; /* Result code */ + struct unixFileId fid; /* Unix file identifier */ + struct unixShmFile *pNew; /* Newly allocated pFile */ + struct stat sStat; /* Result from stat() an fstat() */ + struct unixFile *pDbFd; /* Underlying database file */ + int nPath; /* Size of pDbFd->zPath in bytes */ + + /* Allocate space for the new sqlite3_shm object. Also speculatively + ** allocate space for a new unixShmFile and filename. + */ + p = sqlite3_malloc( sizeof(*p) ); + if( p==0 ) return SQLITE_NOMEM; + memset(p, 0, sizeof(*p)); + pDbFd = (struct unixFile*)fd; + assert( pDbFd->pShm==0 ); + nPath = strlen(pDbFd->zPath); + pNew = sqlite3_malloc( sizeof(*pFile) + nPath + 15 ); + if( pNew==0 ){ + sqlite3_free(p); + return SQLITE_NOMEM; + } + memset(pNew, 0, sizeof(*pNew)); + pNew->zFilename = (char*)&pNew[1]; + sqlite3_snprintf(nPath+15, pNew->zFilename, "%s-wal-index", pDbFd->zPath); + + /* Look to see if there is an existing unixShmFile that can be used. + ** If no matching unixShmFile currently exists, create a new one. + */ + unixEnterMutex(); + rc = stat(pNew->zFilename, &sStat); + if( rc==0 ){ + memset(&fid, 0, sizeof(fid)); + fid.dev = sStat.st_dev; + fid.ino = sStat.st_ino; + for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){ + if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break; + } + } + if( pFile ){ + sqlite3_free(pNew); + }else{ + pFile = pNew; + pNew = 0; + pFile->h = -1; + pFile->pNext = unixShmFileList; + unixShmFileList = pFile; + + pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutex==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); + if( pFile->mutexBuf==0 ){ + rc = SQLITE_NOMEM; + goto shm_open_err; + } + + pFile->h = open(pFile->zFilename, O_RDWR|O_CREAT, 0664); + if( pFile->h<0 ){ + rc = SQLITE_CANTOPEN_BKPT; + goto shm_open_err; + } + + rc = fstat(pFile->h, &sStat); + if( rc ){ + rc = SQLITE_CANTOPEN_BKPT; + goto shm_open_err; + } + pFile->fid.dev = sStat.st_dev; + pFile->fid.ino = sStat.st_ino; + + /* Check to see if another process is holding the dead-man switch. + ** If not, truncate the file to zero length. + */ + if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){ + if( ftruncate(pFile->h, 0) ){ + rc = SQLITE_IOERR; + } + } + if( rc==SQLITE_OK ){ + rc = unixShmSystemLock(pFile, F_RDLCK, UNIX_SHM_DMS); + } + if( rc ) goto shm_open_err; + } + + /* Make the new connection a child of the unixShmFile */ + p->pFile = pFile; + p->pNext = pFile->pFirst; +#ifdef SQLITE_DEBUG + p->id = pFile->nextShmId++; +#endif + pFile->pFirst = p; + pFile->nRef++; + pDbFd->pShm = p; + unixLeaveMutex(); + return SQLITE_OK; + + /* Jump here on any error */ +shm_open_err: + unixShmPurge(); /* This call frees pFile if required */ + sqlite3_free(p); + sqlite3_free(pNew); + unixLeaveMutex(); + return rc; +} + +/* +** Close a connection to shared-memory. Delete the underlying +** storage if deleteFlag is true. +*/ +static int unixShmClose( + sqlite3_file *fd, /* The underlying database file */ + int deleteFlag /* Delete shared-memory if true */ +){ + unixShm *p; /* The connection to be closed */ + unixShmFile *pFile; /* The underlying shared-memory file */ + unixShm **pp; /* For looping over sibling connections */ + unixFile *pDbFd; /* The underlying database file */ + + pDbFd = (unixFile*)fd; + p = pDbFd->pShm; + if( p==0 ) return SQLITE_OK; + pFile = p->pFile; + + /* Verify that the connection being closed holds no locks */ + assert( p->exclMask==0 ); + assert( p->sharedMask==0 ); + + /* Remove connection p from the set of connections associated with pFile */ + sqlite3_mutex_enter(pFile->mutex); + for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} + *pp = p->pNext; + + /* Free the connection p */ + sqlite3_free(p); + pDbFd->pShm = 0; + sqlite3_mutex_leave(pFile->mutex); + + /* If pFile->nRef has reached 0, then close the underlying + ** shared-memory file, too */ + unixEnterMutex(); + assert( pFile->nRef>0 ); + pFile->nRef--; + if( pFile->nRef==0 ){ + if( deleteFlag ) unlink(pFile->zFilename); + unixShmPurge(); + } + unixLeaveMutex(); + + return SQLITE_OK; +} + +/* +** Query and/or changes the size of the underlying storage for +** a shared-memory segment. The reqSize parameter is the new size +** of the underlying storage, or -1 to do just a query. The size +** of the underlying storage (after resizing if resizing occurs) is +** written into pNewSize. +** +** This routine does not (necessarily) change the size of the mapping +** of the underlying storage into memory. Use xShmGet() to change +** the mapping size. +** +** The reqSize parameter is the minimum size requested. The implementation +** is free to expand the storage to some larger amount if it chooses. +*/ +static int unixShmSize( + sqlite3_file *fd, /* The open database file holding SHM */ + int reqSize, /* Requested size. -1 for query only */ + int *pNewSize /* Write new size here */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_OK; + struct stat sStat; + + /* On a query, this loop runs once. When reqSize>=0, the loop potentially + ** runs twice, except if the actual size is already greater than or equal + ** to the requested size, reqSize is set to -1 on the first iteration and + ** the loop only runs once. + */ + while( 1 ){ + if( fstat(pFile->h, &sStat)==0 ){ + *pNewSize = (int)sStat.st_size; + if( reqSize>=0 && reqSize<=(int)sStat.st_size ) break; + }else{ + *pNewSize = 0; + rc = SQLITE_IOERR; + break; + } + if( reqSize<0 ) break; + reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; + reqSize *= SQLITE_UNIX_SHM_INCR; + rc = ftruncate(pFile->h, reqSize); + reqSize = -1; + } + return rc; +} + + +/* +** Map the shared storage into memory. The minimum size of the +** mapping should be reqMapSize if reqMapSize is positive. If +** reqMapSize is zero or negative, the implementation can choose +** whatever mapping size is convenient. +** +** *ppBuf is made to point to the memory which is a mapping of the +** underlying storage. A mutex is acquired to prevent other threads +** from running while *ppBuf is in use in order to prevent other threads +** remapping *ppBuf out from under this thread. The unixShmRelease() +** call will release the mutex. However, if the lock state is CHECKPOINT, +** the mutex is not acquired because CHECKPOINT will never remap the +** buffer. RECOVER might remap, though, so CHECKPOINT will acquire +** the mutex if and when it promotes to RECOVER. +** +** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from +** being remapped also prevents more than one thread from being in +** RECOVER at a time. But, RECOVER sometimes wants to remap itself. +** To prevent RECOVER from losing its lock while remapping, the +** mutex is not released by unixShmRelease() when in RECOVER. +** +** *pNewMapSize is set to the size of the mapping. +** +** *ppBuf and *pNewMapSize might be NULL and zero if no space has +** yet been allocated to the underlying storage. +*/ +static int unixShmGet( + sqlite3_file *fd, /* Database file holding shared memory */ + int reqMapSize, /* Requested size of mapping. -1 means don't care */ + int *pNewMapSize, /* Write new size of mapping here */ + void **ppBuf /* Write mapping buffer origin here */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_OK; + + if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){ + assert( sqlite3_mutex_notheld(pFile->mutex) ); + sqlite3_mutex_enter(pFile->mutexBuf); + p->hasMutexBuf = 1; + } + sqlite3_mutex_enter(pFile->mutex); + if( pFile->szMap==0 || reqMapSize>pFile->szMap ){ + int actualSize; + if( unixShmSize(fd, -1, &actualSize)==SQLITE_OK + && reqMapSizepMMapBuf ){ + munmap(pFile->pMMapBuf, pFile->szMap); + } + pFile->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, + pFile->h, 0); + pFile->szMap = pFile->pMMapBuf ? reqMapSize : 0; + } + *pNewMapSize = pFile->szMap; + *ppBuf = pFile->pMMapBuf; + sqlite3_mutex_leave(pFile->mutex); + return rc; +} + +/* +** Release the lock held on the shared memory segment to that other +** threads are free to resize it if necessary. +** +** If the lock is not currently held, this routine is a harmless no-op. +** +** If the shared-memory object is in lock state RECOVER, then we do not +** really want to release the lock, so in that case too, this routine +** is a no-op. +*/ +static int unixShmRelease(sqlite3_file *fd){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + + if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){ + assert( sqlite3_mutex_notheld(p->pFile->mutex) ); + sqlite3_mutex_leave(p->pFile->mutexBuf); + p->hasMutexBuf = 0; + } + return SQLITE_OK; +} + +/* +** Symbolic names for LOCK states used for debugging. +*/ +#ifdef SQLITE_DEBUG +static const char *azLkName[] = { + "UNLOCK", + "READ", + "READ_FULL", + "WRITE", + "PENDING", + "CHECKPOINT", + "RECOVER" +}; +#endif + + +/* +** Change the lock state for a shared-memory segment. +*/ +static int unixShmLock( + sqlite3_file *fd, /* Database file holding the shared memory */ + int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ + int *pGotLock /* The lock you actually got */ +){ + unixFile *pDbFd = (unixFile*)fd; + unixShm *p = pDbFd->pShm; + unixShmFile *pFile = p->pFile; + int rc = SQLITE_PROTOCOL; + + /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never + ** directly requested; they are side effects from requesting + ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. + */ + assert( desiredLock==SQLITE_SHM_UNLOCK + || desiredLock==SQLITE_SHM_READ + || desiredLock==SQLITE_SHM_WRITE + || desiredLock==SQLITE_SHM_CHECKPOINT + || desiredLock==SQLITE_SHM_RECOVER ); + + /* Return directly if this is just a lock state query, or if + ** the connection is already in the desired locking state. + */ + if( desiredLock==p->lockState + || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) + ){ + OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n", + p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState])); + if( pGotLock ) *pGotLock = p->lockState; + return SQLITE_OK; + } + + OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n", + p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock])); + + if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ + assert( sqlite3_mutex_notheld(pFile->mutex) ); + sqlite3_mutex_enter(pFile->mutexBuf); + p->hasMutexBuf = 1; + } + sqlite3_mutex_enter(pFile->mutex); + switch( desiredLock ){ + case SQLITE_SHM_UNLOCK: { + assert( p->lockState!=SQLITE_SHM_RECOVER ); + unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D); + rc = SQLITE_OK; + p->lockState = SQLITE_SHM_UNLOCK; + break; + } + case SQLITE_SHM_READ: { + if( p->lockState==SQLITE_SHM_UNLOCK ){ + int nAttempt; + rc = SQLITE_BUSY; + assert( p->lockState==SQLITE_SHM_UNLOCK ); + for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B); + if( rc==SQLITE_BUSY ){ + rc = unixShmSharedLock(pFile, p, UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_READ_FULL; + } + }else{ + unixShmUnlock(pFile, p, UNIX_SHM_B); + p->lockState = SQLITE_SHM_READ; + } + } + }else{ + assert( p->lockState==SQLITE_SHM_WRITE + || p->lockState==SQLITE_SHM_RECOVER ); + rc = unixShmSharedLock(pFile, p, UNIX_SHM_A); + unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + p->lockState = SQLITE_SHM_READ; + } + break; + } + case SQLITE_SHM_WRITE: { + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL ); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_WRITE; + } + break; + } + case SQLITE_SHM_CHECKPOINT: { + assert( p->lockState==SQLITE_SHM_UNLOCK + || p->lockState==SQLITE_SHM_PENDING + ); + if( p->lockState==SQLITE_SHM_UNLOCK ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_PENDING; + } + } + if( p->lockState==SQLITE_SHM_PENDING ){ + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_CHECKPOINT; + } + } + break; + } + default: { + assert( desiredLock==SQLITE_SHM_RECOVER ); + assert( p->lockState==SQLITE_SHM_READ + || p->lockState==SQLITE_SHM_READ_FULL + ); + assert( sqlite3_mutex_held(pFile->mutexBuf) ); + rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C); + if( rc==SQLITE_OK ){ + p->lockState = SQLITE_SHM_RECOVER; + } + break; + } + } + sqlite3_mutex_leave(pFile->mutex); + OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n", + p->id, getpid(), azLkName[p->lockState])); + if( pGotLock ) *pGotLock = p->lockState; + return rc; +} + +#else +# define unixShmOpen 0 +# define unixShmSize 0 +# define unixShmGet 0 +# define unixShmRelease 0 +# define unixShmLock 0 +# define unixShmClose 0 +#endif /* #ifndef SQLITE_OMIT_WAL */ + /* ** Here ends the implementation of all sqlite3_file methods. ** ********************** End sqlite3_file Methods ******************************* ******************************************************************************/ @@ -3438,13 +4254,13 @@ ** methods CLOSE, LOCK, UNLOCK, CKRESLOCK. ** ** * An I/O method finder function called FINDER that returns a pointer ** to the METHOD object in the previous bullet. */ -#define IOMETHODS(FINDER, METHOD, CLOSE, LOCK, UNLOCK, CKLOCK) \ +#define IOMETHODS(FINDER, METHOD, VERSION, CLOSE, LOCK, UNLOCK, CKLOCK) \ static const sqlite3_io_methods METHOD = { \ - 1, /* iVersion */ \ + VERSION, /* iVersion */ \ CLOSE, /* xClose */ \ unixRead, /* xRead */ \ unixWrite, /* xWrite */ \ unixTruncate, /* xTruncate */ \ unixSync, /* xSync */ \ @@ -3452,11 +4268,17 @@ LOCK, /* xLock */ \ UNLOCK, /* xUnlock */ \ CKLOCK, /* xCheckReservedLock */ \ unixFileControl, /* xFileControl */ \ unixSectorSize, /* xSectorSize */ \ - unixDeviceCharacteristics /* xDeviceCapabilities */ \ + unixDeviceCharacteristics, /* xDeviceCapabilities */ \ + unixShmOpen, /* xShmOpen */ \ + unixShmSize, /* xShmSize */ \ + unixShmGet, /* xShmGet */ \ + unixShmRelease, /* xShmRelease */ \ + unixShmLock, /* xShmLock */ \ + unixShmClose /* xShmClose */ \ }; \ static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ return &METHOD; \ } \ @@ -3469,26 +4291,29 @@ ** are also created. */ IOMETHODS( posixIoFinder, /* Finder function name */ posixIoMethods, /* sqlite3_io_methods object name */ + 2, /* ShmOpen is enabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ unixUnlock, /* xUnlock method */ unixCheckReservedLock /* xCheckReservedLock method */ ) IOMETHODS( nolockIoFinder, /* Finder function name */ nolockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ nolockClose, /* xClose method */ nolockLock, /* xLock method */ nolockUnlock, /* xUnlock method */ nolockCheckReservedLock /* xCheckReservedLock method */ ) IOMETHODS( dotlockIoFinder, /* Finder function name */ dotlockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ dotlockClose, /* xClose method */ dotlockLock, /* xLock method */ dotlockUnlock, /* xUnlock method */ dotlockCheckReservedLock /* xCheckReservedLock method */ ) @@ -3495,10 +4320,11 @@ #if SQLITE_ENABLE_LOCKING_STYLE && !OS_VXWORKS IOMETHODS( flockIoFinder, /* Finder function name */ flockIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ flockClose, /* xClose method */ flockLock, /* xLock method */ flockUnlock, /* xUnlock method */ flockCheckReservedLock /* xCheckReservedLock method */ ) @@ -3506,10 +4332,11 @@ #if OS_VXWORKS IOMETHODS( semIoFinder, /* Finder function name */ semIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ semClose, /* xClose method */ semLock, /* xLock method */ semUnlock, /* xUnlock method */ semCheckReservedLock /* xCheckReservedLock method */ ) @@ -3517,10 +4344,11 @@ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( afpIoFinder, /* Finder function name */ afpIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ afpClose, /* xClose method */ afpLock, /* xLock method */ afpUnlock, /* xUnlock method */ afpCheckReservedLock /* xCheckReservedLock method */ ) @@ -3541,10 +4369,11 @@ static int proxyUnlock(sqlite3_file*, int); static int proxyCheckReservedLock(sqlite3_file*, int*); IOMETHODS( proxyIoFinder, /* Finder function name */ proxyIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ proxyClose, /* xClose method */ proxyLock, /* xLock method */ proxyUnlock, /* xUnlock method */ proxyCheckReservedLock /* xCheckReservedLock method */ ) @@ -3553,10 +4382,11 @@ /* nfs lockd on OSX 10.3+ doesn't clear write locks when a read lock is set */ #if defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE IOMETHODS( nfsIoFinder, /* Finder function name */ nfsIoMethods, /* sqlite3_io_methods object name */ + 1, /* ShmOpen is disabled */ unixClose, /* xClose method */ unixLock, /* xLock method */ nfsUnlock, /* xUnlock method */ unixCheckReservedLock /* xCheckReservedLock method */ ) @@ -3706,10 +4536,12 @@ OSTRACE3("OPEN %-3d %s\n", h, zFilename); pNew->h = h; pNew->dirfd = dirfd; SET_THREADID(pNew); pNew->fileFlags = 0; + assert( zFilename==0 || zFilename[0]=='/' ); /* Never a relative pathname */ + pNew->zPath = zFilename; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); if( pNew->pId==0 ){ noLock = 1; @@ -4573,816 +5405,10 @@ UNUSED_PARAMETER(NotUsed2); UNUSED_PARAMETER(NotUsed3); return 0; } -#ifndef SQLITE_OMIT_WAL - -/* Forward reference */ -typedef struct unixShm unixShm; -typedef struct unixShmFile unixShmFile; - -/* -** Object used to represent a single file opened and mmapped to provide -** shared memory. When multiple threads all reference the same -** log-summary, each thread has its own unixFile object, but they all -** point to a single instance of this object. In other words, each -** log-summary is opened only once per process. -** -** unixMutexHeld() must be true when creating or destroying -** this object or while reading or writing the following fields: -** -** nRef -** pNext -** -** The following fields are read-only after the object is created: -** -** fid -** zFilename -** -** Either unixShmFile.mutex must be held or unixShmFile.nRef==0 and -** unixMutexHeld() is true when reading or writing any other field -** in this structure. -** -** To avoid deadlocks, mutex and mutexBuf are always released in the -** reverse order that they are acquired. mutexBuf is always acquired -** first and released last. This invariant is check by asserting -** sqlite3_mutex_notheld() on mutex whenever mutexBuf is acquired or -** released. -*/ -struct unixShmFile { - struct unixFileId fid; /* Unique file identifier */ - sqlite3_mutex *mutex; /* Mutex to access this object */ - sqlite3_mutex *mutexBuf; /* Mutex to access zBuf[] */ - char *zFilename; /* Name of the file */ - int h; /* Open file descriptor */ - int szMap; /* Size of the mapping of file into memory */ - char *pMMapBuf; /* Where currently mmapped(). NULL if unmapped */ - int nRef; /* Number of unixShm objects pointing to this */ - unixShm *pFirst; /* All unixShm objects pointing to this */ - unixShmFile *pNext; /* Next in list of all unixShmFile objects */ -#ifdef SQLITE_DEBUG - u8 exclMask; /* Mask of exclusive locks held */ - u8 sharedMask; /* Mask of shared locks held */ - u8 nextShmId; /* Next available unixShm.id value */ -#endif -}; - -/* -** A global array of all unixShmFile objects. -** -** The unixMutexHeld() must be true while reading or writing this list. -*/ -static unixShmFile *unixShmFileList = 0; - -/* -** Structure used internally by this VFS to record the state of an -** open shared memory connection. -** -** unixShm.pFile->mutex must be held while reading or writing the -** unixShm.pNext and unixShm.locks[] elements. -** -** The unixShm.pFile element is initialized when the object is created -** and is read-only thereafter. -*/ -struct unixShm { - unixShmFile *pFile; /* The underlying unixShmFile object */ - unixShm *pNext; /* Next unixShm with the same unixShmFile */ - u8 lockState; /* Current lock state */ - u8 hasMutex; /* True if holding the unixShmFile mutex */ - u8 hasMutexBuf; /* True if holding pFile->mutexBuf */ - u8 sharedMask; /* Mask of shared locks held */ - u8 exclMask; /* Mask of exclusive locks held */ -#ifdef SQLITE_DEBUG - u8 id; /* Id of this connection with its unixShmFile */ -#endif -}; - -/* -** Size increment by which shared memory grows -*/ -#define SQLITE_UNIX_SHM_INCR 4096 - -/* -** Constants used for locking -*/ -#define UNIX_SHM_BASE 32 /* Byte offset of the first lock byte */ -#define UNIX_SHM_DMS 0x01 /* Mask for Dead-Man-Switch lock */ -#define UNIX_SHM_A 0x10 /* Mask for region locks... */ -#define UNIX_SHM_B 0x20 -#define UNIX_SHM_C 0x40 -#define UNIX_SHM_D 0x80 - -#ifdef SQLITE_DEBUG -/* -** Return a pointer to a nul-terminated string in static memory that -** describes a locking mask. The string is of the form "MSABCD" with -** each character representing a lock. "M" for MUTEX, "S" for DMS, -** and "A" through "D" for the region locks. If a lock is held, the -** letter is shown. If the lock is not held, the letter is converted -** to ".". -** -** This routine is for debugging purposes only and does not appear -** in a production build. -*/ -static const char *unixShmLockString(u8 mask){ - static char zBuf[48]; - static int iBuf = 0; - char *z; - - z = &zBuf[iBuf]; - iBuf += 8; - if( iBuf>=sizeof(zBuf) ) iBuf = 0; - - z[0] = (mask & UNIX_SHM_DMS) ? 'S' : '.'; - z[1] = (mask & UNIX_SHM_A) ? 'A' : '.'; - z[2] = (mask & UNIX_SHM_B) ? 'B' : '.'; - z[3] = (mask & UNIX_SHM_C) ? 'C' : '.'; - z[4] = (mask & UNIX_SHM_D) ? 'D' : '.'; - z[5] = 0; - return z; -} -#endif /* SQLITE_DEBUG */ - -/* -** Apply posix advisory locks for all bytes identified in lockMask. -** -** lockMask might contain multiple bits but all bits are guaranteed -** to be contiguous. -** -** Locks block if the mask is exactly UNIX_SHM_C and are non-blocking -** otherwise. -*/ -static int unixShmSystemLock( - unixShmFile *pFile, /* Apply locks to this open shared-memory segment */ - int lockType, /* F_UNLCK, F_RDLCK, or F_WRLCK */ - u8 lockMask /* Which bytes to lock or unlock */ -){ - struct flock f; /* The posix advisory locking structure */ - int lockOp; /* The opcode for fcntl() */ - int i; /* Offset into the locking byte range */ - int rc; /* Result code form fcntl() */ - u8 mask; /* Mask of bits in lockMask */ - - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) || pFile->nRef==0 ); - - /* Initialize the locking parameters */ - memset(&f, 0, sizeof(f)); - f.l_type = lockType; - f.l_whence = SEEK_SET; - if( lockMask==UNIX_SHM_C && lockType!=F_UNLCK ){ - lockOp = F_SETLKW; - OSTRACE(("SHM-LOCK requesting blocking lock\n")); - }else{ - lockOp = F_SETLK; - } - - /* Find the first bit in lockMask that is set */ - for(i=0, mask=0x01; mask!=0 && (lockMask&mask)==0; mask <<= 1, i++){} - assert( mask!=0 ); - f.l_start = i+UNIX_SHM_BASE; - f.l_len = 1; - - /* Extend the locking range for each additional bit that is set */ - mask <<= 1; - while( mask!=0 && (lockMask & mask)!=0 ){ - f.l_len++; - mask <<= 1; - } - - /* Verify that all bits set in lockMask are contiguous */ - assert( mask==0 || (lockMask & ~(mask | (mask-1)))==0 ); - - /* Acquire the system-level lock */ - rc = fcntl(pFile->h, lockOp, &f); - rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY; - - /* Update the global lock state and do debug tracing */ -#ifdef SQLITE_DEBUG - OSTRACE(("SHM-LOCK ")); - if( rc==SQLITE_OK ){ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask &= ~lockMask; - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock ok")); - pFile->exclMask &= ~lockMask; - pFile->sharedMask |= lockMask; - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock ok")); - pFile->exclMask |= lockMask; - pFile->sharedMask &= ~lockMask; - } - }else{ - if( lockType==F_UNLCK ){ - OSTRACE(("unlock failed")); - }else if( lockType==F_RDLCK ){ - OSTRACE(("read-lock failed")); - }else{ - assert( lockType==F_WRLCK ); - OSTRACE(("write-lock failed")); - } - } - OSTRACE((" - change requested %s - afterwards %s:%s\n", - unixShmLockString(lockMask), - unixShmLockString(pFile->sharedMask), - unixShmLockString(pFile->exclMask))); -#endif - - return rc; -} - -/* -** For connection p, unlock all of the locks identified by the unlockMask -** parameter. -*/ -static int unixShmUnlock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to be unlocked */ - u8 unlockMask /* Mask of locks to be unlocked */ -){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ - u8 allMask; /* Union of locks held by connections other than "p" */ - - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); - - /* Compute locks held by sibling connections */ - allMask = 0; - for(pX=pFile->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( (unlockMask & allMask)!=unlockMask ){ - rc = unixShmSystemLock(pFile, F_UNLCK, unlockMask & ~allMask); - }else{ - rc = SQLITE_OK; - } - - /* Undo the local locks */ - if( rc==SQLITE_OK ){ - p->exclMask &= ~unlockMask; - p->sharedMask &= ~unlockMask; - } - return rc; -} - -/* -** Get reader locks for connection p on all locks in the readMask parameter. -*/ -static int unixShmSharedLock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to get the shared locks */ - u8 readMask /* Mask of shared locks to be acquired */ -){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ - u8 allShared; /* Union of locks held by connections other than "p" */ - - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); - - /* 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. - */ - allShared = 0; - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & readMask)!=0 ) return SQLITE_BUSY; - allShared |= pX->sharedMask; - } - - /* Get shared locks at the system level, if necessary */ - if( (~allShared) & readMask ){ - rc = unixShmSystemLock(pFile, F_RDLCK, readMask); - }else{ - rc = SQLITE_OK; - } - - /* Get the local shared locks */ - if( rc==SQLITE_OK ){ - p->sharedMask |= readMask; - } - return rc; -} - -/* -** For connection p, get an exclusive lock on all locks identified in -** the writeMask parameter. -*/ -static int unixShmExclusiveLock( - unixShmFile *pFile, /* The underlying shared-memory file */ - unixShm *p, /* The connection to get the exclusive locks */ - u8 writeMask /* Mask of exclusive locks to be acquired */ -){ - int rc; /* Result code */ - unixShm *pX; /* For looping over all sibling connections */ - - /* Access to the unixShmFile object is serialized by the caller */ - assert( sqlite3_mutex_held(pFile->mutex) ); - - /* Make sure no sibling connections hold locks that will block this - ** lock. If any do, return SQLITE_BUSY right away. - */ - for(pX=pFile->pFirst; pX; pX=pX->pNext){ - if( pX==p ) continue; - if( (pX->exclMask & writeMask)!=0 ) return SQLITE_BUSY; - if( (pX->sharedMask & writeMask)!=0 ) return SQLITE_BUSY; - } - - /* Get the exclusive locks at the system level. Then if successful - ** also mark the local connection as being locked. - */ - rc = unixShmSystemLock(pFile, F_WRLCK, writeMask); - if( rc==SQLITE_OK ){ - p->sharedMask &= ~writeMask; - p->exclMask |= writeMask; - } - return rc; -} - -/* -** Purge the unixShmFileList list of all entries with unixShmFile.nRef==0. -** -** This is not a VFS shared-memory method; it is a utility function called -** by VFS shared-memory methods. -*/ -static void unixShmPurge(void){ - unixShmFile **pp; - unixShmFile *p; - assert( unixMutexHeld() ); - pp = &unixShmFileList; - while( (p = *pp)!=0 ){ - if( p->nRef==0 ){ - if( p->mutex ) sqlite3_mutex_free(p->mutex); - if( p->mutexBuf ) sqlite3_mutex_free(p->mutexBuf); - if( p->h>=0 ) close(p->h); - *pp = p->pNext; - sqlite3_free(p); - }else{ - pp = &p->pNext; - } - } -} - -/* -** Open a shared-memory area. This particular implementation uses -** mmapped files. -** -** zName is a filename used to identify the shared-memory area. The -** implementation does not (and perhaps should not) use this name -** directly, but rather use it as a template for finding an appropriate -** name for the shared-memory storage. In this implementation, the -** string "-index" is appended to zName and used as the name of the -** mmapped file. -** -** When opening a new shared-memory file, if no other instances of that -** file are currently open, in this process or in other processes, then -** the file must be truncated to zero length or have its header cleared. -*/ -static int unixShmOpen( - sqlite3_vfs *pVfs, /* The VFS */ - const char *zName, /* Name of the corresponding database file */ - sqlite3_shm **pShm /* Write the unixShm object created here */ -){ - struct unixShm *p = 0; /* The connection to be opened */ - struct unixShmFile *pFile = 0; /* The underlying mmapped file */ - int rc; /* Result code */ - struct unixFileId fid; /* Unix file identifier */ - struct unixShmFile *pNew; /* Newly allocated pFile */ - struct stat sStat; /* Result from stat() an fstat() */ - int nName; /* Size of zName in bytes */ - - /* Allocate space for the new sqlite3_shm object. Also speculatively - ** allocate space for a new unixShmFile and filename. - */ - p = sqlite3_malloc( sizeof(*p) ); - if( p==0 ) return SQLITE_NOMEM; - memset(p, 0, sizeof(*p)); - nName = strlen(zName); - pNew = sqlite3_malloc( sizeof(*pFile) + nName + 15 ); - if( pNew==0 ){ - sqlite3_free(p); - return SQLITE_NOMEM; - } - memset(pNew, 0, sizeof(*pNew)); - pNew->zFilename = (char*)&pNew[1]; - sqlite3_snprintf(nName+12, pNew->zFilename, "%s-wal-index", zName); - - /* Look to see if there is an existing unixShmFile that can be used. - ** If no matching unixShmFile currently exists, create a new one. - */ - unixEnterMutex(); - rc = stat(pNew->zFilename, &sStat); - if( rc==0 ){ - memset(&fid, 0, sizeof(fid)); - fid.dev = sStat.st_dev; - fid.ino = sStat.st_ino; - for(pFile = unixShmFileList; pFile; pFile=pFile->pNext){ - if( memcmp(&pFile->fid, &fid, sizeof(fid))==0 ) break; - } - } - if( pFile ){ - sqlite3_free(pNew); - }else{ - pFile = pNew; - pNew = 0; - pFile->h = -1; - pFile->pNext = unixShmFileList; - unixShmFileList = pFile; - - pFile->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pFile->mutex==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } - pFile->mutexBuf = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); - if( pFile->mutexBuf==0 ){ - rc = SQLITE_NOMEM; - goto shm_open_err; - } - - pFile->h = open(pFile->zFilename, O_RDWR|O_CREAT, 0664); - if( pFile->h<0 ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } - - rc = fstat(pFile->h, &sStat); - if( rc ){ - rc = SQLITE_CANTOPEN_BKPT; - goto shm_open_err; - } - pFile->fid.dev = sStat.st_dev; - pFile->fid.ino = sStat.st_ino; - - /* Check to see if another process is holding the dead-man switch. - ** If not, truncate the file to zero length. - */ - if( unixShmSystemLock(pFile, F_WRLCK, UNIX_SHM_DMS)==SQLITE_OK ){ - if( ftruncate(pFile->h, 0) ){ - rc = SQLITE_IOERR; - } - } - if( rc==SQLITE_OK ){ - rc = unixShmSystemLock(pFile, F_RDLCK, UNIX_SHM_DMS); - } - if( rc ) goto shm_open_err; - } - - /* Make the new connection a child of the unixShmFile */ - p->pFile = pFile; - p->pNext = pFile->pFirst; -#ifdef SQLITE_DEBUG - p->id = pFile->nextShmId++; -#endif - pFile->pFirst = p; - pFile->nRef++; - *pShm = (sqlite3_shm*)p; - unixLeaveMutex(); - return SQLITE_OK; - - /* Jump here on any error */ -shm_open_err: - unixShmPurge(); /* This call frees pFile if required */ - sqlite3_free(p); - sqlite3_free(pNew); - *pShm = 0; - unixLeaveMutex(); - return rc; -} - -/* -** Close a connection to shared-memory. Delete the underlying -** storage if deleteFlag is true. -*/ -static int unixShmClose( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* The shared-memory to be closed */ - int deleteFlag /* Delete after closing if true */ -){ - unixShm *p; /* The connection to be closed */ - unixShmFile *pFile; /* The underlying shared-memory file */ - unixShm **pp; /* For looping over sibling connections */ - - UNUSED_PARAMETER(pVfs); - if( pSharedMem==0 ) return SQLITE_OK; - p = (struct unixShm*)pSharedMem; - pFile = p->pFile; - - /* Verify that the connection being closed holds no locks */ - assert( p->exclMask==0 ); - assert( p->sharedMask==0 ); - - /* Remove connection p from the set of connections associated with pFile */ - sqlite3_mutex_enter(pFile->mutex); - for(pp=&pFile->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} - *pp = p->pNext; - - /* Free the connection p */ - sqlite3_free(p); - sqlite3_mutex_leave(pFile->mutex); - - /* If pFile->nRef has reached 0, then close the underlying - ** shared-memory file, too */ - unixEnterMutex(); - assert( pFile->nRef>0 ); - pFile->nRef--; - if( pFile->nRef==0 ){ - if( deleteFlag ) unlink(pFile->zFilename); - unixShmPurge(); - } - unixLeaveMutex(); - - return SQLITE_OK; -} - -/* -** Query and/or changes the size of the underlying storage for -** a shared-memory segment. The reqSize parameter is the new size -** of the underlying storage, or -1 to do just a query. The size -** of the underlying storage (after resizing if resizing occurs) is -** written into pNewSize. -** -** This routine does not (necessarily) change the size of the mapping -** of the underlying storage into memory. Use xShmGet() to change -** the mapping size. -** -** The reqSize parameter is the minimum size requested. The implementation -** is free to expand the storage to some larger amount if it chooses. -*/ -static int unixShmSize( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */ - int reqSize, /* Requested size. -1 for query only */ - int *pNewSize /* Write new size here */ -){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_OK; - struct stat sStat; - - UNUSED_PARAMETER(pVfs); - - if( reqSize>=0 ){ - reqSize = (reqSize + SQLITE_UNIX_SHM_INCR - 1)/SQLITE_UNIX_SHM_INCR; - reqSize *= SQLITE_UNIX_SHM_INCR; - rc = ftruncate(pFile->h, reqSize); - } - if( fstat(pFile->h, &sStat)==0 ){ - *pNewSize = (int)sStat.st_size; - }else{ - *pNewSize = 0; - rc = SQLITE_IOERR; - } - return rc; -} - - -/* -** Map the shared storage into memory. The minimum size of the -** mapping should be reqMapSize if reqMapSize is positive. If -** reqMapSize is zero or negative, the implementation can choose -** whatever mapping size is convenient. -** -** *ppBuf is made to point to the memory which is a mapping of the -** underlying storage. A mutex is acquired to prevent other threads -** from running while *ppBuf is in use in order to prevent other threads -** remapping *ppBuf out from under this thread. The unixShmRelease() -** call will release the mutex. However, if the lock state is CHECKPOINT, -** the mutex is not acquired because CHECKPOINT will never remap the -** buffer. RECOVER might remap, though, so CHECKPOINT will acquire -** the mutex if and when it promotes to RECOVER. -** -** RECOVER needs to be atomic. The same mutex that prevents *ppBuf from -** being remapped also prevents more than one thread from being in -** RECOVER at a time. But, RECOVER sometimes wants to remap itself. -** To prevent RECOVER from losing its lock while remapping, the -** mutex is not released by unixShmRelease() when in RECOVER. -** -** *pNewMapSize is set to the size of the mapping. -** -** *ppBuf and *pNewMapSize might be NULL and zero if no space has -** yet been allocated to the underlying storage. -*/ -static int unixShmGet( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer returned by unixShmOpen() */ - int reqMapSize, /* Requested size of mapping. -1 means don't care */ - int *pNewMapSize, /* Write new size of mapping here */ - void **ppBuf /* Write mapping buffer origin here */ -){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_OK; - - if( p->lockState!=SQLITE_SHM_CHECKPOINT && p->hasMutexBuf==0 ){ - assert( sqlite3_mutex_notheld(pFile->mutex) ); - sqlite3_mutex_enter(pFile->mutexBuf); - p->hasMutexBuf = 1; - } - sqlite3_mutex_enter(pFile->mutex); - if( pFile->szMap==0 || reqMapSize>pFile->szMap ){ - int actualSize; - if( unixShmSize(pVfs, pSharedMem, -1, &actualSize)==SQLITE_OK - && reqMapSizepMMapBuf ){ - munmap(pFile->pMMapBuf, pFile->szMap); - } - pFile->pMMapBuf = mmap(0, reqMapSize, PROT_READ|PROT_WRITE, MAP_SHARED, - pFile->h, 0); - pFile->szMap = pFile->pMMapBuf ? reqMapSize : 0; - } - *pNewMapSize = pFile->szMap; - *ppBuf = pFile->pMMapBuf; - sqlite3_mutex_leave(pFile->mutex); - return rc; -} - -/* -** Release the lock held on the shared memory segment to that other -** threads are free to resize it if necessary. -** -** If the lock is not currently held, this routine is a harmless no-op. -** -** If the shared-memory object is in lock state RECOVER, then we do not -** really want to release the lock, so in that case too, this routine -** is a no-op. -*/ -static int unixShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *pSharedMem){ - unixShm *p = (unixShm*)pSharedMem; - UNUSED_PARAMETER(pVfs); - if( p->hasMutexBuf && p->lockState!=SQLITE_SHM_RECOVER ){ - assert( sqlite3_mutex_notheld(p->pFile->mutex) ); - sqlite3_mutex_leave(p->pFile->mutexBuf); - p->hasMutexBuf = 0; - } - return SQLITE_OK; -} - -/* -** Symbolic names for LOCK states used for debugging. -*/ -#ifdef SQLITE_DEBUG -static const char *azLkName[] = { - "UNLOCK", - "READ", - "READ_FULL", - "WRITE", - "PENDING", - "CHECKPOINT", - "RECOVER" -}; -#endif - - -/* -** Change the lock state for a shared-memory segment. -*/ -static int unixShmLock( - sqlite3_vfs *pVfs, /* The VFS */ - sqlite3_shm *pSharedMem, /* Pointer from unixShmOpen() */ - int desiredLock, /* One of SQLITE_SHM_xxxxx locking states */ - int *pGotLock /* The lock you actually got */ -){ - unixShm *p = (unixShm*)pSharedMem; - unixShmFile *pFile = p->pFile; - int rc = SQLITE_PROTOCOL; - - UNUSED_PARAMETER(pVfs); - - /* Note that SQLITE_SHM_READ_FULL and SQLITE_SHM_PENDING are never - ** directly requested; they are side effects from requesting - ** SQLITE_SHM_READ and SQLITE_SHM_CHECKPOINT, respectively. - */ - assert( desiredLock==SQLITE_SHM_UNLOCK - || desiredLock==SQLITE_SHM_READ - || desiredLock==SQLITE_SHM_WRITE - || desiredLock==SQLITE_SHM_CHECKPOINT - || desiredLock==SQLITE_SHM_RECOVER ); - - /* Return directly if this is just a lock state query, or if - ** the connection is already in the desired locking state. - */ - if( desiredLock==p->lockState - || (desiredLock==SQLITE_SHM_READ && p->lockState==SQLITE_SHM_READ_FULL) - ){ - OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s and got %s\n", - p->id, getpid(), azLkName[desiredLock], azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; - return SQLITE_OK; - } - - OSTRACE(("SHM-LOCK shmid-%d, pid-%d request %s->%s\n", - p->id, getpid(), azLkName[p->lockState], azLkName[desiredLock])); - - if( desiredLock==SQLITE_SHM_RECOVER && !p->hasMutexBuf ){ - assert( sqlite3_mutex_notheld(pFile->mutex) ); - sqlite3_mutex_enter(pFile->mutexBuf); - p->hasMutexBuf = 1; - } - sqlite3_mutex_enter(pFile->mutex); - switch( desiredLock ){ - case SQLITE_SHM_UNLOCK: { - assert( p->lockState!=SQLITE_SHM_RECOVER ); - unixShmUnlock(pFile, p, UNIX_SHM_A|UNIX_SHM_B|UNIX_SHM_C|UNIX_SHM_D); - rc = SQLITE_OK; - p->lockState = SQLITE_SHM_UNLOCK; - break; - } - case SQLITE_SHM_READ: { - if( p->lockState==SQLITE_SHM_UNLOCK ){ - int nAttempt; - rc = SQLITE_BUSY; - assert( p->lockState==SQLITE_SHM_UNLOCK ); - for(nAttempt=0; nAttempt<5 && rc==SQLITE_BUSY; nAttempt++){ - rc = unixShmSharedLock(pFile, p, UNIX_SHM_A|UNIX_SHM_B); - if( rc==SQLITE_BUSY ){ - rc = unixShmSharedLock(pFile, p, UNIX_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_READ_FULL; - } - }else{ - unixShmUnlock(pFile, p, UNIX_SHM_B); - p->lockState = SQLITE_SHM_READ; - } - } - }else{ - assert( p->lockState==SQLITE_SHM_WRITE - || p->lockState==SQLITE_SHM_RECOVER ); - rc = unixShmSharedLock(pFile, p, UNIX_SHM_A); - unixShmUnlock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); - p->lockState = SQLITE_SHM_READ; - } - break; - } - case SQLITE_SHM_WRITE: { - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL ); - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C|UNIX_SHM_D); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_WRITE; - } - break; - } - case SQLITE_SHM_CHECKPOINT: { - assert( p->lockState==SQLITE_SHM_UNLOCK - || p->lockState==SQLITE_SHM_PENDING - ); - if( p->lockState==SQLITE_SHM_UNLOCK ){ - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_B|UNIX_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_PENDING; - } - } - if( p->lockState==SQLITE_SHM_PENDING ){ - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_A); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_CHECKPOINT; - } - } - break; - } - default: { - assert( desiredLock==SQLITE_SHM_RECOVER ); - assert( p->lockState==SQLITE_SHM_READ - || p->lockState==SQLITE_SHM_READ_FULL - ); - assert( sqlite3_mutex_held(pFile->mutexBuf) ); - rc = unixShmExclusiveLock(pFile, p, UNIX_SHM_C); - if( rc==SQLITE_OK ){ - p->lockState = SQLITE_SHM_RECOVER; - } - break; - } - } - sqlite3_mutex_leave(pFile->mutex); - OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %s\n", - p->id, getpid(), azLkName[p->lockState])); - if( pGotLock ) *pGotLock = p->lockState; - return rc; -} - -#else -# define unixShmOpen 0 -# define unixShmSize 0 -# define unixShmGet 0 -# define unixShmRelease 0 -# define unixShmLock 0 -# define unixShmClose 0 -#endif /* #ifndef SQLITE_OMIT_WAL */ /* ************************ End of sqlite3_vfs methods *************************** ******************************************************************************/ @@ -6598,16 +6624,10 @@ unixDlClose, /* xDlClose */ \ unixRandomness, /* xRandomness */ \ unixSleep, /* xSleep */ \ unixCurrentTime, /* xCurrentTime */ \ unixGetLastError, /* xGetLastError */ \ - unixShmOpen, /* xShmOpen */ \ - unixShmSize, /* xShmSize */ \ - unixShmGet, /* xShmGet */ \ - unixShmRelease, /* xShmRelease */ \ - unixShmLock, /* xShmLock */ \ - unixShmClose, /* xShmClose */ \ 0, /* xRename */ \ unixCurrentTimeInt64, /* xCurrentTimeInt64 */ \ } /* Index: src/pager.c ================================================================== --- src/pager.c +++ src/pager.c @@ -3075,11 +3075,11 @@ disable_simulated_io_errors(); sqlite3BeginBenignMalloc(); pPager->errCode = 0; pPager->exclusiveMode = 0; #ifndef SQLITE_OMIT_WAL - sqlite3WalClose(pPager->pWal, pPager->fd, + sqlite3WalClose(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, pTmp ); pPager->pWal = 0; #endif @@ -5876,11 +5876,11 @@ */ int sqlite3PagerCheckpoint(Pager *pPager){ int rc = SQLITE_OK; if( pPager->pWal ){ u8 *zBuf = (u8 *)pPager->pTmpSpace; - rc = sqlite3WalCheckpoint(pPager->pWal, pPager->fd, + rc = sqlite3WalCheckpoint(pPager->pWal, (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, zBuf, pPager->xBusyHandler, pPager->pBusyHandlerArg ); } @@ -5888,10 +5888,19 @@ } int sqlite3PagerWalCallback(Pager *pPager){ return sqlite3WalCallback(pPager->pWal); } + +/* +** Return true if the underlying VFS for the given pager supports the +** primitives necessary for write-ahead logging. +*/ +int sqlite3PagerWalSupported(Pager *pPager){ + const sqlite3_io_methods *pMethods = pPager->fd->pMethods; + return pMethods->iVersion>=2 && pMethods->xShmOpen!=0; +} /* ** Open a connection to the write-ahead log file for pager pPager. If ** the log connection is already open, this function is a no-op. ** @@ -5901,16 +5910,18 @@ int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen){ int rc = SQLITE_OK; /* Return code */ assert( pPager->state>=PAGER_SHARED ); if( !pPager->pWal ){ + if( !sqlite3PagerWalSupported(pPager) ) return SQLITE_CANTOPEN; /* Open the connection to the log file. If this operation fails, ** (e.g. due to malloc() failure), unlock the database file and ** return an error code. */ - rc = sqlite3WalOpen(pPager->pVfs, pPager->zFilename, &pPager->pWal); + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, + pPager->zFilename, &pPager->pWal); if( rc==SQLITE_OK ){ pPager->journalMode = PAGER_JOURNALMODE_WAL; } }else{ *pisOpen = 1; @@ -5942,22 +5953,23 @@ rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_SHARED); if( rc==SQLITE_OK ){ rc = pagerHasWAL(pPager, &logexists); } if( rc==SQLITE_OK && logexists ){ - rc = sqlite3WalOpen(pPager->pVfs, pPager->zFilename, &pPager->pWal); + rc = sqlite3WalOpen(pPager->pVfs, pPager->fd, + pPager->zFilename, &pPager->pWal); } } /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on ** the database file, the log and log-summary files will be deleted. */ if( rc==SQLITE_OK && pPager->pWal ){ rc = sqlite3OsLock(pPager->fd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ - rc = sqlite3WalClose(pPager->pWal, pPager->fd, - (pPager->noSync ? 0 : pPager->sync_flags), + rc = sqlite3WalClose(pPager->pWal, + (pPager->noSync ? 0 : pPager->sync_flags), pPager->pageSize, (u8*)pPager->pTmpSpace ); pPager->pWal = 0; }else{ /* If we cannot get an EXCLUSIVE lock, downgrade the PENDING lock Index: src/pager.h ================================================================== --- src/pager.h +++ src/pager.h @@ -133,10 +133,11 @@ int sqlite3PagerOpenSavepoint(Pager *pPager, int n); int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerCheckpoint(Pager *pPager); +int sqlite3PagerWalSupported(Pager *pPager); int sqlite3PagerWalCallback(Pager *pPager); int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen); int sqlite3PagerCloseWal(Pager *pPager); /* Functions used to query pager state and configuration. */ Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -651,10 +651,18 @@ int (*xUnlock)(sqlite3_file*, int); int (*xCheckReservedLock)(sqlite3_file*, int *pResOut); int (*xFileControl)(sqlite3_file*, int op, void *pArg); int (*xSectorSize)(sqlite3_file*); int (*xDeviceCharacteristics)(sqlite3_file*); + /* Methods above are valid for version 1 */ + int (*xShmOpen)(sqlite3_file*); + int (*xShmSize)(sqlite3_file*, int reqSize, int *pNewSize); + int (*xShmGet)(sqlite3_file*, int reqSize, int *pSize, void**); + int (*xShmRelease)(sqlite3_file*); + int (*xShmLock)(sqlite3_file*, int desiredLock, int *gotLock); + int (*xShmClose)(sqlite3_file*, int deleteFlag); + /* Methods above are valid for version 2 */ /* Additional methods may be added in future releases */ }; /* ** CAPI3REF: Standard File Control Opcodes @@ -816,11 +824,10 @@ ** least the number of microseconds given. The xCurrentTime() ** method returns a Julian Day Number for the current date and time. ** */ typedef struct sqlite3_vfs sqlite3_vfs; -typedef struct sqlite3_shm sqlite3_shm; struct sqlite3_vfs { int iVersion; /* Structure version number (currently 2) */ int szOsFile; /* Size of subclassed sqlite3_file */ int mxPathname; /* Maximum file pathname length */ sqlite3_vfs *pNext; /* Next registered VFS */ @@ -841,16 +848,10 @@ int (*xGetLastError)(sqlite3_vfs*, int, char *); /* ** The methods above are in version 1 of the sqlite_vfs object ** definition. Those that follow are added in version 2 or later */ - int (*xShmOpen)(sqlite3_vfs*, const char *zName, sqlite3_shm**); - int (*xShmSize)(sqlite3_vfs*, sqlite3_shm*, int reqSize, int *pNewSize); - int (*xShmGet)(sqlite3_vfs*, sqlite3_shm*, int reqSize, int *pSize, void**); - int (*xShmRelease)(sqlite3_vfs*, sqlite3_shm*); - int (*xShmLock)(sqlite3_vfs*, sqlite3_shm*, int desiredLock, int *gotLock); - int (*xShmClose)(sqlite3_vfs*, sqlite3_shm*, int deleteFlag); int (*xRename)(sqlite3_vfs*, const char *zOld, const char *zNew, int dirSync); int (*xCurrentTimeInt64)(sqlite3_vfs*, sqlite3_int64*); /* ** The methods above are in versions 1 and 2 of the sqlite_vfs object. ** New fields may be appended in figure versions. The iVersion Index: src/test6.c ================================================================== --- src/test6.c +++ src/test6.c @@ -517,13 +517,36 @@ return g.iSectorSize; } static int cfDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceCharacteristics; } + +/* +** Pass-throughs for WAL support. +*/ +static int cfShmOpen(sqlite3_file *pFile){ + return sqlite3OsShmOpen(((CrashFile*)pFile)->pRealFile); +} +static int cfShmSize(sqlite3_file *pFile, int reqSize, int *pNew){ + return sqlite3OsShmSize(((CrashFile*)pFile)->pRealFile, reqSize, pNew); +} +static int cfShmGet(sqlite3_file *pFile, int reqSize, int *pSize, void **pp){ + return sqlite3OsShmGet(((CrashFile*)pFile)->pRealFile, reqSize, pSize, pp); +} +static int cfShmRelease(sqlite3_file *pFile){ + return sqlite3OsShmRelease(((CrashFile*)pFile)->pRealFile); +} +static int cfShmLock(sqlite3_file *pFile, int desired, int *pGot){ + return sqlite3OsShmLock(((CrashFile*)pFile)->pRealFile, desired, pGot); +} +static int cfShmClose(sqlite3_file *pFile, int delFlag){ + return sqlite3OsShmClose(((CrashFile*)pFile)->pRealFile, delFlag); +} + static const sqlite3_io_methods CrashFileVtab = { - 1, /* iVersion */ + 2, /* iVersion */ cfClose, /* xClose */ cfRead, /* xRead */ cfWrite, /* xWrite */ cfTruncate, /* xTruncate */ cfSync, /* xSync */ @@ -531,11 +554,17 @@ cfLock, /* xLock */ cfUnlock, /* xUnlock */ cfCheckReservedLock, /* xCheckReservedLock */ cfFileControl, /* xFileControl */ cfSectorSize, /* xSectorSize */ - cfDeviceCharacteristics /* xDeviceCharacteristics */ + cfDeviceCharacteristics, /* xDeviceCharacteristics */ + cfShmOpen, /* xShmOpen */ + cfShmSize, /* xShmSize */ + cfShmGet, /* xShmGet */ + cfShmRelease, /* xShmRelease */ + cfShmLock, /* xShmLock */ + cfShmClose /* xShmClose */ }; /* ** Application data for the crash VFS */ @@ -655,37 +684,10 @@ } static int cfCurrentTime(sqlite3_vfs *pCfVfs, double *pTimeOut){ sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; return pVfs->xCurrentTime(pVfs, pTimeOut); } -static int cfShmOpen(sqlite3_vfs *pCfVfs, const char *zName, sqlite3_shm **pp){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmOpen(pVfs, zName, pp); -} -static int cfShmSize(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int reqSize, int *pNew){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmSize(pVfs, p, reqSize, pNew); -} -static int cfShmGet(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int reqSize, int *pSize, void **pp){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmGet(pVfs, p, reqSize, pSize, pp); -} -static int cfShmRelease(sqlite3_vfs *pCfVfs, sqlite3_shm *p){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmRelease(pVfs, p); -} -static int cfShmLock(sqlite3_vfs *pCfVfs, sqlite3_shm *p, - int desiredLock, int *gotLock){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmLock(pVfs, p, desiredLock, gotLock); -} -static int cfShmClose(sqlite3_vfs *pCfVfs, sqlite3_shm *p, int delFlag){ - sqlite3_vfs *pVfs = (sqlite3_vfs *)pCfVfs->pAppData; - return pVfs->xShmClose(pVfs, p, delFlag); -} static int processDevSymArgs( Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[], @@ -808,16 +810,10 @@ cfDlClose, /* xDlClose */ cfRandomness, /* xRandomness */ cfSleep, /* xSleep */ cfCurrentTime, /* xCurrentTime */ 0, /* xGetlastError */ - cfShmOpen, /* xShmOpen */ - cfShmSize, /* xShmSize */ - cfShmGet, /* xShmGet */ - cfShmRelease, /* xShmRelease */ - cfShmLock, /* xShmLock */ - cfShmClose, /* xShmClose */ 0, /* xRename */ 0, /* xCurrentTimeInt64 */ }; if( objc!=2 ){ @@ -836,15 +832,10 @@ if( crashVfs.pAppData==0 ){ sqlite3_vfs *pOriginalVfs = sqlite3_vfs_find(0); crashVfs.mxPathname = pOriginalVfs->mxPathname; crashVfs.pAppData = (void *)pOriginalVfs; crashVfs.szOsFile = sizeof(CrashFile) + pOriginalVfs->szOsFile; - if( pOriginalVfs->iVersion<2 || pOriginalVfs->xShmOpen==0 ){ - crashVfs.xShmOpen = 0; - }else{ - crashVfs.xShmOpen = cfShmOpen; - } sqlite3_vfs_register(&crashVfs, 0); }else{ crashVfs.pAppData = 0; sqlite3_vfs_unregister(&crashVfs); } Index: src/test_devsym.c ================================================================== --- src/test_devsym.c +++ src/test_devsym.c @@ -48,10 +48,16 @@ static int devsymUnlock(sqlite3_file*, int); static int devsymCheckReservedLock(sqlite3_file*, int *); static int devsymFileControl(sqlite3_file*, int op, void *pArg); static int devsymSectorSize(sqlite3_file*); static int devsymDeviceCharacteristics(sqlite3_file*); +static int devsymShmOpen(sqlite3_file*); +static int devsymShmSize(sqlite3_file*,int,int*); +static int devsymShmGet(sqlite3_file*,int,int*,void**); +static int devsymShmRelease(sqlite3_file*); +static int devsymShmLock(sqlite3_file*,int,int*); +static int devsymShmClose(sqlite3_file*,int); /* ** Method declarations for devsym_vfs. */ static int devsymOpen(sqlite3_vfs*, const char *, sqlite3_file*, int , int *); @@ -66,17 +72,10 @@ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int devsymRandomness(sqlite3_vfs*, int nByte, char *zOut); static int devsymSleep(sqlite3_vfs*, int microseconds); static int devsymCurrentTime(sqlite3_vfs*, double*); -static int devsymShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **); -static int devsymShmSize(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int devsymShmGet(sqlite3_vfs*, sqlite3_shm *, int , int *, void **); -static int devsymShmRelease(sqlite3_vfs*, sqlite3_shm *); -static int devsymShmLock(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int devsymShmClose(sqlite3_vfs*, sqlite3_shm *, int); - static sqlite3_vfs devsym_vfs = { 2, /* iVersion */ sizeof(devsym_file), /* szOsFile */ DEVSYM_MAX_PATHNAME, /* mxPathname */ 0, /* pNext */ @@ -99,22 +98,16 @@ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ devsymRandomness, /* xRandomness */ devsymSleep, /* xSleep */ devsymCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ - devsymShmOpen, - devsymShmSize, - devsymShmGet, - devsymShmRelease, - devsymShmLock, - devsymShmClose, - 0, - 0, + 0, /* xRename */ + 0 /* xCurrentTimeInt64 */ }; static sqlite3_io_methods devsym_io_methods = { - 1, /* iVersion */ + 2, /* iVersion */ devsymClose, /* xClose */ devsymRead, /* xRead */ devsymWrite, /* xWrite */ devsymTruncate, /* xTruncate */ devsymSync, /* xSync */ @@ -122,11 +115,17 @@ devsymLock, /* xLock */ devsymUnlock, /* xUnlock */ devsymCheckReservedLock, /* xCheckReservedLock */ devsymFileControl, /* xFileControl */ devsymSectorSize, /* xSectorSize */ - devsymDeviceCharacteristics /* xDeviceCharacteristics */ + devsymDeviceCharacteristics, /* xDeviceCharacteristics */ + devsymShmOpen, /* xShmOpen */ + devsymShmSize, /* xShmSize */ + devsymShmGet, /* xShmGet */ + devsymShmRelease, /* xShmRelease */ + devsymShmLock, /* xShmLock */ + devsymShmClose /* xShmClose */ }; struct DevsymGlobal { sqlite3_vfs *pVfs; int iDeviceChar; @@ -235,10 +234,40 @@ ** Return the device characteristic flags supported by an devsym-file. */ static int devsymDeviceCharacteristics(sqlite3_file *pFile){ return g.iDeviceChar; } + +/* +** Shared-memory methods are all pass-thrus. +*/ +static int devsymShmOpen(sqlite3_file *pFile){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmOpen(p->pReal); +} +static int devsymShmSize(sqlite3_file *pFile, int reqSize, int *pSize){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmSize(p->pReal, reqSize, pSize); +} +static int devsymShmGet(sqlite3_file *pFile, int reqSz, int *pSize, void **pp){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmGet(p->pReal, reqSz, pSize, pp); +} +static int devsymShmRelease(sqlite3_file *pFile){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmRelease(p->pReal); +} +static int devsymShmLock(sqlite3_file *pFile, int desired, int *pGot){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmLock(p->pReal, desired, pGot); +} +static int devsymShmClose(sqlite3_file *pFile, int delFlag){ + devsym_file *p = (devsym_file *)pFile; + return sqlite3OsShmClose(p->pReal, delFlag); +} + + /* ** Open an devsym file handle. */ static int devsymOpen( @@ -348,64 +377,19 @@ static int devsymCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return g.pVfs->xCurrentTime(g.pVfs, pTimeOut); } -static int devsymShmOpen( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_shm **pp -){ - return g.pVfs->xShmOpen(g.pVfs, zName, pp); -} -static int devsymShmSize( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int reqSize, - int *pNewSize -){ - return g.pVfs->xShmSize(g.pVfs, p, reqSize, pNewSize); -} -static int devsymShmGet( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int reqMapSize, - int *pMapSize, - void **pp -){ - return g.pVfs->xShmGet(g.pVfs, p, reqMapSize, pMapSize, pp); -} -static int devsymShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *p){ - return g.pVfs->xShmRelease(g.pVfs, p); -} -static int devsymShmLock( - sqlite3_vfs *pVfs, - sqlite3_shm *p, - int desiredLock, - int *gotLock -){ - return g.pVfs->xShmLock(g.pVfs, p, desiredLock, gotLock); -} -static int devsymShmClose(sqlite3_vfs *pVfs, sqlite3_shm *p, int deleteFlag){ - return g.pVfs->xShmClose(g.pVfs, p, deleteFlag); -} - /* ** This procedure registers the devsym vfs with SQLite. If the argument is ** true, the devsym vfs becomes the new default vfs. It is the only publicly ** available function in this file. */ void devsym_register(int iDeviceChar, int iSectorSize){ if( g.pVfs==0 ){ g.pVfs = sqlite3_vfs_find(0); devsym_vfs.szOsFile += g.pVfs->szOsFile; - devsym_vfs.xShmOpen = (g.pVfs->xShmOpen ? devsymShmOpen : 0); - devsym_vfs.xShmSize = (g.pVfs->xShmSize ? devsymShmSize : 0); - devsym_vfs.xShmGet = (g.pVfs->xShmGet ? devsymShmGet : 0); - devsym_vfs.xShmRelease = (g.pVfs->xShmRelease ? devsymShmRelease : 0); - devsym_vfs.xShmLock = (g.pVfs->xShmLock ? devsymShmLock : 0); - devsym_vfs.xShmClose = (g.pVfs->xShmClose ? devsymShmClose : 0); sqlite3_vfs_register(&devsym_vfs, 0); } if( iDeviceChar>=0 ){ g.iDeviceChar = iDeviceChar; }else{ Index: src/test_onefile.c ================================================================== --- src/test_onefile.c +++ src/test_onefile.c @@ -197,15 +197,10 @@ fsDlSym, /* xDlSym */ fsDlClose, /* xDlClose */ fsRandomness, /* xRandomness */ fsSleep, /* xSleep */ fsCurrentTime, /* xCurrentTime */ - 0, /* xShmOpen */ - 0, /* xShmSize */ - 0, /* xShmLock */ - 0, /* xShmClose */ - 0, /* xShmDelete */ 0, /* xRename */ 0 /* xCurrentTimeInt64 */ }, 0, /* pFileList */ 0 /* pParent */ @@ -222,11 +217,16 @@ fsLock, /* xLock */ fsUnlock, /* xUnlock */ fsCheckReservedLock, /* xCheckReservedLock */ fsFileControl, /* xFileControl */ fsSectorSize, /* xSectorSize */ - fsDeviceCharacteristics /* xDeviceCharacteristics */ + fsDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmOpen */ + 0, /* xShmSize */ + 0, /* xShmLock */ + 0, /* xShmClose */ + 0, /* xShmDelete */ }; static sqlite3_io_methods tmp_io_methods = { 1, /* iVersion */ @@ -239,11 +239,16 @@ tmpLock, /* xLock */ tmpUnlock, /* xUnlock */ tmpCheckReservedLock, /* xCheckReservedLock */ tmpFileControl, /* xFileControl */ tmpSectorSize, /* xSectorSize */ - tmpDeviceCharacteristics /* xDeviceCharacteristics */ + tmpDeviceCharacteristics, /* xDeviceCharacteristics */ + 0, /* xShmOpen */ + 0, /* xShmSize */ + 0, /* xShmLock */ + 0, /* xShmClose */ + 0, /* xShmDelete */ }; /* Useful macros used in several places */ #define MIN(x,y) ((x)<(y)?(x):(y)) #define MAX(x,y) ((x)>(y)?(x):(y)) Index: src/test_vfs.c ================================================================== --- src/test_vfs.c +++ src/test_vfs.c @@ -14,19 +14,27 @@ #if SQLITE_TEST /* This file is used for testing only */ #include "sqlite3.h" #include "sqliteInt.h" -typedef struct tvfs_file tvfs_file; -struct tvfs_file { - sqlite3_file base; - sqlite3_file *pReal; -}; - typedef struct Testvfs Testvfs; typedef struct TestvfsShm TestvfsShm; typedef struct TestvfsBuffer TestvfsBuffer; +typedef struct TestvfsFile TestvfsFile; + +/* +** An open file handle. +*/ +struct TestvfsFile { + sqlite3_file base; /* Base class. Must be first */ + sqlite3_vfs *pVfs; /* The VFS */ + const char *zFilename; /* Filename as passed to xOpen() */ + sqlite3_file *pReal; /* The real, underlying file descriptor */ + Tcl_Obj *pShmId; /* Shared memory id for Tcl callbacks */ + TestvfsBuffer *pShm; /* Shared memory buffer */ +}; + /* ** An instance of this structure is allocated for each VFS created. The ** sqlite3_vfs.pAppData field of the VFS structure registered with SQLite ** is set to point to it. @@ -37,10 +45,11 @@ sqlite3_vfs *pVfs; /* The testvfs registered with SQLite */ Tcl_Interp *interp; /* Interpreter to run script in */ int nScript; /* Number of elements in array apScript */ Tcl_Obj **apScript; /* Script to execute */ TestvfsBuffer *pBuffer; /* List of shared buffers */ + int isNoshm; }; /* ** A shared-memory buffer. */ @@ -50,24 +59,16 @@ u8 *a; /* Buffer allocated using ckalloc() */ int nRef; /* Number of references to this object */ TestvfsBuffer *pNext; /* Next in linked list of all buffers */ }; -/* -** A shared-memory handle returned by tvfsShmOpen(). -*/ -struct TestvfsShm { - Tcl_Obj *id; /* Name of this handle */ - TestvfsBuffer *pBuffer; /* Underlying buffer */ -}; - #define PARENTVFS(x) (((Testvfs *)((x)->pAppData))->pParent) /* -** Method declarations for tvfs_file. +** Method declarations for TestvfsFile. */ static int tvfsClose(sqlite3_file*); static int tvfsRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst); static int tvfsWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64 iOfst); static int tvfsTruncate(sqlite3_file*, sqlite3_int64 size); @@ -95,19 +96,19 @@ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ static int tvfsRandomness(sqlite3_vfs*, int nByte, char *zOut); static int tvfsSleep(sqlite3_vfs*, int microseconds); static int tvfsCurrentTime(sqlite3_vfs*, double*); -static int tvfsShmOpen(sqlite3_vfs *, const char *, sqlite3_shm **); -static int tvfsShmSize(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int tvfsShmGet(sqlite3_vfs*, sqlite3_shm *, int , int *, void **); -static int tvfsShmRelease(sqlite3_vfs*, sqlite3_shm *); -static int tvfsShmLock(sqlite3_vfs*, sqlite3_shm *, int , int *); -static int tvfsShmClose(sqlite3_vfs*, sqlite3_shm *, int); +static int tvfsShmOpen(sqlite3_file*); +static int tvfsShmSize(sqlite3_file*, int , int *); +static int tvfsShmGet(sqlite3_file*, int , int *, void **); +static int tvfsShmRelease(sqlite3_file*); +static int tvfsShmLock(sqlite3_file*, int , int *); +static int tvfsShmClose(sqlite3_file*, int); static sqlite3_io_methods tvfs_io_methods = { - 1, /* iVersion */ + 2, /* iVersion */ tvfsClose, /* xClose */ tvfsRead, /* xRead */ tvfsWrite, /* xWrite */ tvfsTruncate, /* xTruncate */ tvfsSync, /* xSync */ @@ -115,18 +116,31 @@ tvfsLock, /* xLock */ tvfsUnlock, /* xUnlock */ tvfsCheckReservedLock, /* xCheckReservedLock */ tvfsFileControl, /* xFileControl */ tvfsSectorSize, /* xSectorSize */ - tvfsDeviceCharacteristics /* xDeviceCharacteristics */ + tvfsDeviceCharacteristics, /* xDeviceCharacteristics */ + tvfsShmOpen, /* xShmOpen */ + tvfsShmSize, /* xShmSize */ + tvfsShmGet, /* xShmGet */ + tvfsShmRelease, /* xShmRelease */ + tvfsShmLock, /* xShmLock */ + tvfsShmClose /* xShmClose */ }; /* ** Close an tvfs-file. */ static int tvfsClose(sqlite3_file *pFile){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; + if( p->pShmId ){ + Tcl_DecrRefCount(p->pShmId); + p->pShmId = 0; + } + if( pFile->pMethods ){ + ckfree((char *)pFile->pMethods); + } return sqlite3OsClose(p->pReal); } /* ** Read data from an tvfs-file. @@ -135,11 +149,11 @@ sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst ){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsRead(p->pReal, zBuf, iAmt, iOfst); } /* ** Write data to an tvfs-file. @@ -148,83 +162,83 @@ sqlite3_file *pFile, const void *zBuf, int iAmt, sqlite_int64 iOfst ){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsWrite(p->pReal, zBuf, iAmt, iOfst); } /* ** Truncate an tvfs-file. */ static int tvfsTruncate(sqlite3_file *pFile, sqlite_int64 size){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsTruncate(p->pReal, size); } /* ** Sync an tvfs-file. */ static int tvfsSync(sqlite3_file *pFile, int flags){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsSync(p->pReal, flags); } /* ** Return the current file-size of an tvfs-file. */ static int tvfsFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsFileSize(p->pReal, pSize); } /* ** Lock an tvfs-file. */ static int tvfsLock(sqlite3_file *pFile, int eLock){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsLock(p->pReal, eLock); } /* ** Unlock an tvfs-file. */ static int tvfsUnlock(sqlite3_file *pFile, int eLock){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsUnlock(p->pReal, eLock); } /* ** Check if another file-handle holds a RESERVED lock on an tvfs-file. */ static int tvfsCheckReservedLock(sqlite3_file *pFile, int *pResOut){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsCheckReservedLock(p->pReal, pResOut); } /* ** File control method. For custom operations on an tvfs-file. */ static int tvfsFileControl(sqlite3_file *pFile, int op, void *pArg){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsFileControl(p->pReal, op, pArg); } /* ** Return the sector-size in bytes for an tvfs-file. */ static int tvfsSectorSize(sqlite3_file *pFile){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsSectorSize(p->pReal); } /* ** Return the device characteristic flags supported by an tvfs-file. */ static int tvfsDeviceCharacteristics(sqlite3_file *pFile){ - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; return sqlite3OsDeviceCharacteristics(p->pReal); } /* ** Open an tvfs file handle. @@ -235,16 +249,32 @@ sqlite3_file *pFile, int flags, int *pOutFlags ){ int rc; - tvfs_file *p = (tvfs_file *)pFile; + TestvfsFile *p = (TestvfsFile *)pFile; + p->pShm = 0; + p->pShmId = 0; + p->zFilename = zName; + p->pVfs = pVfs; p->pReal = (sqlite3_file *)&p[1]; rc = sqlite3OsOpen(PARENTVFS(pVfs), zName, p->pReal, flags, pOutFlags); if( p->pReal->pMethods ){ - pFile->pMethods = &tvfs_io_methods; + sqlite3_io_methods *pMethods; + pMethods = (sqlite3_io_methods *)ckalloc(sizeof(sqlite3_io_methods)); + memcpy(pMethods, &tvfs_io_methods, sizeof(sqlite3_io_methods)); + if( ((Testvfs *)pVfs->pAppData)->isNoshm ){ + pMethods->xShmOpen = 0; + pMethods->xShmGet = 0; + pMethods->xShmSize = 0; + pMethods->xShmRelease = 0; + pMethods->xShmClose = 0; + pMethods->xShmLock = 0; + } + pFile->pMethods = pMethods; } + return rc; } /* ** Delete the file located at zPath. If the dirSync argument is true, @@ -335,12 +365,12 @@ */ static int tvfsCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){ return PARENTVFS(pVfs)->xCurrentTime(PARENTVFS(pVfs), pTimeOut); } -static void tvfsGrowBuffer(TestvfsShm *pShm, int reqSize, int *pNewSize){ - TestvfsBuffer *pBuffer = pShm->pBuffer; +static void tvfsGrowBuffer(TestvfsFile *pFd, int reqSize, int *pNewSize){ + TestvfsBuffer *pBuffer = pFd->pShm; if( reqSize>pBuffer->n ){ pBuffer->a = (u8 *)ckrealloc((char *)pBuffer->a, reqSize); memset(&pBuffer->a[pBuffer->n], 0x55, reqSize-pBuffer->n); pBuffer->n = reqSize; } @@ -403,19 +433,21 @@ return 0; } static int tvfsShmOpen( - sqlite3_vfs *pVfs, - const char *zName, - sqlite3_shm **pp + sqlite3_file *pFileDes ){ - Testvfs *p = (Testvfs *)(pVfs->pAppData); + Testvfs *p; int rc = SQLITE_OK; /* Return code */ Tcl_Obj *pId = 0; /* Id for this connection */ TestvfsBuffer *pBuffer; /* Buffer to open connection to */ - TestvfsShm *pShm; /* New shm handle */ + TestvfsFile *pFd; /* The testvfs file structure */ + + pFd = (TestvfsFile*)pFileDes; + p = (Testvfs *)pFd->pVfs->pAppData; + assert( pFd->pShmId==0 && pFd->pShm==0 ); /* Evaluate the Tcl script: ** ** SCRIPT xShmOpen FILENAME ** @@ -422,109 +454,101 @@ ** If the script returns an SQLite error code other than SQLITE_OK, an ** error is returned to the caller. If it returns SQLITE_OK, the new ** connection is named "anon". Otherwise, the value returned by the ** script is used as the connection name. */ - tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(zName, -1), 0, 0); + tvfsExecTcl(p, "xShmOpen", Tcl_NewStringObj(pFd->zFilename, -1), 0, 0); if( tvfsResultCode(p, &rc) ){ if( rc!=SQLITE_OK ) return rc; pId = Tcl_NewStringObj("anon", -1); }else{ pId = Tcl_GetObjResult(p->interp); } Tcl_IncrRefCount(pId); - - /* Allocate the TestvfsShm handle. */ - pShm = (TestvfsShm *)ckalloc(sizeof(TestvfsShm)); - memset(pShm, 0, sizeof(TestvfsShm)); - pShm->id = pId; + pFd->pShmId = pId; /* Search for a TestvfsBuffer. Create a new one if required. */ for(pBuffer=p->pBuffer; pBuffer; pBuffer=pBuffer->pNext){ - if( 0==strcmp(zName, pBuffer->zFile) ) break; + if( 0==strcmp(pFd->zFilename, pBuffer->zFile) ) break; } if( !pBuffer ){ - int nByte = sizeof(TestvfsBuffer) + strlen(zName) + 1; + int nByte = sizeof(TestvfsBuffer) + strlen(pFd->zFilename) + 1; pBuffer = (TestvfsBuffer *)ckalloc(nByte); memset(pBuffer, 0, nByte); pBuffer->zFile = (char *)&pBuffer[1]; - strcpy(pBuffer->zFile, zName); + strcpy(pBuffer->zFile, pFd->zFilename); pBuffer->pNext = p->pBuffer; p->pBuffer = pBuffer; } /* Connect the TestvfsBuffer to the new TestvfsShm handle and return. */ pBuffer->nRef++; - pShm->pBuffer = pBuffer; - *pp = (sqlite3_shm *)pShm; + pFd->pShm = pBuffer; return SQLITE_OK; } static int tvfsShmSize( - sqlite3_vfs *pVfs, - sqlite3_shm *pShmHandle, + sqlite3_file *pFile, int reqSize, int *pNewSize ){ int rc = SQLITE_OK; - Testvfs *p = (Testvfs *)(pVfs->pAppData); - TestvfsShm *pShm = (TestvfsShm *)pShmHandle; + TestvfsFile *pFd = (TestvfsFile *)pFile; + Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); tvfsExecTcl(p, "xShmSize", - Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); if( rc==SQLITE_OK ){ - tvfsGrowBuffer(pShm, reqSize, pNewSize); + tvfsGrowBuffer(pFd, reqSize, pNewSize); } return rc; } static int tvfsShmGet( - sqlite3_vfs *pVfs, - sqlite3_shm *pShmHandle, + sqlite3_file *pFile, int reqMapSize, int *pMapSize, void **pp ){ int rc = SQLITE_OK; - Testvfs *p = (Testvfs *)(pVfs->pAppData); - TestvfsShm *pShm = (TestvfsShm *)pShmHandle; + TestvfsFile *pFd = (TestvfsFile *)pFile; + Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); tvfsExecTcl(p, "xShmGet", - Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); if( rc==SQLITE_OK ){ - tvfsGrowBuffer(pShm, reqMapSize, pMapSize); - *pp = pShm->pBuffer->a; + tvfsGrowBuffer(pFd, reqMapSize, pMapSize); + *pp = pFd->pShm->a; } return rc; } -static int tvfsShmRelease(sqlite3_vfs *pVfs, sqlite3_shm *pShmHandle){ +static int tvfsShmRelease(sqlite3_file *pFile){ int rc = SQLITE_OK; - Testvfs *p = (Testvfs *)(pVfs->pAppData); - TestvfsShm *pShm = (TestvfsShm *)pShmHandle; + TestvfsFile *pFd = (TestvfsFile *)pFile; + Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); tvfsExecTcl(p, "xShmRelease", - Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); return rc; } static int tvfsShmLock( - sqlite3_vfs *pVfs, - sqlite3_shm *pShmHandle, + sqlite3_file *pFile, int desiredLock, int *gotLock ){ int rc = SQLITE_OK; - Testvfs *p = (Testvfs *)(pVfs->pAppData); - TestvfsShm *pShm = (TestvfsShm *)pShmHandle; + TestvfsFile *pFd = (TestvfsFile *)pFile; + Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); char *zLock = ""; switch( desiredLock ){ case SQLITE_SHM_READ: zLock = "READ"; break; case SQLITE_SHM_WRITE: zLock = "WRITE"; break; @@ -532,11 +556,11 @@ case SQLITE_SHM_RECOVER: zLock = "RECOVER"; break; case SQLITE_SHM_PENDING: zLock = "PENDING"; break; case SQLITE_SHM_UNLOCK: zLock = "UNLOCK"; break; } tvfsExecTcl(p, "xShmLock", - Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, Tcl_NewStringObj(zLock, -1) ); tvfsResultCode(p, &rc); if( rc==SQLITE_OK ){ *gotLock = desiredLock; @@ -544,25 +568,25 @@ return rc; } static int tvfsShmClose( - sqlite3_vfs *pVfs, - sqlite3_shm *pShmHandle, + sqlite3_file *pFile, int deleteFlag ){ int rc = SQLITE_OK; - Testvfs *p = (Testvfs *)(pVfs->pAppData); - TestvfsShm *pShm = (TestvfsShm *)pShmHandle; - TestvfsBuffer *pBuffer = pShm->pBuffer; + TestvfsFile *pFd = (TestvfsFile *)pFile; + Testvfs *p = (Testvfs *)(pFd->pVfs->pAppData); + TestvfsBuffer *pBuffer = pFd->pShm; + assert( pFd->pShmId && pFd->pShm ); #if 0 assert( (deleteFlag!=0)==(pBuffer->nRef==1) ); #endif tvfsExecTcl(p, "xShmClose", - Tcl_NewStringObj(pShm->pBuffer->zFile, -1), pShm->id, 0 + Tcl_NewStringObj(pFd->pShm->zFile, -1), pFd->pShmId, 0 ); tvfsResultCode(p, &rc); pBuffer->nRef--; if( pBuffer->nRef==0 ){ @@ -570,12 +594,13 @@ for(pp=&p->pBuffer; *pp!=pBuffer; pp=&((*pp)->pNext)); *pp = (*pp)->pNext; ckfree((char *)pBuffer->a); ckfree((char *)pBuffer); } - Tcl_DecrRefCount(pShm->id); - ckfree((char *)pShm); + Tcl_DecrRefCount(pFd->pShmId); + pFd->pShmId = 0; + pFd->pShm = 0; return rc; } static int testvfs_obj_cmd( @@ -685,14 +710,13 @@ ClientData cd, Tcl_Interp *interp, int objc, Tcl_Obj *CONST objv[] ){ - static sqlite3_vfs tvfs_vfs = { 2, /* iVersion */ - sizeof(tvfs_file), /* szOsFile */ + sizeof(TestvfsFile), /* szOsFile */ 0, /* mxPathname */ 0, /* pNext */ 0, /* zName */ 0, /* pAppData */ tvfsOpen, /* xOpen */ @@ -712,16 +736,10 @@ #endif /* SQLITE_OMIT_LOAD_EXTENSION */ tvfsRandomness, /* xRandomness */ tvfsSleep, /* xSleep */ tvfsCurrentTime, /* xCurrentTime */ 0, /* xGetLastError */ - tvfsShmOpen, - tvfsShmSize, - tvfsShmGet, - tvfsShmRelease, - tvfsShmLock, - tvfsShmClose, 0, 0, }; Testvfs *p; /* New object */ @@ -768,18 +786,11 @@ pVfs->pAppData = (void *)p; pVfs->zName = p->zName; pVfs->mxPathname = p->pParent->mxPathname; pVfs->szOsFile += p->pParent->szOsFile; p->pVfs = pVfs; - if( isNoshm ){ - pVfs->xShmOpen = 0; - pVfs->xShmGet = 0; - pVfs->xShmSize = 0; - pVfs->xShmRelease = 0; - pVfs->xShmClose = 0; - pVfs->xShmLock = 0; - } + p->isNoshm = isNoshm; Tcl_CreateObjCommand(interp, zVfs, testvfs_obj_cmd, p, testvfs_obj_del); sqlite3_vfs_register(pVfs, 0); return TCL_OK; Index: src/vdbe.c ================================================================== --- src/vdbe.c +++ src/vdbe.c @@ -5246,12 +5246,12 @@ /* Do not allow a transition to journal_mode=WAL for a database ** in temporary storage or if the VFS does not support xShmOpen. */ if( eNew==PAGER_JOURNALMODE_WAL - && (zFilename[0]==0 /* Temp file */ - || pVfs->iVersion<2 || pVfs->xShmOpen==0) /* No xShmOpen support */ + && (zFilename[0]==0 /* Temp file */ + || !sqlite3PagerWalSupported(pPager)) /* No xShmOpen support */ ){ eNew = PAGER_JOURNALMODE_QUERY; } if( eNew!=PAGER_JOURNALMODE_QUERY ){ Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -123,20 +123,21 @@ ** An open write-ahead log file is represented by an instance of the ** following object. */ struct Wal { sqlite3_vfs *pVfs; /* The VFS used to create pFd */ - sqlite3_file *pFd; /* File handle for WAL file */ + sqlite3_file *pDbFd; /* File handle for the database file */ + sqlite3_file *pWalFd; /* File handle for WAL file */ u32 iCallback; /* Value to pass to log callback (or 0) */ - sqlite3_shm *pWIndex; /* The open wal-index file */ int szWIndex; /* Size of the wal-index that is mapped in mem */ u32 *pWiData; /* Pointer to wal-index content in memory */ u8 lockState; /* SQLITE_SHM_xxxx constant showing lock state */ u8 readerType; /* SQLITE_SHM_READ or SQLITE_SHM_READ_FULL */ u8 exclusiveMode; /* Non-zero if connection is in exclusive mode */ + u8 isWindexOpen; /* True if ShmOpen() called on pDbFd */ WalIndexHdr hdr; /* Wal-index for current snapshot */ - char *zName; /* Name of underlying storage */ + char *zWalName; /* Name of WAL file */ }; /* ** This structure is used to implement an iterator that iterates through @@ -221,11 +222,11 @@ int rc = SQLITE_OK; /* Return code */ if( pWal->exclusiveMode || pWal->lockState==desiredStatus ){ pWal->lockState = desiredStatus; }else{ int got = pWal->lockState; - rc = pWal->pVfs->xShmLock(pWal->pVfs, pWal->pWIndex, desiredStatus, &got); + rc = sqlite3OsShmLock(pWal->pDbFd, desiredStatus, &got); pWal->lockState = got; if( got==SQLITE_SHM_READ_FULL || got==SQLITE_SHM_READ ){ pWal->readerType = got; pWal->lockState = SQLITE_SHM_READ; } @@ -402,11 +403,11 @@ ** Release our reference to the wal-index memory map, if we are holding ** it. */ static void walIndexUnmap(Wal *pWal){ if( pWal->pWiData ){ - pWal->pVfs->xShmRelease(pWal->pVfs, pWal->pWIndex); + sqlite3OsShmRelease(pWal->pDbFd); pWal->pWiData = 0; } } /* @@ -416,12 +417,12 @@ ** A value of -1 means "don't care". */ static int walIndexMap(Wal *pWal, int reqSize){ int rc = SQLITE_OK; if( pWal->pWiData==0 || reqSize>pWal->szWIndex ){ - rc = pWal->pVfs->xShmGet(pWal->pVfs, pWal->pWIndex, reqSize, - &pWal->szWIndex, (void**)(char*)&pWal->pWiData); + rc = sqlite3OsShmGet(pWal->pDbFd, reqSize, &pWal->szWIndex, + (void**)(char*)&pWal->pWiData); if( rc==SQLITE_OK && pWal->pWiData==0 ){ /* Make sure pWal->pWiData is not NULL while we are holding the ** lock on the mapping. */ assert( pWal->szWIndex==0 ); pWal->pWiData = &pWal->iCallback; @@ -441,11 +442,11 @@ ** storage to be at least as big as enlargeTo before remapping. */ static int walIndexRemap(Wal *pWal, int enlargeTo){ int rc; int sz; - rc = pWal->pVfs->xShmSize(pWal->pVfs, pWal->pWIndex, enlargeTo, &sz); + rc = sqlite3OsShmSize(pWal->pDbFd, enlargeTo, &sz); if( rc==SQLITE_OK && sz>pWal->szWIndex ){ walIndexUnmap(pWal); rc = walIndexMap(pWal, sz); } return rc; @@ -559,11 +560,11 @@ WalIndexHdr hdr; /* Recovered wal-index header */ assert( pWal->lockState>SQLITE_SHM_READ ); memset(&hdr, 0, sizeof(hdr)); - rc = sqlite3OsFileSize(pWal->pFd, &nSize); + rc = sqlite3OsFileSize(pWal->pWalFd, &nSize); if( rc!=SQLITE_OK ){ return rc; } if( nSize>WAL_FRAME_HDRSIZE ){ @@ -577,11 +578,11 @@ u32 aCksum[2]; /* Running checksum */ /* Read in the first frame header in the file (to determine the ** database page size). */ - rc = sqlite3OsRead(pWal->pFd, aBuf, WAL_HDRSIZE, 0); + rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ return rc; } /* If the database page size is not a power of two, or is greater than @@ -608,11 +609,11 @@ u32 pgno; /* Database page number for frame */ u32 nTruncate; /* dbsize field from frame header */ int isValid; /* True if this frame is valid */ /* Read and decode the next log frame. */ - rc = sqlite3OsRead(pWal->pFd, aFrame, nFrame, iOffset); + rc = sqlite3OsRead(pWal->pWalFd, aFrame, nFrame, iOffset); if( rc!=SQLITE_OK ) break; isValid = walDecodeFrame(aCksum, &pgno, &nTruncate, nPgsz, aData, aFrame); if( !isValid ) break; rc = walIndexAppend(pWal, ++iFrame, pgno); if( rc!=SQLITE_OK ) break; @@ -646,16 +647,15 @@ /* ** Close an open wal-index. */ static void walIndexClose(Wal *pWal, int isDelete){ - sqlite3_shm *pWIndex = pWal->pWIndex; - if( pWIndex ){ - sqlite3_vfs *pVfs = pWal->pVfs; + if( pWal->isWindexOpen ){ int notUsed; - pVfs->xShmLock(pVfs, pWIndex, SQLITE_SHM_UNLOCK, ¬Used); - pVfs->xShmClose(pVfs, pWIndex, isDelete); + sqlite3OsShmLock(pWal->pDbFd, SQLITE_SHM_UNLOCK, ¬Used); + sqlite3OsShmClose(pWal->pDbFd, isDelete); + pWal->isWindexOpen = 0; } } /* ** Open a connection to the log file associated with database zDb. The @@ -673,45 +673,48 @@ ** *ppWal is set to point to a new WAL handle. If an error occurs, ** an SQLite error code is returned and *ppWal is left unmodified. */ int sqlite3WalOpen( sqlite3_vfs *pVfs, /* vfs module to open wal and wal-index */ - const char *zDb, /* Name of database file */ + sqlite3_file *pDbFd, /* The open database file */ + const char *zDbName, /* Name of the database file */ Wal **ppWal /* OUT: Allocated Wal handle */ ){ int rc; /* Return Code */ Wal *pRet; /* Object to allocate and return */ int flags; /* Flags passed to OsOpen() */ - char *zWal; /* Path to WAL file */ + char *zWal; /* Name of write-ahead log file */ int nWal; /* Length of zWal in bytes */ - assert( zDb ); - if( pVfs->xShmOpen==0 ) return SQLITE_CANTOPEN_BKPT; + assert( zDbName && zDbName[0] ); + assert( pDbFd ); /* Allocate an instance of struct Wal to return. */ *ppWal = 0; - nWal = strlen(zDb); - pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile + nWal+5); + nWal = sqlite3Strlen30(zDbName) + 5; + pRet = (Wal*)sqlite3MallocZero(sizeof(Wal) + pVfs->szOsFile + nWal); if( !pRet ){ return SQLITE_NOMEM; } pRet->pVfs = pVfs; - pRet->pFd = (sqlite3_file *)&pRet[1]; - pRet->zName = zWal = pVfs->szOsFile + (char*)pRet->pFd; - sqlite3_snprintf(nWal+5, zWal, "%s-wal", zDb); - rc = pVfs->xShmOpen(pVfs, zDb, &pRet->pWIndex); + pRet->pWalFd = (sqlite3_file *)&pRet[1]; + pRet->pDbFd = pDbFd; + pRet->zWalName = zWal = pVfs->szOsFile + (char*)pRet->pWalFd; + sqlite3_snprintf(nWal, zWal, "%s-wal", zDbName); + rc = sqlite3OsShmOpen(pDbFd); /* Open file handle on the write-ahead log file. */ if( rc==SQLITE_OK ){ + pRet->isWindexOpen = 1; flags = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_MAIN_JOURNAL); - rc = sqlite3OsOpen(pVfs, zWal, pRet->pFd, flags, &flags); + rc = sqlite3OsOpen(pVfs, zWal, pRet->pWalFd, flags, &flags); } if( rc!=SQLITE_OK ){ walIndexClose(pRet, 0); - sqlite3OsClose(pRet->pFd); + sqlite3OsClose(pRet->pWalFd); sqlite3_free(pRet); }else{ *ppWal = pRet; } return rc; @@ -807,11 +810,10 @@ /* ** Checkpoint the contents of the log file. */ static int walCheckpoint( Wal *pWal, /* Wal connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags for OsSync() (or 0) */ int nBuf, /* Size of zBuf in bytes */ u8 *zBuf /* Temporary buffer to use */ ){ int rc; /* Return code */ @@ -831,31 +833,31 @@ goto out; } /* Sync the log file to disk */ if( sync_flags ){ - rc = sqlite3OsSync(pWal->pFd, sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); if( rc!=SQLITE_OK ) goto out; } /* Iterate through the contents of the log, copying data to the db file. */ while( 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){ - rc = sqlite3OsRead(pWal->pFd, zBuf, pgsz, + rc = sqlite3OsRead(pWal->pWalFd, zBuf, pgsz, walFrameOffset(iFrame, pgsz) + WAL_FRAME_HDRSIZE ); if( rc!=SQLITE_OK ) goto out; - rc = sqlite3OsWrite(pFd, zBuf, pgsz, (iDbpage-1)*pgsz); + rc = sqlite3OsWrite(pWal->pDbFd, zBuf, pgsz, (iDbpage-1)*pgsz); if( rc!=SQLITE_OK ) goto out; } /* Truncate the database file */ - rc = sqlite3OsTruncate(pFd, ((i64)pWal->hdr.nPage*(i64)pgsz)); + rc = sqlite3OsTruncate(pWal->pDbFd, ((i64)pWal->hdr.nPage*(i64)pgsz)); if( rc!=SQLITE_OK ) goto out; /* Sync the database file. If successful, update the wal-index. */ if( sync_flags ){ - rc = sqlite3OsSync(pFd, sync_flags); + rc = sqlite3OsSync(pWal->pDbFd, sync_flags); if( rc!=SQLITE_OK ) goto out; } pWal->hdr.iLastPg = 0; pWal->hdr.iCheck1 = 2; pWal->hdr.iCheck2 = 3; @@ -874,13 +876,13 @@ ** into the log at this point. Unfortunately, that turns out to be ** an unwelcome performance hit. Alternatives are... */ #if 0 memset(zBuf, 0, WAL_FRAME_HDRSIZE); - rc = sqlite3OsWrite(pWal->pFd, zBuf, WAL_FRAME_HDRSIZE, 0); + rc = sqlite3OsWrite(pWal->pWalFd, zBuf, WAL_FRAME_HDRSIZE, 0); if( rc!=SQLITE_OK ) goto out; - rc = sqlite3OsSync(pWal->pFd, pWal->sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, pWal->sync_flags); #endif out: walIteratorFree(pIter); return rc; @@ -889,11 +891,10 @@ /* ** Close a connection to a log file. */ int sqlite3WalClose( Wal *pWal, /* Wal to close */ - sqlite3_file *pFd, /* Database file */ int sync_flags, /* Flags to pass to OsSync() (or 0) */ int nBuf, u8 *zBuf /* Buffer of at least nBuf bytes */ ){ int rc = SQLITE_OK; @@ -906,23 +907,23 @@ ** the database. In this case checkpoint the database and unlink both ** the wal and wal-index files. ** ** The EXCLUSIVE lock is not released before returning. */ - rc = sqlite3OsLock(pFd, SQLITE_LOCK_EXCLUSIVE); + rc = sqlite3OsLock(pWal->pDbFd, SQLITE_LOCK_EXCLUSIVE); if( rc==SQLITE_OK ){ - rc = sqlite3WalCheckpoint(pWal, pFd, sync_flags, nBuf, zBuf, 0, 0); + rc = sqlite3WalCheckpoint(pWal, sync_flags, nBuf, zBuf, 0, 0); if( rc==SQLITE_OK ){ isDelete = 1; } walIndexUnmap(pWal); } walIndexClose(pWal, isDelete); - sqlite3OsClose(pWal->pFd); + sqlite3OsClose(pWal->pWalFd); if( isDelete ){ - sqlite3OsDelete(pWal->pVfs, pWal->zName, 0); + sqlite3OsDelete(pWal->pVfs, pWal->zWalName, 0); } sqlite3_free(pWal); } return rc; } @@ -1189,11 +1190,11 @@ */ walIndexUnmap(pWal); if( iRead ){ i64 iOffset = walFrameOffset(iRead, pWal->hdr.pgsz) + WAL_FRAME_HDRSIZE; *pInWal = 1; - return sqlite3OsRead(pWal->pFd, pOut, nOut, iOffset); + return sqlite3OsRead(pWal->pWalFd, pOut, nOut, iOffset); } *pInWal = 0; return SQLITE_OK; } @@ -1288,11 +1289,11 @@ assert( pWal->lockState==SQLITE_SHM_WRITE ); pWal->hdr.iLastPg = iFrame; if( iFrame>0 ){ i64 iOffset = walFrameOffset(iFrame, pWal->hdr.pgsz) + sizeof(u32)*2; - rc = sqlite3OsRead(pWal->pFd, aCksum, sizeof(aCksum), iOffset); + rc = sqlite3OsRead(pWal->pWalFd, aCksum, sizeof(aCksum), iOffset); pWal->hdr.iCheck1 = sqlite3Get4byte(&aCksum[0]); pWal->hdr.iCheck2 = sqlite3Get4byte(&aCksum[4]); } return rc; @@ -1332,11 +1333,11 @@ if( iFrame==0 ){ sqlite3Put4byte(aFrame, nPgsz); sqlite3_randomness(8, &aFrame[4]); pWal->hdr.iCheck1 = sqlite3Get4byte(&aFrame[4]); pWal->hdr.iCheck2 = sqlite3Get4byte(&aFrame[8]); - rc = sqlite3OsWrite(pWal->pFd, aFrame, WAL_HDRSIZE, 0); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, WAL_HDRSIZE, 0); if( rc!=SQLITE_OK ){ return rc; } } @@ -1351,26 +1352,26 @@ iOffset = walFrameOffset(++iFrame, nPgsz); /* Populate and write the frame header */ nDbsize = (isCommit && p->pDirty==0) ? nTruncate : 0; walEncodeFrame(aCksum, p->pgno, nDbsize, nPgsz, p->pData, aFrame); - rc = sqlite3OsWrite(pWal->pFd, aFrame, sizeof(aFrame), iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } /* Write the page data */ - rc = sqlite3OsWrite(pWal->pFd, p->pData, nPgsz, iOffset + sizeof(aFrame)); + rc = sqlite3OsWrite(pWal->pWalFd, p->pData, nPgsz, iOffset + sizeof(aFrame)); if( rc!=SQLITE_OK ){ return rc; } pLast = p; } /* Sync the log file if the 'isSync' flag was specified. */ if( sync_flags ){ - i64 iSegment = sqlite3OsSectorSize(pWal->pFd); + i64 iSegment = sqlite3OsSectorSize(pWal->pWalFd); i64 iOffset = walFrameOffset(iFrame+1, nPgsz); assert( isCommit ); if( iSegmentpgno,nTruncate,nPgsz,pLast->pData,aFrame); - rc = sqlite3OsWrite(pWal->pFd, aFrame, sizeof(aFrame), iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, aFrame, sizeof(aFrame), iOffset); if( rc!=SQLITE_OK ){ return rc; } iOffset += WAL_FRAME_HDRSIZE; - rc = sqlite3OsWrite(pWal->pFd, pLast->pData, nPgsz, iOffset); + rc = sqlite3OsWrite(pWal->pWalFd, pLast->pData, nPgsz, iOffset); if( rc!=SQLITE_OK ){ return rc; } nLast++; iOffset += nPgsz; } - rc = sqlite3OsSync(pWal->pFd, sync_flags); + rc = sqlite3OsSync(pWal->pWalFd, sync_flags); } assert( pWal->pWiData==0 ); /* Append data to the log summary. It is not necessary to lock the ** wal-index to do this as the RESERVED lock held on the db file @@ -1443,11 +1444,10 @@ ** 3. Zero the wal-index header (so new readers will ignore the log). ** 4. Drop the CHECKPOINT lock. */ int sqlite3WalCheckpoint( Wal *pWal, /* Wal connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of temporary buffer */ u8 *zBuf, /* Temporary buffer to use */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */ @@ -1477,11 +1477,11 @@ } /* Copy data from the log to the database file. */ rc = walIndexReadHdr(pWal, &isChanged); if( rc==SQLITE_OK ){ - rc = walCheckpoint(pWal, pFd, sync_flags, nBuf, zBuf); + rc = walCheckpoint(pWal, sync_flags, nBuf, zBuf); } if( isChanged ){ /* If a new wal-index header was loaded before the checkpoint was ** performed, then the pager-cache associated with log pWal is now ** out of date. So zero the cached wal-index header to ensure that Index: src/wal.h ================================================================== --- src/wal.h +++ src/wal.h @@ -39,12 +39,12 @@ ** There is one object of this type for each pager. */ typedef struct Wal Wal; /* Open and close a connection to a write-ahead log. */ -int sqlite3WalOpen(sqlite3_vfs*, const char *zDb, Wal **ppWal); -int sqlite3WalClose(Wal *pWal, sqlite3_file *pFd, int sync_flags, int, u8 *); +int sqlite3WalOpen(sqlite3_vfs*, sqlite3_file*, const char *zName, Wal**); +int sqlite3WalClose(Wal *pWal, int sync_flags, int, u8 *); /* Used by readers to open (lock) and close (unlock) a snapshot. A ** snapshot is like a read-transaction. It is the state of the database ** at an instant in time. sqlite3WalOpenSnapshot gets a read lock and ** preserves the current state even if the other threads or processes @@ -79,11 +79,10 @@ int sqlite3WalFrames(Wal *pWal, int, PgHdr *, Pgno, int, int); /* Copy pages from the log to the database file */ int sqlite3WalCheckpoint( Wal *pWal, /* Write-ahead log connection */ - sqlite3_file *pFd, /* File descriptor open on db file */ int sync_flags, /* Flags to sync db file with (or 0) */ int nBuf, /* Size of buffer nBuf */ u8 *zBuf, /* Temporary buffer to use */ int (*xBusyHandler)(void *), /* Pointer to busy-handler function */ void *pBusyHandlerArg /* Argument to pass to xBusyHandler */