Use a bitvec object to store the set of pages with the 'always-rollback' property for a transaction. (CVS 5622)

FossilOrigin-Name: 9e9325997e11a0368721ed7860f4c37de3502a9b
diff --git a/src/btree.c b/src/btree.c
index 45184c4..e4eff21 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.503 2008/08/25 11:57:17 danielk1977 Exp $
+** $Id: btree.c,v 1.504 2008/08/27 15:16:34 danielk1977 Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** See the header comment on "btreeInt.h" for additional information.
@@ -4347,7 +4347,7 @@
         put4byte(&pTrunk->aData[4], k+1);
         put4byte(&pTrunk->aData[8+k*4], pPage->pgno);
 #ifndef SQLITE_SECURE_DELETE
-        sqlite3PagerDontWrite(pPage->pDbPage);
+        rc = sqlite3PagerDontWrite(pPage->pDbPage);
 #endif
       }
       TRACE(("FREE-PAGE: %d leaf on trunk page %d\n",pPage->pgno,pTrunk->pgno));
@@ -7039,7 +7039,7 @@
           ** page is still on the rollback journal, though.  And that is the 
           ** whole point of this block: to put pages on the rollback journal. 
           */
-          sqlite3PagerDontWrite(pDbPage);
+          rc = sqlite3PagerDontWrite(pDbPage);
         }
         sqlite3PagerUnref(pDbPage);
       }
diff --git a/src/pager.c b/src/pager.c
index 7d122ae..196e23e 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.482 2008/08/27 09:44:40 danielk1977 Exp $
+** @(#) $Id: pager.c,v 1.483 2008/08/27 15:16:34 danielk1977 Exp $
 */
 #ifndef SQLITE_OMIT_DISKIO
 #include "sqliteInt.h"
@@ -191,6 +191,7 @@
   Pgno mxPgno;                /* Maximum allowed size of the database */
   Bitvec *pInJournal;         /* One bit for each page in the database file */
   Bitvec *pInStmt;            /* One bit for each page in the database */
+  Bitvec *pAlwaysRollback;    /* One bit for each page marked always-rollback */
   char *zFilename;            /* Name of the database file */
   char *zJournal;             /* Name of the journal file */
   char *zDirectory;           /* Directory hold database and journal files */
@@ -895,6 +896,8 @@
         pPager->journalOpen = 0;
         sqlite3BitvecDestroy(pPager->pInJournal);
         pPager->pInJournal = 0;
+        sqlite3BitvecDestroy(pPager->pAlwaysRollback);
+        pPager->pAlwaysRollback = 0;
       }
 
       /* If Pager.errCode is set, the contents of the pager cache cannot be
@@ -985,12 +988,14 @@
     }
     sqlite3BitvecDestroy(pPager->pInJournal);
     pPager->pInJournal = 0;
+    sqlite3BitvecDestroy(pPager->pAlwaysRollback);
+    pPager->pAlwaysRollback = 0;
     sqlite3PcacheCleanAll(pPager->pPCache);
 #ifdef SQLITE_CHECK_PAGES
     sqlite3PcacheIterate(pPager->pPCache, pager_set_pagehash);
 #endif
-    sqlite3PcacheSetFlags(pPager->pPCache, 
-       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC | PGHDR_ALWAYS_ROLLBACK), 0
+    sqlite3PcacheSetFlags(pPager->pPCache,
+       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
     );
     pPager->dirtyCache = 0;
     pPager->nRec = 0;
@@ -2201,6 +2206,7 @@
     sqlite3OsClose(pPager->jfd);
   }
   sqlite3BitvecDestroy(pPager->pInJournal);
+  sqlite3BitvecDestroy(pPager->pAlwaysRollback);
   if( pPager->stmtOpen ){
     sqlite3OsClose(pPager->stfd);
   }
@@ -2843,13 +2849,13 @@
       return rc;
     }
 
-    if( nMax<(int)pgno || MEMDB || (noContent && !pPager->alwaysRollback) ){
+    if( nMax<(int)pgno || MEMDB || noContent ){
       if( pgno>pPager->mxPgno ){
         sqlite3PagerUnref(pPg);
         return SQLITE_FULL;
       }
       memset(pPg->pData, 0, pPager->pageSize);
-      if( noContent && !pPager->alwaysRollback ){
+      if( noContent ){
         pPg->flags |= PGHDR_NEED_READ;
       }
       IOTRACE(("ZERO %p %d\n", pPager, pgno));
@@ -2985,7 +2991,6 @@
   pPager->journalOpen = 1;
   pPager->journalStarted = 0;
   pPager->needSync = 0;
-  pPager->alwaysRollback = 0;
   pPager->nRec = 0;
   if( pPager->errCode ){
     rc = pPager->errCode;
@@ -3434,13 +3439,24 @@
 ** page contains critical data, we still need to be sure it gets
 ** rolled back in spite of the sqlite3PagerDontRollback() call.
 */
