]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Merge the latest trunk enhancements into the wal2 branch.
authordrh <>
Thu, 22 Jun 2023 14:20:03 +0000 (14:20 +0000)
committerdrh <>
Thu, 22 Jun 2023 14:20:03 +0000 (14:20 +0000)
FossilOrigin-Name: 80ac494f734d9720fbbb1111741aa99fc319c6c9a32e7f13b5962001d6ace828

1  2 
Makefile.in
Makefile.msc
main.mk
manifest
manifest.uuid
src/btree.c
src/pager.c
src/pragma.c
src/vdbe.c
src/wal.c

diff --cc Makefile.in
Simple merge
diff --cc Makefile.msc
Simple merge
diff --cc main.mk
Simple merge
diff --cc manifest
index 4bcb9b65d43f27d36c5d99ef69242f8bbb11afb4,078f72b8b23b0c677d21448ca15032411c5d3653..816abbb2b142579ca97212055d3590efcfd9e375
+++ b/manifest
@@@ -1,13 -1,13 +1,13 @@@
- C Merge\sall\s3.42.0\srelease\schanges\sinto\sthe\swal2\sbranch.
- D 2023-05-16T13:00:36.381
 -C Simplification\sto\sthe\sgetAndInitPage()\sroutine\sthat\sresults\sin\simproved\nperformance.
 -D 2023-06-22T01:03:39.798
++C Merge\sthe\slatest\strunk\senhancements\sinto\sthe\swal2\sbranch.
++D 2023-06-22T14:20:03.358
  F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
  F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
  F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
- F Makefile.in e9c71f89ac588465e882ac424fa2f567d628ccaf18c967ddfb99a068d0679a71
 -F Makefile.in 0f4cb3955aaff8a40ec3857ba1784bd98b69802e51eff979f874b65713b627b2
++F Makefile.in 3a780c12cb464f43e680a4f36f59615d45acf5a6762b56c205b6ef51ebd26c6d
  F Makefile.linux-gcc f609543700659711fbd230eced1f01353117621dccae7b9fb70daa64236c5241
- F Makefile.msc 4ec8369d75cc0a6f833eb49cfd5a31db5a0094a83474271b488e51523eccd816
- F README.md e05bd8fcb45da04ab045c37f79a98654e8aa3b3b8f302cfbba80a0d510df75f7
- F VERSION 17f95ae2fdf21f0e9575eb0b0511ea63f15d71dfff431b21c2b4adbfa70cfbbf
 -F Makefile.msc 7248d860f71ab164b4cec3c415e6cc1bd9fee860c370d65bd8bb49e9572521e2
++F Makefile.msc 97f9b14ef0418943c315db08f4961932f2fc839603c13ab646079840b0d83329
+ F README.md 8ff80689b9cb9f6e9b842edf31a3358ff53bc538c351799e03dd3e5455e637e5
+ F VERSION c6366dc72582d3144ce87b013cc35fe48d62f6d07d5be0c9716ea33c862144aa
  F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
  F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
  F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
@@@ -496,16 -496,16 +498,16 @@@ F ext/wasm/api/post-js-header.js 47b6b2
  F ext/wasm/api/pre-js.c-pp.js ad906703f7429590f2fbf5e6498513bf727a1a4f0ebfa057afb08161d7511219
  F ext/wasm/api/sqlite3-api-cleanup.js cc21e3486da748463e02bbe51e2464c6ac136587cdfd5aa00cd0b5385f6ca808
  F ext/wasm/api/sqlite3-api-glue.js f1b2dcb944de5138bb5bd9a1559d2e76a4f3ec25260963d709e8237476688803
- F ext/wasm/api/sqlite3-api-oo1.js 2691a34a741015127b210954a1b9586764d3ff0c8a20f00fd15c00f339ecc79f
+ F ext/wasm/api/sqlite3-api-oo1.js 9678dc4d9a5d39632b6ffe6ea94a023119260815bf32f265bf5f6c36c9516db8
  F ext/wasm/api/sqlite3-api-prologue.js 17f4ec398ba34c5c666fea8e8c4eb82064a35b302f2f2eb355283cd8d3f68ed5
- F ext/wasm/api/sqlite3-api-worker1.js 40a5b1813fcbe789f23ae196c833432c8c83e7054d660194ddfc51eab1c5b9bf
+ F ext/wasm/api/sqlite3-api-worker1.js 9f32af64df1a031071912eea7a201557fe39b1738645c0134562bb84e88e2fec
  F ext/wasm/api/sqlite3-license-version-header.js 0c807a421f0187e778dc1078f10d2994b915123c1223fe752b60afdcd1263f89
- F ext/wasm/api/sqlite3-opfs-async-proxy.js 70914ae97784d3028150bbf252e07a423056c42cc345903c81b5fae661ce512f
+ F ext/wasm/api/sqlite3-opfs-async-proxy.js 961bbc3ccc1fa4e91d6519a96e8811ad7ae60173bd969fee7775dacb6eee1da2
  F ext/wasm/api/sqlite3-v-helper.js e5c202a9ecde9ef818536d3f5faf26c03a1a9f5192b1ddea8bdabf30d75ef487
- F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 89640e4874a60cb2d973306b272384ffb45c7915375c7bb0355c7586f88dc39c
+ F ext/wasm/api/sqlite3-vfs-opfs.c-pp.js 05f2563ddebfdc7a0f0ac0eb7cb381bb72043299aae1600ba9367c12f52b3fcc
  F ext/wasm/api/sqlite3-wasi.h 25356084cfe0d40458a902afb465df8c21fc4152c1d0a59b563a3fba59a068f9
  F ext/wasm/api/sqlite3-wasm.c 12a096d8e58a0af0589142bae5a3c27a0c7e19846755a1a37d2c206352fbedda
 -F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js bc06df0d599e625bde6a10a394e326dc68da9ff07fa5404354580f81566e591f
 +F ext/wasm/api/sqlite3-worker1-promiser.c-pp.js f10c3ecd9df06f6320073c2ce230a7ed7c56034d8b88c1e57095f2a97faf423a
  F ext/wasm/api/sqlite3-worker1.c-pp.js da509469755035e919c015deea41b4514b5e84c12a1332e6cc8d42cb2cc1fb75
  F ext/wasm/batch-runner.html 4deeed44fe41496dc6898d9fb17938ea3291f40f4bfb977e29d0cef96fbbe4c8
  F ext/wasm/batch-runner.js 0dad6a02ad796f1003d3b7048947d275c4d6277f63767b8e685c27df8fdac93e
@@@ -557,8 -557,7 +559,7 @@@ F ext/wasm/wasmfs.make cf9a68162d92ca2b
  F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
  F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
  F magic.txt 5ade0bc977aa135e79e3faaea894d5671b26107cc91e70783aa7dc83f22f3ba0
- F main.mk ca46247ab1e6969b3261981c81dab2d41fdcb867eebe341b7b7e607a1b98b3bf
- F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
 -F main.mk 0eb735008653412026092961cecdf7d698156c060e4062a69d911335982d471c
++F main.mk b7cf0350144fdf7551ecca064272b88098dcf206cb0d4c79754ad734a8f1c24b
  F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
  F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
  F mptest/crash01.test 61e61469e257df0850df4293d7d4d6c2af301421
@@@ -570,41 -569,41 +571,41 @@@ F sqlite.pc.in 42b7bf0d02e08b9e77734a47
  F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
  F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
  F sqlite_cfg.h.in baf2e409c63d4e7a765e17769b6ff17c5a82bbd9cbf1e284fd2e4cefaff3fcf2
- F src/alter.c 482c534877fbb543f8295992cde925df55443febac5db5438d5aaba6f78c4940
- F src/analyze.c a1f3061af16c99f73aed0362160176c31a6452de1b02ada1d68f6839f2a37df0
+ F src/alter.c 3ff8c2fca0c0636d43459154bb40d79c882df1b34df77f89c4ec47ab2e2389f5
+ F src/analyze.c d4cc28738c29e009640ec20ebb6936ba6fcefff0d11aa93398d9bb9a5ead6c1f
  F src/attach.c cc9d00d30da916ff656038211410ccf04ed784b7564639b9b61d1839ed69fd39
- F src/auth.c f4fa91b6a90bbc8e0d0f738aa284551739c9543a367071f55574681e0f24f8cf
+ F src/auth.c 19b7ccacae3dfba23fc6f1d0af68134fa216e9040e53b0681b4715445ea030b4
  F src/backup.c 5c97e8023aab1ce14a42387eb3ae00ba5a0644569e3476f38661fa6f824c3523
- F src/bitvec.c 7c849aac407230278445cb069bebc5f89bf2ddd87c5ed9459b070a9175707b3d
- F src/btmutex.c 6ffb0a22c19e2f9110be0964d0731d2ef1c67b5f7fabfbaeb7b9dabc4b7740ca
- F src/btree.c b242c215307e9b4e7739163d003e53a63c1a9873f446d92d44af3cce082b98fb
+ F src/bitvec.c 9eac5f42c11914d5ef00a75605bb205e934f435c579687f985f1f8b0995c8645
+ F src/btmutex.c 79a43670447eacc651519a429f6ece9fd638563cf95b469d6891185ddae2b522
 -F src/btree.c c0c93b6cb4dc133b528c1290bb4ad0f2414452f9a5758ff2b106af718874f39e
++F src/btree.c b1adc6a45e81565f6b399b98d6151a419589dd6329a4b3f936f66c1e59e42ce8
  F src/btree.h aa354b9bad4120af71e214666b35132712b8f2ec11869cb2315c52c81fad45cc
- F src/btreeInt.h b900603c8956bdeb313841f9b67bdeceef32c64d962d35477c07ec25e8cf0f9b
- F src/build.c 7a7217f75f202eff03617ca447bb9c3bc07d5af49da1d3cff2b1a88e8e394686
+ F src/btreeInt.h 3b4eff7155c0cea6971dc51f62e3529934a15a6640ec607dd42a767e379cb3a9
+ F src/build.c a8ae3b32d9aa9bbd2c0e97d7c0dd80def9fbca408425de1608f57ee6f47f45f4
  F src/callback.c db3a45e376deff6a16c0058163fe0ae2b73a2945f3f408ca32cf74960b28d490
  F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
 -F src/ctime.c 20507cc0b0a6c19cd882fcd0eaeda32ae6a4229fb4b024cfdf3183043d9b703d
 +F src/ctime.c f439c21d439aaf4706950a2597474f1a796b557a0750666308202c0f601ef0fd
- F src/date.c aca9e0c08b400b21238b609aea7c09585396cd770985cf8f475560f69222dad3
+ F src/date.c f73f203b3877cef866c60ab402aec2bf89597219b60635cf50cbe3c5e4533e94
  F src/dbpage.c f3eea5f7ec47e09ee7da40f42b25092ecbe961fc59566b8e5f705f34335b2387
  F src/dbstat.c ec92074baa61d883de58c945162d9e666c13cd7cf3a23bc38b4d1c4d0b2c2bef
- F src/delete.c a9c6d3f51c0a31e9b831e0a0580a98d702904b42d216fee530940e40dec34873
- F src/expr.c 941fe758212c6cf0007c6d7daf5368e11c199376ace9b3018494296e18a27eac
+ F src/delete.c cd5f5cd06ed0b6a882ec1a8c2a0d73b3cecb28479ad19e9931c4706c5e2182be
+ F src/expr.c 36f6a47c8a2c20ec3c267a60fc598857876edd60af0cb40caf7b69b651fd73bf
  F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
