]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Get the ABORT conflict resolution algorithm working. (CVS 362)
authordrh <drh@noemail.net>
Sat, 2 Feb 2002 18:49:19 +0000 (18:49 +0000)
committerdrh <drh@noemail.net>
Sat, 2 Feb 2002 18:49:19 +0000 (18:49 +0000)
FossilOrigin-Name: 9be4d4c6f12056782966396dca0b8e2d384d0cf2

12 files changed:
manifest
manifest.uuid
src/btree.c
src/btree.h
src/build.c
src/insert.c
src/pager.c
src/sqliteInt.h
src/update.c
src/vdbe.c
src/vdbe.h
test/pager.test

index 8fe109219c8a8d9be4f1ba3aadb20a7c5f52078c..f37bfe4807ab005e264c5f2eb5e494b550e50390 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Checkpoint\scode\sadded\sto\sthe\spager.\s\sRegression\stests\swork\sbut\sthe\snew\sAPIs\nhave\snot\sbeen\stested\syet.\s(CVS\s361)
-D 2002-02-02T15:01:16
+C Get\sthe\sABORT\sconflict\sresolution\salgorithm\sworking.\s(CVS\s362)
+D 2002-02-02T18:49:20
 F Makefile.in 9fa4277413bf1d9cf91365f07d4108d7d87ed2af
 F Makefile.template 3372d45f8853afdb70bd30cc6fb50a3cd9069834
 F README a4c0ba11354ef6ba0776b400d057c59da47a4cc0
@@ -19,19 +19,19 @@ F ltmain.sh e9ed72eb1d690f447c13945eaf69e28af531eda1
 F publish.sh 5b59f4aff037aafa0e4a3b6fa599495dbd73f360
 F sqlite.1 2e2bb0529ef468ade9e4322bd609d0695fb9ded9
 F src/TODO af7f3cab0228e34149cf98e073aa83d45878e7e6
-F src/btree.c c796e387da340cb628dc1e41f684fc20253f561e
-F src/btree.h 9ead7f54c270d8a554e59352ca7318fdaf411390
-F src/build.c f725dc396d784f723950cf3d47a10f1a69b2f7b7
+F src/btree.c 94deba286af8e1f665d98378c099da2f7455f291
+F src/btree.h a94bef69f5174461331b6b9ae45a2d84f05af6db
+F src/build.c 397d78ce466e3c22d56de85ae23d8a2777d3b9e6
 F src/delete.c f8ad71be53cf18656b6573de65395852fe817f0c
 F src/expr.c a2a87dbd411a508ff89dffa90505ad42dac2f920
 F src/hash.c 8f7c740ef2eaaa8decfa8751f2be30680b123e46
 F src/hash.h a5f5b3ce2d086a172c5879b0b06a27a82eac9fac
-F src/insert.c 42e89cb227ce744802622886db3572f78e72093f
+F src/insert.c 051e909cf4c8505aae930dcd773215404e187f23
 F src/main.c 300320ba68d3e5b22c2c5b2c07fa884878202181
 F src/md5.c 52f677bfc590e09f71d07d7e327bd59da738d07c
 F src/os.c 1953080d14098cd45e5bde88941567688efb72b1
 F src/os.h a17596ecc7f38a228b83ecdb661fb03ce44726d6
-F src/pager.c f7274d47d8c8a38ce363bfc49f8ccf9d9842e951
+F src/pager.c 4059bda97a7e10083b77b7d347fea45426b08589
 F src/pager.h b28f004e2f5541dc60cc32db01bf80cf4d056283
 F src/parse.y 88856227ae8472d0f4ae8514bc9561a6ca060690
 F src/printf.c 300a90554345751f26e1fc0c0333b90a66110a1d
@@ -40,17 +40,17 @@ F src/select.c fc11d5a8c2bae1b62d8028ffb111c773ad6bf161
 F src/shell.c c102dfe388c7618a668c944ff157c49cb48f28e3
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in f57074c84a2c112a5093ba7a9d9636aa9cacc87c
-F src/sqliteInt.h 70fd20107f4953312e76a9630a704c9405161040
+F src/sqliteInt.h 41459cf6cae299f66c3ab16b20b8e24443c06089
 F src/table.c c89698bd5bb4b8d14722d6ee7e9be014c383d24a
 F src/tclsqlite.c b9cf346e95291cb4c4f1bf5ac1d77db6b8ad023d
 F src/test1.c 33efd350dca27c52c58c553c04fd3a6a51f13c1f
 F src/test2.c d410dbd8a90faa466c3ab694fa0aa57f5a773aa6
 F src/test3.c d6775f95fd91f5b3cf0e2382a28e5aaeb68f745b
 F src/tokenize.c 01a09db6adf933e941db1b781789a0c175be6504
-F src/update.c 3fb7c1601bbd379e39881d6b731d3223b822188a
+F src/update.c 95459f94a061860bf8e5716b3426a5ba85c79103
 F src/util.c 8f8973dd55a6ec63be9632fc5de86965c99d6327
-F src/vdbe.c 8e6f1bfff67639b7c3bd07822595fc2ef19f0f34
-F src/vdbe.h 5b1bd518126fc5a30e6ea13fe11de931b32c4b59
+F src/vdbe.c 3e9d9dba06fb7f6fe85ca3c345eedd32bf27f3a7
+F src/vdbe.h 3791edabb212038ae5fbcfa72580204596be01a7
 F src/where.c 2dda39367f193194e4c7d2e0dcab31527d9d8aba
 F test/all.test 2a51e5395ac7c2c539689b123b9782a05e3837fe
 F test/bigrow.test 8ab252dba108f12ad64e337b0f2ff31a807ac578
@@ -74,7 +74,7 @@ F test/main.test 1626345b5f630c5398eede500d9354813b76b0fd
 F test/malloc.test 70fdd0812e2a57eb746aaf015350f58bb8eee0b1
 F test/misc1.test 50a5ca3481fc1f3cd6b978bcd6ed04c06f26a1e6
 F test/notnull.test b1f3e42fc475b0b5827b27b2e9b562081995ff30
