mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-07 02:42:48 +03:00
Fixes to the file locking. 109 tests are now failing. (CVS 1548)
FossilOrigin-Name: dc0763455bbf54c1d8728e16033709caedd6e1c6
This commit is contained in:
32
manifest
32
manifest
@@ -1,5 +1,5 @@
|
||||
C Add\sthe\s"lock_status"\spragma\s-\sonly\savailable\swhen\sSQLITE_DEBUG\sis\sdefined.\nUsed\sfor\stesting\sonly.\s(CVS\s1547)
|
||||
D 2004-06-09T14:17:21
|
||||
C Fixes\sto\sthe\sfile\slocking.\s\s109\stests\sare\snow\sfailing.\s(CVS\s1548)
|
||||
D 2004-06-09T17:37:23
|
||||
F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
|
||||
F Makefile.linux-gcc a9e5a0d309fa7c38e7c14d3ecf7690879d3a5457
|
||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||
@@ -25,7 +25,7 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f
|
||||
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
||||
F src/attach.c 93b8ecec4a8d7b4e9f2479e2327d90c9d01765e8
|
||||
F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79
|
||||
F src/btree.c edb38affc2e83f4299e49104cfe14e6570d8bd32
|
||||
F src/btree.c 7526d697cad3fbf731a9238d047337581d19b43b
|
||||
F src/btree.h 589427ac13bb544d298cd99726e2572a6fe4bdaa
|
||||
F src/build.c 4ea78aba171f02b96254dd7a312e4266d3693bfe
|
||||
F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2
|
||||
@@ -39,16 +39,16 @@ F src/insert.c 4268d9e3959cc845ea243fb4ec7507269404dad9
|
||||
F src/legacy.c ad23746f15f67e34577621b1875f639c94839e1f
|
||||
F src/main.c 8a7725b40fbe645883b2162aee782ad1063435b4
|
||||
F src/md5.c 4302e84ae516c616bb079c4e6d038c0addb33481
|
||||
F src/os.h a3a9c2df29acbff54aef742e85c302d23634019f
|
||||
F src/os_common.h 12074232439f904b3805beeff1245bd1b5532994
|
||||
F src/os.h 23c69c5084e71b5fe199ff1c4e35a4aded0f1380
|
||||
F src/os_common.h 6393ac67a3a7b4aea19ff17529980ecf77eb2348
|
||||
F src/os_mac.c b823874690615ace0dd520d3ad1fe8bfd864b7e0
|
||||
F src/os_mac.h 51d2445f47e182ed32d3bd6937f81070c6fd9bd4
|
||||
F src/os_unix.c 3747274f2712e95f605c0ec66df5ad26e3711e82
|
||||
F src/os_unix.h 7999f2246c6347707e98f7078871ea8ca605df3f
|
||||
F src/os_win.c a13b85a0d4889e3d0b254ed2a61354acddc59fc4
|
||||
F src/os_unix.c 1d6f3d1a87d4aa0e4490bcc47b3f0ff9b2e37e7a
|
||||
F src/os_unix.h 1cd6133cf66dea704b8646b70b2dfdcbdd9b3738
|
||||
F src/os_win.c 6b8f9fcc683bb888e07fc485372803baa68faadb
|
||||
F src/os_win.h 004eec47b1780fcaf07420ddc2072294b698d48c
|
||||
F src/pager.c c483bef234b89531f9fdf6067231d248260d762b
|
||||
F src/pager.h 3576e5f1a0719f74df3082df73f7b39890b06654
|
||||
F src/pager.c ee2e2fa71584ce402e0266b342f96efbf74cb45f
|
||||
F src/pager.h ca8f293e1d623a7c628a1c5e0c6cf43d5bbb80bf
|
||||
F src/parse.y 097438674976355a10cf177bd97326c548820b86
|
||||
F src/pragma.c 3251628662963f30a95133b19d59e4bc08226c76
|
||||
F src/printf.c 63b15f1ea9fe3daa066bb7430fd20d4a2d717dc8
|
||||
@@ -59,7 +59,7 @@ F src/sqlite.h.in 6ad05abc7fd72da74691e1eb45f0eff4117eba4e
|
||||
F src/sqliteInt.h c0a328c7567cdeebe14d7a4668f22946e281ebc9
|
||||
F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2
|
||||
F src/tclsqlite.c f5c5116720baefb7de5d6acf18baedf1e42756cc
|
||||
F src/test1.c fa01e3db097df48963e29cec66f2e080735767d4
|
||||
F src/test1.c f78d6ac0675bc5db48dac9c5379c965bdadb9113
|
||||
F src/test2.c ae18537d8a85e5028c955837797f9da461b908b8
|
||||
F src/test3.c beafd0ccf7b9ae784744be1b1e66ffe8f64c25da
|
||||
F src/test4.c a921a69821fd30209589228e64f94e9f715b6fe2
|
||||
@@ -74,12 +74,12 @@ F src/vdbe.c fec13be8b6f03158bfb3069c7bd6182eb3ef4fca
|
||||
F src/vdbe.h 46f74444a213129bc4b5ce40124dd8ed613b0cde
|
||||
F src/vdbeInt.h ab592f23ed5a1913f9a506bd7b76c5e39377942a
|
||||
F src/vdbeapi.c 4ac95766b0515538037a7aec172ed26142f97cf9
|
||||
F src/vdbeaux.c 136442a40335974a5088dbd684a2168d4d0fec9a
|
||||
F src/vdbeaux.c 5efecdafe745a90e7c5173403d0065bdd38395a7
|
||||
F src/vdbemem.c 04502b81039f9a2b1c9a096e894eecf6d4877508
|
||||
F src/where.c 32578882a245f8ac3303c5cea4664cd51fc73891
|
||||
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
||||
F test/attach.test aed659e52635662bcd5069599aaca823533edf5a
|
||||
F test/attach2.test fe8480cd1aecbe393f2ae60a92c4f60734d89b49
|
||||
F test/attach2.test 228eaca954ddb850c35fba7bdc39aac2ab0c80c0
|
||||
F test/attach3.test 8259ab833b5dcdf4acd75d9653f42f703ce2e013
|
||||
F test/auth.test 95809b8f6a9bec18b94d28cafd03fe27d2f8a9e9
|
||||
F test/bigfile.test ea904b853ce2d703b16c5ce90e2b54951bc1ae81
|
||||
@@ -218,7 +218,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248
|
||||
F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075
|
||||
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
|
||||
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
|
||||
P 428b685b7174ef4589176def1028ad1c9461ff7e
|
||||
R b811ffa94f6a9084cf35549f784cc75b
|
||||
P 0ecbba78fcde8f7715cd74c674b5040ef4953f6e
|
||||
R 81671c9563d17cd7ee532f949a6e58e5
|
||||
U drh
|
||||
Z 897e459988003c5f46676db9047d5c51
|
||||
Z 32ba6607d3f053fb955e6ec92a8309ce
|
||||
|
@@ -1 +1 @@
|
||||
0ecbba78fcde8f7715cd74c674b5040ef4953f6e
|
||||
dc0763455bbf54c1d8728e16033709caedd6e1c6
|
21
src/btree.c
21
src/btree.c
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.161 2004/06/07 16:27:46 drh Exp $
|
||||
** $Id: btree.c,v 1.162 2004/06/09 17:37:23 drh Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -261,7 +261,7 @@ static const char zMagicHeader[] = "SQLite format 3";
|
||||
** The pageDestructor() routine handles that chore.
|
||||
*/
|
||||
struct MemPage {
|
||||
u8 isInit; /* True if previously initialized */
|
||||
u8 isInit; /* True if previously initialized. MUST BE FIRST! */
|
||||
u8 idxShift; /* True if Cell indices have changed */
|
||||
u8 nOverflow; /* Number of overflow cell bodies in aCell[] */
|
||||
u8 intKey; /* True if intkey flag is set */
|
||||
@@ -962,6 +962,22 @@ static void pageDestructor(void *pData, int pageSize){
|
||||
pPage->isInit = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
** During a rollback, when the pager reloads information into the cache
|
||||
** so that the cache is restored to its original state at the start of
|
||||
** the transaction, for each page restored this routine is called.
|
||||
**
|
||||
** This routine needs to reset the extra data section at the end of the
|
||||
** page to agree with the restored data.
|
||||
*/
|
||||
static void pageReinit(void *pData, int pageSize){
|
||||
MemPage *pPage = (MemPage*)&((char*)pData)[pageSize];
|
||||
if( pPage->isInit ){
|
||||
pPage->isInit = 0;
|
||||
initPage(pPage, pPage->pParent);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Open a new database.
|
||||
**
|
||||
@@ -1011,6 +1027,7 @@ int sqlite3BtreeOpen(
|
||||
return rc;
|
||||
}
|
||||
sqlite3pager_set_destructor(pBt->pPager, pageDestructor);
|
||||
sqlite3pager_set_reiniter(pBt->pPager, pageReinit);
|
||||
pBt->pCursor = 0;
|
||||
pBt->pPage1 = 0;
|
||||
pBt->readOnly = sqlite3pager_isreadonly(pBt->pPager);
|
||||
|
6
src/os.h
6
src/os.h
@@ -156,9 +156,6 @@ int sqlite3OsSeek(OsFile*, off_t offset);
|
||||
int sqlite3OsSync(OsFile*);
|
||||
int sqlite3OsTruncate(OsFile*, off_t size);
|
||||
int sqlite3OsFileSize(OsFile*, off_t *pSize);
|
||||
int sqlite3OsReadLock(OsFile*);
|
||||
int sqlite3OsWriteLock(OsFile*);
|
||||
int sqlite3OsUnlock(OsFile*);
|
||||
int sqlite3OsRandomSeed(char*);
|
||||
int sqlite3OsSleep(int ms);
|
||||
int sqlite3OsCurrentTime(double*);
|
||||
@@ -166,6 +163,7 @@ void sqlite3OsEnterMutex(void);
|
||||
void sqlite3OsLeaveMutex(void);
|
||||
char *sqlite3OsFullPathname(const char*);
|
||||
int sqlite3OsLock(OsFile*, int);
|
||||
int sqlite3OsCheckWriteLock(OsFile *id);
|
||||
int sqlite3OsUnlock(OsFile*, int);
|
||||
int sqlite3OsCheckReservedLock(OsFile *id);
|
||||
|
||||
#endif /* _SQLITE_OS_H_ */
|
||||
|
@@ -43,6 +43,7 @@ static unsigned int elapse;
|
||||
#define TRACE3(X,Y,Z) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z)
|
||||
#define TRACE4(X,Y,Z,A) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A)
|
||||
#define TRACE5(X,Y,Z,A,B) if( sqlite3_os_trace ) sqlite3DebugPrintf(X,Y,Z,A,B)
|
||||
#define TRACE6(X,Y,Z,A,B,C) if(sqlite3_os_trace) sqlite3DebugPrintf(X,Y,Z,A,B,C)
|
||||
#else
|
||||
#define TIMER_START
|
||||
#define TIMER_END
|
||||
@@ -52,6 +53,7 @@ static unsigned int elapse;
|
||||
#define TRACE3(X,Y,Z)
|
||||
#define TRACE4(X,Y,Z,A)
|
||||
#define TRACE5(X,Y,Z,A,B)
|
||||
#define TRACE6(X,Y,Z,A,B,C)
|
||||
#endif
|
||||
|
||||
|
||||
|
158
src/os_unix.c
158
src/os_unix.c
@@ -342,10 +342,10 @@ int sqlite3OsOpenReadWrite(
|
||||
){
|
||||
int rc;
|
||||
id->dirfd = -1;
|
||||
id->fd = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
|
||||
if( id->fd<0 ){
|
||||
id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->fd<0 ){
|
||||
id->h = open(zFilename, O_RDWR|O_CREAT|O_LARGEFILE|O_BINARY, 0644);
|
||||
if( id->h<0 ){
|
||||
id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->h<0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
*pReadonly = 1;
|
||||
@@ -353,14 +353,14 @@ int sqlite3OsOpenReadWrite(
|
||||
*pReadonly = 0;
|
||||
}
|
||||
sqlite3OsEnterMutex();
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
|
||||
sqlite3OsLeaveMutex();
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
close(id->h);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
id->locktype = 0;
|
||||
TRACE3("OPEN %-3d %s\n", id->fd, zFilename);
|
||||
TRACE3("OPEN %-3d %s\n", id->h, zFilename);
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -386,16 +386,16 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
id->dirfd = -1;
|
||||
id->fd = open(zFilename,
|
||||
id->h = open(zFilename,
|
||||
O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW|O_LARGEFILE|O_BINARY, 0600);
|
||||
if( id->fd<0 ){
|
||||
if( id->h<0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
sqlite3OsEnterMutex();
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
|
||||
sqlite3OsLeaveMutex();
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
close(id->h);
|
||||
unlink(zFilename);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
@@ -403,7 +403,7 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
if( delFlag ){
|
||||
unlink(zFilename);
|
||||
}
|
||||
TRACE3("OPEN-EX %-3d %s\n", id->fd, zFilename);
|
||||
TRACE3("OPEN-EX %-3d %s\n", id->h, zFilename);
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -418,19 +418,19 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
|
||||
int rc;
|
||||
id->dirfd = -1;
|
||||
id->fd = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->fd<0 ){
|
||||
id->h = open(zFilename, O_RDONLY|O_LARGEFILE|O_BINARY);
|
||||
if( id->h<0 ){
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
sqlite3OsEnterMutex();
|
||||
rc = findLockInfo(id->fd, &id->pLock, &id->pOpen);
|
||||
rc = findLockInfo(id->h, &id->pLock, &id->pOpen);
|
||||
sqlite3OsLeaveMutex();
|
||||
if( rc ){
|
||||
close(id->fd);
|
||||
close(id->h);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
id->locktype = 0;
|
||||
TRACE3("OPEN-RO %-3d %s\n", id->fd, zFilename);
|
||||
TRACE3("OPEN-RO %-3d %s\n", id->h, zFilename);
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -455,7 +455,7 @@ int sqlite3OsOpenDirectory(
|
||||
const char *zDirname,
|
||||
OsFile *id
|
||||
){
|
||||
if( id->fd<0 ){
|
||||
if( id->h<0 ){
|
||||
/* Do not open the directory if the corresponding file is not already
|
||||
** open. */
|
||||
return SQLITE_CANTOPEN;
|
||||
@@ -510,7 +510,7 @@ int sqlite3OsTempFileName(char *zBuf){
|
||||
** Close a file.
|
||||
*/
|
||||
int sqlite3OsClose(OsFile *id){
|
||||
sqlite3OsUnlock(id);
|
||||
sqlite3OsUnlock(id, NO_LOCK);
|
||||
if( id->dirfd>=0 ) close(id->dirfd);
|
||||
id->dirfd = -1;
|
||||
sqlite3OsEnterMutex();
|
||||
@@ -528,16 +528,16 @@ int sqlite3OsClose(OsFile *id){
|
||||
/* If a malloc fails, just leak the file descriptor */
|
||||
}else{
|
||||
pOpen->aPending = aNew;
|
||||
pOpen->aPending[pOpen->nPending-1] = id->fd;
|
||||
pOpen->aPending[pOpen->nPending-1] = id->h;
|
||||
}
|
||||
}else{
|
||||
/* There are no outstanding locks so we can close the file immediately */
|
||||
close(id->fd);
|
||||
close(id->h);
|
||||
}
|
||||
releaseLockInfo(id->pLock);
|
||||
releaseOpenCnt(id->pOpen);
|
||||
sqlite3OsLeaveMutex();
|
||||
TRACE2("CLOSE %-3d\n", id->fd);
|
||||
TRACE2("CLOSE %-3d\n", id->h);
|
||||
OpenCounter(-1);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
@@ -551,9 +551,9 @@ int sqlite3OsRead(OsFile *id, void *pBuf, int amt){
|
||||
int got;
|
||||
SimulateIOError(SQLITE_IOERR);
|
||||
TIMER_START;
|
||||
got = read(id->fd, pBuf, amt);
|
||||
got = read(id->h, pBuf, amt);
|
||||
TIMER_END;
|
||||
TRACE4("READ %-3d %7d %d\n", id->fd, last_page, elapse);
|
||||
TRACE4("READ %-3d %7d %d\n", id->h, last_page, elapse);
|
||||
SEEK(0);
|
||||
/* if( got<0 ) got = 0; */
|
||||
if( got==amt ){
|
||||
@@ -571,12 +571,12 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
|
||||
int wrote = 0;
|
||||
SimulateIOError(SQLITE_IOERR);
|
||||
TIMER_START;
|
||||
while( amt>0 && (wrote = write(id->fd, pBuf, amt))>0 ){
|
||||
while( amt>0 && (wrote = write(id->h, pBuf, amt))>0 ){
|
||||
amt -= wrote;
|
||||
pBuf = &((char*)pBuf)[wrote];
|
||||
}
|
||||
TIMER_END;
|
||||
TRACE4("WRITE %-3d %7d %d\n", id->fd, last_page, elapse);
|
||||
TRACE4("WRITE %-3d %7d %d\n", id->h, last_page, elapse);
|
||||
SEEK(0);
|
||||
if( amt>0 ){
|
||||
return SQLITE_FULL;
|
||||
@@ -589,7 +589,7 @@ int sqlite3OsWrite(OsFile *id, const void *pBuf, int amt){
|
||||
*/
|
||||
int sqlite3OsSeek(OsFile *id, off_t offset){
|
||||
SEEK(offset/1024 + 1);
|
||||
lseek(id->fd, offset, SEEK_SET);
|
||||
lseek(id->h, offset, SEEK_SET);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -606,8 +606,8 @@ int sqlite3OsSeek(OsFile *id, off_t offset){
|
||||
*/
|
||||
int sqlite3OsSync(OsFile *id){
|
||||
SimulateIOError(SQLITE_IOERR);
|
||||
TRACE2("SYNC %-3d\n", id->fd);
|
||||
if( fsync(id->fd) ){
|
||||
TRACE2("SYNC %-3d\n", id->h);
|
||||
if( fsync(id->h) ){
|
||||
return SQLITE_IOERR;
|
||||
}else{
|
||||
if( id->dirfd>=0 ){
|
||||
@@ -625,7 +625,7 @@ int sqlite3OsSync(OsFile *id){
|
||||
*/
|
||||
int sqlite3OsTruncate(OsFile *id, off_t nByte){
|
||||
SimulateIOError(SQLITE_IOERR);
|
||||
return ftruncate(id->fd, nByte)==0 ? SQLITE_OK : SQLITE_IOERR;
|
||||
return ftruncate(id->h, nByte)==0 ? SQLITE_OK : SQLITE_IOERR;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -634,7 +634,7 @@ int sqlite3OsTruncate(OsFile *id, off_t nByte){
|
||||
int sqlite3OsFileSize(OsFile *id, off_t *pSize){
|
||||
struct stat buf;
|
||||
SimulateIOError(SQLITE_IOERR);
|
||||
if( fstat(id->fd, &buf)!=0 ){
|
||||
if( fstat(id->h, &buf)!=0 ){
|
||||
return SQLITE_IOERR;
|
||||
}
|
||||
*pSize = buf.st_size;
|
||||
@@ -647,7 +647,7 @@ int sqlite3OsFileSize(OsFile *id, off_t *pSize){
|
||||
** non-zero. If the file is unlocked or holds only SHARED locks, then
|
||||
** return zero.
|
||||
*/
|
||||
int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
int sqlite3OsCheckReservedLock(OsFile *id){
|
||||
int r = 0;
|
||||
|
||||
sqlite3OsEnterMutex(); /* Needed because id->pLock is shared across threads */
|
||||
@@ -665,14 +665,14 @@ int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
lock.l_start = RESERVED_BYTE;
|
||||
lock.l_len = 1;
|
||||
lock.l_type = F_WRLCK;
|
||||
fcntl(id->fd, F_GETLK, &lock);
|
||||
fcntl(id->h, F_GETLK, &lock);
|
||||
if( lock.l_type!=F_UNLCK ){
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3OsLeaveMutex();
|
||||
TRACE3("TEST WR-LOCK %d %d\n", id->fd, r);
|
||||
TRACE3("TEST WR-LOCK %d %d\n", id->h, r);
|
||||
|
||||
return r;
|
||||
}
|
||||
@@ -698,10 +698,8 @@ int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
** RESERVED -> (PENDING) -> EXCLUSIVE
|
||||
** PENDING -> EXCLUSIVE
|
||||
**
|
||||
** This routine will only increase a lock. The sqlite3OsUnlock() routine
|
||||
** erases all locks at once and returns us immediately to locking level 0.
|
||||
** It is not possible to lower the locking level one step at a time. You
|
||||
** must go straight to locking level 0.
|
||||
** This routine will only increase a lock. Use the sqlite3OsUnlock()
|
||||
** routine to lower a locking level.
|
||||
*/
|
||||
int sqlite3OsLock(OsFile *id, int locktype){
|
||||
int rc = SQLITE_OK;
|
||||
@@ -709,8 +707,8 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
struct flock lock;
|
||||
int s;
|
||||
|
||||
TRACE5("LOCK %d %d was %d(%d)\n",
|
||||
id->fd, locktype, id->locktype, pLock->locktype);
|
||||
TRACE6("LOCK %d %d was %d(%d,%d)\n",
|
||||
id->h, locktype, id->locktype, pLock->locktype, pLock->cnt);
|
||||
|
||||
/* If there is already a lock of this type or more restrictive on the
|
||||
** OsFile, do nothing. Don't use the end_lock: exit path, as
|
||||
@@ -771,7 +769,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
*/
|
||||
lock.l_type = F_RDLCK;
|
||||
lock.l_start = PENDING_BYTE;
|
||||
s = fcntl(id->fd, F_SETLK, &lock);
|
||||
s = fcntl(id->h, F_SETLK, &lock);
|
||||
if( s ){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
goto end_lock;
|
||||
@@ -780,13 +778,13 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
/* Now get the read-lock */
|
||||
lock.l_start = SHARED_FIRST;
|
||||
lock.l_len = SHARED_SIZE;
|
||||
s = fcntl(id->fd, F_SETLK, &lock);
|
||||
s = fcntl(id->h, F_SETLK, &lock);
|
||||
|
||||
/* Drop the temporary PENDING lock */
|
||||
lock.l_start = PENDING_BYTE;
|
||||
lock.l_len = 1L;
|
||||
lock.l_type = F_UNLCK;
|
||||
fcntl(id->fd, F_SETLK, &lock);
|
||||
fcntl(id->h, F_SETLK, &lock);
|
||||
if( s ){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}else{
|
||||
@@ -815,7 +813,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
s = fcntl(id->fd, F_SETLK, &lock);
|
||||
s = fcntl(id->h, F_SETLK, &lock);
|
||||
if( s ){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}
|
||||
@@ -828,47 +826,63 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
|
||||
end_lock:
|
||||
sqlite3OsLeaveMutex();
|
||||
TRACE4("LOCK %d %d %s\n", id->fd, locktype, rc==SQLITE_OK ? "ok" : "failed");
|
||||
TRACE4("LOCK %d %d %s\n", id->h, locktype, rc==SQLITE_OK ? "ok" : "failed");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock the given file descriptor. If the file descriptor was
|
||||
** not previously locked, then this routine is a no-op. If this
|
||||
** library was compiled with large file support (LFS) but LFS is not
|
||||
** available on the host, then an SQLITE_NOLFS is returned.
|
||||
** Lower the locking level on file descriptor id to locktype. locktype
|
||||
** must be either NO_LOCK or SHARED_LOCK.
|
||||
**
|
||||
** If the locking level of the file descriptor is already at or below
|
||||
** the requested locking level, this routine is a no-op.
|
||||
**
|
||||
** It is not possible for this routine to fail.
|
||||
*/
|
||||
int sqlite3OsUnlock(OsFile *id){
|
||||
int rc;
|
||||
if( !id->locktype ) return SQLITE_OK;
|
||||
id->locktype = 0;
|
||||
sqlite3OsEnterMutex();
|
||||
assert( id->pLock->cnt!=0 );
|
||||
if( id->pLock->cnt>1 ){
|
||||
id->pLock->cnt--;
|
||||
rc = SQLITE_OK;
|
||||
}else{
|
||||
int sqlite3OsUnlock(OsFile *id, int locktype){
|
||||
struct lockInfo *pLock;
|
||||
struct flock lock;
|
||||
int s;
|
||||
|
||||
TRACE6("UNLOCK %d %d was %d(%d,%d)\n",
|
||||
id->h, locktype, id->locktype, id->pLock->locktype, id->pLock->cnt);
|
||||
|
||||
assert( locktype<=SHARED_LOCK );
|
||||
if( id->locktype<=locktype ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3OsEnterMutex();
|
||||
pLock = id->pLock;
|
||||
assert( pLock->cnt!=0 );
|
||||
if( id->locktype>SHARED_LOCK ){
|
||||
assert( pLock->locktype==id->locktype );
|
||||
lock.l_type = F_UNLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = PENDING_BYTE;
|
||||
lock.l_len = 2L; assert( PENDING_BYTE+1==RESERVED_BYTE );
|
||||
fcntl(id->h, F_SETLK, &lock);
|
||||
pLock->locktype = SHARED_LOCK;
|
||||
}
|
||||
if( locktype==NO_LOCK ){
|
||||
struct openCnt *pOpen;
|
||||
|
||||
/* Decrement the shared lock counter. Release the lock using an
|
||||
** OS call only when all threads in this same process have released
|
||||
** the lock.
|
||||
*/
|
||||
pLock->cnt--;
|
||||
if( pLock->cnt==0 ){
|
||||
lock.l_type = F_UNLCK;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = lock.l_len = 0L;
|
||||
s = fcntl(id->fd, F_SETLK, &lock);
|
||||
if( s!=0 ){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}else{
|
||||
rc = SQLITE_OK;
|
||||
id->pLock->cnt = 0;
|
||||
id->pLock->locktype = 0;
|
||||
}
|
||||
fcntl(id->h, F_SETLK, &lock);
|
||||
pLock->locktype = NO_LOCK;
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
/* Decrement the count of locks against this same file. When the
|
||||
** count reaches zero, close any other file descriptors whose close
|
||||
** was deferred because of outstanding locks.
|
||||
*/
|
||||
struct openCnt *pOpen = id->pOpen;
|
||||
pOpen = id->pOpen;
|
||||
pOpen->nLock--;
|
||||
assert( pOpen->nLock>=0 );
|
||||
if( pOpen->nLock==0 && pOpen->nPending>0 ){
|
||||
@@ -882,8 +896,8 @@ int sqlite3OsUnlock(OsFile *id){
|
||||
}
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
id->locktype = 0;
|
||||
return rc;
|
||||
id->locktype = locktype;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@@ -64,7 +64,7 @@ typedef struct OsFile OsFile;
|
||||
struct OsFile {
|
||||
struct openCnt *pOpen; /* Info about all open fd's on this inode */
|
||||
struct lockInfo *pLock; /* Info about locks on this inode */
|
||||
int fd; /* The file descriptor */
|
||||
int h; /* The file descriptor */
|
||||
int locktype; /* The type of lock held on this fd */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
};
|
||||
|
23
src/os_win.c
23
src/os_win.c
@@ -508,7 +508,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
** file by this or any other process. If such a lock is held, return
|
||||
** non-zero, otherwise zero.
|
||||
*/
|
||||
int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
int sqlite3OsCheckReservedLock(OsFile *id){
|
||||
int rc;
|
||||
if( id->locktype>=RESERVED_LOCK ){
|
||||
rc = 1;
|
||||
@@ -525,14 +525,19 @@ int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
}
|
||||
|
||||
/*
|
||||
** Unlock the given file descriptor. If the file descriptor was
|
||||
** not previously locked, then this routine is a no-op. If this
|
||||
** library was compiled with large file support (LFS) but LFS is not
|
||||
** available on the host, then an SQLITE_NOLFS is returned.
|
||||
** Lower the locking level on file descriptor id to locktype. locktype
|
||||
** must be either NO_LOCK or SHARED_LOCK.
|
||||
**
|
||||
** If the locking level of the file descriptor is already at or below
|
||||
** the requested locking level, this routine is a no-op.
|
||||
**
|
||||
** It is not possible for this routine to fail.
|
||||
*/
|
||||
int sqlite3OsUnlock(OsFile *id){
|
||||
int sqlite3OsUnlock(OsFile *id, int locktype){
|
||||
int rc, type;
|
||||
TRACE4("UNLOCK %d was %d(%d)\n", id->h, id->locktype, id->sharedLockByte);
|
||||
assert( locktype<=SHARED_LOCK );
|
||||
TRACE4("UNLOCK %d to %d was %d(%d)\n", id->h, locktype,
|
||||
id->locktype, id->sharedLockByte);
|
||||
type = id->locktype;
|
||||
if( type>=EXCLUSIVE_LOCK ){
|
||||
UnlockFile(id->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
|
||||
@@ -540,13 +545,13 @@ int sqlite3OsUnlock(OsFile *id){
|
||||
if( type>=RESERVED_LOCK ){
|
||||
UnlockFile(id->h, RESERVED_BYTE, 0, 1, 0);
|
||||
}
|
||||
if( type>=SHARED_LOCK && type<EXCLUSIVE_LOCK ){
|
||||
if( locktype==NO_LOCK && type>=SHARED_LOCK && type<EXCLUSIVE_LOCK ){
|
||||
unlockReadLock(id);
|
||||
}
|
||||
if( type>=PENDING_LOCK ){
|
||||
UnlockFile(id->h, PENDING_BYTE, 0, 1, 0);
|
||||
}
|
||||
id->locktype = NO_LOCK;
|
||||
id->locktype = locktype;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
312
src/pager.c
312
src/pager.c
@@ -18,7 +18,7 @@
|
||||
** file simultaneously, or one process from reading the database while
|
||||
** another is writing.
|
||||
**
|
||||
** @(#) $Id: pager.c,v 1.114 2004/06/09 14:17:21 drh Exp $
|
||||
** @(#) $Id: pager.c,v 1.115 2004/06/09 17:37:28 drh Exp $
|
||||
*/
|
||||
#include "os.h" /* Must be first to enable large file support */
|
||||
#include "sqliteInt.h"
|
||||
@@ -49,35 +49,42 @@ static Pager *mainPager = 0;
|
||||
** The page cache as a whole is always in one of the following
|
||||
** states:
|
||||
**
|
||||
** SQLITE_UNLOCK The page cache is not currently reading or
|
||||
** PAGER_UNLOCK The page cache is not currently reading or
|
||||
** writing the database file. There is no
|
||||
** data held in memory. This is the initial
|
||||
** state.
|
||||
**
|
||||
** SQLITE_READLOCK The page cache is reading the database.
|
||||
** PAGER_SHARED The page cache is reading the database.
|
||||
** Writing is not permitted. There can be
|
||||
** multiple readers accessing the same database
|
||||
** file at the same time.
|
||||
**
|
||||
** SQLITE_WRITELOCK The page cache is writing the database.
|
||||
** PAGER_RESERVED Writing is permitted to the page cache only.
|
||||
** The original database file has not been modified.
|
||||
** Other processes may still be reading the on-disk
|
||||
** database file.
|
||||
**
|
||||
** PAGER_EXCLUSIVE The page cache is writing the database.
|
||||
** Access is exclusive. No other processes or
|
||||
** threads can be reading or writing while one
|
||||
** process is writing.
|
||||
**
|
||||
** The page cache comes up in SQLITE_UNLOCK. The first time a
|
||||
** sqlite_page_get() occurs, the state transitions to SQLITE_READLOCK.
|
||||
** The page cache comes up in PAGER_UNLOCK. The first time a
|
||||
** sqlite_page_get() occurs, the state transitions to PAGER_SHARED.
|
||||
** After all pages have been released using sqlite_page_unref(),
|
||||
** the state transitions back to SQLITE_UNLOCK. The first time
|
||||
** the state transitions back to PAGER_UNLOCK. The first time
|
||||
** that sqlite_page_write() is called, the state transitions to
|
||||
** SQLITE_WRITELOCK. (Note that sqlite_page_write() can only be
|
||||
** PAGER_RESERVED. (Note that sqlite_page_write() can only be
|
||||
** called on an outstanding page which means that the pager must
|
||||
** be in SQLITE_READLOCK before it transitions to SQLITE_WRITELOCK.)
|
||||
** be in PAGER_SHARED before it transitions to PAGER_RESERVED.)
|
||||
** The sqlite_page_rollback() and sqlite_page_commit() functions
|
||||
** transition the state from SQLITE_WRITELOCK back to SQLITE_READLOCK.
|
||||
** transition the state from PAGER_RESERVED to PAGER_EXCLUSIVE to
|
||||
** PAGER_SHARED.
|
||||
*/
|
||||
#define SQLITE_UNLOCK 0
|
||||
#define SQLITE_READLOCK 1
|
||||
#define SQLITE_WRITELOCK 2
|
||||
#define PAGER_UNLOCK 0
|
||||
#define PAGER_SHARED 1
|
||||
#define PAGER_RESERVED 2
|
||||
#define PAGER_EXCLUSIVE 3
|
||||
|
||||
|
||||
/*
|
||||
@@ -182,6 +189,7 @@ struct Pager {
|
||||
int stmtNRec; /* Number of records in stmt subjournal */
|
||||
int nExtra; /* Add this many bytes to each in-memory page */
|
||||
void (*xDestructor)(void*,int); /* Call this routine when freeing pages */
|
||||
void (*xReiniter)(void*,int); /* Call this routine when reloading pages */
|
||||
int pageSize; /* Number of bytes in a page */
|
||||
int nPage; /* Total number of in-memory pages */
|
||||
int nRef; /* Number of in-memory pages with PgHdr.nRef>0 */
|
||||
@@ -197,12 +205,12 @@ struct Pager {
|
||||
u8 stmtAutoopen; /* Open stmt journal when main journal is opened*/
|
||||
u8 noSync; /* Do not sync the journal if true */
|
||||
u8 fullSync; /* Do extra syncs of the journal for robustness */
|
||||
u8 state; /* SQLITE_UNLOCK, _READLOCK or _WRITELOCK */
|
||||
u8 state; /* PAGER_UNLOCK, _SHARED, _RESERVED, etc. */
|
||||
u8 errMask; /* One of several kinds of errors */
|
||||
u8 tempFile; /* zFilename is a temporary file */
|
||||
u8 readOnly; /* True for a read-only database */
|
||||
u8 needSync; /* True if an fsync() is needed on the journal */
|
||||
u8 dirtyFile; /* True if database file has changed in any way */
|
||||
u8 dirtyCache; /* True if cached pages have changed */
|
||||
u8 alwaysRollback; /* Disable dont_rollback() for all pages */
|
||||
u8 memDb; /* True to inhibit all file I/O */
|
||||
u8 *aInJournal; /* One bit for each page in the database file */
|
||||
@@ -479,11 +487,11 @@ static void pager_reset(Pager *pPager){
|
||||
pPager->pAll = 0;
|
||||
memset(pPager->aHash, 0, sizeof(pPager->aHash));
|
||||
pPager->nPage = 0;
|
||||
if( pPager->state>=SQLITE_WRITELOCK ){
|
||||
if( pPager->state>=PAGER_RESERVED ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
}
|
||||
sqlite3OsUnlock(&pPager->fd);
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
sqlite3OsUnlock(&pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pPager->dbSize = -1;
|
||||
pPager->nRef = 0;
|
||||
assert( pPager->journalOpen==0 );
|
||||
@@ -491,18 +499,19 @@ static void pager_reset(Pager *pPager){
|
||||
|
||||
/*
|
||||
** When this routine is called, the pager has the journal file open and
|
||||
** a write lock on the database. This routine releases the database
|
||||
** write lock and acquires a read lock in its place. The journal file
|
||||
** is deleted and closed.
|
||||
** a RESERVED or EXCLUSIVE lock on the database. This routine releases
|
||||
** the database lock and acquires a SHARED lock in its place. The journal
|
||||
** file is deleted and closed.
|
||||
**
|
||||
** TODO: Consider keeping the journal file open for temporary databases.
|
||||
** This might give a performance improvement on windows where opening
|
||||
** a file is an expensive operation.
|
||||
*/
|
||||
static int pager_unwritelock(Pager *pPager){
|
||||
int rc;
|
||||
PgHdr *pPg;
|
||||
if( pPager->state<SQLITE_WRITELOCK ) return SQLITE_OK;
|
||||
if( pPager->state<PAGER_RESERVED ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
sqlite3pager_stmt_commit(pPager);
|
||||
if( pPager->stmtOpen ){
|
||||
sqlite3OsClose(&pPager->stfd);
|
||||
@@ -520,19 +529,11 @@ static int pager_unwritelock(Pager *pPager){
|
||||
pPg->needSync = 0;
|
||||
}
|
||||
}else{
|
||||
assert( pPager->dirtyFile==0 || pPager->useJournal==0 );
|
||||
assert( pPager->dirtyCache==0 || pPager->useJournal==0 );
|
||||
}
|
||||
rc = sqlite3OsLock(&pPager->fd, SHARED_LOCK);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
}else{
|
||||
/* This can only happen if a process does a BEGIN, then forks and the
|
||||
** child process does the COMMIT. Because of the semantics of unix
|
||||
** file locking, the unlock will fail.
|
||||
*/
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
}
|
||||
return rc;
|
||||
sqlite3OsUnlock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = PAGER_SHARED;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -588,7 +589,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
||||
** at the same time, if there is one.
|
||||
*/
|
||||
pPg = pager_lookup(pPager, pgRec.pgno);
|
||||
TRACE2("PLAYBACK %d\n", pgRec.pgno);
|
||||
TRACE2("PLAYBACK page %d\n", pgRec.pgno);
|
||||
sqlite3OsSeek(&pPager->fd, (pgRec.pgno-1)*(off_t)SQLITE_PAGE_SIZE);
|
||||
rc = sqlite3OsWrite(&pPager->fd, pgRec.aData, SQLITE_PAGE_SIZE);
|
||||
if( pPg ){
|
||||
@@ -721,6 +722,45 @@ delmaster_out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Make every page in the cache agree with what is on disk. In other words,
|
||||
** reread the disk to reset the state of the cache.
|
||||
**
|
||||
** This routine is called after a rollback in which some of the dirty cache
|
||||
** pages had never been written out to disk. We need to roll back the
|
||||
** cache content and the easiest way to do that is to reread the old content
|
||||
** back from the disk.
|
||||
*/
|
||||
static int pager_reload_cache(Pager *pPager){
|
||||
PgHdr *pPg;
|
||||
int rc = SQLITE_OK;
|
||||
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
|
||||
char zBuf[SQLITE_PAGE_SIZE];
|
||||
if( !pPg->dirty ) continue;
|
||||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||
sqlite3OsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1));
|
||||
rc = sqlite3OsRead(&pPager->fd, zBuf, SQLITE_PAGE_SIZE);
|
||||
TRACE2("REFETCH page %d\n", pPg->pgno);
|
||||
CODEC(pPager, zBuf, pPg->pgno, 2);
|
||||
if( rc ) break;
|
||||
}else{
|
||||
memset(zBuf, 0, SQLITE_PAGE_SIZE);
|
||||
}
|
||||
if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE) ){
|
||||
memcpy(PGHDR_TO_DATA(pPg), zBuf, SQLITE_PAGE_SIZE);
|
||||
if( pPager->xReiniter ){
|
||||
pPager->xReiniter(PGHDR_TO_DATA(pPg), pPager->pageSize);
|
||||
}else{
|
||||
memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
|
||||
}
|
||||
}
|
||||
pPg->needSync = 0;
|
||||
pPg->dirty = 0;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** Playback the journal and thus restore the database file to
|
||||
** the state it was in before we started making changes.
|
||||
@@ -887,26 +927,7 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
** pages by reading them back from the original database.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
PgHdr *pPg;
|
||||
for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
|
||||
char zBuf[SQLITE_PAGE_SIZE];
|
||||
if( !pPg->dirty ) continue;
|
||||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||
sqlite3OsSeek(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)(pPg->pgno-1));
|
||||
rc = sqlite3OsRead(&pPager->fd, zBuf, SQLITE_PAGE_SIZE);
|
||||
TRACE2("REFETCH %d\n", pPg->pgno);
|
||||
CODEC(pPager, zBuf, pPg->pgno, 2);
|
||||
if( rc ) break;
|
||||
}else{
|
||||
memset(zBuf, 0, SQLITE_PAGE_SIZE);
|
||||
}
|
||||
if( pPg->nRef==0 || memcmp(zBuf, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE) ){
|
||||
memcpy(PGHDR_TO_DATA(pPg), zBuf, SQLITE_PAGE_SIZE);
|
||||
memset(PGHDR_TO_EXTRA(pPg), 0, pPager->nExtra);
|
||||
}
|
||||
pPg->needSync = 0;
|
||||
pPg->dirty = 0;
|
||||
}
|
||||
pager_reload_cache(pPager);
|
||||
}
|
||||
|
||||
end_playback:
|
||||
@@ -1165,7 +1186,7 @@ int sqlite3pager_open(
|
||||
pPager->stmtJSize = 0;
|
||||
pPager->nPage = 0;
|
||||
pPager->mxPage = mxPage>5 ? mxPage : 10;
|
||||
pPager->state = SQLITE_UNLOCK;
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
pPager->errMask = 0;
|
||||
pPager->tempFile = tempFile;
|
||||
pPager->memDb = memDb;
|
||||
@@ -1194,6 +1215,17 @@ void sqlite3pager_set_destructor(Pager *pPager, void (*xDesc)(void*,int)){
|
||||
pPager->xDestructor = xDesc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Set the reinitializer for this pager. If not NULL, the reinitializer
|
||||
** is called when the content of a page in cache is restored to its original
|
||||
** value as a result of a rollback. The callback gives higher-level code
|
||||
** an opportunity to restore the EXTRA section to agree with the restored
|
||||
** page data.
|
||||
*/
|
||||
void sqlite3pager_set_reiniter(Pager *pPager, void (*xReinit)(void*,int)){
|
||||
pPager->xReiniter = xReinit;
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the total number of pages in the disk file associated with
|
||||
** pPager.
|
||||
@@ -1209,7 +1241,7 @@ int sqlite3pager_pagecount(Pager *pPager){
|
||||
return 0;
|
||||
}
|
||||
n /= SQLITE_PAGE_SIZE;
|
||||
if( pPager->state!=SQLITE_UNLOCK ){
|
||||
if( pPager->state!=PAGER_UNLOCK ){
|
||||
pPager->dbSize = n;
|
||||
}
|
||||
return n;
|
||||
@@ -1330,17 +1362,18 @@ int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
|
||||
int sqlite3pager_close(Pager *pPager){
|
||||
PgHdr *pPg, *pNext;
|
||||
switch( pPager->state ){
|
||||
case SQLITE_WRITELOCK: {
|
||||
case PAGER_RESERVED:
|
||||
case PAGER_EXCLUSIVE: {
|
||||
sqlite3pager_rollback(pPager);
|
||||
if( !pPager->memDb ){
|
||||
sqlite3OsUnlock(&pPager->fd);
|
||||
sqlite3OsUnlock(&pPager->fd, NO_LOCK);
|
||||
}
|
||||
assert( pPager->journalOpen==0 );
|
||||
break;
|
||||
}
|
||||
case SQLITE_READLOCK: {
|
||||
case PAGER_SHARED: {
|
||||
if( !pPager->memDb ){
|
||||
sqlite3OsUnlock(&pPager->fd);
|
||||
sqlite3OsUnlock(&pPager->fd, NO_LOCK);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1485,7 +1518,7 @@ static int syncJournal(Pager *pPager, const char *zMaster){
|
||||
/* Write the nRec value into the journal file header */
|
||||
off_t szJ;
|
||||
if( pPager->fullSync ){
|
||||
TRACE1("SYNC\n");
|
||||
TRACE2("SYNC journal of %d\n", pPager->fd.h);
|
||||
rc = sqlite3OsSync(&pPager->jfd);
|
||||
if( rc!=0 ) return rc;
|
||||
}
|
||||
@@ -1506,7 +1539,7 @@ static int syncJournal(Pager *pPager, const char *zMaster){
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format);
|
||||
sqlite3OsSeek(&pPager->jfd, szJ);
|
||||
}
|
||||
TRACE1("SYNC\n");
|
||||
TRACE2("SYNC journal of %d\n", pPager->fd.h);
|
||||
rc = sqlite3OsSync(&pPager->jfd);
|
||||
if( rc!=0 ) return rc;
|
||||
pPager->journalStarted = 1;
|
||||
@@ -1554,15 +1587,17 @@ static int pager_write_pagelist(PgHdr *pList){
|
||||
** database file. If there is already an EXCLUSIVE lock, the following
|
||||
** calls to sqlite3OsLock() are no-ops.
|
||||
**
|
||||
** The upgrade from a RESERVED to PENDING might return SQLITE_BUSY on
|
||||
** windows because the windows locking mechanism acquires a transient
|
||||
** PENDING lock during its attempts to get a SHARED lock. So if another
|
||||
** process were trying to get a SHARED lock at the same time this process
|
||||
** is upgrading from RESERVED to PENDING, the two could collide.
|
||||
** Moving the lock from RESERVED to EXCLUSIVE actually involves going
|
||||
** through an intermediate state PENDING. A PENDING lock prevents new
|
||||
** readers from attaching to the database but is unsufficient for us to
|
||||
** write. The idea of a PENDING lock is to prevent new readers from
|
||||
** coming in while we wait for existing readers to clear.
|
||||
**
|
||||
** The upgrade from PENDING to EXCLUSIVE can return SQLITE_BUSY if there
|
||||
** are still active readers that were created before the PENDING lock
|
||||
** was acquired.
|
||||
** While the pager is in the RESERVED state, the original database file
|
||||
** is unchanged and we can rollback without having to playback the
|
||||
** journal into the original database file. Once we transition to
|
||||
** EXCLUSIVE, it means the database file has been changed and any rollback
|
||||
** will require a journal playback.
|
||||
*/
|
||||
do {
|
||||
rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
|
||||
@@ -1574,12 +1609,13 @@ static int pager_write_pagelist(PgHdr *pList){
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pPager->state = PAGER_EXCLUSIVE;
|
||||
|
||||
while( pList ){
|
||||
assert( pList->dirty );
|
||||
sqlite3OsSeek(&pPager->fd, (pList->pgno-1)*(off_t)SQLITE_PAGE_SIZE);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 6);
|
||||
TRACE2("STORE %d\n", pList->pgno);
|
||||
TRACE2("STORE page %d\n", pList->pgno);
|
||||
rc = sqlite3OsWrite(&pPager->fd, PGHDR_TO_DATA(pList), SQLITE_PAGE_SIZE);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pList), pList->pgno, 0);
|
||||
if( rc ) return rc;
|
||||
@@ -1657,27 +1693,25 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
pPager->state = PAGER_SHARED;
|
||||
|
||||
/* If a journal file exists, and there is no RESERVED lock on the
|
||||
** database file, then it either needs to be played back or deleted.
|
||||
*/
|
||||
if( pPager->useJournal &&
|
||||
sqlite3OsFileExists(pPager->zJournal) &&
|
||||
!sqlite3OsCheckWriteLock(&pPager->fd)
|
||||
!sqlite3OsCheckReservedLock(&pPager->fd)
|
||||
){
|
||||
int rc;
|
||||
|
||||
/* Get an EXCLUSIVE lock on the database file. */
|
||||
rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
if( sqlite3OsUnlock(&pPager->fd)!=SQLITE_OK ){
|
||||
/* This should never happen! */
|
||||
rc = SQLITE_INTERNAL;
|
||||
}
|
||||
sqlite3OsUnlock(&pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
return rc;
|
||||
}
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
pPager->state = PAGER_EXCLUSIVE;
|
||||
|
||||
/* Open the journal for reading only. Return SQLITE_BUSY if
|
||||
** we are unable to open the journal file.
|
||||
@@ -1689,8 +1723,8 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
*/
|
||||
rc = sqlite3OsOpenReadOnly(pPager->zJournal, &pPager->jfd);
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = sqlite3OsUnlock(&pPager->fd);
|
||||
assert( rc==SQLITE_OK );
|
||||
sqlite3OsUnlock(&pPager->fd, NO_LOCK);
|
||||
pPager->state = PAGER_UNLOCK;
|
||||
return SQLITE_BUSY;
|
||||
}
|
||||
pPager->journalOpen = 1;
|
||||
@@ -1708,8 +1742,8 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
}else{
|
||||
/* Search for page in cache */
|
||||
pPg = pager_lookup(pPager, pgno);
|
||||
if( pPager->memDb && pPager->state==SQLITE_UNLOCK ){
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
if( pPager->memDb && pPager->state==PAGER_UNLOCK ){
|
||||
pPager->state = PAGER_SHARED;
|
||||
}
|
||||
}
|
||||
if( pPg==0 ){
|
||||
@@ -1828,7 +1862,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
assert( pPager->memDb==0 );
|
||||
sqlite3OsSeek(&pPager->fd, (pgno-1)*(off_t)SQLITE_PAGE_SIZE);
|
||||
rc = sqlite3OsRead(&pPager->fd, PGHDR_TO_DATA(pPg), SQLITE_PAGE_SIZE);
|
||||
TRACE2("FETCH %d\n", pPg->pgno);
|
||||
TRACE2("FETCH page %d\n", pPg->pgno);
|
||||
CODEC(pPager, PGHDR_TO_DATA(pPg), pPg->pgno, 3);
|
||||
if( rc!=SQLITE_OK ){
|
||||
off_t fileSize;
|
||||
@@ -1927,30 +1961,30 @@ int sqlite3pager_unref(void *pData){
|
||||
}
|
||||
|
||||
/*
|
||||
** Create a journal file for pPager. There should already be a write
|
||||
** lock on the database file when this routine is called.
|
||||
** Create a journal file for pPager. There should already be a RESERVED
|
||||
** or EXCLUSIVE lock on the database file when this routine is called.
|
||||
**
|
||||
** Return SQLITE_OK if everything. Return an error code and release the
|
||||
** write lock if anything goes wrong.
|
||||
*/
|
||||
static int pager_open_journal(Pager *pPager){
|
||||
int rc;
|
||||
assert( pPager->state==SQLITE_WRITELOCK );
|
||||
assert( pPager->state>=PAGER_RESERVED );
|
||||
assert( pPager->journalOpen==0 );
|
||||
assert( pPager->useJournal );
|
||||
sqlite3pager_pagecount(pPager);
|
||||
pPager->aInJournal = sqliteMalloc( pPager->dbSize/8 + 1 );
|
||||
if( pPager->aInJournal==0 ){
|
||||
sqlite3OsLock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
sqlite3OsUnlock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = PAGER_SHARED;
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3OsOpenExclusive(pPager->zJournal, &pPager->jfd,pPager->tempFile);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(pPager->aInJournal);
|
||||
pPager->aInJournal = 0;
|
||||
sqlite3OsLock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
sqlite3OsUnlock(&pPager->fd, SHARED_LOCK);
|
||||
pPager->state = PAGER_SHARED;
|
||||
return SQLITE_CANTOPEN;
|
||||
}
|
||||
sqlite3OsOpenDirectory(pPager->zDirectory, &pPager->jfd);
|
||||
@@ -2039,7 +2073,7 @@ static int pager_open_journal(Pager *pPager){
|
||||
** files, the opening of the journal file is deferred until there is an
|
||||
** actual need to write to the journal.
|
||||
**
|
||||
** If the database is already write-locked, this routine is a no-op.
|
||||
** If the database is already reserved for writing, this routine is a no-op.
|
||||
*/
|
||||
int sqlite3pager_begin(void *pData, int nMaster){
|
||||
PgHdr *pPg = DATA_TO_PGHDR(pData);
|
||||
@@ -2047,20 +2081,15 @@ int sqlite3pager_begin(void *pData, int nMaster){
|
||||
int rc = SQLITE_OK;
|
||||
assert( pPg->nRef>0 );
|
||||
assert( nMaster>=0 );
|
||||
assert( pPager->state!=SQLITE_UNLOCK );
|
||||
if( pPager->state==SQLITE_READLOCK ){
|
||||
assert( pPager->state!=PAGER_UNLOCK );
|
||||
if( pPager->state==PAGER_SHARED ){
|
||||
assert( pPager->aInJournal==0 );
|
||||
if( pPager->memDb ){
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
pPager->state = PAGER_EXCLUSIVE;
|
||||
pPager->origDbSize = pPager->dbSize;
|
||||
}else{
|
||||
int busy = 1;
|
||||
do {
|
||||
/* If the library grabs an EXCLUSIVE lock here, as in the commented
|
||||
** out line, then it exhibits the old locking behaviour - a writer
|
||||
** excludes all readers, not just other writers.
|
||||
*/
|
||||
/* rc = sqlite3OsLock(&pPager->fd, EXCLUSIVE_LOCK); */
|
||||
rc = sqlite3OsLock(&pPager->fd, RESERVED_LOCK);
|
||||
}while( rc==SQLITE_BUSY &&
|
||||
pPager->pBusyHandler &&
|
||||
@@ -2071,9 +2100,9 @@ int sqlite3pager_begin(void *pData, int nMaster){
|
||||
return rc;
|
||||
}
|
||||
pPager->nMaster = nMaster;
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
pPager->dirtyFile = 0;
|
||||
TRACE1("TRANSACTION\n");
|
||||
pPager->state = PAGER_RESERVED;
|
||||
pPager->dirtyCache = 0;
|
||||
TRACE3("TRANSACTION %d nMaster=%d\n", pPager->fd.h, nMaster);
|
||||
if( pPager->useJournal && !pPager->tempFile ){
|
||||
rc = pager_open_journal(pPager);
|
||||
}
|
||||
@@ -2088,7 +2117,7 @@ int sqlite3pager_begin(void *pData, int nMaster){
|
||||
** changes to a page.
|
||||
**
|
||||
** The first time this routine is called, the pager creates a new
|
||||
** journal and acquires a write lock on the database. If the write
|
||||
** journal and acquires a RESERVED lock on the database. If the RESERVED
|
||||
** lock could not be acquired, this routine returns SQLITE_BUSY. The
|
||||
** calling routine must check for that return value and be careful not to
|
||||
** change any page data until this routine returns SQLITE_OK.
|
||||
@@ -2118,7 +2147,7 @@ int sqlite3pager_write(void *pData){
|
||||
*/
|
||||
pPg->dirty = 1;
|
||||
if( pPg->inJournal && (pPg->inStmt || pPager->stmtInUse==0) ){
|
||||
pPager->dirtyFile = 1;
|
||||
pPager->dirtyCache = 1;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -2129,22 +2158,22 @@ int sqlite3pager_write(void *pData){
|
||||
** First check to see that the transaction journal exists and
|
||||
** create it if it does not.
|
||||
*/
|
||||
assert( pPager->state!=SQLITE_UNLOCK );
|
||||
assert( pPager->state!=PAGER_UNLOCK );
|
||||
rc = sqlite3pager_begin(pData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
assert( pPager->state==SQLITE_WRITELOCK );
|
||||
assert( pPager->state>=PAGER_RESERVED );
|
||||
if( !pPager->journalOpen && pPager->useJournal ){
|
||||
rc = pager_open_journal(pPager);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
assert( pPager->journalOpen || !pPager->useJournal );
|
||||
pPager->dirtyFile = 1;
|
||||
pPager->dirtyCache = 1;
|
||||
|
||||
/* The transaction journal now exists and we have a write lock on the
|
||||
** main database file. Write the current page to the transaction
|
||||
** journal if it is not there already.
|
||||
/* The transaction journal now exists and we have a RESERVED or an
|
||||
** EXCLUSIVE lock on the main database file. Write the current page to
|
||||
** the transaction journal if it is not there already.
|
||||
*/
|
||||
if( !pPg->inJournal && (pPager->useJournal || pPager->memDb) ){
|
||||
if( (int)pPg->pgno <= pPager->origDbSize ){
|
||||
@@ -2152,7 +2181,7 @@ int sqlite3pager_write(void *pData){
|
||||
u32 saved;
|
||||
if( pPager->memDb ){
|
||||
PgHistory *pHist = PGHDR_TO_HIST(pPg, pPager);
|
||||
TRACE2("JOURNAL %d\n", pPg->pgno);
|
||||
TRACE2("JOURNAL page %d\n", pPg->pgno);
|
||||
assert( pHist->pOrig==0 );
|
||||
pHist->pOrig = sqliteMallocRaw( pPager->pageSize );
|
||||
if( pHist->pOrig ){
|
||||
@@ -2171,7 +2200,7 @@ int sqlite3pager_write(void *pData){
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
CODEC(pPager, pData, pPg->pgno, 7);
|
||||
rc = sqlite3OsWrite(&pPager->jfd, &((char*)pData)[-4], szPg);
|
||||
TRACE3("JOURNAL %d %d\n", pPg->pgno, pPg->needSync);
|
||||
TRACE3("JOURNAL page %d needSync=%d\n", pPg->pgno, pPg->needSync);
|
||||
CODEC(pPager, pData, pPg->pgno, 0);
|
||||
if( journal_format>=JOURNAL_FORMAT_3 ){
|
||||
*(u32*)PGHDR_TO_EXTRA(pPg) = saved;
|
||||
@@ -2193,7 +2222,7 @@ int sqlite3pager_write(void *pData){
|
||||
}
|
||||
}else{
|
||||
pPg->needSync = !pPager->journalStarted && !pPager->noSync;
|
||||
TRACE3("APPEND %d %d\n", pPg->pgno, pPg->needSync);
|
||||
TRACE3("APPEND page %d needSync=%d\n", pPg->pgno, pPg->needSync);
|
||||
}
|
||||
if( pPg->needSync ){
|
||||
pPager->needSync = 1;
|
||||
@@ -2214,12 +2243,12 @@ int sqlite3pager_write(void *pData){
|
||||
if( pHist->pStmt ){
|
||||
memcpy(pHist->pStmt, PGHDR_TO_DATA(pPg), pPager->pageSize);
|
||||
}
|
||||
TRACE2("STMT-JOURNAL %d\n", pPg->pgno);
|
||||
TRACE2("STMT-JOURNAL page %d\n", pPg->pgno);
|
||||
}else{
|
||||
store32bits(pPg->pgno, pPg, -4);
|
||||
CODEC(pPager, pData, pPg->pgno, 7);
|
||||
rc = sqlite3OsWrite(&pPager->stfd, ((char*)pData)-4, SQLITE_PAGE_SIZE+4);
|
||||
TRACE2("STMT-JOURNAL %d\n", pPg->pgno);
|
||||
TRACE2("STMT-JOURNAL page %d\n", pPg->pgno);
|
||||
CODEC(pPager, pData, pPg->pgno, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
@@ -2310,7 +2339,7 @@ void sqlite3pager_dont_write(Pager *pPager, Pgno pgno){
|
||||
** corruption during the next transaction.
|
||||
*/
|
||||
}else{
|
||||
TRACE2("DONT_WRITE %d\n", pgno);
|
||||
TRACE3("DONT_WRITE page %d of %d\n", pgno, pPager->fd.h);
|
||||
pPg->dirty = 0;
|
||||
}
|
||||
}
|
||||
@@ -2326,7 +2355,7 @@ void sqlite3pager_dont_rollback(void *pData){
|
||||
PgHdr *pPg = DATA_TO_PGHDR(pData);
|
||||
Pager *pPager = pPg->pPager;
|
||||
|
||||
if( pPager->state!=SQLITE_WRITELOCK || pPager->journalOpen==0 ) return;
|
||||
if( pPager->state!=PAGER_EXCLUSIVE || pPager->journalOpen==0 ) return;
|
||||
if( pPg->alwaysRollback || pPager->alwaysRollback || pPager->memDb ) return;
|
||||
if( !pPg->inJournal && (int)pPg->pgno <= pPager->origDbSize ){
|
||||
assert( pPager->aInJournal!=0 );
|
||||
@@ -2336,7 +2365,7 @@ void sqlite3pager_dont_rollback(void *pData){
|
||||
pPager->aInStmt[pPg->pgno/8] |= 1<<(pPg->pgno&7);
|
||||
page_add_to_stmt_list(pPg);
|
||||
}
|
||||
TRACE2("DONT_ROLLBACK %d\n", pPg->pgno);
|
||||
TRACE3("DONT_ROLLBACK page %d of %d\n", pPg->pgno, pPager->fd.h);
|
||||
}
|
||||
if( pPager->stmtInUse && !pPg->inStmt && (int)pPg->pgno<=pPager->stmtSize ){
|
||||
assert( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize );
|
||||
@@ -2379,10 +2408,10 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
rc = pager_errcode(pPager);
|
||||
return rc;
|
||||
}
|
||||
if( pPager->state!=SQLITE_WRITELOCK ){
|
||||
if( pPager->state<PAGER_RESERVED ){
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
TRACE1("COMMIT\n");
|
||||
TRACE2("COMMIT %d\n", pPager->fd.h);
|
||||
if( pPager->memDb ){
|
||||
pPg = pager_get_all_dirty_pages(pPager);
|
||||
while( pPg ){
|
||||
@@ -2394,11 +2423,10 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
pPg = pPg->pDirty;
|
||||
}
|
||||
pPager->pStmt = 0;
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
pPager->state = PAGER_SHARED;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#if 0
|
||||
if( pPager->dirtyFile==0 ){
|
||||
if( pPager->dirtyCache==0 ){
|
||||
/* Exit early (without doing the time-consuming sqlite3OsSync() calls)
|
||||
** if there have been no changes to the database file. */
|
||||
assert( pPager->needSync==0 );
|
||||
@@ -2407,6 +2435,7 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
#if 0
|
||||
rc = syncJournal(pPager, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
@@ -2420,8 +2449,9 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
}
|
||||
#endif
|
||||
rc = sqlite3pager_sync(pPager, 0);
|
||||
if( rc!=SQLITE_OK ) goto commit_abort;
|
||||
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
}
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
@@ -2437,7 +2467,7 @@ commit_abort:
|
||||
}
|
||||
|
||||
/*
|
||||
** Rollback all changes. The database falls back to read-only mode.
|
||||
** Rollback all changes. The database falls back to PAGER_SHARED mode.
|
||||
** All in-memory cache pages revert to their original data contents.
|
||||
** The journal is deleted.
|
||||
**
|
||||
@@ -2450,7 +2480,7 @@ commit_abort:
|
||||
*/
|
||||
int sqlite3pager_rollback(Pager *pPager){
|
||||
int rc;
|
||||
TRACE1("ROLLBACK\n");
|
||||
TRACE2("ROLLBACK %d\n", pPager->fd.h);
|
||||
if( pPager->memDb ){
|
||||
PgHdr *p;
|
||||
for(p=pPager->pAll; p; p=p->pNextAll){
|
||||
@@ -2473,26 +2503,32 @@ int sqlite3pager_rollback(Pager *pPager){
|
||||
pPager->dbSize = pPager->origDbSize;
|
||||
memoryTruncate(pPager);
|
||||
pPager->stmtInUse = 0;
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
pPager->state = PAGER_SHARED;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
if( !pPager->dirtyFile || !pPager->journalOpen ){
|
||||
if( !pPager->dirtyCache || !pPager->journalOpen ){
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if( pPager->errMask!=0 && pPager->errMask!=PAGER_ERR_FULL ){
|
||||
if( pPager->state>=SQLITE_WRITELOCK ){
|
||||
if( pPager->state>=PAGER_EXCLUSIVE ){
|
||||
pager_playback(pPager, 1);
|
||||
}
|
||||
return pager_errcode(pPager);
|
||||
}
|
||||
if( pPager->state!=SQLITE_WRITELOCK ){
|
||||
return SQLITE_OK;
|
||||
if( pPager->state==PAGER_RESERVED ){
|
||||
int rc2;
|
||||
rc = pager_reload_cache(pPager);
|
||||
rc2 = pager_unwritelock(pPager);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = rc2;
|
||||
}
|
||||
}else{
|
||||
rc = pager_playback(pPager, 1);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
rc = SQLITE_CORRUPT;
|
||||
pPager->errMask |= PAGER_ERR_CORRUPT;
|
||||
@@ -2537,7 +2573,7 @@ int sqlite3pager_stmt_begin(Pager *pPager){
|
||||
int rc;
|
||||
char zTemp[SQLITE_TEMPNAME_SIZE];
|
||||
assert( !pPager->stmtInUse );
|
||||
TRACE1("STMT-BEGIN\n");
|
||||
TRACE2("STMT-BEGIN %d\n", pPager->fd.h);
|
||||
if( pPager->memDb ){
|
||||
pPager->stmtInUse = 1;
|
||||
pPager->stmtSize = pPager->dbSize;
|
||||
@@ -2586,7 +2622,7 @@ stmt_begin_failed:
|
||||
int sqlite3pager_stmt_commit(Pager *pPager){
|
||||
if( pPager->stmtInUse ){
|
||||
PgHdr *pPg, *pNext;
|
||||
TRACE1("STMT-COMMIT\n");
|
||||
TRACE2("STMT-COMMIT %d\n", pPager->fd.h);
|
||||
if( !pPager->memDb ){
|
||||
sqlite3OsSeek(&pPager->stfd, 0);
|
||||
/* sqlite3OsTruncate(&pPager->stfd, 0); */
|
||||
@@ -2618,7 +2654,7 @@ int sqlite3pager_stmt_commit(Pager *pPager){
|
||||
int sqlite3pager_stmt_rollback(Pager *pPager){
|
||||
int rc;
|
||||
if( pPager->stmtInUse ){
|
||||
TRACE1("STMT-ROLLBACK\n");
|
||||
TRACE2("STMT-ROLLBACK %d\n", pPager->fd.h);
|
||||
if( pPager->memDb ){
|
||||
PgHdr *pPg;
|
||||
for(pPg=pPager->pStmt; pPg; pPg=pPg->pNextStmt){
|
||||
@@ -2682,7 +2718,7 @@ int sqlite3pager_sync(Pager *pPager, const char *zMaster){
|
||||
/* If this is an in-memory db, or no pages have been written to, this
|
||||
** function is a no-op.
|
||||
*/
|
||||
if( !pPager->memDb && pPager->dirtyFile ){
|
||||
if( !pPager->memDb && pPager->dirtyCache ){
|
||||
PgHdr *pPg;
|
||||
assert( pPager->journalOpen );
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
** subsystem. The page cache subsystem reads and writes a file a page
|
||||
** at a time and provides a journal for rollback.
|
||||
**
|
||||
** @(#) $Id: pager.h,v 1.32 2004/06/09 14:17:21 drh Exp $
|
||||
** @(#) $Id: pager.h,v 1.33 2004/06/09 17:37:28 drh Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -73,6 +73,7 @@ int sqlite3pager_open(Pager **ppPager, const char *zFilename,
|
||||
int nPage, int nExtra, int useJournal,
|
||||
void *pBusyHandler);
|
||||
void sqlite3pager_set_destructor(Pager*, void(*)(void*,int));
|
||||
void sqlite3pager_set_reiniter(Pager*, void(*)(void*,int));
|
||||
void sqlite3pager_set_cachesize(Pager*, int);
|
||||
int sqlite3pager_close(Pager *pPager);
|
||||
int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage);
|
||||
|
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test1.c,v 1.73 2004/06/09 14:01:51 drh Exp $
|
||||
** $Id: test1.c,v 1.74 2004/06/09 17:37:28 drh Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "tcl.h"
|
||||
@@ -1782,7 +1782,7 @@ static int test_sqlite3OsUnlock(
|
||||
if( getFilePointer(interp, Tcl_GetString(objv[1]), &pFile) ){
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3OsUnlock(pFile);
|
||||
rc = sqlite3OsUnlock(pFile, NO_LOCK);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetResult(interp, (char *)errorName(rc), TCL_STATIC);
|
||||
return TCL_ERROR;
|
||||
|
@@ -934,7 +934,7 @@ static int vdbeCommit(sqlite *db){
|
||||
** database) has a transaction active. There is no need for the
|
||||
** master-journal.
|
||||
*/
|
||||
if( nTrans<=1 ){
|
||||
if( nTrans<=100 ){ /**** FIX ME ****/
|
||||
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
@@ -964,12 +964,10 @@ static int vdbeCommit(sqlite *db){
|
||||
|
||||
/* Select a master journal file name */
|
||||
do {
|
||||
int random;
|
||||
if( zMaster ){
|
||||
u32 random;
|
||||
sqliteFree(zMaster);
|
||||
}
|
||||
sqlite3Randomness(sizeof(random), &random);
|
||||
zMaster = sqlite3_mprintf("%s%d", zMainFile, random);
|
||||
zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random);
|
||||
if( !zMaster ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
|
@@ -12,9 +12,8 @@
|
||||
# focus of this script is testing the ATTACH and DETACH commands
|
||||
# and related functionality.
|
||||
#
|
||||
# $Id: attach2.test,v 1.13 2004/06/09 14:01:58 drh Exp $
|
||||
# $Id: attach2.test,v 1.14 2004/06/09 17:37:29 drh Exp $
|
||||
#
|
||||
set sqlite_os_trace 0
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
@@ -147,6 +146,14 @@ db close
|
||||
for {set i 2} {$i<=15} {incr i} {
|
||||
catch {db$i close}
|
||||
}
|
||||
|
||||
# A procedure to verify the status of locks on a database.
|
||||
#
|
||||
proc lock_status {testnum db expected_result} {
|
||||
do_test attach2-$testnum [subst {
|
||||
execsql {PRAGMA lock_status} $db
|
||||
}] $expected_result
|
||||
}
|
||||
set sqlite_os_trace 0
|
||||
|
||||
# Tests attach2-4.* test that read-locks work correctly with attached
|
||||
@@ -158,6 +165,9 @@ do_test attach2-4.1 {
|
||||
execsql {ATTACH 'test2.db' as file2} db2
|
||||
} {}
|
||||
|
||||
lock_status 4.1.1 db {main unlocked temp unlocked file2 unlocked}
|
||||
lock_status 4.1.2 db2 {main unlocked temp unlocked file2 unlocked}
|
||||
|
||||
do_test attach2-4.2 {
|
||||
# Handle 'db' read-locks test.db
|
||||
execsql {BEGIN}
|
||||
@@ -166,10 +176,18 @@ do_test attach2-4.2 {
|
||||
# db - shared(main)
|
||||
# db2 -
|
||||
} {}
|
||||
|
||||
lock_status 4.2.1 db {main shared temp shared file2 unlocked}
|
||||
lock_status 4.2.2 db2 {main unlocked temp unlocked file2 unlocked}
|
||||
|
||||
do_test attach2-4.3 {
|
||||
# The read lock held by db does not prevent db2 from reading test.db
|
||||
execsql {SELECT * FROM t1} db2
|
||||
} {}
|
||||
|
||||
lock_status 4.3.1 db {main shared temp shared file2 unlocked}
|
||||
lock_status 4.3.2 db2 {main unlocked temp unlocked file2 unlocked}
|
||||
|
||||
do_test attach2-4.4 {
|
||||
# db is holding a read lock on test.db, so we should not be able
|
||||
# to commit a write to test.db from db2
|
||||
@@ -177,6 +195,10 @@ do_test attach2-4.4 {
|
||||
INSERT INTO t1 VALUES(1, 2)
|
||||
} db2
|
||||
} {1 {database is locked}}
|
||||
|
||||
lock_status 4.4.1 db {main shared temp shared file2 unlocked}
|
||||
lock_status 4.4.2 db2 {main unlocked temp unlocked file2 unlocked}
|
||||
|
||||
do_test attach2-4.5 {
|
||||
# Handle 'db2' reserves file2.
|
||||
execsql {BEGIN} db2
|
||||
@@ -185,6 +207,10 @@ do_test attach2-4.5 {
|
||||
# db - shared(main)
|
||||
# db2 - reserved(file2)
|
||||
} {}
|
||||
|
||||
lock_status 4.5.1 db {main shared temp shared file2 unlocked}
|
||||
lock_status 4.5.2 db2 {main unlocked temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.6.1 {
|
||||
# Reads are allowed against a reserved database.
|
||||
catchsql {
|
||||
@@ -194,38 +220,57 @@ do_test attach2-4.6.1 {
|
||||
# db - shared(main), shared(file2)
|
||||
# db2 - reserved(file2)
|
||||
} {0 {}}
|
||||
|
||||
lock_status 4.6.1.1 db {main shared temp shared file2 shared}
|
||||
lock_status 4.6.1.2 db2 {main unlocked temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.6.2 {
|
||||
# Writes against a reserved database are not allowed.
|
||||
catchsql {
|
||||
UPDATE file2.t1 SET a=0;
|
||||
}
|
||||
} {1 {database is locked}}
|
||||
|
||||
lock_status 4.6.2.1 db {main shared temp reserved file2 shared}
|
||||
lock_status 4.6.2.2 db2 {main unlocked temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.7 {
|
||||
# Ensure handle 'db' retains the lock on the main file after
|
||||
# failing to obtain a write-lock on file2.
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(1, 2)
|
||||
} db2
|
||||
} {1 {database is locked}}
|
||||
} {0 {}}
|
||||
|
||||
lock_status 4.7.1 db {main shared temp reserved file2 shared}
|
||||
lock_status 4.7.2 db2 {main reserved temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.8 {
|
||||
# Read lock the main file with db2. Now both db and db2 have a read lock
|
||||
# on the main file, db2 has a write-lock on file2.
|
||||
# We should still be able to read test.db from db2
|
||||
execsql {SELECT * FROM t1} db2
|
||||
# Lock status:
|
||||
# db - shared(main), shared(file2)
|
||||
# db2 - shared(main), reserved(file2)
|
||||
} {}
|
||||
} {1 2}
|
||||
|
||||
lock_status 4.8.1 db {main shared temp reserved file2 shared}
|
||||
lock_status 4.8.2 db2 {main reserved temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.9 {
|
||||
# Try to upgrade the handle 'db' lock.
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES(1, 2)
|
||||
}
|
||||
list $r $msg
|
||||
} {1 {database is locked}}
|
||||
|
||||
lock_status 4.9.1 db {main shared temp reserved file2 shared}
|
||||
lock_status 4.9.2 db2 {main reserved temp reserved file2 reserved}
|
||||
|
||||
do_test attach2-4.10 {
|
||||
# Release the locks held by handle 'db2'
|
||||
execsql {COMMIT} db2
|
||||
} {}
|
||||
|
||||
lock_status 4.10.1 db {main shared temp reserved file2 shared}
|
||||
lock_status 4.10.2 db2 {main unlocked temp unlocked file2 unlocked}
|
||||
|
||||
do_test attach2-4.11 {
|
||||
execsql {SELECT * FROM file2.t1}
|
||||
} {1 2}
|
||||
|
Reference in New Issue
Block a user