- F src/fkey.c 03c134cc8bffe54835f742ddea0b72ebfc8f6b32773d175c71b8afeea6cb5c83
- F src/func.c 03e6b501f3056d0ba398bda17df938b2b566aa0b3ca7e1942a3cd1925d04ec36
+ F src/fkey.c a7fcbf7e66d14dbb73cf49f31489ebf66d0e6006c62b95246924a3bae9f37b36
+ F src/func.c 6303e1ccb80dbd0d9b52f902a01d3b105981486fdfd66f9e1ddfd74aaf3032fc
  F src/global.c bd0892ade7289f6e20bff44c07d06371f2ff9b53cea359e7854b9b72f65adc30
- F src/hash.c c6af5f96a7a76d000f07c5402c48c318c2566beecdee9e78b9d9f60ce7119565
+ F src/hash.c 9ee4269fb1d6632a6fecfb9479c93a1f29271bddbbaf215dd60420bcb80c7220
  F src/hash.h 3340ab6e1d13e725571d7cee6d3e3135f0779a7d8e76a9ce0a85971fa3953c51
- F src/hwtime.h b638809e083b601b618df877b2e89cb87c2a47a01f4def10be4c4ebb54664ac7
+ F src/hwtime.h f9c2dfb84dce7acf95ce6d289e46f5f9d3d1afd328e53da8f8e9008e3b3caae6
  F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
- F src/insert.c a8de1db43335fc4946370a7a7e47d89975ad678ddb15078a150e993ba2fb37d4
- F src/json.c 39b1c7527f3111923e65f168a87b03b591f12a41400a63d05c119794bee36620
+ F src/insert.c 3f0a94082d978bbdd33c38fefea15346c6c6bffb70bc645a71dc0f1f87dd3276
+ F src/json.c 14c474fb1249a46eb44e878e2361f36abfe686b134039b0d1883d93d61505b4a
  F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
- F src/loadext.c be5af440f3192c58681b5d43167dbca3ccbfce394d89faa22378a14264781136
- F src/main.c 035be2e9ba2a0fc1701a8ab1880af3001a968a24556433538a6c073558ee4341
+ F src/loadext.c 176d6b2cb18a6ad73b133db17f6fc351c4d9a2d510deebdb76c22bde9cfd1465
+ F src/main.c 5fd4b65d61ae6155f36756ed508a39b38b49355b031188961e8d923f43f4bc49
  F src/malloc.c 47b82c5daad557d9b963e3873e99c22570fb470719082c6658bf64e3012f7d23
  F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
- F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
+ F src/mem1.c 3bb59158c38e05f6270e761a9f435bf19827a264c13d1631c58b84bdc96d73b2
  F src/mem2.c c8bfc9446fd0798bddd495eb5d9dbafa7d4b7287d8c22d50a83ac9daa26d8a75
  F src/mem3.c 30301196cace2a085cbedee1326a49f4b26deff0af68774ca82c1f7c06fda4f6
  F src/mem5.c b7da5c10a726aacacc9ad7cdcb0667deec643e117591cc69cf9b4b9e7f3e96ff
@@@ -622,39 -621,39 +623,39 @@@ F src/os.h 1ff5ae51d339d0e30d8a9d814f4b
  F src/os_common.h 6c0eb8dd40ef3e12fe585a13e709710267a258e2c8dd1c40b1948a1d14582e06
  F src/os_kv.c 4d39e1f1c180b11162c6dc4aa8ad34053873a639bac6baae23272fc03349986a
  F src/os_setup.h 6011ad7af5db4e05155f385eb3a9b4470688de6f65d6166b8956e58a3d872107
- F src/os_unix.c 1b3ddb7814c4bf37f494c04d2ab30c1ced5b2c927267e1930ce7cd388787a96d
- F src/os_win.c 2b2411279f7b24f927591561303fc5871845732df42641cbf695c23640b16975
+ F src/os_unix.c 95b407307deb902a3bd9a5d5666c7838709cccb337baeee6ef0a53f512d3673e
+ F src/os_win.c 7038223a1cda0a47e2ab4db47f63bf1833fe53ba0542f0f283a062ea13894103
  F src/os_win.h 7b073010f1451abe501be30d12f6bc599824944a
- F src/pager.c 1a68546b71c343a028e09c3c1faa4952ed38d71fa4fe30124616ca564cacb94f
 -F src/pager.c 5ddf3a74c633a008ea6b2f5b3186167e88e2c8ca8a252ecab06ab3f1eb48e60f
 -F src/pager.h f82e9844166e1585f5786837ddc7709966138ced17f568c16af7ccf946c2baa3
++F src/pager.c 0a7a695950183a745ec9b2cf0aa3cbc3803dea7d674a1a5a0b0080ab4aa7df9a
 +F src/pager.h c49ff262186a78bc5f27e3891edefb900afa769b9e2eaeca0322c7f3553536d4
- F src/parse.y 146f9a1db7db5ef4299c6897d335e5abed348c2626190d2877d45ffa210fd4ca
- F src/pcache.c 8ee13acccfd9accbf0af94910b7323dd7f7d55300d92ddafcf40e34fcc8e21be
+ F src/parse.y 8828f9e15f04d469eab9c0f2aed504e534b1c97c68836bed6f07afab29c2ac0b
+ F src/pcache.c 4cd4a0043167da9ba7e19b4d179a0e6354e7fe32c16f781ecf9bf0a5ff63b40b
  F src/pcache.h 1497ce1b823cf00094bb0cf3bac37b345937e6f910890c626b16512316d3abf5
- F src/pcache1.c dee95e3cd2b61e6512dc814c5ab76d5eb36f0bfc9441dbb4260fccc0d12bbddc
- F src/pragma.c 450aab7bd07c7570355f3b34df830869c86882f33b7acca842fbdf45146c0c8a
+ F src/pcache1.c 602acb23c471bb8d557a6f0083cc2be641d6cafcafa19e481eba7ef4c9ca0f00
 -F src/pragma.c 37b8fb02d090262280c86e1e2654bf59d8dbfbfe8dc6733f2b968a11374c095a
++F src/pragma.c 3a7c0f8e06b94ee34ded7468002b4a502fcdd23bb58c3677a185a4addae7604b
  F src/pragma.h e690a356c18e98414d2e870ea791c1be1545a714ba623719deb63f7f226d8bb7
- F src/prepare.c 6350675966bd0e7ac3a464af9dbfe26db6f0d4237f4e1f1acdb17b12ad371e6e
+ F src/prepare.c d6c4354f8ea0dc06962fbabc4b68c4471a45276a2918c929be00f9f537f69eb1
  F src/printf.c b9320cdbeca0b336c3f139fd36dd121e4167dd62b35fbe9ccaa9bab44c0af38d
  F src/random.c 606b00941a1d7dd09c381d3279a058d771f406c5213c9932bbd93d5587be4b9c
- F src/resolve.c 3e53e02ce87c9582bd7e7d22f13f4094a271678d9dc72820fa257a2abb5e4032
- F src/rowset.c ba9515a922af32abe1f7d39406b9d35730ed65efab9443dc5702693b60854c92
- F src/select.c 738c3a3d6929f8be66c319bad17f6b297bd60a4eb14006075c48a28487dc7786
- F src/shell.c.in 52836b4002a2cad8095b451f0c39a6542c23a231eb0ed5e39387bc8b1f7aaa9e
- F src/sqlite.h.in c14a4471fcd897a03631ac7ad3d05505e895e7b6419ec5b96cae9bc4df7a9fc6
+ F src/resolve.c 37953a5f36c60bea413c3c04efcd433b6177009f508ef2ace0494728912fe2e9
+ F src/rowset.c 8432130e6c344b3401a8874c3cb49fefe6873fec593294de077afea2dce5ec97
+ F src/select.c 383b9dba12493c365ee2036bcadd73013b7c0f7d2afcda0c378317c335d60ac2
+ F src/shell.c.in bdd1fdfc77a67651cdc5a158bc9107cf3c2cf3ddb62d7a4da06c6eaaa5e72037
+ F src/sqlite.h.in 3076d78836b6dac53b3ab0875fc8fd15bca8077aad4d33c85336e05af6aef8c7
  F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
  F src/sqlite3ext.h da473ce2b3d0ae407a6300c4a164589b9a6bfdbec9462688a8593ff16f3bb6e4
- F src/sqliteInt.h a3ced8b9ebc573189c87b69f24bf10d2b9cd3cefefaae52623a2fa79e6fdd408
- F src/sqliteLimit.h d7323ffea5208c6af2734574bae933ca8ed2ab728083caa117c9738581a31657
+ F src/sqliteInt.h 8974b60740b108269f51e833e85191be6bf9f06f317ee34a53b7ec215762cf8c
+ F src/sqliteLimit.h 33b1c9baba578d34efe7dfdb43193b366111cdf41476b1e82699e14c11ee1fb6
  F src/status.c 160c445d7d28c984a0eae38c144f6419311ed3eace59b44ac6dafc20db4af749
  F src/table.c 0f141b58a16de7e2fbe81c308379e7279f4c6b50eb08efeec5892794a0ba30d1
- F src/tclsqlite.c 8522a04fb9c84faa1d80354430ae0ee9349727a3a4b32e3cfe39b9be8324cabd
- F src/test1.c 8eab61fb2813aa212d97ab188e85fc9ca7b89d9ff5ff05d59d9aa0c491a6c721
+ F src/tclsqlite.c ecbc3c99c0d0c3ed122a913f143026c26d38d57f33e06bb71185dd5c1efe37cd
+ F src/test1.c e6ab4a00671f052366a01bcb7fdf2e2f6bb4aa884cd01e738c5590dcf47a99ca
  F src/test2.c 827446e259a3b7ab949da1542953edda7b5117982576d3e6f1c24a0dd20a5cef
- F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
+ F src/test3.c e5178558c41ff53236ae0271e9acb3d6885a94981d2eb939536ee6474598840e
  F src/test4.c 4533b76419e7feb41b40582554663ed3cd77aaa54e135cf76b3205098cd6e664
  F src/test5.c 328aae2c010c57a9829d255dc099d6899311672d
- F src/test6.c ae73a3a42bbc982fb9e301b84d30bda65a307be48c6dff20aba1461e17a9b0ce
- F src/test8.c 0c856d6ff6b0d2ff6696addc467a15ed17c6910f14475302cd5b3b4e54406161
+ F src/test6.c e53bc69dc3cb3815fb74df74f38159ec05ba6dd5273216062e26bc797f925530
+ F src/test8.c ccc5d3e2a2bf7248f7da185e2afc4c08b4c6840447f5eb4dd106db165fddbdbc
  F src/test9.c 12e5ba554d2d1cbe0158f6ab3f7ffcd7a86ee4e5
  F src/test_async.c 195ab49da082053fdb0f949c114b806a49ca770a
  F src/test_autoext.c 915d245e736652a219a907909bb6710f0d587871
@@@ -662,16 -661,16 +663,16 @@@ F src/test_backup.c bf5da90c9926df0a4b9
  F src/test_bestindex.c 68c62586d2ae9f032903fe53be743657d0c2aac0a850b880938b668e1161d516
  F src/test_blob.c ae4a0620b478548afb67963095a7417cd06a4ec0a56adb453542203bfdcb31ce
  F src/test_btree.c 8b2dc8b8848cf3a4db93f11578f075e82252a274
- F src/test_config.c 8264637b06a3c1f0727c88d1ea32dcf7986b9e7e358a970cae87cdac8a5b2708
+ F src/test_config.c f0cc1f517deaa96dd384822ae2bb91534fa56aa458528b439830d709941d3932
  F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f
  F src/test_demovfs.c 38a459d1c78fd9afa770445b224c485e079018d6ac07332ff9bd07b54d2b8ce9
