/ Check-in [051ac015]
Login

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

Overview
Comment:Allow sqlite3_snapshot_open() to be called to change the snapshot after a read transaction is already open on database.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | exp-snapshot-open
Files: files | file ages | folders
SHA3-256: 051ac0152048ef52723196c26ca5f2629dafb782aec1c66fc30531bf54335043
User & Date: dan 2018-08-06 17:12:36
Context
2018-08-15
14:03
Allow sqlite3_snapshot_open() to be called to change the snapshot after a read transaction is already open on database. check-in: 41399169 user: dan tags: trunk
2018-08-06
17:12
Allow sqlite3_snapshot_open() to be called to change the snapshot after a read transaction is already open on database. Closed-Leaf check-in: 051ac015 user: dan tags: exp-snapshot-open
02:08
Enhance the edit() function so that it converts text from \r\n back into \n only if the original unedited copy contained no \r\n values. check-in: 20c995d3 user: drh tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to src/main.c.

  4205   4205   #endif
  4206   4206     sqlite3_mutex_enter(db->mutex);
  4207   4207     if( db->autoCommit==0 ){
  4208   4208       int iDb;
  4209   4209       iDb = sqlite3FindDbName(db, zDb);
  4210   4210       if( iDb==0 || iDb>1 ){
  4211   4211         Btree *pBt = db->aDb[iDb].pBt;
  4212         -      if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
  4213         -        rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot);
         4212  +      if( sqlite3BtreeIsInTrans(pBt)==0 ){
         4213  +        Pager *pPager = sqlite3BtreePager(pBt);
         4214  +        int bUnlock = 0;
         4215  +        if( sqlite3BtreeIsInReadTrans(pBt) ){
         4216  +          if( db->nVdbeActive==0 ){
         4217  +            rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot);
         4218  +            if( rc==SQLITE_OK ){
         4219  +              bUnlock = 1;
         4220  +              rc = sqlite3BtreeCommit(pBt);
         4221  +            }
         4222  +          }
         4223  +        }else{
         4224  +          rc = SQLITE_OK;
         4225  +        }
         4226  +        if( rc==SQLITE_OK ){
         4227  +          rc = sqlite3PagerSnapshotOpen(pPager, pSnapshot);
         4228  +        }
  4214   4229           if( rc==SQLITE_OK ){
  4215   4230             rc = sqlite3BtreeBeginTrans(pBt, 0, 0);
  4216         -          sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
         4231  +          sqlite3PagerSnapshotOpen(pPager, 0);
         4232  +        }
         4233  +        if( bUnlock ){
         4234  +          sqlite3PagerSnapshotUnlock(pPager);
  4217   4235           }
  4218   4236         }
  4219   4237       }
  4220   4238     }
  4221   4239   
  4222   4240     sqlite3_mutex_leave(db->mutex);
  4223   4241   #endif   /* SQLITE_OMIT_WAL */

