Merge the latest changes from trunk.
FossilOrigin-Name: 60105c2253d0b617037e975b0d0b729bcb59b950aa2c33d8891394ad45aabb69
This commit is contained in:
commit
b74013300c
@ -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);
|
||||
|
@ -3787,7 +3787,11 @@ int sqlite3rbu_close(sqlite3rbu *p, char **pzErrmsg){
|
||||
|
||||
rbuEditErrmsg(p);
|
||||
rc = p->rc;
|
||||
if( pzErrmsg ){
|
||||
*pzErrmsg = p->zErrmsg;
|
||||
}else{
|
||||
sqlite3_free(p->zErrmsg);
|
||||
}
|
||||
sqlite3_free(p->zState);
|
||||
sqlite3_free(p);
|
||||
}else{
|
||||
|
@ -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
|
||||
|
@ -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]));
|
||||
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 );
|
||||
|
@ -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
|
||||
|
||||
|
54
manifest
54
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
|
||||
|
@ -1 +1 @@
|
||||
61853bc171e7c3af1db6a33ac8b1ad21e1c08e8d6b317fe061fdcd89c9a42e88
|
||||
60105c2253d0b617037e975b0d0b729bcb59b950aa2c33d8891394ad45aabb69
|
101
src/btree.c
101
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( pc<iCellFirst || pc>iCellLast ){
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
return SQLITE_CORRUPT_PGNO(pPage->pgno);
|
||||
}
|
||||
assert( pc>=iCellFirst && pc<=iCellLast );
|
||||
size = pPage->xCellSize(pPage, &src[pc]);
|
||||
cbrk -= size;
|
||||
if( cbrk<iCellFirst || pc+size>usableSize ){
|
||||
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 || pc<iAddr+4 ){
|
||||
*pRc = SQLITE_CORRUPT_BKPT;
|
||||
*pRc = SQLITE_CORRUPT_PGNO(pPg->pgno);
|
||||
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]))<iStart ){
|
||||
if( iFreeBlk<iPtr+4 ){
|
||||
if( iFreeBlk==0 ) break;
|
||||
return SQLITE_CORRUPT_BKPT;
|
||||
return SQLITE_CORRUPT_PGNO(pPage->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( pc<iCellFirst || pc>iCellLast ){
|
||||
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<info.nPayload ){
|
||||
if( pCell+info.nSize > 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 || szNew<pPage->minLocal)
|
||||
){
|
||||
/* 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);
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
267
src/shell.c
267
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; i<sizeof(aPrefix)/sizeof(aPrefix[0]); i++){
|
||||
int n = strlen30(aPrefix[i]);
|
||||
if( strncmp(zIn+7, aPrefix[i], n)==0 && zIn[n+7]==' ' ){
|
||||
char cQuote = quoteChar(zSchema);
|
||||
char *z;
|
||||
if( cQuote ){
|
||||
z = sqlite3_mprintf("%.*s \"%w\".%s", n+7, zIn, zSchema, zIn+n+8);
|
||||
}else{
|
||||
z = sqlite3_mprintf("%.*s %s.%s", n+7, zIn, zSchema, zIn+n+8);
|
||||
}
|
||||
sqlite3_result_text(pCtx, z, -1, sqlite3_free);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
sqlite3_result_value(pCtx, apVal[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
** Implementation of the sha3(X,SIZE) function.
|
||||
**
|
||||
@ -1917,9 +1984,9 @@ static int shell_callback(
|
||||
w = 0;
|
||||
}
|
||||
if( w==0 ){
|
||||
w = strlen30(azCol[i] ? azCol[i] : "");
|
||||
w = strlenChar(azCol[i] ? azCol[i] : "");
|
||||
if( w<10 ) w = 10;
|
||||
n = strlen30(azArg && azArg[i] ? azArg[i] : p->nullValue);
|
||||
n = strlenChar(azArg && azArg[i] ? azArg[i] : p->nullValue);
|
||||
if( w<n ) w = n;
|
||||
}
|
||||
if( i<ArraySize(p->actualWidth) ){
|
||||
@ -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->iIndent<p->nIndent ){
|
||||
@ -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; i<nArg; i++){
|
||||
@ -6030,32 +6126,32 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
createSelftestTable(p);
|
||||
bSelftestExists = 1;
|
||||
}
|
||||
if( bSelftestExists ){
|
||||
rc = sqlite3_get_table(p->db,
|
||||
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",
|
||||
&azTest, &nRow, &nCol, 0);
|
||||
-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);
|
||||
|
||||
k = 0;
|
||||
if( bVerbose>0 ){
|
||||
char *zQuote = sqlite3_mprintf("%q", zSql);
|
||||
printf("%d: %s %s\n", tno, zOp, zSql);
|
||||
@ -6091,9 +6187,10 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
307
test/kvtest.c
307
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 <unistd.h>
|
||||
#else
|
||||
/* Provide Windows equivalent for the needed parts of unistd.h */
|
||||
# include <direct.h>
|
||||
# include <io.h>
|
||||
# define R_OK 2
|
||||
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
||||
@ -123,7 +133,6 @@ static const char zHelp[] =
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
/*
|
||||
** 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; i<argc; i++){
|
||||
const char *z = argv[i];
|
||||
if( z[0]=='-' && z[1]=='-' ) z++;
|
||||
if( strcmp(z,"-tree")==0 ){
|
||||
zFN = sqlite3_mprintf("%s/00", zDir);
|
||||
kvtest_mkdir(zFN);
|
||||
sqlite3_free(zFN);
|
||||
continue;
|
||||
}
|
||||
fatalError("unknown argument: \"%s\"\n", argv[i]);
|
||||
}
|
||||
ePathType = pathType(zDir);
|
||||
if( ePathType!=PATH_DIR && ePathType!=PATH_TREE ){
|
||||
fatalError("object \"%s\" is not a directory", zDir);
|
||||
}
|
||||
rc = sqlite3_open(zDb, &db);
|
||||
if( rc ){
|
||||
fatalError("cannot open database \"%s\": %s", zDb, sqlite3_errmsg(db));
|
||||
}
|
||||
sqlite3_create_function(db, "writefile", 2, SQLITE_UTF8, 0,
|
||||
writefileFunc, 0, 0);
|
||||
zSql = sqlite3_mprintf(
|
||||
"SELECT writefile(printf('%s/%%06d',k),v) FROM kv;",
|
||||
zDir
|
||||
);
|
||||
rc = sqlite3_exec(db, zSql, 0, 0, &zErrMsg);
|
||||
if( rc ) fatalError("database create failed: %s", zErrMsg);
|
||||
sqlite3_free(zSql);
|
||||
rc = sqlite3_prepare_v2(db, "SELECT k, v FROM kv ORDER BY k", -1, &pStmt, 0);
|
||||
if( rc ){
|
||||
fatalError("prepare_v2 failed: %s\n", sqlite3_errmsg(db));
|
||||
}
|
||||
nFN = (int)strlen(zDir);
|
||||
zFN = sqlite3_mprintf("%s/00/00/00.extra---------------------", zDir);
|
||||
if( zFN==0 ){
|
||||
fatalError("malloc failed\n");
|
||||
}
|
||||
zTail = zFN + nFN + 1;
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
int iKey = sqlite3_column_int(pStmt, 0);
|
||||
sqlite3_int64 nData = sqlite3_column_bytes(pStmt, 1);
|
||||
const void *pData = sqlite3_column_blob(pStmt, 1);
|
||||
FILE *out;
|
||||
if( ePathType==PATH_DIR ){
|
||||
sqlite3_snprintf(20, zTail, "%06d", iKey);
|
||||
}else{
|
||||
sqlite3_snprintf(20, zTail, "%02d", iKey/10000);
|
||||
kvtest_mkdir(zFN);
|
||||
sqlite3_snprintf(20, zTail, "%02d/%02d", iKey/10000, (iKey/100)%100);
|
||||
kvtest_mkdir(zFN);
|
||||
sqlite3_snprintf(20, zTail, "%02d/%02d/%02d",
|
||||
iKey/10000, (iKey/100)%100, iKey%100);
|
||||
}
|
||||
out = fopen(zFN, "wb");
|
||||
nWrote = fwrite(pData, 1, nData, out);
|
||||
fclose(out);
|
||||
printf("\r%s ", zTail); fflush(stdout);
|
||||
if( nWrote!=nData ){
|
||||
fatalError("Wrote only %d of %d bytes to %s\n",
|
||||
(int)nWrote, nData, zFN);
|
||||
}
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_close(db);
|
||||
sqlite3_free(zFN);
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
||||
@ -517,7 +587,7 @@ static int exportMain(int argc, char **argv){
|
||||
** NULL is returned if any error is encountered. The final value of *pnByte
|
||||
** is undefined in this case.
|
||||
*/
|
||||
static unsigned char *readFile(const char *zName, int *pnByte){
|
||||
static unsigned char *readFile(const char *zName, sqlite3_int64 *pnByte){
|
||||
FILE *in; /* FILE from which to read content of zName */
|
||||
sqlite3_int64 nIn; /* Size of zName in bytes */
|
||||
size_t nRead; /* Number of bytes actually read */
|
||||
@ -535,7 +605,7 @@ static unsigned char *readFile(const char *zName, int *pnByte){
|
||||
sqlite3_free(pBuf);
|
||||
return 0;
|
||||
}
|
||||
if( pnByte ) *pnByte = (int)nIn;
|
||||
if( pnByte ) *pnByte = nIn;
|
||||
return pBuf;
|
||||
}
|
||||
|
||||
@ -543,7 +613,7 @@ static unsigned char *readFile(const char *zName, int *pnByte){
|
||||
** Overwrite a file with randomness. Do not change the size of the
|
||||
** file.
|
||||
*/
|
||||
static void updateFile(const char *zName, int *pnByte, int doFsync){
|
||||
static void updateFile(const char *zName, sqlite3_int64 *pnByte, int doFsync){
|
||||
FILE *out; /* FILE from which to read content of zName */
|
||||
sqlite3_int64 sz; /* Size of zName in bytes */
|
||||
size_t nWritten; /* Number of bytes actually read */
|
||||
@ -554,7 +624,7 @@ static void updateFile(const char *zName, int *pnByte, int doFsync){
|
||||
if( sz<0 ){
|
||||
fatalError("No such file: \"%s\"", zName);
|
||||
}
|
||||
*pnByte = (int)sz;
|
||||
*pnByte = sz;
|
||||
if( sz==0 ) return;
|
||||
pBuf = sqlite3_malloc64( sz );
|
||||
if( pBuf==0 ){
|
||||
@ -742,16 +812,18 @@ static int runMain(int argc, char **argv){
|
||||
int doIntegrityCk = 0; /* Run PRAGMA integrity_check after the test */
|
||||
int noSync = 0; /* Disable synchronous mode */
|
||||
int doFsync = 0; /* Update disk files synchronously */
|
||||
int doMultiTrans = 0; /* Each operation in its own transaction */
|
||||
int noCheckpoint = 0; /* Omit the checkpoint in WAL mode */
|
||||
sqlite3 *db = 0; /* Database connection */
|
||||
sqlite3_stmt *pStmt = 0; /* Prepared statement for SQL access */
|
||||
sqlite3_blob *pBlob = 0; /* Handle for incremental Blob I/O */
|
||||
sqlite3_int64 tmStart; /* Start time */
|
||||
sqlite3_int64 tmElapsed; /* Elapsed time */
|
||||
int mmapSize = 0; /* --mmap N argument */
|
||||
int nData = 0; /* Bytes of data */
|
||||
sqlite3_int64 nData = 0; /* Bytes of data */
|
||||
sqlite3_int64 nTotal = 0; /* Total data read */
|
||||
unsigned char *pData = 0; /* Content of the blob */
|
||||
int nAlloc = 0; /* Space allocated for pData[] */
|
||||
sqlite3_int64 nAlloc = 0; /* Space allocated for pData[] */
|
||||
const char *zJMode = 0; /* Journal mode */
|
||||
|
||||
|
||||
@ -765,12 +837,42 @@ static int runMain(int argc, char **argv){
|
||||
char *z = argv[i];
|
||||
if( z[0]!='-' ) fatalError("unknown argument: \"%s\"", z);
|
||||
if( z[1]=='-' ) z++;
|
||||
if( strcmp(z, "-asc")==0 ){
|
||||
eOrder = ORDER_ASC;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-blob-api")==0 ){
|
||||
bBlobApi = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-cache-size")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iCache = integerValue(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-count")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
nCount = integerValue(argv[++i]);
|
||||
if( nCount<1 ) fatalError("the --count must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-desc")==0 ){
|
||||
eOrder = ORDER_DESC;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-fsync")==0 ){
|
||||
doFsync = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-integrity-check")==0 ){
|
||||
doIntegrityCk = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-jmode")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
zJMode = argv[++i];
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-mmap")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
mmapSize = integerValue(argv[++i]);
|
||||
@ -782,36 +884,26 @@ static int runMain(int argc, char **argv){
|
||||
iMax = integerValue(argv[++i]);
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-start")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iKey = integerValue(argv[++i]);
|
||||
if( iKey<1 ) fatalError("the --start must be positive");
|
||||
if( strcmp(z, "-multitrans")==0 ){
|
||||
doMultiTrans = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-cache-size")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iCache = integerValue(argv[++i]);
|
||||
if( strcmp(z, "-nocheckpoint")==0 ){
|
||||
noCheckpoint = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-jmode")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
zJMode = argv[++i];
|
||||
if( strcmp(z, "-nosync")==0 ){
|
||||
noSync = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-random")==0 ){
|
||||
eOrder = ORDER_RANDOM;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-asc")==0 ){
|
||||
eOrder = ORDER_ASC;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-desc")==0 ){
|
||||
eOrder = ORDER_DESC;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-blob-api")==0 ){
|
||||
bBlobApi = 1;
|
||||
if( strcmp(z, "-start")==0 ){
|
||||
if( i==argc-1 ) fatalError("missing argument on \"%s\"", argv[i]);
|
||||
iKey = integerValue(argv[++i]);
|
||||
if( iKey<1 ) fatalError("the --start must be positive");
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-stats")==0 ){
|
||||
@ -822,18 +914,6 @@ static int runMain(int argc, char **argv){
|
||||
isUpdateTest = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-integrity-check")==0 ){
|
||||
doIntegrityCk = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-nosync")==0 ){
|
||||
noSync = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-fsync")==0 ){
|
||||
doFsync = 1;
|
||||
continue;
|
||||
}
|
||||
fatalError("unknown option: \"%s\"", argv[i]);
|
||||
}
|
||||
if( eType==PATH_DB ){
|
||||
@ -841,6 +921,7 @@ static int runMain(int argc, char **argv){
|
||||
sqlite3_open(zDb, &db);
|
||||
sqlite3_exec(db, "SELECT rowid FROM sqlite_master LIMIT 1", 0, 0, 0);
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}
|
||||
tmStart = timeOfDay();
|
||||
if( eType==PATH_DB ){
|
||||
@ -876,6 +957,9 @@ static int runMain(int argc, char **argv){
|
||||
zSql = sqlite3_mprintf("PRAGMA journal_mode=%Q", zJMode);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( noCheckpoint ){
|
||||
sqlite3_exec(db, "PRAGMA wal_autocheckpoint=0", 0, 0, 0);
|
||||
}
|
||||
}
|
||||
sqlite3_prepare_v2(db, "PRAGMA journal_mode", -1, &pStmt, 0);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
@ -892,14 +976,19 @@ static int runMain(int argc, char **argv){
|
||||
sqlite3_finalize(pStmt);
|
||||
}
|
||||
pStmt = 0;
|
||||
sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
if( !doMultiTrans ) sqlite3_exec(db, "BEGIN", 0, 0, 0);
|
||||
}
|
||||
if( iMax<=0 ) iMax = 1000;
|
||||
for(i=0; i<nCount; i++){
|
||||
if( eType==PATH_DIR ){
|
||||
/* CASE 1: Reading blobs out of separate files */
|
||||
if( eType==PATH_DIR || eType==PATH_TREE ){
|
||||
/* CASE 1: Reading or writing blobs out of separate files */
|
||||
char *zKey;
|
||||
if( eType==PATH_DIR ){
|
||||
zKey = sqlite3_mprintf("%s/%06d", zDb, iKey);
|
||||
}else{
|
||||
zKey = sqlite3_mprintf("%s/%02d/%02d/%02d", zDb, iKey/10000,
|
||||
(iKey/100)%100, iKey%100);
|
||||
}
|
||||
nData = 0;
|
||||
if( isUpdateTest ){
|
||||
updateFile(zKey, &nData, doFsync);
|
||||
@ -924,18 +1013,18 @@ static int runMain(int argc, char **argv){
|
||||
nData = sqlite3_blob_bytes(pBlob);
|
||||
if( nAlloc<nData+1 ){
|
||||
nAlloc = nData+100;
|
||||
pData = sqlite3_realloc(pData, nAlloc);
|
||||
pData = sqlite3_realloc64(pData, nAlloc);
|
||||
}
|
||||
if( pData==0 ) fatalError("cannot allocate %d bytes", nData+1);
|
||||
if( isUpdateTest ){
|
||||
sqlite3_randomness((int)nData, pData);
|
||||
rc = sqlite3_blob_write(pBlob, pData, nData, 0);
|
||||
rc = sqlite3_blob_write(pBlob, pData, (int)nData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fatalError("could not write the blob at %d: %s", iKey,
|
||||
sqlite3_errmsg(db));
|
||||
}
|
||||
}else{
|
||||
rc = sqlite3_blob_read(pBlob, pData, nData, 0);
|
||||
rc = sqlite3_blob_read(pBlob, pData, (int)nData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fatalError("could not read the blob at %d: %s", iKey,
|
||||
sqlite3_errmsg(db));
|
||||
@ -990,10 +1079,17 @@ static int runMain(int argc, char **argv){
|
||||
display_stats(db, 0);
|
||||
}
|
||||
if( db ){
|
||||
sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
if( !doMultiTrans ) sqlite3_exec(db, "COMMIT", 0, 0, 0);
|
||||
if( !noCheckpoint ){
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}
|
||||
}
|
||||
tmElapsed = timeOfDay() - tmStart;
|
||||
if( db && noCheckpoint ){
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}
|
||||
if( nExtra ){
|
||||
printf("%d cycles due to %d misses\n", nCount, nExtra);
|
||||
}
|
||||
@ -1007,6 +1103,7 @@ static int runMain(int argc, char **argv){
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}
|
||||
}
|
||||
printf("--count %d --max-id %d", nCount-nExtra, iMax);
|
||||
|
@ -216,14 +216,17 @@ if {$tcl_platform(platform)!="windows" || \
|
||||
} {}
|
||||
|
||||
set pwd [pwd]
|
||||
if {![string match {*[_%]*} $pwd]} {
|
||||
do_execsql_test 3.5 {
|
||||
SELECT path, size FROM fstree WHERE path GLOB $pwd || '/subdir/*' ORDER BY 1
|
||||
SELECT path, size FROM fstree
|
||||
WHERE path GLOB $pwd || '/subdir/*' ORDER BY 1
|
||||
} [list \
|
||||
"$pwd/subdir/x1.txt" 143 \
|
||||
"$pwd/subdir/x2.txt" 153 \
|
||||
]
|
||||
do_execsql_test 3.6 {
|
||||
SELECT path, size FROM fstree WHERE path LIKE $pwd || '/subdir/%' ORDER BY 1
|
||||
SELECT path, size FROM fstree
|
||||
WHERE path LIKE $pwd || '/subdir/%' ORDER BY 1
|
||||
} [list \
|
||||
"$pwd/subdir/x1.txt" 143 \
|
||||
"$pwd/subdir/x2.txt" 153 \
|
||||
@ -234,6 +237,7 @@ if {$tcl_platform(platform)!="windows" || \
|
||||
do_execsql_test 3.8 {
|
||||
SELECT size FROM fstree WHERE path = $pwd || '/subdir/x1.txt'
|
||||
} 143
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -424,6 +424,7 @@ proc subreport {title where showFrag} {
|
||||
# avg_payload: Average payload per btree entry.
|
||||
# avg_fanout: Average fanout for internal pages.
|
||||
# avg_unused: Average unused bytes per btree entry.
|
||||
# avg_meta: Average metadata overhead per entry.
|
||||
# ovfl_cnt_percent: Percentage of btree entries that use overflow pages.
|
||||
#
|
||||
set total_pages [expr {$leaf_pages+$int_pages+$ovfl_pages}]
|
||||
@ -433,6 +434,10 @@ proc subreport {title where showFrag} {
|
||||
set total_unused [expr {$ovfl_unused+$int_unused+$leaf_unused}]
|
||||
set avg_payload [divide $payload $nentry]
|
||||
set avg_unused [divide $total_unused $nentry]
|
||||
set total_meta [expr {$storage - $payload - $total_unused}]
|
||||
set total_meta [expr {$total_meta + 4*($ovfl_pages - $ovfl_cnt)}]
|
||||
set meta_percent [percent $total_meta $storage {of metadata}]
|
||||
set avg_meta [divide $total_meta $nentry]
|
||||
if {$int_pages>0} {
|
||||
# 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
|
||||
|
Loading…
Reference in New Issue
Block a user