- F src/test_devsym.c aff2255ea290d7718da08af30cdf18e470ff7325a5eff63e0057b1496ed66593
+ F src/test_devsym.c 649434ed34d0b03fbd5a6b42df80f0f9a7e53f94dd1710aad5dd8831e91c4e86
  F src/test_fs.c ba1e1dc18fd3159fdba0b9c4256f14032159785320dfbd6776eb9973cb75d480
  F src/test_func.c 24df3a346c012b1fc9e1001d346db6054deb426db0a7437e92490630e71c9b0a
 -F src/test_hexio.c 9478e56a0f08e07841a014a93b20e4ba2709ab56d039d1ca8020e26846aa19bd
 +F src/test_hexio.c a605a100e628d39330044ae5f34cb19d50843061ed3178c3f83b37aef65f7e0a
- F src/test_init.c 4413c211a94b62157ca4c145b3f27c497f03c664
+ F src/test_init.c f2cc4774b7c9140f76e45ecbb2ae219f68e3acbbe248c0179db666a70eae9f08
  F src/test_intarray.c 39b4181662a0f33a427748d87218e7578d913e683dc27eab7098bb41617cac71
- F src/test_intarray.h d57ae92f420cda25e22790dac474d60961bd0c500cbaa3338a05152d4a669ef7
+ F src/test_intarray.h 6c3534641108cd1bea517a8e117dcba237081310a29a4c35bd2190caa8972293
  F src/test_journal.c a0b9709b2f12b1ec819eea8a1176f283bca6d688a6d4a502bd6fd79786f4e287
  F src/test_loadext.c 337056bae59f80b9eb00ba82088b39d0f4fe6dfd
  F src/test_malloc.c 21121ea85b49ec0bdb69995847cef9036ef9beca3ce63bbb776e4ea2ecc44b97
@@@ -700,34 -699,34 +701,34 @@@ F src/test_windirent.h da2e5b73c32d0990
  F src/test_window.c cdae419fdcea5bad6dcd9368c685abdad6deb59e9fc8b84b153de513d394ba3f
  F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9
  F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
- F src/tokenize.c 1305797eab3542a0896b552c6e7669c972c1468e11e92b370533c1f37a37082b
- F src/treeview.c fccf3b8c517c1f55cb380c1522febe6921fcb2bd800c16c78cab571d0eb0ccbd
+ F src/tokenize.c 0fb405f9adf3f757c26bfc1ae6d58ac5dccbb918917ba9e5ef0e6673a06563d3
+ F src/treeview.c 1d52fbc4e97161e65858d36e3424ea6e3fc045dd8a679c82b4b9593dc30de3bd
  F src/trigger.c ad6ab9452715fa9a8075442e15196022275b414b9141b566af8cdb7a1605f2b0
- F src/update.c 3f4fb5ad7c9b48d7911974d6579192bb3a6c27f46140b6cbb9139cc8a77b8691
+ F src/update.c 0aa36561167a7c40d01163238c297297962f31a15a8d742216b3c37cdf25f731
  F src/upsert.c 5303dc6c518fa7d4b280ec65170f465c7a70b7ac2b22491598f6d0b4875b3145
  F src/utf.c ee39565f0843775cc2c81135751ddd93eceb91a673ea2c57f61c76f288b041a0
- F src/util.c d4bcb560471cd94e6e17d448311f8d5bf81a7e5276295a53501058ef1b95dd1a
- F src/vacuum.c 84ce7f01f8a7a08748e107a441db83bcec13970190ddcb0c9ff522adbc1c23fd
- F src/vdbe.c 6ca6350f1b56dfb1cc81fecf2ed37d3a0821294236b4ec24ad7947435d2284cc
- F src/vdbe.h 637ae853b7d42ae3951034cc63ab7c8af837861f79504cdb5399552fcd89a884
- F src/vdbeInt.h a4147a4ddf613cb1bcb555ace9e9e74a9c099d65facd88155f191b1fb4d74cfb
- F src/vdbeapi.c b4982cde547054c4f7341198db3c3008a48e1eb028f757601bf5bf2fc026cbcf
- F src/vdbeaux.c 6ee48db408d4c297a363f1e31145c09793a580e7c508bb36063dd017d67117a2
+ F src/util.c 6f9d2f278dcc8d41c618980cd3cfe88e1bafc0626209b917c6773d8202d29ef6
+ F src/vacuum.c 604fcdaebe76f3497c855afcbf91b8fa5046b32de3045bab89cc008d68e40104
 -F src/vdbe.c 6c0de640ef3be08cf2992d588a7501aee0f1003027bc952a6916a35f6e33b4cf
++F src/vdbe.c e3af55622d9ee9d269c9a6e4c1aaa58af2ffda019d8643a45cd7178ab3a53ba8
+ F src/vdbe.h 41485521f68e9437fdb7ec4a90f9d86ab294e9bb8281e33b235915e29122cfc0
+ F src/vdbeInt.h 7bd49eef8f89c1a271fbf12d80a206bf56c876814c5fc6bee340f4e1907095ae
+ F src/vdbeapi.c de9703f8705afc393cc2864669ce28cf9516983c8331d59aa2b978de01634365
+ F src/vdbeaux.c 4d5e68a3850d0b193a692eca6442d7afe35252aaf29728a67adcb542ecabd9ce
  F src/vdbeblob.c 2516697b3ee8154eb8915f29466fb5d4f1ae39ee8b755ea909cefaf57ec5e2ce
- F src/vdbemem.c 1cac4028c0dabbf1f3259f107440e2780e05ac9fe419e9709e6eb4e166ba714b
- F src/vdbesort.c 43756031ca7430f7aec3ef904824a7883c4ede783e51f280d99b9b65c0796e35
+ F src/vdbemem.c 710119a8e35e47813681c48703d65a80ba22792192de90bc51dc0d6366f2a79e
+ F src/vdbesort.c 0d40dca073c94e158ead752ef4225f4fee22dee84145e8c00ca2309afb489015
  F src/vdbetrace.c fe0bc29ebd4e02c8bc5c1945f1d2e6be5927ec12c06d89b03ef2a4def34bf823
  F src/vdbevtab.c aae4bd769410eb7e1d02c42613eec961d514459b1c3c1c63cfc84e92a137daac
- F src/vtab.c 4758a96d36c9a120848386ae603b1ab32a4876e0a1faf81bfcfb524455e583dc
+ F src/vtab.c 1ecf8c3745d29275688d583e12822fa984d421e0286b5ef50c137bc3bf6d7a64
  F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
- F src/wal.c 2bda91cfc1d42df42b013f59c008a6b9ad8827b3453779020b355bc809344272
 -F src/wal.c cbfeeb7415baa545efa244dd34bb5af4ae953a206fed720c6fa7f1ef763ec122
 -F src/wal.h c3aa7825bfa2fe0d85bef2db94655f99870a285778baa36307c0a16da32b226a
++F src/wal.c 2515a345761855b3451895b58f974b3f7d5ec62eaaaf4cca008eb5497fccda33
 +F src/wal.h d01234e828943e002040c22a7e017642962f9fd9b2dc142fa599769ae4e459e9
- F src/walker.c f890a3298418d7cba3b69b8803594fdc484ea241206a8dfa99db6dd36f8cbb3b
- F src/where.c b74a83b4c8f65b218c5c1c8d9122433f85ee1300fd9263ba1697d0e1040eeb36
- F src/whereInt.h e25203e5bfee149f5f1225ae0166cfb4f1e65490c998a024249e98bb0647377c
- F src/wherecode.c b300db0bcd84ad6c2642bf3f509f92fad7b7d697b9856b64dd66d692d184d054
- F src/whereexpr.c 22cf19b0ececeaf838daed1039c5231a8778784eba5ad67b991442a23473fd3f
- F src/window.c e075ea85bea322e30e361fa6e69eddba74f461e99e2a564dc09973f8a1fb27d9
+ F src/walker.c 7c7ea0115345851c3da4e04e2e239a29983b61fb5b038b94eede6aba462640e2
+ F src/where.c 2dc708cf8b6a691fb79f16bbc46567497ee6f991043318d421e294b2da114d93
+ F src/whereInt.h c7d19902863beadec1d04e66aca39c0bcd60b74f05f0eaa7422c7005dfc5d51a
+ F src/wherecode.c bff0bc56cb1a382de266c2db3a691135c18a4360b6ad5e069e5c415d57eb0c38
+ F src/whereexpr.c dc5096eca5ed503999be3bdee8a90c51361289a678d396a220912e9cb73b3c00
+ F src/window.c b7ad9cff3ce8ae6f8cc25e18e1a258426cb6bd2999aace6f5248d781b2a74098
  F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
  F test/affinity2.test ce1aafc86e110685b324e9a763eab4f2a73f737842ec3b687bd965867de90627
  F test/affinity3.test f094773025eddf31135c7ad4cde722b7696f8eb07b97511f98585addf2a510a9
@@@ -2009,8 -1964,8 +1978,8 @@@ F tool/max-limits.c cbb635fbb37ae4d05f2
  F tool/merge-test.tcl de76b62f2de2a92d4c1ca4f976bce0aea6899e0229e250479b229b2a1914b176
  F tool/mkautoconfamal.sh f62353eb6c06ab264da027fd4507d09914433dbdcab9cb011cdc18016f1ab3b8
  F tool/mkccode.tcl 86463e68ce9c15d3041610fedd285ce32a5cf7a58fc88b3202b8b76837650dbe x
 -F tool/mkctimec.tcl 38e3db33210a200aae791635125052a643a27aa0619a0debf19aa9c55e1b2dde x
 +F tool/mkctimec.tcl 64c69c774528f23d623b3b295f4714b49d2b9b44ec0f282b617a3816f1b94bf6 x
- F tool/mkkeywordhash.c 9822bd1f58a70e5f84179df3045bba4791df69180e991bec88662703f2e93761
+ F tool/mkkeywordhash.c b9faa0ae7e14e4dbbcd951cddd786bf46b8a65bb07b129ba8c0cfade723aaffd
  F tool/mkmsvcmin.tcl 8897d515ef7f94772322db95a3b6fce6c614d84fe0bdd06ba5a1c786351d5a1d
  F tool/mkopcodec.tcl 33d20791e191df43209b77d37f0ff0904620b28465cca6990cf8d60da61a07ef
  F tool/mkopcodeh.tcl 769d9e6a8b462323150dc13a8539d6064664b72974f7894befe2491cc73e05cd
@@@ -2084,8 -2041,8 +2055,8 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9
  F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
  F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
  F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P 3bbfbdcd9b0e03b1ed50f41b1714ada5d241b8030f73ced8d0da8fff28a268e0 831d0fb2836b71c9bc51067c49fee4b8f18047814f2ff22d817d25195cf350b0
- R 59994741ac5f3ba035ff58d51126c98c
 -P dc468cfdb825083b3a4b6cb95c913961e9312e22103c5a0cd923b75c83c65e13
 -R 61e892d0fc1ac1905da44ab1762c00e7
++P 137057f95778b3c913854d2182d0fbbfd9dd117db5566dabb5a22d927a59de62 2e9734c2335d8c06fedc9f4cca02baaf326f7fa276bd464f3214f383715a48d6
++R 2f5c3f8e0a859440ee9e176d7384494a
  U drh
- Z 44145ee2c96212f96c05cd0bc7882b39
 -Z c0e995c2a64328b6123221956a5cefda
++Z 1c322048b32c8289d43928aaf4abece7
  # Remove this line to create a well-formed Fossil manifest.
diff --cc manifest.uuid
index e1b84080a7ecf5403aa361ca800424f20b056217,293cf2062a806751802b0e1a47d9769790b636c5..a820650b224e1c1d3c44c105486e9455da31423e
@@@ -1,1 -1,1 +1,1 @@@
- 137057f95778b3c913854d2182d0fbbfd9dd117db5566dabb5a22d927a59de62
 -2e9734c2335d8c06fedc9f4cca02baaf326f7fa276bd464f3214f383715a48d6
