/ Changes On Branch serializable-snapshot
Login

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

Changes In Branch serializable-snapshot Excluding Merge-Ins

This is equivalent to a diff from bee2859b to e2c4bdf5

2016-11-26
19:44
Add the sqlite3_snapshot_recover() interface and related functionality. (check-in: b70c85ce user: drh tags: trunk)
2016-11-23
14:19
Correctly display the names of SQL functions in the VDBE_PROFILE output. (check-in: 161192f1 user: drh tags: trunk)
2016-11-22
22:23
Merge bug fixes and enhancements from trunk. (Closed-Leaf check-in: e2c4bdf5 user: drh tags: serializable-snapshot)
21:11
Enclose the sqlite3WalSnapshotRecover() routine within (check-in: e7be3183 user: drh tags: serializable-snapshot)
20:29
Add a new requirement mark for CAST expressions. (check-in: bee2859b user: drh tags: trunk)
19:15
Update requirement marks to reflect changes in wording in the documentation. No changes to code. (check-in: f5ac98ef user: drh tags: trunk)

Changes to src/main.c.

  3977   3977   int sqlite3_snapshot_get(
  3978   3978     sqlite3 *db, 
  3979   3979     const char *zDb,
  3980   3980     sqlite3_snapshot **ppSnapshot
  3981   3981   ){
  3982   3982     int rc = SQLITE_ERROR;
  3983   3983   #ifndef SQLITE_OMIT_WAL
  3984         -  int iDb;
  3985   3984   
  3986   3985   #ifdef SQLITE_ENABLE_API_ARMOR
  3987   3986     if( !sqlite3SafetyCheckOk(db) ){
  3988   3987       return SQLITE_MISUSE_BKPT;
  3989   3988     }
  3990   3989   #endif
  3991   3990     sqlite3_mutex_enter(db->mutex);
  3992   3991   
  3993         -  iDb = sqlite3FindDbName(db, zDb);
  3994         -  if( iDb==0 || iDb>1 ){
  3995         -    Btree *pBt = db->aDb[iDb].pBt;
  3996         -    if( 0==sqlite3BtreeIsInTrans(pBt) ){
  3997         -      rc = sqlite3BtreeBeginTrans(pBt, 0);
  3998         -      if( rc==SQLITE_OK ){
  3999         -        rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
         3992  +  if( db->autoCommit==0 ){
         3993  +    int iDb = sqlite3FindDbName(db, zDb);
         3994  +    if( iDb==0 || iDb>1 ){
         3995  +      Btree *pBt = db->aDb[iDb].pBt;
         3996  +      if( 0==sqlite3BtreeIsInTrans(pBt) ){
         3997  +        rc = sqlite3BtreeBeginTrans(pBt, 0);
         3998  +        if( rc==SQLITE_OK ){
         3999  +          rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
         4000  +        }
  4000   4001         }
  4001   4002       }
  4002   4003     }
  4003   4004   
  4004   4005     sqlite3_mutex_leave(db->mutex);
  4005   4006   #endif   /* SQLITE_OMIT_WAL */
  4006   4007     return rc;
................................................................................
  4034   4035             rc = sqlite3BtreeBeginTrans(pBt, 0);
  4035   4036             sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0);
  4036   4037           }
  4037   4038         }
  4038   4039       }
  4039   4040     }
  4040   4041   
         4042  +  sqlite3_mutex_leave(db->mutex);
         4043  +#endif   /* SQLITE_OMIT_WAL */
         4044  +  return rc;
         4045  +}
         4046  +
         4047  +/*
         4048  +** Recover as many snapshots as possible from the wal file associated with
         4049  +** schema zDb of database db.
         4050  +*/
         4051  +int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
         4052  +  int rc = SQLITE_ERROR;
         4053  +  int iDb;
         4054  +#ifndef SQLITE_OMIT_WAL
         4055  +
         4056  +#ifdef SQLITE_ENABLE_API_ARMOR
         4057  +  if( !sqlite3SafetyCheckOk(db) ){
         4058  +    return SQLITE_MISUSE_BKPT;
         4059  +  }
         4060  +#endif
         4061  +
         4062  +  sqlite3_mutex_enter(db->mutex);
         4063  +  iDb = sqlite3FindDbName(db, zDb);
         4064  +  if( iDb==0 || iDb>1 ){
         4065  +    Btree *pBt = db->aDb[iDb].pBt;
         4066  +    if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
         4067  +      rc = sqlite3BtreeBeginTrans(pBt, 0);
         4068  +      if( rc==SQLITE_OK ){
         4069  +        rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
         4070  +        sqlite3BtreeCommit(pBt);
         4071  +      }
         4072  +    }
         4073  +  }
  4041   4074     sqlite3_mutex_leave(db->mutex);
  4042   4075   #endif   /* SQLITE_OMIT_WAL */
  4043   4076     return rc;
  4044   4077   }
  4045   4078   
  4046   4079   /*
  4047   4080   ** Free a snapshot handle obtained from sqlite3_snapshot_get().
  4048   4081   */
  4049   4082   void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
  4050   4083     sqlite3_free(pSnapshot);
  4051   4084   }
  4052   4085   #endif /* SQLITE_ENABLE_SNAPSHOT */

Changes to src/pager.c.

  7401   7401     if( pPager->pWal ){
  7402   7402       sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
  7403   7403     }else{
  7404   7404       rc = SQLITE_ERROR;
  7405   7405     }
  7406   7406     return rc;
  7407   7407   }
         7408  +
         7409  +/*
         7410  +** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this 
         7411  +** is not a WAL database, return an error.
         7412  +*/
         7413  +int sqlite3PagerSnapshotRecover(Pager *pPager){
         7414  +  int rc;
         7415  +  if( pPager->pWal ){
         7416  +    rc = sqlite3WalSnapshotRecover(pPager->pWal);
         7417  +  }else{
         7418  +    rc = SQLITE_ERROR;
         7419  +  }
         7420  +  return rc;
         7421  +}
  7408   7422   #endif /* SQLITE_ENABLE_SNAPSHOT */
  7409   7423   #endif /* !SQLITE_OMIT_WAL */
  7410   7424   
  7411   7425   #ifdef SQLITE_ENABLE_ZIPVFS
  7412   7426   /*
  7413   7427   ** A read-lock must be held on the pager when this function is called. If
  7414   7428   ** the pager is in WAL mode and the WAL file currently contains one or more

Changes to src/pager.h.

   178    178     int sqlite3PagerWalCallback(Pager *pPager);
   179    179     int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
   180    180     int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
   181    181     int sqlite3PagerUseWal(Pager *pPager);
   182    182   # ifdef SQLITE_ENABLE_SNAPSHOT
   183    183     int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
   184    184     int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
          185  +  int sqlite3PagerSnapshotRecover(Pager *pPager);
   185    186   # endif
   186    187   #else
   187    188   # define sqlite3PagerUseWal(x) 0
   188    189   #endif
   189    190   
   190    191   #ifdef SQLITE_ENABLE_ZIPVFS
   191    192     int sqlite3PagerWalFramesize(Pager *pPager);

Changes to src/sqlite.h.in.

  8279   8279   ** the most recent version.
  8280   8280   **
  8281   8281   ** The constructor for this object is [sqlite3_snapshot_get()].  The
  8282   8282   ** [sqlite3_snapshot_open()] method causes a fresh read transaction to refer
  8283   8283   ** to an historical snapshot (if possible).  The destructor for 
  8284   8284   ** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
  8285   8285   */
  8286         -typedef struct sqlite3_snapshot sqlite3_snapshot;
         8286  +typedef struct sqlite3_snapshot {
         8287  +  unsigned char hidden[48];
         8288  +} sqlite3_snapshot;
  8287   8289   
  8288   8290   /*
  8289   8291   ** CAPI3REF: Record A Database Snapshot
  8290   8292   ** EXPERIMENTAL
  8291   8293   **
  8292   8294   ** ^The [sqlite3_snapshot_get(D,S,P)] interface attempts to make a
  8293   8295   ** new [sqlite3_snapshot] object that records the current state of
  8294   8296   ** schema S in database connection D.  ^On success, the
  8295   8297   ** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
  8296   8298   ** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
  8297         -** ^If schema S of [database connection] D is not a [WAL mode] database
  8298         -** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
  8299         -** leaves the *P value unchanged and returns an appropriate [error code].
         8299  +** If there is not already a read-transaction open on schema S when
         8300  +** this function is called, one is opened automatically. 
         8301  +**
         8302  +** The following must be true for this function to succeed. If any of
         8303  +** the following statements are false when sqlite3_snapshot_get() is
         8304  +** called, SQLITE_ERROR is returned. The final value of *P is undefined
         8305  +** in this case. 
         8306  +**
         8307  +** <ul>
         8308  +**   <li> The database handle must be in [autocommit mode].
         8309  +**
         8310  +**   <li> Schema S of [database connection] D must be a [WAL mode] database.
         8311  +**
         8312  +**   <li> There must not be a write transaction open on schema S of database
         8313  +**        connection D.
         8314  +**
         8315  +**   <li> One or more transactions must have been written to the current wal
         8316  +**        file since it was created on disk (by any connection). This means
         8317  +**        that a snapshot cannot be taken on a wal mode database with no wal 
         8318  +**        file immediately after it is first opened. At least one transaction
         8319  +**        must be written to it first.
         8320  +** </ul>
         8321  +**
         8322  +** This function may also return SQLITE_NOMEM.  If it is called with the
         8323  +** database handle in autocommit mode but fails for some other reason, 
         8324  +** whether or not a read transaction is opened on schema S is undefined.
  8300   8325   **
  8301   8326   ** The [sqlite3_snapshot] object returned from a successful call to
  8302   8327   ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
  8303   8328   ** to avoid a memory leak.
  8304   8329   **
  8305   8330   ** The [sqlite3_snapshot_get()] interface is only available when the
  8306   8331   ** SQLITE_ENABLE_SNAPSHOT compile-time option is used.
................................................................................
  8385   8410   ** snapshot, and a positive value if P1 is a newer snapshot than P2.
  8386   8411   */
  8387   8412   SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
  8388   8413     sqlite3_snapshot *p1,
  8389   8414     sqlite3_snapshot *p2
  8390   8415   );
  8391   8416   
         8417  +/*
         8418  +** CAPI3REF: Recover snapshots from a wal file
         8419  +** EXPERIMENTAL
         8420  +**
         8421  +** If all connections disconnect from a database file but do not perform
         8422  +** a checkpoint, the existing wal file is opened along with the database
         8423  +** file the next time the database is opened. At this point it is only
         8424  +** possible to successfully call sqlite3_snapshot_open() to open the most
         8425  +** recent snapshot of the database (the one at the head of the wal file),
         8426  +** even though the wal file may contain other valid snapshots for which
         8427  +** clients have sqlite3_snapshot handles.
         8428  +**
         8429  +** This function attempts to scan the wal file associated with database zDb
         8430  +** of database handle db and make all valid snapshots available to
         8431  +** sqlite3_snapshot_open(). It is an error if there is already a read
         8432  +** transaction open on the database, or if the database is not a wal mode
         8433  +** database.
         8434  +**
         8435  +** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
         8436  +*/
         8437  +SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
         8438  +
  8392   8439   /*
  8393   8440   ** Undo the hack that converts floating point types to integer for
  8394   8441   ** builds on processors without floating point support.
  8395   8442   */
  8396   8443   #ifdef SQLITE_OMIT_FLOATING_POINT
  8397   8444   # undef double
  8398   8445   #endif
  8399   8446   
  8400   8447   #ifdef __cplusplus
  8401   8448   }  /* End of the 'extern "C"' block */
  8402   8449   #endif
  8403   8450   #endif /* SQLITE3_H */

