/ Check-in [3674b25e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Turn on the atomic multifile commit logic. It does not work right yet, but it has at least stopped failing asserts. (CVS 1550)
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 3674b25edc37145b2b1275dd20580015ace66aa7
User & Date: drh 2004-06-09 20:03:09
Context
2004-06-09
21:01
If a commit fails due to lock contention right after the COMMIT command, take the database back out of autocommit mode. Do not rollback. This gives the user the chance to try the COMMIT again. (CVS 1551) check-in: 39b4ba95 user: drh tags: trunk
20:03
Turn on the atomic multifile commit logic. It does not work right yet, but it has at least stopped failing asserts. (CVS 1550) check-in: 3674b25e user: drh tags: trunk
19:03
Remove legacy journal formats. (CVS 1549) check-in: a12bef4a user: drh tags: trunk
Changes
Hide Diffs Unified Diffs Show Whitespace Changes Patch

Changes to src/btree.c.

5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.162 2004/06/09 17:37:23 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  }

  if( pBt->pPage1==0 ){
    rc = lockBtree(pBt);
  }

  if( rc==SQLITE_OK && wrflag ){
    rc = sqlite3pager_begin(pBt->pPage1->aData, 0);
    if( rc==SQLITE_OK ){
      rc = newDatabase(pBt);
    }
  }

  if( rc==SQLITE_OK ){
    pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);







|







 







|







5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
....
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** $Id: btree.c,v 1.163 2004/06/09 20:03:09 drh Exp $
**
** This file implements a external (disk-based) database using BTrees.
** For a detailed discussion of BTrees, refer to
**
**     Donald E. Knuth, THE ART OF COMPUTER PROGRAMMING, Volume 3:
**     "Sorting And Searching", pages 473-480. Addison-Wesley
**     Publishing Company, Reading, Massachusetts.
................................................................................
  }

  if( pBt->pPage1==0 ){
    rc = lockBtree(pBt);
  }

  if( rc==SQLITE_OK && wrflag ){
    rc = sqlite3pager_begin(pBt->pPage1->aData, nMaster);
    if( rc==SQLITE_OK ){
      rc = newDatabase(pBt);
    }
  }

  if( rc==SQLITE_OK ){
    pBt->inTrans = (wrflag?TRANS_WRITE:TRANS_READ);

Changes to src/main.c.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1029
1030
1031
1032
1033
1034
1035



1036
1037
1038
1039
1040
1041
1042
....
1157
1158
1159
1160
1161
1162
1163
1164
**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.209 2004/06/09 12:30:06 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................

  /* Also add a UTF-8 case-insensitive collation sequence. */
  sqlite3_create_collation(db, "NOCASE", 0, 0, nocaseCollatingFunc);

  /* Open the backend database driver */
  if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
    db->temp_store = 2;



  }
  rc = sqlite3BtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt);
  if( rc!=SQLITE_OK ){
    /* FIX ME: sqlite3BtreeFactory() should call sqlite3Error(). */
    sqlite3Error(db, rc, 0);
    db->magic = SQLITE_MAGIC_CLOSED;
    goto opendb_out;
................................................................................
){
  int rc;
  char *zName8 = sqlite3utf16to8(zName, -1, SQLITE_BIGENDIAN);
  rc = sqlite3_create_collation(db, zName8, pref16, pCtx, xCompare);
  sqliteFree(zName8);
  return rc;
}








|







 







>
>
>







 







<
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
....
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
....
1160
1161
1162
1163
1164
1165
1166

**
*************************************************************************
** Main file for the SQLite library.  The routines in this file
** implement the programmer interface to the library.  Routines in
** other files are for internal use by SQLite and should not be
** accessed by users of the library.
**
** $Id: main.c,v 1.210 2004/06/09 20:03:09 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>

