]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Refactoring the btree and pager routines into distinct two-phase commit
authordrh <drh@noemail.net>
Fri, 30 Mar 2007 14:06:34 +0000 (14:06 +0000)
committerdrh <drh@noemail.net>
Fri, 30 Mar 2007 14:06:34 +0000 (14:06 +0000)
routines.  We've always done a two-phase commit - this change is just
making that more apparent in the code. (CVS 3762)

FossilOrigin-Name: 66b3ad09ea657d25d48cb75ec2671ea2dc1b6005

manifest
manifest.uuid
src/btree.c
src/btree.h
src/pager.c
src/pager.h
src/test2.c
src/vdbeaux.c

index 685da9f8442c3038d881b2ce38695d0f78dca969..33db0ae37c691a80339240b0e6f5977d0ccbe41c 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Make\syypMinor\savailable\sto\sthe\sstack\soverflow\scallbacks\sin\slemon\ngenerated\sparsers.\s\sThis\sdoes\snot\seffect\sSQLite.\s(CVS\s3761)
-D 2007-03-30T13:35:06
+C Refactoring\sthe\sbtree\sand\spager\sroutines\sinto\sdistinct\stwo-phase\scommit\nroutines.\s\sWe've\salways\sdone\sa\stwo-phase\scommit\s-\sthis\schange\sis\sjust\nmaking\sthat\smore\sapparent\sin\sthe\scode.\s(CVS\s3762)
+D 2007-03-30T14:06:34
 F Makefile.in 2f2c3bf69faf0ae7b8e8af4f94f1986849034530
 F Makefile.linux-gcc 2d8574d1ba75f129aba2019f0b959db380a90935
 F README 9c4e2d6706bdcc3efdd773ce752a8cdab4f90028
@@ -58,8 +58,8 @@ F src/alter.c 2c79ec40f65e33deaf90ca493422c74586e481a3
 F src/analyze.c 4bbf5ddf9680587c6d4917e02e378b6037be3651
 F src/attach.c a16ada4a4654a0d126b8223ec9494ebb81bc5c3c
 F src/auth.c 902f4722661c796b97f007d9606bd7529c02597f
-F src/btree.c 9b41229e7ff4b0fe8556763ce2b466a90aaaa6a4
-F src/btree.h 540dcbbf83435b77d4b6ef87f909c6cecad4dac9
+F src/btree.c 931d8d6eb3e669d6e42305dce3f196bf071a53b6
+F src/btree.h 9b2cc0d113c0bc2d37d244b9a394d56948c9acbf
 F src/build.c ad3374b5409554e504300f77e1fbc6b4c106a57f
 F src/callback.c 31d22b4919c7645cbcbb1591ce2453e8c677c558
 F src/complete.c 7d1a44be8f37de125fcafd3d3a018690b3799675
@@ -86,8 +86,8 @@ F src/os_unix.c 4291be23eec73d1ec04010ae702364b781b5f773
 F src/os_unix.h 5768d56d28240d3fe4537fac08cc85e4fb52279e
 F src/os_win.c c3a8403ea28bbb89d6507fa984c5919bd3fe7539
 F src/os_win.h 41a946bea10f61c158ce8645e7646b29d44f122b
-F src/pager.c b89ea0f592e499ee6d6cda10b84688f8e47a05ba
-F src/pager.h f1b17bf848b3dce5d9afb2701186d3c9a8826f8c
+F src/pager.c f9131543cc602202de8a436ca9207e4c28b3f41e
+F src/pager.h e79a24cf200b8771366217f5bca414f5b7823f42
 F src/parse.y 207ab04273ae13aa4a729b96008d294d5f334ab3
 F src/pragma.c 8fd4f98822007a8d2c34e235ad3c35f1d77b3e51
 F src/prepare.c 37207b2b2ccb41d379b01dd62231686bcc48ef1f
@@ -102,7 +102,7 @@ F src/sqliteInt.h c8d0e5ce27a862836de70fc3eadc1e65cea7e3d8
 F src/table.c 6d0da66dde26ee75614ed8f584a1996467088d06
 F src/tclsqlite.c a8d1166319db5d505b25ac6a9820162afe63fc8a
 F src/test1.c 439bba8da10fbc61c731019cf2894e6057578878