Changes to src/test1.c.

  2306   2306       if( sqlite3TestMakePointerStr(interp, zBuf, pSnapshot) ) return TCL_ERROR;
  2307   2307       Tcl_SetObjResult(interp, Tcl_NewStringObj(zBuf, -1));
  2308   2308     }
  2309   2309     return TCL_OK;
  2310   2310   }
  2311   2311   #endif /* SQLITE_ENABLE_SNAPSHOT */
  2312   2312   
         2313  +#ifdef SQLITE_ENABLE_SNAPSHOT
         2314  +/*
         2315  +** Usage: sqlite3_snapshot_recover DB DBNAME
         2316  +*/
         2317  +static int SQLITE_TCLAPI test_snapshot_recover(
         2318  +  void * clientData,
         2319  +  Tcl_Interp *interp,
         2320  +  int objc,
         2321  +  Tcl_Obj *CONST objv[]
         2322  +){
         2323  +  int rc;
         2324  +  sqlite3 *db;
         2325  +  char *zName;
         2326  +
         2327  +  if( objc!=3 ){
         2328  +    Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
         2329  +    return TCL_ERROR;
         2330  +  }
         2331  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
         2332  +  zName = Tcl_GetString(objv[2]);
         2333  +
         2334  +  rc = sqlite3_snapshot_recover(db, zName);
         2335  +  if( rc!=SQLITE_OK ){
         2336  +    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
         2337  +    return TCL_ERROR;
         2338  +  }else{
         2339  +    Tcl_ResetResult(interp);
         2340  +  }
         2341  +  return TCL_OK;
         2342  +}
         2343  +#endif /* SQLITE_ENABLE_SNAPSHOT */
         2344  +
  2313   2345   #ifdef SQLITE_ENABLE_SNAPSHOT
  2314   2346   /*
  2315   2347   ** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT
  2316   2348   */
  2317   2349   static int SQLITE_TCLAPI test_snapshot_open(
  2318   2350     void * clientData,
  2319   2351     Tcl_Interp *interp,
................................................................................
  2381   2413       return TCL_ERROR;
  2382   2414     }
  2383   2415     p1 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[1]));
  2384   2416     p2 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[2]));
  2385   2417     res = sqlite3_snapshot_cmp(p1, p2);
  2386   2418     Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
  2387   2419     return TCL_OK;
         2420  +}
         2421  +#endif /* SQLITE_ENABLE_SNAPSHOT */
         2422  +
         2423  +#ifdef SQLITE_ENABLE_SNAPSHOT
         2424  +/*
         2425  +** Usage: sqlite3_snapshot_get_blob DB DBNAME
         2426  +*/
         2427  +static int SQLITE_TCLAPI test_snapshot_get_blob(
         2428  +  void * clientData,
         2429  +  Tcl_Interp *interp,
         2430  +  int objc,
         2431  +  Tcl_Obj *CONST objv[]
         2432  +){
         2433  +  int rc;
         2434  +  sqlite3 *db;
         2435  +  char *zName;
         2436  +  sqlite3_snapshot *pSnapshot = 0;
         2437  +
         2438  +  if( objc!=3 ){
         2439  +    Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
         2440  +    return TCL_ERROR;
         2441  +  }
         2442  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
         2443  +  zName = Tcl_GetString(objv[2]);
         2444  +
         2445  +  rc = sqlite3_snapshot_get(db, zName, &pSnapshot);
         2446  +  if( rc!=SQLITE_OK ){
         2447  +    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
         2448  +    return TCL_ERROR;
         2449  +  }else{
         2450  +    Tcl_SetObjResult(interp, 
         2451  +        Tcl_NewByteArrayObj((unsigned char*)pSnapshot, sizeof(sqlite3_snapshot))
         2452  +    );
         2453  +    sqlite3_snapshot_free(pSnapshot);
         2454  +  }
         2455  +  return TCL_OK;
         2456  +}
         2457  +#endif /* SQLITE_ENABLE_SNAPSHOT */
         2458  +
         2459  +#ifdef SQLITE_ENABLE_SNAPSHOT
         2460  +  /*
         2461  +  ** Usage: sqlite3_snapshot_open_blob DB DBNAME SNAPSHOT
         2462  +*/
         2463  +static int SQLITE_TCLAPI test_snapshot_open_blob(
         2464  +  void * clientData,
         2465  +  Tcl_Interp *interp,
         2466  +  int objc,
         2467  +  Tcl_Obj *CONST objv[]
         2468  +){
         2469  +  int rc;
         2470  +  sqlite3 *db;
         2471  +  char *zName;
         2472  +  unsigned char *pBlob;
         2473  +  int nBlob;
         2474  +
         2475  +  if( objc!=4 ){
         2476  +    Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SNAPSHOT");
         2477  +    return TCL_ERROR;
         2478  +  }
         2479  +  if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
         2480  +  zName = Tcl_GetString(objv[2]);
         2481  +  pBlob = Tcl_GetByteArrayFromObj(objv[3], &nBlob);
         2482  +  if( nBlob!=sizeof(sqlite3_snapshot) ){
         2483  +    Tcl_AppendResult(interp, "bad SNAPSHOT", 0);
         2484  +    return TCL_ERROR;
         2485  +  }
         2486  +  rc = sqlite3_snapshot_open(db, zName, (sqlite3_snapshot*)pBlob);
         2487  +  if( rc!=SQLITE_OK ){
         2488  +    Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
         2489  +    return TCL_ERROR;
         2490  +  }
         2491  +  return TCL_OK;
         2492  +}
         2493  +#endif /* SQLITE_ENABLE_SNAPSHOT */
         2494  +
         2495  +#ifdef SQLITE_ENABLE_SNAPSHOT
         2496  +/*
         2497  +** Usage: sqlite3_snapshot_cmp_blob SNAPSHOT1 SNAPSHOT2
         2498  +*/
         2499  +static int SQLITE_TCLAPI test_snapshot_cmp_blob(
         2500  +  void * clientData,
         2501  +  Tcl_Interp *interp,
         2502  +  int objc,
         2503  +  Tcl_Obj *CONST objv[]
         2504  +){
         2505  +  int res;
         2506  +  unsigned char *p1;
         2507  +  unsigned char *p2;
         2508  +  int n1;
         2509  +  int n2;
         2510  +
         2511  +  if( objc!=3 ){
         2512  +    Tcl_WrongNumArgs(interp, 1, objv, "SNAPSHOT1 SNAPSHOT2");
         2513  +    return TCL_ERROR;
         2514  +  }
         2515  +
         2516  +  p1 = Tcl_GetByteArrayFromObj(objv[1], &n1);
         2517  +  p2 = Tcl_GetByteArrayFromObj(objv[2], &n2);
         2518  +
         2519  +  if( n1!=sizeof(sqlite3_snapshot) || n1!=n2 ){
         2520  +    Tcl_AppendResult(interp, "bad SNAPSHOT", 0);
         2521  +    return TCL_ERROR;
         2522  +  }
         2523  +
         2524  +  res = sqlite3_snapshot_cmp((sqlite3_snapshot*)p1, (sqlite3_snapshot*)p2);
         2525  +  Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
         2526  +  return TCL_OK;
  2388   2527   }
  2389   2528   #endif /* SQLITE_ENABLE_SNAPSHOT */
  2390   2529   
  2391   2530   /*
  2392   2531   ** Usage: sqlite3_delete_database FILENAME
  2393   2532   */
  2394   2533   int sqlite3_delete_database(const char*);   /* in test_delete.c */