-F test/pager.test 59bbc4e3d489529ed33db6e15595789e51056077
+F test/pager.test b0c0d00cd5dce0ce21f16926956b195c0ab5044c
 F test/printf.test 3cb415073754cb8ff076f26173143c3cd293a9da
 F test/quick.test 6f023c7a73fc413e6d65b7a1879c79764038dc05
 F test/quote.test 286db944717afa9a9bf829dd85e59185c65d5435
@@ -122,7 +122,7 @@ F www/speed.tcl 83457b2bf6bb430900bd48ca3dd98264d9a916a5
 F www/sqlite.tcl 8b5884354cb615049aed83039f8dfe1552a44279
 F www/tclsqlite.tcl 829b393d1ab187fd7a5e978631b3429318885c49
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P d0e7cf4a83e6abad7129bed356b7492dddaff474
-R 819c88d7bb6a0c5001739d080373471b
+P aaa53e113ef849e34883ead8ae584c722ad967db
+R f2763ccecef4a6c8cd264dd83b3c6a22
 U drh
-Z 5798e92bdcf7b705413adfa7c6a6e6fe
+Z fb37183391242f0b96b036b1bf3ee1ea
index e6ed4d77afb42a735da2be1d55aabf9bb6367d12..50d1d7fd5177de7b60f1704f2472e6134d6b9baf 100644 (file)
@@ -1 +1 @@
-aaa53e113ef849e34883ead8ae584c722ad967db
\ No newline at end of file
+9be4d4c6f12056782966396dca0b8e2d384d0cf2
\ No newline at end of file
index e4b18d53f574d50522bf4e0a5d3791e799ddb015..aea71239048f20fbcf60d7e0eeaf28ceff7b810f 100644 (file)
@@ -9,7 +9,7 @@
 **    May you share freely, never taking more than you give.
 **
 *************************************************************************
-** $Id: btree.c,v 1.46 2002/01/04 03:09:29 drh Exp $
+** $Id: btree.c,v 1.47 2002/02/02 18:49:20 drh Exp $
 **
 ** This file implements a external (disk-based) database using BTrees.
 ** For a detailed discussion of BTrees, refer to
@@ -302,7 +302,8 @@ struct Btree {
   Pager *pPager;        /* The page cache */
   BtCursor *pCursor;    /* A list of all open cursors */
   PageOne *page1;       /* First page of the database */
-  int inTrans;          /* True if a transaction is in progress */
+  u8 inTrans;           /* True if a transaction is in progress */
+  u8 inCkpt;            /* True if there is a checkpoint on the transaction */
   Hash locks;           /* Key: root page number.  Data: lock count */
 };
 typedef Btree Bt;
@@ -691,6 +692,7 @@ static void unlockBtreeIfUnused(Btree *pBt){
     sqlitepager_unref(pBt->page1);
     pBt->page1 = 0;
     pBt->inTrans = 0;
+    pBt->inCkpt = 0;
   }
 }
 
@@ -753,6 +755,7 @@ int sqliteBtreeBeginTrans(Btree *pBt){
   }
   if( rc==SQLITE_OK ){
     pBt->inTrans = 1;
+    pBt->inCkpt = 0;
   }else{
     unlockBtreeIfUnused(pBt);
   }
@@ -770,6 +773,7 @@ int sqliteBtreeCommit(Btree *pBt){
   if( pBt->inTrans==0 ) return SQLITE_ERROR;
   rc = sqlitepager_commit(pBt->pPager);
   pBt->inTrans = 0;
+  pBt->inCkpt = 0;
   unlockBtreeIfUnused(pBt);
   return rc;
 }