++80ac494f734d9720fbbb1111741aa99fc319c6c9a32e7f13b5962001d6ace828
diff --cc src/btree.c
index 3fe4f1d17feaaa2a73d767a3e09e17ff9923c005,aa4e286013c415f2da9404845bc7f9259657d0c2..9be539ee3a084a8b9cc979bfc68a4127fb656701
@@@ -11259,8 -11302,8 +11302,8 @@@ void sqlite3BtreeIncrblobCursor(BtCurso
  int sqlite3BtreeSetVersion(Btree *pBtree, int iVersion){
    BtShared *pBt = pBtree->pBt;
    int rc;                         /* Return code */
-  
 -  assert( iVersion==1 || iVersion==2 );
 +  assert( iVersion==1 || iVersion==2 || iVersion==3 );
  
    /* If setting the version fields to 1, do not automatically open the
    ** WAL connection, even if the version fields are currently set to 2.
diff --cc src/pager.c
index c4cebb6b0af6711c3eba6b9bf37fe982b44668df,7f2cb5ba76388b1c55b5fae5e7deb3790c4ce93f..98692c4162632b9c3b019c1ab11d3d2c2a4c887b
@@@ -917,10 -931,9 +917,10 @@@ static int assert_pager_state(Pager *p)
          ** to journal_mode=wal.
          */
          assert( p->eLock>=RESERVED_LOCK );
-         assert( isOpen(p->jfd) 
-              || p->journalMode==PAGER_JOURNALMODE_OFF 
-              || p->journalMode==PAGER_JOURNALMODE_WAL 
+         assert( isOpen(p->jfd)
+              || p->journalMode==PAGER_JOURNALMODE_OFF
+              || p->journalMode==PAGER_JOURNALMODE_WAL
 +             || p->journalMode==PAGER_JOURNALMODE_WAL2
          );
        }
        assert( pPager->dbOrigSize==pPager->dbFileSize );
        assert( pPager->errCode==SQLITE_OK );
        assert( !pagerUseWal(pPager) );
        assert( p->eLock>=EXCLUSIVE_LOCK );
-       assert( isOpen(p->jfd) 
-            || p->journalMode==PAGER_JOURNALMODE_OFF 
-            || p->journalMode==PAGER_JOURNALMODE_WAL 
+       assert( isOpen(p->jfd)
+            || p->journalMode==PAGER_JOURNALMODE_OFF
+            || p->journalMode==PAGER_JOURNALMODE_WAL
 +           || p->journalMode==PAGER_JOURNALMODE_WAL2
             || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
        );
        assert( pPager->dbOrigSize<=pPager->dbHintSize );
        assert( p->eLock==EXCLUSIVE_LOCK );
        assert( pPager->errCode==SQLITE_OK );
        assert( !pagerUseWal(pPager) );
-       assert( isOpen(p->jfd) 
-            || p->journalMode==PAGER_JOURNALMODE_OFF 
-            || p->journalMode==PAGER_JOURNALMODE_WAL 
+       assert( isOpen(p->jfd)
+            || p->journalMode==PAGER_JOURNALMODE_OFF
+            || p->journalMode==PAGER_JOURNALMODE_WAL
 +           || p->journalMode==PAGER_JOURNALMODE_WAL2
             || (sqlite3OsDeviceCharacteristics(p->fd)&SQLITE_IOCAP_BATCH_ATOMIC)
        );
        break;
@@@ -2059,10 -2070,9 +2059,10 @@@ static int pager_end_transaction(Pager 
        */
        int bDelete = !pPager->tempFile;
        assert( sqlite3JournalIsInMemory(pPager->jfd)==0 );
-       assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE 
-            || pPager->journalMode==PAGER_JOURNALMODE_MEMORY 
+       assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE
+            || pPager->journalMode==PAGER_JOURNALMODE_MEMORY
             || pPager->journalMode==PAGER_JOURNALMODE_WAL
 +           || pPager->journalMode==PAGER_JOURNALMODE_WAL2
        );
        sqlite3OsClose(pPager->jfd);
        if( bDelete ){
@@@ -7599,10 -7590,10 +7594,10 @@@ int sqlite3PagerCloseWal(Pager *pPager
        );
      }
      if( rc==SQLITE_OK && logexists ){
 -      rc = pagerOpenWal(pPager);
 +      rc = pagerOpenWal(pPager, 0);
      }
    }
-     
+    
    /* Checkpoint and close the log. Because an EXCLUSIVE lock is held on
    ** the database file, the log and log-summary files will be deleted.
    */
diff --cc src/pragma.c
Simple merge
diff --cc src/vdbe.c
index fe04be16463fa15344311c11b655f80d519cb78b,7045a1e4bd3ab75424ada566c7d1bf01581b78f7..b58efcb27254ccde98d3d66a6d6e3613aed8a269
@@@ -7720,9 -7719,9 +7720,9 @@@ case OP_JournalMode: {    /* out2 *
    zFilename = sqlite3PagerFilename(pPager, 1);
  
    /* Do not allow a transition to journal_mode=WAL for a database
-   ** in temporary storage or if the VFS does not support shared memory 
+   ** in temporary storage or if the VFS does not support shared memory
    */
 -  if( eNew==PAGER_JOURNALMODE_WAL
 +  if( isWalMode(eNew)
     && (sqlite3Strlen30(zFilename)==0           /* Temp file */
         || !sqlite3PagerWalSupported(pPager))   /* No shared-memory support */
    ){
            (eNew==PAGER_JOURNALMODE_WAL ? "into" : "out of")
        );
        goto abort_due_to_error;
 -    }else{
 -
 -      if( eOld==PAGER_JOURNALMODE_WAL ){
 -        /* If leaving WAL mode, close the log file. If successful, the call
 -        ** to PagerCloseWal() checkpoints and deletes the write-ahead-log
 -        ** file. An EXCLUSIVE lock may still be held on the database file
 -        ** after a successful return.
 -        */
 -        rc = sqlite3PagerCloseWal(pPager, db);
 -        if( rc==SQLITE_OK ){
 -          sqlite3PagerSetJournalMode(pPager, eNew);
 -        }
 -      }else if( eOld==PAGER_JOURNALMODE_MEMORY ){
 -        /* Cannot transition directly from MEMORY to WAL.  Use mode OFF
 -        ** as an intermediate */
 -        sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
 -      }
 +    }
   
 -      /* Open a transaction on the database file. Regardless of the journal
 -      ** mode, this transaction always uses a rollback journal.
 +    if( isWalMode(eOld) ){
 +      /* If leaving WAL mode, close the log file. If successful, the call
 +      ** to PagerCloseWal() checkpoints and deletes the write-ahead-log 
 +      ** file. An EXCLUSIVE lock may still be held on the database file 
 +      ** after a successful return. 
        */
 -      assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
 +      rc = sqlite3PagerCloseWal(pPager, db);
        if( rc==SQLITE_OK ){
 -        rc = sqlite3BtreeSetVersion(pBt, (eNew==PAGER_JOURNALMODE_WAL ? 2 : 1));
 +        sqlite3PagerSetJournalMode(pPager, eNew);
        }
 +    }else if( eOld==PAGER_JOURNALMODE_MEMORY ){
 +      /* Cannot transition directly from MEMORY to WAL.  Use mode OFF
 +      ** as an intermediate */
 +      sqlite3PagerSetJournalMode(pPager, PAGER_JOURNALMODE_OFF);
 +    }
 +
 +    /* Open a transaction on the database file. Regardless of the journal
 +    ** mode, this transaction always uses a rollback journal.
 +    */
 +    assert( sqlite3BtreeTxnState(pBt)!=SQLITE_TXN_WRITE );
 +    if( rc==SQLITE_OK ){
 +      /* 1==rollback, 2==wal, 3==wal2 */
 +      rc = sqlite3BtreeSetVersion(pBt, 
 +          1 + isWalMode(eNew) + (eNew==PAGER_JOURNALMODE_WAL2)
 +      );
++
      }
    }
  #endif /* ifndef SQLITE_OMIT_WAL */
diff --cc src/wal.c
index d011abd2245eabdf5ebe297f5f01d5af8d5c6409,f4d0328b2b4f86ec122e11bc5ac56d82e9b1b683..a78c6d97bd9d04ff3fbc199e87b68dc9a97afbb0
+++ b/src/wal.c
@@@ -446,20 -262,21 +446,21 @@@ int sqlite3WalTrace = 0
  #endif
  
  /*
 -** The maximum (and only) versions of the wal and wal-index formats
 -** that may be interpreted by this version of SQLite.
 -**
 -** If a client begins recovering a WAL file and finds that (a) the checksum
 -** values in the wal-header are correct and (b) the version field is not
 -** WAL_MAX_VERSION, recovery fails and SQLite returns SQLITE_CANTOPEN.
 -**
 -** Similarly, if a client successfully reads a wal-index header (i.e. the
 -** checksum test is successful) and finds that the version field is not
 -** WALINDEX_MAX_VERSION, then no read-transaction is opened and SQLite
 -** returns SQLITE_CANTOPEN.
 +** Both the wal-file and the wal-index contain version fields 
 +** indicating the current version of the system. If a client
 +** reads the header of a wal file (as part of recovery), or the
 +** wal-index (as part of opening a read transaction) and (a) the
 +** header checksum is correct but (b) the version field is not
 +** recognized, the operation fails with SQLITE_CANTOPEN.
 +**
 +** Currently, clients support both version-1 ("journal_mode=wal") and
 +** version-2 ("journal_mode=wal2"). Legacy clients may support version-1
 +** only.
  */
 -#define WAL_MAX_VERSION      3007000
 -#define WALINDEX_MAX_VERSION 3007000
 +#define WAL_VERSION1 3007000      /* For "journal_mode=wal" */
 +#define WAL_VERSION2 3021000      /* For "journal_mode=wal2" */
 +
  /*
  ** Index numbers for various locking bytes.   WAL_NREADER is the number
  ** of available reader locks and should be at least 3.  The default
@@@ -532,13 -316,7 +533,13 @@@ typedef struct WalCkptInfo WalCkptInfo
  **
  ** The szPage value can be any power of 2 between 512 and 32768, inclusive.
  ** Or it can be 1 to represent a 65536-byte page.  The latter case was
- ** added in 3.7.1 when support for 64K pages was added.  
+ ** added in 3.7.1 when support for 64K pages was added.
 +**
 +** WAL2 mode notes: Member variable mxFrame2 is only used in wal2 mode
 +** (when iVersion is set to WAL_VERSION2). The lower 31 bits store
 +** the maximum frame number in file *-wal2. The most significant bit
 +** is a flag - set if clients are currently appending to *-wal2, clear
 +** otherwise.
  */
  struct WalIndexHdr {
    u32 iVersion;                   /* Wal-index version */
@@@ -1408,25 -1094,15 +1409,25 @@@ static void walCleanupHash(Wal *pWal)
    int iLimit = 0;                 /* Zero values greater than this */
    int nByte;                      /* Number of bytes to zero in aPgno[] */
    int i;                          /* Used to iterate through aHash[] */
 +  int iWal = walidxGetFile(&pWal->hdr);
 +  u32 mxFrame = walidxGetMxFrame(&pWal->hdr, iWal);
 +
 +  u32 iExternal;
 +  if( isWalMode2(pWal) ){
 +    iExternal = walExternalEncode(iWal, mxFrame);
 +  }else{
 +    assert( iWal==0 );
 +    iExternal = mxFrame;
 +  }
  
    assert( pWal->writeLock );
 -  testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE-1 );
 -  testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE );
 -  testcase( pWal->hdr.mxFrame==HASHTABLE_NPAGE_ONE+1 );
 +  testcase( mxFrame==HASHTABLE_NPAGE_ONE-1 );
 +  testcase( mxFrame==HASHTABLE_NPAGE_ONE );
 +  testcase( mxFrame==HASHTABLE_NPAGE_ONE+1 );
  
 -  if( pWal->hdr.mxFrame==0 ) return;
 +  if( mxFrame==0 ) return;
  
-   /* Obtain pointers to the hash-table and page-number array containing 
+   /* Obtain pointers to the hash-table and page-number array containing
    ** the entry that corresponds to frame pWal->hdr.mxFrame. It is guaranteed
    ** that the page said hash-table and array reside on is already mapped.(1)
    */
@@@ -1495,11 -1165,11 +1497,11 @@@ static int walIndexAppend(Wal *pWal, in
      int idx;                      /* Value to write to hash-table slot */
      int nCollide;                 /* Number of hash collisions */
  
 -    idx = iFrame - sLoc.iZero;
 +    idx = iExternal - sLoc.iZero;
      assert( idx <= HASHTABLE_NSLOT/2 + 1 );
-     
      /* If this is the first entry to be added to this hash-table, zero the
-     ** entire hash table and aPgno[] array before proceeding. 
+     ** entire hash table and aPgno[] array before proceeding.
      */
      if( idx==1 ){
        int nByte = (int)((u8*)&sLoc.aHash[HASHTABLE_NSLOT] - (u8*)sLoc.aPgno);
    return rc;
  }
  
 +/*
 +** Recover a single wal file - *-wal if iWal==0, or *-wal2 if iWal==1.
 +*/
 +static int walIndexRecoverOne(Wal *pWal, int iWal, u32 *pnCkpt, int *pbZero){
 +  i64 nSize;                      /* Size of log file */
 +  u32 aFrameCksum[2] = {0, 0};
 +  int rc;
 +  sqlite3_file *pWalFd = pWal->apWalFd[iWal];
 +
 +  assert( iWal==0 || iWal==1 );
 +
 +  memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
 +  sqlite3_randomness(8, pWal->hdr.aSalt);
 +
 +  rc = sqlite3OsFileSize(pWalFd, &nSize);
 +  if( rc==SQLITE_OK ){
 +    if( nSize>WAL_HDRSIZE ){
 +      u8 aBuf[WAL_HDRSIZE];         /* Buffer to load WAL header into */
 +      u32 *aPrivate = 0;            /* Heap copy of *-shm pg being populated */
 +      u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
 +      int szFrame;                  /* Number of bytes in buffer aFrame[] */
 +      u8 *aData;                    /* Pointer to data part of aFrame buffer */
 +      int szPage;                   /* Page size according to the log */
 +      u32 magic;                    /* Magic value read from WAL header */
 +      u32 version;                  /* Magic value read from WAL header */
 +      int isValid;                  /* True if this frame is valid */
 +      int iPg;                      /* Current 32KB wal-index page */
 +      int iLastFrame;               /* Last frame in wal, based on size alone */
 +      int iLastPg;                  /* Last shm page used by this wal */
 +  
 +      /* Read in the WAL header. */
 +      rc = sqlite3OsRead(pWalFd, aBuf, WAL_HDRSIZE, 0);
 +      if( rc!=SQLITE_OK ){
 +        return rc;
 +      }
 +  
 +      /* If the database page size is not a power of two, or is greater than
 +      ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid 
 +      ** data. Similarly, if the 'magic' value is invalid, ignore the whole
 +      ** WAL file.
 +      */
 +      magic = sqlite3Get4byte(&aBuf[0]);
 +      szPage = sqlite3Get4byte(&aBuf[8]);
 +      if( (magic&0xFFFFFFFE)!=WAL_MAGIC 
 +       || szPage&(szPage-1) 
 +       || szPage>SQLITE_MAX_PAGE_SIZE 
 +       || szPage<512 
 +      ){
 +        return SQLITE_OK;
 +      }
 +      pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
 +      pWal->szPage = szPage;
 +  
 +      /* Verify that the WAL header checksum is correct */
 +      walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN, 
 +          aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
 +      );
 +      if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
 +       || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
 +      ){
 +        return SQLITE_OK;
 +      }
 +  
 +      memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
 +      *pnCkpt = sqlite3Get4byte(&aBuf[12]);
 +  
 +      /* Verify that the version number on the WAL format is one that
 +      ** are able to understand */
 +      version = sqlite3Get4byte(&aBuf[4]);
 +      if( version!=WAL_VERSION1 && version!=WAL_VERSION2 ){
 +        return SQLITE_CANTOPEN_BKPT;
 +      }
 +      pWal->hdr.iVersion = version;
 +  
 +      /* Malloc a buffer to read frames into. */
 +      szFrame = szPage + WAL_FRAME_HDRSIZE;
 +      aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ);
 +      if( !aFrame ){
 +        return SQLITE_NOMEM_BKPT;
 +      }
 +      aData = &aFrame[WAL_FRAME_HDRSIZE];
 +      aPrivate = (u32*)&aData[szPage];
 +  
 +      /* Read all frames from the log file. */
 +      iLastFrame = (nSize - WAL_HDRSIZE) / szFrame;
 +      if( version==WAL_VERSION2 ){
 +        iLastPg = walFramePage2(iWal, iLastFrame);
 +      }else{
 +        iLastPg = walFramePage(iLastFrame);
 +      }
 +      for(iPg=iWal; iPg<=iLastPg; iPg+=(version==WAL_VERSION2 ? 2 : 1)){
 +        u32 *aShare;
 +        int iFrame;                 /* Index of last frame read */
 +        int iLast;
 +        int iFirst;
 +        int nHdr, nHdr32;
 +
 +        rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare);
 +        assert( aShare!=0 || rc!=SQLITE_OK );
 +        if( aShare==0 ) break;
 +        pWal->apWiData[iPg] = aPrivate;
 +
 +        if( iWal ){
 +          assert( version==WAL_VERSION2 );
 +          iFirst = 1 + (iPg/2)*HASHTABLE_NPAGE;
 +          iLast = iFirst + HASHTABLE_NPAGE - 1;
 +        }else{
 +          int i2 = (version==WAL_VERSION2) ? (iPg/2) : iPg;
 +          iLast = HASHTABLE_NPAGE_ONE+i2*HASHTABLE_NPAGE;
 +          iFirst = 1 + (i2==0?0:HASHTABLE_NPAGE_ONE+(i2-1)*HASHTABLE_NPAGE);
 +        }
 +        iLast = MIN(iLast, iLastFrame);
 +
 +        for(iFrame=iFirst; iFrame<=iLast; iFrame++){
 +          i64 iOffset = walFrameOffset(iFrame, szPage);
 +          u32 pgno;                 /* Database page number for frame */
 +          u32 nTruncate;            /* dbsize field from frame header */
 +
 +          /* Read and decode the next log frame. */
 +          rc = sqlite3OsRead(pWalFd, aFrame, szFrame, iOffset);
 +          if( rc!=SQLITE_OK ) break;
 +          isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
 +          if( !isValid ) break;
 +          rc = walIndexAppend(pWal, iWal, iFrame, pgno);
 +          if( NEVER(rc!=SQLITE_OK) ) break;
 +  
 +          /* If nTruncate is non-zero, this is a commit record. */
 +          if( nTruncate ){
 +            pWal->hdr.mxFrame = iFrame;
 +            pWal->hdr.nPage = nTruncate;
 +            pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
 +            testcase( szPage<=32768 );
 +            testcase( szPage>=65536 );
 +            aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
 +            aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
 +          }
 +        }
 +        pWal->apWiData[iPg] = aShare;
 +        nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0);
 +        nHdr32 = nHdr / sizeof(u32);
 +#ifndef SQLITE_SAFER_WALINDEX_RECOVERY
 +        /* Memcpy() should work fine here, on all reasonable implementations.
 +        ** Technically, memcpy() might change the destination to some
 +        ** intermediate value before setting to the final value, and that might
 +        ** cause a concurrent reader to malfunction.  Memcpy() is allowed to
 +        ** do that, according to the spec, but no memcpy() implementation that
 +        ** we know of actually does that, which is why we say that memcpy()
 +        ** is safe for this.  Memcpy() is certainly a lot faster.
 +        */
 +        memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr);
 +#else
 +        /* In the event that some platform is found for which memcpy()
 +        ** changes the destination to some intermediate value before
 +        ** setting the final value, this alternative copy routine is
 +        ** provided.
 +        */
 +        {
 +          int i;
 +          for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){
 +            if( aShare[i]!=aPrivate[i] ){
 +              /* Atomic memory operations are not required here because if
 +              ** the value needs to be changed, that means it is not being
 +              ** accessed concurrently. */
 +              aShare[i] = aPrivate[i];
 +            }
 +          }
 +        }
 +#endif
 +        if( iFrame<=iLast ) break;
 +      }
 +  
 +      sqlite3_free(aFrame);
 +    }else if( pbZero ){
 +      *pbZero = 1;
 +    }
 +  }
 +
 +  pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
 +  pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
 +
 +  return rc;
 +}
 +
 +static int walOpenWal2(Wal *pWal){
 +  int rc = SQLITE_OK;
 +  if( !isOpen(pWal->apWalFd[1]) ){
 +    int f = (SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_WAL);
 +    rc = sqlite3OsOpen(pWal->pVfs, pWal->zWalName2, pWal->apWalFd[1], f, &f);
 +  }
 +  return rc;
 +}
 +
 +static int walTruncateWal2(Wal *pWal){
 +  int bIs;
 +  int rc;
 +  assert( !isOpen(pWal->apWalFd[1]) );
 +  rc = sqlite3OsAccess(pWal->pVfs, pWal->zWalName2, SQLITE_ACCESS_EXISTS, &bIs);
 +  if( rc==SQLITE_OK && bIs ){
 +    rc = walOpenWal2(pWal);
 +    if( rc==SQLITE_OK ){
 +      rc = sqlite3OsTruncate(pWal->apWalFd[1], 0);
 +      sqlite3OsClose(pWal->apWalFd[1]);
 +    }
 +  }
 +  return rc;
 +}
  
  /*
- ** Recover the wal-index by reading the write-ahead log file. 
+ ** Recover the wal-index by reading the write-ahead log file.
  **
  ** This routine first tries to establish an exclusive lock on the
  ** wal-index to prevent other threads/processes from doing anything
@@@ -1802,96 -1264,163 +1804,96 @@@ static int walIndexRecover(Wal *pWal)
  
    WALTRACE(("WAL%p: recovery begin...\n", pWal));
  
 -  memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
 -
 -  rc = sqlite3OsFileSize(pWal->pWalFd, &nSize);
 -  if( rc!=SQLITE_OK ){
 -    goto recovery_error;
 +  /* Recover the *-wal file. If a valid version-1 header is recovered
 +  ** from it, do not open the *-wal2 file. Even if it exists.
 +  **
 +  ** Otherwise, if the *-wal2 file exists or if the "wal2" flag was 
 +  ** specified when sqlite3WalOpen() was called, open and recover
 +  ** the *-wal2 file. Except, if the *-wal file was zero bytes in size,
 +  ** truncate the *-wal2 to zero bytes in size.
 +  **
 +  ** After this block has run, if the *-wal2 file is open the system
 +  ** starts up in VERSION2 mode. In this case pWal->hdr contains the 
 +  ** wal-index header considering only *-wal2. Stack variable hdr
 +  ** contains the wal-index header considering only *-wal. The hash 
 +  ** tables are populated for both.  
 +  **
 +  ** Or, if the *-wal2 file is not open, start up in VERSION1 mode.
 +  ** pWal->hdr is already populated.
 +  */
 +  rc = walIndexRecoverOne(pWal, 0, &nCkpt1, &bZero);
 +  assert( pWal->hdr.iVersion==0 
 +      || pWal->hdr.iVersion==WAL_VERSION1 
 +      || pWal->hdr.iVersion==WAL_VERSION2 
 +  );
 +  if( rc==SQLITE_OK && bZero ){
 +    rc = walTruncateWal2(pWal);
    }
 -
 -  if( nSize>WAL_HDRSIZE ){
 -    u8 aBuf[WAL_HDRSIZE];         /* Buffer to load WAL header into */
 -    u32 *aPrivate = 0;            /* Heap copy of *-shm hash being populated */
 -    u8 *aFrame = 0;               /* Malloc'd buffer to load entire frame */
 -    int szFrame;                  /* Number of bytes in buffer aFrame[] */
 -    u8 *aData;                    /* Pointer to data part of aFrame buffer */
 -    int szPage;                   /* Page size according to the log */
 -    u32 magic;                    /* Magic value read from WAL header */
 -    u32 version;                  /* Magic value read from WAL header */
 -    int isValid;                  /* True if this frame is valid */
 -    u32 iPg;                      /* Current 32KB wal-index page */
 -    u32 iLastFrame;               /* Last frame in wal, based on nSize alone */
 -
 -    /* Read in the WAL header. */
 -    rc = sqlite3OsRead(pWal->pWalFd, aBuf, WAL_HDRSIZE, 0);
 -    if( rc!=SQLITE_OK ){
 -      goto recovery_error;
 +  if( rc==SQLITE_OK && pWal->hdr.iVersion!=WAL_VERSION1 ){
 +    int bOpen = 1;
 +    sqlite3_vfs *pVfs = pWal->pVfs;
 +    if( pWal->hdr.iVersion==0 && pWal->bWal2==0 ){
 +      rc = sqlite3OsAccess(pVfs, pWal->zWalName2, SQLITE_ACCESS_EXISTS, &bOpen);
      }
 -
 -    /* If the database page size is not a power of two, or is greater than
 -    ** SQLITE_MAX_PAGE_SIZE, conclude that the WAL file contains no valid
 -    ** data. Similarly, if the 'magic' value is invalid, ignore the whole
 -    ** WAL file.
 -    */
 -    magic = sqlite3Get4byte(&aBuf[0]);
 -    szPage = sqlite3Get4byte(&aBuf[8]);
 -    if( (magic&0xFFFFFFFE)!=WAL_MAGIC
 -     || szPage&(szPage-1)
 -     || szPage>SQLITE_MAX_PAGE_SIZE
 -     || szPage<512
 -    ){
 -      goto finished;
 -    }
 -    pWal->hdr.bigEndCksum = (u8)(magic&0x00000001);
 -    pWal->szPage = szPage;
 -    pWal->nCkpt = sqlite3Get4byte(&aBuf[12]);
 -    memcpy(&pWal->hdr.aSalt, &aBuf[16], 8);
 -
 -    /* Verify that the WAL header checksum is correct */
 -    walChecksumBytes(pWal->hdr.bigEndCksum==SQLITE_BIGENDIAN,
 -        aBuf, WAL_HDRSIZE-2*4, 0, pWal->hdr.aFrameCksum
 -    );
 -    if( pWal->hdr.aFrameCksum[0]!=sqlite3Get4byte(&aBuf[24])
 -     || pWal->hdr.aFrameCksum[1]!=sqlite3Get4byte(&aBuf[28])
 -    ){
 -      goto finished;
 +    if( rc==SQLITE_OK && bOpen ){
 +      rc = walOpenWal2(pWal);
 +      if( rc==SQLITE_OK ){
 +        hdr = pWal->hdr;
 +        rc = walIndexRecoverOne(pWal, 1, &nCkpt2, 0);
 +      }
      }
 +  }
  
 -    /* Verify that the version number on the WAL format is one that
 -    ** are able to understand */
 -    version = sqlite3Get4byte(&aBuf[4]);
 -    if( version!=WAL_MAX_VERSION ){
 -      rc = SQLITE_CANTOPEN_BKPT;
 -      goto finished;
 -    }
 +  if( rc==SQLITE_OK ){
 +    volatile WalCkptInfo *pInfo;
  
 -    /* Malloc a buffer to read frames into. */
 -    szFrame = szPage + WAL_FRAME_HDRSIZE;
 -    aFrame = (u8 *)sqlite3_malloc64(szFrame + WALINDEX_PGSZ);
 -    if( !aFrame ){
 -      rc = SQLITE_NOMEM_BKPT;
 -      goto recovery_error;
 -    }
 -    aData = &aFrame[WAL_FRAME_HDRSIZE];
 -    aPrivate = (u32*)&aData[szPage];
 -
 -    /* Read all frames from the log file. */
 -    iLastFrame = (nSize - WAL_HDRSIZE) / szFrame;
 -    for(iPg=0; iPg<=(u32)walFramePage(iLastFrame); iPg++){
 -      u32 *aShare;
 -      u32 iFrame;                 /* Index of last frame read */
 -      u32 iLast = MIN(iLastFrame, HASHTABLE_NPAGE_ONE+iPg*HASHTABLE_NPAGE);
 -      u32 iFirst = 1 + (iPg==0?0:HASHTABLE_NPAGE_ONE+(iPg-1)*HASHTABLE_NPAGE);
 -      u32 nHdr, nHdr32;
 -      rc = walIndexPage(pWal, iPg, (volatile u32**)&aShare);
 -      assert( aShare!=0 || rc!=SQLITE_OK );
 -      if( aShare==0 ) break;
 -      pWal->apWiData[iPg] = aPrivate;
 -
 -      for(iFrame=iFirst; iFrame<=iLast; iFrame++){
 -        i64 iOffset = walFrameOffset(iFrame, szPage);
 -        u32 pgno;                 /* Database page number for frame */
 -        u32 nTruncate;            /* dbsize field from frame header */
 -
 -        /* Read and decode the next log frame. */
 -        rc = sqlite3OsRead(pWal->pWalFd, aFrame, szFrame, iOffset);
 -        if( rc!=SQLITE_OK ) break;
 -        isValid = walDecodeFrame(pWal, &pgno, &nTruncate, aData, aFrame);
 -        if( !isValid ) break;
 -        rc = walIndexAppend(pWal, iFrame, pgno);
 -        if( NEVER(rc!=SQLITE_OK) ) break;
 -
 -        /* If nTruncate is non-zero, this is a commit record. */
 -        if( nTruncate ){
 -          pWal->hdr.mxFrame = iFrame;
 -          pWal->hdr.nPage = nTruncate;
 -          pWal->hdr.szPage = (u16)((szPage&0xff00) | (szPage>>16));
 -          testcase( szPage<=32768 );
 -          testcase( szPage>=65536 );
 -          aFrameCksum[0] = pWal->hdr.aFrameCksum[0];
 -          aFrameCksum[1] = pWal->hdr.aFrameCksum[1];
 +    if( isOpen(pWal->apWalFd[1]) ){
 +      /* The case where *-wal2 may follow *-wal */
 +      if( nCkpt2<=0x0F && nCkpt2==nCkpt1+1 ){
 +        if( pWal->hdr.mxFrame
 +         && sqlite3Get4byte((u8*)(&pWal->hdr.aSalt[0]))==hdr.aFrameCksum[0]
 +         && sqlite3Get4byte((u8*)(&pWal->hdr.aSalt[1]))==hdr.aFrameCksum[1]
 +        ){
 +          walidxSetFile(&pWal->hdr, 1);
 +          walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +          walidxSetMxFrame(&pWal->hdr, 0, hdr.mxFrame);
 +        }else{
 +          pWal->hdr = hdr;
          }
 -      }
 -      pWal->apWiData[iPg] = aShare;
 -      nHdr = (iPg==0 ? WALINDEX_HDR_SIZE : 0);
 -      nHdr32 = nHdr / sizeof(u32);
 -#ifndef SQLITE_SAFER_WALINDEX_RECOVERY
 -      /* Memcpy() should work fine here, on all reasonable implementations.
 -      ** Technically, memcpy() might change the destination to some
 -      ** intermediate value before setting to the final value, and that might
 -      ** cause a concurrent reader to malfunction.  Memcpy() is allowed to
 -      ** do that, according to the spec, but no memcpy() implementation that
 -      ** we know of actually does that, which is why we say that memcpy()
 -      ** is safe for this.  Memcpy() is certainly a lot faster.
 -      */
 -      memcpy(&aShare[nHdr32], &aPrivate[nHdr32], WALINDEX_PGSZ-nHdr);
 -#else
 -      /* In the event that some platform is found for which memcpy()
 -      ** changes the destination to some intermediate value before
 -      ** setting the final value, this alternative copy routine is
 -      ** provided.
 -      */
 -      {
 -        int i;
 -        for(i=nHdr32; i<WALINDEX_PGSZ/sizeof(u32); i++){
 -          if( aShare[i]!=aPrivate[i] ){
 -            /* Atomic memory operations are not required here because if
 -            ** the value needs to be changed, that means it is not being
 -            ** accessed concurrently. */
 -            aShare[i] = aPrivate[i];
 -          }
 +      }else
 +
 +      /* When *-wal may follow *-wal2 */
 +      if( (nCkpt2==0x0F && nCkpt1==0) || (nCkpt2<0x0F && nCkpt2==nCkpt1-1) ){
 +        if( hdr.mxFrame
 +         && sqlite3Get4byte((u8*)(&hdr.aSalt[0]))==pWal->hdr.aFrameCksum[0]
 +         && sqlite3Get4byte((u8*)(&hdr.aSalt[1]))==pWal->hdr.aFrameCksum[1]
 +        ){
 +          SWAP(WalIndexHdr, pWal->hdr, hdr);
 +          walidxSetMxFrame(&pWal->hdr, 1, hdr.mxFrame);
 +        }else{
 +          walidxSetFile(&pWal->hdr, 1);
 +          walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +          walidxSetMxFrame(&pWal->hdr, 0, 0);
          }
 +      }else
 +
 +      /* Fallback */
 +      if( nCkpt1<=nCkpt2 ){
 +        pWal->hdr = hdr;
 +      }else{
 +        walidxSetFile(&pWal->hdr, 1);
 +        walidxSetMxFrame(&pWal->hdr, 1, pWal->hdr.mxFrame);
 +        walidxSetMxFrame(&pWal->hdr, 0, 0);
        }
 -#endif
 -      if( iFrame<=iLast ) break;
 +      pWal->hdr.iVersion = WAL_VERSION2;
 +    }else{
 +      pWal->hdr.iVersion = WAL_VERSION1;
      }
  
 -    sqlite3_free(aFrame);
 -  }
 -
 -finished:
 -  if( rc==SQLITE_OK ){
 -    volatile WalCkptInfo *pInfo;
 -    int i;
 -    pWal->hdr.aFrameCksum[0] = aFrameCksum[0];
 -    pWal->hdr.aFrameCksum[1] = aFrameCksum[1];
      walIndexWriteHdr(pWal);
  
-     /* Reset the checkpoint-header. This is safe because this thread is 
-     ** currently holding locks that exclude all other writers and 
+     /* Reset the checkpoint-header. This is safe because this thread is
+     ** currently holding locks that exclude all other writers and
      ** checkpointers. Then set the values of read-mark slots 1 through N.
      */
      pInfo = walCkptInfo(pWal);
@@@ -1954,12 -1474,10 +1956,12 @@@ static void walIndexClose(Wal *pWal, in
    if( pWal->exclusiveMode!=WAL_HEAPMEMORY_MODE ){
      sqlite3OsShmUnmap(pWal->pDbFd, isDelete);
    }
 +  sqlite3OsClose(pWal->apWalFd[0]);
 +  sqlite3OsClose(pWal->apWalFd[1]);
  }
  
- /* 
- ** Open a connection to the WAL file zWalName. The database file must 
+ /*
+ ** Open a connection to the WAL file zWalName. The database file must
  ** already be opened on connection pDbFd. The buffer that zWalName points
  ** to must remain valid for the lifetime of the returned Wal* handle.
  **
@@@ -2273,8 -1786,8 +2275,8 @@@ static void walIteratorFree(WalIterato
  }
  
  /*
- ** Construct a WalInterator object that can be used to loop over all 
+ ** Construct a WalInterator object that can be used to loop over all
 -** pages in the WAL following frame nBackfill in ascending order. Frames
 +** pages in wal file iWal following frame nBackfill in ascending order. Frames
  ** nBackfill or earlier may be included - excluding them is an optimization
  ** only. The caller must hold the checkpoint lock.
  **
@@@ -2307,18 -1810,12 +2309,18 @@@ static int walIteratorInit
    /* This routine only runs while holding the checkpoint lock. And
    ** it only runs if there is actually content in the log (mxFrame>0).
    */
 -  assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
 -  iLast = pWal->hdr.mxFrame;
 +  iLast = walidxGetMxFrame(&pWal->hdr, iWal);
 +  assert( pWal->ckptLock && iLast>0 );
 +
 +  if( iMode==2 ){
 +    iLastSeg = walFramePage2(iWal, iLast);
 +  }else{
 +    iLastSeg = walFramePage(iLast);
 +  }
 +  nSegment = 1 + (iLastSeg/iMode);
  
    /* Allocate space for the WalIterator object. */
-   nByte = sizeof(WalIterator) 
 -  nSegment = walFramePage(iLast) + 1;
+   nByte = sizeof(WalIterator)
          + (nSegment-1)*sizeof(struct WalSegment)
          + iLast*sizeof(ht_slot);
    p = (WalIterator *)sqlite3_malloc64(nByte);
        }else{
          nEntry = (int)((u32*)sLoc.aHash - (u32*)sLoc.aPgno);
        }
 -      aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[sLoc.iZero];
 -      sLoc.iZero++;
 +      aIndex = &((ht_slot *)&p->aSegment[p->nSegment])[iZero];
 +      iZero++;
-   
        for(j=0; j<nEntry; j++){
          aIndex[j] = (ht_slot)j;
        }
@@@ -2726,13 -2132,11 +2728,13 @@@ static int walCheckpoint
          sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
          rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
          if( rc==SQLITE_OK && nSize<nReq ){
 -          if( (nSize+65536+(i64)pWal->hdr.mxFrame*szPage)<nReq ){
 +          i64 mx = pWal->hdr.mxFrame + (bWal2?walidxGetMxFrame(&pWal->hdr,1):0);
 +          if( (nSize+65536+mx*szPage)<nReq ){
              /* If the size of the final database is larger than the current
              ** database plus the amount of data in the wal file, plus the
-             ** maximum size of the pending-byte page (65536 bytes), then 
+             ** maximum size of the pending-byte page (65536 bytes), then
 -            ** must be corruption somewhere.  */
 +            ** must be corruption somewhere.  Or in the case of wal2 mode,
 +            ** plus the amount of data in both wal files. */
              rc = SQLITE_CORRUPT_BKPT;
            }else{
              sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT,&nReq);
    }
  
    /* If this is an SQLITE_CHECKPOINT_RESTART or TRUNCATE operation, and the
-   ** entire wal file has been copied into the database file, then block 
-   ** until all readers have finished using the wal file. This ensures that 
+   ** entire wal file has been copied into the database file, then block
+   ** until all readers have finished using the wal file. This ensures that
    ** the next process to write to the database restarts the wal file.
    */
 -  if( rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
 +  if( bWal2==0 && rc==SQLITE_OK && eMode!=SQLITE_CHECKPOINT_PASSIVE ){
      assert( pWal->writeLock );
      if( pInfo->nBackfill<pWal->hdr.mxFrame ){
        rc = SQLITE_BUSY;
@@@ -2891,38 -2281,28 +2893,39 @@@ int sqlite3WalClose
        if( pWal->exclusiveMode==WAL_NORMAL_MODE ){
          pWal->exclusiveMode = WAL_EXCLUSIVE_MODE;
        }
 -      rc = sqlite3WalCheckpoint(pWal, db,
 -          SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
 -      );
 -      if( rc==SQLITE_OK ){
 -        int bPersist = -1;
 -        sqlite3OsFileControlHint(
 -            pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist
 +      for(i=0; rc==SQLITE_OK && i<2; i++){
 +        rc = sqlite3WalCheckpoint(pWal, db, 
 +            SQLITE_CHECKPOINT_PASSIVE, 0, 0, sync_flags, nBuf, zBuf, 0, 0
          );
 -        if( bPersist!=1 ){
 -          /* Try to delete the WAL file if the checkpoint completed and
 -          ** fsynced (rc==SQLITE_OK) and if we are not in persistent-wal
 -          ** mode (!bPersist) */
 -          isDelete = 1;
 -        }else if( pWal->mxWalSize>=0 ){
 -          /* Try to truncate the WAL file to zero bytes if the checkpoint
 -          ** completed and fsynced (rc==SQLITE_OK) and we are in persistent
 -          ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
 -          ** non-negative value (pWal->mxWalSize>=0).  Note that we truncate
 -          ** to zero bytes as truncating to the journal_size_limit might
 -          ** leave a corrupt WAL file on disk. */
 -          walLimitSize(pWal, 0);
 +        if( rc==SQLITE_OK ){
 +          int bPersist = -1;
 +          sqlite3OsFileControlHint(
 +              pWal->pDbFd, SQLITE_FCNTL_PERSIST_WAL, &bPersist
 +          );
 +          if( bPersist!=1 ){
 +            /* Try to delete the WAL file if the checkpoint completed and
 +            ** fsyned (rc==SQLITE_OK) and if we are not in persistent-wal
 +            ** mode (!bPersist) */
 +            isDelete = 1;
 +          }else if( pWal->mxWalSize>=0 ){
 +            /* Try to truncate the WAL file to zero bytes if the checkpoint
 +            ** completed and fsynced (rc==SQLITE_OK) and we are in persistent
 +            ** WAL mode (bPersist) and if the PRAGMA journal_size_limit is a
 +            ** non-negative value (pWal->mxWalSize>=0).  Note that we truncate
 +            ** to zero bytes as truncating to the journal_size_limit might
 +            ** leave a corrupt WAL file on disk. */
 +            walLimitSize(pWal, 0);
 +          }
          }
 +
 +        if( isWalMode2(pWal)==0 ) break;
 +
 +        walCkptInfo(pWal)->nBackfill = 0;
 +        walidxSetFile(&pWal->hdr, !walidxGetFile(&pWal->hdr));
 +        pWal->writeLock = 1;
 +        walIndexWriteHdr(pWal);
 +        pWal->writeLock = 0;
++
        }
      }
  
@@@ -3427,164 -2809,131 +3430,165 @@@ static int walTryBeginRead(Wal *pWal, i
    assert( pWal->nWiData>0 );
    assert( pWal->apWiData[0]!=0 );
    pInfo = walCkptInfo(pWal);
 -  if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
 -#ifdef SQLITE_ENABLE_SNAPSHOT
 -   && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
 -#endif
 -  ){
 -    /* The WAL has been completely backfilled (or it is empty).
 -    ** and can be safely ignored.
 -    */
 -    rc = walLockShared(pWal, WAL_READ_LOCK(0));
 +  if( isWalMode2(pWal) ){
 +    /* This connection needs a "part" lock on the current wal file and, 
 +    ** unless pInfo->nBackfill is set to indicate that it has already been
 +    ** checkpointed, a "full" lock on the other wal file.  */
 +    int iWal = walidxGetFile(&pWal->hdr);
 +    int nBackfill = pInfo->nBackfill || walidxGetMxFrame(&pWal->hdr, !iWal)==0;
 +    int eLock = 1 + (iWal*2) + (nBackfill==iWal);
 +
 +    assert( nBackfill==0 || nBackfill==1 );
 +    assert( iWal==0 || iWal==1 );
 +    assert( iWal!=0 || nBackfill!=1 || eLock==WAL_LOCK_PART1 );
 +    assert( iWal!=0 || nBackfill!=0 || eLock==WAL_LOCK_PART1_FULL2 );
 +    assert( iWal!=1 || nBackfill!=1 || eLock==WAL_LOCK_PART2 );
 +    assert( iWal!=1 || nBackfill!=0 || eLock==WAL_LOCK_PART2_FULL1 );
 +
 +    rc = walLockShared(pWal, WAL_READ_LOCK(eLock));
 +    if( rc!=SQLITE_OK ){
 +      return (rc==SQLITE_BUSY ? WAL_RETRY : rc);
 +    }
      walShmBarrier(pWal);
 -    if( rc==SQLITE_OK ){
 -      if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 -        /* It is not safe to allow the reader to continue here if frames
 -        ** may have been appended to the log before READ_LOCK(0) was obtained.
 -        ** When holding READ_LOCK(0), the reader ignores the entire log file,
 -        ** which implies that the database file contains a trustworthy
 -        ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
 -        ** happening, this is usually correct.
 -        **
 -        ** However, if frames have been appended to the log (or if the log
 -        ** is wrapped and written for that matter) before the READ_LOCK(0)
 -        ** is obtained, that is not necessarily true. A checkpointer may
 -        ** have started to backfill the appended frames but crashed before
 -        ** it finished. Leaving a corrupt image in the database file.
 -        */
 -        walUnlockShared(pWal, WAL_READ_LOCK(0));
 -        return WAL_RETRY;
 +    if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
 +      walUnlockShared(pWal, WAL_READ_LOCK(eLock));
 +      return WAL_RETRY;
 +    }else{
 +      pWal->readLock = eLock;
 +    }
 +    assert( pWal->minFrame==0 && walFramePage(pWal->minFrame)==0 );
 +  }else{
 +    u32 mxReadMark;               /* Largest aReadMark[] value */
 +    int mxI;                      /* Index of largest aReadMark[] value */
 +    int i;                        /* Loop counter */
 +    u32 mxFrame;                  /* Wal frame to lock to */
 +    if( !useWal && pInfo->nBackfill==pWal->hdr.mxFrame
 +  #ifdef SQLITE_ENABLE_SNAPSHOT
 +     && (pWal->pSnapshot==0 || pWal->hdr.mxFrame==0)
 +  #endif
 +    ){
 +      /* The WAL has been completely backfilled (or it is empty).
 +      ** and can be safely ignored.
 +      */
 +      rc = walLockShared(pWal, WAL_READ_LOCK(0));
 +      walShmBarrier(pWal);
 +      if( rc==SQLITE_OK ){
 +        if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
 +          /* It is not safe to allow the reader to continue here if frames
 +          ** may have been appended to the log before READ_LOCK(0) was obtained.
 +          ** When holding READ_LOCK(0), the reader ignores the entire log file,
 +          ** which implies that the database file contains a trustworthy
 +          ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
 +          ** happening, this is usually correct.
 +          **
 +          ** However, if frames have been appended to the log (or if the log 
 +          ** is wrapped and written for that matter) before the READ_LOCK(0)
 +          ** is obtained, that is not necessarily true. A checkpointer may
 +          ** have started to backfill the appended frames but crashed before
 +          ** it finished. Leaving a corrupt image in the database file.
 +          */
 +          walUnlockShared(pWal, WAL_READ_LOCK(0));
 +          return WAL_RETRY;
 +        }
 +        pWal->readLock = 0;
 +        return SQLITE_OK;
 +      }else if( rc!=SQLITE_BUSY ){
 +        return rc;
        }
 -      pWal->readLock = 0;
 -      return SQLITE_OK;
 -    }else if( rc!=SQLITE_BUSY ){
 -      return rc;
      }
 -  }
 -
 -  /* If we get this far, it means that the reader will want to use
 -  ** the WAL to get at content from recent commits.  The job now is
 -  ** to select one of the aReadMark[] entries that is closest to
 -  ** but not exceeding pWal->hdr.mxFrame and lock that entry.
 -  */
 -  mxReadMark = 0;
 -  mxI = 0;
 -  mxFrame = pWal->hdr.mxFrame;
 -#ifdef SQLITE_ENABLE_SNAPSHOT
 -  if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
 -    mxFrame = pWal->pSnapshot->mxFrame;
 -  }
 -#endif
 -  for(i=1; i<WAL_NREADER; i++){
 -    u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
 -    if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 -      assert( thisMark!=READMARK_NOT_USED );
 -      mxReadMark = thisMark;
 -      mxI = i;
 +  
 +    /* If we get this far, it means that the reader will want to use
 +    ** the WAL to get at content from recent commits.  The job now is
 +    ** to select one of the aReadMark[] entries that is closest to
 +    ** but not exceeding pWal->hdr.mxFrame and lock that entry.
 +    */
 +    mxReadMark = 0;
 +    mxI = 0;
 +    mxFrame = pWal->hdr.mxFrame;
 +  #ifdef SQLITE_ENABLE_SNAPSHOT
 +    if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
 +      mxFrame = pWal->pSnapshot->mxFrame;
      }
 -  }
 -  if( (pWal->readOnly & WAL_SHM_RDONLY)==0
 -   && (mxReadMark<mxFrame || mxI==0)
 -  ){
 +  #endif
      for(i=1; i<WAL_NREADER; i++){
 -      rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
 -      if( rc==SQLITE_OK ){
 -        AtomicStore(pInfo->aReadMark+i,mxFrame);
 -        mxReadMark = mxFrame;
 +      u32 thisMark = AtomicLoad(pInfo->aReadMark+i);
 +      if( mxReadMark<=thisMark && thisMark<=mxFrame ){
 +        assert( thisMark!=READMARK_NOT_USED );
 +        mxReadMark = thisMark;
          mxI = i;
 -        walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 -        break;
 -      }else if( rc!=SQLITE_BUSY ){
 -        return rc;
        }
      }
 -  }
 -  if( mxI==0 ){
 -    assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
 -    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
 -  }
 +    if( (pWal->readOnly & WAL_SHM_RDONLY)==0
 +     && (mxReadMark<mxFrame || mxI==0)
 +    ){
 +      for(i=1; i<WAL_NREADER; i++){
 +        rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +        if( rc==SQLITE_OK ){
 +          AtomicStore(pInfo->aReadMark+i,mxFrame);
 +          mxReadMark = mxFrame;
 +          mxI = i;
 +          walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
 +          break;
 +        }else if( rc!=SQLITE_BUSY ){
 +          return rc;
 +        }
 +      }
 +    }
 +    if( mxI==0 ){
 +      assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
 +      return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
 +    }
 +  
 +    rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 +    if( rc ){
 +      return rc==SQLITE_BUSY ? WAL_RETRY : rc;
 +    }
 +    /* Now that the read-lock has been obtained, check that neither the
 +    ** value in the aReadMark[] array or the contents of the wal-index
 +    ** header have changed.
 +    **
 +    ** It is necessary to check that the wal-index header did not change
 +    ** between the time it was read and when the shared-lock was obtained
 +    ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
 +    ** that the log file may have been wrapped by a writer, or that frames
 +    ** that occur later in the log than pWal->hdr.mxFrame may have been
 +    ** copied into the database by a checkpointer. If either of these things
 +    ** happened, then reading the database with the current value of
 +    ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
 +    ** instead.
 +    **
 +    ** Before checking that the live wal-index header has not changed
 +    ** since it was read, set Wal.minFrame to the first frame in the wal
 +    ** file that has not yet been checkpointed. This client will not need
 +    ** to read any frames earlier than minFrame from the wal file - they
 +    ** can be safely read directly from the database file.
 +    **
 +    ** Because a ShmBarrier() call is made between taking the copy of 
 +    ** nBackfill and checking that the wal-header in shared-memory still
 +    ** matches the one cached in pWal->hdr, it is guaranteed that the 
 +    ** checkpointer that set nBackfill was not working with a wal-index
 +    ** header newer than that cached in pWal->hdr. If it were, that could
 +    ** cause a problem. The checkpointer could omit to checkpoint
 +    ** a version of page X that lies before pWal->minFrame (call that version
 +    ** A) on the basis that there is a newer version (version B) of the same
 +    ** page later in the wal file. But if version B happens to like past
 +    ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
 +    ** that it can read version A from the database file. However, since
 +    ** we can guarantee that the checkpointer that set nBackfill could not
 +    ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
 +    */
 +    pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
 +    walShmBarrier(pWal);
 +    if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
 +     || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
 +    ){
 +      walUnlockShared(pWal, WAL_READ_LOCK(mxI));
 +      return WAL_RETRY;
 +    }else{
 +      assert( mxReadMark<=pWal->hdr.mxFrame );
 +      pWal->readLock = (i16)mxI;
 +    }
 -  rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
 -  if( rc ){
 -    return rc==SQLITE_BUSY ? WAL_RETRY : rc;
 -  }
 -  /* Now that the read-lock has been obtained, check that neither the
 -  ** value in the aReadMark[] array or the contents of the wal-index
 -  ** header have changed.
 -  **
 -  ** It is necessary to check that the wal-index header did not change
 -  ** between the time it was read and when the shared-lock was obtained
 -  ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
 -  ** that the log file may have been wrapped by a writer, or that frames
 -  ** that occur later in the log than pWal->hdr.mxFrame may have been
 -  ** copied into the database by a checkpointer. If either of these things
 -  ** happened, then reading the database with the current value of
 -  ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
 -  ** instead.
 -  **
 -  ** Before checking that the live wal-index header has not changed
 -  ** since it was read, set Wal.minFrame to the first frame in the wal
 -  ** file that has not yet been checkpointed. This client will not need
 -  ** to read any frames earlier than minFrame from the wal file - they
 -  ** can be safely read directly from the database file.
 -  **
 -  ** Because a ShmBarrier() call is made between taking the copy of
 -  ** nBackfill and checking that the wal-header in shared-memory still
 -  ** matches the one cached in pWal->hdr, it is guaranteed that the
 -  ** checkpointer that set nBackfill was not working with a wal-index
 -  ** header newer than that cached in pWal->hdr. If it were, that could
 -  ** cause a problem. The checkpointer could omit to checkpoint
 -  ** a version of page X that lies before pWal->minFrame (call that version
 -  ** A) on the basis that there is a newer version (version B) of the same
 -  ** page later in the wal file. But if version B happens to like past
 -  ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
 -  ** that it can read version A from the database file. However, since
 -  ** we can guarantee that the checkpointer that set nBackfill could not
 -  ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
 -  */
 -  pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1;
 -  walShmBarrier(pWal);
 -  if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
 -   || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
 -  ){
 -    walUnlockShared(pWal, WAL_READ_LOCK(mxI));
 -    return WAL_RETRY;
 -  }else{
 -    assert( mxReadMark<=pWal->hdr.mxFrame );
 -    pWal->readLock = (i16)mxI;
    }
    return rc;
  }
@@@ -3997,22 -3269,12 +4001,22 @@@ int sqlite3WalReadFrame
    sz = (sz&0xfe00) + ((sz&0x0001)<<16);
    testcase( sz<=32768 );
    testcase( sz>=65536 );
 +
 +  if( isWalMode2(pWal) ){
 +    /* Figure out which of the two wal files, and the frame within, that 
 +    ** iExternal refers to.  */
 +    iWal = walExternalDecode(iExternal, &iRead);
 +  }else{
 +    iRead = iExternal;
 +  }
 +
 +  WALTRACE(("WAL%p: reading frame %d wal %d\n", pWal, iRead, iWal));
    iOffset = walFrameOffset(iRead, sz) + WAL_FRAME_HDRSIZE;
    /* testcase( IS_BIG_INT(iOffset) ); // requires a 4GiB WAL */
 -  return sqlite3OsRead(pWal->pWalFd, pOut, (nOut>sz ? sz : nOut), iOffset);
 +  return sqlite3OsRead(pWal->apWalFd[iWal], pOut, (nOut>sz?sz:nOut), iOffset);
  }
  
- /* 
+ /*
  ** Return the size of the database in pages (or zero, if unknown).
  */
  Pgno sqlite3WalDbsize(Wal *pWal){
@@@ -4109,23 -3371,20 +4113,23 @@@ int sqlite3WalEndWriteTransaction(Wal *
  int sqlite3WalUndo(Wal *pWal, int (*xUndo)(void *, Pgno), void *pUndoCtx){
    int rc = SQLITE_OK;
    if( ALWAYS(pWal->writeLock) ){
 -    Pgno iMax = pWal->hdr.mxFrame;
 +    int iWal = walidxGetFile(&pWal->hdr);
 +    Pgno iMax = walidxGetMxFrame(&pWal->hdr, iWal);
 +    Pgno iNew;
      Pgno iFrame;
  
 +    assert( isWalMode2(pWal) || iWal==0 );
 +
      /* Restore the clients cache of the wal-index header to the state it
-     ** was in before the client began writing to the database. 
+     ** was in before the client began writing to the database.
      */
      memcpy(&pWal->hdr, (void *)walIndexHdr(pWal), sizeof(WalIndexHdr));
 +    assert( walidxGetFile(&pWal->hdr)==iWal );
 +    iNew = walidxGetMxFrame(&pWal->hdr, walidxGetFile(&pWal->hdr));
  
 -    for(iFrame=pWal->hdr.mxFrame+1;
 -        ALWAYS(rc==SQLITE_OK) && iFrame<=iMax;
 -        iFrame++
 -    ){
 +    for(iFrame=iNew+1; ALWAYS(rc==SQLITE_OK) && iFrame<=iMax; iFrame++){
        /* This call cannot fail. Unless the page for which the page number
-       ** is passed as the second argument is (a) in the cache and 
+       ** is passed as the second argument is (a) in the cache and
        ** (b) has an outstanding reference, then xUndo is either a no-op
        ** (if (a) is false) or simply expels the page from the cache (if (b)
        ** is false).
  ** point in the event of a savepoint rollback (via WalSavepointUndo()).
  */
  void sqlite3WalSavepoint(Wal *pWal, u32 *aWalData){
 +  int iWal = walidxGetFile(&pWal->hdr);
    assert( pWal->writeLock );
 -  aWalData[0] = pWal->hdr.mxFrame;
 +  assert( isWalMode2(pWal) || iWal==0 );
 +  aWalData[0] = walidxGetMxFrame(&pWal->hdr, iWal);
    aWalData[1] = pWal->hdr.aFrameCksum[0];
    aWalData[2] = pWal->hdr.aFrameCksum[1];
 -  aWalData[3] = pWal->nCkpt;
 +  aWalData[3] = isWalMode2(pWal) ? iWal : pWal->nCkpt;
  }
  
- /* 
+ /*
  ** Move the write position of the WAL back to the point identified by
  ** the values in the aWalData[] array. aWalData must point to an array
  ** of WAL_SAVEPOINT_NDATA u32 values that has been previously populated
@@@ -4800,8 -3956,8 +4804,8 @@@ int sqlite3WalCheckpoint
      }
    }
  
 -  if( isChanged ){
 +  if( isChanged && pWal->bClosing==0 ){
-     /* If a new wal-index header was loaded before the checkpoint was 
+     /* If a new wal-index header was loaded before the checkpoint was
      ** performed, then the pager-cache associated with pWal is now
      ** out of date. So zero the cached wal-index header to ensure that
      ** next time the pager opens a snapshot on this database it knows that