................................................................................
  7535   7674   #endif
  7536   7675        { "vfs_current_time_int64",           vfsCurrentTimeInt64,   0 },
  7537   7676   #ifdef SQLITE_ENABLE_SNAPSHOT
  7538   7677        { "sqlite3_snapshot_get", test_snapshot_get, 0 },
  7539   7678        { "sqlite3_snapshot_open", test_snapshot_open, 0 },
  7540   7679        { "sqlite3_snapshot_free", test_snapshot_free, 0 },
  7541   7680        { "sqlite3_snapshot_cmp", test_snapshot_cmp, 0 },
         7681  +     { "sqlite3_snapshot_recover", test_snapshot_recover, 0 },
         7682  +     { "sqlite3_snapshot_get_blob", test_snapshot_get_blob, 0 },
         7683  +     { "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 },
         7684  +     { "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
  7542   7685   #endif
  7543   7686        { "sqlite3_delete_database", test_delete_database, 0 },
  7544   7687     };
  7545   7688     static int bitmask_size = sizeof(Bitmask)*8;
  7546   7689     static int longdouble_size = sizeof(LONGDOUBLE_TYPE);
  7547   7690     int i;
  7548   7691     extern int sqlite3_sync_count, sqlite3_fullsync_count;

Changes to src/wal.c.

  2375   2375     }else{
  2376   2376       assert( mxReadMark<=pWal->hdr.mxFrame );
  2377   2377       pWal->readLock = (i16)mxI;
  2378   2378     }
  2379   2379     return rc;
  2380   2380   }
  2381   2381   
         2382  +#ifdef SQLITE_ENABLE_SNAPSHOT
         2383  +/*
         2384  +** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted 
         2385  +** variable so that older snapshots can be accessed. To do this, loop
         2386  +** through all wal frames from nBackfillAttempted to (nBackfill+1), 
         2387  +** comparing their content to the corresponding page with the database
         2388  +** file, if any. Set nBackfillAttempted to the frame number of the
         2389  +** first frame for which the wal file content matches the db file.
         2390  +**
         2391  +** This is only really safe if the file-system is such that any page 
         2392  +** writes made by earlier checkpointers were atomic operations, which 
         2393  +** is not always true. It is also possible that nBackfillAttempted
         2394  +** may be left set to a value larger than expected, if a wal frame
         2395  +** contains content that duplicate of an earlier version of the same
         2396  +** page.
         2397  +**
         2398  +** SQLITE_OK is returned if successful, or an SQLite error code if an
         2399  +** error occurs. It is not an error if nBackfillAttempted cannot be
         2400  +** decreased at all.
         2401  +*/
         2402  +int sqlite3WalSnapshotRecover(Wal *pWal){
         2403  +  int rc;
         2404  +
         2405  +  assert( pWal->readLock>=0 );
         2406  +  rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
         2407  +  if( rc==SQLITE_OK ){
         2408  +    volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
         2409  +    int szPage = (int)pWal->szPage;
         2410  +    i64 szDb;                   /* Size of db file in bytes */
         2411  +
         2412  +    rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
         2413  +    if( rc==SQLITE_OK ){
         2414  +      void *pBuf1 = sqlite3_malloc(szPage);
         2415  +      void *pBuf2 = sqlite3_malloc(szPage);
         2416  +      if( pBuf1==0 || pBuf2==0 ){
         2417  +        rc = SQLITE_NOMEM;
         2418  +      }else{
         2419  +        u32 i = pInfo->nBackfillAttempted;
         2420  +        for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
         2421  +          volatile ht_slot *dummy;
         2422  +          volatile u32 *aPgno;      /* Array of page numbers */
         2423  +          u32 iZero;                /* Frame corresponding to aPgno[0] */
         2424  +          u32 pgno;                 /* Page number in db file */
         2425  +          i64 iDbOff;               /* Offset of db file entry */
         2426  +          i64 iWalOff;              /* Offset of wal file entry */
         2427  +
         2428  +          rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
         2429  +          if( rc!=SQLITE_OK ) break;
         2430  +          pgno = aPgno[i-iZero];
         2431  +          iDbOff = (i64)(pgno-1) * szPage;
         2432  +
         2433  +          if( iDbOff+szPage<=szDb ){
         2434  +            iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
         2435  +            rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
         2436  +
         2437  +            if( rc==SQLITE_OK ){
         2438  +              rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
         2439  +            }
         2440  +
         2441  +            if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
         2442  +              break;
         2443  +            }
         2444  +          }
         2445  +
         2446  +          pInfo->nBackfillAttempted = i-1;
         2447  +        }
         2448  +      }
         2449  +
         2450  +      sqlite3_free(pBuf1);
         2451  +      sqlite3_free(pBuf2);
         2452  +    }
         2453  +    walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
         2454  +  }
         2455  +
         2456  +  return rc;
         2457  +}
         2458  +#endif /* SQLITE_ENABLE_SNAPSHOT */
         2459  +
  2382   2460   /*
  2383   2461   ** Begin a read transaction on the database.
  2384   2462   **
  2385   2463   ** This routine used to be called sqlite3OpenSnapshot() and with good reason:
  2386   2464   ** it takes a snapshot of the state of the WAL and wal-index for the current
  2387   2465   ** instant in time.  The current thread will continue to use this snapshot.
  2388   2466   ** Other threads might append new content to the WAL and wal-index but
................................................................................
  2437   2515         /* It is possible that there is a checkpointer thread running 
  2438   2516         ** concurrent with this code. If this is the case, it may be that the
  2439   2517         ** checkpointer has already determined that it will checkpoint 
  2440   2518         ** snapshot X, where X is later in the wal file than pSnapshot, but 
  2441   2519         ** has not yet set the pInfo->nBackfillAttempted variable to indicate 
  2442   2520         ** its intent. To avoid the race condition this leads to, ensure that
  2443   2521         ** there is no checkpointer process by taking a shared CKPT lock 
  2444         -      ** before checking pInfo->nBackfillAttempted.  */
         2522  +      ** before checking pInfo->nBackfillAttempted.  
         2523  +      **
         2524  +      ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
         2525  +      **       this already?
         2526  +      */
  2445   2527         rc = walLockShared(pWal, WAL_CKPT_LOCK);
  2446   2528   
  2447   2529         if( rc==SQLITE_OK ){
  2448   2530           /* Check that the wal file has not been wrapped. Assuming that it has
  2449   2531           ** not, also check that no checkpointer has attempted to checkpoint any
  2450   2532           ** frames beyond pSnapshot->mxFrame. If either of these conditions are
  2451   2533           ** true, return SQLITE_BUSY_SNAPSHOT. Otherwise, overwrite pWal->hdr
................................................................................
  3389   3471   /* Create a snapshot object.  The content of a snapshot is opaque to
  3390   3472   ** every other subsystem, so the WAL module can put whatever it needs
  3391   3473   ** in the object.
  3392   3474   */
  3393   3475   int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
  3394   3476     int rc = SQLITE_OK;
  3395   3477     WalIndexHdr *pRet;
         3478  +  static const u32 aZero[4] = { 0, 0, 0, 0 };
  3396   3479   
  3397   3480     assert( pWal->readLock>=0 && pWal->writeLock==0 );
  3398   3481   
         3482  +  if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){
         3483  +    *ppSnapshot = 0;
         3484  +    return SQLITE_ERROR;
         3485  +  }
  3399   3486     pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
  3400   3487     if( pRet==0 ){
  3401   3488       rc = SQLITE_NOMEM_BKPT;
  3402   3489     }else{
  3403   3490       memcpy(pRet, &pWal->hdr, sizeof(WalIndexHdr));
  3404   3491       *ppSnapshot = (sqlite3_snapshot*)pRet;
  3405   3492     }

Changes to src/wal.h.

   127    127   ** WAL module is using shared-memory, return false. 
   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  +int sqlite3WalSnapshotRecover(Wal *pWal);
   134    135   #endif
   135    136   
   136    137   #ifdef SQLITE_ENABLE_ZIPVFS
   137    138   /* If the WAL file is not empty, return the number of bytes of content
   138    139   ** stored in each frame (i.e. the db page-size when the WAL was created).
   139    140   */
   140    141   int sqlite3WalFramesize(Wal *pWal);

