]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Untested updates to support atomic multi-file transactions (CVS 1526)
authordanielk1977 <danielk1977@noemail.net>
Thu, 3 Jun 2004 16:08:41 +0000 (16:08 +0000)
committerdanielk1977 <danielk1977@noemail.net>
Thu, 3 Jun 2004 16:08:41 +0000 (16:08 +0000)
FossilOrigin-Name: d57e5252c8baaf615c2cd218a33356ea5d95a5e2

17 files changed:
manifest
manifest.uuid
src/btree.c
src/btree.h
src/build.c
src/delete.c
src/os.h
src/os_unix.c
src/os_unix.h
src/pager.c
src/pager.h
src/pragma.c
src/test3.c
src/vacuum.c
src/vdbe.c
src/vdbeInt.h
src/vdbeaux.c

index 9df241e74ee1c86a94ae456b6c85e50c2ed5ea46..e4b12f275e3342c0ebef239bca79c07ac396ed4a 100644 (file)
--- 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
index e388ab026bd8320ded07839d61ab7f23fa2edcb2..91ad00296f2b50334554ff4a40bc813df6dbd419 100644 (file)
@@ -1 +1 @@
-51348b82c4d5801091537b80059d770410774905
\ No newline at end of file
+d57e5252c8baaf615c2cd218a33356ea5d95a5e2
\ No newline at end of file
index 6059899c376730905f2e7a9b8ba809956e90479e..07ebaa9d57dc96e3723ac71c84fb872a13a740b8 100644 (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;
+}
+
+
index 931cca990ce42109c219fb07c546b3f62cd1c9d5..472fd9052958e5c46e8d3f4c39118ea2b3d430a7 100644 (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 *);
index 48cc1f5968b339758ac2f411ee30435c3e159aee..30f8d04b9e52052ca876c2f4180985f2ebb2ab4e 100644 (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");
index bd77b59bc934a7fa9030c980d378d5199d1644b1..82bb8575b8e4875df50924431b61726bb4d2cab5 100644 (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);
index c94a8f6eb8f3b9242f2e4a00ecf1e171fc2555aa..e496564d3e4f93753d0a5b5b6ffbc87626d3b871 100644 (file)
--- 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_ */
index d8dca021549dcbe0b9a2fa9750f4a38ce7da9a4c..9ea456970762b293eaf1fb3dded3f5e93c027706 100644 (file)
 */
 #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;
 }
 
index 9dab7375c540620cea9762ae339534990e2600d3..e9a016f33607c972b1b3bdcaf83d714050d72dec 100644 (file)
 ** 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 */
 };
 
index d2b84b97b70816ea382745bf085b3b417271bb68..7068df7861e57f8a6f68bbf73aea686214eddd13 100644 (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.
index 1aa70c715da14f08156b98fa7365394fcb3ee27a..132989d76b430d5298e60e352f8b71bb7b3e1e6b 100644 (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*);
index d80005b412d8f9c276f358a529c922bf562170d6..8df5cd381a9913ac9e35624489bbbb604a7717a7 100644 (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 "},
index da90cfaaca3919906626c7ba089bbab85f7150c5..a5e5f92a23055ba81d5b6913134486b5c45a3076 100644 (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;
index d0fca6ea4986c6656c8426969c65d2ad8244c14e..3ebcd66f02d2bda3567eaad52d1871203497b65a 100644 (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 
index bff8480e8316cbdaacbfc08a97be9bcc75613b19..491f107b7bd76499fdbc3ef5f1783bcd90340130 100644 (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
index e52dc51e635a7c7d361ed8d0b3ce547f74b28a2d..40c8fa8acada5a813c30f5b4ac046169cfe088b3 100644 (file)
 #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[];
 
index 0920ae28bed703887e5f857884c232516a2a638b..4c343946c3bffdf3f0b32ee8dc3b9bc8212b7ac3 100644 (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;