mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
When a database file is opened, try to find an unused file descriptor to reuse. This change affects unix (and other systems that use os_unix.c) only. Fix for cvstrac ticket [http://www.sqlite.org/cvstrac/tktview?tn=4018|#4018].
FossilOrigin-Name: 9b4d9ab62d687289837b13b07885e72cc3abe8a9
This commit is contained in:
25
manifest
25
manifest
@@ -1,8 +1,5 @@
|
|||||||
-----BEGIN PGP SIGNED MESSAGE-----
|
C When\sa\sdatabase\sfile\sis\sopened,\stry\sto\sfind\san\sunused\sfile\sdescriptor\sto\sreuse.\s\sThis\schange\saffects\sunix\s(and\sother\ssystems\sthat\suse\sos_unix.c)\sonly.\sFix\sfor\scvstrac\sticket\s[http://www.sqlite.org/cvstrac/tktview?tn=4018|#4018].
|
||||||
Hash: SHA1
|
D 2009-08-21T17:18:03
|
||||||
|
|
||||||
C Change\sthe\sexpression\scode\sgenerator\sto\saccount\sfor\sthe\sfact\sthat\sthe\snew\nsqlite3AtoF()\snever\sreturns\sNaN.\s\sAlso,\sclarification\sof\sa\scomment\sin\nwhere.c.
|
|
||||||
D 2009-08-21T13:22:25
|
|
||||||
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
F Makefile.arm-wince-mingw32ce-gcc fcd5e9cd67fe88836360bb4f9ef4cb7f8e2fb5a0
|
||||||
F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8
|
F Makefile.in 73ddeec9dd10b85876c5c2ce1fdce627e1dcc7f8
|
||||||
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
F Makefile.linux-gcc d53183f4aa6a9192d249731c90dbdffbd2c68654
|
||||||
@@ -148,7 +145,7 @@ F src/os.c 9fea283e336ee31caa4654d6cb05a129a1c42d2f
|
|||||||
F src/os.h 00a1334a4eecee7f7bef79ac606b88d325119f21
|
F src/os.h 00a1334a4eecee7f7bef79ac606b88d325119f21
|
||||||
F src/os_common.h 8c61457df58f1a4bd5f5adc3e90e01b37bf7afbc
|
F src/os_common.h 8c61457df58f1a4bd5f5adc3e90e01b37bf7afbc
|
||||||
F src/os_os2.c bed77dc26e3a95ce4a204936b9a1ca6fe612fcc5
|
F src/os_os2.c bed77dc26e3a95ce4a204936b9a1ca6fe612fcc5
|
||||||
F src/os_unix.c 85f6ed28690a3c4546edaa25252d52761490aa33
|
F src/os_unix.c 1546de71b888c9a2bb0589d04e7e4267d40ef944
|
||||||
F src/os_win.c 58bb163f327e79726dd119344d908e4d98483c3f
|
F src/os_win.c 58bb163f327e79726dd119344d908e4d98483c3f
|
||||||
F src/pager.c a47be286477ed6c7b9a342dd53d4e4043f29d8c2
|
F src/pager.c a47be286477ed6c7b9a342dd53d4e4043f29d8c2
|
||||||
F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f
|
F src/pager.h 11852d044c86cf5a9d6e34171fb0c4fcf1f6265f
|
||||||
@@ -667,6 +664,7 @@ F test/tkt3929.test 6a4c3baefb4e75127356b7d675b5df42c35c00d1
|
|||||||
F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767
|
F test/tkt3935.test e15261fedb9e30a4305a311da614a5d8e693c767
|
||||||
F test/tkt3992.test c193b9643b1c25d020c503a986d5e4089e65c530
|
F test/tkt3992.test c193b9643b1c25d020c503a986d5e4089e65c530
|
||||||
F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
F test/tkt3997.test a335fa41ca3985660a139df7b734a26ef53284bd
|
||||||
|
F test/tkt4018.test f581cf52dc359171875cb649bdc38b525d7b9309
|
||||||
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
F test/tokenize.test ce430a7aed48fc98301611429595883fdfcab5d7
|
||||||
F test/trace.test 19ffbc09885c3321d56358a5738feae8587fb377
|
F test/trace.test 19ffbc09885c3321d56358a5738feae8587fb377
|
||||||
F test/trans.test d887cb07630dc39879a322d958ad8b006137485c
|
F test/trans.test d887cb07630dc39879a322d958ad8b006137485c
|
||||||
@@ -750,14 +748,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
|
|||||||
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
|
||||||
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
|
||||||
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
|
F tool/vdbe-compress.tcl 672f81d693a03f80f5ae60bfefacd8a349e76746
|
||||||
P 11a669b6537d6bac67764fd91a319234345ac504
|
P 75f596a04a74eb3a538c7be5b41756c970a21a1b
|
||||||
R 94e34ada24b7bc67fb34c394a020d935
|
R a2800e2c5932867798528426b058f0c9
|
||||||
U drh
|
U dan
|
||||||
Z 7ccc9d752ad0ca6d6e2c43e814512aef
|
Z 0551703c5a252ef4437a61cdae64c5ea
|
||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
Version: GnuPG v1.4.6 (GNU/Linux)
|
|
||||||
|
|
||||||
iD8DBQFKjp+UoxKgR168RlERAk1eAKCAALyYlvahPmzq/BF+Xyh+nb94XwCfazBH
|
|
||||||
BpbVoKhuQkjDxksK+/ovr6g=
|
|
||||||
=rFJ2
|
|
||||||
-----END PGP SIGNATURE-----
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
75f596a04a74eb3a538c7be5b41756c970a21a1b
|
9b4d9ab62d687289837b13b07885e72cc3abe8a9
|
||||||
273
src/os_unix.c
273
src/os_unix.c
@@ -181,6 +181,7 @@ struct unixFile {
|
|||||||
unsigned char locktype; /* The type of lock held on this fd */
|
unsigned char locktype; /* The type of lock held on this fd */
|
||||||
int lastErrno; /* The unix errno from the last I/O error */
|
int lastErrno; /* The unix errno from the last I/O error */
|
||||||
void *lockingContext; /* Locking style specific state */
|
void *lockingContext; /* Locking style specific state */
|
||||||
|
int flags; /* Flags value returned by xOpen() */
|
||||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||||
int openFlags; /* The flags specified at open() */
|
int openFlags; /* The flags specified at open() */
|
||||||
#endif
|
#endif
|
||||||
@@ -202,11 +203,6 @@ struct unixFile {
|
|||||||
unsigned char transCntrChng; /* True if the transaction counter changed */
|
unsigned char transCntrChng; /* True if the transaction counter changed */
|
||||||
unsigned char dbUpdate; /* True if any part of database file changed */
|
unsigned char dbUpdate; /* True if any part of database file changed */
|
||||||
unsigned char inNormalWrite; /* True if in a normal write operation */
|
unsigned char inNormalWrite; /* True if in a normal write operation */
|
||||||
|
|
||||||
/* If true, that means we are dealing with a database file that has
|
|
||||||
** a range of locking bytes from PENDING_BYTE through PENDING_BYTE+511
|
|
||||||
** which should never be read or written. Asserts() will verify this */
|
|
||||||
unsigned char isLockable; /* True if file might be locked */
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef SQLITE_TEST
|
#ifdef SQLITE_TEST
|
||||||
/* In test mode, increase the size of this structure a bit so that
|
/* In test mode, increase the size of this structure a bit so that
|
||||||
@@ -753,7 +749,10 @@ struct unixOpenCnt {
|
|||||||
int nRef; /* Number of pointers to this structure */
|
int nRef; /* Number of pointers to this structure */
|
||||||
int nLock; /* Number of outstanding locks */
|
int nLock; /* Number of outstanding locks */
|
||||||
int nPending; /* Number of pending close() operations */
|
int nPending; /* Number of pending close() operations */
|
||||||
int *aPending; /* Malloced space holding fds awaiting close() */
|
struct PendingClose {
|
||||||
|
int fd; /* File descriptor to close */
|
||||||
|
int flags; /* Flags this file descriptor was opened with */
|
||||||
|
} *aPending; /* Malloced space holding fds awaiting close() */
|
||||||
#if OS_VXWORKS
|
#if OS_VXWORKS
|
||||||
sem_t *pSem; /* Named POSIX semaphore */
|
sem_t *pSem; /* Named POSIX semaphore */
|
||||||
char aSemName[MAX_PATHNAME+1]; /* Name of that semaphore */
|
char aSemName[MAX_PATHNAME+1]; /* Name of that semaphore */
|
||||||
@@ -1405,6 +1404,60 @@ end_lock:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Close all file descriptors accumuated in the p->aPending[] array. If
|
||||||
|
** all such file descriptors are closed without error, the aPending[]
|
||||||
|
** array is deleted and SQLITE_OK returned.
|
||||||
|
**
|
||||||
|
** Otherwise, if an error occurs, then successfully closed file descriptor
|
||||||
|
** entries in the aPending[] array are set to -1, the aPending[] array
|
||||||
|
** not deleted and SQLITE_IOERR_CLOSE returned.
|
||||||
|
*/
|
||||||
|
static int closePendingFds(unixFile *pFile){
|
||||||
|
struct unixOpenCnt *pOpen = pFile->pOpen;
|
||||||
|
struct PendingClose *aPending = pOpen->aPending;
|
||||||
|
int i;
|
||||||
|
int rc = SQLITE_OK;
|
||||||
|
assert( unixMutexHeld() );
|
||||||
|
for(i=0; i<pOpen->nPending; i++){
|
||||||
|
if( aPending[i].fd>=0 ){
|
||||||
|
if( close(aPending[i].fd) ){
|
||||||
|
pFile->lastErrno = errno;
|
||||||
|
rc = SQLITE_IOERR_CLOSE;
|
||||||
|
}else{
|
||||||
|
aPending[i].fd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( rc==SQLITE_OK ){
|
||||||
|
sqlite3_free(aPending);
|
||||||
|
pOpen->nPending = 0;
|
||||||
|
pOpen->aPending = 0;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Add the file descriptor used by file handle pFile to the corresponding
|
||||||
|
** aPending[] array to be closed after some other connection releases
|
||||||
|
** a lock.
|
||||||
|
*/
|
||||||
|
static void setPendingFd(unixFile *pFile){
|
||||||
|
struct PendingClose *aNew;
|
||||||
|
struct unixOpenCnt *pOpen = pFile->pOpen;
|
||||||
|
int nByte = (pOpen->nPending+1)*sizeof(pOpen->aPending[0]);
|
||||||
|
aNew = sqlite3_realloc(pOpen->aPending, nByte);
|
||||||
|
if( aNew==0 ){
|
||||||
|
/* If a malloc fails, just leak the file descriptor */
|
||||||
|
}else{
|
||||||
|
pOpen->aPending = aNew;
|
||||||
|
pOpen->aPending[pOpen->nPending].fd = pFile->h;
|
||||||
|
pOpen->aPending[pOpen->nPending].flags = pFile->flags;
|
||||||
|
pOpen->nPending++;
|
||||||
|
pFile->h = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Lower the locking level on file descriptor pFile to locktype. locktype
|
** Lower the locking level on file descriptor pFile to locktype. locktype
|
||||||
** must be either NO_LOCK or SHARED_LOCK.
|
** must be either NO_LOCK or SHARED_LOCK.
|
||||||
@@ -1487,7 +1540,6 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
|||||||
}
|
}
|
||||||
if( locktype==NO_LOCK ){
|
if( locktype==NO_LOCK ){
|
||||||
struct unixOpenCnt *pOpen;
|
struct unixOpenCnt *pOpen;
|
||||||
int rc2 = SQLITE_OK;
|
|
||||||
|
|
||||||
/* Decrement the shared lock counter. Release the lock using an
|
/* Decrement the shared lock counter. Release the lock using an
|
||||||
** OS call only when all threads in this same process have released
|
** OS call only when all threads in this same process have released
|
||||||
@@ -1522,29 +1574,12 @@ static int unixUnlock(sqlite3_file *id, int locktype){
|
|||||||
pOpen->nLock--;
|
pOpen->nLock--;
|
||||||
assert( pOpen->nLock>=0 );
|
assert( pOpen->nLock>=0 );
|
||||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||||
int i;
|
int rc2 = closePendingFds(pFile);
|
||||||
for(i=0; i<pOpen->nPending; i++){
|
|
||||||
/* close pending fds, but if closing fails don't free the array
|
|
||||||
** assign -1 to the successfully closed descriptors and record the
|
|
||||||
** error. The next attempt to unlock will try again. */
|
|
||||||
if( pOpen->aPending[i] < 0 ) continue;
|
|
||||||
if( close(pOpen->aPending[i]) ){
|
|
||||||
pFile->lastErrno = errno;
|
|
||||||
rc2 = SQLITE_IOERR_CLOSE;
|
|
||||||
}else{
|
|
||||||
pOpen->aPending[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( rc2==SQLITE_OK ){
|
|
||||||
sqlite3_free(pOpen->aPending);
|
|
||||||
pOpen->nPending = 0;
|
|
||||||
pOpen->aPending = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( rc==SQLITE_OK ){
|
if( rc==SQLITE_OK ){
|
||||||
rc = rc2;
|
rc = rc2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
end_unlock:
|
end_unlock:
|
||||||
unixLeaveMutex();
|
unixLeaveMutex();
|
||||||
@@ -1612,17 +1647,7 @@ static int unixClose(sqlite3_file *id){
|
|||||||
** descriptor to pOpen->aPending. It will be automatically closed when
|
** descriptor to pOpen->aPending. It will be automatically closed when
|
||||||
** the last lock is cleared.
|
** the last lock is cleared.
|
||||||
*/
|
*/
|
||||||
int *aNew;
|
setPendingFd(pFile);
|
||||||
struct unixOpenCnt *pOpen = pFile->pOpen;
|
|
||||||
aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
|
|
||||||
if( aNew==0 ){
|
|
||||||
/* If a malloc fails, just leak the file descriptor */
|
|
||||||
}else{
|
|
||||||
pOpen->aPending = aNew;
|
|
||||||
pOpen->aPending[pOpen->nPending] = pFile->h;
|
|
||||||
pOpen->nPending++;
|
|
||||||
pFile->h = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
releaseLockInfo(pFile->pLock);
|
releaseLockInfo(pFile->pLock);
|
||||||
releaseOpenCnt(pFile->pOpen);
|
releaseOpenCnt(pFile->pOpen);
|
||||||
@@ -2592,26 +2617,14 @@ static int afpUnlock(sqlite3_file *id, int locktype) {
|
|||||||
pOpen->nLock--;
|
pOpen->nLock--;
|
||||||
assert( pOpen->nLock>=0 );
|
assert( pOpen->nLock>=0 );
|
||||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||||
int i;
|
rc = closePendingFds(pFile);
|
||||||
for(i=0; i<pOpen->nPending; i++){
|
|
||||||
if( pOpen->aPending[i] < 0 ) continue;
|
|
||||||
if( close(pOpen->aPending[i]) ){
|
|
||||||
pFile->lastErrno = errno;
|
|
||||||
rc = SQLITE_IOERR_CLOSE;
|
|
||||||
}else{
|
|
||||||
pOpen->aPending[i] = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if( rc==SQLITE_OK ){
|
|
||||||
sqlite3_free(pOpen->aPending);
|
|
||||||
pOpen->nPending = 0;
|
|
||||||
pOpen->aPending = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unixLeaveMutex();
|
unixLeaveMutex();
|
||||||
if( rc==SQLITE_OK ) pFile->locktype = locktype;
|
if( rc==SQLITE_OK ){
|
||||||
|
pFile->locktype = locktype;
|
||||||
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2629,17 +2642,7 @@ static int afpClose(sqlite3_file *id) {
|
|||||||
** descriptor to pOpen->aPending. It will be automatically closed when
|
** descriptor to pOpen->aPending. It will be automatically closed when
|
||||||
** the last lock is cleared.
|
** the last lock is cleared.
|
||||||
*/
|
*/
|
||||||
int *aNew;
|
setPendingFd(pFile);
|
||||||
struct unixOpenCnt *pOpen = pFile->pOpen;
|
|
||||||
aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) );
|
|
||||||
if( aNew==0 ){
|
|
||||||
/* If a malloc fails, just leak the file descriptor */
|
|
||||||
}else{
|
|
||||||
pOpen->aPending = aNew;
|
|
||||||
pOpen->aPending[pOpen->nPending] = pFile->h;
|
|
||||||
pOpen->nPending++;
|
|
||||||
pFile->h = -1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
releaseOpenCnt(pFile->pOpen);
|
releaseOpenCnt(pFile->pOpen);
|
||||||
sqlite3_free(pFile->lockingContext);
|
sqlite3_free(pFile->lockingContext);
|
||||||
@@ -2725,22 +2728,25 @@ static int unixRead(
|
|||||||
int amt,
|
int amt,
|
||||||
sqlite3_int64 offset
|
sqlite3_int64 offset
|
||||||
){
|
){
|
||||||
|
unixFile *pFile = (unixFile *)id;
|
||||||
int got;
|
int got;
|
||||||
assert( id );
|
assert( id );
|
||||||
|
|
||||||
/* Never read or write any of the bytes in the locking range */
|
/* If this is a database file (not a journal, master-journal or temp
|
||||||
assert( ((unixFile*)id)->isLockable==0
|
** file), the bytes in the locking range should never be read or written. */
|
||||||
|
assert( (pFile->flags&SQLITE_OPEN_MAIN_DB)==0
|
||||||
|| offset>=PENDING_BYTE+512
|
|| offset>=PENDING_BYTE+512
|
||||||
|| offset+amt<=PENDING_BYTE );
|
|| offset+amt<=PENDING_BYTE
|
||||||
|
);
|
||||||
|
|
||||||
got = seekAndRead((unixFile*)id, offset, pBuf, amt);
|
got = seekAndRead(pFile, offset, pBuf, amt);
|
||||||
if( got==amt ){
|
if( got==amt ){
|
||||||
return SQLITE_OK;
|
return SQLITE_OK;
|
||||||
}else if( got<0 ){
|
}else if( got<0 ){
|
||||||
/* lastErrno set by seekAndRead */
|
/* lastErrno set by seekAndRead */
|
||||||
return SQLITE_IOERR_READ;
|
return SQLITE_IOERR_READ;
|
||||||
}else{
|
}else{
|
||||||
((unixFile*)id)->lastErrno = 0; /* not a system error */
|
pFile->lastErrno = 0; /* not a system error */
|
||||||
/* Unread parts of the buffer must be zero-filled */
|
/* Unread parts of the buffer must be zero-filled */
|
||||||
memset(&((char*)pBuf)[got], 0, amt-got);
|
memset(&((char*)pBuf)[got], 0, amt-got);
|
||||||
return SQLITE_IOERR_SHORT_READ;
|
return SQLITE_IOERR_SHORT_READ;
|
||||||
@@ -2794,14 +2800,17 @@ static int unixWrite(
|
|||||||
int amt,
|
int amt,
|
||||||
sqlite3_int64 offset
|
sqlite3_int64 offset
|
||||||
){
|
){
|
||||||
|
unixFile *pFile = (unixFile*)id;
|
||||||
int wrote = 0;
|
int wrote = 0;
|
||||||
assert( id );
|
assert( id );
|
||||||
assert( amt>0 );
|
assert( amt>0 );
|
||||||
|
|
||||||
/* Never read or write any of the bytes in the locking range */
|
/* If this is a database file (not a journal, master-journal or temp
|
||||||
assert( ((unixFile*)id)->isLockable==0
|
** file), the bytes in the locking range should never be read or written. */
|
||||||
|
assert( (pFile->flags&SQLITE_OPEN_MAIN_DB)==0
|
||||||
|| offset>=PENDING_BYTE+512
|
|| offset>=PENDING_BYTE+512
|
||||||
|| offset+amt<=PENDING_BYTE );
|
|| offset+amt<=PENDING_BYTE
|
||||||
|
);
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
/* If we are doing a normal write to a database file (as opposed to
|
/* If we are doing a normal write to a database file (as opposed to
|
||||||
@@ -2810,8 +2819,7 @@ static int unixWrite(
|
|||||||
** has changed. If the transaction counter is modified, record that
|
** has changed. If the transaction counter is modified, record that
|
||||||
** fact too.
|
** fact too.
|
||||||
*/
|
*/
|
||||||
if( ((unixFile*)id)->inNormalWrite ){
|
if( pFile->inNormalWrite ){
|
||||||
unixFile *pFile = (unixFile*)id;
|
|
||||||
pFile->dbUpdate = 1; /* The database has been modified */
|
pFile->dbUpdate = 1; /* The database has been modified */
|
||||||
if( offset<=24 && offset+amt>=27 ){
|
if( offset<=24 && offset+amt>=27 ){
|
||||||
int rc;
|
int rc;
|
||||||
@@ -2826,7 +2834,7 @@ static int unixWrite(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while( amt>0 && (wrote = seekAndWrite((unixFile*)id, offset, pBuf, amt))>0 ){
|
while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){
|
||||||
amt -= wrote;
|
amt -= wrote;
|
||||||
offset += wrote;
|
offset += wrote;
|
||||||
pBuf = &((char*)pBuf)[wrote];
|
pBuf = &((char*)pBuf)[wrote];
|
||||||
@@ -2838,7 +2846,7 @@ static int unixWrite(
|
|||||||
/* lastErrno set by seekAndWrite */
|
/* lastErrno set by seekAndWrite */
|
||||||
return SQLITE_IOERR_WRITE;
|
return SQLITE_IOERR_WRITE;
|
||||||
}else{
|
}else{
|
||||||
((unixFile*)id)->lastErrno = 0; /* not a system error */
|
pFile->lastErrno = 0; /* not a system error */
|
||||||
return SQLITE_FULL;
|
return SQLITE_FULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3650,6 +3658,58 @@ static int getTempname(int nBuf, char *zBuf){
|
|||||||
static int proxyTransformUnixFile(unixFile*, const char*);
|
static int proxyTransformUnixFile(unixFile*, const char*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Search for an unused file descriptor that was opened on the database
|
||||||
|
** file (not a journal or master-journal file) identified by pathname
|
||||||
|
** zPath with SQLITE_OPEN_XXX flags matching those passed as the second
|
||||||
|
** argument to this function.
|
||||||
|
**
|
||||||
|
** Such a file descriptor may exist if a database connection was closed
|
||||||
|
** but the associated file descriptor could not be closed because some
|
||||||
|
** other file descriptor open on the same file is holding a file-lock.
|
||||||
|
** Refer to comments in the unixClose() function and the lengthy comment
|
||||||
|
** describing "Posix Advisory Locking" at the start of this file for
|
||||||
|
** further details. Also, ticket #4018.
|
||||||
|
**
|
||||||
|
** If a suitable file descriptor is found, then it is returned. If no
|
||||||
|
** such file descriptor is located, -1 is returned.
|
||||||
|
*/
|
||||||
|
static int findReusableFd(const char *zPath, int flags){
|
||||||
|
int fd = -1; /* Return value */
|
||||||
|
struct stat sStat; /* Results of stat() call */
|
||||||
|
|
||||||
|
/* A stat() call may fail for various reasons. If this happens, it is
|
||||||
|
** almost certain that an open() call on the same path will also fail.
|
||||||
|
** For this reason, if an error occurs in the stat() call here, it is
|
||||||
|
** ignored and -1 is returned. The caller will try to open a new file
|
||||||
|
** descriptor on the same path, fail, and return an error to SQLite.
|
||||||
|
**
|
||||||
|
** Even if a subsequent open() call does succeed, the consequences of
|
||||||
|
** not searching for a resusable file descriptor are not dire. */
|
||||||
|
if( 0==stat(zPath, &sStat) ){
|
||||||
|
struct unixOpenCnt *p;
|
||||||
|
struct unixFileId id;
|
||||||
|
id.dev = sStat.st_dev;
|
||||||
|
id.ino = sStat.st_ino;
|
||||||
|
|
||||||
|
unixEnterMutex();
|
||||||
|
for(p=openList; p&& memcmp(&id, &p->fileId, sizeof(id)); p=p->pNext);
|
||||||
|
if( p && p->aPending ){
|
||||||
|
int i;
|
||||||
|
struct PendingClose *aPending = p->aPending;
|
||||||
|
for(i=0; i<p->nPending; i++){
|
||||||
|
if( aPending[i].fd>=0 && flags==aPending[i].flags ){
|
||||||
|
fd = aPending[i].fd;
|
||||||
|
aPending[i].fd = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unixLeaveMutex();
|
||||||
|
}
|
||||||
|
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Open the file zPath.
|
** Open the file zPath.
|
||||||
@@ -3680,12 +3740,13 @@ static int unixOpen(
|
|||||||
int flags, /* Input flags to control the opening */
|
int flags, /* Input flags to control the opening */
|
||||||
int *pOutFlags /* Output flags returned to SQLite core */
|
int *pOutFlags /* Output flags returned to SQLite core */
|
||||||
){
|
){
|
||||||
|
unixFile *p = (unixFile *)pFile;
|
||||||
int fd = -1; /* File descriptor returned by open() */
|
int fd = -1; /* File descriptor returned by open() */
|
||||||
int dirfd = -1; /* Directory file descriptor */
|
int dirfd = -1; /* Directory file descriptor */
|
||||||
int openFlags = 0; /* Flags to pass to open() */
|
int openFlags = 0; /* Flags to pass to open() */
|
||||||
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
int eType = flags&0xFFFFFF00; /* Type of file to open */
|
||||||
int noLock; /* True to omit locking primitives */
|
int noLock; /* True to omit locking primitives */
|
||||||
int rc = SQLITE_OK;
|
int rc = SQLITE_OK; /* Function Return Code */
|
||||||
|
|
||||||
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
|
int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE);
|
||||||
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
|
int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE);
|
||||||
@@ -3720,11 +3781,10 @@ static int unixOpen(
|
|||||||
assert(isDelete==0 || isCreate);
|
assert(isDelete==0 || isCreate);
|
||||||
|
|
||||||
/* The main DB, main journal, and master journal are never automatically
|
/* The main DB, main journal, and master journal are never automatically
|
||||||
** deleted
|
** deleted. Nor are they ever temporary files. */
|
||||||
*/
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB );
|
||||||
assert( eType!=SQLITE_OPEN_MAIN_DB || !isDelete );
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL );
|
||||||
assert( eType!=SQLITE_OPEN_MAIN_JOURNAL || !isDelete );
|
assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL );
|
||||||
assert( eType!=SQLITE_OPEN_MASTER_JOURNAL || !isDelete );
|
|
||||||
|
|
||||||
/* Assert that the upper layer has set one of the "file-type" flags. */
|
/* Assert that the upper layer has set one of the "file-type" flags. */
|
||||||
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|
||||||
@@ -3733,9 +3793,19 @@ static int unixOpen(
|
|||||||
|| eType==SQLITE_OPEN_TRANSIENT_DB
|
|| eType==SQLITE_OPEN_TRANSIENT_DB
|
||||||
);
|
);
|
||||||
|
|
||||||
memset(pFile, 0, sizeof(unixFile));
|
memset(p, 0, sizeof(unixFile));
|
||||||
|
|
||||||
if( !zName ){
|
if( eType==SQLITE_OPEN_MAIN_DB ){
|
||||||
|
/* Try to find an unused file descriptor to reuse. This is not done
|
||||||
|
** for vxworks. Not because vxworks would not benefit from the change
|
||||||
|
** (it might, we're not sure), but because no way to test it is
|
||||||
|
** currently available. It is better not to risk breaking vxworks for
|
||||||
|
** the sake of such an obscure feature. */
|
||||||
|
#if !OS_VXWORKS
|
||||||
|
fd = findReusableFd(zName, flags);
|
||||||
|
#endif
|
||||||
|
}else if( !zName ){
|
||||||
|
/* If zName is NULL, the upper layer is requesting a temp file. */
|
||||||
assert(isDelete && !isOpenDirectory);
|
assert(isDelete && !isOpenDirectory);
|
||||||
rc = getTempname(MAX_PATHNAME+1, zTmpname);
|
rc = getTempname(MAX_PATHNAME+1, zTmpname);
|
||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
@@ -3744,12 +3814,17 @@ static int unixOpen(
|
|||||||
zName = zTmpname;
|
zName = zTmpname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Determine the value of the flags parameter passed to POSIX function
|
||||||
|
** open(). These must be calculated even if open() is not called, as
|
||||||
|
** they may be stored as part of the file handle and used by the
|
||||||
|
** 'conch file' locking functions later on. */
|
||||||
if( isReadonly ) openFlags |= O_RDONLY;
|
if( isReadonly ) openFlags |= O_RDONLY;
|
||||||
if( isReadWrite ) openFlags |= O_RDWR;
|
if( isReadWrite ) openFlags |= O_RDWR;
|
||||||
if( isCreate ) openFlags |= O_CREAT;
|
if( isCreate ) openFlags |= O_CREAT;
|
||||||
if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
|
if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW);
|
||||||
openFlags |= (O_LARGEFILE|O_BINARY);
|
openFlags |= (O_LARGEFILE|O_BINARY);
|
||||||
|
|
||||||
|
if( fd<0 ){
|
||||||
fd = open(zName, openFlags, isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS);
|
fd = open(zName, openFlags, isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS);
|
||||||
OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags);
|
OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags);
|
||||||
if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
|
if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){
|
||||||
@@ -3761,6 +3836,13 @@ static int unixOpen(
|
|||||||
if( fd<0 ){
|
if( fd<0 ){
|
||||||
return SQLITE_CANTOPEN;
|
return SQLITE_CANTOPEN;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
assert( fd>=0 );
|
||||||
|
p->flags = flags;
|
||||||
|
if( pOutFlags ){
|
||||||
|
*pOutFlags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
if( isDelete ){
|
if( isDelete ){
|
||||||
#if OS_VXWORKS
|
#if OS_VXWORKS
|
||||||
zPath = zName;
|
zPath = zName;
|
||||||
@@ -3770,23 +3852,18 @@ static int unixOpen(
|
|||||||
}
|
}
|
||||||
#if SQLITE_ENABLE_LOCKING_STYLE
|
#if SQLITE_ENABLE_LOCKING_STYLE
|
||||||
else{
|
else{
|
||||||
((unixFile*)pFile)->openFlags = openFlags;
|
p->openFlags = openFlags;
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if( pOutFlags ){
|
|
||||||
*pOutFlags = flags;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
|
||||||
if( (flags & SQLITE_OPEN_MAIN_DB)!=0 ){
|
|
||||||
((unixFile*)pFile)->isLockable = 1;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
assert( fd>=0 );
|
|
||||||
if( isOpenDirectory ){
|
if( isOpenDirectory ){
|
||||||
rc = openDirectory(zPath, &dirfd);
|
rc = openDirectory(zPath, &dirfd);
|
||||||
if( rc!=SQLITE_OK ){
|
if( rc!=SQLITE_OK ){
|
||||||
|
/* It is safe to close fd at this point, because it is guaranteed not
|
||||||
|
** to be open on a database file. If it were open on a database file,
|
||||||
|
** it would not be safe to close as this would cause any locks held
|
||||||
|
** on the file by this process to be released. */
|
||||||
|
assert( eType!=SQLITE_OPEN_MAIN_DB );
|
||||||
close(fd); /* silently leak if fail, already in error */
|
close(fd); /* silently leak if fail, already in error */
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -3803,14 +3880,12 @@ static int unixOpen(
|
|||||||
char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
|
char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING");
|
||||||
int useProxy = 0;
|
int useProxy = 0;
|
||||||
|
|
||||||
/* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy,
|
/* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means
|
||||||
** 0 means never use proxy, NULL means use proxy for non-local files only
|
** never use proxy, NULL means use proxy for non-local files only. */
|
||||||
*/
|
|
||||||
if( envforce!=NULL ){
|
if( envforce!=NULL ){
|
||||||
useProxy = atoi(envforce)>0;
|
useProxy = atoi(envforce)>0;
|
||||||
}else{
|
}else{
|
||||||
struct statfs fsInfo;
|
struct statfs fsInfo;
|
||||||
|
|
||||||
if( statfs(zPath, &fsInfo) == -1 ){
|
if( statfs(zPath, &fsInfo) == -1 ){
|
||||||
((unixFile*)pFile)->lastErrno = errno;
|
((unixFile*)pFile)->lastErrno = errno;
|
||||||
if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
|
if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */
|
||||||
|
|||||||
89
test/tkt4018.test
Normal file
89
test/tkt4018.test
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
# 2009 August 20
|
||||||
|
#
|
||||||
|
# The author disclaims copyright to this source code. In place of
|
||||||
|
# a legal notice, here is a blessing:
|
||||||
|
#
|
||||||
|
# May you do good and not evil.
|
||||||
|
# May you find forgiveness for yourself and forgive others.
|
||||||
|
# May you share freely, never taking more than you give.
|
||||||
|
#
|
||||||
|
#***********************************************************************
|
||||||
|
# This file implements regression tests for SQLite library.
|
||||||
|
#
|
||||||
|
# This file implements tests to verify that ticket #4018 has been
|
||||||
|
# fixed.
|
||||||
|
#
|
||||||
|
|
||||||
|
set testdir [file dirname $argv0]
|
||||||
|
source $testdir/tester.tcl
|
||||||
|
|
||||||
|
proc testsql {sql} {
|
||||||
|
set fd [open tf_main.tcl w]
|
||||||
|
puts $fd [subst -nocommands {
|
||||||
|
sqlite3_test_control_pending_byte 0x0010000
|
||||||
|
sqlite3 db test.db
|
||||||
|
set rc [catch { db eval {$sql} } msg]
|
||||||
|
puts -nonewline "[set rc] {[set msg]}"
|
||||||
|
flush stdout
|
||||||
|
exit
|
||||||
|
}]
|
||||||
|
close $fd
|
||||||
|
set fd [open "| [info nameofexec] ./tf_main.tcl" r]
|
||||||
|
set res [read $fd]
|
||||||
|
close $fd
|
||||||
|
return $res
|
||||||
|
}
|
||||||
|
|
||||||
|
do_test tkt4018-1.1 {
|
||||||
|
execsql {
|
||||||
|
CREATE TABLE t1(a, b);
|
||||||
|
BEGIN;
|
||||||
|
SELECT * FROM t1;
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
# The database is locked by connection [db]. Open and close a second
|
||||||
|
# connection to test.db 20000 times. If file-descriptors are not being
|
||||||
|
# reused, then the process will quickly exceed its maximum number of
|
||||||
|
# file descriptors (1024 by default on linux).
|
||||||
|
do_test tkt4018-1.2 {
|
||||||
|
for {set i 0} {$i < 10000} {incr i} {
|
||||||
|
sqlite3 db2 test.db
|
||||||
|
db2 close
|
||||||
|
}
|
||||||
|
} {}
|
||||||
|
|
||||||
|
# Now check that connection [db] is still holding a SHARED lock by
|
||||||
|
# having a second process try to write the db.
|
||||||
|
do_test tkt4018-1.3 {
|
||||||
|
testsql {INSERT INTO t1 VALUES(3, 4)}
|
||||||
|
} {1 {database is locked}}
|
||||||
|
|
||||||
|
# Sanity checking. Have [db] release the lock and then retry the
|
||||||
|
# INSERT from the previous test case.
|
||||||
|
do_test tkt4018-1.4 {
|
||||||
|
db eval COMMIT
|
||||||
|
testsql {INSERT INTO t1 VALUES(3, 4)}
|
||||||
|
} {0 {}}
|
||||||
|
|
||||||
|
# Check that reusing a file descriptor cannot change a read-only
|
||||||
|
# connection into a read-write connection.
|
||||||
|
do_test tkt4018-2.1 {
|
||||||
|
sqlite3 db2 test.db
|
||||||
|
execsql {INSERT INTO t1 VALUES(1, 2)} db2
|
||||||
|
} {}
|
||||||
|
do_test tkt4018-2.2 {
|
||||||
|
execsql {
|
||||||
|
BEGIN;
|
||||||
|
SELECT * FROM t1 ORDER BY a;
|
||||||
|
}
|
||||||
|
} {1 2 3 4}
|
||||||
|
do_test tkt4018-2.3 {
|
||||||
|
db2 close
|
||||||
|
sqlite3 db2 test.db -readonly 1
|
||||||
|
execsql COMMIT
|
||||||
|
catchsql {INSERT INTO t1 VALUES(5, 6)} db2
|
||||||
|
} {1 {attempt to write a readonly database}}
|
||||||
|
db2 close
|
||||||
|
|
||||||
|
finish_test
|
||||||
Reference in New Issue
Block a user