- C Merge\srecent\schanges\sfrom\strunk\sinto\sreuse-schema
- D 2019-04-15T15:35:43.643
-C Fix\sa\sproblem\swith\sthe\sfix\sfor\s[9cf6c9bb51]\s(commit\s[658b84d7])\sthat\scould\scause\sa\scursor\sto\sbe\sleft\sin\san\sinvalid\sstate\sfollowing\sa\s(rowid\s<\stext-value)\ssearch.
-D 2019-05-14T20:25:22.199
++C Merge\sall\sthe\slatest\strunk\senhancements\sinto\sthe\sreuse-schema\sbranch.
++D 2019-05-14T22:25:26.972
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
-F Makefile.in 4640daf826b80947a924ac44275c451ffc13007c7c866a5730c8ce5cf9e1dc74
+F Makefile.in c4b55bbca70511d8757e38358dfbc06dd74abd51d61bf4939d2caea023c9b97b
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
-F Makefile.msc 26957950b2b4f3b26e311eeea70437f85a77765f71d3a06489466d66ee321100
+F Makefile.msc 93f8f1352fe1a03249a0f7a24c42df04fef01a74a56fe309420b4b3931945bdf
F README.md 74745e53db87fdc86f571dd7ec1bd18e154d0abd6d37d2292a1062e931318a29
- F VERSION 288d756b1b7be03ecdbf1795c23af2c8425f2e46ba6979a14ef53360308f080d
+ F VERSION cc8cd90333c65cdf4cb346f356a2ce1eb0f5b7fa1d17a34d7350103e7320af1f
F aclocal.m4 a5c22d164aff7ed549d53a90fa56d56955281f50
F art/sqlite370.eps aa97a671332b432a54e1d74ff5e8775be34200c2
F art/sqlite370.ico af56c1d00fee7cd4753e8631ed60703ed0fc6e90
F install-sh 9d4de14ab9fb0facae2f48780b874848cbf2f895 x
F ltmain.sh 3ff0879076df340d2e23ae905484d8c15d5fdea8
F magic.txt 8273bf49ba3b0c8559cb2774495390c31fd61c60
- F main.mk 2db4b35d6fec85ffa158bd5f078bde988434e96746932240a028bab0216579a6
-F main.mk 125adda36bb32c99dc3a11340bd029ef373b9523eac2b2af76087bfe82d4fdf8
++F main.mk b5dd74c752335a232b454f55387012e9326ee6002a2d0c2a2f3b6dc8b8dec642
F mkso.sh fd21c06b063bb16a5d25deea1752c2da6ac3ed83
F mptest/config01.test 3c6adcbc50b991866855f1977ff172eb6d901271
F mptest/config02.test 4415dfe36c48785f751e16e32c20b077c28ae504
F sqlite.pc.in 42b7bf0d02e08b9e77734a47798d1a55a9e0716b
F sqlite3.1 fc7ad8990fc8409983309bb80de8c811a7506786
F sqlite3.pc.in 48fed132e7cb71ab676105d2a4dc77127d8c1f3a
- F src/alter.c 7516a6e36dd5634ef7f28541c5dfc3861f2e619ffa9d0aeab54c565d1254f0f6
-F src/alter.c 85b41586b2527c8288b249fb0beb96f25860e5b2bf94c02f788b3d0f686354ee
-F src/analyze.c 58db66344a5c58dcabb57f26696f6f2993956c830446da40b444051d2fdaf644
-F src/attach.c 78e986baee90cb7b83fb9eafa79c22581a8ada14030fd633b0683c95cf11213c
++F src/alter.c 7b0602147d45191494399507303531909a25e0f6ce7e91621a30c6be3f6a21f5
+F src/analyze.c e75c3c1d6534265f74a4763282f17e9ad946606ef0a68c5517fcfb355cc243d0
- F src/attach.c d42f6c3a7f838714ae4a95cc26151d96f481c8adee7cb179ac03be05843e5a37
++F src/attach.c 95c3e4b1aa81c16a537cfa0c990dbcfa6e460e382b6ff888752f8e70075a4def
F src/auth.c 0fac71038875693a937e506bceb492c5f136dd7b1249fbd4ae70b4e8da14f9df
- F src/backup.c 78d3cecfbe28230a3a9a1793e2ead609f469be43e8f486ca996006be551857ab
+ F src/backup.c b1d37f6f7f5913944583733ed0f9e182f3ece0d42c27f46701142141a6e6fd33
F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33
F src/btmutex.c 8acc2f464ee76324bf13310df5692a262b801808984c1b79defb2503bbafadb6
- F src/btree.c 958939f608e351a36756e3749596472baa0e5aae54eebd14e6beffe7a68aafc7
+ F src/btree.c 5e15f903fd44b076b864a8d2449d63b44a546efabb66fca7dfed90f106f5c756
F src/btree.h c11446f07ec0e9dc85af8041cb0855c52f5359c8b2a43e47e02a685282504d89
F src/btreeInt.h 6111c15868b90669f79081039d19e7ea8674013f907710baa3c814dc3f8bfd3f
- F src/build.c ed008d999a9851c561874d8101ef9b759acd19c6cc1dbbb51461abe055f4368c
-F src/build.c aead3d74794ed4dac396fdd9323ba2272e3734f07c5d7f1f57389e5afbd99f3f
-F src/callback.c 25dda5e1c2334a367b94a64077b1d06b2553369f616261ca6783c48bcb6bda73
++F src/build.c ae462f7766fa342ad6700f958111a0028253709ed0c2e01a458bf575e49b44fe
+F src/callback.c 5a9a83d7cecce452902da0a24bf51f6e4d232b5dc54b67418276ca9ad82c5bde
F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e
F src/ctime.c 109e58d00f62e8e71ee1eb5944ac18b90171c928ab2e082e058056e1137cc20b
F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
F src/dbpage.c 135eb3b5e74f9ef74bde5cec2571192c90c86984fa534c88bf4a055076fa19b7
F src/dbstat.c c12833de69cb655751487d2c5a59607e36be1c58ba1f4bd536609909ad47b319
F src/delete.c d08c9e01a2664afd12edcfa3a9c6578517e8ff8735f35509582693adbe0edeaf
- F src/expr.c e100212835d20498780e7c6d2bdb16c677ecc04350fb75db3bf192a86ba48c92
+ F src/expr.c a41e5a03d60e2d99a8eac1535f21258e3833b29a3f52789e76284d97b54bed5f
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
- F src/fkey.c 878bec1b4d9aab3b34585121b834fce9b2780352b7282510e4ffc94b75ce6c83
- F src/func.c 2ccf4ae12430b1ae7096be5f0675887e1bd0732828af0ac0f7496339b7c6edee
-F src/fkey.c 0e14d4bef8eac2d87bbd517e492d9084c65008d117823f8922c5e7b2b599bd33
++F src/fkey.c 4b54ea67779fb17590861382acc0243d05bde9f3ce680504506db2f5428ee00e
+ F src/func.c 08d6d07d138735dd79f12a2b0c623d1dc9270d0eea61b8be584625391ef84475
F src/global.c 0dea3065ea72a65ae941559b6686aad6516d4913e76fa4f79a95ff7787f624ec
F src/hash.c 8d7dda241d0ebdafb6ffdeda3149a412d7df75102cecfc1021c98d6219823b19
F src/hash.h 9d56a9079d523b648774c1784b74b89bd93fac7b365210157482e4319a468f38
F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da
F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71
- F src/insert.c fc3cf5c371f9a400144e8c2f148ab29cd3f67f7da7eaf47e6a6959f8255fd92c
+ F src/insert.c 4ffc3aa5d2aed178b501533428a76e150907e92a1e4bf7af4ffbcb0d77e99823
F src/legacy.c d7874bc885906868cd51e6c2156698f2754f02d9eee1bae2d687323c3ca8e5aa
F src/loadext.c 22afc33c3a61b4fd80a60a54f1882688371e6bc64685df2696b008fce65a999c
- F src/main.c 815aee401c9a031449f28d26fdf5d33104cbcd5c86709b11614362cd12e5445c
-F src/main.c 3c3925b0bcb4c45687fd52f54c79e98e379252e1d3393f8b7dcccfa26181b661
++F src/main.c 7d9370ced0eff622de28df31656220bf78046c66d0ac8d53c80409bdf306335d
F src/malloc.c 0f9da2a66b230a5785af94b9672126845099b57b70a32c987d04ac28c69da990
F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de
F src/pcache.c 696a01f1a6370c1b50a09c15972bc3bee3333f8fcd1f2da8e9a76b1b062c59ee
F src/pcache.h 4f87acd914cef5016fae3030343540d75f5b85a1877eed1a2a19b9f284248586
F src/pcache1.c be64b2f3908a7f97c56c963676eb12f0d6254c95b28cdc1d73a186eff213219d
- F src/pragma.c 13e40651f965e9ac6d4fd44d6c48c4152185209b677b253dc2141c32c14c8dd6
- F src/pragma.h 9d7658a7be442c1149409aab8ebdde012f387de66fca20056fc9e0791e40dbda
-F src/pragma.c 2e9fbfcb23cb72eabb38ab6fa84c36a65f9c4839ce1e9bb3dd982ab26b67a5a8
-F src/pragma.h 482c26f352efd7a4ed1354d83ffa992e13004f6528edeee44cdbfd5025a490bd
-F src/prepare.c 78027c6231fbb19ca186a5f5f0c0a1375d9c2cec0655273f9bd90d9ff74a34b3
++F src/pragma.c 5e8c63fd746a6a521c657b7d2ce02205286c6a0b90b9fdf3f682d8d8b9e7cd66
++F src/pragma.h 85a1a57c0c9f63f615cf4bde60f8a413a42e1931eef577dfa0e41934fca492bb
+F src/prepare.c ed5110154bd7644665398b8e5801937f83af3a069c928244235462b5e3adcef3
F src/printf.c 67f79227273a9009d86a017619717c3f554f50b371294526da59faa6014ed2cd
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
- F src/resolve.c 567888ee3faec14dae06519b4306201771058364a37560186a3e0e755ebc4cb8
+ F src/resolve.c 408632d9531ca8f1df8591f00530797daaa7bde3fe0d3211de4d431cbb99347e
F src/rowset.c d977b011993aaea002cab3e0bb2ce50cf346000dff94e944d547b989f4b1fe93
- F src/select.c 9263f5c30dd44c7ac2eb29f40a7ec64322a96885b71c00de6bc30b756c2e1c49
- F src/shell.c.in 30eac60e064eca559c173b46e243c912c30c90c34bdcaafeabb03b5537c72a1c
- F src/sqlite.h.in d8e38484e42e20e6e677cb222c67ff8487142a82e2ed38eeb908ebd57a5a8b76
+ F src/select.c ef860c7e5882c89c030432a6d2cf13c67d1d51fd511cf45cbdfd5c2faf44d51d
-F src/shell.c.in 6c992809abf20dbb4aad89299d7c15c98ddf2504b23c83ef71eb435ad392cdc3
-F src/sqlite.h.in 0605c88d98c85fbcba8bbd9716e7cc10b361e7b21cf2375171130f577388c943
++F src/shell.c.in 9353bb93d04b31c823fdd9140beb10d7dc9f19c878d79f081fe08bdede11f6e7
++F src/sqlite.h.in 66e5751c85cd2602213e86945adf26387dba919ac578e55eac0e9b08e453c914
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
F src/sqlite3ext.h 9ecc93b8493bd20c0c07d52e2ac0ed8bab9b549c7f7955b59869597b650dd8b5
- F src/sqliteInt.h 666517f86d73f4f36cc17cb2221ee43117a54342228e99f09bba227cdbcfbe75
-F src/sqliteInt.h b78a1f0bdc46582f33876932f8b45a264be7ee4f3f6c31102f924d33fa6177ee
++F src/sqliteInt.h b14459b20bfccd8e0a58d463b7df605908ffd4adc8a360c5ed15e98e95e0d828
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
-F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
+F src/status.c d9f7d66ae8bee9ffbe4cf7ae72a8d3d429a269336dee1e705ecb3dbea27fa5e2
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
-F src/tclsqlite.c cfe7f93daf9d8787f65e099efb67d7cdfc2c35236dec5d3f6758520bd3519424
+F src/tclsqlite.c 5947f3797410e213cde1d61f61fbfda706ee4ee2e1db6e0b2376e3206dabc71d
- F src/test1.c cfb303eeddd3670409af6b58d2ddb928b8e9e70822d681d3df88dfaabb7bea6a
+ F src/test1.c f4e0be5c344587b2beac474a58018a3833208fb6bbec35d37d58b1270a7a5917
F src/test2.c 3efb99ab7f1fc8d154933e02ae1378bac9637da5
F src/test3.c 61798bb0d38b915067a8c8e03f5a534b431181f802659a6616f9b4ff7d872644
F src/test4.c 405834f6a93ec395cc4c9bb8ecebf7c3d8079e7ca16ae65e82d01afd229694bb
F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c
F src/tokenize.c d3615f0cbe4db5949503bf5916f3cd4fa5de855d5b4ef560f3b6dd5629423a1e
F src/treeview.c 56724725c62a0d0f408f7c257475dc33309198afee36a1d18be1bc268b09055e
-F src/trigger.c bb034c08eca111e66a19cda045903a12547c1be2294b5570d794b869d9c44a73
+F src/trigger.c 020d7ad68bee5c3f0f2244eb8c2b53394f908a4f63fe0017dc8cd001e4998931
- F src/update.c 0b973357d88092140531e07ff641139c26fb4380b0b9f5ed98c5f7691b4604d1
+ F src/update.c 3cb9150d2cf661d938e2f1b1749945f3faa767f88febdb739ab1793bbf895ff2
F src/upsert.c 0dd81b40206841814d46942a7337786932475f085716042d0cb2fc7791bf8ca4
F src/utf.c 2f0fac345c7660d5c5bd3df9e9d8d33d4c27f366bcfb09e07443064d751a0507
- F src/util.c e12939405e77906d06ab0b78c5f513dcd2b7cec2fbb553877b0abfece6067141
- F src/vacuum.c 36b6328b64e571eaa8d8e82d57658bb5b381539303f15c05a28cc80d8862e908
- F src/vdbe.c 47d08dda2ade559d85c75ccc4623c6aa7f5c063bf323a382b307145ed66ae7ab
+ F src/util.c 4c0669e042b4e50a08a9e5fd14cecc76e5f877efa288533dccddb6fe98f4d6b5
-F src/vacuum.c 82dcec9e7b1afa980288718ad11bc499651c722d7b9f32933c4d694d91cb6ebf
-F src/vdbe.c d14841dc77e5b0cbd5cb857dee026f17b07a9d1ab147c1c2c9557227be6a52c9
-F src/vdbe.h 712bca562eaed1c25506b9faf9680bdc75fc42e2f4a1cd518d883fa79c7a4237
++F src/vacuum.c 5b9b4771ffb47aaef038c4ad5f1acd76dfbc9e0a4f101c1ba7f7ac9c2ba96374
++F src/vdbe.c 28d2d2a821c8b2b125c0f3bad976cb50db384f8c912eb1c7676ca96828f9cd2e
+F src/vdbe.h 323218c4bfd64c719ba85d05fbc87cdd126991cadb39e73ccac7b59f30c3d53e
- F src/vdbeInt.h 2c12704db9740c8e899786ecfc7a5797a9d067563496eb1b6ed03c592d7b8d90
- F src/vdbeapi.c 2ddd60f4a351f15ee98d841e346af16111ad59dfa4d25d2dd4012e9875bf7d92
- F src/vdbeaux.c 793111f45c2973491bcad36e345514cdaa0b16f0a7b2e9ae887a7050e4d6efdf
+ F src/vdbeInt.h 3ba14553508d66f58753952d6dd287dce4ec735de02c6440858b4891aed51c17
+ F src/vdbeapi.c f9161e5c77f512fbb80091ce8af621d19c9556bda5e734cffaac1198407400da
-F src/vdbeaux.c d444f4a3ff9c571965329a186701a57fe445e4c3f4c42f87402aca75386ba358
-F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
++F src/vdbeaux.c 95cabb0e8c49b762f4ced25a54b38036a3975e65140e623ce034979cc3e7d93b
+F src/vdbeblob.c 08e58c6bef990008b8c2dbad4c99c56d3b622ee79433004484cce29b536e2ab9
- F src/vdbemem.c 8e6889761e344babdb8a56dd1ac8911501fa648396544d1644f1cd6a87c80dc0
- F src/vdbesort.c 31c7794a517e8b0a1704988f1f7596b74c6fc07eeb7bb85776f50a391ed9d94f
- F src/vdbetrace.c 79d6dbbc479267b255a7de8080eee6e729928a0ef93ed9b0bfa5618875b48392
- F src/vtab.c 9fe701febd1cfb44af4810b7f1bcc2db327c3a818e070b53aad132dd193d61a0
+ F src/vdbemem.c b76b42ac9d6a36fc55a0797929fc94cc33e1334eea2792f5ee1eef868ce13320
+ F src/vdbesort.c 66592d478dbb46f19aed0b42222325eadb84deb40a90eebe25c6e7c1d8468f47
+ F src/vdbetrace.c fa3bf238002f0bbbdfb66cc8afb0cea284ff9f148d6439bc1f6f2b4c3b7143f0
-F src/vtab.c 1fa256c6ddad7a81e2a4dc080d015d4b0a7135767717d311298e47f6fca64bb3
++F src/vtab.c 4bb7440736c84abc5500ab0bf4946db4f5e48775513d58b28df212043ba5c2d1
F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9
- F src/wal.c 9eccc7ebb532a7b0fd3cabc16cff576b9afa763472272db67d84fb8cec96f5c0
+ F src/wal.c b09a2a9cab50efa08451a8c81d47052120ad5da174048c6d0b08d405384abdf2
F src/wal.h 606292549f5a7be50b6227bd685fa76e3a4affad71bb8ac5ce4cb5c79f6a176a
F src/walker.c 7607f1a68130c028255d8d56094ea602fc402c79e1e35a46e6282849d90d5fe4
- F src/where.c ff2955dc2743c1af05ba5a8232ab72724d9a63b76dbee256368f40fd3ef82db5
- F src/whereInt.h 5f14db426ca46a83eabab1ae9aa6d4b8f27504ad35b64c290916289b1ddb2e88
- F src/wherecode.c 83be72e8d1c0231d0db06ffe5cfd32c7834bd00d2ed869306a2c1e0828488752
- F src/whereexpr.c 90859652920f153d2c03f075488744be2926625ebd36911bcbcb17d0d29c891c
- F src/window.c 038c248267e74ff70a2bb9b1884d40fd145c5183b017823ecb6cbb14bc781478
+ F src/where.c 99c7b718ef846ac952016083aaf4e22ede2290beceaf4730a2df55c023251369
+ F src/whereInt.h 1b728f71654ebf8421a1715497a587f02d6f538e819af58dc826908f8577e810
+ F src/wherecode.c 37a1004237d630d785c47bba2290eac652a7a8b0047518eba3cb7c808b604c4a
+ F src/whereexpr.c 4219bdd5d310ba6424166d918efef301c21e1b7f6444e964b415c4a5b877a8fe
+ F src/window.c 5be2cf7d8763cc97137fc44d015aed8a1a4a56fe9700d7933ed560172617c756
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
F test/affinity3.test 6a101af2fc945ce2912f6fe54dd646018551710d
F tool/mkopcodec.tcl d1b6362bd3aa80d5520d4d6f3765badf01f6c43c
F tool/mkopcodeh.tcl 352a4319c0ad869eb26442bf7c3b015aa15594c21f1cce5a6420dbe999367c21
F tool/mkopts.tcl 680f785fdb09729fd9ac50632413da4eadbdf9071535e3f26d03795828ab07fa
- F tool/mkpragmatab.tcl a3ca473c1ddf1fd6b95e2670907b7856c26c52091a5daa78e88b21a471c39e48
- F tool/mkshellc.tcl 1f45770aea226ac093a9c72f718efbb88a2a2833409ec2e1c4cecae4202626f5
-F tool/mkpragmatab.tcl d8887dfbd5a40c9e5de2c011db989af52152b9bcc64059d9e93b28edf38af9b9
++F tool/mkpragmatab.tcl 34f68553b3903fda0707fa282dd6eb267de07dbafd1f05342014a82f87fc8a96
+ F tool/mkshellc.tcl 70a9978e363b0f3280ca9ce1c46d72563ff479c1930a12a7375e3881b7325712
F tool/mksourceid.c d458f9004c837bee87a6382228ac20d3eae3c49ea3b0a5aace936f8b60748d3b
F tool/mkspeedsql.tcl a1a334d288f7adfe6e996f2e712becf076745c97
F tool/mksqlite3c-noext.tcl 4f7cfef5152b0c91920355cbfc1d608a4ad242cb819f1aea07f6d0274f584a7f
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
- P 9f591ac07871958d173ae4cf9065c8000a9efb80676d158935d2700fff61a361 734192d876e0489cb34e454aaa4a9c6858145791a49959b31dee2917a8e41a7d
- R afca53b9169e39309b1923b1610dd287
-P 228e1087c0602470e450586499de5a3e87e266c688bc828f20e3bad2fdc65ff1
-R d132bb7525b36863c7f0dbefbbac4ccc
-U dan
-Z 61ab05a485e7c528107861afc63f60a9
++P 5c6c9e7f6f83d565b317e3442e8bc664478adb407fe416d068fe0dcd17ffa5b5 bc7d2c1656396bb4f5f1f814e60dbf816cc91c5a521b54ad593cd3da0fe8dcb4
++R 253230c497ea5d35d5c003fbd477012b
+U drh
- Z c8407b9bef52280edc181feafa94c840
++Z fae651c1a82209099a4f420d4dbb336e
/* Allowed values for ShellState.openMode
*/
- #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
- #define SHELL_OPEN_NORMAL 1 /* Normal database file */
- #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
- #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
- #define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
- #define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
- #define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
- #define SHELL_OPEN_SHAREDSCHEMA 7 /* Open for schema reuse */
-#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
-#define SHELL_OPEN_NORMAL 1 /* Normal database file */
-#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
-#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
-#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
-#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
-#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
++#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
++#define SHELL_OPEN_NORMAL 1 /* Normal database file */
++#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
++#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
++#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
++#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
++#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
++#define SHELL_OPEN_SHAREDSCHEMA 7 /* Open for schema reuse */
/* Allowed values for ShellState.eTraceType
*/
rc = arExecSql(pAr, zSql2);
sqlite3_free(zSql2);
}
- end_ar_transaction:
- if( rc!=SQLITE_OK ){
- sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
- }else{
- rc = arExecSql(pAr, "RELEASE ar;");
- if( pAr->bZip && pAr->zFile ){
- zSql = sqlite3_mprintf("DROP TABLE %s", zTemp);
- arExecSql(pAr, zSql);
- sqlite3_free(zSql);
+ end_ar_transaction:
+ if( rc!=SQLITE_OK ){
+ sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
+ }else{
+ rc = arExecSql(pAr, "RELEASE ar;");
+ if( pAr->bZip && pAr->zFile ){
+ zSql = sqlite3_mprintf("DROP TABLE %s", zTemp);
+ arExecSql(pAr, zSql);
+ sqlite3_free(zSql);
+ }
+ }
+ sqlite3_free(zExists);
+ return rc;
+ }
+
+ /*
+ ** Implementation of ".ar" dot command.
+ */
+ static int arDotCommand(
+ ShellState *pState, /* Current shell tool state */
+ int fromCmdLine, /* True if -A command-line option, not .ar cmd */
+ char **azArg, /* Array of arguments passed to dot command */
+ int nArg /* Number of entries in azArg[] */
+ ){
+ ArCommand cmd;
+ int rc;
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.fromCmdLine = fromCmdLine;
+ rc = arParseCommand(azArg, nArg, &cmd);
+ if( rc==SQLITE_OK ){
+ int eDbType = SHELL_OPEN_UNSPEC;
+ cmd.p = pState;
+ cmd.db = pState->db;
+ if( cmd.zFile ){
+ eDbType = deduceDatabaseType(cmd.zFile, 1);
+ }else{
+ eDbType = pState->openMode;
+ }
+ if( eDbType==SHELL_OPEN_ZIPFILE ){
+ if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){
+ if( cmd.zFile==0 ){
+ cmd.zSrcTable = sqlite3_mprintf("zip");
+ }else{
+ cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile);
+ }
+ }
+ cmd.bZip = 1;
+ }else if( cmd.zFile ){
+ int flags;
+ if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
+ if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
+ || cmd.eCmd==AR_CMD_UPDATE ){
+ flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+ }else{
+ flags = SQLITE_OPEN_READONLY;
+ }
+ cmd.db = 0;
+ if( cmd.bDryRun ){
+ utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
+ eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
+ }
+ rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
+ eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
+ if( rc!=SQLITE_OK ){
+ utf8_printf(stderr, "cannot open file: %s (%s)\n",
+ cmd.zFile, sqlite3_errmsg(cmd.db)
+ );
+ goto end_ar_command;
+ }
+ sqlite3_fileio_init(cmd.db, 0, 0);
+ sqlite3_sqlar_init(cmd.db, 0, 0);
+ sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p,
+ shellPutsFunc, 0, 0);
+
+ }
+ if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
+ if( cmd.eCmd!=AR_CMD_CREATE
+ && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
+ ){
+ utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
+ rc = SQLITE_ERROR;
+ goto end_ar_command;
+ }
+ cmd.zSrcTable = sqlite3_mprintf("sqlar");
+ }
+
+ switch( cmd.eCmd ){
+ case AR_CMD_CREATE:
+ rc = arCreateOrUpdateCommand(&cmd, 0, 0);
+ break;
+
+ case AR_CMD_EXTRACT:
+ rc = arExtractCommand(&cmd);
+ break;
+
+ case AR_CMD_LIST:
+ rc = arListCommand(&cmd);
+ break;
+
+ case AR_CMD_HELP:
+ arUsage(pState->out);
+ break;
+
+ case AR_CMD_INSERT:
+ rc = arCreateOrUpdateCommand(&cmd, 1, 0);
+ break;
+
+ default:
+ assert( cmd.eCmd==AR_CMD_UPDATE );
+ rc = arCreateOrUpdateCommand(&cmd, 1, 1);
+ break;
+ }
+ }
+ end_ar_command:
+ if( cmd.db!=pState->db ){
+ close_db(cmd.db);
+ }
+ sqlite3_free(cmd.zSrcTable);
+
+ return rc;
+ }
+ /* End of the ".archive" or ".ar" command logic
+ **********************************************************************************/
+ #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
+
+ #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
+ /*
+ ** If (*pRc) is not SQLITE_OK when this function is called, it is a no-op.
+ ** Otherwise, the SQL statement or statements in zSql are executed using
+ ** database connection db and the error code written to *pRc before
+ ** this function returns.
+ */
+ static void shellExec(sqlite3 *db, int *pRc, const char *zSql){
+ int rc = *pRc;
+ if( rc==SQLITE_OK ){
+ char *zErr = 0;
+ rc = sqlite3_exec(db, zSql, 0, 0, &zErr);
+ if( rc!=SQLITE_OK ){
+ raw_printf(stderr, "SQL error: %s\n", zErr);
+ }
+ *pRc = rc;
+ }
+ }
+
+ /*
+ ** Like shellExec(), except that zFmt is a printf() style format string.
+ */
+ static void shellExecPrintf(sqlite3 *db, int *pRc, const char *zFmt, ...){
+ char *z = 0;
+ if( *pRc==SQLITE_OK ){
+ va_list ap;
+ va_start(ap, zFmt);
+ z = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( z==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ shellExec(db, pRc, z);
+ }
+ sqlite3_free(z);
+ }
+ }
+
+ /*
+ ** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+ ** Otherwise, an attempt is made to allocate, zero and return a pointer
+ ** to a buffer nByte bytes in size. If an OOM error occurs, *pRc is set
+ ** to SQLITE_NOMEM and NULL returned.
+ */
+ static void *shellMalloc(int *pRc, sqlite3_int64 nByte){
+ void *pRet = 0;
+ if( *pRc==SQLITE_OK ){
+ pRet = sqlite3_malloc64(nByte);
+ if( pRet==0 ){
+ *pRc = SQLITE_NOMEM;
+ }else{
+ memset(pRet, 0, nByte);
+ }
+ }
+ return pRet;
+ }
+
+ /*
+ ** If *pRc is not SQLITE_OK when this function is called, it is a no-op.
+ ** Otherwise, zFmt is treated as a printf() style string. The result of
+ ** formatting it along with any trailing arguments is written into a
+ ** buffer obtained from sqlite3_malloc(), and pointer to which is returned.
+ ** It is the responsibility of the caller to eventually free this buffer
+ ** using a call to sqlite3_free().
+ **
+ ** If an OOM error occurs, (*pRc) is set to SQLITE_NOMEM and a NULL
+ ** pointer returned.
+ */
+ static char *shellMPrintf(int *pRc, const char *zFmt, ...){
+ char *z = 0;
+ if( *pRc==SQLITE_OK ){
+ va_list ap;
+ va_start(ap, zFmt);
+ z = sqlite3_vmprintf(zFmt, ap);
+ va_end(ap);
+ if( z==0 ){
+ *pRc = SQLITE_NOMEM;
+ }
+ }
+ return z;
+ }
++static int sharedSchemaFix(ShellState *pState, const char *zDb, int eFix){
++ int rc = SQLITE_OK;
++ i64 iLast = 0;
++ int iCookie = 0;
++ int iAutoVacuum = 0;
++ sqlite3_stmt *pStmt = 0;
++
++ shellExecPrintf(pState->db, &rc, "ATTACH '%q' AS _shared_schema_tmp", zDb);
++ shellExecPrintf(pState->db, &rc, "PRAGMA writable_schema = 1");
++ shellExecPrintf(pState->db, &rc, "BEGIN");
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "SELECT max(rowid) FROM _shared_schema_tmp.sqlite_master"
++ );
++ sqlite3_step(pStmt);
++ iLast = sqlite3_column_int64(pStmt, 0);
++ shellFinalize(&rc, pStmt);
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "INSERT INTO _shared_schema_tmp.sqlite_master SELECT "
++ " type, name, tbl_name, ("
++ " SELECT rootpage FROM _shared_schema_tmp.sqlite_master WHERE "
++ " type IS o.type AND name IS o.name AND rowid<=?"
++ " ), sql FROM main.sqlite_master AS o"
++ );
++ sqlite3_bind_int64(pStmt, 1, iLast);
++ sqlite3_step(pStmt);
++ shellFinalize(&rc, pStmt);
++
++ shellExecPrintf(pState->db, &rc,
++ "DELETE FROM _shared_schema_tmp.sqlite_master WHERE rowid<=%lld",
++ iLast
++ );
++ shellExecPrintf(pState->db, &rc, "COMMIT");
++ sqlite3_exec(pState->db, "PRAGMA writable_schema = 0", 0, 0, 0);
++
++ /* Copy the auto-vacuum setting from main to the target db */
++ shellPreparePrintf(pState->db, &rc, &pStmt, "PRAGMA main.auto_vacuum");
++ sqlite3_step(pStmt);
++ iAutoVacuum = sqlite3_column_int(pStmt, 0);
++ shellFinalize(&rc, pStmt);
++ shellExecPrintf(pState->db, &rc,
++ "PRAGMA _shared_schema_tmp.auto_vacuum = %d", iAutoVacuum
++ );
++
++ /* Vacuum the db in order to standardize the rootpage numbers. */
++ shellExecPrintf(pState->db, &rc, "VACUUM _shared_schema_tmp");
++
++ /* Set the schema-cookie value to the same as database "main" */
++ shellPreparePrintf(pState->db, &rc, &pStmt, "PRAGMA main.schema_version");
++ sqlite3_step(pStmt);
++ iCookie = sqlite3_column_int(pStmt, 0);
++ shellFinalize(&rc, pStmt);
++ shellExecPrintf(pState->db, &rc,
++ "PRAGMA _shared_schema_tmp.schema_version = %d", iCookie
++ );
++
++ sqlite3_exec(pState->db, "DETACH _shared_schema_tmp", 0, 0, 0);
++ return rc;
++}
++
++static int sharedSchemaCheck(ShellState *pState, const char *zDb, int *peFix){
++ int rc = SQLITE_OK;
++ int bFailed = 0;
++ sqlite3_stmt *pStmt = 0;
++
++ if( peFix ) *peFix = 0;
++ shellExecPrintf(pState->db, &rc, "ATTACH '%q' AS _shared_schema_tmp", zDb);
++
++ /* Check if this database has the same set of objects as the current db */
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "SELECT type, name FROM _shared_schema_tmp.sqlite_master AS o "
++ "WHERE NOT EXISTS ("
++ " SELECT 1 FROM main.sqlite_master "
++ " WHERE name IS o.name AND type IS o.type"
++ ")"
++ " UNION ALL "
++ "SELECT type, name FROM main.sqlite_master AS o "
++ "WHERE NOT EXISTS ("
++ " SELECT 1 FROM _shared_schema_tmp.sqlite_master "
++ " WHERE name IS o.name AND type IS o.type"
++ ")"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ utf8_printf(pState->out, "%s is NOT compatible (objects)\n", zDb);
++ bFailed = 1;
++ }
++ shellFinalize(&rc, pStmt);
++
++ /* Check if this database has the same set of SQL statements as the
++ ** current db. */
++ if( bFailed==0 ){
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "SELECT 1 FROM _shared_schema_tmp.sqlite_master AS o "
++ "WHERE sql IS NOT ("
++ " SELECT sql FROM main.sqlite_master "
++ " WHERE name IS o.name AND type IS o.type"
++ ")"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ utf8_printf(pState->out, "%s is NOT compatible (SQL)\n", zDb);
++ bFailed = 1;
++ }
++ shellFinalize(&rc, pStmt);
++ }
++
++ /* Check if this database has the same set of root pages as the current
++ ** db. */
++ if( bFailed==0 ){
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "SELECT 1 FROM _shared_schema_tmp.sqlite_master AS o "
++ "WHERE rootpage IS NOT ("
++ " SELECT rootpage FROM main.sqlite_master "
++ " WHERE name IS o.name AND type IS o.type"
++ ")"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ if( peFix==0 ){
++ utf8_printf(pState->out, "%s is NOT compatible (root pages)\n", zDb);
++ }
++ bFailed = 1;
++ if( peFix ) *peFix = 1;
++ }
++ shellFinalize(&rc, pStmt);
++ }
++
++ if( bFailed==0 ){
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "SELECT 1 WHERE ("
++ " SELECT group_concat(rootpage || '.' || name || '.' || sql, '.') "
++ " FROM _shared_schema_tmp.sqlite_master"
++ ") IS NOT ("
++ " SELECT group_concat(rootpage || '.' || name || '.' || sql, '.') "
++ " FROM main.sqlite_master"
++ ")"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ if( peFix==0 ){
++ utf8_printf(pState->out,
++ "%s is NOT compatible (order of sqlite_master rows)\n", zDb
++ );
++ }
++ bFailed = 1;
++ if( peFix ) *peFix = 2;
++ }
++ shellFinalize(&rc, pStmt);
++ }
++
++ if( bFailed==0 ){
++ int iMain = -1;
++ int iNew = +1;
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "PRAGMA main.schema_version"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ iMain = sqlite3_column_int(pStmt, 0);
++ }
++ shellFinalize(&rc, pStmt);
++ shellPreparePrintf(pState->db, &rc, &pStmt,
++ "PRAGMA _shared_schema_tmp.schema_version"
++ );
++ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
++ iNew = sqlite3_column_int(pStmt, 0);
++ }
++ shellFinalize(&rc, pStmt);
++ if( rc==SQLITE_OK && iMain!=iNew ){
++ if( peFix==0 ){
++ utf8_printf(pState->out,
++ "%s is NOT compatible (schema cookie)\n", zDb
++ );
++ }
++ bFailed = 1;
++ if( peFix ) *peFix = 3;
++ }
++ }
++
++ if( rc==SQLITE_OK && bFailed==0 ){
++ utf8_printf(pState->out, "%s is compatible\n", zDb);
++ }
++
++ sqlite3_exec(pState->db, "DETACH _shared_schema_tmp", 0, 0, 0);
++ return rc;
++}
++
++/*
++** .shared-schema check|fix DB1 DB2...
++*/
++static int sharedSchemaDotCommand(
++ ShellState *pState, /* Current shell tool state */
++ char **azArg, /* Array of arguments passed to dot command */
++ int nArg /* Number of entries in azArg[] */
++){
++ int rc = SQLITE_OK;
++ int bFix = 0; /* Fix databases if possible */
++ int n1;
++ int i;
++ if( nArg<3 ){
++ goto shared_schema_usage;
++ }
++
++ n1 = (int)strlen(azArg[1]);
++ if( n1>0 && n1<=3 && memcmp("fix", azArg[1], n1)==0 ){
++ bFix = 1;
++ }else if( n1==0 || n1>5 || memcmp("check", azArg[1], n1) ){
++ goto shared_schema_usage;
++ }
++
++ for(i=2; rc==SQLITE_OK && i<nArg; i++){
++ int eFix = 0;
++ rc = sharedSchemaCheck(pState, azArg[i], bFix ? &eFix : 0);
++ if( rc==SQLITE_OK && bFix && eFix ){
++ utf8_printf(pState->out, "Fixing %s... ", azArg[i]);
++ fflush(pState->out);
++ rc = sharedSchemaFix(pState, azArg[i], eFix);
++ if( rc==SQLITE_OK ){
++ rc = sharedSchemaCheck(pState, azArg[i], &eFix);
++ if( rc==SQLITE_OK && eFix ){
++ utf8_printf(pState->out, "VACUUMing main... ");
++ fflush(pState->out);
++ rc = sqlite3_exec(pState->db, "VACUUM main", 0, 0, 0);
++ if( rc==SQLITE_OK ){
++ rc = sharedSchemaCheck(pState, azArg[i], 0);
++ }
++ }
++ }
+ }
+ }
- sqlite3_free(zExists);
++
+ return rc;
++ shared_schema_usage:
++ raw_printf(stderr, "usage: .shared-schema check|fix DB1 DB2...\n");
++ return SQLITE_ERROR;
+}
/*
- ** Implementation of ".ar" dot command.
+ ** When running the ".recover" command, each output table, and the special
+ ** orphaned row table if it is required, is represented by an instance
+ ** of the following struct.
*/
- static int arDotCommand(
- ShellState *pState, /* Current shell tool state */
- int fromCmdLine, /* True if -A command-line option, not .ar cmd */
- char **azArg, /* Array of arguments passed to dot command */
- int nArg /* Number of entries in azArg[] */
+ typedef struct RecoverTable RecoverTable;
+ struct RecoverTable {
+ char *zQuoted; /* Quoted version of table name */
+ int nCol; /* Number of columns in table */
+ char **azlCol; /* Array of column lists */
+ int iPk; /* Index of IPK column */
+ };
+
+ /*
+ ** Free a RecoverTable object allocated by recoverFindTable() or
+ ** recoverOrphanTable().
+ */
+ static void recoverFreeTable(RecoverTable *pTab){
+ if( pTab ){
+ sqlite3_free(pTab->zQuoted);
+ if( pTab->azlCol ){
+ int i;
+ for(i=0; i<=pTab->nCol; i++){
+ sqlite3_free(pTab->azlCol[i]);
+ }
+ sqlite3_free(pTab->azlCol);
+ }
+ sqlite3_free(pTab);
+ }
+ }
+
+ /*
+ ** This function is a no-op if (*pRc) is not SQLITE_OK when it is called.
+ ** Otherwise, it allocates and returns a RecoverTable object based on the
+ ** final four arguments passed to this function. It is the responsibility
+ ** of the caller to eventually free the returned object using
+ ** recoverFreeTable().
+ */
+ static RecoverTable *recoverNewTable(
+ int *pRc, /* IN/OUT: Error code */
+ const char *zName, /* Name of table */
+ const char *zSql, /* CREATE TABLE statement */
+ int bIntkey,
+ int nCol
){
- ArCommand cmd;
- int rc;
- memset(&cmd, 0, sizeof(cmd));
- cmd.fromCmdLine = fromCmdLine;
- rc = arParseCommand(azArg, nArg, &cmd);
+ sqlite3 *dbtmp = 0; /* sqlite3 handle for testing CREATE TABLE */
+ int rc = *pRc;
+ RecoverTable *pTab = 0;
+
+ pTab = (RecoverTable*)shellMalloc(&rc, sizeof(RecoverTable));
if( rc==SQLITE_OK ){
- int eDbType = SHELL_OPEN_UNSPEC;
- cmd.p = pState;
- cmd.db = pState->db;
- if( cmd.zFile ){
- eDbType = deduceDatabaseType(cmd.zFile, 1);
- }else{
- eDbType = pState->openMode;
+ int nSqlCol = 0;
+ int bSqlIntkey = 0;
+ sqlite3_stmt *pStmt = 0;
+
+ rc = sqlite3_open("", &dbtmp);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(dbtmp, "PRAGMA writable_schema = on", 0, 0, 0);
}
- if( eDbType==SHELL_OPEN_ZIPFILE ){
- if( cmd.eCmd==AR_CMD_EXTRACT || cmd.eCmd==AR_CMD_LIST ){
- if( cmd.zFile==0 ){
- cmd.zSrcTable = sqlite3_mprintf("zip");
- }else{
- cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile);
+ if( rc==SQLITE_OK ){
+ rc = sqlite3_exec(dbtmp, zSql, 0, 0, 0);
+ if( rc==SQLITE_ERROR ){
+ rc = SQLITE_OK;
+ goto finished;
+ }
+ }
+ shellPreparePrintf(dbtmp, &rc, &pStmt,
+ "SELECT count(*) FROM pragma_table_info(%Q)", zName
+ );
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ nSqlCol = sqlite3_column_int(pStmt, 0);
+ }
+ shellFinalize(&rc, pStmt);
+
+ if( rc!=SQLITE_OK || nSqlCol<nCol ){
+ goto finished;
+ }
+
+ shellPreparePrintf(dbtmp, &rc, &pStmt,
+ "SELECT ("
+ " SELECT substr(data,1,1)==X'0D' FROM sqlite_dbpage WHERE pgno=rootpage"
+ ") FROM sqlite_master WHERE name = %Q", zName
+ );
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ bSqlIntkey = sqlite3_column_int(pStmt, 0);
+ }
+ shellFinalize(&rc, pStmt);
+
+ if( bIntkey==bSqlIntkey ){
+ int i;
+ const char *zPk = "_rowid_";
+ sqlite3_stmt *pPkFinder = 0;
+
+ /* If this is an intkey table and there is an INTEGER PRIMARY KEY,
+ ** set zPk to the name of the PK column, and pTab->iPk to the index
+ ** of the column, where columns are 0-numbered from left to right.
+ ** Or, if this is a WITHOUT ROWID table or if there is no IPK column,
+ ** leave zPk as "_rowid_" and pTab->iPk at -2. */
+ pTab->iPk = -2;
+ if( bIntkey ){
+ shellPreparePrintf(dbtmp, &rc, &pPkFinder,
+ "SELECT cid, name FROM pragma_table_info(%Q) "
+ " WHERE pk=1 AND type='integer' COLLATE nocase"
+ " AND NOT EXISTS (SELECT cid FROM pragma_table_info(%Q) WHERE pk=2)"
+ , zName, zName
+ );
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPkFinder) ){
+ pTab->iPk = sqlite3_column_int(pPkFinder, 0);
+ zPk = (const char*)sqlite3_column_text(pPkFinder, 1);
}
}
- cmd.bZip = 1;
- }else if( cmd.zFile ){
- int flags;
- if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
- if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
- || cmd.eCmd==AR_CMD_UPDATE ){
- flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
+
+ pTab->zQuoted = shellMPrintf(&rc, "%Q", zName);
+ pTab->azlCol = (char**)shellMalloc(&rc, sizeof(char*) * (nSqlCol+1));
+ pTab->nCol = nSqlCol;
+
+ if( bIntkey ){
+ pTab->azlCol[0] = shellMPrintf(&rc, "%Q", zPk);
}else{
- flags = SQLITE_OPEN_READONLY;
+ pTab->azlCol[0] = shellMPrintf(&rc, "");
}
- cmd.db = 0;
- if( cmd.bDryRun ){
- utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
- eDbType==SHELL_OPEN_APPENDVFS ? " using 'apndvfs'" : "");
+ i = 1;
+ shellPreparePrintf(dbtmp, &rc, &pStmt,
+ "SELECT %Q || group_concat(name, ', ') "
+ " FILTER (WHERE cid!=%d) OVER (ORDER BY %s cid) "
+ "FROM pragma_table_info(%Q)",
+ bIntkey ? ", " : "", pTab->iPk,
+ bIntkey ? "" : "(CASE WHEN pk=0 THEN 1000000 ELSE pk END), ",
+ zName
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zText = (const char*)sqlite3_column_text(pStmt, 0);
+ pTab->azlCol[i] = shellMPrintf(&rc, "%s%s", pTab->azlCol[0], zText);
+ i++;
}
- rc = sqlite3_open_v2(cmd.zFile, &cmd.db, flags,
- eDbType==SHELL_OPEN_APPENDVFS ? "apndvfs" : 0);
- if( rc!=SQLITE_OK ){
- utf8_printf(stderr, "cannot open file: %s (%s)\n",
- cmd.zFile, sqlite3_errmsg(cmd.db)
+ shellFinalize(&rc, pStmt);
+
+ shellFinalize(&rc, pPkFinder);
+ }
+ }
+
+ finished:
+ sqlite3_close(dbtmp);
+ *pRc = rc;
+ if( rc!=SQLITE_OK || (pTab && pTab->zQuoted==0) ){
+ recoverFreeTable(pTab);
+ pTab = 0;
+ }
+ return pTab;
+ }
+
+ /*
+ ** This function is called to search the schema recovered from the
+ ** sqlite_master table of the (possibly) corrupt database as part
+ ** of a ".recover" command. Specifically, for a table with root page
+ ** iRoot and at least nCol columns. Additionally, if bIntkey is 0, the
+ ** table must be a WITHOUT ROWID table, or if non-zero, not one of
+ ** those.
+ **
+ ** If a table is found, a (RecoverTable*) object is returned. Or, if
+ ** no such table is found, but bIntkey is false and iRoot is the
+ ** root page of an index in the recovered schema, then (*pbNoop) is
+ ** set to true and NULL returned. Or, if there is no such table or
+ ** index, NULL is returned and (*pbNoop) set to 0, indicating that
+ ** the caller should write data to the orphans table.
+ */
+ static RecoverTable *recoverFindTable(
+ ShellState *pState, /* Shell state object */
+ int *pRc, /* IN/OUT: Error code */
+ int iRoot, /* Root page of table */
+ int bIntkey, /* True for an intkey table */
+ int nCol, /* Number of columns in table */
+ int *pbNoop /* OUT: True if iRoot is root of index */
+ ){
+ sqlite3_stmt *pStmt = 0;
+ RecoverTable *pRet = 0;
+ int bNoop = 0;
+ const char *zSql = 0;
+ const char *zName = 0;
+
+ /* Search the recovered schema for an object with root page iRoot. */
+ shellPreparePrintf(pState->db, pRc, &pStmt,
+ "SELECT type, name, sql FROM recovery.schema WHERE rootpage=%d", iRoot
+ );
+ while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zType = (const char*)sqlite3_column_text(pStmt, 0);
+ if( bIntkey==0 && sqlite3_stricmp(zType, "index")==0 ){
+ bNoop = 1;
+ break;
+ }
+ if( sqlite3_stricmp(zType, "table")==0 ){
+ zName = (const char*)sqlite3_column_text(pStmt, 1);
+ zSql = (const char*)sqlite3_column_text(pStmt, 2);
+ pRet = recoverNewTable(pRc, zName, zSql, bIntkey, nCol);
+ break;
+ }
+ }
+
+ shellFinalize(pRc, pStmt);
+ *pbNoop = bNoop;
+ return pRet;
+ }
+
+ /*
+ ** Return a RecoverTable object representing the orphans table.
+ */
+ static RecoverTable *recoverOrphanTable(
+ ShellState *pState, /* Shell state object */
+ int *pRc, /* IN/OUT: Error code */
+ const char *zLostAndFound, /* Base name for orphans table */
+ int nCol /* Number of user data columns */
+ ){
+ RecoverTable *pTab = 0;
+ if( nCol>=0 && *pRc==SQLITE_OK ){
+ int i;
+
+ /* This block determines the name of the orphan table. The prefered
+ ** name is zLostAndFound. But if that clashes with another name
+ ** in the recovered schema, try zLostAndFound_0, zLostAndFound_1
+ ** and so on until a non-clashing name is found. */
+ int iTab = 0;
+ char *zTab = shellMPrintf(pRc, "%s", zLostAndFound);
+ sqlite3_stmt *pTest = 0;
+ shellPrepare(pState->db, pRc,
+ "SELECT 1 FROM recovery.schema WHERE name=?", &pTest
+ );
+ if( pTest ) sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
+ while( *pRc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pTest) ){
+ shellReset(pRc, pTest);
+ sqlite3_free(zTab);
+ zTab = shellMPrintf(pRc, "%s_%d", zLostAndFound, iTab++);
+ sqlite3_bind_text(pTest, 1, zTab, -1, SQLITE_TRANSIENT);
+ }
+ shellFinalize(pRc, pTest);
+
+ pTab = (RecoverTable*)shellMalloc(pRc, sizeof(RecoverTable));
+ if( pTab ){
+ pTab->zQuoted = shellMPrintf(pRc, "%Q", zTab);
+ pTab->nCol = nCol;
+ pTab->iPk = -2;
+ if( nCol>0 ){
+ pTab->azlCol = (char**)shellMalloc(pRc, sizeof(char*) * (nCol+1));
+ if( pTab->azlCol ){
+ pTab->azlCol[nCol] = shellMPrintf(pRc, "");
+ for(i=nCol-1; i>=0; i--){
+ pTab->azlCol[i] = shellMPrintf(pRc, "%s, NULL", pTab->azlCol[i+1]);
+ }
+ }
+ }
+
+ if( *pRc!=SQLITE_OK ){
+ recoverFreeTable(pTab);
+ pTab = 0;
+ }else{
+ raw_printf(pState->out,
+ "CREATE TABLE %s(rootpgno INTEGER, "
+ "pgno INTEGER, nfield INTEGER, id INTEGER", pTab->zQuoted
);
- goto end_ar_command;
+ for(i=0; i<nCol; i++){
+ raw_printf(pState->out, ", c%d", i);
+ }
+ raw_printf(pState->out, ");\n");
}
- sqlite3_fileio_init(cmd.db, 0, 0);
- sqlite3_sqlar_init(cmd.db, 0, 0);
- sqlite3_create_function(cmd.db, "shell_putsnl", 1, SQLITE_UTF8, cmd.p,
- shellPutsFunc, 0, 0);
+ }
+ sqlite3_free(zTab);
+ }
+ return pTab;
+ }
+
+ /*
+ ** This function is called to recover data from the database. A script
+ ** to construct a new database containing all recovered data is output
+ ** on stream pState->out.
+ */
+ static int recoverDatabaseCmd(ShellState *pState, int nArg, char **azArg){
+ int rc = SQLITE_OK;
+ sqlite3_stmt *pLoop = 0; /* Loop through all root pages */
+ sqlite3_stmt *pPages = 0; /* Loop through all pages in a group */
+ sqlite3_stmt *pCells = 0; /* Loop through all cells in a page */
+ const char *zRecoveryDb = ""; /* Name of "recovery" database */
+ const char *zLostAndFound = "lost_and_found";
+ int i;
+ int nOrphan = -1;
+ RecoverTable *pOrphan = 0;
+ int bFreelist = 1; /* 0 if --freelist-corrupt is specified */
+ for(i=1; i<nArg; i++){
+ char *z = azArg[i];
+ int n;
+ if( z[0]=='-' && z[1]=='-' ) z++;
+ n = strlen(z);
+ if( n<=17 && memcmp("-freelist-corrupt", z, n)==0 ){
+ bFreelist = 0;
+ }else
+ if( n<=12 && memcmp("-recovery-db", z, n)==0 && i<(nArg-1) ){
+ i++;
+ zRecoveryDb = azArg[i];
+ }else
+ if( n<=15 && memcmp("-lost-and-found", z, n)==0 && i<(nArg-1) ){
+ i++;
+ zLostAndFound = azArg[i];
}
- if( cmd.zSrcTable==0 && cmd.bZip==0 && cmd.eCmd!=AR_CMD_HELP ){
- if( cmd.eCmd!=AR_CMD_CREATE
- && sqlite3_table_column_metadata(cmd.db,0,"sqlar","name",0,0,0,0,0)
- ){
- utf8_printf(stderr, "database does not contain an 'sqlar' table\n");
- rc = SQLITE_ERROR;
- goto end_ar_command;
- }
- cmd.zSrcTable = sqlite3_mprintf("sqlar");
+ else{
+ raw_printf(stderr, "unexpected option: %s\n", azArg[i]);
+ raw_printf(stderr, "options are:\n");
+ raw_printf(stderr, " --freelist-corrupt\n");
+ raw_printf(stderr, " --recovery-db DATABASE\n");
+ raw_printf(stderr, " --lost-and-found TABLE-NAME\n");
+ return 1;
}
+ }
- switch( cmd.eCmd ){
- case AR_CMD_CREATE:
- rc = arCreateOrUpdateCommand(&cmd, 0, 0);
- break;
+ shellExecPrintf(pState->db, &rc,
+ /* Attach an in-memory database named 'recovery'. Create an indexed
+ ** cache of the sqlite_dbptr virtual table. */
+ "ATTACH %Q AS recovery;"
+ "DROP TABLE IF EXISTS recovery.dbptr;"
+ "DROP TABLE IF EXISTS recovery.freelist;"
+ "DROP TABLE IF EXISTS recovery.map;"
+ "DROP TABLE IF EXISTS recovery.schema;"
+ "CREATE TABLE recovery.freelist(pgno INTEGER PRIMARY KEY);", zRecoveryDb
+ );
- case AR_CMD_EXTRACT:
- rc = arExtractCommand(&cmd);
- break;
+ if( bFreelist ){
+ shellExec(pState->db, &rc,
+ "WITH trunk(pgno) AS ("
+ " SELECT shell_int32("
+ " (SELECT data FROM sqlite_dbpage WHERE pgno=1), 8) AS x "
+ " WHERE x>0"
+ " UNION"
+ " SELECT shell_int32("
+ " (SELECT data FROM sqlite_dbpage WHERE pgno=trunk.pgno), 0) AS x "
+ " FROM trunk WHERE x>0"
+ "),"
+ "freelist(data, n, freepgno) AS ("
+ " SELECT data, min(16384, shell_int32(data, 1)-1), t.pgno "
+ " FROM trunk t, sqlite_dbpage s WHERE s.pgno=t.pgno"
+ " UNION ALL"
+ " SELECT data, n-1, shell_int32(data, 2+n) "
+ " FROM freelist WHERE n>=0"
+ ")"
+ "REPLACE INTO recovery.freelist SELECT freepgno FROM freelist;"
+ );
+ }
- case AR_CMD_LIST:
- rc = arListCommand(&cmd);
- break;
+ shellExec(pState->db, &rc,
+ "CREATE TABLE recovery.dbptr("
+ " pgno, child, PRIMARY KEY(child, pgno)"
+ ") WITHOUT ROWID;"
+ "INSERT OR IGNORE INTO recovery.dbptr(pgno, child) "
+ " SELECT * FROM sqlite_dbptr"
+ " WHERE pgno NOT IN freelist AND child NOT IN freelist;"
- case AR_CMD_HELP:
- arUsage(pState->out);
- break;
+ /* Delete any pointer to page 1. This ensures that page 1 is considered
+ ** a root page, regardless of how corrupt the db is. */
+ "DELETE FROM recovery.dbptr WHERE child = 1;"
- case AR_CMD_INSERT:
- rc = arCreateOrUpdateCommand(&cmd, 1, 0);
- break;
+ /* Delete all pointers to any pages that have more than one pointer
+ ** to them. Such pages will be treated as root pages when recovering
+ ** data. */
+ "DELETE FROM recovery.dbptr WHERE child IN ("
+ " SELECT child FROM recovery.dbptr GROUP BY child HAVING count(*)>1"
+ ");"
- default:
- assert( cmd.eCmd==AR_CMD_UPDATE );
- rc = arCreateOrUpdateCommand(&cmd, 1, 1);
- break;
+ /* Create the "map" table that will (eventually) contain instructions
+ ** for dealing with each page in the db that contains one or more
+ ** records. */
+ "CREATE TABLE recovery.map("
+ "pgno INTEGER PRIMARY KEY, maxlen INT, intkey, root INT"
+ ");"
+
+ /* Populate table [map]. If there are circular loops of pages in the
+ ** database, the following adds all pages in such a loop to the map
+ ** as individual root pages. This could be handled better. */
+ "WITH pages(i, maxlen) AS ("
+ " SELECT page_count, ("
+ " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=page_count"
+ " ) FROM pragma_page_count WHERE page_count>0"
+ " UNION ALL"
+ " SELECT i-1, ("
+ " SELECT max(field+1) FROM sqlite_dbdata WHERE pgno=i-1"
+ " ) FROM pages WHERE i>=2"
+ ")"
+ "INSERT INTO recovery.map(pgno, maxlen, intkey, root) "
+ " SELECT i, maxlen, NULL, ("
+ " WITH p(orig, pgno, parent) AS ("
+ " SELECT 0, i, (SELECT pgno FROM recovery.dbptr WHERE child=i)"
+ " UNION "
+ " SELECT i, p.parent, "
+ " (SELECT pgno FROM recovery.dbptr WHERE child=p.parent) FROM p"
+ " )"
+ " SELECT pgno FROM p WHERE (parent IS NULL OR pgno = orig)"
+ ") "
+ "FROM pages WHERE maxlen > 0 AND i NOT IN freelist;"
+ "UPDATE recovery.map AS o SET intkey = ("
+ " SELECT substr(data, 1, 1)==X'0D' FROM sqlite_dbpage WHERE pgno=o.pgno"
+ ");"
+
+ /* Extract data from page 1 and any linked pages into table
+ ** recovery.schema. With the same schema as an sqlite_master table. */
+ "CREATE TABLE recovery.schema(type, name, tbl_name, rootpage, sql);"
+ "INSERT INTO recovery.schema SELECT "
+ " max(CASE WHEN field=0 THEN value ELSE NULL END),"
+ " max(CASE WHEN field=1 THEN value ELSE NULL END),"
+ " max(CASE WHEN field=2 THEN value ELSE NULL END),"
+ " max(CASE WHEN field=3 THEN value ELSE NULL END),"
+ " max(CASE WHEN field=4 THEN value ELSE NULL END)"
+ "FROM sqlite_dbdata WHERE pgno IN ("
+ " SELECT pgno FROM recovery.map WHERE root=1"
+ ")"
+ "GROUP BY pgno, cell;"
+ "CREATE INDEX recovery.schema_rootpage ON schema(rootpage);"
+ );
+
+ /* Open a transaction, then print out all non-virtual, non-"sqlite_%"
+ ** CREATE TABLE statements that extracted from the existing schema. */
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pStmt = 0;
+ raw_printf(pState->out, "BEGIN;\n");
+ raw_printf(pState->out, "PRAGMA writable_schema = on;\n");
+ shellPrepare(pState->db, &rc,
+ "SELECT sql FROM recovery.schema "
+ "WHERE type='table' AND sql LIKE 'create table%'", &pStmt
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zCreateTable = (const char*)sqlite3_column_text(pStmt, 0);
+ raw_printf(pState->out, "CREATE TABLE IF NOT EXISTS %s;\n",
+ &zCreateTable[12]
+ );
}
+ shellFinalize(&rc, pStmt);
}
- end_ar_command:
- if( cmd.db!=pState->db ){
- close_db(cmd.db);
+
+ /* Figure out if an orphan table will be required. And if so, how many
+ ** user columns it should contain */
+ shellPrepare(pState->db, &rc,
+ "SELECT coalesce(max(maxlen), -2) FROM recovery.map WHERE root>1"
+ , &pLoop
+ );
+ if( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){
+ nOrphan = sqlite3_column_int(pLoop, 0);
+ }
+ shellFinalize(&rc, pLoop);
+ pLoop = 0;
+
+ shellPrepare(pState->db, &rc,
+ "SELECT pgno FROM recovery.map WHERE root=?", &pPages
+ );
+ shellPrepare(pState->db, &rc,
+ "SELECT max(field), group_concat(shell_escape_crnl(quote(value)), ', ')"
+ "FROM sqlite_dbdata WHERE pgno = ? AND field != ?"
+ "GROUP BY cell", &pCells
+ );
+
+ /* Loop through each root page. */
+ shellPrepare(pState->db, &rc,
+ "SELECT root, intkey, max(maxlen) FROM recovery.map"
+ " WHERE root>1 GROUP BY root, intkey ORDER BY root=("
+ " SELECT rootpage FROM recovery.schema WHERE name='sqlite_sequence'"
+ ")", &pLoop
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pLoop) ){
+ int iRoot = sqlite3_column_int(pLoop, 0);
+ int bIntkey = sqlite3_column_int(pLoop, 1);
+ int nCol = sqlite3_column_int(pLoop, 2);
+ int bNoop = 0;
+ RecoverTable *pTab;
+
+ pTab = recoverFindTable(pState, &rc, iRoot, bIntkey, nCol, &bNoop);
+ if( bNoop || rc ) continue;
+ if( pTab==0 ){
+ if( pOrphan==0 ){
+ pOrphan = recoverOrphanTable(pState, &rc, zLostAndFound, nOrphan);
+ }
+ pTab = pOrphan;
+ if( pTab==0 ) break;
+ }
+
+ if( 0==sqlite3_stricmp(pTab->zQuoted, "'sqlite_sequence'") ){
+ raw_printf(pState->out, "DELETE FROM sqlite_sequence;\n");
+ }
+ sqlite3_bind_int(pPages, 1, iRoot);
+ sqlite3_bind_int(pCells, 2, pTab->iPk);
+
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pPages) ){
+ int iPgno = sqlite3_column_int(pPages, 0);
+ sqlite3_bind_int(pCells, 1, iPgno);
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pCells) ){
+ int nField = sqlite3_column_int(pCells, 0);
+ const char *zVal = (const char*)sqlite3_column_text(pCells, 1);
+
+ nField = nField+1;
+ if( pTab==pOrphan ){
+ raw_printf(pState->out,
+ "INSERT INTO %s VALUES(%d, %d, %d, %s%s%s);\n",
+ pTab->zQuoted, iRoot, iPgno, nField,
+ bIntkey ? "" : "NULL, ", zVal, pTab->azlCol[nField]
+ );
+ }else{
+ raw_printf(pState->out, "INSERT INTO %s(%s) VALUES( %s );\n",
+ pTab->zQuoted, pTab->azlCol[nField], zVal
+ );
+ }
+ }
+ shellReset(&rc, pCells);
+ }
+ shellReset(&rc, pPages);
+ if( pTab!=pOrphan ) recoverFreeTable(pTab);
+ }
+ shellFinalize(&rc, pLoop);
+ shellFinalize(&rc, pPages);
+ shellFinalize(&rc, pCells);
+ recoverFreeTable(pOrphan);
+
+ /* The rest of the schema */
+ if( rc==SQLITE_OK ){
+ sqlite3_stmt *pStmt = 0;
+ shellPrepare(pState->db, &rc,
+ "SELECT sql, name FROM recovery.schema "
+ "WHERE sql NOT LIKE 'create table%'", &pStmt
+ );
+ while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pStmt) ){
+ const char *zSql = (const char*)sqlite3_column_text(pStmt, 0);
+ if( sqlite3_strnicmp(zSql, "create virt", 11)==0 ){
+ const char *zName = (const char*)sqlite3_column_text(pStmt, 1);
+ char *zPrint = shellMPrintf(&rc,
+ "INSERT INTO sqlite_master VALUES('table', %Q, %Q, 0, %Q)",
+ zName, zName, zSql
+ );
+ raw_printf(pState->out, "%s;\n", zPrint);
+ sqlite3_free(zPrint);
+ }else{
+ raw_printf(pState->out, "%s;\n", zSql);
+ }
+ }
+ shellFinalize(&rc, pStmt);
}
- sqlite3_free(cmd.zSrcTable);
+ if( rc==SQLITE_OK ){
+ raw_printf(pState->out, "PRAGMA writable_schema = off;\n");
+ raw_printf(pState->out, "COMMIT;\n");
+ }
+ sqlite3_exec(pState->db, "DETACH recovery", 0, 0, 0);
return rc;
}
- /* End of the ".archive" or ".ar" command logic
- **********************************************************************************/
- #endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB) */
+ #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
/*