Changes to test/snapshot.test.

    22     22   # "PRAGMA journal_mode=memory", which fails if the database is in wal mode
    23     23   # and there are one or more existing connections.
    24     24   if {[permutation]=="inmemory_journal"} {
    25     25     finish_test
    26     26     return
    27     27   }
    28     28   
    29         -#-------------------------------------------------------------------------
    30         -# Check some error conditions in snapshot_get(). It is an error if:
    31         -#
    32         -#  1) snapshot_get() is called on a non-WAL database, or
    33         -#  2) there is an open write transaction on the database.
    34         -#
    35         -do_execsql_test 1.0 {
    36         -  CREATE TABLE t1(a, b);
    37         -  INSERT INTO t1 VALUES(1, 2);
    38         -  INSERT INTO t1 VALUES(3, 4);
    39         -}
    40         -
    41         -do_test 1.1.1 {
    42         -  execsql { BEGIN; SELECT * FROM t1; }
    43         -  list [catch { sqlite3_snapshot_get db main } msg] $msg
    44         -} {1 SQLITE_ERROR}
    45         -do_execsql_test 1.1.2 COMMIT
    46         -
    47         -do_test 1.2.1 {
    48         -  execsql {
    49         -    PRAGMA journal_mode = WAL;
    50         -    BEGIN;
    51         -      INSERT INTO t1 VALUES(5, 6);
    52         -      INSERT INTO t1 VALUES(7, 8);
    53         -  }
    54         -  list [catch { sqlite3_snapshot_get db main } msg] $msg
    55         -} {1 SQLITE_ERROR}
    56         -do_execsql_test 1.3.2 COMMIT
    57         -
    58         -#-------------------------------------------------------------------------
    59         -# Check that a simple case works. Reuse the database created by the
    60         -# block of tests above.
    61         -#
    62         -do_execsql_test 2.1.0 {
    63         -  BEGIN;
    64         -    SELECT * FROM t1;
    65         -} {1 2 3 4 5 6 7 8}
    66         -
    67         -do_test 2.1.1 {
    68         -  set snapshot [sqlite3_snapshot_get db main]
    69         -  execsql {
    70         -    COMMIT;
    71         -    INSERT INTO t1 VALUES(9, 10);
    72         -    SELECT * FROM t1;
    73         -  }
    74         -} {1 2 3 4 5 6 7 8 9 10}
    75         -
    76         -do_test 2.1.2 {
    77         -  execsql BEGIN
    78         -  sqlite3_snapshot_open db main $snapshot
    79         -  execsql {
    80         -    SELECT * FROM t1;
    81         -  }
    82         -} {1 2 3 4 5 6 7 8}
    83         -
    84         -do_test 2.1.3 {
    85         -  sqlite3_snapshot_free $snapshot
    86         -  execsql COMMIT
    87         -} {}
    88         -
    89         -do_test 2.2.0 {
    90         -  sqlite3 db2 test.db
    91         -  execsql {
           29  +foreach {tn tcl} {
           30  +  1 {
           31  +    proc snapshot_get {DB DBNAME} {
           32  +      uplevel [list sqlite3_snapshot_get $DB $DBNAME]
           33  +    }
           34  +    proc snapshot_open {DB DBNAME SNAPSHOT} {
           35  +      uplevel [list sqlite3_snapshot_open $DB $DBNAME $SNAPSHOT]
           36  +    }
           37  +    proc snapshot_free {SNAPSHOT} {
           38  +      uplevel [list sqlite3_snapshot_free $SNAPSHOT]
           39  +    }
           40  +    proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
           41  +      uplevel [list sqlite3_snapshot_cmp $SNAPSHOT1 $SNAPSHOT2]
           42  +    }
           43  +  }
           44  +
           45  +  2 {
           46  +    proc snapshot_get {DB DBNAME} {
           47  +      uplevel [list sqlite3_snapshot_get_blob $DB $DBNAME]
           48  +    }
           49  +    proc snapshot_open {DB DBNAME SNAPSHOT} {
           50  +      uplevel [list sqlite3_snapshot_open_blob $DB $DBNAME $SNAPSHOT]
           51  +    }
           52  +    proc snapshot_free {SNAPSHOT} {
           53  +    }
           54  +    proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
           55  +      uplevel [list sqlite3_snapshot_cmp_blob $SNAPSHOT1 $SNAPSHOT2]
           56  +    }
           57  +  }
           58  +} {
           59  +
           60  +  reset_db
           61  +  eval $tcl
           62  +
           63  +  #-------------------------------------------------------------------------
           64  +  # Check some error conditions in snapshot_get(). It is an error if:
           65  +  #
           66  +  #  1) snapshot_get() is called on a non-WAL database, or
           67  +  #  2) there is an open write transaction on the database.
           68  +  #  3) the database handle is in auto-commit mode
           69  +  #
           70  +  do_execsql_test $tn.1.0 {
           71  +    CREATE TABLE t1(a, b);
           72  +    INSERT INTO t1 VALUES(1, 2);
           73  +    INSERT INTO t1 VALUES(3, 4);
           74  +  }
           75  +
           76  +  do_test $tn.1.1.1 {
           77  +    execsql { BEGIN; SELECT * FROM t1; }
           78  +    list [catch { snapshot_get db main } msg] $msg
           79  +  } {1 SQLITE_ERROR}
           80  +  do_execsql_test $tn.1.1.2 COMMIT
           81  +
           82  +  do_test $tn.1.2.1 {
           83  +    execsql {
           84  +      PRAGMA journal_mode = WAL;
           85  +      BEGIN;
           86  +        INSERT INTO t1 VALUES(5, 6);
           87  +        INSERT INTO t1 VALUES(7, 8);
           88  +    }
           89  +    list [catch { snapshot_get db main } msg] $msg
           90  +  } {1 SQLITE_ERROR}
           91  +  do_execsql_test $tn.1.2.2 COMMIT
           92  +
           93  +  do_test $tn.1.3.1 {
           94  +    list [catch { snapshot_get db main } msg] $msg
           95  +  } {1 SQLITE_ERROR}
           96  +  do_test $tn.1.3.2 {
           97  +    db trans { set snap [snapshot_get db main] }
           98  +    snapshot_free $snap
           99  +  } {}
          100  +
          101  +  #-------------------------------------------------------------------------
          102  +  # Check that a simple case works. Reuse the database created by the
          103  +  # block of tests above.
          104  +  #
          105  +  do_execsql_test $tn.2.1.0 {
    92    106       BEGIN;
    93    107         SELECT * FROM t1;
    94         -  } db2
    95         -} {1 2 3 4 5 6 7 8 9 10}
    96         -
    97         -do_test 2.2.1 {
    98         -  set snapshot [sqlite3_snapshot_get db2 main]
    99         -  execsql {
   100         -    INSERT INTO t1 VALUES(11, 12);
   101         -    SELECT * FROM t1;
   102         -  }
   103         -} {1 2 3 4 5 6 7 8 9 10 11 12}
   104         -
   105         -do_test 2.2.2 {
   106         -  execsql BEGIN
   107         -  sqlite3_snapshot_open db main $snapshot
   108         -  execsql {
   109         -    SELECT * FROM t1;
   110         -  }
   111         -} {1 2 3 4 5 6 7 8 9 10}
   112         -
   113         -do_test 2.2.3 {
   114         -  sqlite3_snapshot_free $snapshot
   115         -  execsql COMMIT
   116         -  execsql COMMIT db2
   117         -  db2 close
   118         -} {}
   119         -
   120         -do_test 2.3.1 {
   121         -  execsql { DELETE FROM t1 WHERE a>6 }
   122         -  set snapshot [sqlite3_snapshot_get db main]
   123         -  execsql {
   124         -    INSERT INTO t1 VALUES('a', 'b');
   125         -    INSERT INTO t1 VALUES('c', 'd');
   126         -    SELECT * FROM t1;
   127         -  }
   128         -} {1 2 3 4 5 6 a b c d}
   129         -do_test 2.3.2 {
   130         -  execsql BEGIN
   131         -  sqlite3_snapshot_open db main $snapshot
   132         -  execsql { SELECT * FROM t1 }
   133         -} {1 2 3 4 5 6}
   134         -
   135         -do_test 2.3.3 {
   136         -  catchsql {
   137         -    INSERT INTO t1 VALUES('x','y')
   138         -  }
   139         -} {1 {database is locked}}
   140         -do_test 2.3.4 {
   141         -  execsql COMMIT
   142         -  sqlite3_snapshot_free $snapshot
   143         -} {}
   144         -
   145         -#-------------------------------------------------------------------------
   146         -# Check some errors in sqlite3_snapshot_open(). It is an error if:
   147         -#
   148         -#   1) the db is in auto-commit mode,
   149         -#   2) the db has an open (read or write) transaction,
   150         -#   3) the db is not a wal database,
   151         -#
   152         -# Reuse the database created by earlier tests.
   153         -#
   154         -do_execsql_test 3.0.0 {
   155         -  CREATE TABLE t2(x, y);
   156         -  INSERT INTO t2 VALUES('a', 'b');
   157         -  INSERT INTO t2 VALUES('c', 'd');
   158         -  BEGIN;
   159         -    SELECT * FROM t2;
   160         -} {a b c d}
   161         -do_test 3.0.1 {
   162         -  set snapshot [sqlite3_snapshot_get db main]
   163         -  execsql { COMMIT }
   164         -  execsql { INSERT INTO t2 VALUES('e', 'f'); }
   165         -} {}
   166         -
   167         -do_test 3.1 {
   168         -  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
   169         -} {1 SQLITE_ERROR}
   170         -
   171         -do_test 3.2.1 {
   172         -  execsql {
          108  +  } {1 2 3 4 5 6 7 8}
          109  +
          110  +  do_test $tn.2.1.1 {
          111  +    set snapshot [snapshot_get db main]
          112  +    execsql {
          113  +      COMMIT;
          114  +      INSERT INTO t1 VALUES(9, 10);
          115  +      SELECT * FROM t1;
          116  +    }
          117  +  } {1 2 3 4 5 6 7 8 9 10}
          118  +
          119  +  do_test $tn.2.1.2 {
          120  +    execsql BEGIN
          121  +    snapshot_open db main $snapshot
          122  +    execsql {
          123  +      SELECT * FROM t1;
          124  +    }
          125  +  } {1 2 3 4 5 6 7 8}
          126  +
          127  +  do_test $tn.2.1.3 {
          128  +    snapshot_free $snapshot
          129  +    execsql COMMIT
          130  +  } {}
          131  +
          132  +  do_test $tn.2.2.0 {
          133  +    sqlite3 db2 test.db
          134  +    execsql {
          135  +      BEGIN;
          136  +        SELECT * FROM t1;
          137  +    } db2
          138  +  } {1 2 3 4 5 6 7 8 9 10}
          139  +
          140  +  do_test $tn.2.2.1 {
          141  +    set snapshot [snapshot_get db2 main]
          142  +    execsql {
          143  +      INSERT INTO t1 VALUES(11, 12);
          144  +      SELECT * FROM t1;
          145  +    }
          146  +  } {1 2 3 4 5 6 7 8 9 10 11 12}
          147  +
          148  +  do_test $tn.2.2.2 {
          149  +    execsql BEGIN
          150  +    snapshot_open db main $snapshot
          151  +    execsql {
          152  +      SELECT * FROM t1;
          153  +    }
          154  +  } {1 2 3 4 5 6 7 8 9 10}
          155  +
          156  +  do_test $tn.2.2.3 {
          157  +    snapshot_free $snapshot
          158  +    execsql COMMIT
          159  +    execsql COMMIT db2
          160  +    db2 close
          161  +  } {}
          162  +
          163  +  do_test $tn.2.3.1 {
          164  +    execsql { DELETE FROM t1 WHERE a>6 }
          165  +    db trans { set snapshot [snapshot_get db main] }
          166  +    execsql {
          167  +      INSERT INTO t1 VALUES('a', 'b');
          168  +      INSERT INTO t1 VALUES('c', 'd');
          169  +      SELECT * FROM t1;
          170  +    }
          171  +  } {1 2 3 4 5 6 a b c d}
          172  +  do_test $tn.2.3.2 {
          173  +    execsql BEGIN
          174  +    snapshot_open db main $snapshot
          175  +    execsql { SELECT * FROM t1 }
          176  +  } {1 2 3 4 5 6}
          177  +
          178  +  do_test $tn.2.3.3 {
          179  +    catchsql {
          180  +      INSERT INTO t1 VALUES('x','y')
          181  +    }
          182  +  } {1 {database is locked}}
          183  +  do_test $tn.2.3.4 {
          184  +    execsql COMMIT
          185  +    snapshot_free $snapshot
          186  +  } {}
          187  +
          188  +  #-------------------------------------------------------------------------
          189  +  # Check some errors in snapshot_open(). It is an error if:
          190  +  #
          191  +  #   1) the db is in auto-commit mode,
          192  +  #   2) the db has an open (read or write) transaction,
          193  +  #   3) the db is not a wal database,
          194  +  #
          195  +  # Reuse the database created by earlier tests.
          196  +  #
          197  +  do_execsql_test $tn.3.0.0 {
          198  +    CREATE TABLE t2(x, y);
          199  +    INSERT INTO t2 VALUES('a', 'b');
          200  +    INSERT INTO t2 VALUES('c', 'd');
   173    201       BEGIN;
   174    202         SELECT * FROM t2;
   175         -  }
   176         -} {a b c d e f}
   177         -do_test 3.2.2 {
   178         -  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
   179         -} {1 SQLITE_ERROR}
   180         -
   181         -do_test 3.2.3 {
   182         -  execsql {
   183         -    COMMIT;
   184         -    BEGIN;
   185         -      INSERT INTO t2 VALUES('g', 'h');
   186         -  }
   187         -  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
   188         -} {1 SQLITE_ERROR}
   189         -do_execsql_test 3.2.4 COMMIT
   190         -
   191         -do_test 3.3.1 {
   192         -  execsql { PRAGMA journal_mode = DELETE }
   193         -  execsql { BEGIN }
   194         -  list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
   195         -} {1 SQLITE_ERROR}
   196         -
   197         -do_test 3.3.2 {
   198         -  sqlite3_snapshot_free $snapshot
   199         -  execsql COMMIT
   200         -} {}
   201         -
   202         -#-------------------------------------------------------------------------
   203         -# Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
   204         -# no longer exists because the wal file has been checkpointed.
   205         -#
   206         -#   1. Reading a snapshot from the middle of a wal file is not possible
   207         -#      after the wal file has been checkpointed.
   208         -#
   209         -#   2. That a snapshot from the end of a wal file can not be read once
   210         -#      the wal file has been wrapped.
   211         -#
   212         -do_execsql_test 4.1.0 {
   213         -  PRAGMA journal_mode = wal;
   214         -  CREATE TABLE t3(i, j);
   215         -  INSERT INTO t3 VALUES('o', 't');
   216         -  INSERT INTO t3 VALUES('t', 'f');
   217         -  BEGIN;
   218         -    SELECT * FROM t3;
   219         -} {wal o t t f}
   220         -
   221         -do_test 4.1.1 {
   222         -  set snapshot [sqlite3_snapshot_get db main]
   223         -  execsql COMMIT
   224         -} {}
   225         -do_test 4.1.2 {
   226         -  execsql { 
   227         -    INSERT INTO t3 VALUES('f', 's'); 
   228         -    BEGIN;
   229         -  }
   230         -  sqlite3_snapshot_open db main $snapshot
   231         -  execsql { SELECT * FROM t3 }
   232         -} {o t t f}
   233         -
   234         -do_test 4.1.3 {
   235         -  execsql { 
   236         -    COMMIT;
   237         -    PRAGMA wal_checkpoint;
   238         -    BEGIN;
   239         -  }
   240         -  list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
   241         -} {1 SQLITE_BUSY_SNAPSHOT}
   242         -do_test 4.1.4 {
   243         -  sqlite3_snapshot_free $snapshot
   244         -  execsql COMMIT
   245         -} {}
   246         -
   247         -do_test 4.2.1 {
   248         -  execsql {
   249         -    INSERT INTO t3 VALUES('s', 'e');
   250         -    INSERT INTO t3 VALUES('n', 't');
          203  +  } {a b c d}
          204  +  do_test $tn.3.0.1 {
          205  +    set snapshot [snapshot_get db main]
          206  +    execsql { COMMIT }
          207  +    execsql { INSERT INTO t2 VALUES('e', 'f'); }
          208  +  } {}
          209  +
          210  +  do_test $tn.3.1 {
          211  +    list [catch {snapshot_open db main $snapshot } msg] $msg
          212  +  } {1 SQLITE_ERROR}
          213  +
          214  +  do_test $tn.3.2.1 {
          215  +    execsql {
          216  +      BEGIN;
          217  +        SELECT * FROM t2;
          218  +    }
          219  +  } {a b c d e f}
          220  +  do_test $tn.3.2.2 {
          221  +    list [catch {snapshot_open db main $snapshot } msg] $msg
          222  +  } {1 SQLITE_ERROR}
          223  +
          224  +  do_test $tn.3.2.3 {
          225  +    execsql {
          226  +      COMMIT;
          227  +      BEGIN;
          228  +        INSERT INTO t2 VALUES('g', 'h');
          229  +    }
          230  +    list [catch {snapshot_open db main $snapshot } msg] $msg
          231  +  } {1 SQLITE_ERROR}
          232  +  do_execsql_test $tn.3.2.4 COMMIT
          233  +
          234  +  do_test $tn.3.3.1 {
          235  +    execsql { PRAGMA journal_mode = DELETE }
          236  +    execsql { BEGIN }
          237  +    list [catch {snapshot_open db main $snapshot } msg] $msg
          238  +  } {1 SQLITE_ERROR}
          239  +
          240  +  do_test $tn.$tn.3.3.2 {
          241  +    snapshot_free $snapshot
          242  +    execsql COMMIT
          243  +  } {}
          244  +
          245  +  #-------------------------------------------------------------------------
          246  +  # Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
          247  +  # no longer exists because the wal file has been checkpointed.
          248  +  #
          249  +  #   1. Reading a snapshot from the middle of a wal file is not possible
          250  +  #      after the wal file has been checkpointed.
          251  +  #
          252  +  #   2. That a snapshot from the end of a wal file can not be read once
          253  +  #      the wal file has been wrapped.
          254  +  #
          255  +  do_execsql_test $tn.4.1.0 {
          256  +    PRAGMA journal_mode = wal;
          257  +    CREATE TABLE t3(i, j);
          258  +    INSERT INTO t3 VALUES('o', 't');
          259  +    INSERT INTO t3 VALUES('t', 'f');
   251    260       BEGIN;
   252    261         SELECT * FROM t3;
   253         -  }
   254         -} {o t t f f s s e n t}
   255         -do_test 4.2.2 {
   256         -  set snapshot [sqlite3_snapshot_get db main]
   257         -  execsql {
   258         -    COMMIT;
   259         -    PRAGMA wal_checkpoint;
   260         -    BEGIN;
   261         -  }
   262         -  sqlite3_snapshot_open db main $snapshot
   263         -  execsql { SELECT * FROM t3 }
   264         -} {o t t f f s s e n t}
   265         -do_test 4.2.3 {
   266         -  execsql {
   267         -    COMMIT;
   268         -    INSERT INTO t3 VALUES('e', 't');
          262  +  } {wal o t t f}
          263  +
          264  +  do_test $tn.4.1.1 {
          265  +    set snapshot [snapshot_get db main]
          266  +    execsql COMMIT
          267  +  } {}
          268  +  do_test $tn.4.1.2 {
          269  +    execsql { 
          270  +      INSERT INTO t3 VALUES('f', 's'); 
          271  +      BEGIN;
          272  +    }
          273  +    snapshot_open db main $snapshot
          274  +    execsql { SELECT * FROM t3 }
          275  +  } {o t t f}
          276  +
          277  +  do_test $tn.4.1.3 {
          278  +    execsql { 
          279  +      COMMIT;
          280  +      PRAGMA wal_checkpoint;
          281  +      BEGIN;
          282  +    }
          283  +    list [catch {snapshot_open db main $snapshot} msg] $msg
          284  +  } {1 SQLITE_BUSY_SNAPSHOT}
          285  +  do_test $tn.4.1.4 {
          286  +    snapshot_free $snapshot
          287  +    execsql COMMIT
          288  +  } {}
          289  +
          290  +  do_test $tn.4.2.1 {
          291  +    execsql {
          292  +      INSERT INTO t3 VALUES('s', 'e');
          293  +      INSERT INTO t3 VALUES('n', 't');
          294  +      BEGIN;
          295  +        SELECT * FROM t3;
          296  +    }
          297  +  } {o t t f f s s e n t}
          298  +  do_test $tn.4.2.2 {
          299  +    set snapshot [snapshot_get db main]
          300  +    execsql {
          301  +      COMMIT;
          302  +      PRAGMA wal_checkpoint;
          303  +      BEGIN;
          304  +    }
          305  +    snapshot_open db main $snapshot
          306  +    execsql { SELECT * FROM t3 }
          307  +  } {o t t f f s s e n t}
          308  +  do_test $tn.4.2.3 {
          309  +    execsql {
          310  +      COMMIT;
          311  +      INSERT INTO t3 VALUES('e', 't');
          312  +      BEGIN;
          313  +    }
          314  +    list [catch {snapshot_open db main $snapshot} msg] $msg
          315  +  } {1 SQLITE_BUSY_SNAPSHOT}
          316  +  do_test $tn.4.2.4 {
          317  +    snapshot_free $snapshot
          318  +  } {}
          319  +
          320  +  #-------------------------------------------------------------------------
          321  +  # Check that SQLITE_BUSY is returned if a checkpoint is running when
          322  +  # sqlite3_snapshot_open() is called.
          323  +  #
          324  +  reset_db
          325  +  db close
          326  +  testvfs tvfs
          327  +  sqlite3 db test.db -vfs tvfs
          328  +
          329  +  do_execsql_test $tn.5.1 {
          330  +    PRAGMA journal_mode = wal;
          331  +    CREATE TABLE x1(x, xx, xxx);
          332  +    INSERT INTO x1 VALUES('z', 'zz', 'zzz');
   269    333       BEGIN;
   270         -  }
   271         -  list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
   272         -} {1 SQLITE_BUSY_SNAPSHOT}
   273         -do_test 4.2.4 {
   274         -  sqlite3_snapshot_free $snapshot
   275         -} {}
   276         -
   277         -#-------------------------------------------------------------------------
   278         -# Check that SQLITE_BUSY is returned if a checkpoint is running when
   279         -# sqlite3_snapshot_open() is called.
   280         -#
   281         -reset_db
   282         -db close
   283         -testvfs tvfs
   284         -sqlite3 db test.db -vfs tvfs
   285         -
   286         -do_execsql_test 5.1 {
   287         -  PRAGMA journal_mode = wal;
   288         -  CREATE TABLE x1(x, xx, xxx);
   289         -  INSERT INTO x1 VALUES('z', 'zz', 'zzz');
   290         -  BEGIN;
   291         -    SELECT * FROM x1;
   292         -} {wal z zz zzz}
   293         -
   294         -do_test 5.2 {
   295         -  set ::snapshot [sqlite3_snapshot_get db main]
   296         -  sqlite3 db2 test.db -vfs tvfs
   297         -  execsql {
   298         -    INSERT INTO x1 VALUES('a', 'aa', 'aaa');
   299         -    COMMIT;
          334  +      SELECT * FROM x1;
          335  +  } {wal z zz zzz}
          336  +
          337  +  do_test $tn.5.2 {
          338  +    set ::snapshot [snapshot_get db main]
          339  +    sqlite3 db2 test.db -vfs tvfs
          340  +    execsql {
          341  +      INSERT INTO x1 VALUES('a', 'aa', 'aaa');
          342  +      COMMIT;
          343  +    }
          344  +  } {}
          345  +
          346  +  set t53 0
          347  +  proc write_callback {args} {
          348  +    do_test $tn.5.3.[incr ::t53] {
          349  +      execsql BEGIN
          350  +      list [catch { snapshot_open db main $::snapshot } msg] $msg
          351  +    } {1 SQLITE_BUSY}
          352  +    catchsql COMMIT
   300    353     }
   301         -} {}
   302    354   
   303         -set t53 0
   304         -proc write_callback {args} {
   305         -  do_test 5.3.[incr ::t53] {
   306         -    execsql BEGIN
   307         -    list [catch { sqlite3_snapshot_open db main $::snapshot } msg] $msg
   308         -  } {1 SQLITE_BUSY}
   309         -  catchsql COMMIT
   310         -}
   311         -
   312         -tvfs filter xWrite
   313         -tvfs script write_callback
   314         -db2 eval { PRAGMA wal_checkpoint }
   315         -db close
   316         -db2 close
   317         -tvfs delete
   318         -sqlite3_snapshot_free $snapshot
   319         -
   320         -#-------------------------------------------------------------------------
   321         -# Test that sqlite3_snapshot_get() may be called immediately after
   322         -# "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
   323         -# be called after opening the db handle and running the script
   324         -# "PRAGMA user_version; BEGIN".
   325         -reset_db
   326         -do_execsql_test 6.1 {
   327         -  PRAGMA journal_mode = wal;
   328         -  CREATE TABLE x1(x, xx, xxx);
   329         -  INSERT INTO x1 VALUES('z', 'zz', 'zzz');
   330         -  BEGIN;
   331         -    PRAGMA user_version;
   332         -} {wal 0}
   333         -do_test 6.2 {
   334         -  set ::snapshot [sqlite3_snapshot_get db main]
   335         -  execsql {
   336         -    INSERT INTO x1 VALUES('a', 'aa', 'aaa');
   337         -    COMMIT;
   338         -  }
   339         -} {}
   340         -do_test 6.3 {
   341         -  sqlite3 db2 test.db 
   342         -  db2 eval "PRAGMA user_version ; BEGIN"
   343         -  sqlite3_snapshot_open db2 main $::snapshot
   344         -  db2 eval { SELECT * FROM x1 }
   345         -} {z zz zzz}
   346         -do_test 6.4 {
          355  +  tvfs filter xWrite
          356  +  tvfs script write_callback
          357  +  db2 eval { PRAGMA wal_checkpoint }
          358  +  db close
   347    359     db2 close
   348         -  sqlite3 db2 test.db 
   349         -  db2 eval "PRAGMA application_id"
   350         -  db2 eval "BEGIN"
   351         -  sqlite3_snapshot_open db2 main $::snapshot
   352         -  db2 eval { SELECT * FROM x1 }
   353         -} {z zz zzz}
   354         -
   355         -do_test 6.5 {
   356         -  db2 close
   357         -  sqlite3 db2 test.db 
   358         -  db2 eval "BEGIN"
   359         -  list [catch {sqlite3_snapshot_open db2 main $::snapshot} msg] $msg
   360         -} {1 SQLITE_ERROR}
          360  +  tvfs delete
          361  +  snapshot_free $snapshot
          362  +
          363  +  #-------------------------------------------------------------------------
          364  +  # Test that sqlite3_snapshot_get() may be called immediately after
          365  +  # "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
          366  +  # be called after opening the db handle and running the script
          367  +  # "PRAGMA user_version; BEGIN".
          368  +  reset_db
          369  +  do_execsql_test $tn.6.1 {
          370  +    PRAGMA journal_mode = wal;
          371  +    CREATE TABLE x1(x, xx, xxx);
          372  +    INSERT INTO x1 VALUES('z', 'zz', 'zzz');
          373  +    BEGIN;
          374  +      PRAGMA user_version;
          375  +  } {wal 0}
          376  +  do_test $tn.6.2 {
          377  +    set ::snapshot [snapshot_get db main]
          378  +    execsql {
          379  +      INSERT INTO x1 VALUES('a', 'aa', 'aaa');
          380  +      COMMIT;
          381  +    }
          382  +  } {}
          383  +  do_test $tn.6.3 {
          384  +    sqlite3 db2 test.db 
          385  +    db2 eval "PRAGMA user_version ; BEGIN"
          386  +    snapshot_open db2 main $::snapshot
          387  +    db2 eval { SELECT * FROM x1 }
          388  +  } {z zz zzz}
          389  +  do_test $tn.6.4 {
          390  +    db2 close
          391  +    sqlite3 db2 test.db 
          392  +    db2 eval "PRAGMA application_id"
          393  +    db2 eval "BEGIN"
          394  +    snapshot_open db2 main $::snapshot
          395  +    db2 eval { SELECT * FROM x1 }
          396  +  } {z zz zzz}
          397  +
          398  +  do_test $tn.6.5 {
          399  +    db2 close
          400  +    sqlite3 db2 test.db 
          401  +    db2 eval "BEGIN"
          402  +    list [catch {snapshot_open db2 main $::snapshot} msg] $msg
          403  +  } {1 SQLITE_ERROR}
   361    404   
   362         -sqlite3_snapshot_free $snapshot
          405  +  snapshot_free $snapshot
   363    406   
   364         -#-------------------------------------------------------------------------
   365         -# The following tests investigate the sqlite3_snapshot_cmp() API.
   366         -#
   367         -
   368         -# Compare snapshots $p1 and $p2, checking that the result is $r.
   369         -#
   370         -proc do_snapshot_cmp_test {tn p1 p2 r} {
   371         -  uplevel [list do_test $tn.1 [list sqlite3_snapshot_cmp $p1 $p2] $r]
   372         -  uplevel [list do_test $tn.2 [list sqlite3_snapshot_cmp $p2 $p1] [expr $r*-1]]
   373         -  uplevel [list do_test $tn.3 [list sqlite3_snapshot_cmp $p1 $p1] 0]
   374         -  uplevel [list do_test $tn.4 [list sqlite3_snapshot_cmp $p2 $p2] 0]
   375         -}
          407  +  #-------------------------------------------------------------------------
          408  +  # The following tests investigate the sqlite3_snapshot_cmp() API.
          409  +  #
   376    410   
   377         -catch { db2 close }
   378         -reset_db
   379         -
   380         -do_execsql_test 7.1 {
   381         -  PRAGMA journal_mode = wal;
   382         -  CREATE TABLE t1(x);
   383         -} wal
   384         -
   385         -do_test 7.1.2 {
   386         -  execsql { BEGIN ; PRAGMA application_id }
   387         -  set p1 [sqlite3_snapshot_get db main]
   388         -  execsql {
   389         -    INSERT INTO t1 VALUES(10);
   390         -    COMMIT;
          411  +  # Compare snapshots $p1 and $p2, checking that the result is $r.
          412  +  #
          413  +  proc do_snapshot_cmp_test {tn p1 p2 r} {
          414  +    uplevel [list do_test $tn.1 [list snapshot_cmp $p1 $p2] $r]
          415  +    uplevel [list do_test $tn.2 [list snapshot_cmp $p2 $p1] [expr $r*-1]]
          416  +    uplevel [list do_test $tn.3 [list snapshot_cmp $p1 $p1] 0]
          417  +    uplevel [list do_test $tn.4 [list snapshot_cmp $p2 $p2] 0]
   391    418     }
   392         -  execsql { BEGIN ; PRAGMA application_id }
   393         -  set p2 [sqlite3_snapshot_get db main]
   394         -  execsql COMMIT
   395         -} {}
          419  +
          420  +  catch { db2 close }
          421  +  reset_db
          422  +
          423  +  do_execsql_test $tn.7.1 {
          424  +    PRAGMA journal_mode = wal;
          425  +    CREATE TABLE t1(x);
          426  +  } wal
          427  +
          428  +  do_test $tn.7.1.2 {
          429  +    execsql { BEGIN ; PRAGMA application_id }
          430  +    set p1 [snapshot_get db main]
          431  +    execsql {
          432  +      INSERT INTO t1 VALUES(10);
          433  +      COMMIT;
          434  +    }
          435  +    execsql { BEGIN ; PRAGMA application_id }
          436  +    set p2 [snapshot_get db main]
          437  +    execsql COMMIT
          438  +  } {}
   396    439   
   397         -do_snapshot_cmp_test 7.1.3 $p1 $p2 -1
   398         -sqlite3_snapshot_free $p1
   399         -sqlite3_snapshot_free $p2
          440  +  do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1
          441  +  snapshot_free $p1
          442  +  snapshot_free $p2
   400    443   
   401         -do_execsql_test 7.2.1 {
   402         -  INSERT INTO t1 VALUES(11);
   403         -  INSERT INTO t1 VALUES(12);
   404         -  INSERT INTO t1 VALUES(13);
   405         -  BEGIN; 
   406         -    PRAGMA application_id;
   407         -} {0}
   408         -do_test 7.2.2 {
   409         -  set p1 [sqlite3_snapshot_get db main]
   410         -  execsql {
   411         -    COMMIT;
   412         -    INSERT INTO t1 VALUES(14);
   413         -    PRAGMA wal_checkpoint;
   414         -    BEGIN;
          444  +  do_execsql_test $tn.7.2.1 {
          445  +    INSERT INTO t1 VALUES(11);
          446  +    INSERT INTO t1 VALUES(12);
          447  +    INSERT INTO t1 VALUES(13);
          448  +    BEGIN; 
   415    449         PRAGMA application_id;
   416         -  }
   417         -  set p2 [sqlite3_snapshot_get db main]
   418         -  execsql COMMIT
   419         -} {}
          450  +  } {0}
          451  +  do_test $tn.7.2.2 {
          452  +    set p1 [snapshot_get db main]
          453  +    execsql {
          454  +      COMMIT;
          455  +      INSERT INTO t1 VALUES(14);
          456  +      PRAGMA wal_checkpoint;
          457  +      BEGIN;
          458  +        PRAGMA application_id;
          459  +    }
          460  +    set p2 [snapshot_get db main]
          461  +    execsql COMMIT
          462  +  } {}
   420    463   
   421         -do_snapshot_cmp_test 7.2.3 $p1 $p2 -1
   422         -sqlite3_snapshot_free $p2
          464  +  do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1
          465  +  snapshot_free $p2
   423    466   
   424         -do_test 7.3.1 {
   425         -  execsql {
   426         -    INSERT INTO t1 VALUES(14);
   427         -    BEGIN;
   428         -      PRAGMA application_id;
   429         -  }
   430         -  set p2 [sqlite3_snapshot_get db main]
   431         -  execsql COMMIT
   432         -} {}
          467  +  do_test $tn.7.3.1 {
          468  +    execsql {
          469  +      INSERT INTO t1 VALUES(14);
          470  +      BEGIN;
          471  +        PRAGMA application_id;
          472  +    }
          473  +    set p2 [snapshot_get db main]
          474  +    execsql COMMIT
          475  +  } {}
   433    476   
   434         -do_snapshot_cmp_test 7.3.2 $p1 $p2 -1
   435         -sqlite3_snapshot_free $p1
   436         -sqlite3_snapshot_free $p2
          477  +  do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1
          478  +  snapshot_free $p1
          479  +  snapshot_free $p2
          480  +}
   437    481   
   438    482   finish_test