-F src/test2.c dc48c84ce68b3bc2f2d01871d709f20dc77003b0
+F src/test2.c 710a7252e22a8c690136a2b609e90fdad2697f35
 F src/test3.c 65f92247cf8592854e9bf5115b3fb711f8b33280
 F src/test4.c 8b784cd82de158a2317cb4ac4bc86f91ad315e25
 F src/test5.c 7162f8526affb771c4ed256826eee7bb9eca265f
@@ -127,7 +127,7 @@ F src/vdbe.c d5a4fc1e18af2a2873d022450c4a776f6c17bac3
 F src/vdbe.h 0025259af1939fb264a545816c69e4b5b8d52691
 F src/vdbeInt.h 4b19fd8febad3fd14c4c97adaefc06754d323132
 F src/vdbeapi.c 7ac14f2e3b2283dcd35f5edefd31a8342cff783c
-F src/vdbeaux.c d768c22c8aa0fbf9bf1408b4fc9ed0b38b1146ca
+F src/vdbeaux.c 5efa6d0f6096f213fbdffa0b1bf0a4c8ae40d145
 F src/vdbefifo.c 3ca8049c561d5d67cbcb94dc909ae9bb68c0bf8f
 F src/vdbemem.c d3696b4b0e5f32272659816cdfa2348c650b1ba0
 F src/vtab.c 7fbda947e28cbe7adb3ba752a76ca9ef29936750
@@ -447,7 +447,7 @@ F www/tclsqlite.tcl bb0d1357328a42b1993d78573e587c6dcbc964b9
 F www/vdbe.tcl 87a31ace769f20d3627a64fa1fade7fed47b90d0
 F www/version3.tcl 890248cf7b70e60c383b0e84d77d5132b3ead42b
 F www/whentouse.tcl 97e2b5cd296f7d8057e11f44427dea8a4c2db513
-P 339941d83ae397d69084f41483afb1ea44d44967
-R b47b2f2fe5cfddcd0105ea435c37f3d5
+P 70c8c7e2ce5213778e63c200a6637849920deea6
+R a5a112b3409ee86a088223323643c5e6
 U drh
-Z 511499bbee13f7559f6a7e8fb29eeb3d
+Z 49017f06f92594291d8addf7b78eb7c9
index c096c2b752f3db1d1d5ad9c1bc9fa11241e69ad9..66ce391f31fbed05aa4102af88dc46fe331a1443 100644 (file)
@@ -1 +1 @@
-70c8c7e2ce5213778e63c200a6637849920deea6
\ No newline at end of file
+66b3ad09ea657d25d48cb75ec2671ea2dc1b6005
\ No newline at end of file
index fe103ddec9b4efed62eee876be9d1f68d356e313..5f4af95113cf457115d0fc106475d74d62b0e11a 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.346 2007/03/30 11:12:08 drh Exp $
+** $Id: btree.c,v 1.347 2007/03/30 14:06:34 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -2407,6 +2407,50 @@ autovacuum_out:
 }
 #endif
 
+/*
+** This routine does the first phase of a two-phase commit.  This routine
+** causes a rollback journal to be created (if it does not already exist)
+** and populated with enough information so that if a power loss occurs
+** the database can be restored to its original state by playing back
+** the journal.  Then the contents of the journal are flushed out to
+** the disk.  After the journal is safely on oxide, the changes to the
+** database are written into the database file and flushed to oxide.
+** At the end of this call, the rollback journal still exists on the
+** disk and we are still holding all locks, so the transaction has not
+** committed.  See sqlite3BtreeCommit() for the second phase of the
+** commit process.
+**
+** 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 sqlite3BtreeCommitPhaseOne(Btree *p, const char *zMaster){
+  int rc = SQLITE_OK;
+  if( p->inTrans==TRANS_WRITE ){
+    BtShared *pBt = p->pBt;
+    Pgno nTrunc = 0;
+#ifndef SQLITE_OMIT_AUTOVACUUM
+    if( pBt->autoVacuum ){
+      rc = autoVacuumCommit(pBt, &nTrunc); 
+      if( rc!=SQLITE_OK ){
+        return rc;
+      }
+    }
+#endif
+    rc = sqlite3PagerCommitPhaseOne(pBt->pPager, zMaster, nTrunc);
+  }
+  return rc;
+}
+
 /*
 ** Commit the transaction currently in progress.
 **
@@ -2421,7 +2465,7 @@ autovacuum_out:
 ** This will release the write lock on the database file.  If there
 ** are no active cursors, it also releases the read lock.
 */