/*
** A pointer to this structure is used to communicate information
................................................................................

  /* Also add a UTF-8 case-insensitive collation sequence. */
  sqlite3_create_collation(db, "NOCASE", 0, 0, nocaseCollatingFunc);

  /* Open the backend database driver */
  if( zFilename[0]==':' && strcmp(zFilename,":memory:")==0 ){
    db->temp_store = 2;
    db->nMaster = 0;    /* Disable atomic multi-file commit for :memory: */
  }else{
    db->nMaster = -1;   /* Size of master journal filename initially unknown */
  }
  rc = sqlite3BtreeFactory(db, zFilename, 0, MAX_PAGES, &db->aDb[0].pBt);
  if( rc!=SQLITE_OK ){
    /* FIX ME: sqlite3BtreeFactory() should call sqlite3Error(). */
    sqlite3Error(db, rc, 0);
    db->magic = SQLITE_MAGIC_CLOSED;
    goto opendb_out;
................................................................................
){
  int rc;
  char *zName8 = sqlite3utf16to8(zName, -1, SQLITE_BIGENDIAN);
  rc = sqlite3_create_collation(db, zName8, pref16, pCtx, xCompare);
  sqliteFree(zName8);
  return rc;
}

Changes to src/pager.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1930
1931
1932
1933
1934
1935
1936
1937