Added test/snapshot2.test.

            1  +# 2016 November 18
            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  +# This file implements regression tests for SQLite library. The focus
           12  +# of this file is the sqlite3_snapshot_xxx() APIs.
           13  +#
           14  +
           15  +set testdir [file dirname $argv0]
           16  +source $testdir/tester.tcl
           17  +ifcapable !snapshot {finish_test; return}
           18  +set testprefix snapshot2
           19  +
           20  +# This test does not work with the inmemory_journal permutation. The reason
           21  +# is that each connection opened as part of this permutation executes
           22  +# "PRAGMA journal_mode=memory", which fails if the database is in wal mode
           23  +# and there are one or more existing connections.
           24  +if {[permutation]=="inmemory_journal"} {
           25  +  finish_test
           26  +  return
           27  +}
           28  +
           29  +#-------------------------------------------------------------------------
           30  +# Check that it is not possible to obtain a snapshot immediately after
           31  +# a wal mode database with an empty wal file is opened. But it is after
           32  +# the file has been written, even by some other connection.
           33  +#
           34  +do_execsql_test 1.0 {
           35  +  PRAGMA journal_mode = wal;
           36  +  CREATE TABLE t1(a, b, c);
           37  +  INSERT INTO t1 VALUES(1, 2, 3);
           38  +  INSERT INTO t1 VALUES(4, 5, 6);
           39  +} {wal}
           40  +
           41  +db close
           42  +do_test 1.1.1 { list [file exists test.db] [file exists test.db-wal] } {1 0}
           43  +
           44  +sqlite3 db test.db
           45  +do_execsql_test 1.1.2 { SELECT * FROM t1 } {1 2 3 4 5 6}
           46  +
           47  +do_test 1.1.3 {
           48  +  execsql BEGIN
           49  +  list [catch { sqlite3_snapshot_get_blob db main } msg] $msg
           50  +} {1 SQLITE_ERROR}
           51  +execsql COMMIT
           52  +
           53  +do_test 1.1.4 {
           54  +  execsql { INSERT INTO t1 VALUES(7, 8, 9) }
           55  +  execsql BEGIN
           56  +  string length [sqlite3_snapshot_get_blob db main]
           57  +} 48
           58  +execsql COMMIT
           59  +
           60  +db close
           61  +do_test 1.2.1 { list [file exists test.db] [file exists test.db-wal] } {1 0}
           62  +
           63  +sqlite3 db test.db
           64  +do_execsql_test 1.2.2 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8 9}
           65  +
           66  +do_test 1.2.3 {
           67  +  execsql BEGIN
           68  +  list [catch { sqlite3_snapshot_get_blob db main } msg] $msg
           69  +} {1 SQLITE_ERROR}
           70  +execsql COMMIT
           71  +
           72  +do_test 1.2.4 {
           73  +  sqlite3 db2 test.db
           74  +  execsql { INSERT INTO t1 VALUES(10, 11, 12) } db2
           75  +  execsql BEGIN
           76  +  string length [sqlite3_snapshot_get_blob db main]
           77  +} 48
           78  +execsql COMMIT
           79  +db2 close
           80  +
           81  +#-------------------------------------------------------------------------
           82  +# Simple tests for sqlite3_snapshot_recover().
           83  +#
           84  +reset_db
           85  +do_execsql_test 2.0 {
           86  +  CREATE TABLE t1(x);
           87  +  PRAGMA journal_mode = wal;
           88  +  INSERT INTO t1 VALUES(1);
           89  +  INSERT INTO t1 VALUES(2);
           90  +} {wal}
           91  +
           92  +do_test 2.1 {
           93  +  db trans { set snap [sqlite3_snapshot_get_blob db main] }
           94  +  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
           95  +  db close
           96  +  sqlite3 db test.db
           97  +
           98  +  execsql {SELECT * FROM sqlite_master}
           99  +  execsql BEGIN
          100  +  sqlite3_snapshot_open_blob db main $snap
          101  +  execsql COMMIT;
          102  +  execsql { INSERT INTO t1 VALUES(3); }
          103  +} {}
          104  +
          105  +do_test 2.2 {
          106  +  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
          107  +  db close
          108  +  sqlite3 db test.db
          109  +
          110  +  execsql {SELECT * FROM sqlite_master}
          111  +  execsql BEGIN
          112  +  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
          113  +} {1 SQLITE_BUSY_SNAPSHOT}
          114  +
          115  +do_test 2.3 {
          116  +  execsql COMMIT
          117  +  sqlite3_snapshot_recover db main
          118  +  execsql BEGIN
          119  +  sqlite3_snapshot_open_blob db main $snap
          120  +  execsql { SELECT * FROM t1 }
          121  +} {1 2}
          122  +
          123  +do_test 2.4 {
          124  +  execsql COMMIT
          125  +  execsql { SELECT * FROM t1 }
          126  +} {1 2 3}
          127  +
          128  +do_test 2.5 {
          129  +  execsql { PRAGMA wal_checkpoint }
          130  +  sqlite3_db_config db NO_CKPT_ON_CLOSE 1
          131  +  db close
          132  +  sqlite3 db test.db
          133  +
          134  +  sqlite3_snapshot_recover db main
          135  +  execsql BEGIN
          136  +  list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
          137  +} {1 SQLITE_BUSY_SNAPSHOT}
          138  +
          139  +#-------------------------------------------------------------------------
          140  +# Check that calling sqlite3_snapshot_recover() does not confuse the
          141  +# pager cache.
          142  +reset_db
          143  +do_execsql_test 3.0 {
          144  +  PRAGMA journal_mode = wal;
          145  +  CREATE TABLE t1(x, y);
          146  +  INSERT INTO t1 VALUES('a', 'b');
          147  +  INSERT INTO t1 VALUES('c', 'd');
          148  +} {wal}
          149  +do_test 3.1 {
          150  +  sqlite3 db2 test.db
          151  +  execsql { INSERT INTO t1 VALUES('e', 'f') } db2
          152  +  db2 close
          153  +  sqlite3_snapshot_recover db main
          154  +} {}
          155  +do_execsql_test 3.2 {
          156  +  SELECT * FROM t1;
          157  +} {a b c d e f}
          158  +
          159  +#-------------------------------------------------------------------------
          160  +# Check that sqlite3_snapshot_recover() returns an error if it is called
          161  +# with an open read-transaction. Or on a database that does not exist. Or
          162  +# on the temp database. Or on a db that is not in wal mode.
          163  +#
          164  +do_test 4.1 {
          165  +  sqlite3_snapshot_recover db main
          166  +} {}
          167  +do_test 4.2 {
          168  +  execsql {
          169  +    BEGIN;
          170  +      SELECT * FROM sqlite_master;
          171  +  }
          172  +  list [catch { sqlite3_snapshot_recover db main } msg] $msg
          173  +} {1 SQLITE_ERROR}
          174  +do_test 4.3 {
          175  +  execsql COMMIT
          176  +  sqlite3_snapshot_recover db main
          177  +} {}
          178  +do_test 4.4 {
          179  +  list [catch { sqlite3_snapshot_recover db aux } msg] $msg
          180  +} {1 SQLITE_ERROR}
          181  +do_test 4.5 {
          182  +  forcedelete test.db2
          183  +  execsql {
          184  +    ATTACH 'test.db2' AS aux;
          185  +    PRAGMA aux.journal_mode = wal;
          186  +    CREATE TABLE aux.t2(x, y);
          187  +  }
          188  +  list [catch { sqlite3_snapshot_recover db aux } msg] $msg
          189  +} {0 {}}
          190  +do_test 4.6 {
          191  +  list [catch { sqlite3_snapshot_recover db temp } msg] $msg
          192  +} {1 SQLITE_ERROR}
          193  +do_test 4.7 {
          194  +  execsql {
          195  +    PRAGMA aux.journal_mode = delete;
          196  +  }
          197  +  list [catch { sqlite3_snapshot_recover db aux } msg] $msg
          198  +} {1 SQLITE_ERROR}
          199  +
          200  +finish_test
          201  +
          202  +