-int sqlite3BtreeCommit(Btree *p){
+int sqlite3BtreeCommitPhaseTwo(Btree *p){
   BtShared *pBt = p->pBt;
 
   btreeIntegrity(p);
@@ -2433,7 +2477,7 @@ int sqlite3BtreeCommit(Btree *p){
     int rc;
     assert( pBt->inTransaction==TRANS_WRITE );
     assert( pBt->nTransaction>0 );
-    rc = sqlite3PagerCommit(pBt->pPager);
+    rc = sqlite3PagerCommitPhaseTwo(pBt->pPager);
     if( rc!=SQLITE_OK ){
       return rc;
     }
@@ -2464,6 +2508,18 @@ int sqlite3BtreeCommit(Btree *p){
   return SQLITE_OK;
 }
 
+/*
+** Do both phases of a commit.
+*/
+int sqlite3BtreeCommit(Btree *p){
+  int rc;
+  rc = sqlite3BtreeCommitPhaseOne(p, 0);
+  if( rc==SQLITE_OK ){
+    rc = sqlite3BtreeCommitPhaseTwo(p);
+  }
+  return rc;
+}
+
 #ifndef NDEBUG
 /*
 ** Return the number of write-cursors open on this handle. This is for use
@@ -6522,50 +6578,6 @@ int sqlite3BtreeIsInReadTrans(Btree *p){
   return (p && (p->inTrans!=TRANS_NONE));
 }
 
-/*
-** This routine does the first phase of a 2-phase commit.  This routine
-** causes a rollback journal to be created (if it does not already exist)
-** and populated with enough information so that if a power loss occurs
-** the database can be restored to its original state by playing back
-** the journal.  Then the contents of the journal are flushed out to
-** the disk.  After the journal is safely on oxide, the changes to the
-** database are written into the database file and flushed to oxide.
-** At the end of this call, the rollback journal still exists on the
-** disk and we are still holding all locks, so the transaction has not
-** committed.  See sqlite3BtreeCommit() for the second phase of the
-** commit process.
-**
-** 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 *p, const char *zMaster){
-  int rc = SQLITE_OK;
-  if( p->inTrans==TRANS_WRITE ){
-    BtShared *pBt = p->pBt;
-    Pgno nTrunc = 0;
-#ifndef SQLITE_OMIT_AUTOVACUUM
-    if( pBt->autoVacuum ){
-      rc = autoVacuumCommit(pBt, &nTrunc); 
-      if( rc!=SQLITE_OK ){
-        return rc;
-      }
-    }
-#endif
-    rc = sqlite3PagerSync(pBt->pPager, zMaster, nTrunc);
-  }
-  return rc;
-}
-
 /*
 ** This function returns a pointer to a blob of memory associated with
 ** a single shared-btree. The memory is used by client code for it's own
index 513e9a082681cc3f21eb8a488ef237d844bffe47..50b5317b62edd823c1e9df7160df5a9d692a3cd6 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.73 2007/03/29 05:51:49 drh Exp $
+** @(#) $Id: btree.h,v 1.74 2007/03/30 14:06:34 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -67,6 +67,8 @@ int sqlite3BtreeGetReserve(Btree*);
 int sqlite3BtreeSetAutoVacuum(Btree *, int);
 int sqlite3BtreeGetAutoVacuum(Btree *);
 int sqlite3BtreeBeginTrans(Btree*,int);
+int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
+int sqlite3BtreeCommitPhaseTwo(Btree*);
 int sqlite3BtreeCommit(Btree*);
 int sqlite3BtreeRollback(Btree*);
 int sqlite3BtreeBeginStmt(Btree*);
@@ -76,7 +78,6 @@ int sqlite3BtreeCreateTable(Btree*, int*, int flags);
 int sqlite3BtreeIsInTrans(Btree*);
 int sqlite3BtreeIsInStmt(Btree*);
 int sqlite3BtreeIsInReadTrans(Btree*);
-int sqlite3BtreeSync(Btree*, const char *zMaster);
 void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
 int sqlite3BtreeSchemaLocked(Btree *);
 int sqlite3BtreeLockTable(Btree *, int, u8);
index 82d7f4ecd2dd60cad7e91609f61be19006a09ab0..fdf0c9379b65971ea712c20307e561424d9ae56e 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.306 2007/03/29 18:19:52 drh Exp $
+** @(#) $Id: pager.c,v 1.307 2007/03/30 14:06:34 drh Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -160,7 +160,7 @@ struct PgHdr {
   u8 inStmt;                     /* TRUE if in the statement subjournal */
   u8 dirty;                      /* TRUE if we need to write back changes */
   u8 needSync;                   /* Sync journal before writing this page */
-  u8 alwaysRollback;             /* Disable dont_rollback() for this page */
+  u8 alwaysRollback;             /* Disable DontRollback() for this page */
   short int nRef;                /* Number of users of this page */
   PgHdr *pDirty, *pPrevDirty;    /* Dirty pages */
   u32 notUsed;                   /* Buffer space */
@@ -236,9 +236,12 @@ struct Pager {
   u8 readOnly;                /* True for a read-only database */
   u8 needSync;                /* True if an fsync() is needed on the journal */
   u8 dirtyCache;              /* True if cached pages have changed */
-  u8 alwaysRollback;          /* Disable dont_rollback() for all pages */
+  u8 alwaysRollback;          /* Disable DontRollback() for all pages */
   u8 memDb;                   /* True to inhibit all file I/O */
   u8 setMaster;               /* True if a m-j name has been written to jrnl */
+  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
+  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
+  u8 changeCountDone;         /* Set after incrementing the change-counter */
   int errCode;                /* One of several kinds of errors */
   int dbSize;                 /* Number of pages in the file */
   int origDbSize;             /* dbSize before the current change */
@@ -286,9 +289,6 @@ struct Pager {
 #endif
   char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
   u32 iChangeCount;           /* Db change-counter for which cache is valid */
-  u8 doNotSync;               /* Boolean. While true, do not spill the cache */
-  u8 exclusiveMode;           /* Boolean. True if locking_mode==EXCLUSIVE */
-  u8 changeCountDone;         /* Set after incrementing the change-counter */
 };
 
 /*
@@ -910,16 +910,23 @@ static void pager_reset(Pager *pPager){
 }
 
 /*
+** This routine ends a transaction.  A transaction is ended by either
+** a COMMIT or a ROLLBACK.
+**
 ** When this routine is called, the pager has the journal file open and
-** a RESERVED or EXCLUSIVE lock on the database.  This routine releases
-** the database lock and acquires a SHARED lock in its place.  The journal
-** file is deleted and closed.
+** a RESERVED or EXCLUSIVE lock on the database.  This routine will release
+** the database lock and acquires a SHARED lock in its place if that is
+** the appropriate thing to do.  Release locks usually is appropriate,
+** unless we are in exclusive access mode or unless this is a 
+** COMMIT AND BEGIN or ROLLBACK AND BEGIN operation.
+**
+** The journal file is either deleted or truncated.
 **
 ** TODO: Consider keeping the journal file open for temporary databases.
 ** This might give a performance improvement on windows where opening
 ** a file is an expensive operation.
 */
-static int pager_unwritelock(Pager *pPager){
+static int pager_end_transaction(Pager *pPager){
   PgHdr *pPg;
   int rc = SQLITE_OK;
   int rc2 = SQLITE_OK;
@@ -1423,7 +1430,7 @@ static int pager_playback(Pager *pPager, int isHot){
 
 end_playback:
   if( rc==SQLITE_OK ){
-    rc = pager_unwritelock(pPager);
+    rc = pager_end_transaction(pPager);
   }
   if( zMaster ){
     /* If there was a master journal and this routine will return success,
@@ -2563,7 +2570,7 @@ static int pager_recycle(Pager *pPager, int syncOk, PgHdr **ppPg){
 
   /* If the page we are recycling is marked as alwaysRollback, then
   ** set the global alwaysRollback flag, thus disabling the
-  ** sqlite_dont_rollback() optimization for the rest of this transaction.
+  ** sqlite3PagerDontRollback() optimization for the rest of this transaction.
   ** It is necessary to do this because the page marked alwaysRollback
   ** might be reloaded at a later time but at that point we won't remember
   ** that is was marked alwaysRollback.  This means that all pages must
@@ -3096,7 +3103,7 @@ static int pager_open_journal(Pager *pPager){
     rc = sqlite3PagerStmtBegin(pPager);
   }
   if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
-    rc = pager_unwritelock(pPager);
+    rc = pager_end_transaction(pPager);
     if( rc==SQLITE_OK ){
       rc = SQLITE_FULL;
     }
@@ -3127,7 +3134,7 @@ failed_to_open_journal:
 ** Acquire a write-lock on the database.  The lock is removed when
 ** the any of the following happen:
 **
-**   *  sqlite3PagerCommit() is called.
+**   *  sqlite3PagerCommitPhaseTwo() is called.
 **   *  sqlite3PagerRollback() is called.
 **   *  sqlite3PagerClose() is called.
 **   *  sqlite3PagerUnref() is called to on every outstanding page.
@@ -3528,9 +3535,9 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void *pData){
 ** a transaction then removed from the freelist during a later part
 ** of the same transaction and reused for some other purpose.  When it
 ** is first added to the freelist, this routine is called.  When reused,
-** the dont_rollback() routine is called.  But because the page contains
-** critical data, we still need to be sure it gets rolled back in spite
-** of the dont_rollback() call.
+** the sqlite3PagerDontRollback() routine is called.  But because the
+** page contains critical data, we still need to be sure it gets
+** rolled back in spite of the sqlite3PagerDontRollback() call.
 */
 void sqlite3PagerDontWrite(Pager *pPager, Pgno pgno){
   PgHdr *pPg;
@@ -3594,6 +3601,129 @@ void sqlite3PagerDontRollback(DbPage *pPg){
 }
 
 
+/*
+** This routine is called to increment the database file change-counter,
+** stored at byte 24 of the pager file.
+*/
+static int pager_incr_changecounter(Pager *pPager){
+  PgHdr *pPgHdr;
+  u32 change_counter;
+  int rc;
+
+  if( !pPager->changeCountDone ){
+    /* Open page 1 of the file for writing. */
+    rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
+    if( rc!=SQLITE_OK ) return rc;
+    rc = sqlite3PagerWrite(pPgHdr);
+    if( rc!=SQLITE_OK ) return rc;
+  
+    /* Read the current value at byte 24. */
+    change_counter = retrieve32bits(pPgHdr, 24);
+  
+    /* Increment the value just read and write it back to byte 24. */
+    change_counter++;
+    put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
+    pPager->iChangeCount = change_counter;
+  
+    /* Release the page reference. */
+    sqlite3PagerUnref(pPgHdr);
+    pPager->changeCountDone = 1;
+  }
+  return SQLITE_OK;
+}
+
+/*
+** 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 sqlite3PagerCommitPhaseOne() call.
+**
+** If parameter nTrunc is non-zero, then the pager file is truncated to
+** nTrunc pages (this is used by auto-vacuum databases).
+*/
+int sqlite3PagerCommitPhaseOne(Pager *pPager, const char *zMaster, Pgno nTrunc){
+  int rc = SQLITE_OK;
+
+  PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", 
+      pPager->zFilename, zMaster, nTrunc);
+
+  /* If this is an in-memory db, or no pages have been written to, or this
+  ** function has already been called, it is a no-op.
+  */
+  if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
+    PgHdr *pPg;
+    assert( pPager->journalOpen );
+
+    /* If a master journal file name has already been written to the
+    ** journal file, then no sync is required. This happens when it is
+    ** written, then the process fails to upgrade from a RESERVED to an
+    ** EXCLUSIVE lock. The next time the process tries to commit the
+    ** transaction the m-j name will have already been written.
+    */
+    if( !pPager->setMaster ){
+      rc = pager_incr_changecounter(pPager);
+      if( rc!=SQLITE_OK ) goto sync_exit;
+#ifndef SQLITE_OMIT_AUTOVACUUM
+      if( nTrunc!=0 ){
+        /* If this transaction has made the database smaller, then all pages
+        ** being discarded by the truncation must be written to the journal
+        ** file.
+        */
+        Pgno i;
+        int iSkip = PAGER_MJ_PGNO(pPager);
+        for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){
+          if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){
+            rc = sqlite3PagerGet(pPager, i, &pPg);
+            if( rc!=SQLITE_OK ) goto sync_exit;
+            rc = sqlite3PagerWrite(pPg);
+            sqlite3PagerUnref(pPg);
+            if( rc!=SQLITE_OK ) goto sync_exit;
+          }
+        } 
+      }
+#endif
+      rc = writeMasterJournal(pPager, zMaster);
+      if( rc!=SQLITE_OK ) goto sync_exit;
+      rc = syncJournal(pPager);
+      if( rc!=SQLITE_OK ) goto sync_exit;
+    }
+
+#ifndef SQLITE_OMIT_AUTOVACUUM
+    if( nTrunc!=0 ){
+      rc = sqlite3PagerTruncate(pPager, nTrunc);
+      if( rc!=SQLITE_OK ) goto sync_exit;
+    }
+#endif
+
+    /* 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;
+
+    /* Sync the database file. */
+    if( !pPager->noSync ){
+      rc = sqlite3OsSync(pPager->fd, 0);
+    }
+    IOTRACE(("DBSYNC %p\n", pPager))
+
+    pPager->state = PAGER_SYNCED;
+  }else if( MEMDB && nTrunc!=0 ){
+    rc = sqlite3PagerTruncate(pPager, nTrunc);
+  }
+
+sync_exit:
+  return rc;
+}
+
+
 /*
 ** Commit all changes to the database and release the write lock.
 **
@@ -3601,7 +3731,7 @@ void sqlite3PagerDontRollback(DbPage *pPg){
 ** and an error code is returned.  If the commit worked, SQLITE_OK
 ** is returned.
 */
-int sqlite3PagerCommit(Pager *pPager){
+int sqlite3PagerCommitPhaseTwo(Pager *pPager){
   int rc;
   PgHdr *pPg;
 
@@ -3640,12 +3770,12 @@ int sqlite3PagerCommit(Pager *pPager){
     /* Exit early (without doing the time-consuming sqlite3OsSync() calls)
     ** if there have been no changes to the database file. */
     assert( pPager->needSync==0 );
-    rc = pager_unwritelock(pPager);
+    rc = pager_end_transaction(pPager);
   }else{
     assert( pPager->journalOpen );
-    rc = sqlite3PagerSync(pPager, 0, 0);
+    rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);
     if( rc==SQLITE_OK ){
-      rc = pager_unwritelock(pPager);
+      rc = pager_end_transaction(pPager);
     }
   }
   return pager_error(pPager, rc);
@@ -3703,7 +3833,7 @@ int sqlite3PagerRollback(Pager *pPager){
   }
 
   if( !pPager->dirtyCache || !pPager->journalOpen ){
-    rc = pager_unwritelock(pPager);
+    rc = pager_end_transaction(pPager);
     return rc;
   }
 
@@ -3716,7 +3846,7 @@ int sqlite3PagerRollback(Pager *pPager){
   if( pPager->state==PAGER_RESERVED ){
     int rc2;
     rc = pager_playback(pPager, 0);
-    rc2 = pager_unwritelock(pPager);
+    rc2 = pager_end_transaction(pPager);
     if( rc==SQLITE_OK ){
       rc = rc2;
     }
@@ -3926,128 +4056,6 @@ void sqlite3PagerSetCodec(
   pPager->pCodecArg = pCodecArg;
 }
 
-/*
-** This routine is called to increment the database file change-counter,
-** stored at byte 24 of the pager file.
-*/
-static int pager_incr_changecounter(Pager *pPager){
-  PgHdr *pPgHdr;
-  u32 change_counter;
-  int rc;
-
-  if( !pPager->changeCountDone ){
-    /* Open page 1 of the file for writing. */
-    rc = sqlite3PagerGet(pPager, 1, &pPgHdr);
-    if( rc!=SQLITE_OK ) return rc;
-    rc = sqlite3PagerWrite(pPgHdr);
-    if( rc!=SQLITE_OK ) return rc;
-  
-    /* Read the current value at byte 24. */
-    change_counter = retrieve32bits(pPgHdr, 24);
-  
-    /* Increment the value just read and write it back to byte 24. */
-    change_counter++;
-    put32bits(((char*)PGHDR_TO_DATA(pPgHdr))+24, change_counter);
-    pPager->iChangeCount = change_counter;
-  
-    /* Release the page reference. */
-    sqlite3PagerUnref(pPgHdr);
-    pPager->changeCountDone = 1;
-  }
-  return SQLITE_OK;
-}
-
-/*
-** 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 sqlite3PagerSync() call.
-**
-** If parameter nTrunc is non-zero, then the pager file is truncated to
-** nTrunc pages (this is used by auto-vacuum databases).
-*/
-int sqlite3PagerSync(Pager *pPager, const char *zMaster, Pgno nTrunc){
-  int rc = SQLITE_OK;
-
-  PAGERTRACE4("DATABASE SYNC: File=%s zMaster=%s nTrunc=%d\n", 
-      pPager->zFilename, zMaster, nTrunc);
-
-  /* If this is an in-memory db, or no pages have been written to, or this
-  ** function has already been called, it is a no-op.
-  */
-  if( pPager->state!=PAGER_SYNCED && !MEMDB && pPager->dirtyCache ){
-    PgHdr *pPg;
-    assert( pPager->journalOpen );
-
-    /* If a master journal file name has already been written to the
-    ** journal file, then no sync is required. This happens when it is
-    ** written, then the process fails to upgrade from a RESERVED to an
-    ** EXCLUSIVE lock. The next time the process tries to commit the
-    ** transaction the m-j name will have already been written.
-    */
-    if( !pPager->setMaster ){
-      rc = pager_incr_changecounter(pPager);
-      if( rc!=SQLITE_OK ) goto sync_exit;
-#ifndef SQLITE_OMIT_AUTOVACUUM
-      if( nTrunc!=0 ){
-        /* If this transaction has made the database smaller, then all pages
-        ** being discarded by the truncation must be written to the journal
-        ** file.
-        */
-        Pgno i;
-        int iSkip = PAGER_MJ_PGNO(pPager);
-        for( i=nTrunc+1; i<=pPager->origDbSize; i++ ){
-          if( !(pPager->aInJournal[i/8] & (1<<(i&7))) && i!=iSkip ){
-            rc = sqlite3PagerGet(pPager, i, &pPg);
-            if( rc!=SQLITE_OK ) goto sync_exit;
-            rc = sqlite3PagerWrite(pPg);
-            sqlite3PagerUnref(pPg);
-            if( rc!=SQLITE_OK ) goto sync_exit;
-          }
-        } 
-      }
-#endif
-      rc = writeMasterJournal(pPager, zMaster);
-      if( rc!=SQLITE_OK ) goto sync_exit;
-      rc = syncJournal(pPager);
-      if( rc!=SQLITE_OK ) goto sync_exit;
-    }
-
-#ifndef SQLITE_OMIT_AUTOVACUUM
-    if( nTrunc!=0 ){
-      rc = sqlite3PagerTruncate(pPager, nTrunc);
-      if( rc!=SQLITE_OK ) goto sync_exit;
-    }
-#endif
-
-    /* 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;
-
-    /* Sync the database file. */
-    if( !pPager->noSync ){
-      rc = sqlite3OsSync(pPager->fd, 0);
-    }
-    IOTRACE(("DBSYNC %p\n", pPager))
-
-    pPager->state = PAGER_SYNCED;
-  }else if( MEMDB && nTrunc!=0 ){
-    rc = sqlite3PagerTruncate(pPager, nTrunc);
-  }
-
-sync_exit:
-  return rc;
-}
-
 #ifndef SQLITE_OMIT_AUTOVACUUM
 /*
 ** Move the page identified by pData to location pgno in the file. 
index e92bd0693a4113b679cfa7b6e291102ac7147268..a6f639e05d503e978367db801dc9a14369d4a80b 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.56 2007/03/27 16:19:52 danielk1977 Exp $
+** @(#) $Id: pager.h,v 1.57 2007/03/30 14:06:34 drh Exp $
 */
 
 #ifndef _PAGER_H_
@@ -101,8 +101,8 @@ int sqlite3PagerOverwrite(Pager *pPager, Pgno pgno, void*);
 int sqlite3PagerPagecount(Pager*);
 int sqlite3PagerTruncate(Pager*,Pgno);
 int sqlite3PagerBegin(DbPage*, int exFlag);
-int sqlite3PagerCommit(Pager*);
-int sqlite3PagerSync(Pager*,const char *zMaster, Pgno);
+int sqlite3PagerCommitPhaseOne(Pager*,const char *zMaster, Pgno);
+int sqlite3PagerCommitPhaseTwo(Pager*);
 int sqlite3PagerRollback(Pager*);
 int sqlite3PagerIsreadonly(Pager*);
 int sqlite3PagerStmtBegin(Pager*);
index 828423e4e3a56778616597e9f296a7fff61ee8f2..00e05c037920bddda8ed83f15acf3d658db63597 100644 (file)
@@ -13,7 +13,7 @@
 ** is not included in the SQLite library.  It is used for automated
 ** testing of the SQLite library.
 **
-** $Id: test2.c,v 1.41 2007/03/19 17:44:28 danielk1977 Exp $
+** $Id: test2.c,v 1.42 2007/03/30 14:06:34 drh Exp $
 */
 #include "sqliteInt.h"
 #include "os.h"
@@ -163,7 +163,12 @@ static int pager_commit(
     return TCL_ERROR;
   }
   pPager = sqlite3TextToPtr(argv[1]);
-  rc = sqlite3PagerCommit(pPager);
+  rc = sqlite3PagerCommitPhaseOne(pPager, 0, 0);
+  if( rc!=SQLITE_OK ){
+    Tcl_AppendResult(interp, errorName(rc), 0);
+    return TCL_ERROR;
+  }
+  rc = sqlite3PagerCommitPhaseTwo(pPager);
   if( rc!=SQLITE_OK ){
     Tcl_AppendResult(interp, errorName(rc), 0);
     return TCL_ERROR;
index 23bd3b6b035f764b76c20679212f22b3683e7259..9befa36f59f8a7c4fe84c1e8ea3745b3874edf67 100644 (file)
@@ -1090,19 +1090,19 @@ static int vdbeCommit(sqlite3 *db){
     for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
       Btree *pBt = db->aDb[i].pBt;
       if( pBt ){
-        rc = sqlite3BtreeSync(pBt, 0);
+        rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
       }
     }
 
-    /* Do the commit only if all databases successfully synced.
-    ** If one of the BtreeCommit() calls fails, this indicates an IO error
-    ** while deleting or truncating a journal file. It is unlikely, but
-    ** could happen. In this case abandon processing and return the error.
+    /* Do the commit only if all databases successfully complete phase 1. 
+    ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
+    ** IO error while deleting or truncating a journal file. It is unlikely,
+    ** but could happen. In this case abandon processing and return the error.
     */
     for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
       Btree *pBt = db->aDb[i].pBt;
       if( pBt ){
-        rc = sqlite3BtreeCommit(pBt);
+        rc = sqlite3BtreeCommitPhaseTwo(pBt);
       }
     }
     if( rc==SQLITE_OK ){
@@ -1182,16 +1182,16 @@ static int vdbeCommit(sqlite3 *db){
     ** 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.
+    ** If the error occurs during the first call to
+    ** sqlite3BtreeCommitPhaseOne(), 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; rc==SQLITE_OK && i<db->nDb; i++){ 
       Btree *pBt = db->aDb[i].pBt;
       if( pBt && sqlite3BtreeIsInTrans(pBt) ){
-        rc = sqlite3BtreeSync(pBt, zMaster);
+        rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
       }
     }
     sqlite3OsClose(&master);
@@ -1223,17 +1223,17 @@ static int vdbeCommit(sqlite3 *db){
     }
 
     /* 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 guaranteed,
-    ** but some stray 'cold' journals may be lying around. Returning an
-    ** error code won't help matters.
+    ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and
+    ** deleting or truncating journals. If something goes wrong while
+    ** this is happening we don't really care. The integrity of the
+    ** transaction is already guaranteed, but some stray 'cold' journals
+    ** may be lying around. Returning an error code won't help matters.
     */
     disable_simulated_io_errors();
     for(i=0; i<db->nDb; i++){ 
       Btree *pBt = db->aDb[i].pBt;
       if( pBt ){
-        sqlite3BtreeCommit(pBt);
+        sqlite3BtreeCommitPhaseTwo(pBt);
       }
     }
     enable_simulated_io_errors();