aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authordan <dan@noemail.net>2016-11-18 20:49:43 +0000
committerdan <dan@noemail.net>2016-11-18 20:49:43 +0000
commit1158498dce3f73e3666c06f7067f69f06fe14fb4 (patch)
tree9b214dbeb222bd04a096b41be56923a4eddd63f0 /src
parentedace5d4f16897f813564259afe5200acd03ce0d (diff)
downloadsqlite-1158498dce3f73e3666c06f7067f69f06fe14fb4.tar.gz
sqlite-1158498dce3f73e3666c06f7067f69f06fe14fb4.zip
Add experimental sqlite3_snapshot_recover() API.
FossilOrigin-Name: 174a6076a8d7bebe5efebf55f3fdc5d87c589cc7
Diffstat (limited to 'src')
-rw-r--r--src/main.c28
-rw-r--r--src/pager.c14
-rw-r--r--src/pager.h1
-rw-r--r--src/sqlite.h.in6
-rw-r--r--src/test1.c33
-rw-r--r--src/wal.c65
-rw-r--r--src/wal.h1
7 files changed, 147 insertions, 1 deletions
diff --git a/src/main.c b/src/main.c
index 29e166447..c0494135c 100644
--- a/src/main.c
+++ b/src/main.c
@@ -4045,6 +4045,34 @@ int sqlite3_snapshot_open(
}
/*
+** Recover as many snapshots as possible from the wal file associated with
+** schema zDb of database db.
+*/
+int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb){
+ int rc = SQLITE_ERROR;
+ int iDb;
+#ifndef SQLITE_OMIT_WAL
+
+#ifdef SQLITE_ENABLE_API_ARMOR
+ if( !sqlite3SafetyCheckOk(db) ){
+ return SQLITE_MISUSE_BKPT;
+ }
+#endif
+
+ sqlite3_mutex_enter(db->mutex);
+ iDb = sqlite3FindDbName(db, zDb);
+ if( iDb==0 || iDb>1 ){
+ Btree *pBt = db->aDb[iDb].pBt;
+ if( 0==sqlite3BtreeIsInReadTrans(pBt) ){
+ rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
+ }
+ }
+ sqlite3_mutex_leave(db->mutex);
+#endif /* SQLITE_OMIT_WAL */
+ return rc;
+}
+
+/*
** Free a snapshot handle obtained from sqlite3_snapshot_get().
*/
void sqlite3_snapshot_free(sqlite3_snapshot *pSnapshot){
diff --git a/src/pager.c b/src/pager.c
index 04ce19547..9975f3fcb 100644
--- a/src/pager.c
+++ b/src/pager.c
@@ -7405,6 +7405,20 @@ int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
}
return rc;
}
+
+/*
+** If this is a WAL database, call sqlite3WalSnapshotRecover(). If this
+** is not a WAL database, return an error.
+*/
+int sqlite3PagerSnapshotRecover(Pager *pPager){
+ int rc;
+ if( pPager->pWal ){
+ rc = sqlite3WalSnapshotRecover(pPager->pWal);
+ }else{
+ rc = SQLITE_ERROR;
+ }
+ return rc;
+}
#endif /* SQLITE_ENABLE_SNAPSHOT */
#endif /* !SQLITE_OMIT_WAL */
diff --git a/src/pager.h b/src/pager.h
index 3003c21ec..dd57f598b 100644
--- a/src/pager.h
+++ b/src/pager.h
@@ -182,6 +182,7 @@ int sqlite3PagerSharedLock(Pager *pPager);
# ifdef SQLITE_ENABLE_SNAPSHOT
int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
+ int sqlite3PagerSnapshotRecover(Pager *pPager);
# endif
#else
# define sqlite3PagerUseWal(x) 0
diff --git a/src/sqlite.h.in b/src/sqlite.h.in
index 8f3b40292..9c921b8c9 100644
--- a/src/sqlite.h.in
+++ b/src/sqlite.h.in
@@ -8415,6 +8415,12 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
);
/*
+** CAPI3REF: Recover snapshots from a wal file
+** EXPERIMENTAL
+*/
+SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb);
+
+/*
** Undo the hack that converts floating point types to integer for
** builds on processors without floating point support.
*/
diff --git a/src/test1.c b/src/test1.c
index e31ad95f4..de16246b5 100644
--- a/src/test1.c
+++ b/src/test1.c
@@ -2312,6 +2312,38 @@ static int SQLITE_TCLAPI test_snapshot_get(
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
+** Usage: sqlite3_snapshot_recover DB DBNAME
+*/
+static int SQLITE_TCLAPI test_snapshot_recover(
+ void * clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *CONST objv[]
+){
+ int rc;
+ sqlite3 *db;
+ char *zName;
+
+ if( objc!=3 ){
+ Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME");
+ return TCL_ERROR;
+ }
+ if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
+ zName = Tcl_GetString(objv[2]);
+
+ rc = sqlite3_snapshot_recover(db, zName);
+ if( rc!=SQLITE_OK ){
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
+ return TCL_ERROR;
+ }else{
+ Tcl_ResetResult(interp);
+ }
+ return TCL_OK;
+}
+#endif /* SQLITE_ENABLE_SNAPSHOT */
+
+#ifdef SQLITE_ENABLE_SNAPSHOT
+/*
** Usage: sqlite3_snapshot_open DB DBNAME SNAPSHOT
*/
static int SQLITE_TCLAPI test_snapshot_open(
@@ -7646,6 +7678,7 @@ int Sqlitetest1_Init(Tcl_Interp *interp){
{ "sqlite3_snapshot_open", test_snapshot_open, 0 },
{ "sqlite3_snapshot_free", test_snapshot_free, 0 },
{ "sqlite3_snapshot_cmp", test_snapshot_cmp, 0 },
+ { "sqlite3_snapshot_recover", test_snapshot_recover, 0 },
{ "sqlite3_snapshot_get_blob", test_snapshot_get_blob, 0 },
{ "sqlite3_snapshot_open_blob", test_snapshot_open_blob, 0 },
{ "sqlite3_snapshot_cmp_blob", test_snapshot_cmp_blob, 0 },
diff --git a/src/wal.c b/src/wal.c
index 669c428ef..178278816 100644
--- a/src/wal.c
+++ b/src/wal.c
@@ -2380,6 +2380,65 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
}
/*
+** Recover as many snapshots as possible from the wal file.
+*/
+int sqlite3WalSnapshotRecover(Wal *pWal){
+ int dummy;
+ int rc;
+
+ rc = sqlite3WalBeginReadTransaction(pWal, &dummy);
+ if( rc==SQLITE_OK ){
+ rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
+ if( rc==SQLITE_OK ){
+ volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
+ int szPage = (int)pWal->szPage;
+ void *pBuf1 = sqlite3_malloc(szPage);
+ void *pBuf2 = sqlite3_malloc(szPage);
+
+ if( pBuf1==0 || pBuf2==0 ){
+ rc = SQLITE_NOMEM;
+ }else{
+ u32 i = pInfo->nBackfillAttempted;
+ for(i=pInfo->nBackfillAttempted; i>pInfo->nBackfill; i--){
+ volatile ht_slot *dummy;
+ volatile u32 *aPgno; /* Array of page numbers */
+ u32 iZero; /* Frame corresponding to aPgno[0] */
+ u32 pgno; /* Page number in db file */
+ i64 iDbOff; /* Offset of db file entry */
+ i64 iWalOff; /* Offset of wal file entry */
+ rc = walHashGet(pWal, walFramePage(i), &dummy, &aPgno, &iZero);
+
+ if( rc==SQLITE_OK ){
+ pgno = aPgno[i-iZero];
+ iDbOff = (i64)(pgno-1) * szPage;
+ iWalOff = walFrameOffset(i, szPage) + WAL_FRAME_HDRSIZE;
+ rc = sqlite3OsRead(pWal->pWalFd, pBuf1, szPage, iWalOff);
+ }
+
+ if( rc==SQLITE_OK ){
+ rc = sqlite3OsRead(pWal->pDbFd, pBuf2, szPage, iDbOff);
+ }
+
+ if( rc!=SQLITE_OK || 0==memcmp(pBuf1, pBuf2, szPage) ){
+ break;
+ }
+
+ pInfo->nBackfillAttempted = i-1;
+ }
+ }
+
+ sqlite3_free(pBuf1);
+ sqlite3_free(pBuf2);
+ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
+ }
+
+ sqlite3WalEndReadTransaction(pWal);
+ }
+
+ return rc;
+}
+
+/*
** Begin a read transaction on the database.
**
** This routine used to be called sqlite3OpenSnapshot() and with good reason:
@@ -2441,7 +2500,11 @@ int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
** has not yet set the pInfo->nBackfillAttempted variable to indicate
** its intent. To avoid the race condition this leads to, ensure that
** there is no checkpointer process by taking a shared CKPT lock
- ** before checking pInfo->nBackfillAttempted. */
+ ** before checking pInfo->nBackfillAttempted.
+ **
+ ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
+ ** this already?
+ */
rc = walLockShared(pWal, WAL_CKPT_LOCK);
if( rc==SQLITE_OK ){
diff --git a/src/wal.h b/src/wal.h
index 16d9d6e0d..4f6d01dad 100644
--- a/src/wal.h
+++ b/src/wal.h
@@ -131,6 +131,7 @@ int sqlite3WalHeapMemory(Wal *pWal);
#ifdef SQLITE_ENABLE_SNAPSHOT
int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot);
void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot);
+int sqlite3WalSnapshotRecover(Wal *pWal);
#endif
#ifdef SQLITE_ENABLE_ZIPVFS