Changes to test/snapshot_fault.test.

   154    154         PRAGMA integrity_check;
   155    155       }]
   156    156       if {$res != "1 2 3 ok"} { error "res is $res" }
   157    157     }
   158    158   
   159    159     sqlite3_snapshot_free $::snapshot
   160    160   }
          161  +
          162  +#-------------------------------------------------------------------------
          163  +# Test the handling of faults that occur within sqlite3_snapshot_recover().
          164  +#
          165  +reset_db
          166  +do_execsql_test 4.0 {
          167  +  PRAGMA journal_mode = wal;
          168  +  CREATE TABLE t1(zzz);
          169  +  INSERT INTO t1 VALUES('abc');
          170  +  INSERT INTO t1 VALUES('def');
          171  +} {wal}
          172  +faultsim_save_and_close
          173  +
          174  +do_test 4.0.1 {
          175  +  faultsim_restore_and_reopen
          176  +  db eval { SELECT * FROM sqlite_master } 
          177  +  sqlite3_snapshot_recover db main
          178  +} {}
          179  +db close
          180  +
          181  +do_faultsim_test 4.0 -faults oom* -prep {
          182  +  faultsim_restore_and_reopen
          183  +  db eval { SELECT * FROM sqlite_master } 
          184  +} -body {
          185  +  sqlite3_snapshot_recover db main
          186  +} -test {
          187  +  faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
          188  +}
          189  +
          190  +# The following test cases contrive to call sqlite3_snapshot_recover()
          191  +# before all pages of the *-shm file have been mapped. This tests an
          192  +# extra branch of error handling logic in snapshot_recover().
          193  +#
          194  +reset_db
          195  +do_execsql_test 4.1.0 {
          196  +  PRAGMA page_size = 512;
          197  +  PRAGMA journal_mode = wal;
          198  +  PRAGMA wal_autocheckpoint = 0;
          199  +  CREATE TABLE t1(zzz);
          200  +  INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
          201  +  PRAGMA user_version = 211;
          202  +} {wal 0}
          203  +
          204  +do_test 4.1.1 {
          205  +  list [file size test.db-shm] [file size test.db]
          206  +} {98304 512}
          207  +
          208  +faultsim_save_and_close
          209  +do_faultsim_test 4.1 -faults shm* -prep {
          210  +  catch { db2 close } 
          211  +  catch { db close } 
          212  +  faultsim_restore_and_reopen
          213  +  sqlite3 db2 test.db
          214  +  db2 eval { SELECT * FROM sqlite_master } 
          215  +  db eval BEGIN
          216  +  sqlite3_snapshot_get_blob db main
          217  +  db eval COMMIT
          218  +} -body {
          219  +  sqlite3_snapshot_recover db main
          220  +} -test {
          221  +  faultsim_test_result {0 {}} {1 SQLITE_IOERR}
          222  +}
   161    223   
   162    224   
   163    225   
   164    226   finish_test