From: dan Date: Mon, 6 Aug 2018 17:12:36 +0000 (+0000) Subject: Allow sqlite3_snapshot_open() to be called to change the snapshot after a X-Git-Tag: version-3.25.0~69^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fa3d4c19a9ee0d87fc0fe5aec374cd2fe47af24e;p=thirdparty%2Fsqlite.git Allow sqlite3_snapshot_open() to be called to change the snapshot after a read transaction is already open on database. FossilOrigin-Name: 051ac0152048ef52723196c26ca5f2629dafb782aec1c66fc30531bf54335043 --- diff --git a/manifest b/manifest index 7ddefd3b49..ab55cf5445 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Enhance\sthe\sedit()\sfunction\sso\sthat\sit\sconverts\stext\sfrom\s\\r\\n\sback\sinto\s\\n\nonly\sif\sthe\soriginal\sunedited\scopy\scontained\sno\s\\r\\n\svalues. -D 2018-08-06T02:08:53.886 +C Allow\ssqlite3_snapshot_open()\sto\sbe\scalled\sto\schange\sthe\ssnapshot\safter\sa\nread\stransaction\sis\salready\sopen\son\sdatabase. +D 2018-08-06T17:12:36.835 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea F Makefile.in 0a3a6c81e6fcb969ff9106e882f0a08547014ba463cb6beca4c4efaecc924ee6 @@ -462,7 +462,7 @@ F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 F src/insert.c 894594952bcda1dc6e1549871e4022517563545ffc7a3f4e9e5f3faa788893fd F src/legacy.c 134ab3e3fae00a0f67a5187981d6935b24b337bcf0f4b3e5c9fa5763da95bf4e F src/loadext.c 6aae5739198d96c51ae6eb97c4a5b1744c22ed7a5a565a5399a717780d48a36b -F src/main.c dc023f468eda20aed1fb7c300673cbb40617607b5771840e4229ec239dade250 +F src/main.c 3c7f2159687af1b1b0d0e0776f888adb94534e0bc52e7a1a1509f83e24cb6297 F src/malloc.c 07295435093ce354c6d9063ac05a2eeae28bd251d2e63c48b3d67c12c76f7e18 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -485,8 +485,8 @@ F src/os_setup.h 0dbaea40a7d36bf311613d31342e0b99e2536586 F src/os_unix.c d9cf5ae0c79f31019d8325e8736c83914aeed64d8327a8d91a62b6439b748948 F src/os_win.c 070cdbb400097c6cda54aa005356095afdc2f3ee691d17192c54724ef146a971 F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a -F src/pager.c 76d29b8a960dcb8b67210f095899d91e4a90673a6674ea58cfd1115b705a7fb9 -F src/pager.h c571b064df842ec8f2e90855dead9acf4cbe0d1b2c05afe0ef0d0145f7fd0388 +F src/pager.c 705de01dff9c3df9739c37a6d3b58cd2b1734fdabcef829b16cdc7721a9eeaa4 +F src/pager.h ecc554a55bc55d1c4ba5e17137b72e238e00bd81e72ff2662d8b9c8c10ae3963 F src/parse.y 704c94624d41d7d46a5467574130e55aa8029a563f4df538f0121475eae46e34 F src/pcache.c 135ef0bc6fb2e3b7178d49ab5c9176254c8a691832c1bceb1156b2fbdd0869bd F src/pcache.h 072f94d29281cffd99e46c1539849f248c4b56ae7684c1f36626797fee375170 @@ -500,7 +500,7 @@ F src/resolve.c 797088662ed61102485e3070ba3b3f7828bd5ef6a588223ba6865d77d52f6cea F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac F src/select.c ae7396a314cc1bb1d767947cd57094e3a9ffcbb155ebc1b1c391e028c44a9a04 F src/shell.c.in 6e0aad854be738a5d0368940459399be211e9ac43aebe92bb9ed46cfe38d0e1f -F src/sqlite.h.in c6451bb876adced3aba5b1682c6317d215c5eceaba21a6ce979e71a0b8d0bf95 +F src/sqlite.h.in 82b5768e36ce796ecf93c73bd88bad99def831ce7d470138e213ac693bf4ceab F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 9887b27e69c01e79c2cbe74ef73bf01af5b5703d6a7f0a4371e386d7249cb1c7 F src/sqliteInt.h a5d212bb7ae5cfc0540af6fb09eee2092a45fe083fac4191ee64ff70e7d4d78a @@ -508,7 +508,7 @@ F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6 F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 F src/tclsqlite.c e0bf71a6d24b8c23393c000abffab05979bbca2a72d0b0f79260e2cf1527fda5 -F src/test1.c 55424c026dd93c06ad84ff4e46cec64aa3e12e767d50c31886e6a69ee53fe81e +F src/test1.c 31c491ccb536bd9916a084e732ffe783b3c8973f2586d5a56aed0e3a9701dfff F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5 F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644 F src/test4.c 18ec393bb4d0ad1de729f0b94da7267270f3d8e6 @@ -580,8 +580,8 @@ F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2 F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392 F src/vtab.c 678992ac8ec677a3f9b08126aaf891441083805e3b42574e3654d44538381c14 F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 -F src/wal.c d44a0811afd2155b1157c38b33141d4ac028fda6232485bed664015bb05819ca -F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a +F src/wal.c d0d541116c378937fad99d89737cf36cfc67ff94ba1d29b2bf93bc7333e07e25 +F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a F src/walker.c ba7225773931760cf60bf22f34d0cce2588df7ce5ce0f215a52eb88234b55ac4 F src/where.c 155809967fbab889374dedf970ea6561b8fb519fcb165d6ba00776552ecc5cde F src/whereInt.h b90ef9b9707ef750eab2a7a080c48fb4900315033274689def32d0cf5a81ebe4 @@ -1269,10 +1269,11 @@ F test/skipscan2.test ef143c6e4a5ba4f19c1d1e3f517811f7942bdf2142736cc568feb34e0b F test/skipscan3.test ec5bab3f81c7038b43450e7b3062e04a198bdbb5 F test/skipscan5.test 67817a4b6857c47e0e33ba3e506da6f23ef68de2 F test/skipscan6.test 0b4cd1b4ac9f84d91454df513c99a4932fa07e8f27b8049bea605068b3e34ac7 -F test/snapshot.test 85735bd997a4f6d710140c28fd860519a299649f +F test/snapshot.test fef12fc5c16ff21c4748509401cfba7d9a3d91156f1bfe23fb881d3bfc65ddfe F test/snapshot2.test 925e42427e923262db63c9d7155183f889e3e99feaedec4075f659e51608344f F test/snapshot3.test 9719443594a04778861bd20d12596c5f880af177d6cd62f111da3198cafc6096 F test/snapshot_fault.test 52c5e97ebd218846a8ae2da4d147d3e77d71f963 +F test/snapshot_up.test b778a04561a67b8bfde828f473a8d31dbde23e3f648e36237e0369421e08f23c F test/soak.test 18944cf21b94a7fe0df02016a6ee1e9632bc4e8d095a0cb49d95e15d5cca2d5c F test/softheap1.test 843cd84db9891b2d01b9ab64cef3e9020f98d087 F test/sort.test c2adc635c2564241fefec0b3a68391ef6868fd3b @@ -1754,7 +1755,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 1caaaaa70f21fe71fbe0af227eea8d1367870e2575eedf248cc2a0b515783390 -R 599391bf767ca60a524e57a614c06c9d -U drh -Z e1c623c52da1d23ca607e4f314a88bce +P 20c995d3f0f4de5410962172cb59da0f25edf0c62e199420186cc59ea874e981 +R 462315bcd4a2eb76f7dc221ad2f185d1 +T *branch * exp-snapshot-open +T *sym-exp-snapshot-open * +T -sym-trunk * +U dan +Z f721373f748dbb5b0d3c5c4f1daf5230 diff --git a/manifest.uuid b/manifest.uuid index 1b923067c8..ec1e91062f 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -20c995d3f0f4de5410962172cb59da0f25edf0c62e199420186cc59ea874e981 \ No newline at end of file +051ac0152048ef52723196c26ca5f2629dafb782aec1c66fc30531bf54335043 \ No newline at end of file diff --git a/src/main.c b/src/main.c index 4ae5c7ecdd..4ea7c20779 100644 --- a/src/main.c +++ b/src/main.c @@ -4209,11 +4209,29 @@ int sqlite3_snapshot_open( iDb = sqlite3FindDbName(db, zDb); if( iDb==0 || iDb>1 ){ Btree *pBt = db->aDb[iDb].pBt; - if( 0==sqlite3BtreeIsInReadTrans(pBt) ){ - rc = sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), pSnapshot); + if( sqlite3BtreeIsInTrans(pBt)==0 ){ + Pager *pPager = sqlite3BtreePager(pBt); + int bUnlock = 0; + if( sqlite3BtreeIsInReadTrans(pBt) ){ + if( db->nVdbeActive==0 ){ + rc = sqlite3PagerSnapshotCheck(pPager, pSnapshot); + if( rc==SQLITE_OK ){ + bUnlock = 1; + rc = sqlite3BtreeCommit(pBt); + } + } + }else{ + rc = SQLITE_OK; + } + if( rc==SQLITE_OK ){ + rc = sqlite3PagerSnapshotOpen(pPager, pSnapshot); + } if( rc==SQLITE_OK ){ rc = sqlite3BtreeBeginTrans(pBt, 0, 0); - sqlite3PagerSnapshotOpen(sqlite3BtreePager(pBt), 0); + sqlite3PagerSnapshotOpen(pPager, 0); + } + if( bUnlock ){ + sqlite3PagerSnapshotUnlock(pPager); } } } diff --git a/src/pager.c b/src/pager.c index 9fafa17cb0..a7d076ab0b 100644 --- a/src/pager.c +++ b/src/pager.c @@ -7653,6 +7653,38 @@ int sqlite3PagerSnapshotRecover(Pager *pPager){ } return rc; } + +/* +** The caller currently has a read transaction open on the database. +** If this is not a WAL database, SQLITE_ERROR is returned. Otherwise, +** this function takes a SHARED lock on the CHECKPOINTER slot and then +** checks if the snapshot passed as the second argument is still +** available. If so, SQLITE_OK is returned. +** +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER +** lock is released before returning. +*/ +int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot){ + int rc; + if( pPager->pWal ){ + rc = sqlite3WalSnapshotCheck(pPager->pWal, pSnapshot); + }else{ + rc = SQLITE_ERROR; + } + return rc; +} + +/* +** Release a lock obtained by an earlier successful call to +** sqlite3PagerSnapshotCheck(). +*/ +void sqlite3PagerSnapshotUnlock(Pager *pPager){ + assert( pPager->pWal ); + return sqlite3WalSnapshotUnlock(pPager->pWal); +} + #endif /* SQLITE_ENABLE_SNAPSHOT */ #endif /* !SQLITE_OMIT_WAL */ diff --git a/src/pager.h b/src/pager.h index 730e366fbb..5b07a226b8 100644 --- a/src/pager.h +++ b/src/pager.h @@ -186,6 +186,8 @@ int sqlite3PagerSharedLock(Pager *pPager); int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot); int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot); int sqlite3PagerSnapshotRecover(Pager *pPager); + int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot); + void sqlite3PagerSnapshotUnlock(Pager *pPager); # endif #else # define sqlite3PagerUseWal(x,y) 0 diff --git a/src/sqlite.h.in b/src/sqlite.h.in index bdecac5d10..5222cee391 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -9035,22 +9035,33 @@ SQLITE_EXPERIMENTAL int sqlite3_snapshot_get( ** CAPI3REF: Start a read transaction on an historical snapshot ** METHOD: sqlite3_snapshot ** -** ^The [sqlite3_snapshot_open(D,S,P)] interface starts a -** read transaction for schema S of -** [database connection] D such that the read transaction -** refers to historical [snapshot] P, rather than the most -** recent change to the database. -** ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK on success -** or an appropriate [error code] if it fails. -** -** ^In order to succeed, a call to [sqlite3_snapshot_open(D,S,P)] must be -** the first operation following the [BEGIN] that takes the schema S -** out of [autocommit mode]. -** ^In other words, schema S must not currently be in -** a transaction for [sqlite3_snapshot_open(D,S,P)] to work, but the -** database connection D must be out of [autocommit mode]. -** ^A [snapshot] will fail to open if it has been overwritten by a -** [checkpoint]. +** ^The [sqlite3_snapshot_open(D,S,P)] interface either starts a new read +** transaction or upgrades an existing one for schema S of +** [database connection] D such that the read transaction refers to +** historical [snapshot] P, rather than the most recent change to the +** database. ^The [sqlite3_snapshot_open()] interface returns SQLITE_OK +** on success or an appropriate [error code] if it fails. +** +** ^In order to succeed, the database connection must not be in +** [autocommit mode] when [sqlite3_snapshot_open(D,S,P)] is called. If there +** is already a read transaction open on schema S, then the database handle +** must have no active statements (SELECT statements that have been passed +** to sqlite3_step() but not sqlite3_reset() or sqlite3_finalize()). +** SQLITE_ERROR is returned if either of these conditions is violated, or +** if schema S does not exist, or if the snapshot object is invalid. +** +** ^A call to sqlite3_snapshot_open() will fail to open if the specified +** snapshot has been overwritten by a [checkpoint]. In this case +** SQLITE_BUSY_SNAPSHOT is returned. +** +** If there is already a read transaction open when this function is +** invoked, then the same read transaction remains open (on the same +** database snapshot) if SQLITE_ERROR, SQLITE_BUSY or SQLITE_BUSY_SNAPSHOT +** is returned. If another error code - for example SQLITE_PROTOCOL or an +** SQLITE_IOERR error code - is returned, then the final state of the +** read transaction is undefined. If SQLITE_OK is returned, then the +** read transaction is now open on database snapshot P. +** ** ^(A call to [sqlite3_snapshot_open(D,S,P)] will fail if the ** database connection D does not know that the database file for ** schema S is in [WAL mode]. A database connection might not know diff --git a/src/test1.c b/src/test1.c index 93c71aa320..7f301714bd 100644 --- a/src/test1.c +++ b/src/test1.c @@ -2393,6 +2393,8 @@ static int SQLITE_TCLAPI test_snapshot_open( if( rc!=SQLITE_OK ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); return TCL_ERROR; + }else{ + Tcl_ResetResult(interp); } return TCL_OK; } diff --git a/src/wal.c b/src/wal.c index 885a2bd733..0ea327161b 100644 --- a/src/wal.c +++ b/src/wal.c @@ -3769,6 +3769,43 @@ int sqlite3_snapshot_cmp(sqlite3_snapshot *p1, sqlite3_snapshot *p2){ if( pHdr1->mxFrame>pHdr2->mxFrame ) return +1; return 0; } + +/* +** The caller currently has a read transaction open on the database. +** This function takes a SHARED lock on the CHECKPOINTER slot and then +** checks if the snapshot passed as the second argument is still +** available. If so, SQLITE_OK is returned. +** +** If the snapshot is not available, SQLITE_ERROR is returned. Or, if +** the CHECKPOINTER lock cannot be obtained, SQLITE_BUSY. If any error +** occurs (any value other than SQLITE_OK is returned), the CHECKPOINTER +** lock is released before returning. +*/ +int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot){ + int rc; + rc = walLockShared(pWal, WAL_CKPT_LOCK); + if( rc==SQLITE_OK ){ + WalIndexHdr *pNew = (WalIndexHdr*)pSnapshot; + if( memcmp(pNew->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt)) + || pNew->mxFramenBackfillAttempted + ){ + rc = SQLITE_BUSY_SNAPSHOT; + walUnlockShared(pWal, WAL_CKPT_LOCK); + } + } + return rc; +} + +/* +** Release a lock obtained by an earlier successful call to +** sqlite3WalSnapshotCheck(). +*/ +void sqlite3WalSnapshotUnlock(Wal *pWal){ + assert( pWal ); + walUnlockShared(pWal, WAL_CKPT_LOCK); +} + + #endif /* SQLITE_ENABLE_SNAPSHOT */ #ifdef SQLITE_ENABLE_ZIPVFS diff --git a/src/wal.h b/src/wal.h index d97300a684..1610607481 100644 --- a/src/wal.h +++ b/src/wal.h @@ -132,6 +132,8 @@ int sqlite3WalHeapMemory(Wal *pWal); int sqlite3WalSnapshotGet(Wal *pWal, sqlite3_snapshot **ppSnapshot); void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot); int sqlite3WalSnapshotRecover(Wal *pWal); +int sqlite3WalSnapshotCheck(Wal *pWal, sqlite3_snapshot *pSnapshot); +void sqlite3WalSnapshotUnlock(Wal *pWal); #endif #ifdef SQLITE_ENABLE_ZIPVFS diff --git a/test/snapshot.test b/test/snapshot.test index 99d3ed47c3..10400523da 100644 --- a/test/snapshot.test +++ b/test/snapshot.test @@ -217,9 +217,19 @@ foreach {tn tcl} { SELECT * FROM t2; } } {a b c d e f} - do_test $tn.3.2.2 { - list [catch {snapshot_open db main $snapshot } msg] $msg + + # Update - it is no longer an error to have a read-transaction open, + # provided there are no active SELECT statements. + do_test $tn.3.2.2a { + db eval "SELECT * FROM t2" { + set res [list [catch {snapshot_open db main $snapshot } msg] $msg] + break + } + set res } {1 SQLITE_ERROR} + do_test $tn.3.2.2b { + snapshot_open db main $snapshot + } {} do_test $tn.3.2.3 { execsql { @@ -231,12 +241,17 @@ foreach {tn tcl} { } {1 SQLITE_ERROR} do_execsql_test $tn.3.2.4 COMMIT - do_test $tn.3.3.1 { + do_test $tn.3.3.1a { execsql { PRAGMA journal_mode = DELETE } execsql { BEGIN } list [catch {snapshot_open db main $snapshot } msg] $msg } {1 SQLITE_ERROR} + do_test $tn.3.3.1b { + execsql { COMMIT ; BEGIN ; SELECT * FROM t2 } + list [catch {snapshot_open db main $snapshot } msg] $msg + } {1 SQLITE_ERROR} + do_test $tn.$tn.3.3.2 { snapshot_free $snapshot execsql COMMIT diff --git a/test/snapshot_up.test b/test/snapshot_up.test new file mode 100644 index 0000000000..f7c2e443cf --- /dev/null +++ b/test/snapshot_up.test @@ -0,0 +1,184 @@ +# 2018 August 6 +# +# 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. +# +#*********************************************************************** +# +# Tests for calling sqlite3_snapshot_open() when there is already +# a read transaction open on the database. +# + +set testdir [file dirname $argv0] +source $testdir/tester.tcl +ifcapable !snapshot {finish_test; return} +set testprefix snapshot_up + +# 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 +} + +do_execsql_test 1.0 { + CREATE TABLE t1(a, b, c); + PRAGMA journal_mode = wal; + INSERT INTO t1 VALUES(1, 2, 3); + INSERT INTO t1 VALUES(4, 5, 6); + INSERT INTO t1 VALUES(7, 8, 9); +} {wal} + +do_test 1.1 { + execsql BEGIN + set ::snap1 [sqlite3_snapshot_get db main] + execsql COMMIT + execsql { INSERT INTO t1 VALUES(10, 11, 12); } + execsql BEGIN + set ::snap2 [sqlite3_snapshot_get db main] + execsql COMMIT + execsql { INSERT INTO t1 VALUES(13, 14, 15); } + execsql BEGIN + set ::snap3 [sqlite3_snapshot_get db main] + execsql COMMIT +} {} + +do_execsql_test 1.2 { + BEGIN; + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8 9 10 11 12 13 14 15} + +do_test 1.3 { + sqlite3_snapshot_open db main $::snap1 + execsql { SELECT * FROM t1 } +} {1 2 3 4 5 6 7 8 9} + +do_test 1.4 { + sqlite3_snapshot_open db main $::snap2 + execsql { SELECT * FROM t1 } +} {1 2 3 4 5 6 7 8 9 10 11 12} + +do_test 1.5 { + sqlite3 db2 test.db + execsql { PRAGMA wal_checkpoint } db2 +} {0 5 4} + +do_execsql_test 1.6 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8 9 10 11 12} + +do_test 1.7 { + list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg +} {1 SQLITE_BUSY_SNAPSHOT} + +do_execsql_test 1.8 { + SELECT * FROM t1 +} {1 2 3 4 5 6 7 8 9 10 11 12} + +do_test 1.9 { + execsql { COMMIT ; BEGIN } + list [catch { sqlite3_snapshot_open db main $::snap1 } msg] $msg +} {1 SQLITE_BUSY_SNAPSHOT} + +do_test 1.10 { + execsql { COMMIT } + execsql { + PRAGMA wal_checkpoint; + DELETE FROM t1 WHERE a = 1; + } db2 + execsql BEGIN + set ::snap4 [sqlite3_snapshot_get db main] + execsql COMMIT + execsql { + DELETE FROM t1 WHERE a = 4; + } db2 +} {} + +do_test 1.11 { + execsql { + BEGIN; + SELECT * FROM t1 + } +} {7 8 9 10 11 12 13 14 15} +do_test 1.12 { + sqlite3_snapshot_open db main $::snap4 + execsql { SELECT * FROM t1 } +} {4 5 6 7 8 9 10 11 12 13 14 15} + +do_test 1.13 { + list [catch { sqlite3_snapshot_open db main $::snap3 } msg] $msg +} {1 SQLITE_BUSY_SNAPSHOT} +do_test 1.14 { + execsql { SELECT * FROM t1 } +} {4 5 6 7 8 9 10 11 12 13 14 15} + +db close +db2 close +sqlite3 db test.db +do_execsql_test 1.15 { + BEGIN; + SELECT * FROM t1 +} {7 8 9 10 11 12 13 14 15} +do_test 1.16 { + list [catch { sqlite3_snapshot_open db main $::snap4 } msg] $msg +} {1 SQLITE_BUSY_SNAPSHOT} +do_execsql_test 1.17 { COMMIT } + +sqlite3_snapshot_free $::snap1 +sqlite3_snapshot_free $::snap2 +sqlite3_snapshot_free $::snap3 +sqlite3_snapshot_free $::snap4 + +#------------------------------------------------------------------------- +catch { db close } +sqlite3 db test.db +sqlite3 db2 test.db +sqlite3 db3 test.db + +proc xBusy {args} { return 1 } +db3 busy xBusy + +do_test 2.1 { + execsql { INSERT INTO t1 VALUES(16, 17, 18) } db2 + execsql BEGIN + set ::snap1 [sqlite3_snapshot_get db main] + execsql COMMIT + execsql { INSERT INTO t1 VALUES(19, 20, 21) } db2 + execsql BEGIN + set ::snap2 [sqlite3_snapshot_get db main] + execsql COMMIT + set {} {} +} {} + +do_execsql_test -db db2 2.2 { + BEGIN; + INSERT INTO t1 VALUES(19, 20, 21); +} + +do_test 2.3 { + execsql BEGIN + sqlite3_snapshot_open db main $::snap1 + execsql { SELECT * FROM t1 } +} {7 8 9 10 11 12 13 14 15 16 17 18} + +proc xBusy {args} { + set ::res [list [catch { sqlite3_snapshot_open db main $::snap2 } msg] $msg] + return 1 +} +db3 busy xBusy +do_test 2.4 { + execsql {PRAGMA wal_checkpoint = restart} db3 + set ::res +} {1 SQLITE_BUSY} + +sqlite3_snapshot_free $::snap1 +sqlite3_snapshot_free $::snap2 + +finish_test +