Changes to src/pager.c.

  7649   7649     if( pPager->pWal ){
  7650   7650       rc = sqlite3WalSnapshotRecover(pPager->pWal);
  7651   7651     }else{
  7652   7652       rc = SQLITE_ERROR;
  7653   7653     }
  7654   7654     return rc;
  7655   7655   }
         7656  +
         7657  +/*
         7658  +** The caller currently has a read transaction open on the database.
         7659  +** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise,
         7660  +** this function takes a SHARED lock on the CHECKPOINTER slot and then
         7661  +** checks if the snapshot passed as the second argument is still 
         7662  +** available. If so, SQLITE_OK is returned.
         7663  +**
         7664  +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
         7665  +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error
         7666  +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER
         7667  +** lock is released before returning.
         7668  +*/
         7669  +int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot){
         7670  +  int rc;
         7671  +  if( pPager->pWal ){
         7672  +    rc = sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot);
         7673  +  }else{
         7674  +    rc = SQLITE_ERROR;
         7675  +  }
         7676  +  return rc;
         7677  +}
         7678  +
         7679  +/*
         7680  +** Release a lock obtained by an earlier successful call to
         7681  +** sqlite3PagerSnapshotCheck().
         7682  +*/
         7683  +void sqlite3PagerSnapshotUnlock(Pager *pPager){
         7684  +  assert( pPager->pWal );
         7685  +  return sqlite3WalSnapshotUnlock(pPager->pWal);
         7686  +}
         7687  +
  7656   7688   #endif /* SQLITE_ENABLE_SNAPSHOT */
  7657   7689   #endif /* !SQLITE_OMIT_WAL */
  7658   7690   
  7659   7691   #ifdef SQLITE_ENABLE_ZIPVFS
  7660   7692   /*
  7661   7693   ** A read-lock must be held on the pager when this function is called. If
  7662   7694   ** the pager is in WAL mode and the WAL file currently contains one or more

Changes to src/pager.h.

   182    182   # ifdef SQLITE_DIRECT_OVERFLOW_READ
   183    183     int sqlite3PagerUseWal(Pager *pPager, Pgno);
   184    184   # endif
   185    185   # ifdef SQLITE_ENABLE_SNAPSHOT
   186    186     int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
   187    187     int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
   188    188     int sqlite3PagerSnapshotRecover(Pager *pPager);
          189  +  int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
          190  +  void sqlite3PagerSnapshotUnlock(Pager *pPager);
   189    191   # endif
   190    192   #else
   191    193   # define sqlite3PagerUseWal(x,y) 0
   192    194   #endif
   193    195   
   194    196   #ifdef SQLITE_ENABLE_ZIPVFS
   195    197     int sqlite3PagerWalFramesize(Pager *pPager);

Changes to src/sqlite.h.in.

  9031   9031     sqlite3_snapshot **ppSnapshot
  9032   9032   );
  9033   9033   
  9034   9034   /*
  9035   9035   ** CAPI3REF: Start a read transaction on an historical snapshot
  9036   9036   ** METHOD: sqlite3_snapshot
  9037   9037   **
  9038         -** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a
  9039         -** read transaction for schema S of
  9040         -** [database connection] D such that the read transaction
  9041         -** refers to historical [snapshot] P, rather than the most
  9042         -** recent change to the database.
  9043         -** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success
  9044         -** or an appropriate [error code] if it fails.
  9045         -**
  9046         -** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be
  9047         -** the first operation following the [BEGIN] that takes the schema S
  9048         -** out of [autocommit mode].
  9049         -** ^In other words, schema S must not currently be in
  9050         -** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the
  9051         -** database connection D must be out of [autocommit mode].
  9052         -** ^A [snapshot] will fail to open if it has been overwritten by a
  9053         -** [checkpoint].
         9038  +** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read 
         9039  +** transaction or upgrades an existing one for schema S of 
         9040  +** [database connection] D such that the read transaction refers to 
         9041  +** historical [snapshot] P, rather than the most recent change to the 
         9042  +** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK 
         9043  +** on success or an appropriate [error code] if it fails.
         9044  +**
         9045  +** ^In order to succeed, the database connection must not be in 
         9046  +** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there
         9047  +** is already a read transaction open on schema S, then the database handle
         9048  +** must have no active statements (SELECT statements that have been passed
         9049  +** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). 
         9050  +** SQLITE_ERROR is returned if either of these conditions is violated, or
         9051  +** if schema S does not exist, or if the snapshot object is invalid.
         9052  +**
         9053  +** ^A call to sqlite3_snapshot_open() will fail to open if the specified
         9054  +** snapshot has been overwritten by a [checkpoint]. In this case 
         9055  +** SQLITE_BUSY_SNAPSHOT is returned.
         9056  +**
         9057  +** If there is already a read transaction open when this function is 
         9058  +** invoked, then the same read transaction remains open (on the same
         9059  +** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT
         9060  +** is returned. If another error code - for example SQLITE_PROTOCOL or an
         9061  +** SQLITE_IOERR error code - is returned, then the final state of the
         9062  +** read transaction is undefined. If SQLITE_OK is returned, then the 
         9063  +** read transaction is now open on database snapshot P.
         9064  +**
  9054   9065   ** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the
  9055   9066   ** database connection D does not know that the database file for
  9056   9067   ** schema S is in [WAL mode].  A database connection might not know
  9057   9068   ** that the database file is in [WAL mode] if there has been no prior
  9058   9069   ** I/O on that database connection, or if the database entered [WAL mode] 
  9059   9070   ** after the most recent I/O on the database connection.)^
  9060   9071   ** (Hint: Run "[PRAGMA application_id]" against a newly opened

Changes to src/test1.c.

  2389   2389     zName = Tcl_GetString(objv[2]);
  2390   2390     pSnapshot = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[3]));
  2391   2391   
  2392   2392     rc = sqlite3_snapshot_open(db, zName, pSnapshot);
  2393   2393     if( rc!=SQLITE_OK ){
  2394   2394       Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
  2395   2395       return TCL_ERROR;
         2396  +  }else{
         2397  +    Tcl_ResetResult(interp);
  2396   2398     }
  2397   2399     return TCL_OK;
  2398   2400   }
  2399   2401   #endif /* SQLITE_ENABLE_SNAPSHOT */
  2400   2402   
  2401   2403   #ifdef SQLITE_ENABLE_SNAPSHOT
  2402   2404   /*

Changes to src/wal.c.

  3765   3765     ** is incremented each time the wal file is restarted.  */
  3766   3766     if( pHdr1->aSalt[0]<pHdr2->aSalt[0] ) return -1;
  3767   3767     if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1;
  3768   3768     if( pHdr1->mxFrame<pHdr2->mxFrame ) return -1;
  3769   3769     if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1;
  3770   3770     return 0;
  3771   3771   }
         3772  +
         3773  +/*
         3774  +** The caller currently has a read transaction open on the database.
         3775  +** This function takes a SHARED lock on the CHECKPOINTER slot and then
         3776  +** checks if the snapshot passed as the second argument is still 
         3777  +** available. If so, SQLITE_OK is returned.
         3778  +**
         3779  +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if
         3780  +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error
         3781  +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER
         3782  +** lock is released before returning.
         3783  +*/
         3784  +int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){
         3785  +  int rc;
         3786  +  rc = walLockShared(pWal, WAL_CKPT_LOCK);
         3787  +  if( rc==SQLITE_OK ){
         3788  +    WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot;
         3789  +    if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
         3790  +     || pNew->mxFrame<walCkptInfo(pWal)->nBackfillAttempted
         3791  +    ){
         3792  +      rc = SQLITE_BUSY_SNAPSHOT;
         3793  +      walUnlockShared(pWal, WAL_CKPT_LOCK);
         3794  +    }
         3795  +  }
         3796  +  return rc;
         3797  +}
         3798  +
         3799  +/*
         3800  +** Release a lock obtained by an earlier successful call to
         3801  +** sqlite3WalSnapshotCheck().
         3802  +*/
         3803  +void sqlite3WalSnapshotUnlock(Wal *pWal){
         3804  +  assert( pWal );
         3805  +  walUnlockShared(pWal, WAL_CKPT_LOCK);
         3806  +}
         3807  +
         3808  +
  3772   3809   #endif /* SQLITE_ENABLE_SNAPSHOT */
  3773   3810   
  3774   3811   #ifdef SQLITE_ENABLE_ZIPVFS
  3775   3812   /*
  3776   3813   ** If the argument is not NULL, it points to a Wal object that holds a
  3777   3814   ** read-lock. This function returns the database page-size if it is known,
  3778   3815   ** or zero if it is not (or if pWal is NULL).

Changes to src/wal.h.

   128    128   */
   129    129   int sqlite3WalHeapMemory(Wal *pWal);
   130    130   
   131    131   #ifdef SQLITE_ENABLE_SNAPSHOT
   132    132   int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
   133    133   void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
   134    134   int sqlite3WalSnapshotRecover(Wal *pWal);
          135  +int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot);
          136  +void sqlite3WalSnapshotUnlock(Wal *pWal);
   135    137   #endif
   136    138   
   137    139   #ifdef SQLITE_ENABLE_ZIPVFS
   138    140   /* If the WAL file is not empty, return the number of bytes of content
   139    141   ** stored in each frame (i.e. the db page-size when the WAL was created).
   140    142   */
   141    143   int sqlite3WalFramesize(Wal *pWal);

