From: drh Date: Tue, 14 May 2019 22:25:26 +0000 (+0000) Subject: Merge all the latest trunk enhancements into the reuse-schema branch. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=28c156d6d626db6c48251c12d906308d3a0ab0b0;p=thirdparty%2Fsqlite.git Merge all the latest trunk enhancements into the reuse-schema branch. FossilOrigin-Name: 018f3199b9ba043fbbf88ec46b4fa16728f6507e000f4813ed346aaf6665add6 --- 28c156d6d626db6c48251c12d906308d3a0ab0b0 diff --cc manifest index 42abd6db63,5cd70b117f..18384295c0 --- a/manifest +++ b/manifest @@@ -1,13 -1,13 +1,13 @@@ - 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 @@@ -441,7 -443,7 +444,7 @@@ F ext/userauth/userauth.c f81aa5a3ecacf 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 @@@ -453,37 -455,37 +456,37 @@@ F spec.template 86a4a43b99ebb3e75e6b9a7 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 @@@ -512,24 -514,24 +515,24 @@@ F src/parse.y 22f64d8a8910acd1758045051 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 @@@ -586,31 -587,31 +589,31 @@@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f 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 @@@ -1759,8 -1758,8 +1766,8 @@@ F tool/mkmsvcmin.tcl cad0c7b54d7dd92bc8 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 @@@ -1826,7 -1825,7 +1833,7 @@@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a9 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 diff --cc manifest.uuid index 9d0192aec4,a58fb53a14..61c703c36b --- a/manifest.uuid +++ b/manifest.uuid @@@ -1,1 -1,1 +1,1 @@@ - 5c6c9e7f6f83d565b317e3442e8bc664478adb407fe416d068fe0dcd17ffa5b5 -bc7d2c1656396bb4f5f1f814e60dbf816cc91c5a521b54ad593cd3da0fe8dcb4 ++018f3199b9ba043fbbf88ec46b4fa16728f6507e000f4813ed346aaf6665add6 diff --cc src/attach.c index 51e51c107d,55e0eb5363..7a39674081 --- a/src/attach.c +++ b/src/attach.c @@@ -231,17 -231,17 +231,17 @@@ static void attachFunc ** way we found it. */ if( rc==SQLITE_OK ){ - sqlite3BtreeEnterAll(db); db->init.iDb = 0; db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); - if( !REOPEN_AS_MEMDB(db) ){ + if( !IsSharedSchema(db) && !REOPEN_AS_MEMDB(db) ){ + sqlite3BtreeEnterAll(db); rc = sqlite3Init(db, &zErrDyn); + sqlite3BtreeLeaveAll(db); + assert( zErrDyn==0 || rc!=SQLITE_OK ); } - sqlite3BtreeLeaveAll(db); - assert( zErrDyn==0 || rc!=SQLITE_OK ); } #ifdef SQLITE_USER_AUTHENTICATION - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){ u8 newAuth = 0; rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth); if( newAuthauth.authLevel ){ diff --cc src/shell.c.in index 4051a2fd27,f9f82b01ca..07e21d829e --- a/src/shell.c.in +++ b/src/shell.c.in @@@ -1071,14 -1075,13 +1075,14 @@@ struct ShellState /* 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 */ @@@ -6163,130 -6060,759 +6066,990 @@@ static int arCreateOrUpdateCommand 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 && iout, "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 || nSqlColiPk 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; iout, ", 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; idb, &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) */ /*