aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordanielk1977 <danielk1977@noemail.net>2004-06-03 16:08:41 +0000
committerdanielk1977 <danielk1977@noemail.net>2004-06-03 16:08:41 +0000
commit13adf8a0716f0fa05c42fcedcf9a210aefa22b08 (patch)
tree23b1459dfe656b13611cb31540df0f497c05ea18
parentecb2a9644b4ddcc829fccb8ccda295fd38c2b9fb (diff)
downloadsqlite-13adf8a0716f0fa05c42fcedcf9a210aefa22b08.tar.gz
sqlite-13adf8a0716f0fa05c42fcedcf9a210aefa22b08.zip
Untested updates to support atomic multi-file transactions (CVS 1526)
FossilOrigin-Name: d57e5252c8baaf615c2cd218a33356ea5d95a5e2
-rw-r--r--manifest40
-rw-r--r--manifest.uuid2
-rw-r--r--src/btree.c40
-rw-r--r--src/btree.h5
-rw-r--r--src/build.c4
-rw-r--r--src/delete.c3
-rw-r--r--src/os.h1
-rw-r--r--src/os_unix.c73
-rw-r--r--src/os_unix.h5
-rw-r--r--src/pager.c291
-rw-r--r--src/pager.h5
-rw-r--r--src/pragma.c4
-rw-r--r--src/test3.c4
-rw-r--r--src/vacuum.c4
-rw-r--r--src/vdbe.c7
-rw-r--r--src/vdbeInt.h8
-rw-r--r--src/vdbeaux.c180
17 files changed, 563 insertions, 113 deletions
diff --git a/manifest b/manifest
index 9df241e74..e4b12f275 100644
--- a/manifest
+++ b/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
diff --git a/manifest.uuid b/manifest.uuid
index e388ab026..91ad00296 100644
--- a/manifest.uuid
+++ b/manifest.uuid
@@ -1 +1 @@
-51348b82c4d5801091537b80059d770410774905 \ No newline at end of file
+d57e5252c8baaf615c2cd218a33356ea5d95a5e2 \ No newline at end of file
diff --git a/src/btree.c b/src/btree.c
index 6059899c3..07ebaa9d5 100644
--- a/src/btree.c
+++ b/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;
+}
+
+
diff --git a/src/btree.h b/src/btree.h
index 931cca990..472fd9052 100644
--- a/src/btree.h
+++ b/src/btree.h
@@ -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 *);
diff --git a/src/build.c b/src/build.c
index 48cc1f596..30f8d04b9 100644
--- a/src/build.c
+++ b/src/build.c
@@ -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");
diff --git a/src/delete.c b/src/delete.c
index bd77b59bc..82bb8575b 100644
--- a/src/delete.c
+++ b/src/delete.c
@@ -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);
diff --git a/src/os.h b/src/os.h
index c94a8f6eb..e496564d3 100644
--- a/src/os.h
+++ b/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_ */
diff --git a/src/os_unix.c b/src/os_unix.c
index d8dca0215..9ea456970 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -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;
@@ -662,6 +666,41 @@ int sqlite3OsWriteLock(OsFile *id){
}
/*
+** 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;
}
diff --git a/src/os_unix.h b/src/os_unix.h
index 9dab7375c..e9a016f33 100644
--- a/src/os_unix.h
+++ b/src/os_unix.h
@@ -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 */
};
diff --git a/src/pager.c b/src/pager.c
index d2b84b97b..7068df786 100644
--- a/src/pager.c
+++ b/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:
*/
@@ -602,6 +610,104 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd, int format){
}
/*
+** 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.
diff --git a/src/pager.h b/src/pager.h
index 1aa70c715..132989d76 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -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*);
diff --git a/src/pragma.c b/src/pragma.c
index d80005b41..8df5cd381 100644
--- a/src/pragma.c
+++ b/src/pragma.c
@@ -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 "},
diff --git a/src/test3.c b/src/test3.c
index da90cfaac..a5e5f92a2 100644
--- a/src/test3.c
+++ b/src/test3.c
@@ -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;
diff --git a/src/vacuum.c b/src/vacuum.c
index d0fca6ea4..3ebcd66f0 100644
--- a/src/vacuum.c
+++ b/src/vacuum.c
@@ -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
diff --git a/src/vdbe.c b/src/vdbe.c
index bff8480e8..491f107b7 100644
--- a/src/vdbe.c
+++ b/src/vdbe.c
@@ -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
diff --git a/src/vdbeInt.h b/src/vdbeInt.h
index e52dc51e6..40c8fa8ac 100644
--- a/src/vdbeInt.h
+++ b/src/vdbeInt.h
@@ -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[];
diff --git a/src/vdbeaux.c b/src/vdbeaux.c
index 0920ae28b..4c343946c 100644
--- a/src/vdbeaux.c
+++ b/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;