mirror of
https://github.com/sqlite/sqlite.git
synced 2025-08-05 15:55:57 +03:00
Untested updates to support atomic multi-file transactions (CVS 1526)
FossilOrigin-Name: d57e5252c8baaf615c2cd218a33356ea5d95a5e2
This commit is contained in:
40
manifest
40
manifest
@@ -1,5 +1,5 @@
|
||||
C Fix\sa\ssegfault\sin\ssqlite3OsLock()\s(CVS\s1525)
|
||||
D 2004-06-02T06:30:16
|
||||
C Untested\supdates\sto\ssupport\satomic\smulti-file\stransactions\s(CVS\s1526)
|
||||
D 2004-06-03T16:08:41
|
||||
F Makefile.in ab7b0d5118e2da97bac66be8684a1034e3500f5a
|
||||
F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
|
||||
F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
|
||||
@@ -25,11 +25,11 @@ F sqlite.def fc4f5734786fe4743cfe2aa98eb2da4b089edb5f
|
||||
F sqlite.pc.in 30552343140c53304c2a658c080fbe810cd09ca2
|
||||
F src/attach.c c315c58cb16fd6e913b3bfa6412aedecb4567fa5
|
||||
F src/auth.c 5c2f0bea4729c98c2be3b69d6b466fc51448fe79
|
||||
F src/btree.c bf725408c6ac656327b0f5546c7c5d1eb065d181
|
||||
F src/btree.h 1e2beb41b4b4a4fc41da67cb4692614938066f2f
|
||||
F src/build.c 13985630bf6b3db4c7e7d4c9476bb94495b7d5fa
|
||||
F src/btree.c 3cf513520d5b6fe54cc4c5fb44ce5c6231f1a535
|
||||
F src/btree.h 7b682341772eb1199de3f9c29ce5e34f96836d17
|
||||
F src/build.c e12e602f06e37a0fbcb49af17cba68ad85e101b6
|
||||
F src/date.c 8e6fa3173386fb29fdef012ee08a853c1e9908b2
|
||||
F src/delete.c 72f8febf6170cda830f509c8f9dffbed3df3596c
|
||||
F src/delete.c b30f08250c9ed53a25a13c7c04599c1e8753992d
|
||||
F src/encode.c a876af473d1d636faa3dca51c7571f2e007eea37
|
||||
F src/expr.c 5145de7d25a4b960a4afdb754a9e88b60cce0405
|
||||
F src/func.c 3b87e2e8b9aaa3a6d36b2c9616e7f404be38a667
|
||||
@@ -39,18 +39,18 @@ F src/insert.c 4268d9e3959cc845ea243fb4ec7507269404dad9
|
||||
F src/legacy.c ad23746f15f67e34577621b1875f639c94839e1f
|
||||
F src/main.c d2a7632f459e9c270bb6e313e10916fc840f4a6e
|
||||
F src/md5.c 4302e84ae516c616bb079c4e6d038c0addb33481
|
||||
F src/os.h 833b9639720d1602d9bfa4ca69c615ec2bfe625a
|
||||
F src/os.h cc2fd62b2e8e11103701913871908ff77532af58
|
||||
F src/os_common.h 744286a27de55c52f1b18921e8d17abbf7fafc0f
|
||||
F src/os_mac.c b823874690615ace0dd520d3ad1fe8bfd864b7e0
|
||||
F src/os_mac.h 51d2445f47e182ed32d3bd6937f81070c6fd9bd4
|
||||
F src/os_unix.c 36682ee5e93c2fdad63ff44f7e10ac7d4a39f746
|
||||
F src/os_unix.h 426e1480f0847a7f8ba22aa9ac5115520875610b
|
||||
F src/os_unix.c 3175540f8d1c820dab7a470c50875c221c3a98cd
|
||||
F src/os_unix.h 7999f2246c6347707e98f7078871ea8ca605df3f
|
||||
F src/os_win.c 92b51a38437b98d8aa3ac05b57c71e1d1092e5be
|
||||
F src/os_win.h 5d41af24caaef6c13a2d8e2399caa1c57d45c84d
|
||||
F src/pager.c 048872f1ccd27e4c17d77098eb6e86990a7a9b88
|
||||
F src/pager.h 78a00ac280899bcba1a89dc51585dcae6b7b3253
|
||||
F src/pager.c 1619b6a0338cefa3b4d8be54afbbe1c46e85e64e
|
||||
F src/pager.h ade5bee4a0771adf82180fd702f170cb0031d811
|
||||
F src/parse.y 27c1ce09f9d309be91f9e537df2fb00892990af4
|
||||
F src/pragma.c 7f432dee3c94460638df1e5fffeb59a560943d13
|
||||
F src/pragma.c 1b58d852b84b36a8b84e2245dd29b63c377414ec
|
||||
F src/printf.c ef750e8e2398ca7e8b58be991075f08c6a7f0e53
|
||||
F src/random.c eff68e3f257e05e81eae6c4d50a51eb88beb4ff3
|
||||
F src/select.c 0297717eb7331604687c2e29c147d3a311359df1
|
||||
@@ -61,7 +61,7 @@ F src/table.c af14284fa36c8d41f6829e3f2819dce07d3e2de2
|
||||
F src/tclsqlite.c 3db6b868bd844bfb71720c8e573f4c9b0d536bd5
|
||||
F src/test1.c 4a3cc1b628a29f24c0a43227a035d0f2a96eb634
|
||||
F src/test2.c 6195a1ca2c8d0d2d93644e86da3289b403486872
|
||||
F src/test3.c 86117b74ec7353d76f5cd85c144c7cda23a7e11b
|
||||
F src/test3.c b3f331bda440ae21b6cab5171f28ddb402001f26
|
||||
F src/test4.c caf675e443460ec76b04d78e1688986c17c82cec
|
||||
F src/test5.c e731274b902eaad09b195cfbac06768dfefba93e
|
||||
F src/tokenize.c 183c5d7da11affab5d70d903d33409c8c0ce6c5b
|
||||
@@ -69,12 +69,12 @@ F src/trigger.c 04b2c310d0d056b213609cab6df5fff03d5eaf88
|
||||
F src/update.c 259f06e7b22c684b2d3dda54a18185892d6e9573
|
||||
F src/utf.c c8be20ecdcb10659e23c43e35d835460e964d248
|
||||
F src/util.c d3d2f62ec94160db3cb2b092267405ba99122152
|
||||
F src/vacuum.c c91acc316127411980982938d050b299d42b81ef
|
||||
F src/vdbe.c 4ce596ee57b663d4c428bee5c40f51094525acd9
|
||||
F src/vacuum.c b921eb778842592e1fb48a9d4cef7e861103878f
|
||||
F src/vdbe.c 2cf1376f23e2f8ddd1a55143ea9e0e289095504d
|
||||
F src/vdbe.h e73f890e0f2a6c42b183d7d6937947930fe4fdeb
|
||||
F src/vdbeInt.h f19df2246cf61f4dc27b13c0f9c278a379a935ee
|
||||
F src/vdbeInt.h 9f5df0a21474be02fe870cbb0a414d09b66eb31a
|
||||
F src/vdbeapi.c 77d2e681a992ef189032cd9c1b7bf922f01ebe3e
|
||||
F src/vdbeaux.c 5842109cc9fa76bd454305861c5936b370b9458a
|
||||
F src/vdbeaux.c 55c6d501175edb35cd84430302bbbde8dad4b752
|
||||
F src/vdbemem.c 5d029d83bc60eaf9c45837fcbc0b03348ec95d7a
|
||||
F src/where.c 444a7c3a8b1eb7bba072e489af628555d21d92a4
|
||||
F test/all.test 569a92a8ee88f5300c057cc4a8f50fbbc69a3242
|
||||
@@ -214,7 +214,7 @@ F www/support.tcl 1801397edd271cc39a2aadd54e701184b5181248
|
||||
F www/tclsqlite.tcl 19191cf2a1010eaeff74c51d83fd5f5a4d899075
|
||||
F www/vdbe.tcl 59288db1ac5c0616296b26dce071c36cb611dfe9
|
||||
F www/whentouse.tcl a8335bce47cc2fddb07f19052cb0cb4d9129a8e4
|
||||
P 165d69a04cca719dec2b042117f848f153721a1d
|
||||
R e34294435e363d8121bbbf40938d6dd7
|
||||
P 51348b82c4d5801091537b80059d770410774905
|
||||
R 0e06f4b108dd74f5024eb81017752495
|
||||
U danielk1977
|
||||
Z cf403c13a566790d4729e792131db901
|
||||
Z 4a23c8e1ebb05080684ee0992bb72463
|
||||
|
@@ -1 +1 @@
|
||||
51348b82c4d5801091537b80059d770410774905
|
||||
d57e5252c8baaf615c2cd218a33356ea5d95a5e2
|
40
src/btree.c
40
src/btree.c
@@ -9,7 +9,7 @@
|
||||
** May you share freely, never taking more than you give.
|
||||
**
|
||||
*************************************************************************
|
||||
** $Id: btree.c,v 1.155 2004/06/02 01:22:02 drh Exp $
|
||||
** $Id: btree.c,v 1.156 2004/06/03 16:08:41 danielk1977 Exp $
|
||||
**
|
||||
** This file implements a external (disk-based) database using BTrees.
|
||||
** For a detailed discussion of BTrees, refer to
|
||||
@@ -1200,8 +1200,13 @@ static int newDatabase(Btree *pBt){
|
||||
** sqlite3BtreeInsert()
|
||||
** sqlite3BtreeDelete()
|
||||
** sqlite3BtreeUpdateMeta()
|
||||
**
|
||||
** If wrflag is true, then nMaster specifies the maximum length of
|
||||
** a master journal file name supplied later via sqlite3BtreeSync().
|
||||
** This is so that appropriate space can be allocated in the journal file
|
||||
** when it is created..
|
||||
*/
|
||||
int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
|
||||
int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag, int nMaster){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* If the btree is already in a write-transaction, or it
|
||||
@@ -1221,7 +1226,7 @@ int sqlite3BtreeBeginTrans(Btree *pBt, int wrflag){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK && wrflag ){
|
||||
rc = sqlite3pager_begin(pBt->pPage1->aData);
|
||||
rc = sqlite3pager_begin(pBt->pPage1->aData, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = newDatabase(pBt);
|
||||
}
|
||||
@@ -2073,7 +2078,7 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
|
||||
upr = pPage->nCell-1;
|
||||
pageIntegrity(pPage);
|
||||
while( lwr<=upr ){
|
||||
const void *pCellKey;
|
||||
void *pCellKey;
|
||||
i64 nCellKey;
|
||||
pCur->idx = (lwr+upr)/2;
|
||||
pCur->info.nSize = 0;
|
||||
@@ -2088,13 +2093,13 @@ int sqlite3BtreeMoveto(BtCursor *pCur, const void *pKey, i64 nKey, int *pRes){
|
||||
}
|
||||
}else{
|
||||
int available;
|
||||
pCellKey = fetchPayload(pCur, &available, 0);
|
||||
pCellKey = (void *)fetchPayload(pCur, &available, 0);
|
||||
if( available>=nCellKey ){
|
||||
c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
|
||||
}else{
|
||||
pCellKey = sqliteMallocRaw( nCellKey );
|
||||
if( pCellKey==0 ) return SQLITE_NOMEM;
|
||||
rc = sqlite3BtreeKey(pCur, 0, nCellKey, pCellKey);
|
||||
rc = sqlite3BtreeKey(pCur, 0, nCellKey, (void *)pCellKey);
|
||||
c = pCur->xCompare(pCur->pArg, nCellKey, pCellKey, nKey, pKey);
|
||||
sqliteFree(pCellKey);
|
||||
if( rc ) return rc;
|
||||
@@ -4220,3 +4225,26 @@ int sqlite3BtreeIsInTrans(Btree *pBt){
|
||||
int sqlite3BtreeIsInStmt(Btree *pBt){
|
||||
return (pBt && pBt->inStmt);
|
||||
}
|
||||
|
||||
/*
|
||||
** This call is a no-op if no write-transaction is currently active on pBt.
|
||||
**
|
||||
** Otherwise, sync the database file for the btree pBt. zMaster points to
|
||||
** the name of a master journal file that should be written into the
|
||||
** individual journal file, or is NULL, indicating no master journal file
|
||||
** (single database transaction).
|
||||
**
|
||||
** When this is called, the master journal should already have been
|
||||
** created, populated with this journal pointer and synced to disk.
|
||||
**
|
||||
** Once this is routine has returned, the only thing required to commit
|
||||
** the write-transaction for this database file is to delete the journal.
|
||||
*/
|
||||
int sqlite3BtreeSync(Btree *pBt, const char *zMaster){
|
||||
if( pBt->inTrans==TRANS_WRITE ){
|
||||
return sqlite3pager_sync(pBt->pPager, zMaster);
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
|
||||
|
@@ -13,7 +13,7 @@
|
||||
** subsystem. See comments in the source code for a detailed description
|
||||
** of what each interface routine does.
|
||||
**
|
||||
** @(#) $Id: btree.h,v 1.51 2004/05/31 10:01:35 danielk1977 Exp $
|
||||
** @(#) $Id: btree.h,v 1.52 2004/06/03 16:08:41 danielk1977 Exp $
|
||||
*/
|
||||
#ifndef _BTREE_H_
|
||||
#define _BTREE_H_
|
||||
@@ -41,7 +41,7 @@ int sqlite3BtreeOpen(const char *zFilename, Btree **, int nCache, int flags);
|
||||
int sqlite3BtreeClose(Btree*);
|
||||
int sqlite3BtreeSetCacheSize(Btree*,int);
|
||||
int sqlite3BtreeSetSafetyLevel(Btree*,int);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int);
|
||||
int sqlite3BtreeBeginTrans(Btree*,int,int);
|
||||
int sqlite3BtreeCommit(Btree*);
|
||||
int sqlite3BtreeRollback(Btree*);
|
||||
int sqlite3BtreeBeginStmt(Btree*);
|
||||
@@ -50,6 +50,7 @@ int sqlite3BtreeRollbackStmt(Btree*);
|
||||
int sqlite3BtreeCreateTable(Btree*, int*, int flags);
|
||||
int sqlite3BtreeIsInTrans(Btree*);
|
||||
int sqlite3BtreeIsInStmt(Btree*);
|
||||
int sqlite3BtreeSync(Btree*, const char *zMaster);
|
||||
|
||||
const char *sqlite3BtreeGetFilename(Btree *);
|
||||
int sqlite3BtreeCopyFile(Btree *, Btree *);
|
||||
|
@@ -23,7 +23,7 @@
|
||||
** ROLLBACK
|
||||
** PRAGMA
|
||||
**
|
||||
** $Id: build.c,v 1.205 2004/05/31 18:51:58 drh Exp $
|
||||
** $Id: build.c,v 1.206 2004/06/03 16:08:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -556,7 +556,7 @@ void sqlite3StartTable(
|
||||
return;
|
||||
}
|
||||
if( db->flags & !db->autoCommit ){
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1);
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[1].pBt, 1, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3ErrorMsg(pParse, "unable to get a write lock on "
|
||||
"the temporary database file");
|
||||
|
@@ -12,7 +12,7 @@
|
||||
** This file contains C code routines that are called by the parser
|
||||
** to handle DELETE FROM statements.
|
||||
**
|
||||
** $Id: delete.c,v 1.71 2004/05/28 16:00:22 drh Exp $
|
||||
** $Id: delete.c,v 1.72 2004/06/03 16:08:41 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
|
||||
@@ -377,7 +377,6 @@ void sqlite3GenerateRowIndexDelete(
|
||||
Index *pIdx;
|
||||
|
||||
for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){
|
||||
int j;
|
||||
if( aIdxUsed!=0 && aIdxUsed[i-1]==0 ) continue;
|
||||
sqlite3GenerateIndexKey(v, pIdx, iCur);
|
||||
sqlite3VdbeAddOp(v, OP_IdxDelete, iCur+i, 0);
|
||||
|
1
src/os.h
1
src/os.h
@@ -114,5 +114,6 @@ void sqlite3OsEnterMutex(void);
|
||||
void sqlite3OsLeaveMutex(void);
|
||||
char *sqlite3OsFullPathname(const char*);
|
||||
int sqlite3OsLock(OsFile*, int);
|
||||
int sqlite3OsCheckWriteLock(OsFile *id);
|
||||
|
||||
#endif /* _SQLITE_OS_H_ */
|
||||
|
@@ -61,6 +61,10 @@
|
||||
*/
|
||||
#include "os_common.h"
|
||||
|
||||
#if defined(THREADSAFE) && defined(__linux__)
|
||||
#define getpid pthread_self
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Here is the dirt on POSIX advisory locks: ANSI STD 1003.1 (1996)
|
||||
** section 6.5.2.2 lines 483 through 490 specify that when a process
|
||||
@@ -170,7 +174,7 @@ struct lockKey {
|
||||
*/
|
||||
struct lockInfo {
|
||||
struct lockKey key; /* The lookup key */
|
||||
int cnt; /* 0: unlocked. -1: write lock. 1...: read lock. */
|
||||
int cnt; /* Number of locks held */
|
||||
int locktype; /* One of SHARED_LOCK, RESERVED_LOCK etc. */
|
||||
int nRef; /* Number of pointers to this structure */
|
||||
};
|
||||
@@ -355,7 +359,7 @@ int sqlite3OsOpenReadWrite(
|
||||
close(id->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
id->locked = 0;
|
||||
id->locktype = 0;
|
||||
TRACE3("OPEN %-3d %s\n", id->fd, zFilename);
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
@@ -395,7 +399,7 @@ int sqlite3OsOpenExclusive(const char *zFilename, OsFile *id, int delFlag){
|
||||
unlink(zFilename);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
id->locked = 0;
|
||||
id->locktype = 0;
|
||||
if( delFlag ){
|
||||
unlink(zFilename);
|
||||
}
|
||||
@@ -425,7 +429,7 @@ int sqlite3OsOpenReadOnly(const char *zFilename, OsFile *id){
|
||||
close(id->fd);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
id->locked = 0;
|
||||
id->locktype = 0;
|
||||
TRACE3("OPEN-RO %-3d %s\n", id->fd, zFilename);
|
||||
OpenCounter(+1);
|
||||
return SQLITE_OK;
|
||||
@@ -661,6 +665,41 @@ int sqlite3OsWriteLock(OsFile *id){
|
||||
return sqlite3OsLock(id, EXCLUSIVE_LOCK);
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine checks if there is a RESERVED lock held on the specified
|
||||
** file by this or any other process. If such a lock is held, return
|
||||
** non-zero, otherwise zero.
|
||||
*/
|
||||
int sqlite3OsCheckWriteLock(OsFile *id){
|
||||
int r = 0;
|
||||
|
||||
sqlite3OsEnterMutex();
|
||||
|
||||
/* Check if a thread in this process holds such a lock */
|
||||
if( id->pLock->locktype>SHARED_LOCK ){
|
||||
r = 1;
|
||||
}
|
||||
|
||||
/* Otherwise see if some other process holds it. Just check the whole
|
||||
** file for write-locks, rather than any specific bytes.
|
||||
*/
|
||||
if( !r ){
|
||||
struct flock lock;
|
||||
lock.l_whence = SEEK_SET;
|
||||
lock.l_start = 0;
|
||||
lock.l_len = 0;
|
||||
lock.l_type = F_RDLCK;
|
||||
fcntl(id->fd, F_GETLK, &lock);
|
||||
if( lock.l_type!=F_UNLCK ){
|
||||
r = 1;
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3OsLeaveMutex();
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
** Lock the file with the lock specified by parameter locktype - one
|
||||
** of the following:
|
||||
@@ -677,17 +716,17 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
int s;
|
||||
|
||||
/* It is an error to request any kind of lock before a shared lock */
|
||||
if( locktype>SHARED_LOCK && id->locked==0 ){
|
||||
if( locktype>SHARED_LOCK && id->locktype==0 ){
|
||||
rc = sqlite3OsLock(id, SHARED_LOCK);
|
||||
if( rc!=SQLITE_OK ) return rc;
|
||||
}
|
||||
assert( locktype==SHARED_LOCK || id->locked!=0 );
|
||||
assert( locktype==SHARED_LOCK || id->locktype!=0 );
|
||||
|
||||
/* 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
|
||||
** sqlite3OsEnterMutex() hasn't been called yet.
|
||||
*/
|
||||
if( id->locked>=locktype ){
|
||||
if( id->locktype>=locktype ){
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
@@ -696,7 +735,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
/* If some thread using this PID has a lock via a different OsFile*
|
||||
** handle that precludes the requested lock, return BUSY.
|
||||
*/
|
||||
if( (id->locked!=pLock->locktype &&
|
||||
if( (id->locktype!=pLock->locktype &&
|
||||
(pLock->locktype>RESERVED_LOCK || locktype!=SHARED_LOCK)) ||
|
||||
(locktype>RESERVED_LOCK && pLock->cnt>1)
|
||||
){
|
||||
@@ -711,15 +750,15 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
if( locktype==SHARED_LOCK &&
|
||||
(pLock->locktype==SHARED_LOCK || pLock->locktype==RESERVED_LOCK) ){
|
||||
assert( locktype==SHARED_LOCK );
|
||||
assert( id->locked==0 );
|
||||
assert( id->locktype==0 );
|
||||
assert( pLock->cnt>0 );
|
||||
id->locked = SHARED_LOCK;
|
||||
id->locktype = SHARED_LOCK;
|
||||
pLock->cnt++;
|
||||
id->pOpen->nLock++;
|
||||
goto end_lock;
|
||||
}
|
||||
|
||||
lock.l_len = 0L;
|
||||
lock.l_len = 1L;
|
||||
lock.l_whence = SEEK_SET;
|
||||
|
||||
/* If control gets to this point, then actually go ahead and make
|
||||
@@ -749,7 +788,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
if( s ){
|
||||
rc = (errno==EINVAL) ? SQLITE_NOLFS : SQLITE_BUSY;
|
||||
}else{
|
||||
id->locked = SHARED_LOCK;
|
||||
id->locktype = SHARED_LOCK;
|
||||
id->pOpen->nLock++;
|
||||
pLock->cnt = 1;
|
||||
}
|
||||
@@ -758,7 +797,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
** assumed that there is a SHARED or greater lock on the file
|
||||
** already.
|
||||
*/
|
||||
assert( 0!=id->locked );
|
||||
assert( 0!=id->locktype );
|
||||
lock.l_type = F_WRLCK;
|
||||
switch( locktype ){
|
||||
case RESERVED_LOCK:
|
||||
@@ -780,7 +819,7 @@ int sqlite3OsLock(OsFile *id, int locktype){
|
||||
}
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
id->locked = locktype;
|
||||
id->locktype = locktype;
|
||||
pLock->locktype = locktype;
|
||||
assert( pLock->locktype==RESERVED_LOCK || pLock->cnt==1 );
|
||||
}
|
||||
@@ -798,8 +837,8 @@ end_lock:
|
||||
*/
|
||||
int sqlite3OsUnlock(OsFile *id){
|
||||
int rc;
|
||||
if( !id->locked ) return SQLITE_OK;
|
||||
id->locked = 0;
|
||||
if( !id->locktype ) return SQLITE_OK;
|
||||
id->locktype = 0;
|
||||
sqlite3OsEnterMutex();
|
||||
assert( id->pLock->cnt!=0 );
|
||||
if( id->pLock->cnt>1 ){
|
||||
@@ -840,7 +879,7 @@ int sqlite3OsUnlock(OsFile *id){
|
||||
}
|
||||
}
|
||||
sqlite3OsLeaveMutex();
|
||||
id->locked = 0;
|
||||
id->locktype = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@@ -56,13 +56,16 @@
|
||||
** of an open file handle. It is defined differently for each architecture.
|
||||
**
|
||||
** This is the definition for Unix.
|
||||
**
|
||||
** OsFile.locktype takes one of the values SHARED_LOCK, RESERVED_LOCK,
|
||||
** PENDING_LOCK or EXCLUSIVE_LOCK.
|
||||
*/
|
||||
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 locked; /* True if this instance holds the lock */
|
||||
int locktype; /* The type of lock held on this fd */
|
||||
int dirfd; /* File descriptor for the directory */
|
||||
};
|
||||
|
||||
|
291
src/pager.c
291
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.109 2004/05/31 08:26:49 danielk1977 Exp $
|
||||
** @(#) $Id: pager.c,v 1.110 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "os.h" /* Must be first to enable large file support */
|
||||
#include "sqliteInt.h"
|
||||
@@ -212,6 +212,7 @@ struct Pager {
|
||||
PgHdr *pAll; /* List of all pages */
|
||||
PgHdr *pStmt; /* List of pages in the statement subjournal */
|
||||
PgHdr *aHash[N_PG_HASH]; /* Hash table to map page number of PgHdr */
|
||||
int nMaster; /* Number of bytes to reserve for master j.p */
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -299,11 +300,18 @@ int journal_format = 3;
|
||||
** to which journal format is being used. The following macros figure out
|
||||
** the sizes based on format numbers.
|
||||
*/
|
||||
/*
|
||||
#define JOURNAL_HDR_SZ(X) \
|
||||
(sizeof(aJournalMagic1) + sizeof(Pgno) + ((X)>=3)*2*sizeof(u32))
|
||||
*/
|
||||
#define JOURNAL_HDR_SZ(pPager, X) (\
|
||||
sizeof(aJournalMagic1) + \
|
||||
sizeof(Pgno) + \
|
||||
((X)>=3?3*sizeof(u32)+(pPager)->nMaster:0) )
|
||||
#define JOURNAL_PG_SZ(X) \
|
||||
(SQLITE_PAGE_SIZE + sizeof(Pgno) + ((X)>=3)*sizeof(u32))
|
||||
|
||||
|
||||
/*
|
||||
** Enable reference count tracking here:
|
||||
*/
|
||||
@@ -601,6 +609,104 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Parameter zMaster is the name of a master journal file. A single journal
|
||||
** file that referred to the master journal file has just been rolled back.
|
||||
** This routine checks if it is possible to delete the master journal file,
|
||||
** and does so if it is.
|
||||
*/
|
||||
static int pager_delmaster(const char *zMaster){
|
||||
int rc;
|
||||
int master_open = 0;
|
||||
OsFile master;
|
||||
char *zMasterJournal = 0; /* Contents of master journal file */
|
||||
off_t nMasterJournal; /* Size of master journal file */
|
||||
|
||||
/* Open the master journal file exclusively in case some other process
|
||||
** is running this routine also. Not that it makes too much difference.
|
||||
*/
|
||||
rc = sqlite3OsOpenExclusive(zMaster, &master, 0);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
master_open = 1;
|
||||
|
||||
rc = sqlite3OsFileSize(&master, &nMasterJournal);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
|
||||
if( nMasterJournal>0 ){
|
||||
char *zDb;
|
||||
zMasterJournal = (char *)sqliteMalloc(nMasterJournal);
|
||||
if( !zMasterJournal ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto delmaster_out;
|
||||
}
|
||||
rc = sqlite3OsRead(&master, zMasterJournal, nMasterJournal);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
|
||||
zDb = zMasterJournal;
|
||||
while( (zDb-zMasterJournal)<nMasterJournal ){
|
||||
char *zJournal = 0;
|
||||
sqlite3SetString(&zJournal, zDb, "-journal", 0);
|
||||
if( !zJournal ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto delmaster_out;
|
||||
}
|
||||
if( sqlite3OsFileExists(zJournal) ){
|
||||
/* One of the journals pointed to by the master journal exists.
|
||||
** Open it and check if it points at the master journal. If
|
||||
** so, return without deleting the master journal file.
|
||||
*/
|
||||
OsFile journal;
|
||||
int nMaster;
|
||||
|
||||
rc = sqlite3OsOpenReadOnly(zJournal, &journal);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsClose(&journal);
|
||||
sqliteFree(zJournal);
|
||||
goto delmaster_out;
|
||||
}
|
||||
sqlite3OsClose(&journal);
|
||||
|
||||
/* Seek to the point in the journal where the master journal name
|
||||
** is stored. Read the master journal name into memory obtained
|
||||
** from malloc.
|
||||
*/
|
||||
rc = sqlite3OsSeek(&journal, sizeof(aJournalMagic3)+2*sizeof(u32));
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
rc = read32bits(3, &journal, (u32 *)&nMaster);
|
||||
if( rc!=SQLITE_OK ) goto delmaster_out;
|
||||
if( nMaster>0 && nMaster==strlen(zMaster)+1 ){
|
||||
char *zMasterPtr = (char *)sqliteMalloc(nMaster);
|
||||
if( !zMasterPtr ){
|
||||
rc = SQLITE_NOMEM;
|
||||
}
|
||||
rc = sqlite3OsRead(&journal, zMasterPtr, nMaster);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(zMasterPtr);
|
||||
goto delmaster_out;
|
||||
}
|
||||
if( 0==strncmp(zMasterPtr, zMaster, nMaster) ){
|
||||
/* We have a match. Do not delete the master journal file. */
|
||||
sqliteFree(zMasterPtr);
|
||||
goto delmaster_out;
|
||||
}
|
||||
}
|
||||
}
|
||||
zDb += (strlen(zDb)+1);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3OsDelete(zMaster);
|
||||
|
||||
delmaster_out:
|
||||
if( zMasterJournal ){
|
||||
sqliteFree(zMasterJournal);
|
||||
}
|
||||
if( master_open ){
|
||||
sqlite3OsClose(&master);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Playback the journal and thus restore the database file to
|
||||
** the state it was in before we started making changes.
|
||||
@@ -661,6 +767,7 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
int format; /* Format of the journal file. */
|
||||
unsigned char aMagic[sizeof(aJournalMagic1)];
|
||||
int rc;
|
||||
char *zMaster = 0; /* Name of master journal file if any */
|
||||
|
||||
/* Figure out how many records are in the journal. Abort early if
|
||||
** the journal is empty.
|
||||
@@ -701,7 +808,7 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
goto end_playback;
|
||||
}
|
||||
if( format>=JOURNAL_FORMAT_3 ){
|
||||
if( szJ < sizeof(aMagic) + 3*sizeof(u32) ){
|
||||
if( szJ < sizeof(aMagic) + 4*sizeof(u32) ){
|
||||
/* Ignore the journal if it is too small to contain a complete
|
||||
** header. We already did this test once above, but at the prior
|
||||
** test, we did not know the journal format and so we had to assume
|
||||
@@ -715,11 +822,28 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
rc = read32bits(format, &pPager->jfd, &pPager->cksumInit);
|
||||
if( rc ) goto end_playback;
|
||||
if( nRec==0xffffffff || useJournalSize ){
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(3))/JOURNAL_PG_SZ(3);
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(pPager, 3))/JOURNAL_PG_SZ(3);
|
||||
}
|
||||
|
||||
/* Check if a master journal file is specified. If one is specified,
|
||||
** only proceed with the playback if it still exists.
|
||||
*/
|
||||
rc = read32bits(format, &pPager->jfd, &pPager->nMaster);
|
||||
if( rc ) goto end_playback;
|
||||
if( pPager->nMaster>0 ){
|
||||
zMaster = sqliteMalloc(pPager->nMaster);
|
||||
if( !zMaster ){
|
||||
rc = SQLITE_NOMEM;
|
||||
goto end_playback;
|
||||
}
|
||||
rc = sqlite3OsRead(&pPager->jfd, zMaster, pPager->nMaster);
|
||||
if( rc!=SQLITE_OK || (strlen(zMaster) && !sqlite3OsFileExists(zMaster)) ){
|
||||
goto end_playback;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(2))/JOURNAL_PG_SZ(2);
|
||||
assert( nRec*JOURNAL_PG_SZ(2)+JOURNAL_HDR_SZ(2)==szJ );
|
||||
nRec = (szJ - JOURNAL_HDR_SZ(pPager, 2))/JOURNAL_PG_SZ(2);
|
||||
assert( nRec*JOURNAL_PG_SZ(2)+JOURNAL_HDR_SZ(pPager, 2)==szJ );
|
||||
}
|
||||
rc = read32bits(format, &pPager->jfd, &mxPg);
|
||||
if( rc!=SQLITE_OK ){
|
||||
@@ -772,7 +896,21 @@ static int pager_playback(Pager *pPager, int useJournalSize){
|
||||
}
|
||||
|
||||
end_playback:
|
||||
if( zMaster ){
|
||||
/* If there was a master journal and this routine will return true,
|
||||
** see if it is possible to delete the master journal. If errors
|
||||
** occur during this process, ignore them.
|
||||
*/
|
||||
if( rc==SQLITE_OK ){
|
||||
pager_delmaster(zMaster);
|
||||
}
|
||||
sqliteFree(zMaster);
|
||||
}
|
||||
if( rc!=SQLITE_OK ){
|
||||
/* FIX ME: We shouldn't delete the journal if an error occured during
|
||||
** rollback. It may have been a transient error and the rollback may
|
||||
** succeed next time it is attempted.
|
||||
*/
|
||||
pager_unwritelock(pPager);
|
||||
pPager->errMask |= PAGER_ERR_CORRUPT;
|
||||
rc = SQLITE_CORRUPT;
|
||||
@@ -1064,7 +1202,7 @@ int sqlite3pager_pagecount(Pager *pPager){
|
||||
/*
|
||||
** Forward declaration
|
||||
*/
|
||||
static int syncJournal(Pager*);
|
||||
static int syncJournal(Pager*, const char*);
|
||||
|
||||
|
||||
/*
|
||||
@@ -1156,7 +1294,7 @@ int sqlite3pager_truncate(Pager *pPager, Pgno nPage){
|
||||
memoryTruncate(pPager);
|
||||
return SQLITE_OK;
|
||||
}
|
||||
syncJournal(pPager);
|
||||
syncJournal(pPager, 0);
|
||||
rc = sqlite3OsTruncate(&pPager->fd, SQLITE_PAGE_SIZE*(off_t)nPage);
|
||||
if( rc==SQLITE_OK ){
|
||||
pPager->dbSize = nPage;
|
||||
@@ -1302,14 +1440,14 @@ int sqlite3pager_ref(void *pData){
|
||||
** This routine clears the needSync field of every page current held in
|
||||
** memory.
|
||||
*/
|
||||
static int syncJournal(Pager *pPager){
|
||||
static int syncJournal(Pager *pPager, const char *zMaster){
|
||||
PgHdr *pPg;
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* Sync the journal before modifying the main database
|
||||
** (assuming there is a journal and it needs to be synced.)
|
||||
*/
|
||||
if( pPager->needSync ){
|
||||
if( pPager->needSync || zMaster ){
|
||||
if( !pPager->tempFile ){
|
||||
assert( pPager->journalOpen );
|
||||
/* assert( !pPager->noSync ); // noSync might be set if synchronous
|
||||
@@ -1320,7 +1458,7 @@ static int syncJournal(Pager *pPager){
|
||||
** with the nRec computed from the size of the journal file.
|
||||
*/
|
||||
off_t hdrSz, pgSz, jSz;
|
||||
hdrSz = JOURNAL_HDR_SZ(journal_format);
|
||||
hdrSz = JOURNAL_HDR_SZ(pPager, journal_format);
|
||||
pgSz = JOURNAL_PG_SZ(journal_format);
|
||||
rc = sqlite3OsFileSize(&pPager->jfd, &jSz);
|
||||
if( rc!=0 ) return rc;
|
||||
@@ -1338,7 +1476,17 @@ static int syncJournal(Pager *pPager){
|
||||
sqlite3OsSeek(&pPager->jfd, sizeof(aJournalMagic1));
|
||||
rc = write32bits(&pPager->jfd, pPager->nRec);
|
||||
if( rc ) return rc;
|
||||
szJ = JOURNAL_HDR_SZ(journal_format) +
|
||||
|
||||
/* Write the name of the master journal file if one is specified */
|
||||
if( zMaster ){
|
||||
assert( strlen(zMaster)<pPager->nMaster );
|
||||
rc = sqlite3OsSeek(&pPager->jfd, sizeof(aJournalMagic3) + 3*4);
|
||||
if( rc ) return rc;
|
||||
rc = sqlite3OsWrite(&pPager->jfd, zMaster, strlen(zMaster)+1);
|
||||
if( rc ) return rc;
|
||||
}
|
||||
|
||||
szJ = JOURNAL_HDR_SZ(pPager, journal_format) +
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format);
|
||||
sqlite3OsSeek(&pPager->jfd, szJ);
|
||||
}
|
||||
@@ -1451,7 +1599,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
return pager_errcode(pPager);
|
||||
}
|
||||
|
||||
/* If this is the first page accessed, then get a read lock
|
||||
/* If this is the first page accessed, then get a SHARED lock
|
||||
** on the database file.
|
||||
*/
|
||||
if( pPager->nRef==0 && !pPager->memDb ){
|
||||
@@ -1461,14 +1609,17 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
}
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
|
||||
/* If a journal file exists, try to play it back.
|
||||
/* 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) ){
|
||||
if( pPager->useJournal &&
|
||||
sqlite3OsFileExists(pPager->zJournal) &&
|
||||
!sqlite3OsCheckWriteLock(&pPager->fd)
|
||||
){
|
||||
int rc;
|
||||
|
||||
/* Get a write lock on the database
|
||||
*/
|
||||
rc = sqlite3OsWriteLock(&pPager->fd);
|
||||
/* 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! */
|
||||
@@ -1545,7 +1696,7 @@ int sqlite3pager_get(Pager *pPager, Pgno pgno, void **ppPage){
|
||||
** it can't be helped.
|
||||
*/
|
||||
if( pPg==0 ){
|
||||
int rc = syncJournal(pPager);
|
||||
int rc = syncJournal(pPager, 0);
|
||||
if( rc!=0 ){
|
||||
sqlite3pager_rollback(pPager);
|
||||
return SQLITE_IOERR;
|
||||
@@ -1764,6 +1915,14 @@ static int pager_open_journal(Pager *pPager){
|
||||
}
|
||||
pPager->origDbSize = pPager->dbSize;
|
||||
if( journal_format==JOURNAL_FORMAT_3 ){
|
||||
/* Create the header for a format 3 journal:
|
||||
** - 8 bytes: Magic identifying journal format 3.
|
||||
** - 4 bytes: Number of records in journal, or -1 no-sync mode is on.
|
||||
** - 4 bytes: Magic used for page checksums.
|
||||
** - 4 bytes: Number of bytes reserved for master journal ptr (nMaster)
|
||||
** - nMaster bytes: Space for a master journal pointer.
|
||||
** - 4 bytes: Initial database page count.
|
||||
*/
|
||||
rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic3, sizeof(aJournalMagic3));
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = write32bits(&pPager->jfd, pPager->noSync ? 0xffffffff : 0);
|
||||
@@ -1772,6 +1931,22 @@ static int pager_open_journal(Pager *pPager){
|
||||
sqlite3Randomness(sizeof(pPager->cksumInit), &pPager->cksumInit);
|
||||
rc = write32bits(&pPager->jfd, pPager->cksumInit);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = write32bits(&pPager->jfd, pPager->nMaster);
|
||||
}
|
||||
|
||||
/* Unless the size reserved for the master-journal pointer is 0, set
|
||||
** the first byte of the master journal pointer to 0x00. Either way,
|
||||
** this is interpreted as 'no master journal' in the event of a
|
||||
** rollback after a crash.
|
||||
*/
|
||||
if( rc==SQLITE_OK && pPager->nMaster>0 ){
|
||||
rc = sqlite3OsWrite(&pPager->jfd, "", 1);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3OsSeek(&pPager->jfd,
|
||||
sizeof(aJournalMagic3) + 3*4 + pPager->nMaster);
|
||||
}
|
||||
}else if( journal_format==JOURNAL_FORMAT_2 ){
|
||||
rc = sqlite3OsWrite(&pPager->jfd, aJournalMagic2, sizeof(aJournalMagic2));
|
||||
}else{
|
||||
@@ -1802,22 +1977,26 @@ static int pager_open_journal(Pager *pPager){
|
||||
** * sqlite3pager_close() is called.
|
||||
** * sqlite3pager_unref() is called to on every outstanding page.
|
||||
**
|
||||
** The parameter to this routine is a pointer to any open page of the
|
||||
** database file. Nothing changes about the page - it is used merely
|
||||
** to acquire a pointer to the Pager structure and as proof that there
|
||||
** is already a read-lock on the database.
|
||||
** The first parameter to this routine is a pointer to any open page of the
|
||||
** database file. Nothing changes about the page - it is used merely to
|
||||
** acquire a pointer to the Pager structure and as proof that there is
|
||||
** already a read-lock on the database.
|
||||
**
|
||||
** A journal file is opened if this is not a temporary file. For
|
||||
** temporary files, the opening of the journal file is deferred until
|
||||
** there is an actual need to write to the journal.
|
||||
** The second parameter indicates how much space in bytes to reserve for a
|
||||
** master journal file-name at the start of the journal when it is created.
|
||||
**
|
||||
** A journal file is opened if this is not a temporary file. For temporary
|
||||
** 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.
|
||||
*/
|
||||
int sqlite3pager_begin(void *pData){
|
||||
int sqlite3pager_begin(void *pData, int nMaster){
|
||||
PgHdr *pPg = DATA_TO_PGHDR(pData);
|
||||
Pager *pPager = pPg->pPager;
|
||||
int rc = SQLITE_OK;
|
||||
assert( pPg->nRef>0 );
|
||||
assert( nMaster>=0 );
|
||||
assert( pPager->state!=SQLITE_UNLOCK );
|
||||
if( pPager->state==SQLITE_READLOCK ){
|
||||
assert( pPager->aInJournal==0 );
|
||||
@@ -1829,6 +2008,7 @@ int sqlite3pager_begin(void *pData){
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
pPager->nMaster = nMaster;
|
||||
pPager->state = SQLITE_WRITELOCK;
|
||||
pPager->dirtyFile = 0;
|
||||
TRACE1("TRANSACTION\n");
|
||||
@@ -1888,7 +2068,7 @@ int sqlite3pager_write(void *pData){
|
||||
** create it if it does not.
|
||||
*/
|
||||
assert( pPager->state!=SQLITE_UNLOCK );
|
||||
rc = sqlite3pager_begin(pData);
|
||||
rc = sqlite3pager_begin(pData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
return rc;
|
||||
}
|
||||
@@ -1917,7 +2097,7 @@ int sqlite3pager_write(void *pData){
|
||||
memcpy(pHist->pOrig, PGHDR_TO_DATA(pPg), pPager->pageSize);
|
||||
}
|
||||
pPg->inJournal = 1;
|
||||
}else {
|
||||
}else{
|
||||
if( journal_format>=JOURNAL_FORMAT_3 ){
|
||||
u32 cksum = pager_cksum(pPager, pPg->pgno, pData);
|
||||
saved = *(u32*)PGHDR_TO_EXTRA(pPg);
|
||||
@@ -2155,6 +2335,7 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
pPager->state = SQLITE_READLOCK;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
#if 0
|
||||
if( pPager->dirtyFile==0 ){
|
||||
/* Exit early (without doing the time-consuming sqlite3OsSync() calls)
|
||||
** if there have been no changes to the database file. */
|
||||
@@ -2164,7 +2345,7 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
return rc;
|
||||
}
|
||||
assert( pPager->journalOpen );
|
||||
rc = syncJournal(pPager);
|
||||
rc = syncJournal(pPager, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
goto commit_abort;
|
||||
}
|
||||
@@ -2175,6 +2356,10 @@ int sqlite3pager_commit(Pager *pPager){
|
||||
goto commit_abort;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
rc = sqlite3pager_sync(pPager, 0);
|
||||
if( rc!=SQLITE_OK ) goto commit_abort;
|
||||
|
||||
rc = pager_unwritelock(pPager);
|
||||
pPager->dbSize = -1;
|
||||
return rc;
|
||||
@@ -2310,10 +2495,11 @@ int sqlite3pager_stmt_begin(Pager *pPager){
|
||||
rc = sqlite3OsFileSize(&pPager->jfd, &pPager->stmtJSize);
|
||||
if( rc ) goto stmt_begin_failed;
|
||||
assert( pPager->stmtJSize ==
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format)+JOURNAL_HDR_SZ(journal_format) );
|
||||
pPager->nRec*JOURNAL_PG_SZ(journal_format) +
|
||||
JOURNAL_HDR_SZ(pPager, journal_format) );
|
||||
#endif
|
||||
pPager->stmtJSize = pPager->nRec*JOURNAL_PG_SZ(journal_format)
|
||||
+ JOURNAL_HDR_SZ(journal_format);
|
||||
+ JOURNAL_HDR_SZ(pPager, journal_format);
|
||||
pPager->stmtSize = pPager->dbSize;
|
||||
if( !pPager->stmtOpen ){
|
||||
rc = sqlite3pager_opentemp(zTemp, &pPager->stfd);
|
||||
@@ -2414,6 +2600,49 @@ void sqlite3pager_set_codec(
|
||||
pPager->pCodecArg = pCodecArg;
|
||||
}
|
||||
|
||||
/*
|
||||
** Sync the database file for the pager pPager. zMaster points to the name
|
||||
** of a master journal file that should be written into the individual
|
||||
** journal file. zMaster may be NULL, which is interpreted as no master
|
||||
** journal (a single database transaction).
|
||||
**
|
||||
** This routine ensures that the journal is synced, all dirty pages written
|
||||
** to the database file and the database file synced. The only thing that
|
||||
** remains to commit the transaction is to delete the journal file (or
|
||||
** master journal file if specified).
|
||||
**
|
||||
** Note that if zMaster==NULL, this does not overwrite a previous value
|
||||
** passed to an sqlite3pager_sync() call.
|
||||
*/
|
||||
int sqlite3pager_sync(Pager *pPager, const char *zMaster){
|
||||
int rc = SQLITE_OK;
|
||||
|
||||
/* 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 ){
|
||||
PgHdr *pPg;
|
||||
assert( pPager->journalOpen );
|
||||
|
||||
/* Sync the journal file */
|
||||
rc = syncJournal(pPager, zMaster);
|
||||
if( rc!=SQLITE_OK ) goto sync_exit;
|
||||
|
||||
/* Write all dirty pages to the database file */
|
||||
pPg = pager_get_all_dirty_pages(pPager);
|
||||
rc = pager_write_pagelist(pPg);
|
||||
if( rc!=SQLITE_OK ) goto sync_exit;
|
||||
|
||||
/* If any pages were actually written, sync the database file */
|
||||
if( pPg && !pPager->noSync ){
|
||||
rc = sqlite3OsSync(&pPager->fd);
|
||||
}
|
||||
}
|
||||
|
||||
sync_exit:
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_TEST
|
||||
/*
|
||||
** Print a listing of all referenced pages and their ref count.
|
||||
|
@@ -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.29 2004/05/14 01:58:13 drh Exp $
|
||||
** @(#) $Id: pager.h,v 1.30 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -84,8 +84,9 @@ int sqlite3pager_iswriteable(void*);
|
||||
int sqlite3pager_overwrite(Pager *pPager, Pgno pgno, void*);
|
||||
int sqlite3pager_pagecount(Pager*);
|
||||
int sqlite3pager_truncate(Pager*,Pgno);
|
||||
int sqlite3pager_begin(void*);
|
||||
int sqlite3pager_begin(void*,int);
|
||||
int sqlite3pager_commit(Pager*);
|
||||
int sqlite3pager_sync(Pager*,const char *zMaster);
|
||||
int sqlite3pager_rollback(Pager*);
|
||||
int sqlite3pager_isreadonly(Pager*);
|
||||
int sqlite3pager_stmt_begin(Pager*);
|
||||
|
@@ -11,7 +11,7 @@
|
||||
*************************************************************************
|
||||
** This file contains code used to implement the PRAGMA command.
|
||||
**
|
||||
** $Id: pragma.c,v 1.35 2004/05/31 08:26:49 danielk1977 Exp $
|
||||
** $Id: pragma.c,v 1.36 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include <ctype.h>
|
||||
@@ -632,7 +632,7 @@ void sqlite3Pragma(Parse *pParse, Token *pLeft, Token *pRight, int minusFlag){
|
||||
loopTop = sqlite3VdbeAddOp(v, OP_Rewind, 1, 0);
|
||||
sqlite3VdbeAddOp(v, OP_MemIncr, 1, 0);
|
||||
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
|
||||
int k, jmp2;
|
||||
int jmp2;
|
||||
static VdbeOpList idxErr[] = {
|
||||
{ OP_MemIncr, 0, 0, 0},
|
||||
{ OP_String8, 0, 0, "rowid "},
|
||||
|
@@ -13,7 +13,7 @@
|
||||
** is not included in the SQLite library. It is used for automated
|
||||
** testing of the SQLite library.
|
||||
**
|
||||
** $Id: test3.c,v 1.41 2004/05/31 10:01:35 danielk1977 Exp $
|
||||
** $Id: test3.c,v 1.42 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "pager.h"
|
||||
@@ -129,7 +129,7 @@ static int btree_begin_transaction(
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( Tcl_GetInt(interp, argv[1], (int*)&pBt) ) return TCL_ERROR;
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 1);
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 1, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_AppendResult(interp, errorName(rc), 0);
|
||||
return TCL_ERROR;
|
||||
|
@@ -14,7 +14,7 @@
|
||||
** Most of the code in this file may be omitted by defining the
|
||||
** SQLITE_OMIT_VACUUM macro.
|
||||
**
|
||||
** $Id: vacuum.c,v 1.20 2004/05/31 10:01:35 danielk1977 Exp $
|
||||
** $Id: vacuum.c,v 1.21 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -196,7 +196,7 @@ int sqlite3RunVacuum(char **pzErrMsg, sqlite *db){
|
||||
u32 meta;
|
||||
|
||||
assert( 0==sqlite3BtreeIsInTrans(pMain) );
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1);
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[0].pBt, 1, 0);
|
||||
if( rc!=SQLITE_OK ) goto end_of_vacuum;
|
||||
|
||||
/* Copy Btree meta values 3 and 4. These correspond to SQL layer meta
|
||||
|
@@ -43,7 +43,7 @@
|
||||
** in this file for details. If in doubt, do not deviate from existing
|
||||
** commenting and indentation practices when changing or adding code.
|
||||
**
|
||||
** $Id: vdbe.c,v 1.355 2004/06/02 01:22:02 drh Exp $
|
||||
** $Id: vdbe.c,v 1.356 2004/06/03 16:08:42 danielk1977 Exp $
|
||||
*/
|
||||
#include "sqliteInt.h"
|
||||
#include "os.h"
|
||||
@@ -2289,7 +2289,8 @@ case OP_Transaction: {
|
||||
pBt = db->aDb[i].pBt;
|
||||
|
||||
while( pBt && busy ){
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[i].pBt, pOp->p2);
|
||||
int nMaster = strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt))+11;
|
||||
rc = sqlite3BtreeBeginTrans(db->aDb[i].pBt, pOp->p2, nMaster);
|
||||
switch( rc ){
|
||||
case SQLITE_BUSY: {
|
||||
if( db->xBusyCallback==0 ){
|
||||
@@ -2553,7 +2554,7 @@ case OP_OpenTemp: {
|
||||
rc = sqlite3BtreeFactory(db, 0, 1, TEMP_PAGES, &pCx->pBt);
|
||||
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1);
|
||||
rc = sqlite3BtreeBeginTrans(pCx->pBt, 1, 0);
|
||||
}
|
||||
if( rc==SQLITE_OK ){
|
||||
/* If a transient index is required, create it by calling
|
||||
|
@@ -24,10 +24,10 @@
|
||||
#define intToKey(X) (X)
|
||||
|
||||
/*
|
||||
** The makefile scans this source file and creates the following
|
||||
** array of string constants which are the names of all VDBE opcodes.
|
||||
** This array is defined in a separate source code file named opcode.c
|
||||
** which is automatically generated by the makefile.
|
||||
** The makefile scans the vdbe.c source file and creates the following
|
||||
** array of string constants which are the names of all VDBE opcodes. This
|
||||
** array is defined in a separate source code file named opcode.c which is
|
||||
** automatically generated by the makefile.
|
||||
*/
|
||||
extern char *sqlite3OpcodeNames[];
|
||||
|
||||
|
180
src/vdbeaux.c
180
src/vdbeaux.c
@@ -895,6 +895,150 @@ int sqlite3VdbeSetColName(Vdbe *p, int idx, const char *zName, int N){
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** A read or write transaction may or may not be active on database handle
|
||||
** db. If a transaction is active, commit it. If there is a
|
||||
** write-transaction spanning more than one database file, this routine
|
||||
** takes care of the master journal trickery.
|
||||
*/
|
||||
static int vdbeCommit(sqlite *db){
|
||||
int i;
|
||||
int nTrans = 0; /* Number of databases with an active write-transaction */
|
||||
int rc = SQLITE_OK;
|
||||
int needXcommit = 0;
|
||||
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeIsInTrans(pBt) ){
|
||||
needXcommit = 1;
|
||||
if( i!=1 ) nTrans++;
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are any write-transactions at all, invoke the commit hook */
|
||||
if( needXcommit && db->xCommitCallback ){
|
||||
if( db->xCommitCallback(db->pCommitArg) ){
|
||||
return SQLITE_CONSTRAINT;
|
||||
}
|
||||
}
|
||||
|
||||
/* The simple case - if less than two databases have write-transactions
|
||||
** active, there is no need for the master-journal.
|
||||
*/
|
||||
if( nTrans<2 ){
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
int rc2 = sqlite3BtreeCommit(db->aDb[i].pBt);
|
||||
if( rc==SQLITE_OK ) rc = rc2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The complex case - There is a multi-file write-transaction active.
|
||||
** This requires a master journal file to ensure the transaction is
|
||||
** committed atomicly.
|
||||
*/
|
||||
else{
|
||||
char *zMaster = 0; /* File-name for the master journal */
|
||||
char const *zMainFile = sqlite3BtreeGetFilename(db->aDb[0].pBt);
|
||||
OsFile master;
|
||||
|
||||
/* Select a master journal file name */
|
||||
do {
|
||||
int random;
|
||||
if( zMaster ){
|
||||
sqliteFree(zMaster);
|
||||
}
|
||||
sqlite3Randomness(sizeof(random), &random);
|
||||
zMaster = sqlite3_mprintf("%s%d", zMainFile, random);
|
||||
if( !zMaster ){
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
}while( sqlite3OsFileExists(zMaster) );
|
||||
|
||||
/* Open the master journal. */
|
||||
rc = sqlite3OsOpenExclusive(zMaster, &master, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(zMaster);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Write the name of each database file in the transaction into the new
|
||||
** master journal file. If an error occurs at this point close
|
||||
** and delete the master journal file. All the individual journal files
|
||||
** still have 'null' as the master journal pointer, so they will roll
|
||||
** back independantly if a failure occurs.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeIsInTrans(pBt) ){
|
||||
char const *zFile = sqlite3BtreeGetFilename(pBt);
|
||||
rc = sqlite3OsWrite(&master, zFile, strlen(zFile));
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsClose(&master);
|
||||
sqlite3OsDelete(zMaster);
|
||||
sqliteFree(zMaster);
|
||||
return rc;
|
||||
}
|
||||
rc = sqlite3OsWrite(&master, "\0", 1);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqlite3OsClose(&master);
|
||||
sqlite3OsDelete(zMaster);
|
||||
sqliteFree(zMaster);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Sync the master journal file */
|
||||
rc = sqlite3OsSync(&master);
|
||||
sqlite3OsClose(&master);
|
||||
|
||||
/* Sync all the db files involved in the transaction. The same call
|
||||
** sets the master journal pointer in each individual journal. If
|
||||
** an error occurs here, do not delete the master journal file.
|
||||
**
|
||||
** If the error occurs during the first call to sqlite3BtreeSync(),
|
||||
** then there is a chance that the master journal file will be
|
||||
** orphaned. But we cannot delete it, in case the master journal
|
||||
** file name was written into the journal file before the failure
|
||||
** occured.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt && sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeSync(pBt, zMaster);
|
||||
if( rc!=SQLITE_OK ){
|
||||
sqliteFree(zMaster);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqliteFree(zMaster);
|
||||
zMaster = 0;
|
||||
|
||||
/* Delete the master journal file. This commits the transaction. */
|
||||
rc = sqlite3OsDelete(zMaster);
|
||||
assert( rc==SQLITE_OK );
|
||||
|
||||
/* All files and directories have already been synced, so the following
|
||||
** calls to sqlite3BtreeCommit() are only closing files and deleting
|
||||
** journals. If something goes wrong while this is happening we don't
|
||||
** really care. The integrity of the transaction is already guarenteed,
|
||||
** but some stray 'cold' journals may be lying around. Returning an
|
||||
** error code won't help matters.
|
||||
*/
|
||||
for(i=0; i<db->nDb; i++){
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( pBt ){
|
||||
sqlite3BtreeCommit(pBt);
|
||||
}
|
||||
}
|
||||
}
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
** This routine checks that the sqlite3.activeVdbeCnt count variable
|
||||
** matches the number of vdbe's in the list sqlite3.pVdbe that are
|
||||
@@ -932,7 +1076,6 @@ int sqlite3VdbeReset(Vdbe *p, char **pzErrMsg){
|
||||
sqlite *db = p->db;
|
||||
int i;
|
||||
int (*xFunc)(Btree *pBt) = 0; /* Function to call on each btree backend */
|
||||
int needXcommit = 0;
|
||||
|
||||
if( p->magic!=VDBE_MAGIC_RUN && p->magic!=VDBE_MAGIC_HALT ){
|
||||
sqlite3SetString(pzErrMsg, sqlite3ErrStr(SQLITE_MISUSE), (char*)0);
|
||||
@@ -956,14 +1099,24 @@ int sqlite3VdbeReset(Vdbe *p, char **pzErrMsg){
|
||||
}
|
||||
Cleanup(p);
|
||||
|
||||
/* Figure out which function to call on the btree backends that
|
||||
** have active transactions.
|
||||
/* What is done now depends on the exit status of the vdbe, the value of
|
||||
** the sqlite.autoCommit flag and whether or not there are any other
|
||||
** queries in progress. A transaction or statement transaction may need
|
||||
** to be committed or rolled back on each open database file.
|
||||
*/
|
||||
checkActiveVdbeCnt(db);
|
||||
if( db->autoCommit && db->activeVdbeCnt==1 ){
|
||||
if( p->rc==SQLITE_OK || p->errorAction==OE_Fail ){
|
||||
xFunc = sqlite3BtreeCommit;
|
||||
needXcommit = 1;
|
||||
/* The auto-commit flag is true, there are no other active queries
|
||||
** using this handle and the vdbe program was successful or hit an
|
||||
** 'OR FAIL' constraint. This means a commit is required, which is
|
||||
** handled a little differently from the other options.
|
||||
*/
|
||||
p->rc = vdbeCommit(db);
|
||||
if( p->rc!=SQLITE_OK ){
|
||||
sqlite3Error(p->db, p->rc, 0);
|
||||
xFunc = sqlite3BtreeRollback;
|
||||
}
|
||||
}else{
|
||||
xFunc = sqlite3BtreeRollback;
|
||||
}
|
||||
@@ -978,19 +1131,14 @@ int sqlite3VdbeReset(Vdbe *p, char **pzErrMsg){
|
||||
}
|
||||
}
|
||||
|
||||
for(i=0; xFunc && i<db->nDb; i++){
|
||||
/* If xFunc is not NULL, then it is one of sqlite3BtreeRollback,
|
||||
** sqlite3BtreeRollbackStmt or sqlite3BtreeCommitStmt. Call it once on
|
||||
** each backend. If an error occurs and the return code is still
|
||||
** SQLITE_OK, set the return code to the new error value.
|
||||
*/
|
||||
for(i=0; xFunc && i<db->nDb; i++){
|
||||
int rc;
|
||||
Btree *pBt = db->aDb[i].pBt;
|
||||
if( sqlite3BtreeIsInTrans(pBt) ){
|
||||
if( db->xCommitCallback && needXcommit ){
|
||||
if( db->xCommitCallback(db->pCommitArg)!=0 ){
|
||||
p->rc = SQLITE_CONSTRAINT;
|
||||
sqlite3Error(db, SQLITE_CONSTRAINT, 0);
|
||||
xFunc = sqlite3BtreeRollback;
|
||||
}
|
||||
needXcommit = 0;
|
||||
}
|
||||
}
|
||||
if( pBt ){
|
||||
rc = xFunc(pBt);
|
||||
if( p->rc==SQLITE_OK ) p->rc = rc;
|
||||
|
Reference in New Issue
Block a user