-void sqlite3PagerDontWrite(DbPage *pDbPage){
+int sqlite3PagerDontWrite(DbPage *pDbPage){
   PgHdr *pPg = pDbPage;
   Pager *pPager = pPg->pPager;
+  int rc;
 
-  if( MEMDB ) return;
-  pPg->flags |= PGHDR_ALWAYS_ROLLBACK;
-  if( (pPg->flags&PGHDR_DIRTY) && !pPager->stmtInUse ){
+  if( MEMDB || pPg->pgno>pPager->origDbSize ){
+    return SQLITE_OK;
+  }
+  if( pPager->pAlwaysRollback==0 ){
+    assert( pPager->pInJournal );
+    pPager->pAlwaysRollback = sqlite3BitvecCreate(pPager->origDbSize);
+    if( !pPager->pAlwaysRollback ){
+      return SQLITE_NOMEM;
+    }
+  }
+  rc = sqlite3BitvecSet(pPager->pAlwaysRollback, pPg->pgno);
+
+  if( rc==SQLITE_OK && (pPg->flags&PGHDR_DIRTY) && !pPager->stmtInUse ){
     assert( pPager->state>=PAGER_SHARED );
     if( pPager->dbSize==(int)pPg->pgno && pPager->origDbSize<pPager->dbSize ){
       /* If this pages is the last page in the file and the file has grown
@@ -3460,6 +3476,7 @@
 #endif
     }
   }
+  return rc;
 }
 
 /*
@@ -3482,15 +3499,16 @@
   ** this page (DontWrite() sets the alwaysRollback flag), then this
   ** function is a no-op.
   */
-  if( pPager->journalOpen==0 || (pPg->flags&PGHDR_ALWAYS_ROLLBACK) 
-   || pPager->alwaysRollback 
+  if( pPager->journalOpen==0 
+   || sqlite3BitvecTest(pPager->pAlwaysRollback, pPg->pgno)
+   || pPg->pgno>pPager->origDbSize
   ){
     return;
   }
   assert( !MEMDB );    /* For a memdb, pPager->journalOpen is always 0 */
 
 #ifdef SQLITE_SECURE_DELETE
-  if( pPg->inJournal || (int)pPg->pgno > pPager->origDbSize ){
+  if( pPg->inJournal || (int)pPg->pgno>pPager->origDbSize ){
     return;
   }
 #endif
@@ -3782,7 +3800,7 @@
     sqlite3PcacheCommit(pPager->pPCache, 0);
     sqlite3PcacheCleanAll(pPager->pPCache);
     sqlite3PcacheSetFlags(pPager->pPCache, 
-       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC | PGHDR_ALWAYS_ROLLBACK), 0
+       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
     );
     pPager->state = PAGER_SHARED;
   }else{
@@ -3813,7 +3831,7 @@
     sqlite3PcacheRollback(pPager->pPCache, 0);
     sqlite3PcacheCleanAll(pPager->pPCache);
     sqlite3PcacheSetFlags(pPager->pPCache, 
-       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC | PGHDR_ALWAYS_ROLLBACK), 0
+       ~(PGHDR_IN_JOURNAL | PGHDR_NEED_SYNC), 0
     );
     pPager->dbSize = pPager->origDbSize;
     pager_truncate_cache(pPager);
@@ -4232,8 +4250,4 @@
   return pPager->journalSizeLimit;
 }
 
-void sqlite3PagerAlwaysRollback(Pager *pPager){
-  pPager->alwaysRollback = 1;
-}
-
 #endif /* SQLITE_OMIT_DISKIO */
diff --git a/src/pager.h b/src/pager.h
index 50bce8a..305eff1 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.80 2008/08/22 17:34:45 drh Exp $
+** @(#) $Id: pager.h,v 1.81 2008/08/27 15:16:34 danielk1977 Exp $
 */
 
 #ifndef _PAGER_H_
@@ -95,7 +95,7 @@
 int sqlite3PagerStmtCommit(Pager*);
 int sqlite3PagerStmtRollback(Pager*);
 void sqlite3PagerDontRollback(DbPage*);
-void sqlite3PagerDontWrite(DbPage*);
+int sqlite3PagerDontWrite(DbPage*);
 int sqlite3PagerRefcount(Pager*);
 void sqlite3PagerSetSafetyLevel(Pager*,int,int);
 const char *sqlite3PagerFilename(Pager*);
diff --git a/src/pcache.c b/src/pcache.c
index db2ec83..07300ad 100644
--- a/src/pcache.c
+++ b/src/pcache.c
@@ -11,7 +11,7 @@
 *************************************************************************
 ** This file implements that page cache.
 **
-** @(#) $Id: pcache.c,v 1.16 2008/08/27 09:44:40 danielk1977 Exp $
+** @(#) $Id: pcache.c,v 1.17 2008/08/27 15:16:34 danielk1977 Exp $
 */
 #include "sqliteInt.h"
 
@@ -497,15 +497,6 @@
     pcacheRemoveFromLruList(p);
     pcacheRemoveFromHash(p);
     pcacheRemoveFromList(&p->pCache->pClean, p);
-
-    /* If the always-rollback flag is set on the page being recycled, set 
-    ** the always-rollback flag on the corresponding pager. TODO: This is
-    ** a thread-safety problem.
-    */
-    if( p->flags&PGHDR_ALWAYS_ROLLBACK ){
-      assert(p->pPager);
-      sqlite3PagerAlwaysRollback(p->pPager);
-    }
   }
 
   return p;
diff --git a/src/pcache.h b/src/pcache.h
index 0fc3b7d..8dcc365 100644
--- a/src/pcache.h
+++ b/src/pcache.h
@@ -12,7 +12,7 @@
 ** This header file defines the interface that the sqlite page cache
 ** subsystem. 
 **
-** @(#) $Id: pcache.h,v 1.6 2008/08/26 18:05:48 danielk1977 Exp $
+** @(#) $Id: pcache.h,v 1.7 2008/08/27 15:16:34 danielk1977 Exp $
 */
 
 #ifndef _PCACHE_H_
@@ -48,7 +48,6 @@
 #define PGHDR_IN_STMTJRNL       0x002  /* Page is in the statement journal */
 #define PGHDR_DIRTY             0x004  /* Page has changed */
 #define PGHDR_NEED_SYNC         0x008  /* Peed to fsync this page */
-#define PGHDR_ALWAYS_ROLLBACK   0x010  /* Force writing to journal */
 #define PGHDR_NEED_READ         0x020  /* Content is unread */
 #define PGHDR_IS_INIT           0x040  /* pData is initialized */
 #define PGHDR_REUSE_UNLIKELY    0x080  /* Hint: Reuse is unlikely */