Index: src/sqlite.h.in ================================================================== --- src/sqlite.h.in +++ src/sqlite.h.in @@ -8125,10 +8125,37 @@ ** ** The [sqlite3_snapshot_free()] interface is only available when the ** SQLITE_ENABLE_SNAPSHOT compile-time option is used. */ SQLITE_EXPERIMENTAL void sqlite3_snapshot_free(sqlite3_snapshot*); + +/* +** CAPI3REF: Compare the ages of two snapshot handles. +** EXPERIMENTAL +** +** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages +** of two valid snapshot handles. +** +** If the two snapshot handles are not associated with the same database +** file, the result of the comparison is undefined. +** +** Additionally, the result of the comparison is only valid if both of the +** snapshot handles were obtained by calling sqlite3_snapshot_get() since the +** last time the wal file was deleted. The wal file is deleted when the +** database is changed back to rollback mode or when the number of database +** clients drops to zero. If either snapshot handle was obtained before the +** wal file was last deleted, the value returned by this function +** is undefined. +** +** Otherwise, this API returns a negative value if P1 refers to an older +** snapshot than P2, zero if the two handles refer to the same database +** snapshot, and a positive value if P1 is a newer snapshot than P2. +*/ +SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp( + sqlite3_snapshot *p1, + sqlite3_snapshot *p2 +); /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ Index: src/test1.c ================================================================== --- src/test1.c +++ src/test1.c @@ -2357,10 +2357,35 @@ sqlite3_snapshot_free(pSnapshot); return TCL_OK; } #endif /* SQLITE_ENABLE_SNAPSHOT */ +#ifdef SQLITE_ENABLE_SNAPSHOT +/* +** Usage: sqlite3_snapshot_cmp SNAPSHOT1 SNAPSHOT2 +*/ +static int test_snapshot_cmp( + void * clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *CONST objv[] +){ + int res; + sqlite3_snapshot *p1; + sqlite3_snapshot *p2; + if( objc!=3 ){ + Tcl_WrongNumArgs(interp, 1, objv, "SNAPSHOT1 SNAPSHOT2"); + return TCL_ERROR; + } + p1 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[1])); + p2 = (sqlite3_snapshot*)sqlite3TestTextToPtr(Tcl_GetString(objv[2])); + res = sqlite3_snapshot_cmp(p1, p2); + Tcl_SetObjResult(interp, Tcl_NewIntObj(res)); + return TCL_OK; +} +#endif /* SQLITE_ENABLE_SNAPSHOT */ + /* ** Usage: sqlite3_next_stmt DB STMT ** ** Return the next statment in sequence after STMT. */ @@ -7247,10 +7272,11 @@ { "vfs_current_time_int64", vfsCurrentTimeInt64, 0 }, #ifdef SQLITE_ENABLE_SNAPSHOT { "sqlite3_snapshot_get", test_snapshot_get, 0 }, { "sqlite3_snapshot_open", test_snapshot_open, 0 }, { "sqlite3_snapshot_free", test_snapshot_free, 0 }, + { "sqlite3_snapshot_cmp", test_snapshot_cmp, 0 }, #endif }; static int bitmask_size = sizeof(Bitmask)*8; static int longdouble_size = sizeof(LONGDOUBLE_TYPE); int i; Index: src/wal.c ================================================================== --- src/wal.c +++ src/wal.c @@ -3397,10 +3397,27 @@ /* Try to open on pSnapshot when the next read-transaction starts */ void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){ pWal->pSnapshot = (WalIndexHdr*)pSnapshot; } + +/* +** Return a +ve value if snapshot p1 is newer than p2. A -ve value if +** p1 is older than p2 and zero if p1 and p2 are the same snapshot. +*/ +int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){ + WalIndexHdr *pHdr1 = (WalIndexHdr*)p1; + WalIndexHdr *pHdr2 = (WalIndexHdr*)p2; + + /* aSalt[0] is a copy of the value stored in the wal file header. It + ** is incremented each time the wal file is restarted. */ + if( pHdr1->aSalt[0]aSalt[0] ) return -1; + if( pHdr1->aSalt[0]>pHdr2->aSalt[0] ) return +1; + if( pHdr1->mxFramemxFrame ) return -1; + if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1; + return 0; +} #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_ZIPVFS /* ** If the argument is not NULL, it points to a Wal object that holds a Index: test/snapshot.test ================================================================== --- test/snapshot.test +++ test/snapshot.test @@ -362,7 +362,81 @@ db2 eval "BEGIN" list [catch {sqlite3_snapshot_open db2 main $::snapshot} msg] $msg } {1 SQLITE_ERROR} sqlite3_snapshot_free $snapshot + +#------------------------------------------------------------------------- +# The following tests investigate the sqlite3_snapshot_cmp() API. +# + +# Compare snapshots $p1 and $p2, checking that the result is $r. +# +proc do_snapshot_cmp_test {tn p1 p2 r} { + uplevel [list do_test $tn.1 [list sqlite3_snapshot_cmp $p1 $p2] $r] + uplevel [list do_test $tn.2 [list sqlite3_snapshot_cmp $p2 $p1] [expr $r*-1]] + uplevel [list do_test $tn.3 [list sqlite3_snapshot_cmp $p1 $p1] 0] + uplevel [list do_test $tn.4 [list sqlite3_snapshot_cmp $p2 $p2] 0] +} + +catch { db2 close } +reset_db + +do_execsql_test 7.1 { + PRAGMA journal_mode = wal; + CREATE TABLE t1(x); +} wal + +do_test 7.1.2 { + execsql { BEGIN ; PRAGMA application_id } + set p1 [sqlite3_snapshot_get db main] + execsql { + INSERT INTO t1 VALUES(10); + COMMIT; + } + execsql { BEGIN ; PRAGMA application_id } + set p2 [sqlite3_snapshot_get db main] + execsql COMMIT +} {} + +do_snapshot_cmp_test 7.1.3 $p1 $p2 -1 +sqlite3_snapshot_free $p1 +sqlite3_snapshot_free $p2 + +do_execsql_test 7.2.1 { + INSERT INTO t1 VALUES(11); + INSERT INTO t1 VALUES(12); + INSERT INTO t1 VALUES(13); + BEGIN; + PRAGMA application_id; +} {0} +do_test 7.2.2 { + set p1 [sqlite3_snapshot_get db main] + execsql { + COMMIT; + INSERT INTO t1 VALUES(14); + PRAGMA wal_checkpoint; + BEGIN; + PRAGMA application_id; + } + set p2 [sqlite3_snapshot_get db main] + execsql COMMIT +} {} + +do_snapshot_cmp_test 7.2.3 $p1 $p2 -1 +sqlite3_snapshot_free $p2 + +do_test 7.3.1 { + execsql { + INSERT INTO t1 VALUES(14); + BEGIN; + PRAGMA application_id; + } + set p2 [sqlite3_snapshot_get db main] + execsql COMMIT +} {} + +do_snapshot_cmp_test 7.3.2 $p1 $p2 -1 +sqlite3_snapshot_free $p1 +sqlite3_snapshot_free $p2 finish_test