Changes to test/snapshot.test.

   213    213   
   214    214     do_test $tn.3.2.1 {
   215    215       execsql {
   216    216         BEGIN;
   217    217           SELECT * FROM t2;
   218    218       }
   219    219     } {a b c d e f}
   220         -  do_test $tn.3.2.2 {
   221         -    list [catch {snapshot_open db main $snapshot } msg] $msg
          220  +
          221  +  # Update - it is no longer an error to have a read-transaction open, 
          222  +  # provided there are no active SELECT statements.
          223  +  do_test $tn.3.2.2a {
          224  +    db eval "SELECT * FROM t2" {
          225  +      set res [list [catch {snapshot_open db main $snapshot } msg] $msg]
          226  +      break
          227  +    }
          228  +    set res
   222    229     } {1 SQLITE_ERROR}
          230  +  do_test $tn.3.2.2b {
          231  +    snapshot_open db main $snapshot
          232  +  } {}
   223    233   
   224    234     do_test $tn.3.2.3 {
   225    235       execsql {
   226    236         COMMIT;
   227    237         BEGIN;
   228    238           INSERT INTO t2 VALUES('g', 'h');
   229    239       }
   230    240       list [catch {snapshot_open db main $snapshot } msg] $msg
   231    241     } {1 SQLITE_ERROR}
   232    242     do_execsql_test $tn.3.2.4 COMMIT
   233    243   
   234         -  do_test $tn.3.3.1 {
          244  +  do_test $tn.3.3.1a {
   235    245       execsql { PRAGMA journal_mode = DELETE }
   236    246       execsql { BEGIN }
   237    247       list [catch {snapshot_open db main $snapshot } msg] $msg
   238    248     } {1 SQLITE_ERROR}
          249  +
          250  +  do_test $tn.3.3.1b {
          251  +    execsql { COMMIT ; BEGIN ; SELECT * FROM t2 }
          252  +    list [catch {snapshot_open db main $snapshot } msg] $msg
          253  +  } {1 SQLITE_ERROR}
   239    254   
   240    255     do_test $tn.$tn.3.3.2 {
   241    256       snapshot_free $snapshot
   242    257       execsql COMMIT
   243    258     } {}
   244    259   
   245    260     #-------------------------------------------------------------------------

Added test/snapshot_up.test.

            1  +# 2018 August 6
            2  +#
            3  +# The author disclaims copyright to this source code.  In place of
            4  +# a legal notice, here is a blessing:
            5  +#
            6  +#    May you do good and not evil.
            7  +#    May you find forgiveness for yourself and forgive others.
            8  +#    May you share freely, never taking more than you give.
            9  +#
           10  +#***********************************************************************
           11  +#
           12  +# Tests for calling sqlite3_snapshot_open() when there is already
           13  +# a read transaction open on the database.
           14  +#
           15  +
           16  +set testdir [file dirname $argv0]
           17  +source $testdir/tester.tcl
           18  +ifcapable !snapshot {finish_test; return}
           19  +set testprefix snapshot_up
           20  +
           21  +# This test does not work with the inmemory_journal permutation. The reason
           22  +# is that each connection opened as part of this permutation executes
           23  +# "PRAGMA journal_mode=memory", which fails if the database is in wal mode
           24  +# and there are one or more existing connections.
           25  +if {[permutation]=="inmemory_journal"} {
           26  +  finish_test
           27  +  return
           28  +}
           29  +
           30  +do_execsql_test 1.0 {
           31  +  CREATE TABLE t1(a, b, c);
           32  +  PRAGMA journal_mode = wal;
           33  +  INSERT INTO t1 VALUES(1, 2, 3);
           34  +  INSERT INTO t1 VALUES(4, 5, 6);
           35  +  INSERT INTO t1 VALUES(7, 8, 9);
           36  +} {wal}
           37  +
           38  +do_test 1.1 {
           39  +  execsql BEGIN
           40  +  set ::snap1 [sqlite3_snapshot_get db main]
           41  +  execsql COMMIT
           42  +  execsql { INSERT INTO t1 VALUES(10, 11, 12); }
           43  +  execsql BEGIN
           44  +  set ::snap2 [sqlite3_snapshot_get db main]
           45  +  execsql COMMIT
           46  +  execsql { INSERT INTO t1 VALUES(13, 14, 15); }
           47  +  execsql BEGIN
           48  +  set ::snap3 [sqlite3_snapshot_get db main]
           49  +  execsql COMMIT
           50  +} {}
           51  +
           52  +do_execsql_test 1.2 {
           53  +  BEGIN;
           54  +    SELECT * FROM t1
           55  +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15}
           56  +
           57  +do_test 1.3 {
           58  +  sqlite3_snapshot_open db main $::snap1
           59  +  execsql { SELECT * FROM t1 }
           60  +} {1 2 3 4 5 6 7 8 9}
           61  +
           62  +do_test 1.4 {
           63  +  sqlite3_snapshot_open db main $::snap2
           64  +  execsql { SELECT * FROM t1 }
           65  +} {1 2 3 4 5 6 7 8 9 10 11 12}
           66  +
           67  +do_test 1.5 {
           68  +  sqlite3 db2 test.db
           69  +  execsql { PRAGMA wal_checkpoint } db2
           70  +} {0 5 4}
           71  +
           72  +do_execsql_test 1.6 {
           73  +  SELECT * FROM t1
           74  +} {1 2 3 4 5 6 7 8 9 10 11 12}
           75  +
           76  +do_test 1.7 {
           77  +  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
           78  +} {1 SQLITE_BUSY_SNAPSHOT}
           79  +
           80  +do_execsql_test 1.8 {
           81  +  SELECT * FROM t1
           82  +} {1 2 3 4 5 6 7 8 9 10 11 12}
           83  +
           84  +do_test 1.9 {
           85  +  execsql { COMMIT ; BEGIN }
           86  +  list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg
           87  +} {1 SQLITE_BUSY_SNAPSHOT}
           88  +
           89  +do_test 1.10 {
           90  +  execsql { COMMIT }
           91  +  execsql {
           92  +    PRAGMA wal_checkpoint;
           93  +    DELETE FROM t1 WHERE a = 1;
           94  +  } db2
           95  +  execsql BEGIN
           96  +  set ::snap4 [sqlite3_snapshot_get db main]
           97  +  execsql COMMIT
           98  +  execsql {
           99  +    DELETE FROM t1 WHERE a = 4;
          100  +  } db2
          101  +} {}
          102  +
          103  +do_test 1.11 {
          104  +  execsql { 
          105  +    BEGIN;
          106  +      SELECT * FROM t1
          107  +  }
          108  +} {7 8 9 10 11 12 13 14 15}
          109  +do_test 1.12 {
          110  +  sqlite3_snapshot_open db main $::snap4
          111  +  execsql { SELECT * FROM t1 }
          112  +} {4 5 6 7 8 9 10 11 12 13 14 15}
          113  +
          114  +do_test 1.13 {
          115  +  list [catch { sqlite3_snapshot_open db main $::snap3 } msg] $msg
          116  +} {1 SQLITE_BUSY_SNAPSHOT}
          117  +do_test 1.14 {
          118  +  execsql { SELECT * FROM t1 }
          119  +} {4 5 6 7 8 9 10 11 12 13 14 15}
          120  +
          121  +db close
          122  +db2 close
          123  +sqlite3 db test.db
          124  +do_execsql_test 1.15 {
          125  +  BEGIN;
          126  +    SELECT * FROM t1
          127  +} {7 8 9 10 11 12 13 14 15}
          128  +do_test 1.16 {
          129  +  list [catch { sqlite3_snapshot_open db main $::snap4 } msg] $msg
          130  +} {1 SQLITE_BUSY_SNAPSHOT}
          131  +do_execsql_test 1.17 { COMMIT }
          132  +
          133  +sqlite3_snapshot_free $::snap1
          134  +sqlite3_snapshot_free $::snap2
          135  +sqlite3_snapshot_free $::snap3
          136  +sqlite3_snapshot_free $::snap4
          137  +
          138  +#-------------------------------------------------------------------------
          139  +catch { db close }
          140  +sqlite3 db test.db
          141  +sqlite3 db2 test.db
          142  +sqlite3 db3 test.db
          143  +
          144  +proc xBusy {args} { return 1 }
          145  +db3 busy xBusy
          146  +
          147  +do_test 2.1 {
          148  +  execsql { INSERT INTO t1 VALUES(16, 17, 18) } db2
          149  +  execsql BEGIN
          150  +  set ::snap1 [sqlite3_snapshot_get db main]
          151  +  execsql COMMIT
          152  +  execsql { INSERT INTO t1 VALUES(19, 20, 21) } db2
          153  +  execsql BEGIN
          154  +  set ::snap2 [sqlite3_snapshot_get db main]
          155  +  execsql COMMIT
          156  +  set {} {}
          157  +} {}
          158  +
          159  +do_execsql_test -db db2 2.2 {
          160  +  BEGIN;
          161  +    INSERT INTO t1 VALUES(19, 20, 21);
          162  +}
          163  +
          164  +do_test 2.3 {
          165  +  execsql BEGIN
          166  +  sqlite3_snapshot_open db main $::snap1
          167  +  execsql { SELECT * FROM t1 }
          168  +} {7 8 9 10 11 12 13 14 15 16 17 18}
          169  +
          170  +proc xBusy {args} { 
          171  +  set ::res [list [catch { sqlite3_snapshot_open db main $::snap2 } msg] $msg]
          172  +  return 1
          173  +}
          174  +db3 busy xBusy
          175  +do_test 2.4 {
          176  +  execsql {PRAGMA wal_checkpoint = restart} db3
          177  +  set ::res
          178  +} {1 SQLITE_BUSY}
          179  +
          180  +sqlite3_snapshot_free $::snap1
          181  +sqlite3_snapshot_free $::snap2
          182  +
          183  +finish_test
          184  +