diff --git a/ext/rbu/rbuvacuum.test b/ext/rbu/rbuvacuum.test index 86f4aa770e..dba0ef37e0 100644 --- a/ext/rbu/rbuvacuum.test +++ b/ext/rbu/rbuvacuum.test @@ -298,6 +298,14 @@ do_test 2.1.2 { list [catch { rbu close } msg] $msg } {1 {SQLITE_ERROR - cannot vacuum wal mode database}} +do_test 2.1.3 { + sqlite3rbu_vacuum rbu test.db state.db + rbu step +} {SQLITE_ERROR} +do_test 2.1.4 { + list [catch { rbu close_no_error } msg] $msg +} {1 SQLITE_ERROR} + reset_db do_execsql_test 2.2.0 { CREATE TABLE tx(a PRIMARY KEY, b BLOB); diff --git a/ext/rbu/sqlite3rbu.c b/ext/rbu/sqlite3rbu.c index 7dd232b4b5..e0f413f021 100644 --- a/ext/rbu/sqlite3rbu.c +++ b/ext/rbu/sqlite3rbu.c @@ -3787,7 +3787,11 @@ int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){ rbuEditErrmsg(p); rc = p->rc; - *pzErrmsg = p->zErrmsg; + if( pzErrmsg ){ + *pzErrmsg = p->zErrmsg; + }else{ + sqlite3_free(p->zErrmsg); + } sqlite3_free(p->zState); sqlite3_free(p); }else{ diff --git a/ext/rbu/sqlite3rbu.h b/ext/rbu/sqlite3rbu.h index 12112c14e1..e55fe4685d 100644 --- a/ext/rbu/sqlite3rbu.h +++ b/ext/rbu/sqlite3rbu.h @@ -420,10 +420,10 @@ int sqlite3rbu_savestate(sqlite3rbu *pRbu); ** ** If an error has already occurred as part of an sqlite3rbu_step() ** or sqlite3rbu_open() call, or if one occurs within this function, an -** SQLite error code is returned. Additionally, *pzErrmsg may be set to -** point to a buffer containing a utf-8 formatted English language error -** message. It is the responsibility of the caller to eventually free any -** such buffer using sqlite3_free(). +** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, +** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted +** English language error message. It is the responsibility of the caller to +** eventually free any such buffer using sqlite3_free(). ** ** Otherwise, if no error occurs, this function returns SQLITE_OK if the ** update has been partially applied, or SQLITE_DONE if it has been diff --git a/ext/rbu/test_rbu.c b/ext/rbu/test_rbu.c index b1a2252741..fba90dcdc4 100644 --- a/ext/rbu/test_rbu.c +++ b/ext/rbu/test_rbu.c @@ -78,6 +78,7 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd( {"db", 3, "RBU"}, /* 6 */ {"state", 2, ""}, /* 7 */ {"progress", 2, ""}, /* 8 */ + {"close_no_error", 2, ""}, /* 9 */ {0,0,0} }; int iCmd; @@ -102,11 +103,16 @@ static int SQLITE_TCLAPI test_sqlite3rbu_cmd( break; } + case 9: /* close_no_error */ case 1: /* close */ { char *zErrmsg = 0; int rc; Tcl_DeleteCommand(interp, Tcl_GetString(objv[0])); - rc = sqlite3rbu_close(pRbu, &zErrmsg); + if( iCmd==1 ){ + rc = sqlite3rbu_close(pRbu, &zErrmsg); + }else{ + rc = sqlite3rbu_close(pRbu, 0); + } if( rc==SQLITE_OK || rc==SQLITE_DONE ){ Tcl_SetObjResult(interp, Tcl_NewStringObj(sqlite3ErrName(rc), -1)); assert( zErrmsg==0 ); diff --git a/ext/session/sessiondiff.test b/ext/session/sessiondiff.test index f2a8a2c724..b00af0eb3b 100644 --- a/ext/session/sessiondiff.test +++ b/ext/session/sessiondiff.test @@ -16,6 +16,7 @@ if {![info exists testdir]} { set testdir [file join [file dirname [info script]] .. .. test] } source $testdir/tester.tcl +ifcapable !session {finish_test; return} set testprefix sessiondiff set PROG [test_find_sqldiff] @@ -111,4 +112,3 @@ do_changeset_test 1.2 { } finish_test - diff --git a/manifest b/manifest index 7926ed9749..7ce1a70118 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Fix\sanother\slsmtest\sbuild\sproblem.\sAdd\sthe\s"-trans\sBOOLEAN"\soption\sto\s"lsmtest\nspeed2". -D 2017-06-07T19:46:18.561 +C Merge\sthe\slatest\schanges\sfrom\strunk. +D 2017-06-15T15:06:26.353 F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434 F Makefile.msc 8eeb80162074004e906b53d7340a12a14c471a83743aab975947e95ce061efcc @@ -308,11 +308,11 @@ F ext/rbu/rbufts.test 828cd689da825f0a7b7c53ffc1f6f7fdb6fa5bda F ext/rbu/rbuprogress.test 1849d4e0e50616edf5ce75ce7db86622e656b5cf F ext/rbu/rburesume.test 8acb77f4a422ff55acfcfc9cc15a5cb210b1de83 F ext/rbu/rbusave.test 0f43b6686084f426ddd040b878426452fd2c2f48 -F ext/rbu/rbuvacuum.test 4a977447c15c2581ab668781d9ef4294382530e0 +F ext/rbu/rbuvacuum.test ff357e9b556ca7ad4673da0ff7f244def919ff858e0f9f350d3e30fdd83a62a8 F ext/rbu/rbuvacuum2.test 2074ab14fe66e1c7e7210c62562650dcd215bbaa -F ext/rbu/sqlite3rbu.c 2a89efba9eeba8e6c89a498dc195e8efbdde2694 -F ext/rbu/sqlite3rbu.h 6fb6294c34a9ca93b5894a33bca530c6f08decba -F ext/rbu/test_rbu.c 5aa22616afac6f71ebd3d9bc9bf1006cfabcca88 +F ext/rbu/sqlite3rbu.c d1438580a451eebda3bfd42ef69b677512f00125285e0e4e789b6131a45f6dd8 +F ext/rbu/sqlite3rbu.h fc25e1fcd99b5c6d32b1b5b1c73122632e873ac89bd0be9bf646db362b7ce02c +F ext/rbu/test_rbu.c ec18cfc69a104309df23c359e3c80306c9a6bdd1d2c53c8b70ae158e9832dcd6 F ext/rtree/README 6315c0d73ebf0ec40dedb5aa0e942bc8b54e3761 F ext/rtree/rtree.c 8205d6e4466f766e57ce1b8aa38224ac9e1cec2d2bf4684cd1cc5a6ddf9b7014 F ext/rtree/rtree.h 834dbcb82dc85b2481cde6a07cdadfddc99e9b9e @@ -356,7 +356,7 @@ F ext/session/sessionG.test 01ef705096a9d3984eebdcca79807a211dee1b60 F ext/session/session_common.tcl 7776eda579773113b30c7abfd4545c445228cb73 F ext/session/session_speed_test.c edc1f96fd5e0e4b16eb03e2a73041013d59e8723 F ext/session/sessionat.test b25d61d663ebc795506bf74079dc4ba0092fad25 -F ext/session/sessiondiff.test 7889d8e84cd130fe3712ed7c511f883e0b2a398ed2905d54e48a24edce49bfab +F ext/session/sessiondiff.test ad13dd65664bae26744e1f18eb3cbd5588349b7e9118851d8f9364248d67bcec F ext/session/sessionfault.test da273f2712b6411e85e71465a1733b8501dbf6f7 F ext/session/sessionfault2.test 04aa0bc9aa70ea43d8de82c4f648db4de1e990b0 F ext/session/sessionwor.test 2f3744236dc8b170a695b7d8ddc8c743c7e79fdc @@ -388,10 +388,10 @@ F src/auth.c 79f96c6f33bf0e5da8d1c282cee5ebb1852bb8a6ccca3e485d7c459b035d9c3c F src/backup.c faf17e60b43233c214aae6a8179d24503a61e83b F src/bitvec.c 17ea48eff8ba979f1f5b04cc484c7bb2be632f33 F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca -F src/btree.c 8ac6ae352c63998228718b5f11faa0da2676710898a47284de78e96cb41dca35 +F src/btree.c 430e34151e6ef37e42d3f956bb062907c80ff91e1380704b967b8c1a02a98f64 F src/btree.h 3edc5329bc59534d2d15b4f069a9f54b779a7e51289e98fa481ae3c0e526a5ca F src/btreeInt.h a392d353104b4add58b4a59cb185f5d5693dde832c565b77d8d4c343ed98f610 -F src/build.c 4026a9c554b233e50c5e9ad46963e676cf54dd2306d952aa1eaa07a1bc9ce14f +F src/build.c 88a8cdc11d1c081ed565aa3e795bdf9160f4556463b4c4555e9860b59dd80340 F src/callback.c 2e76147783386374bf01b227f752c81ec872d730 F src/complete.c a3634ab1e687055cd002e11b8f43eb75c17da23e F src/ctime.c 47d91a25ad8f199a71a5b1b7b169d6dd0d6e98c5719eca801568798743d1161c @@ -407,10 +407,10 @@ F src/hash.c 63d0ee752a3b92d4695b2b1f5259c4621b2cfebd F src/hash.h ab34c5c54a9e9de2e790b24349ba5aab3dbb4fd4 F src/hwtime.h 747c1bbe9df21a92e9c50f3bbec1de841dc5e5da F src/in-operator.md 10cd8f4bcd225a32518407c2fb2484089112fd71 -F src/insert.c ea52232babcee65f1f9833d0fefc3b598662a004c19a178ef4fc0981e2f02a58 +F src/insert.c 974499a3999d339a4c1ba8ef129a988d9f136b3789e423808b38cdc19d28adbe F src/legacy.c e88ed13c2d531decde75d42c2e35623fb9ce3cb0 F src/loadext.c a72909474dadce771d3669bf84bf689424f6f87d471fee898589c3ef9b2acfd9 -F src/main.c 1054e4dbb4fcca84246ed48b35164a996f5a52daa56c275157a5d583c0e2cd00 +F src/main.c 65eb6093de90ab4372f2ae0d0cd0acfe718ea26ef0145435e0d9eecb854c9d7e F src/malloc.c e20bb2b48abec52d3faf01cce12e8b4f95973755fafec98d45162dfdab111978 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645 F src/mem1.c c12a42539b1ba105e3707d0e628ad70e611040d8f5e38cf942cee30c867083de @@ -445,12 +445,12 @@ F src/printf.c 8757834f1b54dae512fb25eb1acc8e94a0d15dd2290b58f2563f65973265adb2 F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384 F src/resolve.c adf3ef9843135b1383321ad751f16f5a40c3f37925154555a3e61653d2a954e8 F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac -F src/select.c d93205e43af302d9eb147fddecc956509ee9d0dde6297ee3f93c591f60f0e295 -F src/shell.c 3f761fe604174b31aacd2ea2eacef5e6fe550111d60c0d71532cc008c68cf3f3 -F src/sqlite.h.in ad7f4101e3613b1134d1ad6c61ff385424ffac0d542627fd31f26667fdd91c94 +F src/select.c 0d2afdbdba5fbc61432c5454a35e0236e7aa4aa3756986a7d51b81a508e8083a +F src/shell.c c45ae9a95faf5a495280bfa2d6b7956c707f347e56eb8b1985c852d152e46894 +F src/sqlite.h.in 67fa8bd29808e7988e0ce36c8d4c6043eb1727f94522fc612687aa5af51931e6 F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8 F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28 -F src/sqliteInt.h 82800fc19fbdeb35a0773c5d727da717652f4c421d191d2460219c7aab953462 +F src/sqliteInt.h f90955604b4d8e5a1f672bd230978640be89e0bbbf833d352cf4945ce3a799df F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b F src/status.c a9e66593dfb28a9e746cba7153f84d49c1ddc4b1 F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34 @@ -474,7 +474,7 @@ F src/test_config.c edcba290248dc18736dd814c9b95863c6762e0b35753048d8cbe5bf65f7a F src/test_delete.c e2fe07646dff6300b48d49b2fee2fe192ed389e834dd635e3b3bac0ce0bf9f8f F src/test_demovfs.c a0c3bdd45ed044115c2c9f7779e56eafff18741e F src/test_devsym.c 4e58dec2602d8e139ca08659f62a62450587cb58 -F src/test_fs.c e16cbe68d3b107e00a907c20a9a02629870eb69b +F src/test_fs.c 35a2f7dd8a915900873386331386d9ba1ae1b5026d74fd20c2807bc76221f291 F src/test_func.c a4fdab3363b436c1b12660e9362ce3f3782b7b5e F src/test_hexio.c 1d4469ca61ab202a1fcec6543f584d2407205e8d F src/test_init.c 4413c211a94b62157ca4c145b3f27c497f03c664 @@ -507,7 +507,7 @@ F src/test_wsd.c 41cadfd9d97fe8e3e4e44f61a4a8ccd6f7ca8fe9 F src/threads.c 4ae07fa022a3dc7c5beb373cf744a85d3c5c6c3c F src/tokenize.c 1003d6d90c6783206c711f0a9397656fa5b055209f4d092caa43bb3bf5215db5 F src/treeview.c 6cf8d7fe9e63fae57dad1bb57f6615e14eac0c527e43d868e805042cae8ed1f7 -F src/trigger.c c9f0810043b265724fdb1bdd466894f984dfc182 +F src/trigger.c d1cae560bfacc8bfb3a072d73658245c1714c0389097da69b4cb23877a082d7e F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23 @@ -518,7 +518,7 @@ F src/vdbeInt.h cdcdabad4f5d6bf7a3beb826a7f33ee6f8f1cb220042bedd5b7d4bf2ea1d179f F src/vdbeapi.c c961d8d9e0f52e2df60a6ddbbccd7d99dc4d00103db7e53f77fcef44fbd23178 F src/vdbeaux.c bc9b3228f6d99bef0d0ecaf3a0e0e8358b3873242d0d2fe944226de3fdf9521e F src/vdbeblob.c 359891617358deefc85bef7bcf787fa6b77facb9 -F src/vdbemem.c 2c70f8f5de6c71fb99a22c5b83be9fab5c47cdd8e279fa44a8c00cfed06d7e89 +F src/vdbemem.c 94b17d851f31d4fd075d47d373d4b33ed3962a6ecb82cf385018025751091360 F src/vdbesort.c f512c68d0bf7e0105316a5594c4329358c8ee9cae3b25138df041d97516c0372 F src/vdbetrace.c 41963d5376f0349842b5fc4aaaaacd7d9cdc0834 F src/vtab.c 35b9bdc2b41de32a417141d12097bcc4e29a77ed7cdb8f836d1d2305d946b61b @@ -526,7 +526,7 @@ F src/vxworks.h d2988f4e5a61a4dfe82c6524dd3d6e4f2ce3cdb9 F src/wal.c 40c543f0a2195d1b0dc88ef12142bea690009344 F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71 F src/walker.c d46044e7a5842560dfe7122d93ff5145dd4a96f4d0bf5ba5910a7731b8c01e79 -F src/where.c 67f98714b07ec3c1d5e033a63d23c0fd70c24861b7b46b69b10700f22dca6ffe +F src/where.c aa213e1b1c29eb8946a9f25108a18666a745ae5bac41b58d0be98730937a7785 F src/whereInt.h 2a4b634d63ce488b46d4b0da8f2eaa8f9aeab202bc25ef76f007de5e3fba1f20 F src/wherecode.c 339ee802d9d311acf0cba8b5a9a092e167ef71c3a777d4b3e57de25d193251c7 F src/whereexpr.c a2fe3811d45af45a5c6667caabc15e01054fe6228c64e86e1f7d2ba5ef5284f9 @@ -579,7 +579,7 @@ F test/autoindex2.test 12ef578928102baaa0dc23ad397601a2f4ecb0df F test/autoindex3.test a3be0d1a53a7d2edff208a5e442312957047e972 F test/autoindex4.test 49d3cd791a9baa16fb461d7ea3de80d019a819cf F test/autoindex5.test 96f084a5e6024ea07cace5888df3223f3ea86990 -F test/autovacuum.test 92c24eedbdb68e49f3fb71f26f9ce6d8988cac15 +F test/autovacuum.test 0831cd34e14695d297187f7f6519265e3121c5b0a1720e548e86829e796129e9 F test/autovacuum_ioerr2.test 8a367b224183ad801e0e24dcb7d1501f45f244b4 F test/avtrans.test 0252654f4295ddda3b2cce0e894812259e655a85 F test/backcompat.test 3e64cedda754c778ef6bbe417b6e7a295e662a4d @@ -965,7 +965,7 @@ F test/json102.test eeb54efa221e50b74a2d6fb9259963b48d7414dca3ce2fdfdeed45cb2848 F test/json103.test c5f6b85e69de05f6b3195f9f9d5ce9cd179099a0 F test/json104.test 877d5845f6303899b7889ea5dd1bea99076e3100574d5c536082245c5805dcaa F test/keyword1.test 37ef6bba5d2ed5b07ecdd6810571de2956599dff -F test/kvtest.c bf2e7ae3de92d98147e8cd49fc550ee3382599aa20b7e2b60038b409a5b2ef34 +F test/kvtest.c d2b8cfc91047ebf6cac4f3a04f19c3a864e4ecfd683bbb65c395df450b8dc79c F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63 F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200 F test/like.test 0603f4fa0dad50987f70032c05800cbfa8985302 @@ -1456,7 +1456,7 @@ F test/vtabC.test 4528f459a13136f982e75614d120aef165f17292 F test/vtabD.test 05b3f1d77117271671089e48719524b676842e96 F test/vtabE.test d5024aa42754962f6bb0afd261681686488e7afe F test/vtabF.test 1918844c7c902f6a16c8dacf1ec8f84886d6e78b -F test/vtabH.test 5f9253eb9e41ba9fe94f4aa3e9230191893d7764 +F test/vtabH.test 26d54e8b5407f797638b787a55f9c88323850a58dd142de02d06b9a1159bd283 F test/vtabI.test 751b07636700dbdea328e4265b6077ccd6811a3f F test/vtab_alter.test 9e374885248f69e251bdaacf480b04a197f125e5 F test/vtab_err.test 0d4d8eb4def1d053ac7c5050df3024fd47a3fbd8 @@ -1581,7 +1581,7 @@ F tool/showlocks.c 9920bcc64f58378ff1118caead34147201f48c68 F tool/showstat4.c b14159aa062f661b394ba37b6b7b94bfb8012ab9 F tool/showwal.c ad9d768f96ca6199ad3a8c9562d679680bd032dd01204ea3e5ea6fb931d81847 F tool/soak1.tcl 8d407956e1a45b485a8e072470a3e629a27037fe -F tool/spaceanal.tcl ab7d9bf68062907282a64b3e12ccbfad47193c5a +F tool/spaceanal.tcl a91e09e9e3dd10e67ffe4a01155161fb5e6b6d399002716593780493452c4b28 F tool/speed-check.sh 9630ba0468b609c52f48309243d4eb6e9c34deda F tool/speedtest.tcl 06c76698485ccf597b9e7dbb1ac70706eb873355 F tool/speedtest16.c ecb6542862151c3e6509bbc00509b234562ae81e @@ -1622,7 +1622,7 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0 -P 501238b9fbb4eece084b4bfce84a5f37a986f34db8081df517e60ef81f357454 -R 2f17872e101c8015a88bc34e46b72bd4 -U dan -Z f9a3b900d1113fa410083f6360886457 +P 61853bc171e7c3af1db6a33ac8b1ad21e1c08e8d6b317fe061fdcd89c9a42e88 9afd7a2ffd3a39456190ad05e85ff6485298aae262d9e0698a58c1d73507a36f +R 86dc51b3b4b5ca3aa4d0c441dc00d984 +U drh +Z 8f32c68f28804c0ec498baf7082045dc diff --git a/manifest.uuid b/manifest.uuid index 9d32958b14..28893f9498 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -61853bc171e7c3af1db6a33ac8b1ad21e1c08e8d6b317fe061fdcd89c9a42e88 \ No newline at end of file +60105c2253d0b617037e975b0d0b729bcb59b950aa2c33d8891394ad45aabb69 \ No newline at end of file diff --git a/src/btree.c b/src/btree.c index eeb076c4d8..1f06e45031 100644 --- a/src/btree.c +++ b/src/btree.c @@ -771,7 +771,7 @@ static int btreeMoveto( if( pIdxKey==0 ) return SQLITE_NOMEM_BKPT; sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, pIdxKey); if( pIdxKey->nField==0 ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pCur->apPage[pCur->iPage]->pgno); goto moveto_done; } }else{ @@ -1000,7 +1000,7 @@ static int ptrmapGet(BtShared *pBt, Pgno key, u8 *pEType, Pgno *pPgno){ if( pPgno ) *pPgno = get4byte(&pPtrmap[offset+1]); sqlite3PagerUnref(pDbPage); - if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_BKPT; + if( *pEType<1 || *pEType>5 ) return SQLITE_CORRUPT_PGNO(iPtrmap); return SQLITE_OK; } @@ -1385,7 +1385,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ int sz = get2byte(&data[iFree+2]); int top = get2byte(&data[hdr+5]); if( iFree2 ){ - if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_BKPT; + if( iFree+sz>iFree2 ) return SQLITE_CORRUPT_PGNO(pPage->pgno); sz2 = get2byte(&data[iFree2+2]); assert( iFree+sz+sz2+iFree2-(iFree+sz) <= usableSize ); memmove(&data[iFree+sz+sz2], &data[iFree+sz], iFree2-(iFree+sz)); @@ -1416,13 +1416,13 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ ** if PRAGMA cell_size_check=ON. */ if( pciCellLast ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( pc>=iCellFirst && pc<=iCellLast ); size = pPage->xCellSize(pPage, &src[pc]); cbrk -= size; if( cbrkusableSize ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); testcase( cbrk+size==usableSize ); @@ -1442,7 +1442,7 @@ static int defragmentPage(MemPage *pPage, int nMaxFrag){ defragment_out: if( data[hdr+7]+cbrk-iCellFirst!=pPage->nFree ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); @@ -1481,7 +1481,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ /* EVIDENCE-OF: R-06866-39125 Freeblocks are always connected in order of ** increasing offset. */ if( pc>usableSize-4 || pcpgno); return 0; } /* EVIDENCE-OF: R-22710-53328 The third and fourth bytes of each @@ -1492,7 +1492,7 @@ static u8 *pageFindSlot(MemPage *pPg, int nByte, int *pRc){ testcase( x==4 ); testcase( x==3 ); if( pc < pPg->cellOffset+2*pPg->nCell || size+pc > usableSize ){ - *pRc = SQLITE_CORRUPT_BKPT; + *pRc = SQLITE_CORRUPT_PGNO(pPg->pgno); return 0; }else if( x<4 ){ /* EVIDENCE-OF: R-11498-58022 In a well-formed b-tree page, the total @@ -1559,7 +1559,7 @@ static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ if( top==0 && pPage->pBt->usableSize==65536 ){ top = 65536; }else{ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } } @@ -1655,11 +1655,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ while( (iFreeBlk = get2byte(&data[iPtr]))pgno); } iPtr = iFreeBlk; } - if( iFreeBlk>iLast ) return SQLITE_CORRUPT_BKPT; + if( iFreeBlk>iLast ) return SQLITE_CORRUPT_PGNO(pPage->pgno); assert( iFreeBlk>iPtr || iFreeBlk==0 ); /* At this point: @@ -1670,9 +1670,11 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ */ if( iFreeBlk && iEnd+3>=iFreeBlk ){ nFrag = iFreeBlk - iEnd; - if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_BKPT; + if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PGNO(pPage->pgno); iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); - if( iEnd > pPage->pBt->usableSize ) return SQLITE_CORRUPT_BKPT; + if( iEnd > pPage->pBt->usableSize ){ + return SQLITE_CORRUPT_PGNO(pPage->pgno); + } iSize = iEnd - iStart; iFreeBlk = get2byte(&data[iFreeBlk]); } @@ -1684,20 +1686,20 @@ static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ if( iPtr>hdr+1 ){ int iPtrEnd = iPtr + get2byte(&data[iPtr+2]); if( iPtrEnd+3>=iStart ){ - if( iPtrEnd>iStart ) return SQLITE_CORRUPT_BKPT; + if( iPtrEnd>iStart ) return SQLITE_CORRUPT_PGNO(pPage->pgno); nFrag += iStart - iPtrEnd; iSize = iEnd - iPtr; iStart = iPtr; } } - if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_BKPT; + if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PGNO(pPage->pgno); data[hdr+7] -= nFrag; } if( iStart==get2byte(&data[hdr+5]) ){ /* The new freeblock is at the beginning of the cell content area, ** so just extend the cell content area rather than create another ** freelist entry */ - if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_BKPT; + if( iPtr!=hdr+1 ) return SQLITE_CORRUPT_PGNO(pPage->pgno); put2byte(&data[hdr+1], iFreeBlk); put2byte(&data[hdr+5], iEnd); }else{ @@ -1765,7 +1767,7 @@ static int decodeFlags(MemPage *pPage, int flagByte){ }else{ /* EVIDENCE-OF: R-47608-56469 Any other value for the b-tree page type is ** an error. */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } pPage->max1bytePayload = pBt->max1bytePayload; return SQLITE_OK; @@ -1805,7 +1807,9 @@ static int btreeInitPage(MemPage *pPage){ data = pPage->aData; /* EVIDENCE-OF: R-28594-02890 The one-byte flag at offset 0 indicating ** the b-tree page type. */ - if( decodeFlags(pPage, data[hdr]) ) return SQLITE_CORRUPT_BKPT; + if( decodeFlags(pPage, data[hdr]) ){ + return SQLITE_CORRUPT_PGNO(pPage->pgno); + } assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); pPage->maskPage = (u16)(pBt->pageSize - 1); pPage->nOverflow = 0; @@ -1823,7 +1827,7 @@ static int btreeInitPage(MemPage *pPage){ pPage->nCell = get2byte(&data[hdr+3]); if( pPage->nCell>MX_CELL(pBt) ){ /* To many cells for a single page. The page must be corrupt */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } testcase( pPage->nCell==MX_CELL(pBt) ); /* EVIDENCE-OF: R-24089-57979 If a page contains no cells (which is only @@ -1851,12 +1855,12 @@ static int btreeInitPage(MemPage *pPage){ testcase( pc==iCellFirst ); testcase( pc==iCellLast ); if( pciCellLast ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } sz = pPage->xCellSize(pPage, &data[pc]); testcase( pc+sz==usableSize ); if( pc+sz>usableSize ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } } if( !pPage->leaf ) iCellLast++; @@ -1874,11 +1878,12 @@ static int btreeInitPage(MemPage *pPage){ /* EVIDENCE-OF: R-55530-52930 In a well-formed b-tree page, there will ** always be at least one cell before the first freeblock. */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } while( 1 ){ if( pc>iCellLast ){ - return SQLITE_CORRUPT_BKPT; /* Freeblock off the end of the page */ + /* Freeblock off the end of the page */ + return SQLITE_CORRUPT_PGNO(pPage->pgno); } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); @@ -1887,10 +1892,12 @@ static int btreeInitPage(MemPage *pPage){ pc = next; } if( next>0 ){ - return SQLITE_CORRUPT_BKPT; /* Freeblock not in ascending order */ + /* Freeblock not in ascending order */ + return SQLITE_CORRUPT_PGNO(pPage->pgno); } if( pc+size>(unsigned int)usableSize ){ - return SQLITE_CORRUPT_BKPT; /* Last freeblock extends past page end */ + /* Last freeblock extends past page end */ + return SQLITE_CORRUPT_PGNO(pPage->pgno); } } @@ -1902,7 +1909,7 @@ static int btreeInitPage(MemPage *pPage){ ** area, according to the page header, lies within the page. */ if( nFree>usableSize ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } pPage->nFree = (u16)(nFree - iCellFirst); pPage->isInit = 1; @@ -2069,7 +2076,7 @@ static int getAndInitPage( /* If obtaining a child page for a cursor, we must verify that the page is ** compatible with the root page. */ if( pCur && ((*ppPage)->nCell<1 || (*ppPage)->intKey!=pCur->curIntKey) ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pgno); releasePage(*ppPage); goto getAndInitPage_error; } @@ -3400,7 +3407,7 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ if( eType==PTRMAP_OVERFLOW2 ){ /* The pointer is always the first 4 bytes of the page in this case. */ if( get4byte(pPage->aData)!=iFrom ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } put4byte(pPage->aData, iTo); }else{ @@ -3419,7 +3426,7 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ pPage->xParseCell(pPage, pCell, &info); if( info.nLocal pPage->aData+pPage->pBt->usableSize ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } if( iFrom==get4byte(pCell+info.nSize-4) ){ put4byte(pCell+info.nSize-4, iTo); @@ -3437,7 +3444,7 @@ static int modifyPagePointer(MemPage *pPage, Pgno iFrom, Pgno iTo, u8 eType){ if( i==nCell ){ if( eType!=PTRMAP_BTREE || get4byte(&pPage->aData[pPage->hdrOffset+8])!=iFrom ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } put4byte(&pPage->aData[pPage->hdrOffset+8], iTo); } @@ -4545,7 +4552,7 @@ static int accessPayload( ** &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ** but is recast into its current form to avoid integer overflow problems */ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pPage->pgno); } /* Check if data must be read/written to/from the btree page itself. */ @@ -4692,7 +4699,8 @@ static int accessPayload( } if( rc==SQLITE_OK && amt>0 ){ - return SQLITE_CORRUPT_BKPT; /* Overflow chain ends prematurely */ + /* Overflow chain ends prematurely */ + return SQLITE_CORRUPT_PGNO(pPage->pgno); } return rc; } @@ -4958,7 +4966,7 @@ static int moveToRoot(BtCursor *pCur){ ** (or the freelist). */ assert( pRoot->intKey==1 || pRoot->intKey==0 ); if( pRoot->isInit==0 || (pCur->pKeyInfo==0)!=pRoot->intKey ){ - return SQLITE_CORRUPT_BKPT; + return SQLITE_CORRUPT_PGNO(pCur->apPage[pCur->iPage]->pgno); } skip_init: @@ -5231,7 +5239,9 @@ int sqlite3BtreeMovetoUnpacked( pCell = findCellPastPtr(pPage, idx); if( pPage->intKeyLeaf ){ while( 0x80 <= *(pCell++) ){ - if( pCell>=pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; + if( pCell>=pPage->aDataEnd ){ + return SQLITE_CORRUPT_PGNO(pPage->pgno); + } } } getVarint(pCell, (u64*)&nCellKey); @@ -5304,7 +5314,7 @@ int sqlite3BtreeMovetoUnpacked( testcase( nCell==1 ); /* Invalid key size: 0x80 0x80 0x01 */ testcase( nCell==2 ); /* Minimum legal index key size */ if( nCell<2 ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pPage->pgno); goto moveto_finish; } pCellKey = sqlite3Malloc( nCell+18 ); @@ -5706,7 +5716,7 @@ static int allocateBtreePage( } testcase( iTrunk==mxPage ); if( iTrunk>mxPage || nSearch++ > n ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(pPrevTrunk ? pPrevTrunk->pgno : 1); }else{ rc = btreeGetUnusedPage(pBt, iTrunk, &pTrunk, 0); } @@ -5735,7 +5745,7 @@ static int allocateBtreePage( TRACE(("ALLOCATE: %d trunk - %d free pages left\n", *pPgno, n-1)); }else if( k>(u32)(pBt->usableSize/4 - 2) ){ /* Value of k is out of range. Database corruption */ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; #ifndef SQLITE_OMIT_AUTOVACUUM }else if( searchList @@ -5769,7 +5779,7 @@ static int allocateBtreePage( MemPage *pNewTrunk; Pgno iNewTrunk = get4byte(&pTrunk->aData[8]); if( iNewTrunk>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; } testcase( iNewTrunk==mxPage ); @@ -5834,7 +5844,7 @@ static int allocateBtreePage( iPage = get4byte(&aData[8+closest*4]); testcase( iPage==mxPage ); if( iPage>mxPage ){ - rc = SQLITE_CORRUPT_BKPT; + rc = SQLITE_CORRUPT_PGNO(iTrunk); goto end_allocate_page; } testcase( iPage==mxPage ); @@ -6104,7 +6114,8 @@ static int clearCell( return SQLITE_OK; /* No overflow pages. Return without doing anything */ } if( pCell+pInfo->nSize-1 > pPage->aData+pPage->maskPage ){ - return SQLITE_CORRUPT_BKPT; /* Cell extends past end of page */ + /* Cell extends past end of page */ + return SQLITE_CORRUPT_PGNO(pPage->pgno); } ovflPgno = get4byte(pCell + pInfo->nSize - 4); assert( pBt->usableSize > 4 ); @@ -8162,12 +8173,18 @@ int sqlite3BtreeInsert( memcpy(newCell, oldCell, 4); } rc = clearCell(pPage, oldCell, &info); - if( info.nSize==szNew && info.nLocal==info.nPayload ){ + if( info.nSize==szNew && info.nLocal==info.nPayload + && (!ISAUTOVACUUM || szNewminLocal) + ){ /* Overwrite the old cell with the new if they are the same size. ** We could also try to do this if the old cell is smaller, then add ** the leftover space to the free list. But experiments show that ** doing that is no faster then skipping this optimization and just - ** calling dropCell() and insertCell(). */ + ** calling dropCell() and insertCell(). + ** + ** This optimization cannot be used on an autovacuum database if the + ** new entry uses overflow pages, as the insertCell() call below is + ** necessary to add the PTRMAP_OVERFLOW1 pointer-map entry. */ assert( rc==SQLITE_OK ); /* clearCell never fails when nLocal==nPayload */ if( oldCell+szNew > pPage->aDataEnd ) return SQLITE_CORRUPT_BKPT; memcpy(oldCell, newCell, szNew); diff --git a/src/build.c b/src/build.c index e04406d857..c4bb0300a5 100644 --- a/src/build.c +++ b/src/build.c @@ -939,7 +939,11 @@ void sqlite3StartTable( pTable->iPKey = -1; pTable->pSchema = db->aDb[iDb].pSchema; pTable->nTabRef = 1; +#ifdef SQLITE_DEFAULT_ROWEST + pTable->nRowLogEst = sqlite3LogEst(SQLITE_DEFAULT_ROWEST); +#else pTable->nRowLogEst = 200; assert( 200==sqlite3LogEst(1048576) ); +#endif assert( pParse->pNewTable==0 ); pParse->pNewTable = pTable; diff --git a/src/insert.c b/src/insert.c index 48502b7e07..e684eaafa9 100644 --- a/src/insert.c +++ b/src/insert.c @@ -524,6 +524,7 @@ void sqlite3Insert( if( pParse->nErr || db->mallocFailed ){ goto insert_cleanup; } + dest.iSDParm = 0; /* Suppress a harmless compiler warning */ /* If the Select object is really just a simple VALUES() list with a ** single row (the common case) then keep that one row of values diff --git a/src/main.c b/src/main.c index 36e10383a4..44b7d5df89 100644 --- a/src/main.c +++ b/src/main.c @@ -3338,6 +3338,12 @@ int sqlite3CantopenError(int lineno){ return reportError(SQLITE_CANTOPEN, lineno, "cannot open file"); } #ifdef SQLITE_DEBUG +int sqlite3CorruptPgnoError(int lineno, Pgno pgno){ + char zMsg[100]; + sqlite3_snprintf(sizeof(zMsg), zMsg, "database corruption page %d", pgno); + testcase( sqlite3GlobalConfig.xLog!=0 ); + return reportError(SQLITE_CORRUPT, lineno, zMsg); +} int sqlite3NomemError(int lineno){ testcase( sqlite3GlobalConfig.xLog!=0 ); return reportError(SQLITE_NOMEM, lineno, "OOM"); diff --git a/src/select.c b/src/select.c index 51cd089a2f..6c99776e19 100644 --- a/src/select.c +++ b/src/select.c @@ -5290,6 +5290,8 @@ int sqlite3Select( if( pPrior ){ sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor); explainSetInteger(pItem->iSelectId, pPrior->iSelectId); + assert( pPrior->pSelect!=0 ); + pSub->nSelectRow = pPrior->pSelect->nSelectRow; }else{ sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor); explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId); diff --git a/src/shell.c b/src/shell.c index 322c6e5c4f..0c109720a0 100644 --- a/src/shell.c +++ b/src/shell.c @@ -509,6 +509,18 @@ static int strlen30(const char *z){ return 0x3fffffff & (int)(z2 - z); } +/* +** Return the length of a string in characters. Multibyte UTF8 characters +** count as a single character. +*/ +static int strlenChar(const char *z){ + int n = 0; + while( *z ){ + if( (0xc0&*(z++))!=0x80 ) n++; + } + return n; +} + /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer @@ -1173,6 +1185,61 @@ static unsigned char *SHA3Final(SHA3Context *p){ return &p->u.x[p->nRate]; } +/* +** SQL function: shell_add_schema(S,X) +** +** Add the schema name X to the CREATE statement in S and return the result. +** Examples: +** +** CREATE TABLE t1(x) -> CREATE TABLE xyz.t1(x); +** +** Also works on +** +** CREATE INDEX +** CREATE UNIQUE INDEX +** CREATE VIEW +** CREATE TRIGGER +** CREATE VIRTUAL TABLE +** +** This UDF is used by the .schema command to insert the schema name of +** attached databases into the middle of the sqlite_master.sql field. +*/ +static void shellAddSchemaName( + sqlite3_context *pCtx, + int nVal, + sqlite3_value **apVal +){ + static const char *aPrefix[] = { + "TABLE", + "INDEX", + "UNIQUE INDEX", + "VIEW", + "TRIGGER", + "VIRTUAL TABLE" + }; + int i = 0; + const char *zIn = (const char*)sqlite3_value_text(apVal[0]); + const char *zSchema = (const char*)sqlite3_value_text(apVal[1]); + assert( nVal==2 ); + if( zIn!=0 && strncmp(zIn, "CREATE ", 7)==0 ){ + for(i=0; inullValue); + n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue); if( wactualWidth) ){ @@ -1954,8 +2021,8 @@ static int shell_callback( }else{ w = 10; } - if( p->cMode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ - w = strlen30(azArg[i]); + if( p->cMode==MODE_Explain && azArg[i] && strlenChar(azArg[i])>w ){ + w = strlenChar(azArg[i]); } if( i==1 && p->aiIndent && p->pStmt ){ if( p->iIndentnIndent ){ @@ -3524,6 +3591,9 @@ static void open_db(ShellState *p, int keepAlive){ sha3QueryFunc, 0, 0); sqlite3_create_function(p->db, "sha3_query", 2, SQLITE_UTF8, 0, sha3QueryFunc, 0, 0); + sqlite3_create_function(p->db, "shell_add_schema", 2, SQLITE_UTF8, 0, + shellAddSchemaName, 0, 0); + } } @@ -5677,12 +5747,17 @@ static int do_meta_command(char *zLine, ShellState *p){ }else if( c=='s' && strncmp(azArg[0], "schema", n)==0 ){ + ShellText sSelect; ShellState data; char *zErrMsg = 0; + const char *zDiv = 0; + int iSchema = 0; + open_db(p, 0); memcpy(&data, p, sizeof(data)); data.showHeader = 0; data.cMode = data.mode = MODE_Semi; + initText(&sSelect); if( nArg>=2 && optionMatch(azArg[1], "indent") ){ data.cMode = data.mode = MODE_Pretty; nArg--; @@ -5720,33 +5795,62 @@ static int do_meta_command(char *zLine, ShellState *p){ callback(&data, 1, new_argv, new_colv); rc = SQLITE_OK; }else{ - char *zSql; - zSql = sqlite3_mprintf( - "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " - "WHERE lower(tbl_name) LIKE %Q" - " AND type!='meta' AND sql NOTNULL " - "ORDER BY rowid", azArg[1]); - rc = sqlite3_exec(p->db, zSql, callback, &data, &zErrMsg); - sqlite3_free(zSql); + zDiv = "("; } }else if( nArg==1 ){ - rc = sqlite3_exec(p->db, - "SELECT sql FROM " - " (SELECT sql sql, type type, tbl_name tbl_name, name name, rowid x" - " FROM sqlite_master UNION ALL" - " SELECT sql, type, tbl_name, name, rowid FROM sqlite_temp_master) " - "WHERE type!='meta' AND sql NOTNULL AND name NOT LIKE 'sqlite_%' " - "ORDER BY rowid", - callback, &data, &zErrMsg - ); + zDiv = "("; }else{ raw_printf(stderr, "Usage: .schema ?--indent? ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; } + if( zDiv ){ + sqlite3_stmt *pStmt = 0; + sqlite3_prepare_v2(p->db, "SELECT name FROM pragma_database_list", + -1, &pStmt, 0); + appendText(&sSelect, "SELECT sql FROM", 0); + iSchema = 0; + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + const char *zDb = (const char*)sqlite3_column_text(pStmt, 0); + char zScNum[30]; + sqlite3_snprintf(sizeof(zScNum), zScNum, "%d", ++iSchema); + appendText(&sSelect, zDiv, 0); + zDiv = " UNION ALL "; + if( strcmp(zDb, "main")!=0 ){ + appendText(&sSelect, "SELECT shell_add_schema(sql,", 0); + appendText(&sSelect, zDb, '"'); + appendText(&sSelect, ") AS sql, type, tbl_name, name, rowid,", 0); + appendText(&sSelect, zScNum, 0); + appendText(&sSelect, " AS snum, ", 0); + appendText(&sSelect, zDb, '\''); + appendText(&sSelect, " AS sname FROM ", 0); + appendText(&sSelect, zDb, '"'); + appendText(&sSelect, ".sqlite_master", 0); + }else{ + appendText(&sSelect, "SELECT sql, type, tbl_name, name, rowid, ", 0); + appendText(&sSelect, zScNum, 0); + appendText(&sSelect, " AS snum, 'main' AS sname FROM sqlite_master",0); + } + } + sqlite3_finalize(pStmt); + appendText(&sSelect, ") WHERE ", 0); + if( nArg>1 ){ + char *zQarg = sqlite3_mprintf("%Q", azArg[1]); + if( strchr(azArg[1], '.') ){ + appendText(&sSelect, "lower(printf('%s.%s',sname,tbl_name))", 0); + }else{ + appendText(&sSelect, "lower(tbl_name)", 0); + } + appendText(&sSelect, strchr(azArg[1], '*') ? " GLOB " : " LIKE ", 0); + appendText(&sSelect, zQarg, 0); + appendText(&sSelect, " AND ", 0); + sqlite3_free(zQarg); + } + appendText(&sSelect, "type!='meta' AND sql IS NOT NULL" + " ORDER BY snum, rowid", 0); + rc = sqlite3_exec(p->db, sSelect.z, callback, &data, &zErrMsg); + freeText(&sSelect); + } if( zErrMsg ){ utf8_printf(stderr,"Error: %s\n", zErrMsg); sqlite3_free(zErrMsg); @@ -5988,19 +6092,11 @@ static int do_meta_command(char *zLine, ShellState *p){ int bIsInit = 0; /* True to initialize the SELFTEST table */ int bVerbose = 0; /* Verbose output */ int bSelftestExists; /* True if SELFTEST already exists */ - char **azTest = 0; /* Content of the SELFTEST table */ - int nRow = 0; /* Number of rows in the SELFTEST table */ - int nCol = 4; /* Number of columns in the SELFTEST table */ - int i; /* Loop counter */ + int i, k; /* Loop counters */ int nTest = 0; /* Number of tests runs */ int nErr = 0; /* Number of errors seen */ ShellText str; /* Answer for a query */ - static char *azDefaultTest[] = { - 0, 0, 0, 0, - "0", "memo", "Missing SELFTEST table - default checks only", "", - "1", "run", "PRAGMA integrity_check", "ok" - }; - static const int nDefaultRow = 2; + sqlite3_stmt *pStmt = 0; /* Query against the SELFTEST table */ open_db(p,0); for(i=1; idb, - "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", - &azTest, &nRow, &nCol, 0); + initText(&str); + appendText(&str, "x", 0); + for(k=bSelftestExists; k>=0; k--){ + if( k==1 ){ + rc = sqlite3_prepare_v2(p->db, + "SELECT tno,op,cmd,ans FROM selftest ORDER BY tno", + -1, &pStmt, 0); + }else{ + rc = sqlite3_prepare_v2(p->db, + "VALUES(0,'memo','Missing SELFTEST table - default checks only','')," + " (1,'run','PRAGMA integrity_check','ok')", + -1, &pStmt, 0); + } if( rc ){ raw_printf(stderr, "Error querying the selftest table\n"); rc = 1; - sqlite3_free_table(azTest); + sqlite3_finalize(pStmt); goto meta_command_exit; - }else if( nRow==0 ){ - sqlite3_free_table(azTest); - azTest = azDefaultTest; - nRow = nDefaultRow; } - }else{ - azTest = azDefaultTest; - nRow = nDefaultRow; - } - initText(&str); - appendText(&str, "x", 0); - for(i=1; i<=nRow; i++){ - int tno = atoi(azTest[i*nCol]); - const char *zOp = azTest[i*nCol+1]; - const char *zSql = azTest[i*nCol+2]; - const char *zAns = azTest[i*nCol+3]; + for(i=1; sqlite3_step(pStmt)==SQLITE_ROW; i++){ + int tno = sqlite3_column_int(pStmt, 0); + const char *zOp = (const char*)sqlite3_column_text(pStmt, 1); + const char *zSql = (const char*)sqlite3_column_text(pStmt, 2); + const char *zAns = (const char*)sqlite3_column_text(pStmt, 3); - if( bVerbose>0 ){ - char *zQuote = sqlite3_mprintf("%q", zSql); - printf("%d: %s %s\n", tno, zOp, zSql); - sqlite3_free(zQuote); - } - if( strcmp(zOp,"memo")==0 ){ - utf8_printf(p->out, "%s\n", zSql); - }else - if( strcmp(zOp,"run")==0 ){ - char *zErrMsg = 0; - str.n = 0; - str.z[0] = 0; - rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); - nTest++; - if( bVerbose ){ - utf8_printf(p->out, "Result: %s\n", str.z); + k = 0; + if( bVerbose>0 ){ + char *zQuote = sqlite3_mprintf("%q", zSql); + printf("%d: %s %s\n", tno, zOp, zSql); + sqlite3_free(zQuote); } - if( rc || zErrMsg ){ - nErr++; + if( strcmp(zOp,"memo")==0 ){ + utf8_printf(p->out, "%s\n", zSql); + }else + if( strcmp(zOp,"run")==0 ){ + char *zErrMsg = 0; + str.n = 0; + str.z[0] = 0; + rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg); + nTest++; + if( bVerbose ){ + utf8_printf(p->out, "Result: %s\n", str.z); + } + if( rc || zErrMsg ){ + nErr++; + rc = 1; + utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); + sqlite3_free(zErrMsg); + }else if( strcmp(zAns,str.z)!=0 ){ + nErr++; + rc = 1; + utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); + utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); + } + }else + { + utf8_printf(stderr, + "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); rc = 1; - utf8_printf(p->out, "%d: error-code-%d: %s\n", tno, rc, zErrMsg); - sqlite3_free(zErrMsg); - }else if( strcmp(zAns,str.z)!=0 ){ - nErr++; - rc = 1; - utf8_printf(p->out, "%d: Expected: [%s]\n", tno, zAns); - utf8_printf(p->out, "%d: Got: [%s]\n", tno, str.z); + break; } - }else - { - utf8_printf(stderr, - "Unknown operation \"%s\" on selftest line %d\n", zOp, tno); - rc = 1; - break; - } - } + } /* End loop over rows of content from SELFTEST */ + sqlite3_finalize(pStmt); + } /* End loop over k */ freeText(&str); - if( azTest!=azDefaultTest ) sqlite3_free_table(azTest); utf8_printf(p->out, "%d errors out of %d tests\n", nErr, nTest); }else @@ -6302,59 +6399,47 @@ static int do_meta_command(char *zLine, ShellState *p){ sqlite3_stmt *pStmt; char **azResult; int nRow, nAlloc; - char *zSql = 0; int ii; + ShellText s; + initText(&s); open_db(p, 0); rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0); if( rc ) return shellDatabaseError(p->db); - /* Create an SQL statement to query for the list of tables in the - ** main and all attached databases where the table name matches the - ** LIKE pattern bound to variable "?1". */ - if( c=='t' ){ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_master" - " WHERE type IN ('table','view')" - " AND name NOT LIKE 'sqlite_%%'" - " AND name LIKE ?1"); - }else if( nArg>2 ){ + if( nArg>2 && c=='i' ){ /* It is an historical accident that the .indexes command shows an error ** when called with the wrong number of arguments whereas the .tables ** command does not. */ raw_printf(stderr, "Usage: .indexes ?LIKE-PATTERN?\n"); rc = 1; goto meta_command_exit; - }else{ - zSql = sqlite3_mprintf( - "SELECT name FROM sqlite_master" - " WHERE type='index'" - " AND tbl_name LIKE ?1"); } - for(ii=0; zSql && sqlite3_step(pStmt)==SQLITE_ROW; ii++){ + for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){ const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1); - if( zDbName==0 || ii==0 ) continue; - if( c=='t' ){ - zSql = sqlite3_mprintf( - "%z UNION ALL " - "SELECT '%q.' || name FROM \"%w\".sqlite_master" - " WHERE type IN ('table','view')" - " AND name NOT LIKE 'sqlite_%%'" - " AND name LIKE ?1", zSql, zDbName, zDbName); + if( zDbName==0 ) continue; + if( s.z && s.z[0] ) appendText(&s, " UNION ALL ", 0); + if( sqlite3_stricmp(zDbName, "main")==0 ){ + appendText(&s, "SELECT name FROM ", 0); }else{ - zSql = sqlite3_mprintf( - "%z UNION ALL " - "SELECT '%q.' || name FROM \"%w\".sqlite_master" - " WHERE type='index'" - " AND tbl_name LIKE ?1", zSql, zDbName, zDbName); + appendText(&s, "SELECT ", 0); + appendText(&s, zDbName, '\''); + appendText(&s, "||'.'||name FROM ", 0); + } + appendText(&s, zDbName, '"'); + appendText(&s, ".sqlite_master ", 0); + if( c=='t' ){ + appendText(&s," WHERE type IN ('table','view')" + " AND name NOT LIKE 'sqlite_%'" + " AND name LIKE ?1", 0); + }else{ + appendText(&s," WHERE type='index'" + " AND tbl_name LIKE ?1", 0); } } rc = sqlite3_finalize(pStmt); - if( zSql && rc==SQLITE_OK ){ - zSql = sqlite3_mprintf("%z ORDER BY 1", zSql); - if( zSql ) rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - } - sqlite3_free(zSql); - if( !zSql ) return shellNomemError(); + appendText(&s, " ORDER BY 1", 0); + rc = sqlite3_prepare_v2(p->db, s.z, -1, &pStmt, 0); + freeText(&s); if( rc ) return shellDatabaseError(p->db); /* Run the SQL statement prepared by the above block. Store the results diff --git a/src/sqlite.h.in b/src/sqlite.h.in index 06445d4448..1e4389215d 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -5620,7 +5620,9 @@ SQLITE_DEPRECATED void sqlite3_soft_heap_limit(int N); ** ^If the column-name parameter to sqlite3_table_column_metadata() is a ** NULL pointer, then this routine simply checks for the existence of the ** table and returns SQLITE_OK if the table exists and SQLITE_ERROR if it -** does not. +** does not. If the table name parameter T in a call to +** sqlite3_table_column_metadata(X,D,T,C,...) is NULL then the result is +** undefined behavior. ** ** ^The column is identified by the second, third and fourth parameters to ** this function. ^(The second parameter is either the name of the database diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 8c52e02c8d..9252c352b8 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -3402,11 +3402,14 @@ int sqlite3CantopenError(int); #ifdef SQLITE_DEBUG int sqlite3NomemError(int); int sqlite3IoerrnomemError(int); + int sqlite3CorruptPgnoError(int,Pgno); # define SQLITE_NOMEM_BKPT sqlite3NomemError(__LINE__) # define SQLITE_IOERR_NOMEM_BKPT sqlite3IoerrnomemError(__LINE__) +# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptPgnoError(__LINE__,(P)) #else # define SQLITE_NOMEM_BKPT SQLITE_NOMEM # define SQLITE_IOERR_NOMEM_BKPT SQLITE_IOERR_NOMEM +# define SQLITE_CORRUPT_PGNO(P) sqlite3CorruptError(__LINE__) #endif /* diff --git a/src/test_fs.c b/src/test_fs.c index dd590a6623..8192beb99b 100644 --- a/src/test_fs.c +++ b/src/test_fs.c @@ -545,6 +545,7 @@ static int fstreeFilter( zDir = zQuery; } } + if( nDir==0 ) nDir = 1; sqlite3_bind_text(pCsr->pStmt, 1, zDir, nDir, SQLITE_TRANSIENT); sqlite3_bind_text(pCsr->pStmt, 2, zRoot, nRoot, SQLITE_TRANSIENT); diff --git a/src/trigger.c b/src/trigger.c index bdf964084b..dfa0941417 100644 --- a/src/trigger.c +++ b/src/trigger.c @@ -306,6 +306,7 @@ void sqlite3FinishTrigger( if( v==0 ) goto triggerfinish_cleanup; sqlite3BeginWriteOperation(pParse, 0, iDb); z = sqlite3DbStrNDup(db, (char*)pAll->z, pAll->n); + testcase( z==0 ); sqlite3NestedParse(pParse, "INSERT INTO %Q.%s VALUES('trigger',%Q,%Q,0,'CREATE TRIGGER %q')", db->aDb[iDb].zDbSName, MASTER_NAME, zName, diff --git a/src/vdbemem.c b/src/vdbemem.c index e95a8d1b9d..88c57f1aba 100644 --- a/src/vdbemem.c +++ b/src/vdbemem.c @@ -1325,7 +1325,7 @@ static int valueFromExpr( } }else if( op==TK_UMINUS ) { /* This branch happens for multiple negative signs. Ex: -(-5) */ - if( SQLITE_OK==sqlite3ValueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal) + if( SQLITE_OK==valueFromExpr(db,pExpr->pLeft,enc,affinity,&pVal,pCtx) && pVal!=0 ){ sqlite3VdbeMemNumerify(pVal); diff --git a/src/where.c b/src/where.c index 89c4edeb8c..99b0df4e5d 100644 --- a/src/where.c +++ b/src/where.c @@ -4577,7 +4577,7 @@ WhereInfo *sqlite3WhereBegin( static const char zLabel[] = "0123456789abcdefghijklmnopqrstuvwyxz" "ABCDEFGHIJKLMNOPQRSTUVWYXZ"; for(p=pWInfo->pLoops, i=0; p; p=p->pNextLoop, i++){ - p->cId = zLabel[i%sizeof(zLabel)]; + p->cId = zLabel[i%(sizeof(zLabel)-1)]; whereLoopPrint(p, sWLB.pWC); } } diff --git a/test/autovacuum.test b/test/autovacuum.test index 5c14ed85d3..431c4b8a2a 100644 --- a/test/autovacuum.test +++ b/test/autovacuum.test @@ -705,5 +705,12 @@ do_test autovacuum-9.5 { file size test.db } $::sqlite_pending_byte +do_execsql_test autovacuum-10.1 { + DROP TABLE t1; + CREATE TABLE t1(a INTEGER PRIMARY KEY, b); + INSERT INTO t1 VALUES(25, randomblob(104)); + REPLACE INTO t1 VALUES(25, randomblob(1117)); + PRAGMA integrity_check; +} {ok} finish_test diff --git a/test/kvtest.c b/test/kvtest.c index aab0052686..b80adfea09 100644 --- a/test/kvtest.c +++ b/test/kvtest.c @@ -71,14 +71,21 @@ static const char zHelp[] = "\n" " --variance V Randomly vary M by plus or minus V\n" "\n" -" kvtest export DBFILE DIRECTORY\n" +" kvtest export DBFILE DIRECTORY [--tree]\n" "\n" " Export all the blobs in the kv table of DBFILE into separate\n" -" files in DIRECTORY.\n" +" files in DIRECTORY. DIRECTORY is created if it does not previously\n" +" exist. If the --tree option is used, then the blobs are written\n" +" into a hierarchy of directories, using names like 00/00/00,\n" +" 00/00/01, 00/00/02, and so forth. Without the --tree option, all\n" +" files are in the top-level directory with names like 000000, 000001,\n" +" 000002, and so forth.\n" "\n" -" kvtest stat DBFILE\n" +" kvtest stat DBFILE [options]\n" "\n" -" Display summary information about DBFILE\n" +" Display summary information about DBFILE. Options:\n" +"\n" +" --vacuum Run VACUUM on the database file\n" "\n" " kvtest run DBFILE [options]\n" "\n" @@ -94,12 +101,14 @@ static const char zHelp[] = " --integrity-check Run \"PRAGMA integrity_check\" after test\n" " --max-id N Maximum blob key to use\n" " --mmap N Mmap as much as N bytes of DBFILE\n" +" --multitrans Each read or write in its own transaction\n" +" --nocheckpoint Omit the checkpoint on WAL mode writes\n" " --nosync Set \"PRAGMA synchronous=OFF\"\n" " --jmode MODE Set MODE journal mode prior to starting\n" " --random Read blobs in a random order\n" " --start N Start reading with this blob key\n" " --stats Output operating stats before exiting\n" -" --update To an overwrite test\n" +" --update Do an overwrite test\n" ; /* Reference resources used */ @@ -115,6 +124,7 @@ static const char zHelp[] = # include #else /* Provide Windows equivalent for the needed parts of unistd.h */ +# include # include # define R_OK 2 # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) @@ -123,7 +133,6 @@ static const char zHelp[] = #endif #include -#include /* ** The following macros are used to cast pointers to integers and @@ -144,15 +153,9 @@ static const char zHelp[] = #if defined(__PTRDIFF_TYPE__) /* This case should work for GCC */ # define SQLITE_INT_TO_PTR(X) ((void*)(__PTRDIFF_TYPE__)(X)) # define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(__PTRDIFF_TYPE__)(X)) -#elif !defined(__GNUC__) /* Works for compilers other than LLVM */ -# define SQLITE_INT_TO_PTR(X) ((void*)&((char*)0)[X]) -# define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(((char*)X)-(char*)0)) -#elif defined(HAVE_STDINT_H) /* Use this case if we have ANSI headers */ +#else # define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X)) # define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(intptr_t)(X)) -#else /* Generates a warning - but it always works */ -# define SQLITE_INT_TO_PTR(X) ((void*)(X)) -# define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(X)) #endif /* @@ -237,13 +240,23 @@ static int integerValue(const char *zArg){ /* ** Check the filesystem object zPath. Determine what it is: ** -** PATH_DIR A directory +** PATH_DIR A single directory holding many files +** PATH_TREE A directory hierarchy with files at the leaves ** PATH_DB An SQLite database ** PATH_NEXIST Does not exist ** PATH_OTHER Something else +** +** PATH_DIR means all of the separate files are grouped together +** into a single directory with names like 000000, 000001, 000002, and +** so forth. PATH_TREE means there is a hierarchy of directories so +** that no single directory has too many entries. The files have names +** like 00/00/00, 00/00/01, 00/00/02 and so forth. The decision between +** PATH_DIR and PATH_TREE is determined by the presence of a subdirectory +** named "00" at the top-level. */ #define PATH_DIR 1 -#define PATH_DB 2 +#define PATH_TREE 2 +#define PATH_DB 3 #define PATH_NEXIST 0 #define PATH_OTHER 99 static int pathType(const char *zPath){ @@ -253,7 +266,15 @@ static int pathType(const char *zPath){ memset(&x, 0, sizeof(x)); rc = stat(zPath, &x); if( rc<0 ) return PATH_OTHER; - if( S_ISDIR(x.st_mode) ) return PATH_DIR; + if( S_ISDIR(x.st_mode) ){ + char *zLayer1 = sqlite3_mprintf("%s/00", zPath); + memset(&x, 0, sizeof(x)); + rc = stat(zLayer1, &x); + sqlite3_free(zLayer1); + if( rc<0 ) return PATH_DIR; + if( S_ISDIR(x.st_mode) ) return PATH_TREE; + return PATH_DIR; + } if( (x.st_size%512)==0 ) return PATH_DB; return PATH_OTHER; } @@ -364,6 +385,7 @@ static int statMain(int argc, char **argv){ sqlite3 *db; char *zSql; sqlite3_stmt *pStmt; + int doVacuum = 0; assert( strcmp(argv[1],"stat")==0 ); assert( argc>=3 ); @@ -372,12 +394,21 @@ static int statMain(int argc, char **argv){ char *z = argv[i]; if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z); if( z[1]=='-' ) z++; + if( strcmp(z, "-vacuum")==0 ){ + doVacuum = 1; + continue; + } fatalError("unknown option: \"%s\"", argv[i]); } rc = sqlite3_open(zDb, &db); if( rc ){ fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db)); } + if( doVacuum ){ + printf("Vacuuming...."); fflush(stdout); + sqlite3_exec(db, "VACUUM", 0, 0, 0); + printf(" done\n"); + } zSql = sqlite3_mprintf( "SELECT count(*), min(length(v)), max(length(v)), avg(length(v))" " FROM kv" @@ -410,41 +441,24 @@ static int statMain(int argc, char **argv){ printf("Page-count: %8d\n", sqlite3_column_int(pStmt, 0)); } sqlite3_finalize(pStmt); + zSql = sqlite3_mprintf("PRAGMA freelist_count"); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc ) fatalError("cannot prepare SQL [%s]: %s", zSql, sqlite3_errmsg(db)); + sqlite3_free(zSql); + if( sqlite3_step(pStmt)==SQLITE_ROW ){ + printf("Freelist-count: %8d\n", sqlite3_column_int(pStmt, 0)); + } + sqlite3_finalize(pStmt); + rc = sqlite3_prepare_v2(db, "PRAGMA integrity_check(10)", -1, &pStmt, 0); + if( rc ) fatalError("cannot prepare integrity check: %s", sqlite3_errmsg(db)); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + printf("Integrity-check: %s\n", sqlite3_column_text(pStmt, 0)); + } + sqlite3_finalize(pStmt); sqlite3_close(db); return 0; } -/* -** Implementation of the "writefile(X,Y)" SQL function. The argument Y -** is written into file X. The number of bytes written is returned. Or -** NULL is returned if something goes wrong, such as being unable to open -** file X for writing. -*/ -static void writefileFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - FILE *out; - const char *z; - sqlite3_int64 rc; - const char *zFile; - - zFile = (const char*)sqlite3_value_text(argv[0]); - if( zFile==0 ) return; - out = fopen(zFile, "wb"); - if( out==0 ) return; - z = (const char*)sqlite3_value_blob(argv[1]); - if( z==0 ){ - rc = 0; - }else{ - rc = fwrite(z, 1, sqlite3_value_bytes(argv[1]), out); - } - fclose(out); - printf("\r%s ", zFile); fflush(stdout); - sqlite3_result_int64(context, rc); -} - /* ** remember(V,PTR) ** @@ -465,6 +479,17 @@ static void rememberFunc( sqlite3_result_int64(pCtx, v); } +/* +** Make sure a directory named zDir exists. +*/ +static void kvtest_mkdir(const char *zDir){ +#if defined(_WIN32) + (void)mkdir(zDir); +#else + (void)mkdir(zDir, 0755); +#endif +} + /* ** Export the kv table to individual files in the filesystem */ @@ -472,32 +497,77 @@ static int exportMain(int argc, char **argv){ char *zDb; char *zDir; sqlite3 *db; - char *zSql; + sqlite3_stmt *pStmt; int rc; - char *zErrMsg = 0; + int ePathType; + int nFN; + char *zFN; + char *zTail; + size_t nWrote; + int i; assert( strcmp(argv[1],"export")==0 ); assert( argc>=3 ); + if( argc<4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY [OPTIONS]"); zDb = argv[2]; - if( argc!=4 ) fatalError("Usage: kvtest export DATABASE DIRECTORY"); zDir = argv[3]; - if( pathType(zDir)!=PATH_DIR ){ + kvtest_mkdir(zDir); + for(i=4; i0} { # TODO: Is this formula correct? set nTab [mem eval " @@ -460,9 +465,11 @@ proc subreport {title where showFrag} { statline {Bytes used after compression} $compressed_size $pct } statline {Bytes of payload} $payload $payload_percent + statline {Bytes of metadata} $total_meta $meta_percent if {$cnt==1} {statline {B-tree depth} $depth} statline {Average payload per entry} $avg_payload statline {Average unused bytes per entry} $avg_unused + statline {Average metadata per entry} $avg_meta if {[info exists avg_fanout]} { statline {Average fanout} $avg_fanout } @@ -757,6 +764,15 @@ Bytes of payload at the right is the bytes of payload divided by the bytes of storage consumed. +Bytes of metadata + + The amount of formatting and structural information stored on for the + table or index. Metadata includes the btree page header, the cell pointer + array, the size field for each cell, the left child pointer or non-leaf + cells, the overflow pointers for overflow cells, and the rowid value for + rowid table cells. In other words, metadata is everything that is not + unused space and that is not content. + Average payload per entry The average amount of payload on each entry. This is just the bytes of