1938
1939
1940
1941
1942
1943
1944
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.116 2004/06/09 19:03:55 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  if( rc==SQLITE_OK ){
    rc = write32bits(&pPager->jfd, pPager->dbSize);
  }
  if( rc==SQLITE_OK ){
    rc = write32bits(&pPager->jfd, pPager->nMaster);
  }
  if( rc==SQLITE_OK ){
    rc = sqlite3OsSeek(&pPager->jfd, 24 + pPager->nMaster);

  }
  if( pPager->stmtAutoopen && rc==SQLITE_OK ){
    rc = sqlite3pager_stmt_begin(pPager);
  }
  if( rc!=SQLITE_OK ){
    rc = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){







|







 







|
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
....
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
** The pager is used to access a database disk file.  It implements
** atomic commit and rollback through the use of a journal file that
** is separate from the database file.  The pager also implements file
** locking to prevent two processes from writing the same database
** file simultaneously, or one process from reading the database while
** another is writing.
**
** @(#) $Id: pager.c,v 1.117 2004/06/09 20:03:09 drh Exp $
*/
#include "os.h"         /* Must be first to enable large file support */
#include "sqliteInt.h"
#include "pager.h"
#include <assert.h>
#include <string.h>

................................................................................
  if( rc==SQLITE_OK ){
    rc = write32bits(&pPager->jfd, pPager->dbSize);
  }
  if( rc==SQLITE_OK ){
    rc = write32bits(&pPager->jfd, pPager->nMaster);
  }
  if( rc==SQLITE_OK ){
    sqlite3OsSeek(&pPager->jfd, 24 + pPager->nMaster - 1);
    rc = sqlite3OsWrite(&pPager->jfd, "\000", 1);
  }
  if( pPager->stmtAutoopen && rc==SQLITE_OK ){
    rc = sqlite3pager_stmt_begin(pPager);
  }
  if( rc!=SQLITE_OK ){
    rc = pager_unwritelock(pPager);
    if( rc==SQLITE_OK ){

Changes to src/sqliteInt.h.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
418
419
420
421
422
423
424

425
426
427
428
429
430
431
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.275 2004/06/09 14:01:51 drh Exp $
*/
#include "config.h"
#include "sqlite3.h"
#include "hash.h"
#include "parse.h"
#include <stdio.h>
#include <stdlib.h>
................................................................................
#endif

  int errCode;                  /* Most recent error code (SQLITE_*) */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */
  void *zErrMsg16;              /* Most recent error message (UTF-16 encoded) */
  u8 enc;                       /* Text encoding for this database. */
  u8 autoCommit;                /* The auto-commit flag. */

};

/*
** Possible values for the sqlite.flags and or Db.flags fields.
**
** On sqlite.flags, the SQLITE_InTrans value means that we have
** executed a BEGIN.  On Db.flags, SQLITE_InTrans means a statement







|







 







>







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
** Internal interface definitions for SQLite.
**
** @(#) $Id: sqliteInt.h,v 1.276 2004/06/09 20:03:09 drh Exp $
*/
#include "config.h"
#include "sqlite3.h"
#include "hash.h"
#include "parse.h"
#include <stdio.h>
#include <stdlib.h>
................................................................................
#endif

  int errCode;                  /* Most recent error code (SQLITE_*) */
  char *zErrMsg;                /* Most recent error message (UTF-8 encoded) */
  void *zErrMsg16;              /* Most recent error message (UTF-16 encoded) */
  u8 enc;                       /* Text encoding for this database. */
  u8 autoCommit;                /* The auto-commit flag. */
  int nMaster;                  /* Length of master journal name. -1=unknown */
};

/*
** Possible values for the sqlite.flags and or Db.flags fields.
**
** On sqlite.flags, the SQLITE_InTrans value means that we have
** executed a BEGIN.  On Db.flags, SQLITE_InTrans means a statement

Changes to src/vdbe.c.

39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
....
2315
2316
2317
2318
2319
2320
2321

2322

2323
2324
2325
2326
2327
2328
2329
2330
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** 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.361 2004/06/09 09:55:19 danielk1977 Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
................................................................................
  int i = pOp->p1;
  Btree *pBt;

  assert( i>=0 && i<db->nDb );
  pBt = db->aDb[i].pBt;

  if( pBt ){

    int nMaster = strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt))+11;

    rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, nMaster);
    if( rc==SQLITE_BUSY ){
        if( db->busyHandler.xFunc==0 ){
          p->pc = pc;
          p->rc = SQLITE_BUSY;
          p->pTos = pTos;
          return SQLITE_BUSY;
        }else{







|







 







>
|
>
|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
....
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
**
** Various scripts scan this source file in order to generate HTML
** documentation, headers files, or other derived files.  The formatting
** of the code in this file is, therefore, important.  See other comments
** 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.362 2004/06/09 20:03:10 drh Exp $
*/
#include "sqliteInt.h"
#include "os.h"
#include <ctype.h>
#include "vdbeInt.h"

/*
................................................................................
  int i = pOp->p1;
  Btree *pBt;

  assert( i>=0 && i<db->nDb );
  pBt = db->aDb[i].pBt;

  if( pBt ){
    if( db->nMaster<0 ){
      db->nMaster = strlen(sqlite3BtreeGetFilename(db->aDb[0].pBt))+20;
    }
    rc = sqlite3BtreeBeginTrans(pBt, pOp->p2, db->nMaster);
    if( rc==SQLITE_BUSY ){
        if( db->busyHandler.xFunc==0 ){
          p->pc = pc;
          p->rc = SQLITE_BUSY;
          p->pTos = pTos;
          return SQLITE_BUSY;
        }else{

Changes to src/vdbeaux.c.

929
930
931
932
933
934
935




936
937

938
939
940
941
942
943
944
...
970
971
972
973
974
975
976

977
978
979
980
981
982
983
...
984
985
986
987
988
989
990

991
992

993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012



1013
1014
1015
1016
1017
1018
1019
      return SQLITE_CONSTRAINT;
    }
  }

  /* The simple case - no more than one database file (not counting the TEMP
  ** database) has a transaction active.   There is no need for the
  ** master-journal.




  */
  if( nTrans<=100 ){  /**** FIX ME ****/

    for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = sqlite3BtreeSync(pBt, 0);
      }
    }

................................................................................
      zMaster = sqlite3MPrintf("%s-mj%08X", 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







>
>
>
>

<
>







 







>







 







>


>
|
<
<
<
<
<
<
<












>
>
>







929
930
931
932
933
934
935
936
937
938
939
940

941
942
943
944
945
946
947
948
...
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
...
989
990
991
992
993
994
995
996
997
998
999
1000







1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
      return SQLITE_CONSTRAINT;
    }
  }

  /* The simple case - no more than one database file (not counting the TEMP
  ** database) has a transaction active.   There is no need for the
  ** master-journal.
  **
  ** if db->nMaster==0, it means the main database is :memory:.  In that case
  ** we do not support atomic multi-file commits, so use the simple case then
  ** too.
  */

  if( db->nMaster<=0 || nTrans<=1 ){
    for(i=0; rc==SQLITE_OK && i<db->nDb; i++){ 
      Btree *pBt = db->aDb[i].pBt;
      if( pBt ){
        rc = sqlite3BtreeSync(pBt, 0);
      }
    }

................................................................................
      zMaster = sqlite3MPrintf("%s-mj%08X", zMainFile, random);
      if( !zMaster ){
        return SQLITE_NOMEM;
      }
    }while( sqlite3OsFileExists(zMaster) );

    /* Open the master journal. */
    assert( strlen(zMaster)<db->nMaster );
    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( i==1 ) continue;   /* Ignore the TEMP database */
      if( pBt && sqlite3BtreeIsInTrans(pBt) ){
        char const *zFile = sqlite3BtreeGetFilename(pBt);
        if( zFile[0]==0 ) continue;  /* Ignore :memory: databases */
        rc = sqlite3OsWrite(&master, zFile, strlen(zFile)+1);







        if( rc!=SQLITE_OK ){
          sqlite3OsClose(&master);
          sqlite3OsDelete(zMaster);
          sqliteFree(zMaster);
          return rc;
        }
      }
    }

    /* Sync the master journal file */
    rc = sqlite3OsSync(&master);
    sqlite3OsClose(&master);

    /* FIXME:  Sync the directory that contains the master journal to
    ** make sure the i-node is up to date. */

    /* 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

Changes to test/pager2.test.

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is page cache subsystem.
#
# $Id: pager2.test,v 1.1 2004/05/12 13:30:09 drh Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl

if {[info commands pager_open]!=""} {
db close
................................................................................
  set v [catch {
    page_write $::g1 "Page-One"
  } msg]
  lappend v $msg
} {0 {}}
do_test pager2-2.15 {
  pager_stats $::p1
} {ref 1 page 1 max 10 size 1 state 2 err 0 hit 1 miss 1 ovfl 0}
do_test pager2-2.16 {
  page_read $::g1
} {Page-One}
do_test pager2-2.17 {
  set v [catch {
    pager_commit $::p1
  } msg]
................................................................................
      }
    }
    set res
  } {}
  do_test pager2-4.5.$i.1 {
    page_write $g1 "Page-1 v$i"
    lrange [pager_stats $p1] 8 9
  } {state 2}
  do_test pager2-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_stmt_begin $p1
................................................................................
      }
    }
    set res
  } {}
  do_test pager2-4.5.$i.5 {
    page_write $g1 "Page-1 v$i"
    lrange [pager_stats $p1] 8 9
  } {state 2}
  do_test pager2-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_stmt_begin $p1







|







 







|







 







|







 







|







7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
...
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
...
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
...
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
#    May you find forgiveness for yourself and forgive others.
#    May you share freely, never taking more than you give.
#
#***********************************************************************
# This file implements regression tests for SQLite library.  The
# focus of this script is page cache subsystem.
#
# $Id: pager2.test,v 1.2 2004/06/09 20:03:10 drh Exp $


set testdir [file dirname $argv0]
source $testdir/tester.tcl

if {[info commands pager_open]!=""} {
db close
................................................................................
  set v [catch {
    page_write $::g1 "Page-One"
  } msg]
  lappend v $msg
} {0 {}}
do_test pager2-2.15 {
  pager_stats $::p1
} {ref 1 page 1 max 10 size 1 state 3 err 0 hit 1 miss 1 ovfl 0}
do_test pager2-2.16 {
  page_read $::g1
} {Page-One}
do_test pager2-2.17 {
  set v [catch {
    pager_commit $::p1
  } msg]
................................................................................
      }
    }
    set res
  } {}
  do_test pager2-4.5.$i.1 {
    page_write $g1 "Page-1 v$i"
    lrange [pager_stats $p1] 8 9
  } {state 3}
  do_test pager2-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_stmt_begin $p1
................................................................................
      }
    }
    set res
  } {}
  do_test pager2-4.5.$i.5 {
    page_write $g1 "Page-1 v$i"
    lrange [pager_stats $p1] 8 9
  } {state 3}
  do_test pager2-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_stmt_begin $p1