Add the sqlite3_snapshot_recover() interface and related functionality.
FossilOrigin-Name: b70c85ce6d07b08e13f4ea99edb8f4855a6d7580
This commit is contained in:
commit
dd5fa17d3c
30
manifest
30
manifest
@ -1,5 +1,5 @@
|
||||
C Clarification\sof\sthe\sbehavior\sof\ssqlite3_stmt_readonly()\son\sBEGIN\sIMMEDIATE\nstatements.\s\sThe\sis\scomment\schanges\sonly\s-\sno\schanges\sto\scode.
|
||||
D 2016-11-25T20:20:40.663
|
||||
C Add\sthe\ssqlite3_snapshot_recover()\sinterface\sand\srelated\sfunctionality.
|
||||
D 2016-11-26T19:44:40.853
|
||||
F Makefile.in 6b572807415d3f0a379cebc9461416d8df4a12c8
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc bb4d970894abbbe0e88d00aac29bd52af8bc95f4
|
||||
@ -352,7 +352,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
|
||||
F src/insert.c 2f8d2c1fa472409625cecacddd39a3b1c6811a26
|
||||
F src/legacy.c 75d3023be8f0d2b99d60f905090341a03358c58e
|
||||
F src/loadext.c 5d6642d141c07d366e43d359e94ec9de47add41d
|
||||
F src/main.c 694ac90557abdaa62151a6090670e107b0f2c2ab
|
||||
F src/main.c 5669ae83c6bd47f090aee26a6b548a882d69e9e1
|
||||
F src/malloc.c 5ee7c2d3dcb1b0a902c9c6d0115deef54736bdfa
|
||||
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
|
||||
F src/mem1.c 6919bcf12f221868ea066eec27e579fed95ce98b
|
||||
@ -374,8 +374,8 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586
|
||||
F src/os_unix.c be9ca0f901a2b6c1bc93dc338f4863675180c189
|
||||
F src/os_win.c cf90abd4e50d9f56d2c20ce8e005aff55d7bd8e9
|
||||
F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
|
||||
F src/pager.c a31e2c25563065ebfc9308f2ba3a061901fd60a8
|
||||
F src/pager.h 07d6938df0b74e4abe8f57807a8b0e1084321d8b
|
||||
F src/pager.c 4e4aea7ced5734753ccbff4cf4bb4d032cf2173e
|
||||
F src/pager.h d1e944291030351f362a0a7da9b5c3e34e603e39
|
||||
F src/parse.y 0338f906b61e311c2b7e11a3f89b0092c780b664
|
||||
F src/pcache.c 5ff2a08f76a9c1b22f43eb063b7068fb085465ac
|
||||
F src/pcache.h 2cedcd8407eb23017d92790b112186886e179490
|
||||
@ -389,7 +389,7 @@ F src/resolve.c bb070cf5f23611c44ab7e4788803684e385fc3fb
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c 668c6865b16813dab5f9c18d3672fed11a5c34c5
|
||||
F src/shell.c a3fc2c719ed6d381895cbdb66a4a9b6a791cb02e
|
||||
F src/sqlite.h.in e30a501ccb8c8bd25b6559c65ba240c6370802ae
|
||||
F src/sqlite.h.in 566e3aa20e40f3eceda8bfb36c72504b4f10b500
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 8648034aa702469afb553231677306cc6492a1ae
|
||||
F src/sqliteInt.h c471d791b10c0f2164c8b7a87adc338e703c09cc
|
||||
@ -397,7 +397,7 @@ F src/sqliteLimit.h c0373387c287c8d0932510b5547ecde31b5da247
|
||||
F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1
|
||||
F src/table.c 5226df15ab9179b9ed558d89575ea0ce37b03fc9
|
||||
F src/tclsqlite.c aef87dcd8cb66564d560ab48d43d19ac812a1eab
|
||||
F src/test1.c f52bed152c97be23be794efa234374d0de593eeb
|
||||
F src/test1.c d6a047ea534fb68fedcb5a47f4db3baef6748294
|
||||
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
|
||||
F src/test3.c d03f5b5da9a2410b7a91c64b0d3306ed28ab6fee
|
||||
F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6
|
||||
@ -465,8 +465,8 @@ F src/vdbesort.c 91fda3909326860382b0ca8aa251e609c6a9d62c
|
||||
F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834
|
||||
F src/vtab.c e02cacb5c7ae742631edeb9ae9f53d399f093fd8
|
||||
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
|
||||
F src/wal.c 7e8557314bcf867ffe3fff1d8ef2d207ace8e065
|
||||
F src/wal.h bf03a23da3100ab25e5c0363450233cfee09cfc2
|
||||
F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344
|
||||
F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
|
||||
F src/walker.c 91a6df7435827e41cff6bb7df50ea00934ee78b0
|
||||
F src/where.c 6bbf9284f4f15a6fa48663d033870cc0d7f5ee66
|
||||
F src/whereInt.h 2bcc3d176e6091cb8f50a30b65c006e88a73614d
|
||||
@ -1102,8 +1102,9 @@ F test/skipscan2.test d1d1450952b7275f0b0a3a981f0230532743951a
|
||||
F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5
|
||||
F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2
|
||||
F test/skipscan6.test 5866039d03a56f5bd0b3d172a012074a1d90a15b
|
||||
F test/snapshot.test a19465046168b4420b5afeed37c3214e42a49f4a
|
||||
F test/snapshot_fault.test 062ff0438a074978d45e9f9a92e7ad459b74ee73
|
||||
F test/snapshot.test 85735bd997a4f6d710140c28fd860519a299649f
|
||||
F test/snapshot2.test eb083df2e617708a4a93d70965f14268d4934120
|
||||
F test/snapshot_fault.test 52c5e97ebd218846a8ae2da4d147d3e77d71f963
|
||||
F test/soak.test 0b5b6375c9f4110c828070b826b3b4b0bb65cd5f
|
||||
F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087
|
||||
F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b
|
||||
@ -1534,7 +1535,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 6ac7b07a4aff2e1a9031289e3dafdb9ac0071c24
|
||||
R 32f39fc3fb2e8643f8b6bcf49fda406b
|
||||
P a4205a83e4ed977a89ecae665604993711f7dd3f e2c4bdf54fc1b9605226c7d6c79ef4f8302d6213
|
||||
R e3a0631511335a83b0a91c37cc46bd7e
|
||||
T +closed e2c4bdf54fc1b9605226c7d6c79ef4f8302d6213
|
||||
U drh
|
||||
Z be231616b2371336e459be64a91b2f84
|
||||
Z 12f51558a308f260b27f24c87a70fdb2
|
||||
|
@ -1 +1 @@
|
||||
a4205a83e4ed977a89ecae665604993711f7dd3f
|
||||
b70c85ce6d07b08e13f4ea99edb8f4855a6d7580
|
49
src/main.c
49
src/main.c
@ -3981,7 +3981,6 @@ int sqlite3_snapshot_get(
|
||||
){
|
||||
int rc = SQLITE_ERROR;
|
||||
#ifndef SQLITE_OMIT_WAL
|
||||
int iDb;
|
||||
|
||||
#ifdef SQLITE_ENABLE_API_ARMOR
|
||||
if( !sqlite3SafetyCheckOk(db) ){
|
||||
@ -3990,13 +3989,15 @@ int sqlite3_snapshot_get(
|
||||
#endif
|
||||
sqlite3_mutex_enter(db->mutex);
|
||||
|
||||
iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
if( db->autoCommit==0 ){
|
||||
int iDb = sqlite3FindDbName(db, zDb);
|
||||
if( iDb==0 || iDb>1 ){
|
||||
Btree *pBt = db->aDb[iDb].pBt;
|
||||
if( 0==sqlite3BtreeIsInTrans(pBt) ){
|
||||
rc = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotGet(sqlite3BtreePager(pBt), ppSnapshot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4043,6 +4044,38 @@ int sqlite3_snapshot_open(
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** 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 = sqlite3BtreeBeginTrans(pBt, 0);
|
||||
if( rc==SQLITE_OK ){
|
||||
rc = sqlite3PagerSnapshotRecover(sqlite3BtreePager(pBt));
|
||||
sqlite3BtreeCommit(pBt);
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_mutex_leave(db->mutex);
|
||||
#endif /* SQLITE_OMIT_WAL */
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
** Free a snapshot handle obtained from sqlite3_snapshot_get().
|
||||
*/
|
||||
|
14
src/pager.c
14
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 */
|
||||
|
||||
|
@ -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
|
||||
|
@ -8287,7 +8287,9 @@ int sqlite3_system_errno(sqlite3*);
|
||||
** to an historical snapshot (if possible). The destructor for
|
||||
** sqlite3_snapshot objects is [sqlite3_snapshot_free()].
|
||||
*/
|
||||
typedef struct sqlite3_snapshot sqlite3_snapshot;
|
||||
typedef struct sqlite3_snapshot {
|
||||
unsigned char hidden[48];
|
||||
} sqlite3_snapshot;
|
||||
|
||||
/*
|
||||
** CAPI3REF: Record A Database Snapshot
|
||||
@ -8298,9 +8300,32 @@ typedef struct sqlite3_snapshot sqlite3_snapshot;
|
||||
** schema S in database connection D. ^On success, the
|
||||
** [sqlite3_snapshot_get(D,S,P)] interface writes a pointer to the newly
|
||||
** created [sqlite3_snapshot] object into *P and returns SQLITE_OK.
|
||||
** ^If schema S of [database connection] D is not a [WAL mode] database
|
||||
** that is in a read transaction, then [sqlite3_snapshot_get(D,S,P)]
|
||||
** leaves the *P value unchanged and returns an appropriate [error code].
|
||||
** If there is not already a read-transaction open on schema S when
|
||||
** this function is called, one is opened automatically.
|
||||
**
|
||||
** The following must be true for this function to succeed. If any of
|
||||
** the following statements are false when sqlite3_snapshot_get() is
|
||||
** called, SQLITE_ERROR is returned. The final value of *P is undefined
|
||||
** in this case.
|
||||
**
|
||||
** <ul>
|
||||
** <li> The database handle must be in [autocommit mode].
|
||||
**
|
||||
** <li> Schema S of [database connection] D must be a [WAL mode] database.
|
||||
**
|
||||
** <li> There must not be a write transaction open on schema S of database
|
||||
** connection D.
|
||||
**
|
||||
** <li> One or more transactions must have been written to the current wal
|
||||
** file since it was created on disk (by any connection). This means
|
||||
** that a snapshot cannot be taken on a wal mode database with no wal
|
||||
** file immediately after it is first opened. At least one transaction
|
||||
** must be written to it first.
|
||||
** </ul>
|
||||
**
|
||||
** This function may also return SQLITE_NOMEM. If it is called with the
|
||||
** database handle in autocommit mode but fails for some other reason,
|
||||
** whether or not a read transaction is opened on schema S is undefined.
|
||||
**
|
||||
** The [sqlite3_snapshot] object returned from a successful call to
|
||||
** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()]
|
||||
@ -8393,6 +8418,28 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_cmp(
|
||||
sqlite3_snapshot *p2
|
||||
);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Recover snapshots from a wal file
|
||||
** EXPERIMENTAL
|
||||
**
|
||||
** If all connections disconnect from a database file but do not perform
|
||||
** a checkpoint, the existing wal file is opened along with the database
|
||||
** file the next time the database is opened. At this point it is only
|
||||
** possible to successfully call sqlite3_snapshot_open() to open the most
|
||||
** recent snapshot of the database (the one at the head of the wal file),
|
||||
** even though the wal file may contain other valid snapshots for which
|
||||
** clients have sqlite3_snapshot handles.
|
||||
**
|
||||
** This function attempts to scan the wal file associated with database zDb
|
||||
** of database handle db and make all valid snapshots available to
|
||||
** sqlite3_snapshot_open(). It is an error if there is already a read
|
||||
** transaction open on the database, or if the database is not a wal mode
|
||||
** database.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
|
||||
*/
|
||||
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.
|
||||
|
143
src/test1.c
143
src/test1.c
@ -2310,6 +2310,38 @@ static int SQLITE_TCLAPI test_snapshot_get(
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
#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
|
||||
@ -2388,6 +2420,113 @@ static int SQLITE_TCLAPI test_snapshot_cmp(
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
/*
|
||||
** Usage: sqlite3_snapshot_get_blob DB DBNAME
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_snapshot_get_blob(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
char *zName;
|
||||
sqlite3_snapshot *pSnapshot = 0;
|
||||
|
||||
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_get(db, zName, &pSnapshot);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}else{
|
||||
Tcl_SetObjResult(interp,
|
||||
Tcl_NewByteArrayObj((unsigned char*)pSnapshot, sizeof(sqlite3_snapshot))
|
||||
);
|
||||
sqlite3_snapshot_free(pSnapshot);
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
/*
|
||||
** Usage: sqlite3_snapshot_open_blob DB DBNAME SNAPSHOT
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_snapshot_open_blob(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int rc;
|
||||
sqlite3 *db;
|
||||
char *zName;
|
||||
unsigned char *pBlob;
|
||||
int nBlob;
|
||||
|
||||
if( objc!=4 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "DB DBNAME SNAPSHOT");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
if( getDbPointer(interp, Tcl_GetString(objv[1]), &db) ) return TCL_ERROR;
|
||||
zName = Tcl_GetString(objv[2]);
|
||||
pBlob = Tcl_GetByteArrayFromObj(objv[3], &nBlob);
|
||||
if( nBlob!=sizeof(sqlite3_snapshot) ){
|
||||
Tcl_AppendResult(interp, "bad SNAPSHOT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
rc = sqlite3_snapshot_open(db, zName, (sqlite3_snapshot*)pBlob);
|
||||
if( rc!=SQLITE_OK ){
|
||||
Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1));
|
||||
return TCL_ERROR;
|
||||
}
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
/*
|
||||
** Usage: sqlite3_snapshot_cmp_blob SNAPSHOT1 SNAPSHOT2
|
||||
*/
|
||||
static int SQLITE_TCLAPI test_snapshot_cmp_blob(
|
||||
void * clientData,
|
||||
Tcl_Interp *interp,
|
||||
int objc,
|
||||
Tcl_Obj *CONST objv[]
|
||||
){
|
||||
int res;
|
||||
unsigned char *p1;
|
||||
unsigned char *p2;
|
||||
int n1;
|
||||
int n2;
|
||||
|
||||
if( objc!=3 ){
|
||||
Tcl_WrongNumArgs(interp, 1, objv, "SNAPSHOT1 SNAPSHOT2");
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
p1 = Tcl_GetByteArrayFromObj(objv[1], &n1);
|
||||
p2 = Tcl_GetByteArrayFromObj(objv[2], &n2);
|
||||
|
||||
if( n1!=sizeof(sqlite3_snapshot) || n1!=n2 ){
|
||||
Tcl_AppendResult(interp, "bad SNAPSHOT", 0);
|
||||
return TCL_ERROR;
|
||||
}
|
||||
|
||||
res = sqlite3_snapshot_cmp((sqlite3_snapshot*)p1, (sqlite3_snapshot*)p2);
|
||||
Tcl_SetObjResult(interp, Tcl_NewIntObj(res));
|
||||
return TCL_OK;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
/*
|
||||
** Usage: sqlite3_delete_database FILENAME
|
||||
*/
|
||||
@ -7539,6 +7678,10 @@ 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 },
|
||||
#endif
|
||||
{ "sqlite3_delete_database", test_delete_database, 0 },
|
||||
};
|
||||
|
89
src/wal.c
89
src/wal.c
@ -2379,6 +2379,84 @@ static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int cnt){
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef SQLITE_ENABLE_SNAPSHOT
|
||||
/*
|
||||
** Attempt to reduce the value of the WalCkptInfo.nBackfillAttempted
|
||||
** variable so that older snapshots can be accessed. To do this, loop
|
||||
** through all wal frames from nBackfillAttempted to (nBackfill+1),
|
||||
** comparing their content to the corresponding page with the database
|
||||
** file, if any. Set nBackfillAttempted to the frame number of the
|
||||
** first frame for which the wal file content matches the db file.
|
||||
**
|
||||
** This is only really safe if the file-system is such that any page
|
||||
** writes made by earlier checkpointers were atomic operations, which
|
||||
** is not always true. It is also possible that nBackfillAttempted
|
||||
** may be left set to a value larger than expected, if a wal frame
|
||||
** contains content that duplicate of an earlier version of the same
|
||||
** page.
|
||||
**
|
||||
** SQLITE_OK is returned if successful, or an SQLite error code if an
|
||||
** error occurs. It is not an error if nBackfillAttempted cannot be
|
||||
** decreased at all.
|
||||
*/
|
||||
int sqlite3WalSnapshotRecover(Wal *pWal){
|
||||
int rc;
|
||||
|
||||
assert( pWal->readLock>=0 );
|
||||
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
|
||||
if( rc==SQLITE_OK ){
|
||||
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
|
||||
int szPage = (int)pWal->szPage;
|
||||
i64 szDb; /* Size of db file in bytes */
|
||||
|
||||
rc = sqlite3OsFileSize(pWal->pDbFd, &szDb);
|
||||
if( rc==SQLITE_OK ){
|
||||
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 ) break;
|
||||
pgno = aPgno[i-iZero];
|
||||
iDbOff = (i64)(pgno-1) * szPage;
|
||||
|
||||
if( iDbOff+szPage<=szDb ){
|
||||
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);
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
#endif /* SQLITE_ENABLE_SNAPSHOT */
|
||||
|
||||
/*
|
||||
** Begin a read transaction on the database.
|
||||
**
|
||||
@ -2441,7 +2519,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 ){
|
||||
@ -3393,9 +3475,14 @@ int sqlite3WalHeapMemory(Wal *pWal){
|
||||
int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot){
|
||||
int rc = SQLITE_OK;
|
||||
WalIndexHdr *pRet;
|
||||
static const u32 aZero[4] = { 0, 0, 0, 0 };
|
||||
|
||||
assert( pWal->readLock>=0 && pWal->writeLock==0 );
|
||||
|
||||
if( memcmp(&pWal->hdr.aFrameCksum[0],aZero,16)==0 ){
|
||||
*ppSnapshot = 0;
|
||||
return SQLITE_ERROR;
|
||||
}
|
||||
pRet = (WalIndexHdr*)sqlite3_malloc(sizeof(WalIndexHdr));
|
||||
if( pRet==0 ){
|
||||
rc = SQLITE_NOMEM_BKPT;
|
||||
|
@ -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
|
||||
|
@ -26,413 +26,457 @@ if {[permutation]=="inmemory_journal"} {
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check some error conditions in snapshot_get(). It is an error if:
|
||||
#
|
||||
# 1) snapshot_get() is called on a non-WAL database, or
|
||||
# 2) there is an open write transaction on the database.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
}
|
||||
|
||||
do_test 1.1.1 {
|
||||
execsql { BEGIN; SELECT * FROM t1; }
|
||||
list [catch { sqlite3_snapshot_get db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test 1.1.2 COMMIT
|
||||
|
||||
do_test 1.2.1 {
|
||||
execsql {
|
||||
PRAGMA journal_mode = WAL;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8);
|
||||
foreach {tn tcl} {
|
||||
1 {
|
||||
proc snapshot_get {DB DBNAME} {
|
||||
uplevel [list sqlite3_snapshot_get $DB $DBNAME]
|
||||
}
|
||||
proc snapshot_open {DB DBNAME SNAPSHOT} {
|
||||
uplevel [list sqlite3_snapshot_open $DB $DBNAME $SNAPSHOT]
|
||||
}
|
||||
proc snapshot_free {SNAPSHOT} {
|
||||
uplevel [list sqlite3_snapshot_free $SNAPSHOT]
|
||||
}
|
||||
proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
|
||||
uplevel [list sqlite3_snapshot_cmp $SNAPSHOT1 $SNAPSHOT2]
|
||||
}
|
||||
}
|
||||
list [catch { sqlite3_snapshot_get db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test 1.3.2 COMMIT
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that a simple case works. Reuse the database created by the
|
||||
# block of tests above.
|
||||
#
|
||||
do_execsql_test 2.1.0 {
|
||||
BEGIN;
|
||||
SELECT * FROM t1;
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
|
||||
do_test 2.1.1 {
|
||||
set snapshot [sqlite3_snapshot_get db main]
|
||||
execsql {
|
||||
COMMIT;
|
||||
INSERT INTO t1 VALUES(9, 10);
|
||||
SELECT * FROM t1;
|
||||
2 {
|
||||
proc snapshot_get {DB DBNAME} {
|
||||
uplevel [list sqlite3_snapshot_get_blob $DB $DBNAME]
|
||||
}
|
||||
proc snapshot_open {DB DBNAME SNAPSHOT} {
|
||||
uplevel [list sqlite3_snapshot_open_blob $DB $DBNAME $SNAPSHOT]
|
||||
}
|
||||
proc snapshot_free {SNAPSHOT} {
|
||||
}
|
||||
proc snapshot_cmp {SNAPSHOT1 SNAPSHOT2} {
|
||||
uplevel [list sqlite3_snapshot_cmp_blob $SNAPSHOT1 $SNAPSHOT2]
|
||||
}
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
} {
|
||||
|
||||
do_test 2.1.2 {
|
||||
execsql BEGIN
|
||||
sqlite3_snapshot_open db main $snapshot
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
reset_db
|
||||
eval $tcl
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check some error conditions in snapshot_get(). It is an error if:
|
||||
#
|
||||
# 1) snapshot_get() is called on a non-WAL database, or
|
||||
# 2) there is an open write transaction on the database.
|
||||
# 3) the database handle is in auto-commit mode
|
||||
#
|
||||
do_execsql_test $tn.1.0 {
|
||||
CREATE TABLE t1(a, b);
|
||||
INSERT INTO t1 VALUES(1, 2);
|
||||
INSERT INTO t1 VALUES(3, 4);
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
|
||||
do_test 2.1.3 {
|
||||
sqlite3_snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test $tn.1.1.1 {
|
||||
execsql { BEGIN; SELECT * FROM t1; }
|
||||
list [catch { snapshot_get db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test $tn.1.1.2 COMMIT
|
||||
|
||||
do_test 2.2.0 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
do_test $tn.1.2.1 {
|
||||
execsql {
|
||||
PRAGMA journal_mode = WAL;
|
||||
BEGIN;
|
||||
INSERT INTO t1 VALUES(5, 6);
|
||||
INSERT INTO t1 VALUES(7, 8);
|
||||
}
|
||||
list [catch { snapshot_get db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test $tn.1.2.2 COMMIT
|
||||
|
||||
do_test $tn.1.3.1 {
|
||||
list [catch { snapshot_get db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test $tn.1.3.2 {
|
||||
db trans { set snap [snapshot_get db main] }
|
||||
snapshot_free $snap
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that a simple case works. Reuse the database created by the
|
||||
# block of tests above.
|
||||
#
|
||||
do_execsql_test $tn.2.1.0 {
|
||||
BEGIN;
|
||||
SELECT * FROM t1;
|
||||
} db2
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
|
||||
do_test 2.2.1 {
|
||||
set snapshot [sqlite3_snapshot_get db2 main]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(11, 12);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
do_test $tn.2.1.1 {
|
||||
set snapshot [snapshot_get db main]
|
||||
execsql {
|
||||
COMMIT;
|
||||
INSERT INTO t1 VALUES(9, 10);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
do_test 2.2.2 {
|
||||
execsql BEGIN
|
||||
sqlite3_snapshot_open db main $snapshot
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
do_test $tn.2.1.2 {
|
||||
execsql BEGIN
|
||||
snapshot_open db main $snapshot
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8}
|
||||
|
||||
do_test 2.2.3 {
|
||||
sqlite3_snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
execsql COMMIT db2
|
||||
db2 close
|
||||
} {}
|
||||
do_test $tn.2.1.3 {
|
||||
snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 2.3.1 {
|
||||
execsql { DELETE FROM t1 WHERE a>6 }
|
||||
set snapshot [sqlite3_snapshot_get db main]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
INSERT INTO t1 VALUES('c', 'd');
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 a b c d}
|
||||
do_test 2.3.2 {
|
||||
execsql BEGIN
|
||||
sqlite3_snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 2 3 4 5 6}
|
||||
do_test $tn.2.2.0 {
|
||||
sqlite3 db2 test.db
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM t1;
|
||||
} db2
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
do_test 2.3.3 {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES('x','y')
|
||||
}
|
||||
} {1 {database is locked}}
|
||||
do_test 2.3.4 {
|
||||
execsql COMMIT
|
||||
sqlite3_snapshot_free $snapshot
|
||||
} {}
|
||||
do_test $tn.2.2.1 {
|
||||
set snapshot [snapshot_get db2 main]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(11, 12);
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10 11 12}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check some errors in sqlite3_snapshot_open(). It is an error if:
|
||||
#
|
||||
# 1) the db is in auto-commit mode,
|
||||
# 2) the db has an open (read or write) transaction,
|
||||
# 3) the db is not a wal database,
|
||||
#
|
||||
# Reuse the database created by earlier tests.
|
||||
#
|
||||
do_execsql_test 3.0.0 {
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
INSERT INTO t2 VALUES('c', 'd');
|
||||
BEGIN;
|
||||
SELECT * FROM t2;
|
||||
} {a b c d}
|
||||
do_test 3.0.1 {
|
||||
set snapshot [sqlite3_snapshot_get db main]
|
||||
execsql { COMMIT }
|
||||
execsql { INSERT INTO t2 VALUES('e', 'f'); }
|
||||
} {}
|
||||
do_test $tn.2.2.2 {
|
||||
execsql BEGIN
|
||||
snapshot_open db main $snapshot
|
||||
execsql {
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 7 8 9 10}
|
||||
|
||||
do_test 3.1 {
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test $tn.2.2.3 {
|
||||
snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
execsql COMMIT db2
|
||||
db2 close
|
||||
} {}
|
||||
|
||||
do_test 3.2.1 {
|
||||
execsql {
|
||||
do_test $tn.2.3.1 {
|
||||
execsql { DELETE FROM t1 WHERE a>6 }
|
||||
db trans { set snapshot [snapshot_get db main] }
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
INSERT INTO t1 VALUES('c', 'd');
|
||||
SELECT * FROM t1;
|
||||
}
|
||||
} {1 2 3 4 5 6 a b c d}
|
||||
do_test $tn.2.3.2 {
|
||||
execsql BEGIN
|
||||
snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 2 3 4 5 6}
|
||||
|
||||
do_test $tn.2.3.3 {
|
||||
catchsql {
|
||||
INSERT INTO t1 VALUES('x','y')
|
||||
}
|
||||
} {1 {database is locked}}
|
||||
do_test $tn.2.3.4 {
|
||||
execsql COMMIT
|
||||
snapshot_free $snapshot
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check some errors in snapshot_open(). It is an error if:
|
||||
#
|
||||
# 1) the db is in auto-commit mode,
|
||||
# 2) the db has an open (read or write) transaction,
|
||||
# 3) the db is not a wal database,
|
||||
#
|
||||
# Reuse the database created by earlier tests.
|
||||
#
|
||||
do_execsql_test $tn.3.0.0 {
|
||||
CREATE TABLE t2(x, y);
|
||||
INSERT INTO t2 VALUES('a', 'b');
|
||||
INSERT INTO t2 VALUES('c', 'd');
|
||||
BEGIN;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {a b c d e f}
|
||||
do_test 3.2.2 {
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
} {a b c d}
|
||||
do_test $tn.3.0.1 {
|
||||
set snapshot [snapshot_get db main]
|
||||
execsql { COMMIT }
|
||||
execsql { INSERT INTO t2 VALUES('e', 'f'); }
|
||||
} {}
|
||||
|
||||
do_test 3.2.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('g', 'h');
|
||||
}
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test 3.2.4 COMMIT
|
||||
do_test $tn.3.1 {
|
||||
list [catch {snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_test 3.3.1 {
|
||||
execsql { PRAGMA journal_mode = DELETE }
|
||||
execsql { BEGIN }
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test $tn.3.2.1 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM t2;
|
||||
}
|
||||
} {a b c d e f}
|
||||
do_test $tn.3.2.2 {
|
||||
list [catch {snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_test 3.3.2 {
|
||||
sqlite3_snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test $tn.3.2.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
BEGIN;
|
||||
INSERT INTO t2 VALUES('g', 'h');
|
||||
}
|
||||
list [catch {snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_execsql_test $tn.3.2.4 COMMIT
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
|
||||
# no longer exists because the wal file has been checkpointed.
|
||||
#
|
||||
# 1. Reading a snapshot from the middle of a wal file is not possible
|
||||
# after the wal file has been checkpointed.
|
||||
#
|
||||
# 2. That a snapshot from the end of a wal file can not be read once
|
||||
# the wal file has been wrapped.
|
||||
#
|
||||
do_execsql_test 4.1.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t3(i, j);
|
||||
INSERT INTO t3 VALUES('o', 't');
|
||||
INSERT INTO t3 VALUES('t', 'f');
|
||||
BEGIN;
|
||||
SELECT * FROM t3;
|
||||
} {wal o t t f}
|
||||
do_test $tn.3.3.1 {
|
||||
execsql { PRAGMA journal_mode = DELETE }
|
||||
execsql { BEGIN }
|
||||
list [catch {snapshot_open db main $snapshot } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
do_test 4.1.1 {
|
||||
set snapshot [sqlite3_snapshot_get db main]
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test 4.1.2 {
|
||||
execsql {
|
||||
INSERT INTO t3 VALUES('f', 's');
|
||||
BEGIN;
|
||||
}
|
||||
sqlite3_snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t3 }
|
||||
} {o t t f}
|
||||
do_test $tn.$tn.3.3.2 {
|
||||
snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 4.1.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
PRAGMA wal_checkpoint;
|
||||
BEGIN;
|
||||
}
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
do_test 4.1.4 {
|
||||
sqlite3_snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test 4.2.1 {
|
||||
execsql {
|
||||
INSERT INTO t3 VALUES('s', 'e');
|
||||
INSERT INTO t3 VALUES('n', 't');
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that SQLITE_BUSY_SNAPSHOT is returned if the specified snapshot
|
||||
# no longer exists because the wal file has been checkpointed.
|
||||
#
|
||||
# 1. Reading a snapshot from the middle of a wal file is not possible
|
||||
# after the wal file has been checkpointed.
|
||||
#
|
||||
# 2. That a snapshot from the end of a wal file can not be read once
|
||||
# the wal file has been wrapped.
|
||||
#
|
||||
do_execsql_test $tn.4.1.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t3(i, j);
|
||||
INSERT INTO t3 VALUES('o', 't');
|
||||
INSERT INTO t3 VALUES('t', 'f');
|
||||
BEGIN;
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {o t t f f s s e n t}
|
||||
do_test 4.2.2 {
|
||||
set snapshot [sqlite3_snapshot_get db main]
|
||||
execsql {
|
||||
COMMIT;
|
||||
PRAGMA wal_checkpoint;
|
||||
} {wal o t t f}
|
||||
|
||||
do_test $tn.4.1.1 {
|
||||
set snapshot [snapshot_get db main]
|
||||
execsql COMMIT
|
||||
} {}
|
||||
do_test $tn.4.1.2 {
|
||||
execsql {
|
||||
INSERT INTO t3 VALUES('f', 's');
|
||||
BEGIN;
|
||||
}
|
||||
snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t3 }
|
||||
} {o t t f}
|
||||
|
||||
do_test $tn.4.1.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
PRAGMA wal_checkpoint;
|
||||
BEGIN;
|
||||
}
|
||||
list [catch {snapshot_open db main $snapshot} msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
do_test $tn.4.1.4 {
|
||||
snapshot_free $snapshot
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_test $tn.4.2.1 {
|
||||
execsql {
|
||||
INSERT INTO t3 VALUES('s', 'e');
|
||||
INSERT INTO t3 VALUES('n', 't');
|
||||
BEGIN;
|
||||
SELECT * FROM t3;
|
||||
}
|
||||
} {o t t f f s s e n t}
|
||||
do_test $tn.4.2.2 {
|
||||
set snapshot [snapshot_get db main]
|
||||
execsql {
|
||||
COMMIT;
|
||||
PRAGMA wal_checkpoint;
|
||||
BEGIN;
|
||||
}
|
||||
snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t3 }
|
||||
} {o t t f f s s e n t}
|
||||
do_test $tn.4.2.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
INSERT INTO t3 VALUES('e', 't');
|
||||
BEGIN;
|
||||
}
|
||||
list [catch {snapshot_open db main $snapshot} msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
do_test $tn.4.2.4 {
|
||||
snapshot_free $snapshot
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that SQLITE_BUSY is returned if a checkpoint is running when
|
||||
# sqlite3_snapshot_open() is called.
|
||||
#
|
||||
reset_db
|
||||
db close
|
||||
testvfs tvfs
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
|
||||
do_execsql_test $tn.5.1 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE x1(x, xx, xxx);
|
||||
INSERT INTO x1 VALUES('z', 'zz', 'zzz');
|
||||
BEGIN;
|
||||
SELECT * FROM x1;
|
||||
} {wal z zz zzz}
|
||||
|
||||
do_test $tn.5.2 {
|
||||
set ::snapshot [snapshot_get db main]
|
||||
sqlite3 db2 test.db -vfs tvfs
|
||||
execsql {
|
||||
INSERT INTO x1 VALUES('a', 'aa', 'aaa');
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
|
||||
set t53 0
|
||||
proc write_callback {args} {
|
||||
do_test $tn.5.3.[incr ::t53] {
|
||||
execsql BEGIN
|
||||
list [catch { snapshot_open db main $::snapshot } msg] $msg
|
||||
} {1 SQLITE_BUSY}
|
||||
catchsql COMMIT
|
||||
}
|
||||
sqlite3_snapshot_open db main $snapshot
|
||||
execsql { SELECT * FROM t3 }
|
||||
} {o t t f f s s e n t}
|
||||
do_test 4.2.3 {
|
||||
execsql {
|
||||
COMMIT;
|
||||
INSERT INTO t3 VALUES('e', 't');
|
||||
BEGIN;
|
||||
}
|
||||
list [catch {sqlite3_snapshot_open db main $snapshot} msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
do_test 4.2.4 {
|
||||
sqlite3_snapshot_free $snapshot
|
||||
} {}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that SQLITE_BUSY is returned if a checkpoint is running when
|
||||
# sqlite3_snapshot_open() is called.
|
||||
#
|
||||
reset_db
|
||||
db close
|
||||
testvfs tvfs
|
||||
sqlite3 db test.db -vfs tvfs
|
||||
|
||||
do_execsql_test 5.1 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE x1(x, xx, xxx);
|
||||
INSERT INTO x1 VALUES('z', 'zz', 'zzz');
|
||||
BEGIN;
|
||||
SELECT * FROM x1;
|
||||
} {wal z zz zzz}
|
||||
|
||||
do_test 5.2 {
|
||||
set ::snapshot [sqlite3_snapshot_get db main]
|
||||
sqlite3 db2 test.db -vfs tvfs
|
||||
execsql {
|
||||
INSERT INTO x1 VALUES('a', 'aa', 'aaa');
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
|
||||
set t53 0
|
||||
proc write_callback {args} {
|
||||
do_test 5.3.[incr ::t53] {
|
||||
execsql BEGIN
|
||||
list [catch { sqlite3_snapshot_open db main $::snapshot } msg] $msg
|
||||
} {1 SQLITE_BUSY}
|
||||
catchsql COMMIT
|
||||
}
|
||||
|
||||
tvfs filter xWrite
|
||||
tvfs script write_callback
|
||||
db2 eval { PRAGMA wal_checkpoint }
|
||||
db close
|
||||
db2 close
|
||||
tvfs delete
|
||||
sqlite3_snapshot_free $snapshot
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3_snapshot_get() may be called immediately after
|
||||
# "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
|
||||
# be called after opening the db handle and running the script
|
||||
# "PRAGMA user_version; BEGIN".
|
||||
reset_db
|
||||
do_execsql_test 6.1 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE x1(x, xx, xxx);
|
||||
INSERT INTO x1 VALUES('z', 'zz', 'zzz');
|
||||
BEGIN;
|
||||
PRAGMA user_version;
|
||||
} {wal 0}
|
||||
do_test 6.2 {
|
||||
set ::snapshot [sqlite3_snapshot_get db main]
|
||||
execsql {
|
||||
INSERT INTO x1 VALUES('a', 'aa', 'aaa');
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
do_test 6.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "PRAGMA user_version ; BEGIN"
|
||||
sqlite3_snapshot_open db2 main $::snapshot
|
||||
db2 eval { SELECT * FROM x1 }
|
||||
} {z zz zzz}
|
||||
do_test 6.4 {
|
||||
tvfs filter xWrite
|
||||
tvfs script write_callback
|
||||
db2 eval { PRAGMA wal_checkpoint }
|
||||
db close
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "PRAGMA application_id"
|
||||
db2 eval "BEGIN"
|
||||
sqlite3_snapshot_open db2 main $::snapshot
|
||||
db2 eval { SELECT * FROM x1 }
|
||||
} {z zz zzz}
|
||||
tvfs delete
|
||||
snapshot_free $snapshot
|
||||
|
||||
do_test 6.5 {
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "BEGIN"
|
||||
list [catch {sqlite3_snapshot_open db2 main $::snapshot} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
#-------------------------------------------------------------------------
|
||||
# Test that sqlite3_snapshot_get() may be called immediately after
|
||||
# "BEGIN; PRAGMA user_version;". And that sqlite3_snapshot_open() may
|
||||
# be called after opening the db handle and running the script
|
||||
# "PRAGMA user_version; BEGIN".
|
||||
reset_db
|
||||
do_execsql_test $tn.6.1 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE x1(x, xx, xxx);
|
||||
INSERT INTO x1 VALUES('z', 'zz', 'zzz');
|
||||
BEGIN;
|
||||
PRAGMA user_version;
|
||||
} {wal 0}
|
||||
do_test $tn.6.2 {
|
||||
set ::snapshot [snapshot_get db main]
|
||||
execsql {
|
||||
INSERT INTO x1 VALUES('a', 'aa', 'aaa');
|
||||
COMMIT;
|
||||
}
|
||||
} {}
|
||||
do_test $tn.6.3 {
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "PRAGMA user_version ; BEGIN"
|
||||
snapshot_open db2 main $::snapshot
|
||||
db2 eval { SELECT * FROM x1 }
|
||||
} {z zz zzz}
|
||||
do_test $tn.6.4 {
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "PRAGMA application_id"
|
||||
db2 eval "BEGIN"
|
||||
snapshot_open db2 main $::snapshot
|
||||
db2 eval { SELECT * FROM x1 }
|
||||
} {z zz zzz}
|
||||
|
||||
sqlite3_snapshot_free $snapshot
|
||||
do_test $tn.6.5 {
|
||||
db2 close
|
||||
sqlite3 db2 test.db
|
||||
db2 eval "BEGIN"
|
||||
list [catch {snapshot_open db2 main $::snapshot} msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# The following tests investigate the sqlite3_snapshot_cmp() API.
|
||||
#
|
||||
snapshot_free $snapshot
|
||||
|
||||
# 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]
|
||||
#-------------------------------------------------------------------------
|
||||
# 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 snapshot_cmp $p1 $p2] $r]
|
||||
uplevel [list do_test $tn.2 [list snapshot_cmp $p2 $p1] [expr $r*-1]]
|
||||
uplevel [list do_test $tn.3 [list snapshot_cmp $p1 $p1] 0]
|
||||
uplevel [list do_test $tn.4 [list snapshot_cmp $p2 $p2] 0]
|
||||
}
|
||||
|
||||
catch { db2 close }
|
||||
reset_db
|
||||
|
||||
do_execsql_test $tn.7.1 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(x);
|
||||
} wal
|
||||
|
||||
do_test $tn.7.1.2 {
|
||||
execsql { BEGIN ; PRAGMA application_id }
|
||||
set p1 [snapshot_get db main]
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(10);
|
||||
COMMIT;
|
||||
}
|
||||
execsql { BEGIN ; PRAGMA application_id }
|
||||
set p2 [snapshot_get db main]
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_snapshot_cmp_test $tn.7.1.3 $p1 $p2 -1
|
||||
snapshot_free $p1
|
||||
snapshot_free $p2
|
||||
|
||||
do_execsql_test $tn.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 $tn.7.2.2 {
|
||||
set p1 [snapshot_get db main]
|
||||
execsql {
|
||||
COMMIT;
|
||||
INSERT INTO t1 VALUES(14);
|
||||
PRAGMA wal_checkpoint;
|
||||
BEGIN;
|
||||
PRAGMA application_id;
|
||||
}
|
||||
set p2 [snapshot_get db main]
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_snapshot_cmp_test $tn.7.2.3 $p1 $p2 -1
|
||||
snapshot_free $p2
|
||||
|
||||
do_test $tn.7.3.1 {
|
||||
execsql {
|
||||
INSERT INTO t1 VALUES(14);
|
||||
BEGIN;
|
||||
PRAGMA application_id;
|
||||
}
|
||||
set p2 [snapshot_get db main]
|
||||
execsql COMMIT
|
||||
} {}
|
||||
|
||||
do_snapshot_cmp_test $tn.7.3.2 $p1 $p2 -1
|
||||
snapshot_free $p1
|
||||
snapshot_free $p2
|
||||
}
|
||||
|
||||
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
|
||||
|
202
test/snapshot2.test
Normal file
202
test/snapshot2.test
Normal file
@ -0,0 +1,202 @@
|
||||
# 2016 November 18
|
||||
#
|
||||
# The author disclaims copyright to this source code. In place of
|
||||
# a legal notice, here is a blessing:
|
||||
#
|
||||
# May you do good and not evil.
|
||||
# May you find forgiveness for yourself and forgive others.
|
||||
# May you share freely, never taking more than you give.
|
||||
#
|
||||
#***********************************************************************
|
||||
# This file implements regression tests for SQLite library. The focus
|
||||
# of this file is the sqlite3_snapshot_xxx() APIs.
|
||||
#
|
||||
|
||||
set testdir [file dirname $argv0]
|
||||
source $testdir/tester.tcl
|
||||
ifcapable !snapshot {finish_test; return}
|
||||
set testprefix snapshot2
|
||||
|
||||
# This test does not work with the inmemory_journal permutation. The reason
|
||||
# is that each connection opened as part of this permutation executes
|
||||
# "PRAGMA journal_mode=memory", which fails if the database is in wal mode
|
||||
# and there are one or more existing connections.
|
||||
if {[permutation]=="inmemory_journal"} {
|
||||
finish_test
|
||||
return
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that it is not possible to obtain a snapshot immediately after
|
||||
# a wal mode database with an empty wal file is opened. But it is after
|
||||
# the file has been written, even by some other connection.
|
||||
#
|
||||
do_execsql_test 1.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(a, b, c);
|
||||
INSERT INTO t1 VALUES(1, 2, 3);
|
||||
INSERT INTO t1 VALUES(4, 5, 6);
|
||||
} {wal}
|
||||
|
||||
db close
|
||||
do_test 1.1.1 { list [file exists test.db] [file exists test.db-wal] } {1 0}
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 1.1.2 { SELECT * FROM t1 } {1 2 3 4 5 6}
|
||||
|
||||
do_test 1.1.3 {
|
||||
execsql BEGIN
|
||||
list [catch { sqlite3_snapshot_get_blob db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
execsql COMMIT
|
||||
|
||||
do_test 1.1.4 {
|
||||
execsql { INSERT INTO t1 VALUES(7, 8, 9) }
|
||||
execsql BEGIN
|
||||
string length [sqlite3_snapshot_get_blob db main]
|
||||
} 48
|
||||
execsql COMMIT
|
||||
|
||||
db close
|
||||
do_test 1.2.1 { list [file exists test.db] [file exists test.db-wal] } {1 0}
|
||||
|
||||
sqlite3 db test.db
|
||||
do_execsql_test 1.2.2 { SELECT * FROM t1 } {1 2 3 4 5 6 7 8 9}
|
||||
|
||||
do_test 1.2.3 {
|
||||
execsql BEGIN
|
||||
list [catch { sqlite3_snapshot_get_blob db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
execsql COMMIT
|
||||
|
||||
do_test 1.2.4 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { INSERT INTO t1 VALUES(10, 11, 12) } db2
|
||||
execsql BEGIN
|
||||
string length [sqlite3_snapshot_get_blob db main]
|
||||
} 48
|
||||
execsql COMMIT
|
||||
db2 close
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Simple tests for sqlite3_snapshot_recover().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 2.0 {
|
||||
CREATE TABLE t1(x);
|
||||
PRAGMA journal_mode = wal;
|
||||
INSERT INTO t1 VALUES(1);
|
||||
INSERT INTO t1 VALUES(2);
|
||||
} {wal}
|
||||
|
||||
do_test 2.1 {
|
||||
db trans { set snap [sqlite3_snapshot_get_blob db main] }
|
||||
sqlite3_db_config db NO_CKPT_ON_CLOSE 1
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
execsql {SELECT * FROM sqlite_master}
|
||||
execsql BEGIN
|
||||
sqlite3_snapshot_open_blob db main $snap
|
||||
execsql COMMIT;
|
||||
execsql { INSERT INTO t1 VALUES(3); }
|
||||
} {}
|
||||
|
||||
do_test 2.2 {
|
||||
sqlite3_db_config db NO_CKPT_ON_CLOSE 1
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
execsql {SELECT * FROM sqlite_master}
|
||||
execsql BEGIN
|
||||
list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
|
||||
do_test 2.3 {
|
||||
execsql COMMIT
|
||||
sqlite3_snapshot_recover db main
|
||||
execsql BEGIN
|
||||
sqlite3_snapshot_open_blob db main $snap
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 2}
|
||||
|
||||
do_test 2.4 {
|
||||
execsql COMMIT
|
||||
execsql { SELECT * FROM t1 }
|
||||
} {1 2 3}
|
||||
|
||||
do_test 2.5 {
|
||||
execsql { PRAGMA wal_checkpoint }
|
||||
sqlite3_db_config db NO_CKPT_ON_CLOSE 1
|
||||
db close
|
||||
sqlite3 db test.db
|
||||
|
||||
sqlite3_snapshot_recover db main
|
||||
execsql BEGIN
|
||||
list [catch { sqlite3_snapshot_open_blob db main $snap } msg] $msg
|
||||
} {1 SQLITE_BUSY_SNAPSHOT}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that calling sqlite3_snapshot_recover() does not confuse the
|
||||
# pager cache.
|
||||
reset_db
|
||||
do_execsql_test 3.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(x, y);
|
||||
INSERT INTO t1 VALUES('a', 'b');
|
||||
INSERT INTO t1 VALUES('c', 'd');
|
||||
} {wal}
|
||||
do_test 3.1 {
|
||||
sqlite3 db2 test.db
|
||||
execsql { INSERT INTO t1 VALUES('e', 'f') } db2
|
||||
db2 close
|
||||
sqlite3_snapshot_recover db main
|
||||
} {}
|
||||
do_execsql_test 3.2 {
|
||||
SELECT * FROM t1;
|
||||
} {a b c d e f}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Check that sqlite3_snapshot_recover() returns an error if it is called
|
||||
# with an open read-transaction. Or on a database that does not exist. Or
|
||||
# on the temp database. Or on a db that is not in wal mode.
|
||||
#
|
||||
do_test 4.1 {
|
||||
sqlite3_snapshot_recover db main
|
||||
} {}
|
||||
do_test 4.2 {
|
||||
execsql {
|
||||
BEGIN;
|
||||
SELECT * FROM sqlite_master;
|
||||
}
|
||||
list [catch { sqlite3_snapshot_recover db main } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 4.3 {
|
||||
execsql COMMIT
|
||||
sqlite3_snapshot_recover db main
|
||||
} {}
|
||||
do_test 4.4 {
|
||||
list [catch { sqlite3_snapshot_recover db aux } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 4.5 {
|
||||
forcedelete test.db2
|
||||
execsql {
|
||||
ATTACH 'test.db2' AS aux;
|
||||
PRAGMA aux.journal_mode = wal;
|
||||
CREATE TABLE aux.t2(x, y);
|
||||
}
|
||||
list [catch { sqlite3_snapshot_recover db aux } msg] $msg
|
||||
} {0 {}}
|
||||
do_test 4.6 {
|
||||
list [catch { sqlite3_snapshot_recover db temp } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
do_test 4.7 {
|
||||
execsql {
|
||||
PRAGMA aux.journal_mode = delete;
|
||||
}
|
||||
list [catch { sqlite3_snapshot_recover db aux } msg] $msg
|
||||
} {1 SQLITE_ERROR}
|
||||
|
||||
finish_test
|
||||
|
||||
|
@ -159,6 +159,68 @@ do_faultsim_test 3.0 -prep {
|
||||
sqlite3_snapshot_free $::snapshot
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------
|
||||
# Test the handling of faults that occur within sqlite3_snapshot_recover().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.0 {
|
||||
PRAGMA journal_mode = wal;
|
||||
CREATE TABLE t1(zzz);
|
||||
INSERT INTO t1 VALUES('abc');
|
||||
INSERT INTO t1 VALUES('def');
|
||||
} {wal}
|
||||
faultsim_save_and_close
|
||||
|
||||
do_test 4.0.1 {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM sqlite_master }
|
||||
sqlite3_snapshot_recover db main
|
||||
} {}
|
||||
db close
|
||||
|
||||
do_faultsim_test 4.0 -faults oom* -prep {
|
||||
faultsim_restore_and_reopen
|
||||
db eval { SELECT * FROM sqlite_master }
|
||||
} -body {
|
||||
sqlite3_snapshot_recover db main
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_NOMEM} {1 SQLITE_IOERR_NOMEM}
|
||||
}
|
||||
|
||||
# The following test cases contrive to call sqlite3_snapshot_recover()
|
||||
# before all pages of the *-shm file have been mapped. This tests an
|
||||
# extra branch of error handling logic in snapshot_recover().
|
||||
#
|
||||
reset_db
|
||||
do_execsql_test 4.1.0 {
|
||||
PRAGMA page_size = 512;
|
||||
PRAGMA journal_mode = wal;
|
||||
PRAGMA wal_autocheckpoint = 0;
|
||||
CREATE TABLE t1(zzz);
|
||||
INSERT INTO t1 VALUES(randomblob( 500 * 9500 ));
|
||||
PRAGMA user_version = 211;
|
||||
} {wal 0}
|
||||
|
||||
do_test 4.1.1 {
|
||||
list [file size test.db-shm] [file size test.db]
|
||||
} {98304 512}
|
||||
|
||||
faultsim_save_and_close
|
||||
do_faultsim_test 4.1 -faults shm* -prep {
|
||||
catch { db2 close }
|
||||
catch { db close }
|
||||
faultsim_restore_and_reopen
|
||||
sqlite3 db2 test.db
|
||||
db2 eval { SELECT * FROM sqlite_master }
|
||||
db eval BEGIN
|
||||
sqlite3_snapshot_get_blob db main
|
||||
db eval COMMIT
|
||||
} -body {
|
||||
sqlite3_snapshot_recover db main
|
||||
} -test {
|
||||
faultsim_test_result {0 {}} {1 SQLITE_IOERR}
|
||||
}
|
||||
|
||||
|
||||
|
||||
finish_test
|
||||
|
Loading…
x
Reference in New Issue
Block a user