mirror of https://github.com/sqlite/sqlite
Merge in trunk enhancements.
FossilOrigin-Name: f8bbb608cbf6c245628e3d362e9181fb3dc402b1d8241bcb687caf395f63c916
This commit is contained in:
commit
0282c03abc
|
@ -23,6 +23,26 @@ or embedded controllers.</p>
|
|||
<p>This document is an introduction to the Lemon
|
||||
parser generator.</p>
|
||||
|
||||
<h2>Security Note</h2>
|
||||
|
||||
<p>The language parser code created by Lemon is very robust and
|
||||
is well-suited for use in internet-facing applications that need to
|
||||
safely process maliciously crafted inputs.
|
||||
|
||||
<p>The "lemon.exe" command-line tool itself works great when given a valid
|
||||
input grammar file and almost always gives helpful
|
||||
error messages for malformed inputs. However, it is possible for
|
||||
a malicious user to craft a grammar file that will cause
|
||||
lemon.exe to crash.
|
||||
We do not see this as a problem, as lemon.exe is not intended to be used
|
||||
with hostile inputs.
|
||||
To summarize:</p>
|
||||
|
||||
<ul>
|
||||
<li>Parser code generated by lemon → Robust and secure
|
||||
<li>The "lemon.exe" command line tool itself → Not so much
|
||||
</ul>
|
||||
|
||||
<h2>Theory of Operation</h2>
|
||||
|
||||
<p>The main goal of Lemon is to translate a context free grammar (CFG)
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
** The generate_series "function" is really a virtual table with the
|
||||
** following schema:
|
||||
**
|
||||
** CREATE FUNCTION generate_series(
|
||||
** CREATE TABLE generate_series(
|
||||
** value,
|
||||
** start HIDDEN,
|
||||
** stop HIDDEN,
|
||||
|
|
35
manifest
35
manifest
|
@ -1,5 +1,5 @@
|
|||
C Add\sinterfaces\ssqlite3_prepare_v3()\sand\ssqlite3_prepare16_v3()\swith\sthe\nextra\sprepFlags\sargument.\s\sAdd\sthe\sSQLITE_PREPARE_PERSISTENT\soption\sas\sone\nbit\sin\sthat\sargument.
|
||||
D 2017-06-01T00:54:35.692
|
||||
C Merge\sin\strunk\senhancements.
|
||||
D 2017-06-07T16:25:25.048
|
||||
F Makefile.in 1cc758ce3374a32425e4d130c2fe7b026b20de5b8843243de75f087c0a2661fb
|
||||
F Makefile.linux-gcc 7bc79876b875010e8c8f9502eb935ca92aa3c434
|
||||
F Makefile.msc 8eeb80162074004e906b53d7340a12a14c471a83743aab975947e95ce061efcc
|
||||
|
@ -33,7 +33,7 @@ F config.sub 9ebe4c3b3dab6431ece34f16828b594fb420da55
|
|||
F configure 1bcc61cdd063171d8945551c265e5701a770deeff77e0ad634f8d22e4e91c831 x
|
||||
F configure.ac 13f45f02e6c51dd0e347315b5401c3f047712b7f79b7f35619115c23755afcff
|
||||
F contrib/sqlitecon.tcl 210a913ad63f9f991070821e599d600bd913e0ad
|
||||
F doc/lemon.html b5a3c07d33ecb8e019ce8f7660fe2dbbad9d7977
|
||||
F doc/lemon.html 1f8b8d4c9f5cfe40e679fee279cc9eb2da8e6eb74ad406028538d7864cc4b6cb
|
||||
F doc/pager-invariants.txt 27fed9a70ddad2088750c4a2b493b63853da2710
|
||||
F doc/vfs-shm.txt e101f27ea02a8387ce46a05be2b1a902a021d37a
|
||||
F ext/README.md fd5f78013b0a2bc6f0067afb19e6ad040e89a10179b4f6f03eee58fac5f169bd
|
||||
|
@ -228,7 +228,7 @@ F ext/misc/regexp.c a68d25c659bd2d893cd1215667bbf75ecb9dc7d4
|
|||
F ext/misc/remember.c 8440f8d0b452c5cdefb62b57135ccd1267aa729d
|
||||
F ext/misc/rot13.c 1ac6f95f99b575907b9b09c81a349114cf9be45a
|
||||
F ext/misc/scrub.c 1c5bfb8b0cd18b602fcb55755e84abf0023ac2fb
|
||||
F ext/misc/series.c e11e534ada797d5b816d7e7a93c022306563ca35
|
||||
F ext/misc/series.c b0f5f346aca9b7ff7caaf0da2efb4ad462441abd4dcd92a460cb573b3ea2370b
|
||||
F ext/misc/sha1.c 0b9e9b855354910d3ca467bf39099d570e73db56
|
||||
F ext/misc/shathree.c fa185d7aee0ad0aca5e091b4a2db7baff11796170e5793b5de99e511a13af448
|
||||
F ext/misc/showauth.c 732578f0fe4ce42d577e1c86dc89dd14a006ab52
|
||||
|
@ -351,13 +351,13 @@ F src/btmutex.c 0e9ce2d56159b89b9bc8e197e023ee11e39ff8ca
|
|||
F src/btree.c 8ac6ae352c63998228718b5f11faa0da2676710898a47284de78e96cb41dca35
|
||||
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
|
||||
F src/date.c cc42a41c7422389860d40419a5e3bce5eaf6e7835c3ba2677751dc653550a5c7
|
||||
F src/dbstat.c 19ee7a4e89979d4df8e44cfac7a8f905ec89b77d
|
||||
F src/delete.c bc2bfc227002ebe091e7cc321f09a48e52f86e1cd5ff9b028009db0671556749
|
||||
F src/delete.c 3213547e97b676c6fa79948b7a9ede4801ea04a01a2043241deafedf132ecf5d
|
||||
F src/expr.c 452c6f3aa656aabf3eefe96bb5f316b2c987fbc12c647964e4ed880f193ca31f
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c db65492ae549c3b548c9ef1f279ce1684f1c473b116e1c56a90878cd5dcf968d
|
||||
|
@ -367,7 +367,7 @@ 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
|
||||
|
@ -406,7 +406,7 @@ 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/shell.c eca7e7fe8dae859aa56e462c5b35c735976fa1e5e1d7a90fd5a32aa4615c1825
|
||||
F src/sqlite.h.in e644e75c21ab6247ebd8bca07a999d7d108fe8be00adc14d8c4b6004f1e75c34
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 58fd0676d3111d02e62e5a35992a7d3da5d3f88753acc174f2d37b774fbbdd28
|
||||
|
@ -434,7 +434,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
|
||||
|
@ -472,7 +472,7 @@ F src/update.c c443935c652af9365e033f756550b5032d02e1b06eb2cb890ed7511ae0c051dc
|
|||
F src/utf.c 699001c79f28e48e9bcdf8a463da029ea660540c
|
||||
F src/util.c fc081ec6f63448dcd80d3dfad35baecfa104823254a815b081a4d9fe76e1db23
|
||||
F src/vacuum.c 1fe4555cd8c9b263afb85b5b4ee3a4a4181ad569
|
||||
F src/vdbe.c cce462ad3cf1cad1944e105f773712a979e23fbe302328dc2885b0f4a612e1f6
|
||||
F src/vdbe.c 6783778df820f3e0c68289fe5d12ddd34168b0eb1acf248668fec6e2ea1012d5
|
||||
F src/vdbe.h dde459b1e8a02b8445ecfd5959f38cd5ebb6b0ad392d491d8b159ac8193d231a
|
||||
F src/vdbeInt.h ba7a9436196e693cc51532138ba9431af2716efbc1afe62b03b2724a67853764
|
||||
F src/vdbeapi.c 899d8f021c89ab348708b3a9b00b855f5ecc3c0f949a75359a61a3c621021281
|
||||
|
@ -488,7 +488,7 @@ F src/wal.h 06b2a0b599cc0f53ea97f497cf8c6b758c999f71
|
|||
F src/walker.c d46044e7a5842560dfe7122d93ff5145dd4a96f4d0bf5ba5910a7731b8c01e79
|
||||
F src/where.c 67f98714b07ec3c1d5e033a63d23c0fd70c24861b7b46b69b10700f22dca6ffe
|
||||
F src/whereInt.h 2a4b634d63ce488b46d4b0da8f2eaa8f9aeab202bc25ef76f007de5e3fba1f20
|
||||
F src/wherecode.c 8ad48867660519e262a401720845dc76934f86f558ec9606335fafcd7a2554f8
|
||||
F src/wherecode.c 339ee802d9d311acf0cba8b5a9a092e167ef71c3a777d4b3e57de25d193251c7
|
||||
F src/whereexpr.c a2fe3811d45af45a5c6667caabc15e01054fe6228c64e86e1f7d2ba5ef5284f9
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
|
@ -925,7 +925,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 b9a9822dda05a1aa481215a52e2fc93cd8b22ee5
|
||||
F test/kvtest.c d2b8cfc91047ebf6cac4f3a04f19c3a864e4ecfd683bbb65c395df450b8dc79c
|
||||
F test/lastinsert.test 42e948fd6442f07d60acbd15d33fb86473e0ef63
|
||||
F test/laststmtchanges.test ae613f53819206b3222771828d024154d51db200
|
||||
F test/like.test 0603f4fa0dad50987f70032c05800cbfa8985302
|
||||
|
@ -1416,7 +1416,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
|
||||
|
@ -1582,10 +1582,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 c26cf978eead1c9d265eddabaa421e7735b472fcf2792cd2bdeb0901bcf3fb44
|
||||
R 920bebc856e573465ecc651ded3907f5
|
||||
T *branch * prepare_v3
|
||||
T *sym-prepare_v3 *
|
||||
T -sym-trunk *
|
||||
P 4a25c5883380fe5990d8180adb58c3bdc7a3d081bc4c69cd4de3cd57074fb251 234ede26e30f20e6c33002739ed8be35dbfb5c77700bd857ff31072b9b7df347
|
||||
R 0957dad5bbe072099826cf9a4a9a3885
|
||||
U drh
|
||||
Z a2627d28422eb06f03507d6701dbaec5
|
||||
Z 6991b6c9f5798221e32381bb8967907a
|
||||
|
|
|
@ -1 +1 @@
|
|||
4a25c5883380fe5990d8180adb58c3bdc7a3d081bc4c69cd4de3cd57074fb251
|
||||
f8bbb608cbf6c245628e3d362e9181fb3dc402b1d8241bcb687caf395f63c916
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -459,7 +459,7 @@ void sqlite3DeleteFrom(
|
|||
sqlite3VdbeAddOp4Int(v, OP_IdxInsert, iEphCur, iKey, iPk, nPk);
|
||||
}else{
|
||||
/* Add the rowid of the row to be deleted to the RowSet */
|
||||
nKey = 1; /* OP_Seek always uses a single rowid */
|
||||
nKey = 1; /* OP_DeferredSeek always uses a single rowid */
|
||||
sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, iKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
20
src/shell.c
20
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
|
||||
|
@ -1917,9 +1929,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 +1966,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 ){
|
||||
|
|
|
@ -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);
|
||||
|
|
20
src/vdbe.c
20
src/vdbe.c
|
@ -2497,7 +2497,9 @@ case OP_Column: {
|
|||
pC = p->apCsr[pOp->p1];
|
||||
p2 = pOp->p2;
|
||||
|
||||
/* If the cursor cache is stale, bring it up-to-date */
|
||||
/* If the cursor cache is stale (meaning it is not currently point at
|
||||
** the correct row) then bring it up-to-date by doing the necessary
|
||||
** B-Tree seek. */
|
||||
rc = sqlite3VdbeCursorMoveto(&pC, &p2);
|
||||
if( rc ) goto abort_due_to_error;
|
||||
|
||||
|
@ -5265,8 +5267,8 @@ case OP_IdxDelete: {
|
|||
break;
|
||||
}
|
||||
|
||||
/* Opcode: Seek P1 * P3 P4 *
|
||||
** Synopsis: Move P3 to P1.rowid
|
||||
/* Opcode: DeferredSeek P1 * P3 P4 *
|
||||
** Synopsis: Move P3 to P1.rowid if needed
|
||||
**
|
||||
** P1 is an open index cursor and P3 is a cursor on the corresponding
|
||||
** table. This opcode does a deferred seek of the P3 table cursor
|
||||
|
@ -5293,11 +5295,11 @@ case OP_IdxDelete: {
|
|||
**
|
||||
** See also: Rowid, MakeRecord.
|
||||
*/
|
||||
case OP_Seek:
|
||||
case OP_IdxRowid: { /* out2 */
|
||||
VdbeCursor *pC; /* The P1 index cursor */
|
||||
VdbeCursor *pTabCur; /* The P2 table cursor (OP_Seek only) */
|
||||
i64 rowid; /* Rowid that P1 current points to */
|
||||
case OP_DeferredSeek:
|
||||
case OP_IdxRowid: { /* out2 */
|
||||
VdbeCursor *pC; /* The P1 index cursor */
|
||||
VdbeCursor *pTabCur; /* The P2 table cursor (OP_DeferredSeek only) */
|
||||
i64 rowid; /* Rowid that P1 current points to */
|
||||
|
||||
assert( pOp->p1>=0 && pOp->p1<p->nCursor );
|
||||
pC = p->apCsr[pOp->p1];
|
||||
|
@ -5323,7 +5325,7 @@ case OP_IdxRowid: { /* out2 */
|
|||
if( rc!=SQLITE_OK ){
|
||||
goto abort_due_to_error;
|
||||
}
|
||||
if( pOp->opcode==OP_Seek ){
|
||||
if( pOp->opcode==OP_DeferredSeek ){
|
||||
assert( pOp->p3>=0 && pOp->p3<p->nCursor );
|
||||
pTabCur = p->apCsr[pOp->p3];
|
||||
assert( pTabCur!=0 );
|
||||
|
|
|
@ -966,10 +966,10 @@ static void codeCursorHint(
|
|||
**
|
||||
** Normally, this is just:
|
||||
**
|
||||
** OP_Seek $iCur $iRowid
|
||||
** OP_DeferredSeek $iCur $iRowid
|
||||
**
|
||||
** However, if the scan currently being coded is a branch of an OR-loop and
|
||||
** the statement currently being coded is a SELECT, then P3 of the OP_Seek
|
||||
** the statement currently being coded is a SELECT, then P3 of OP_DeferredSeek
|
||||
** is set to iIdxCur and P4 is set to point to an array of integers
|
||||
** containing one entry for each column of the table cursor iCur is open
|
||||
** on. For each table column, if the column is the i'th column of the
|
||||
|
@ -988,7 +988,7 @@ static void codeDeferredSeek(
|
|||
assert( iIdxCur>0 );
|
||||
assert( pIdx->aiColumn[pIdx->nColumn-1]==-1 );
|
||||
|
||||
sqlite3VdbeAddOp3(v, OP_Seek, iIdxCur, 0, iCur);
|
||||
sqlite3VdbeAddOp3(v, OP_DeferredSeek, iIdxCur, 0, iCur);
|
||||
if( (pWInfo->wctrlFlags & WHERE_OR_SUBCLAUSE)
|
||||
&& DbMaskAllZero(sqlite3ParseToplevel(pParse)->writeMask)
|
||||
){
|
||||
|
|
441
test/kvtest.c
441
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"
|
||||
|
@ -90,12 +97,18 @@ static const char zHelp[] =
|
|||
" --cache-size N Database cache size\n"
|
||||
" --count N Read N blobs\n"
|
||||
" --desc Read blobs in descending order\n"
|
||||
" --fsync Synchronous file writes\n"
|
||||
" --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 Do an overwrite test\n"
|
||||
;
|
||||
|
||||
/* Reference resources used */
|
||||
|
@ -111,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)
|
||||
|
@ -118,6 +132,31 @@ static const char zHelp[] =
|
|||
# define access _access
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*
|
||||
** The following macros are used to cast pointers to integers and
|
||||
** integers to pointers. The way you do this varies from one compiler
|
||||
** to the next, so we have developed the following set of #if statements
|
||||
** to generate appropriate macros for a wide range of compilers.
|
||||
**
|
||||
** The correct "ANSI" way to do this is to use the intptr_t type.
|
||||
** Unfortunately, that typedef is not available on all compilers, or
|
||||
** if it is available, it requires an #include of specific headers
|
||||
** that vary from one machine to the next.
|
||||
**
|
||||
** Ticket #3860: The llvm-gcc-4.2 compiler from Apple chokes on
|
||||
** the ((void*)&((char*)0)[X]) construct. But MSVC chokes on ((void*)(X)).
|
||||
** So we have to define the macros in different ways depending on the
|
||||
** compiler.
|
||||
*/
|
||||
#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))
|
||||
#else
|
||||
# define SQLITE_INT_TO_PTR(X) ((void*)(intptr_t)(X))
|
||||
# define SQLITE_PTR_TO_INT(X) ((sqlite3_int64)(intptr_t)(X))
|
||||
#endif
|
||||
|
||||
/*
|
||||
** Show thqe help text and quit.
|
||||
|
@ -201,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){
|
||||
|
@ -217,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;
|
||||
}
|
||||
|
@ -328,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 );
|
||||
|
@ -336,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"
|
||||
|
@ -374,39 +441,53 @@ 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.
|
||||
** remember(V,PTR)
|
||||
**
|
||||
** Return the integer value V. Also save the value of V in a
|
||||
** C-language variable whose address is PTR.
|
||||
*/
|
||||
static void writefileFunc(
|
||||
sqlite3_context *context,
|
||||
static void rememberFunc(
|
||||
sqlite3_context *pCtx,
|
||||
int argc,
|
||||
sqlite3_value **argv
|
||||
){
|
||||
FILE *out;
|
||||
const char *z;
|
||||
sqlite3_int64 rc;
|
||||
const char *zFile;
|
||||
sqlite3_int64 v;
|
||||
sqlite3_int64 ptr;
|
||||
assert( argc==2 );
|
||||
v = sqlite3_value_int64(argv[0]);
|
||||
ptr = sqlite3_value_int64(argv[1]);
|
||||
*(sqlite3_int64*)SQLITE_INT_TO_PTR(ptr) = v;
|
||||
sqlite3_result_int64(pCtx, v);
|
||||
}
|
||||
|
||||
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);
|
||||
/*
|
||||
** 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
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -416,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;
|
||||
}
|
||||
|
@ -461,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 */
|
||||
|
@ -479,10 +605,55 @@ 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;
|
||||
}
|
||||
|
||||
/*
|
||||
** Overwrite a file with randomness. Do not change the size of the
|
||||
** file.
|
||||
*/
|
||||
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 */
|
||||
unsigned char *pBuf; /* Content to store on disk */
|
||||
const char *zMode = "wb"; /* Mode for fopen() */
|
||||
|
||||
sz = fileSize(zName);
|
||||
if( sz<0 ){
|
||||
fatalError("No such file: \"%s\"", zName);
|
||||
}
|
||||
*pnByte = sz;
|
||||
if( sz==0 ) return;
|
||||
pBuf = sqlite3_malloc64( sz );
|
||||
if( pBuf==0 ){
|
||||
fatalError("Cannot allocate %lld bytes\n", sz);
|
||||
}
|
||||
sqlite3_randomness((int)sz, pBuf);
|
||||
#if defined(_WIN32)
|
||||
if( doFsync ) zMode = "wbc";
|
||||
#endif
|
||||
out = fopen(zName, zMode);
|
||||
if( out==0 ){
|
||||
fatalError("Cannot open \"%s\" for writing\n", zName);
|
||||
}
|
||||
nWritten = fwrite(pBuf, 1, (size_t)sz, out);
|
||||
if( doFsync ){
|
||||
#if defined(_WIN32)
|
||||
fflush(out);
|
||||
#else
|
||||
fsync(fileno(out));
|
||||
#endif
|
||||
}
|
||||
fclose(out);
|
||||
if( nWritten!=(size_t)sz ){
|
||||
fatalError("Wrote only %d of %d bytes to \"%s\"\n",
|
||||
(int)nWritten, (int)sz, zName);
|
||||
}
|
||||
sqlite3_free(pBuf);
|
||||
}
|
||||
|
||||
/*
|
||||
** Return the current time in milliseconds since the beginning of
|
||||
** the Julian epoch.
|
||||
|
@ -637,16 +808,22 @@ static int runMain(int argc, char **argv){
|
|||
int bBlobApi = 0; /* Use the incremental blob I/O API */
|
||||
int bStats = 0; /* Print stats before exiting */
|
||||
int eOrder = ORDER_ASC; /* Access order */
|
||||
int isUpdateTest = 0; /* Do in-place updates rather than reads */
|
||||
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 */
|
||||
|
||||
|
||||
|
@ -660,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]);
|
||||
|
@ -677,44 +884,45 @@ 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 ){
|
||||
bStats = 1;
|
||||
continue;
|
||||
}
|
||||
if( strcmp(z, "-update")==0 ){
|
||||
isUpdateTest = 1;
|
||||
continue;
|
||||
}
|
||||
fatalError("unknown option: \"%s\"", argv[i]);
|
||||
}
|
||||
if( eType==PATH_DB ){
|
||||
/* Recover any prior crashes prior to starting the timer */
|
||||
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 ){
|
||||
char *zSql;
|
||||
|
@ -724,9 +932,13 @@ static int runMain(int argc, char **argv){
|
|||
}
|
||||
zSql = sqlite3_mprintf("PRAGMA mmap_size=%d", mmapSize);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
zSql = sqlite3_mprintf("PRAGMA cache_size=%d", iCache);
|
||||
sqlite3_exec(db, zSql, 0, 0, 0);
|
||||
sqlite3_free(zSql);
|
||||
if( noSync ){
|
||||
sqlite3_exec(db, "PRAGMA synchronous=OFF", 0, 0, 0);
|
||||
}
|
||||
pStmt = 0;
|
||||
sqlite3_prepare_v2(db, "PRAGMA page_size", -1, &pStmt, 0);
|
||||
if( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
|
@ -745,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 ){
|
||||
|
@ -761,22 +976,32 @@ 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;
|
||||
zKey = sqlite3_mprintf("%s/%06d", zDb, iKey);
|
||||
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;
|
||||
pData = readFile(zKey, &nData);
|
||||
if( isUpdateTest ){
|
||||
updateFile(zKey, &nData, doFsync);
|
||||
}else{
|
||||
pData = readFile(zKey, &nData);
|
||||
sqlite3_free(pData);
|
||||
}
|
||||
sqlite3_free(zKey);
|
||||
sqlite3_free(pData);
|
||||
}else if( bBlobApi ){
|
||||
/* CASE 2: Reading from database using the incremental BLOB I/O API */
|
||||
if( pBlob==0 ){
|
||||
rc = sqlite3_blob_open(db, "main", "kv", "v", iKey, 0, &pBlob);
|
||||
rc = sqlite3_blob_open(db, "main", "kv", "v", iKey,
|
||||
isUpdateTest, &pBlob);
|
||||
if( rc ){
|
||||
fatalError("could not open sqlite3_blob handle: %s",
|
||||
sqlite3_errmsg(db));
|
||||
|
@ -788,20 +1013,39 @@ 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);
|
||||
rc = sqlite3_blob_read(pBlob, pData, nData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fatalError("could not read the blob at %d: %s", iKey,
|
||||
sqlite3_errmsg(db));
|
||||
if( isUpdateTest ){
|
||||
sqlite3_randomness((int)nData, pData);
|
||||
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, (int)nData, 0);
|
||||
if( rc!=SQLITE_OK ){
|
||||
fatalError("could not read the blob at %d: %s", iKey,
|
||||
sqlite3_errmsg(db));
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
/* CASE 3: Reading from database using SQL */
|
||||
if( pStmt==0 ){
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0);
|
||||
if( isUpdateTest ){
|
||||
sqlite3_create_function(db, "remember", 2, SQLITE_UTF8, 0,
|
||||
rememberFunc, 0, 0);
|
||||
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"UPDATE kv SET v=randomblob(remember(length(v),?2))"
|
||||
" WHERE k=?1", -1, &pStmt, 0);
|
||||
sqlite3_bind_int64(pStmt, 2, SQLITE_PTR_TO_INT(&nData));
|
||||
}else{
|
||||
rc = sqlite3_prepare_v2(db,
|
||||
"SELECT v FROM kv WHERE k=?1", -1, &pStmt, 0);
|
||||
}
|
||||
if( rc ){
|
||||
fatalError("cannot prepare query: %s", sqlite3_errmsg(db));
|
||||
}
|
||||
|
@ -809,12 +1053,11 @@ static int runMain(int argc, char **argv){
|
|||
sqlite3_reset(pStmt);
|
||||
}
|
||||
sqlite3_bind_int(pStmt, 1, iKey);
|
||||
nData = 0;
|
||||
rc = sqlite3_step(pStmt);
|
||||
if( rc==SQLITE_ROW ){
|
||||
nData = sqlite3_column_bytes(pStmt, 0);
|
||||
pData = (unsigned char*)sqlite3_column_blob(pStmt, 0);
|
||||
}else{
|
||||
nData = 0;
|
||||
}
|
||||
}
|
||||
if( eOrder==ORDER_ASC ){
|
||||
|
@ -835,13 +1078,33 @@ static int runMain(int argc, char **argv){
|
|||
if( bStats ){
|
||||
display_stats(db, 0);
|
||||
}
|
||||
if( db ) sqlite3_close(db);
|
||||
if( db ){
|
||||
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);
|
||||
}
|
||||
if( eType==PATH_DB ){
|
||||
printf("SQLite version: %s\n", sqlite3_libversion());
|
||||
if( doIntegrityCk ){
|
||||
sqlite3_open(zDb, &db);
|
||||
sqlite3_prepare_v2(db, "PRAGMA integrity_check", -1, &pStmt, 0);
|
||||
while( sqlite3_step(pStmt)==SQLITE_ROW ){
|
||||
printf("integrity-check: %s\n", sqlite3_column_text(pStmt, 0));
|
||||
}
|
||||
sqlite3_finalize(pStmt);
|
||||
sqlite3_close(db);
|
||||
db = 0;
|
||||
}
|
||||
}
|
||||
printf("--count %d --max-id %d", nCount-nExtra, iMax);
|
||||
switch( eOrder ){
|
||||
|
@ -852,11 +1115,17 @@ static int runMain(int argc, char **argv){
|
|||
if( eType==PATH_DB ){
|
||||
printf("--cache-size %d --jmode %s\n", iCache, zJMode);
|
||||
printf("--mmap %d%s\n", mmapSize, bBlobApi ? " --blob-api" : "");
|
||||
if( noSync ) printf("--nosync\n");
|
||||
}
|
||||
if( iPagesize ) printf("Database page size: %d\n", iPagesize);
|
||||
printf("Total elapsed time: %.3f\n", tmElapsed/1000.0);
|
||||
printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
|
||||
printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
|
||||
if( isUpdateTest ){
|
||||
printf("Microseconds per BLOB write: %.3f\n", tmElapsed*1000.0/nCount);
|
||||
printf("Content write rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
|
||||
}else{
|
||||
printf("Microseconds per BLOB read: %.3f\n", tmElapsed*1000.0/nCount);
|
||||
printf("Content read rate: %.1f MB/s\n", nTotal/(1000.0*tmElapsed));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -216,24 +216,28 @@ if {$tcl_platform(platform)!="windows" || \
|
|||
} {}
|
||||
|
||||
set pwd [pwd]
|
||||
do_execsql_test 3.5 {
|
||||
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
|
||||
} [list \
|
||||
"$pwd/subdir/x1.txt" 143 \
|
||||
"$pwd/subdir/x2.txt" 153 \
|
||||
]
|
||||
do_execsql_test 3.7 {
|
||||
SELECT sum(size) FROM fstree WHERE path LIKE $pwd || '/subdir/%'
|
||||
} 296
|
||||
do_execsql_test 3.8 {
|
||||
SELECT size FROM fstree WHERE path = $pwd || '/subdir/x1.txt'
|
||||
} 143
|
||||
if {![string match {*[_%]*} $pwd]} {
|
||||
do_execsql_test 3.5 {
|
||||
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
|
||||
} [list \
|
||||
"$pwd/subdir/x1.txt" 143 \
|
||||
"$pwd/subdir/x2.txt" 153 \
|
||||
]
|
||||
do_execsql_test 3.7 {
|
||||
SELECT sum(size) FROM fstree WHERE path LIKE $pwd || '/subdir/%'
|
||||
} 296
|
||||
do_execsql_test 3.8 {
|
||||
SELECT size FROM fstree WHERE path = $pwd || '/subdir/x1.txt'
|
||||
} 143
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue