1
0
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:
danielk1977
2004-06-03 16:08:41 +00:00
parent ecb2a9644b
commit 13adf8a071
17 changed files with 563 additions and 113 deletions

View File

@@ -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

View File

@@ -1 +1 @@
51348b82c4d5801091537b80059d770410774905
d57e5252c8baaf615c2cd218a33356ea5d95a5e2

View File

@@ -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;
}

View File

@@ -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 *);

View File

@@ -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");

View 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);

View File

@@ -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_ */

View File

@@ -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;
}

View File

@@ -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 */
};

View File

@@ -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.

View File

@@ -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*);

View File

@@ -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 "},

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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[];

View File

@@ -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;