Index: src/os_unix.c ================================================================== --- src/os_unix.c +++ src/os_unix.c @@ -201,13 +201,11 @@ #endif #ifdef __QNXNTO__ int sectorSize; /* Device sector size */ int deviceCharacteristics; /* Precomputed device characteristics */ #endif -#if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ -#endif #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) unsigned fsFlags; /* cached details from statfs() */ #endif #if OS_VXWORKS struct vxworksFileId *pId; /* Unique file ID */ @@ -253,11 +251,12 @@ #endif #define UNIXFILE_PSOW 0x10 /* SQLITE_IOCAP_POWERSAFE_OVERWRITE */ #define UNIXFILE_DELETE 0x20 /* Delete on close */ #define UNIXFILE_URI 0x40 /* Filename might have query parameters */ #define UNIXFILE_NOLOCK 0x80 /* Do no file locking */ -#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() warnings have been issued */ +#define UNIXFILE_WARNED 0x0100 /* verifyDbFile() has issued warnings */ +#define UNIXFILE_DEFERRED 0x0200 /* File has not yet been opened */ /* ** Include code that is common to all os_*.c files */ #include "os_common.h" @@ -1355,13 +1354,18 @@ static int unixCheckReservedLock(sqlite3_file *id, int *pResOut){ int rc = SQLITE_OK; int reserved = 0; unixFile *pFile = (unixFile*)id; + assert( pFile ); SimulateIOError( return SQLITE_IOERR_CHECKRESERVEDLOCK; ); - assert( pFile ); + if( pFile->ctrlFlags==UNIXFILE_DEFERRED ){ + *pResOut = 0; + return SQLITE_OK; + } + unixEnterMutex(); /* Because pFile->pInode is shared across threads */ /* Check if a thread in this process holds such a lock */ if( pFile->pInode->eFileLock>SHARED_LOCK ){ reserved = 1; @@ -1436,10 +1440,12 @@ }else{ rc = osFcntl(pFile->h, F_SETLK, pLock); } return rc; } + +static int unixOpen(sqlite3_vfs*, const char*, sqlite3_file*, int, int *); /* ** Lock the file with the lock specified by parameter eFileLock - one ** of the following: ** @@ -1521,10 +1527,31 @@ OSTRACE(("LOCK %d %s ok (already held) (unix)\n", pFile->h, azFileLock(eFileLock))); return SQLITE_OK; } + if( pFile->ctrlFlags==UNIXFILE_DEFERRED ){ + int eOrigLock = pFile->eFileLock; + if( eFileLock==SHARED_LOCK ){ + int statrc; + struct stat sBuf; + memset(&sBuf, 0, sizeof(sBuf)); + statrc = osStat(pFile->zPath, &sBuf); + if( statrc && errno==ENOENT ){ + pFile->eFileLock = SHARED_LOCK; + return SQLITE_OK; + } + } + + rc = unixOpen(pFile->pVfs, pFile->zPath, id, pFile->openFlags, 0); + if( rc==SQLITE_OK && eOrigLock ){ + rc = unixLock(id, eOrigLock); + } + if( rc!=SQLITE_OK ) return rc; + } + assert( (pFile->ctrlFlags & UNIXFILE_DEFERRED)==0 ); + /* Make sure the locking sequence is correct. ** (1) We never move from unlocked to anything higher than shared lock. ** (2) SQLite never explicitly requests a pendig lock. ** (3) A shared lock is always held when a reserve lock is requested. */ @@ -1724,10 +1751,11 @@ assert( eFileLock<=SHARED_LOCK ); if( pFile->eFileLock<=eFileLock ){ return SQLITE_OK; } unixEnterMutex(); + if( pFile->ctrlFlags==UNIXFILE_DEFERRED ) goto end_unlock; pInode = pFile->pInode; assert( pInode->nShared!=0 ); if( pFile->eFileLock>SHARED_LOCK ){ assert( pInode->eFileLock==pFile->eFileLock ); @@ -1931,22 +1959,26 @@ verifyDbFile(pFile); unixUnlock(id, NO_LOCK); unixEnterMutex(); /* unixFile.pInode is always valid here. Otherwise, a different close - ** routine (e.g. nolockClose()) would be called instead. - */ - assert( pFile->pInode->nLock>0 || pFile->pInode->bProcessLock==0 ); - if( ALWAYS(pFile->pInode) && pFile->pInode->nLock ){ - /* If there are outstanding locks, do not actually close the file just - ** yet because that would clear those locks. Instead, add the file - ** descriptor to pInode->pUnused list. It will be automatically closed - ** when the last lock is cleared. - */ - setPendingFd(pFile); - } - releaseInodeInfo(pFile); + ** routine (e.g. nolockClose()) would be called instead. */ + assert( pFile->pInode==0 + || pFile->pInode->nLock>0 + || pFile->pInode->bProcessLock==0 + ); + if( pFile->pInode ){ + if( pFile->pInode->nLock ){ + /* If there are outstanding locks, do not actually close the file just + ** yet because that would clear those locks. Instead, add the file + ** descriptor to pInode->pUnused list. It will be automatically closed + ** when the last lock is cleared. + */ + setPendingFd(pFile); + } + releaseInodeInfo(pFile); + } rc = closeUnixFile(id); unixLeaveMutex(); return rc; } @@ -3171,10 +3203,27 @@ || offset>=PENDING_BYTE+512 || offset+amt<=PENDING_BYTE ); #endif + if( pFile->ctrlFlags==UNIXFILE_DEFERRED ){ + int rc; + struct stat sBuf; + memset(&sBuf, 0, sizeof(sBuf)); + rc = osStat(pFile->zPath, &sBuf); + if( rc!=0 ){ + memset(pBuf, 0, amt); + rc = (errno==ENOENT ? SQLITE_IOERR_SHORT_READ : SQLITE_IOERR_FSTAT); + }else{ + rc = unixOpen(pFile->pVfs, pFile->zPath, id, pFile->openFlags, 0); + } + if( rc!=SQLITE_OK ){ + return rc; + } + } + assert( (pFile->ctrlFlags & UNIXFILE_DEFERRED)==0 ); + #if SQLITE_MAX_MMAP_SIZE>0 /* Deal with as much of this read request as possible by transfering ** data from the memory mapping using memcpy(). */ if( offsetmmapSize ){ if( offset+amt <= pFile->mmapSize ){ @@ -3623,14 +3672,23 @@ /* ** Determine the current size of a file in bytes */ static int unixFileSize(sqlite3_file *id, i64 *pSize){ + unixFile *pFile = (unixFile*)id; int rc; struct stat buf; assert( id ); - rc = osFstat(((unixFile*)id)->h, &buf); + if( pFile->ctrlFlags==UNIXFILE_DEFERRED ){ + rc = osStat(pFile->zPath, &buf); + if( rc && errno==ENOENT ){ + rc = 0; + buf.st_size = 0; + } + }else{ + rc = osFstat(pFile->h, &buf); + } SimulateIOError( rc=1 ); if( rc!=0 ){ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_FSTAT; } @@ -3641,11 +3699,10 @@ ** in the OS-X msdos filesystem. In order to avoid problems with upper ** layers, we need to report this file size as zero even though it is ** really 1. Ticket #3260. */ if( *pSize==1 ) *pSize = 0; - return SQLITE_OK; } #if SQLITE_ENABLE_LOCKING_STYLE && defined(__APPLE__) @@ -5174,11 +5231,11 @@ OSTRACE(("OPEN %-3d %s\n", h, zFilename)); pNew->h = h; pNew->pVfs = pVfs; pNew->zPath = zFilename; - pNew->ctrlFlags = (u8)ctrlFlags; + pNew->ctrlFlags = (unsigned short)ctrlFlags; #if SQLITE_MAX_MMAP_SIZE>0 pNew->mmapSizeMax = sqlite3GlobalConfig.szMmap; #endif if( sqlite3_uri_boolean(((ctrlFlags & UNIXFILE_URI) ? zFilename : 0), "psow", SQLITE_POWERSAFE_OVERWRITE) ){ @@ -5820,10 +5877,41 @@ sqlite3_free(p->pUnused); } return rc; } +static int unixOpenDeferred( + sqlite3_vfs *pVfs, /* The VFS for which this is the xOpen method */ + const char *zPath, /* Pathname of file to be opened */ + sqlite3_file *pFile, /* The file descriptor to be filled in */ + int flags, /* Input flags to control the opening */ + int *pOutFlags /* Output flags returned to SQLite core */ +){ + const int mask1 = SQLITE_OPEN_MAIN_DB | SQLITE_OPEN_READWRITE + | SQLITE_OPEN_CREATE; + const int mask2 = SQLITE_OPEN_READONLY | SQLITE_OPEN_DELETEONCLOSE + | SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_AUTOPROXY; + int rc = SQLITE_OK; /* Return code */ + + /* If all the flags in mask1 are set, and all the flags in mask2 are + ** clear, then this will be a deferred open. */ + if( zPath && (flags & (mask1 | mask2))==mask1 ){ + unixFile *p = (unixFile*)pFile; + memset(p, 0, sizeof(unixFile)); + + p->pMethod = (**(finder_type*)pVfs->pAppData)(0, 0); + p->pVfs = pVfs; + p->h = -1; + p->ctrlFlags = UNIXFILE_DEFERRED; + p->openFlags = flags; + p->zPath = zPath; + if( pOutFlags ) *pOutFlags = flags; + }else{ + rc = unixOpen(pVfs, zPath, pFile, flags, pOutFlags); + } + return rc; +} /* ** Delete the file at zPath. If the dirSync argument is true, fsync() ** the directory after deleting the file. */ @@ -7374,11 +7462,11 @@ sizeof(unixFile), /* szOsFile */ \ MAX_PATHNAME, /* mxPathname */ \ 0, /* pNext */ \ VFSNAME, /* zName */ \ (void*)&FINDER, /* pAppData */ \ - unixOpen, /* xOpen */ \ + unixOpenDeferred, /* xOpen */ \ unixDelete, /* xDelete */ \ unixAccess, /* xAccess */ \ unixFullPathname, /* xFullPathname */ \ unixDlOpen, /* xDlOpen */ \ unixDlError, /* xDlError */ \ Index: src/test2.c ================================================================== --- src/test2.c +++ src/test2.c @@ -532,11 +532,14 @@ sqlite3_free(zFile); return TCL_ERROR; } offset = n; offset *= 1024*1024; + sqlite3OsLock(fd, SHARED_LOCK); + sqlite3OsLock(fd, EXCLUSIVE_LOCK); rc = sqlite3OsWrite(fd, "Hello, World!", 14, offset); + sqlite3OsUnlock(fd, NO_LOCK); sqlite3OsCloseFree(fd); sqlite3_free(zFile); if( rc ){ Tcl_AppendResult(interp, "write failed: ", sqlite3ErrName(rc), 0); return TCL_ERROR; Index: test/permutations.test ================================================================== --- test/permutations.test +++ test/permutations.test @@ -133,11 +133,12 @@ test_suite "veryquick" -prefix "" -description { "Very" quick test suite. Runs in less than 5 minutes on a workstation. This test suite is the same as the "quick" tests, except that some files that test malloc and IO errors are omitted. } -files [ - test_set $allquicktests -exclude *malloc* *ioerr* *fault* + test_set $allquicktests -exclude *malloc* *ioerr* *fault* \ + *multiplex* *quota* walbak.test ] test_suite "mmap" -prefix "mm-" -description { Similar to veryquick. Except with memory mapping disabled. } -presql {