@@ -788,6 +792,7 @@ int sqliteBtreeRollback(Btree *pBt){
   BtCursor *pCur;
   if( pBt->inTrans==0 ) return SQLITE_OK;
   pBt->inTrans = 0;
+  pBt->inCkpt = 0;
   for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
     if( pCur->pPage ){
       sqlitepager_unref(pCur->pPage);
@@ -799,6 +804,61 @@ int sqliteBtreeRollback(Btree *pBt){
   return rc;
 }
 
+/*
+** Set the checkpoint for the current transaction.  The checkpoint serves
+** as a sub-transaction that can be rolled back independently of the
+** main transaction.  You must start a transaction before starting a
+** checkpoint.  The checkpoint is ended automatically if the transaction
+** commits or rolls back.
+**
+** Only one checkpoint may be active at a time.  It is an error to try
+** to start a new checkpoint if another checkpoint is already active.
+*/
+int sqliteBtreeBeginCkpt(Btree *pBt){
+  int rc;
+  if( !pBt->inTrans || pBt->inCkpt ) return SQLITE_ERROR;
+  rc = sqlitepager_ckpt_begin(pBt->pPager);
+  pBt->inCkpt = 1;
+  return rc;
+}
+
+
+/*
+** Commit a checkpoint to transaction currently in progress.  If no
+** checkpoint is active, this is a no-op.
+*/
+int sqliteBtreeCommitCkpt(Btree *pBt){
+  int rc;
+  if( pBt->inCkpt ){
+    rc = sqlitepager_ckpt_commit(pBt->pPager);
+  }else{
+    rc = SQLITE_OK;
+  }
+  return rc;
+}
+
+/*
+** Rollback the checkpoint to the current transaction.  If there
+** is no active checkpoint or transaction, this routine is a no-op.
+**
+** All cursors will be invalided by this operation.  Any attempt
+** to use a cursor that was open at the beginning of this operation
+** will result in an error.
+*/
+int sqliteBtreeRollbackCkpt(Btree *pBt){
+  int rc;
+  BtCursor *pCur;
+  if( pBt->inCkpt==0 ) return SQLITE_OK;
+  for(pCur=pBt->pCursor; pCur; pCur=pCur->pNext){
+    if( pCur->pPage ){
+      sqlitepager_unref(pCur->pPage);
+      pCur->pPage = 0;
+    }
+  }
+  rc = sqlitepager_ckpt_rollback(pBt->pPager);
+  return rc;
+}
+
 /*
 ** Create a new cursor for the BTree whose root is on the page
 ** iTable.  The act of acquiring a cursor gets a read lock on 
index a86966136bcf5d2b264ddd939fc8b260a037ff81..eb78f4b725e51cdfbb8923e6293b233d1fabdc3c 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.19 2002/01/04 03:09:30 drh Exp $
+** @(#) $Id: btree.h,v 1.20 2002/02/02 18:49:20 drh Exp $
 */
 #ifndef _BTREE_H_
 #define _BTREE_H_
@@ -28,6 +28,9 @@ int sqliteBtreeSetCacheSize(Btree*, int);
 int sqliteBtreeBeginTrans(Btree*);
 int sqliteBtreeCommit(Btree*);
 int sqliteBtreeRollback(Btree*);
+int sqliteBtreeBeginCkpt(Btree*);
+int sqliteBtreeCommitCkpt(Btree*);
+int sqliteBtreeRollbackCkpt(Btree*);
 
 int sqliteBtreeCreateTable(Btree*, int*);
 int sqliteBtreeCreateIndex(Btree*, int*);
index fa8d92a0a663220a7dd8a98ab27c3092a8ad39be..e45008c7fdd4578c2051212c51879dc0dbb186d1 100644 (file)
@@ -25,7 +25,7 @@
 **     ROLLBACK
 **     PRAGMA
 **
-** $Id: build.c,v 1.70 2002/01/31 15:54:22 drh Exp $
+** $Id: build.c,v 1.71 2002/02/02 18:49:20 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -1338,7 +1338,7 @@ void sqliteCopy(
   v = sqliteGetVdbe(pParse);
   if( v ){
     int openOp;
-    sqliteBeginWriteOperation(pParse);
+    sqliteBeginMultiWriteOperation(pParse);
     addr = sqliteVdbeAddOp(v, OP_FileOpen, 0, 0);
     sqliteVdbeChangeP3(v, addr, pFilename->z, pFilename->n);
     sqliteVdbeDequoteP3(v, addr);
@@ -1493,20 +1493,40 @@ void sqliteRollbackTransaction(Parse *pParse){
 
 /*
 ** Generate VDBE code that prepares for doing an operation that
-** might change the database.  If we are in the middle of a transaction,
-** then this sets a checkpoint.  If we are not in a transaction, then
-** start a transaction.
+** might change the database.  The operation will be atomic in the
+** sense that it will either do its changes completely or not at
+** all.  So there is not need to set a checkpoint is a transaction
+** is already in effect.
 */
 void sqliteBeginWriteOperation(Parse *pParse){
   Vdbe *v;
   v = sqliteGetVdbe(pParse);
   if( v==0 ) return;
-  if( pParse->db->flags & SQLITE_InTrans ){
-    /* sqliteVdbeAddOp(v, OP_CheckPoint, 0, 0); */
-  }else{
+  if( (pParse->db->flags & SQLITE_InTrans)==0  ){
+    sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
+    sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
+    pParse->schemaVerified = 1;
+  }
+}
+
+/*
+** Generate VDBE code that prepares for doing an operation that
+** might change the database.  The operation might not be atomic in
+** the sense that an error may be discovered and the operation might
+** abort after some changes have been made.  If we are in the middle 
+** of a transaction, then this sets a checkpoint.  If we are not in
+** a transaction, then start a transaction.
+*/
+void sqliteBeginMultiWriteOperation(Parse *pParse){
+  Vdbe *v;
+  v = sqliteGetVdbe(pParse);
+  if( v==0 ) return;
+  if( (pParse->db->flags & SQLITE_InTrans)==0 ){
     sqliteVdbeAddOp(v, OP_Transaction, 0, 0);
     sqliteVdbeAddOp(v, OP_VerifyCookie, pParse->db->schema_cookie, 0);
     pParse->schemaVerified = 1;
+  }else{
+    sqliteVdbeAddOp(v, OP_Checkpoint, 0, 0);
   }
 }
 
index 5632c76b470fc7b6c61d3cd6f59a46d0fc3e896e..3e92c26d652593a44e2c3ac038fb8df788eba078 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle INSERT statements in SQLite.
 **
-** $Id: insert.c,v 1.39 2002/01/31 15:54:22 drh Exp $
+** $Id: insert.c,v 1.40 2002/02/02 18:49:20 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -79,7 +79,11 @@ void sqliteInsert(
   */
   v = sqliteGetVdbe(pParse);
   if( v==0 ) goto insert_cleanup;
-  sqliteBeginWriteOperation(pParse);
+  if( pSelect ){
+    sqliteBeginMultiWriteOperation(pParse);
+  }else{
+    sqliteBeginWriteOperation(pParse);
+  }
 
   /* Figure out how many columns of data are supplied.  If the data
   ** is coming from a SELECT statement, then this step has to generate
index a6f112a5c8f9179fb00d97b9d98e8bd293a1b2d0..d4fe1852de06bc980765577a4916f6d8ed81b5a6 100644 (file)
@@ -18,7 +18,7 @@
 ** file simultaneously, or one process from reading the database while
 ** another is writing.
 **
-** @(#) $Id: pager.c,v 1.37 2002/02/02 15:01:16 drh Exp $
+** @(#) $Id: pager.c,v 1.38 2002/02/02 18:49:20 drh Exp $
 */
 #include "sqliteInt.h"
 #include "pager.h"
 **                       threads can be reading or writing while one
 **                       process is writing.
 **
-**   SQLITE_CHECKPOINT   The page cache is writing to the database and
-**                       preserving its changes so that it can back them
-**                       out later if need be.
-**
 ** The page cache comes up in SQLITE_UNLOCK.  The first time a
 ** sqlite_page_get() occurs, the state transitions to SQLITE_READLOCK.
 ** After all pages have been released using sqlite_page_unref(),
 ** be in SQLITE_READLOCK before it transitions to SQLITE_WRITELOCK.)
 ** The sqlite_page_rollback() and sqlite_page_commit() functions 
 ** transition the state from SQLITE_WRITELOCK back to SQLITE_READLOCK.
-**
-** The sqlite_ckpt_begin() function moves the state from SQLITE_WRITELOCK
-** to SQLITE_CHECKPOINT.  The state transitions back to SQLITE_WRITELOCK
-** on calls to sqlite_ckpt_commit() or sqlite_ckpt_rollback().  While
-** in SQLITE_CHECKPOINT, calls to sqlite_commit() or sqlite_rollback()
-** transition directly back to SQLITE_READLOCK.
-**
-** The code does unequality comparisons on these constants so the order
-** must be preserved.
 */
 #define SQLITE_UNLOCK      0
 #define SQLITE_READLOCK    1
 #define SQLITE_WRITELOCK   2
-#define SQLITE_CHECKPOINT  3
 
 
 /*
@@ -254,12 +240,12 @@ static int pager_unwritelock(Pager *pPager){
   int rc;
   PgHdr *pPg;
   if( pPager->state<SQLITE_WRITELOCK ) return SQLITE_OK;
+  sqlitepager_ckpt_commit(pPager);
   sqliteOsClose(&pPager->jfd);
   pPager->journalOpen = 0;
   sqliteOsDelete(pPager->zJournal);
   rc = sqliteOsReadLock(&pPager->fd);
   assert( rc==SQLITE_OK );
-  sqliteFree( pPager->aInCkpt );
   sqliteFree( pPager->aInJournal );
   pPager->aInJournal = 0;
   for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
@@ -279,7 +265,7 @@ static int pager_playback_one_page(Pager *pPager, OsFile *jfd){
   PgHdr *pPg;              /* An existing page in the cache */
   PageRecord pgRec;
 
-  rc = sqliteOsRead(&pPager->jfd, &pgRec, sizeof(pgRec));
+  rc = sqliteOsRead(jfd, &pgRec, sizeof(pgRec));
   if( rc!=SQLITE_OK ) return rc;
 
   /* Sanity checking on the page */
@@ -383,8 +369,9 @@ end_playback:
 ** This is similar to playing back the transaction journal but with
 ** a few extra twists.
 **
-**    (1)  The original size of the database file is stored in
-**         pPager->ckptSize, not in the journal file itself.
+**    (1)  The number of pages in the database file at the start of
+**         the checkpoint is stored in pPager->ckptSize, not in the
+**         journal file itself.
 **
 **    (2)  In addition to playing back the checkpoint journal, also
 **         playback all pages of the transaction journal beginning
@@ -397,7 +384,7 @@ static int pager_ckpt_playback(Pager *pPager){
 
   /* Truncate the database back to its original size.
   */
-  rc = sqliteOsTruncate(&pPager->fd, pPager->ckptSize);
+  rc = sqliteOsTruncate(&pPager->fd, pPager->ckptSize*SQLITE_PAGE_SIZE);
   pPager->dbSize = pPager->ckptSize;
 
   /* Figure out how many records are in the checkpoint journal.
@@ -437,14 +424,9 @@ static int pager_ckpt_playback(Pager *pPager){
   
 
 end_ckpt_playback:
-  sqliteOsClose(&pPager->cpfd);
-  pPager->ckptOpen = 0;
   if( rc!=SQLITE_OK ){
-    pager_unwritelock(pPager);
     pPager->errMask |= PAGER_ERR_CORRUPT;
     rc = SQLITE_CORRUPT;
-  }else{
-    rc = pager_unwritelock(pPager);
   }
   return rc;
 }
@@ -595,7 +577,6 @@ int sqlitepager_pagecount(Pager *pPager){
 int sqlitepager_close(Pager *pPager){
   PgHdr *pPg, *pNext;
   switch( pPager->state ){
-    case SQLITE_CHECKPOINT:
     case SQLITE_WRITELOCK: {
       sqlitepager_rollback(pPager);
       sqliteOsUnlock(&pPager->fd);
@@ -883,7 +864,7 @@ int sqlitepager_get(Pager *pPager, Pgno pgno, void **ppPage){
     }else{
       pPg->inJournal = 0;
     }
-    if( pPager->aInCkpt && (int)pgno*SQLITE_PAGE_SIZE<=pPager->ckptSize ){
+    if( pPager->aInCkpt && (int)pgno<=pPager->ckptSize ){
       pPg->inCkpt = (pPager->aInCkpt[pgno/8] & (1<<(pgno&7)))!=0;
     }else{
       pPg->inCkpt = 0;
@@ -1111,8 +1092,7 @@ int sqlitepager_write(void *pData){
   /* If the checkpoint journal is open and the page is not in it,
   ** then write the current page to the checkpoint journal.
   */
-  if( pPager->ckptOpen && !pPg->inCkpt 
-      && (int)pPg->pgno*SQLITE_PAGE_SIZE < pPager->ckptSize ){
+  if( pPager->ckptOpen && !pPg->inCkpt && (int)pPg->pgno<=pPager->ckptSize ){
     assert( pPg->inJournal );
     rc = sqliteOsWrite(&pPager->cpfd, &pPg->pgno, sizeof(Pgno));
     if( rc==SQLITE_OK ){
@@ -1268,7 +1248,7 @@ int sqlitepager_ckpt_begin(Pager *pPager){
   }
   rc = sqliteOsFileSize(&pPager->jfd, &pPager->ckptJSize);
   if( rc ) goto ckpt_begin_failed;
-  pPager->ckptSize = pPager->dbSize * SQLITE_PAGE_SIZE;
+  pPager->ckptSize = pPager->dbSize;
   rc = sqlitepager_opentemp(zTemp, &pPager->cpfd);
   if( rc ) goto ckpt_begin_failed;
   pPager->ckptOpen = 1;
@@ -1286,10 +1266,16 @@ ckpt_begin_failed:
 ** Commit a checkpoint.
 */
 int sqlitepager_ckpt_commit(Pager *pPager){
-  assert( pPager->ckptOpen );
-  sqliteOsClose(&pPager->cpfd);
-  sqliteFree(pPager->aInCkpt);
-  pPager->ckptOpen = 0;
+  if( pPager->ckptOpen ){
+    PgHdr *pPg;
+    sqliteOsClose(&pPager->cpfd);
+    pPager->ckptOpen = 0;
+    sqliteFree( pPager->aInCkpt );
+    pPager->aInCkpt = 0;
+    for(pPg=pPager->pAll; pPg; pPg=pPg->pNextAll){
+      pPg->inCkpt = 0;
+    }
+  }
   return SQLITE_OK;
 }
 
@@ -1298,11 +1284,12 @@ int sqlitepager_ckpt_commit(Pager *pPager){
 */
 int sqlitepager_ckpt_rollback(Pager *pPager){
   int rc;
-  assert( pPager->ckptOpen );
-  rc = pager_ckpt_playback(pPager);
-  sqliteOsClose(&pPager->cpfd);
-  sqliteFree(pPager->aInCkpt);
-  pPager->ckptOpen = 0;
+  if( pPager->ckptOpen ){
+    rc = pager_ckpt_playback(pPager);
+    sqlitepager_ckpt_commit(pPager);
+  }else{
+    rc = SQLITE_OK;
+  }
   return rc;
 }
 
index eb4630efb5ecfdd2fba550f3f5c04a215c819fd2..9a095b33f9e8864972437999a0a0d3b1fefb2a8a 100644 (file)
@@ -11,7 +11,7 @@
 *************************************************************************
 ** Internal interface definitions for SQLite.
 **
-** @(#) $Id: sqliteInt.h,v 1.83 2002/01/31 15:54:22 drh Exp $
+** @(#) $Id: sqliteInt.h,v 1.84 2002/02/02 18:49:21 drh Exp $
 */
 #include "sqlite.h"
 #include "hash.h"
@@ -565,4 +565,5 @@ void sqliteGenerateRowIndexDelete(Vdbe*, Table*, int, char*);
 void sqliteGenerateConstraintChecks(Parse*,Table*,int,char*,int,int,int,int);
 void sqliteCompleteInsertion(Parse*, Table*, int, char*, int, int);
 void sqliteBeginWriteOperation(Parse*);
+void sqliteBeginMultiWriteOperation(Parse*);
 void sqliteEndWriteOperation(Parse*);
index 4b3a71111ec1bf9be45e1c498d86d328f9454d2b..c7f12888d22b88c61ce3357655e8f21dc77df8fa 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle UPDATE statements.
 **
-** $Id: update.c,v 1.32 2002/01/31 15:54:22 drh Exp $
+** $Id: update.c,v 1.33 2002/02/02 18:49:21 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -161,7 +161,7 @@ void sqliteUpdate(
   */
   v = sqliteGetVdbe(pParse);
   if( v==0 ) goto update_cleanup;
-  sqliteBeginWriteOperation(pParse);
+  sqliteBeginMultiWriteOperation(pParse);
 
   /* Begin the database scan
   */
index acc6d33455f5cc2100101bd6370c730fe858d481..59ec69ddb5e1b6e1a9bc1eba99b0e58b1751cbbe 100644 (file)
@@ -30,7 +30,7 @@
 ** But other routines are also provided to help in building up
 ** a program instruction by instruction.
 **
-** $Id: vdbe.c,v 1.113 2002/01/31 15:54:22 drh Exp $
+** $Id: vdbe.c,v 1.114 2002/02/02 18:49:21 drh Exp $
 */
 #include "sqliteInt.h"
 #include <ctype.h>
@@ -860,35 +860,35 @@ void sqliteVdbeDelete(Vdbe *p){
 ** this array, then copy and paste it into this file, if you want.
 */
 static char *zOpName[] = { 0,
-  "Transaction",       "Commit",            "Rollback",          "ReadCookie",
-  "SetCookie",         "VerifyCookie",      "Open",              "OpenTemp",
-  "OpenWrite",         "OpenAux",           "OpenWrAux",         "Close",
-  "MoveTo",            "NewRecno",          "PutIntKey",         "PutStrKey",
-  "Distinct",          "Found",             "NotFound",          "IsUnique",
-  "NotExists",         "Delete",            "Column",            "KeyAsData",
-  "Recno",             "FullKey",           "Rewind",            "Next",
-  "Destroy",           "Clear",             "CreateIndex",       "CreateTable",
-  "Reorganize",        "IdxPut",            "IdxDelete",         "IdxRecno",
-  "IdxGT",             "IdxGE",             "MemLoad",           "MemStore",
-  "ListWrite",         "ListRewind",        "ListRead",          "ListReset",
-  "SortPut",           "SortMakeRec",       "SortMakeKey",       "Sort",
-  "SortNext",          "SortCallback",      "SortReset",         "FileOpen",
-  "FileRead",          "FileColumn",        "AggReset",          "AggFocus",
-  "AggIncr",           "AggNext",           "AggSet",            "AggGet",
-  "SetInsert",         "SetFound",          "SetNotFound",       "MakeRecord",
-  "MakeKey",           "MakeIdxKey",        "IncrKey",           "Goto",
-  "If",                "Halt",              "ColumnCount",       "ColumnName",
-  "Callback",          "NullCallback",      "Integer",           "String",
-  "Pop",               "Dup",               "Pull",              "Push",
-  "MustBeInt",         "Add",               "AddImm",            "Subtract",
-  "Multiply",          "Divide",            "Remainder",         "BitAnd",
-  "BitOr",             "BitNot",            "ShiftLeft",         "ShiftRight",
-  "AbsValue",          "Precision",         "Min",               "Max",
-  "Like",              "Glob",              "Eq",                "Ne",
-  "Lt",                "Le",                "Gt",                "Ge",
-  "IsNull",            "NotNull",           "Negative",          "And",
-  "Or",                "Not",               "Concat",            "Noop",
-  "Strlen",            "Substr",            "Limit",           
+  "Transaction",       "Checkpoint",        "Commit",            "Rollback",
+  "ReadCookie",        "SetCookie",         "VerifyCookie",      "Open",
+  "OpenTemp",          "OpenWrite",         "OpenAux",           "OpenWrAux",
+  "Close",             "MoveTo",            "NewRecno",          "PutIntKey",
+  "PutStrKey",         "Distinct",          "Found",             "NotFound",
+  "IsUnique",          "NotExists",         "Delete",            "Column",
+  "KeyAsData",         "Recno",             "FullKey",           "Rewind",
+  "Next",              "Destroy",           "Clear",             "CreateIndex",
+  "CreateTable",       "Reorganize",        "IdxPut",            "IdxDelete",
+  "IdxRecno",          "IdxGT",             "IdxGE",             "MemLoad",
+  "MemStore",          "ListWrite",         "ListRewind",        "ListRead",
+  "ListReset",         "SortPut",           "SortMakeRec",       "SortMakeKey",
+  "Sort",              "SortNext",          "SortCallback",      "SortReset",
+  "FileOpen",          "FileRead",          "FileColumn",        "AggReset",
+  "AggFocus",          "AggIncr",           "AggNext",           "AggSet",
+  "AggGet",            "SetInsert",         "SetFound",          "SetNotFound",
+  "MakeRecord",        "MakeKey",           "MakeIdxKey",        "IncrKey",
+  "Goto",              "If",                "Halt",              "ColumnCount",
+  "ColumnName",        "Callback",          "NullCallback",      "Integer",
+  "String",            "Pop",               "Dup",               "Pull",
+  "Push",              "MustBeInt",         "Add",               "AddImm",
+  "Subtract",          "Multiply",          "Divide",            "Remainder",
+  "BitAnd",            "BitOr",             "BitNot",            "ShiftLeft",
+  "ShiftRight",        "AbsValue",          "Precision",         "Min",
+  "Max",               "Like",              "Glob",              "Eq",
+  "Ne",                "Lt",                "Le",                "Gt",
+  "Ge",                "IsNull",            "NotNull",           "Negative",
+  "And",               "Or",                "Not",               "Concat",
+  "Noop",              "Strlen",            "Substr",            "Limit",
 };
 
 /*
@@ -1072,8 +1072,9 @@ int sqliteVdbeExec(
   sqlite *db = p->db;        /* The database */
   char **zStack;             /* Text stack */
   Stack *aStack;             /* Additional stack information */
-  int rollbackOnError = 1;   /* Do a ROLLBACK if an error is encountered */
-  char zBuf[100];            /* Space to sprintf() an integer */
+  int errorAction = OE_Abort; /* Recovery action to do in case of an error */
+  int undoTransOnError = 0;   /* If error, either ROLLBACK or COMMIT */
+  char zBuf[100];             /* Space to sprintf() an integer */
 
 
   /* No instruction ever pushes more than a single element onto the
@@ -1172,7 +1173,7 @@ case OP_Goto: {
 case OP_Halt: {
   if( pOp->p1!=SQLITE_OK ){
     rc = pOp->p1;
-    rollbackOnError = pOp->p2!=OE_Fail;
+    errorAction = pOp->p2;
     goto abort_due_to_error;
   }else{
     pc = p->nOp-1;
@@ -2319,18 +2320,32 @@ case OP_IncrKey: {
   break;
 }
 
+/* Opcode: Checkpoint * * *
+**
+** Begin a checkpoint.  A checkpoint is the beginning of a operation that
+** is part of a larger transaction but which might need to be rolled back
+** itself without effecting the containing transaction.  A checkpoint will
+** be automatically committed or rollback when the VDBE halts.
+*/
+case OP_Checkpoint: {
+  rc = sqliteBtreeBeginCkpt(pBt);
+  if( rc==SQLITE_OK && db->pBeTemp ){
+     rc = sqliteBtreeBeginCkpt(pBt);
+  }
+  break;
+}
+
 /* Opcode: Transaction * * *
 **
 ** Begin a transaction.  The transaction ends when a Commit or Rollback
-** opcode is encountered or whenever there is an execution error that causes
-** a script to abort.  A transaction is not ended by a Halt.
+** opcode is encountered.  Depending on the ON CONFLICT setting, the
+** transaction might also be rolled back if an error is encountered.
 **
 ** A write lock is obtained on the database file when a transaction is
 ** started.  No other process can read or write the file while the
 ** transaction is underway.  Starting a transaction also creates a
-** rollback journal.
-** A transaction must be started before any changes can be made to the
-** database.
+** rollback journal.  A transaction must be started before any changes
+** can be made to the database.
 */
 case OP_Transaction: {
   int busy = 0;
@@ -2359,6 +2374,7 @@ case OP_Transaction: {
       }
     }
   }while( busy );
+  undoTransOnError = 1;
   break;
 }
 
@@ -4466,14 +4482,39 @@ default: {
 
 cleanup:
   Cleanup(p);
-  if( rc!=SQLITE_OK && rollbackOnError ){
-    closeAllCursors(p);
-    sqliteBtreeRollback(pBt);
-    if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
-    sqliteRollbackInternalChanges(db);
-    db->flags &= ~SQLITE_InTrans;
-    db->onError = OE_Default;
-  }
+  if( rc!=SQLITE_OK ){
+    switch( errorAction ){
+      case OE_Abort: {
+        if( !undoTransOnError ){
+          sqliteBtreeRollbackCkpt(pBt);
+          if( db->pBeTemp ) sqliteBtreeRollbackCkpt(db->pBeTemp);
+          break;
+        }
+        /* Fall through to ROLLBACK */
+      }
+      case OE_Rollback: {
+        sqliteBtreeRollback(pBt);
+        if( db->pBeTemp ) sqliteBtreeRollback(db->pBeTemp);
+        sqliteRollbackInternalChanges(db);
+        db->flags &= ~SQLITE_InTrans;
+        db->onError = OE_Default;
+        break;
+      }
+      default: {
+        if( undoTransOnError ){
+          sqliteBtreeCommit(pBt);
+          if( db->pBeTemp ) sqliteBtreeCommit(db->pBeTemp);
+          sqliteCommitInternalChanges(db);
+          db->flags &= ~SQLITE_InTrans;
+          db->onError = OE_Default;
+        }
+        break;
+      }
+    }
+  }else{
+    sqliteBtreeCommitCkpt(pBt);
+    if( db->pBeTemp ) sqliteBtreeCommitCkpt(db->pBeTemp);
+  }   
   return rc;
 
   /* Jump to here if a malloc() fails.  It's hard to get a malloc()
index 2fc688042a391eb661b522a460ab38a0a55e3017..6a63afd5ab048cdd75f27eb87b85a1ea6644b810 100644 (file)
@@ -15,7 +15,7 @@
 ** or VDBE.  The VDBE implements an abstract machine that runs a
 ** simple program to access and modify the underlying database.
 **
-** $Id: vdbe.h,v 1.39 2002/01/29 18:41:25 drh Exp $
+** $Id: vdbe.h,v 1.40 2002/02/02 18:49:21 drh Exp $
 */
 #ifndef _SQLITE_VDBE_H_
 #define _SQLITE_VDBE_H_
@@ -69,139 +69,140 @@ typedef struct VdbeOp VdbeOp;
 ** can be used to renumber these opcodes when new opcodes are inserted.
 */
 #define OP_Transaction         1
-#define OP_Commit              2
-#define OP_Rollback            3
-
-#define OP_ReadCookie          4
-#define OP_SetCookie           5
-#define OP_VerifyCookie        6
-
-#define OP_Open                7
-#define OP_OpenTemp            8
-#define OP_OpenWrite           9
-#define OP_OpenAux            10
-#define OP_OpenWrAux          11
-#define OP_Close              12
-#define OP_MoveTo             13
-#define OP_NewRecno           14
-#define OP_PutIntKey          15
-#define OP_PutStrKey          16
-#define OP_Distinct           17
-#define OP_Found              18
-#define OP_NotFound           19
-#define OP_IsUnique           20
-#define OP_NotExists          21
-#define OP_Delete             22
-#define OP_Column             23
-#define OP_KeyAsData          24
-#define OP_Recno              25
-#define OP_FullKey            26
-#define OP_Rewind             27
-#define OP_Next               28
-
-#define OP_Destroy            29
-#define OP_Clear              30
-#define OP_CreateIndex        31
-#define OP_CreateTable        32
-#define OP_Reorganize         33
-
-#define OP_IdxPut             34
-#define OP_IdxDelete          35
-#define OP_IdxRecno           36
-#define OP_IdxGT              37
-#define OP_IdxGE              38
-
-#define OP_MemLoad            39
-#define OP_MemStore           40
-
-#define OP_ListWrite          41
-#define OP_ListRewind         42
-#define OP_ListRead           43
-#define OP_ListReset          44
-
-#define OP_SortPut            45
-#define OP_SortMakeRec        46
-#define OP_SortMakeKey        47
-#define OP_Sort               48
-#define OP_SortNext           49
-#define OP_SortCallback       50
-#define OP_SortReset          51
-
-#define OP_FileOpen           52
-#define OP_FileRead           53
-#define OP_FileColumn         54
-
-#define OP_AggReset           55
-#define OP_AggFocus           56
-#define OP_AggIncr            57
-#define OP_AggNext            58
-#define OP_AggSet             59
-#define OP_AggGet             60
-
-#define OP_SetInsert          61
-#define OP_SetFound           62
-#define OP_SetNotFound        63
-
-#define OP_MakeRecord         64
-#define OP_MakeKey            65
-#define OP_MakeIdxKey         66
-#define OP_IncrKey            67
-
-#define OP_Goto               68
-#define OP_If                 69
-#define OP_Halt               70
-
-#define OP_ColumnCount        71
-#define OP_ColumnName         72
-#define OP_Callback           73
-#define OP_NullCallback       74
-
-#define OP_Integer            75
-#define OP_String             76
-#define OP_Pop                77
-#define OP_Dup                78
-#define OP_Pull               79
-#define OP_Push               80
-#define OP_MustBeInt          81
-
-#define OP_Add                82
-#define OP_AddImm             83
-#define OP_Subtract           84
-#define OP_Multiply           85
-#define OP_Divide             86
-#define OP_Remainder          87
-#define OP_BitAnd             88
-#define OP_BitOr              89
-#define OP_BitNot             90
-#define OP_ShiftLeft          91
-#define OP_ShiftRight         92
-#define OP_AbsValue           93
-#define OP_Precision          94
-#define OP_Min                95
-#define OP_Max                96
-#define OP_Like               97
-#define OP_Glob               98
-#define OP_Eq                 99
-#define OP_Ne                100
-#define OP_Lt                101
-#define OP_Le                102
-#define OP_Gt                103
-#define OP_Ge                104
-#define OP_IsNull            105
-#define OP_NotNull           106
-#define OP_Negative          107
-#define OP_And               108
-#define OP_Or                109
-#define OP_Not               110
-#define OP_Concat            111
-#define OP_Noop              112
-
-#define OP_Strlen            113
-#define OP_Substr            114
-
-#define OP_Limit             115
-
-#define OP_MAX               115
+#define OP_Checkpoint          2
+#define OP_Commit              3
+#define OP_Rollback            4
+
+#define OP_ReadCookie          5
+#define OP_SetCookie           6
+#define OP_VerifyCookie        7
+
+#define OP_Open                8
+#define OP_OpenTemp            9
+#define OP_OpenWrite          10
+#define OP_OpenAux            11
+#define OP_OpenWrAux          12
+#define OP_Close              13
+#define OP_MoveTo             14
+#define OP_NewRecno           15
+#define OP_PutIntKey          16
+#define OP_PutStrKey          17
+#define OP_Distinct           18
+#define OP_Found              19
+#define OP_NotFound           20
+#define OP_IsUnique           21
+#define OP_NotExists          22
+#define OP_Delete             23
+#define OP_Column             24
+#define OP_KeyAsData          25
+#define OP_Recno              26
+#define OP_FullKey            27
+#define OP_Rewind             28
+#define OP_Next               29
+
+#define OP_Destroy            30
+#define OP_Clear              31
+#define OP_CreateIndex        32
+#define OP_CreateTable        33
+#define OP_Reorganize         34
+
+#define OP_IdxPut             35
+#define OP_IdxDelete          36
+#define OP_IdxRecno           37
+#define OP_IdxGT              38
+#define OP_IdxGE              39
+
+#define OP_MemLoad            40
+#define OP_MemStore           41
+
+#define OP_ListWrite          42
+#define OP_ListRewind         43
+#define OP_ListRead           44
+#define OP_ListReset          45
+
+#define OP_SortPut            46
+#define OP_SortMakeRec        47
+#define OP_SortMakeKey        48
+#define OP_Sort               49
+#define OP_SortNext           50
+#define OP_SortCallback       51
+#define OP_SortReset          52
+
+#define OP_FileOpen           53
+#define OP_FileRead           54
+#define OP_FileColumn         55
+
+#define OP_AggReset           56
+#define OP_AggFocus           57
+#define OP_AggIncr            58
+#define OP_AggNext            59
+#define OP_AggSet             60
+#define OP_AggGet             61
+
+#define OP_SetInsert          62
+#define OP_SetFound           63
+#define OP_SetNotFound        64
+
+#define OP_MakeRecord         65
+#define OP_MakeKey            66
+#define OP_MakeIdxKey         67
+#define OP_IncrKey            68
+
+#define OP_Goto               69
+#define OP_If                 70
+#define OP_Halt               71
+
+#define OP_ColumnCount        72
+#define OP_ColumnName         73
+#define OP_Callback           74
+#define OP_NullCallback       75
+
+#define OP_Integer            76
+#define OP_String             77
+#define OP_Pop                78
+#define OP_Dup                79
+#define OP_Pull               80
+#define OP_Push               81
+#define OP_MustBeInt          82
+
+#define OP_Add                83
+#define OP_AddImm             84
+#define OP_Subtract           85
+#define OP_Multiply           86
+#define OP_Divide             87
+#define OP_Remainder          88
+#define OP_BitAnd             89
+#define OP_BitOr              90
+#define OP_BitNot             91
+#define OP_ShiftLeft          92
+#define OP_ShiftRight         93
+#define OP_AbsValue           94
+#define OP_Precision          95
+#define OP_Min                96
+#define OP_Max                97
+#define OP_Like               98
+#define OP_Glob               99
+#define OP_Eq                100
+#define OP_Ne                101
+#define OP_Lt                102
+#define OP_Le                103
+#define OP_Gt                104
+#define OP_Ge                105
+#define OP_IsNull            106
+#define OP_NotNull           107
+#define OP_Negative          108
+#define OP_And               109
+#define OP_Or                110
+#define OP_Not               111
+#define OP_Concat            112
+#define OP_Noop              113
+
+#define OP_Strlen            114
+#define OP_Substr            115
+
+#define OP_Limit             116
+
+#define OP_MAX               116
 
 /*
 ** Prototypes for the VDBE interface.  See comments on the implementation
index a39806f3a69db5387f8f558921883f39af59586f..727b2d005dd50a1c1a6cf9a3326c4b5c79d4fc7c 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this script is page cache subsystem.
 #
-# $Id: pager.test,v 1.10 2001/09/16 00:13:28 drh Exp $
+# $Id: pager.test,v 1.11 2002/02/02 18:49:21 drh Exp $
 
 
 set testdir [file dirname $argv0]
@@ -243,6 +243,163 @@ do_test pager-3.99 {
   pager_close $::p1
 } {}
 
+# tests of the checkpoint mechanism and api
+#
+do_test pager-4.0 {
+  set v [catch {
+    file delete -force ptf1.db
+    set ::p1 [pager_open ptf1.db 15]
+  } msg]
+  if {$v} {lappend v $msg}
+  set v
+} {0}
+do_test pager-4.1 {
+  set g1 [page_get $::p1 1]
+  page_write $g1 "Page-1 v0"
+  for {set i 2} {$i<=20} {incr i} {
+    set gx [page_get $::p1 $i]
+    page_write $gx "Page-$i v0"
+    page_unref $gx
+  }
+  pager_commit $::p1
+} {}
+for {set i 1} {$i<=20} {incr i} {
+  do_test pager-4.2.$i {
+    set gx [page_get $p1 $i]
+    set v [page_read $gx]
+    page_unref $gx
+    set v
+  } "Page-$i v0"
+}
+do_test pager-4.3 {
+  lrange [pager_stats $::p1] 0 1
+} {ref 1}
+do_test pager-4.4 {
+  lrange [pager_stats $::p1] 8 9
+} {state 1}
+
+for {set i 1} {$i<20} {incr i} {
+  do_test pager-4.5.$i.0 {
+    set res {}
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      set value [page_read $gx]
+      page_unref $gx
+      set shouldbe "Page-$j v[expr {$i-1}]"
+      if {$value!=$shouldbe} {
+        lappend res $value $shouldbe
+      }
+    }
+    set res
+  } {}
+  do_test pager-4.5.$i.1 {
+    page_write $g1 "Page-1 v$i"
+    lrange [pager_stats $p1] 8 9
+  } {state 2}
+  do_test pager-4.5.$i.2 {
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      page_write $gx "Page-$j v$i"
+      page_unref $gx
+      if {$j==$i} {
+        pager_ckpt_begin $p1
+      }
+    }
+  } {}
+  do_test pager-4.5.$i.3 {
+    set res {}
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      set value [page_read $gx]
+      page_unref $gx
+      set shouldbe "Page-$j v$i"
+      if {$value!=$shouldbe} {
+        lappend res $value $shouldbe
+      }
+    }
+    set res
+  } {}
+  do_test pager-4.5.$i.4 {
+    pager_rollback $p1
+    set res {}
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      set value [page_read $gx]
+      page_unref $gx
+      set shouldbe "Page-$j v[expr {$i-1}]"
+      if {$value!=$shouldbe} {
+        lappend res $value $shouldbe
+      }
+    }
+    set res
+  } {}
+  do_test pager-4.5.$i.5 {
+    page_write $g1 "Page-1 v$i"
+    lrange [pager_stats $p1] 8 9
+  } {state 2}
+  do_test pager-4.5.$i.6 {
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      page_write $gx "Page-$j v$i"
+      page_unref $gx
+      if {$j==$i} {
+        pager_ckpt_begin $p1
+      }
+    }
+  } {}
+  do_test pager-4.5.$i.7 {
+    pager_ckpt_rollback $p1
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      set value [page_read $gx]
+      page_unref $gx
+      if {$j<=$i || $i==1} {
+        set shouldbe "Page-$j v$i"
+      } else {
+        set shouldbe "Page-$j v[expr {$i-1}]"
+      }
+      if {$value!=$shouldbe} {
+        lappend res $value $shouldbe
+      }
+    }
+    set res
+  } {}
+  do_test pager-4.5.$i.8 {
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      page_write $gx "Page-$j v$i"
+      page_unref $gx
+      if {$j==$i} {
+        pager_ckpt_begin $p1
+      }
+    }
+  } {}
+  do_test pager-4.5.$i.9 {
+    pager_ckpt_commit $p1
+    for {set j 2} {$j<=20} {incr j} {
+      set gx [page_get $p1 $j]
+      set value [page_read $gx]
+      page_unref $gx
+      set shouldbe "Page-$j v$i"
+      if {$value!=$shouldbe} {
+        lappend res $value $shouldbe
+      }
+    }
+    set res
+  } {}
+  do_test pager-4.5.$i.10 {
+    pager_commit $p1
+    lrange [pager_stats $p1] 8 9
+  } {state 1}
+}
+
+do_test pager-4.99 {
+  pager_close $::p1
+} {}
+
+
+
+  file delete -force ptf1.db
 
 } ;# end if( not mem: and has pager_open command );