Begin reengineering the EXPLAIN QUERY PLAN function to provide more
intuitive output. FossilOrigin-Name: 70b48a7972dfbb44af3ccd8ccd830e984bec88d80a78b3566a5de86a16e7fc14
This commit is contained in:
parent
8c2e6c5fe0
commit
e2ca99c907
27
manifest
27
manifest
@ -1,5 +1,5 @@
|
||||
C The\sSQLITE_ALLOW_SQLITE_MASTER_INDEX\scompile-time\soption\sallows\sa\sCREATE\sINDEX\nstatement\sagainst\sthe\ssqlite_master\stable.\s\sOnce\screated,\sthe\sindex\sworks,\sand\nis\susable\sby\slegacy\sinstances\sof\sSQLite.
|
||||
D 2018-05-01T18:39:31.984
|
||||
C Begin\sreengineering\sthe\sEXPLAIN\sQUERY\sPLAN\sfunction\sto\sprovide\smore\nintuitive\soutput.
|
||||
D 2018-05-02T00:33:43.157
|
||||
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
|
||||
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
|
||||
F Makefile.in 5ce9343cba9c189046f1afe6d2bcc1f68079439febc05267b98aec6ecc752439
|
||||
@ -445,7 +445,7 @@ F src/date.c ebe1dc7c8a347117bb02570f1a931c62dd78f4a2b1b516f4837d45b7d6426957
|
||||
F src/dbpage.c 8db4c97f630e7d83f884ea75caf1ffd0988c160e9d530194d93721c80821e0f6
|
||||
F src/dbstat.c edabb82611143727511a45ca0859b8cd037851ebe756ae3db289859dd18b6f91
|
||||
F src/delete.c b0f90749e22d5e41a12dbf940f4811138cf97da54b46b737089b93eb64a2896f
|
||||
F src/expr.c 5c9a6dc7f56d245d7bb82ba36fdba2ca3bdd1881b61ee60293dd98c766742cd9
|
||||
F src/expr.c 1757b6896ac542df8a90c0e00badc752abd90e04a852b7a947e33e94ee1fb46d
|
||||
F src/fault.c 460f3e55994363812d9d60844b2a6de88826e007
|
||||
F src/fkey.c d617daf66b5515e2b42c1405b2b4984c30ca50fb705ab164271a9bf66c69e331
|
||||
F src/func.c 94f42cba2cc1c34aeaa441022ba0170ec3fec4bba54db4e0ded085c6dc0fdc51
|
||||
@ -493,12 +493,12 @@ F src/printf.c d3b7844ddeb11fbbdd38dd84d09c9c1ac171d21fb038473c3aa97981201cc660
|
||||
F src/random.c 80f5d666f23feb3e6665a6ce04c7197212a88384
|
||||
F src/resolve.c 6415381a0e9d22c0e7cba33ca4a53f81474190862f5d4838190f5eb5b0b47bc9
|
||||
F src/rowset.c 7b7e7e479212e65b723bf40128c7b36dc5afdfac
|
||||
F src/select.c daf07d8defce3311f9e69f1280a874d78bc1d16c305f6aa689640f7afa02842f
|
||||
F src/shell.c.in 54b902ab7d840f60ddfabc13124c85d4980342c88aff7679f2cc25f67c21ade7
|
||||
F src/select.c 8481fa6ec8bb3b24465da8e81438d4d85056d69232f501c153ab8402409c8619
|
||||
F src/shell.c.in 29309f2ab656c8817fbc3b7910b9af8464557b91cba75277a03669399c8e2730
|
||||
F src/sqlite.h.in d669de545f18f2f01362de02e309cd7f15185958c71bac8f53cd5438b46d2bea
|
||||
F src/sqlite3.rc 5121c9e10c3964d5755191c80dd1180c122fc3a8
|
||||
F src/sqlite3ext.h 83a3c4ce93d650bedfd1aa558cb85a516bd6d094445ee989740827d0d944368d
|
||||
F src/sqliteInt.h 8ac0138eae10337b745b03dad0124fd63ae911c0503e795729503e7fc6234d57
|
||||
F src/sqliteInt.h 337e4fe0a9e3bef575699bd0063ac2dcc31b73c905206d5425d442fe7fbb2798
|
||||
F src/sqliteLimit.h 1513bfb7b20378aa0041e7022d04acb73525de35b80b252f1b83fedb4de6a76b
|
||||
F src/status.c 46e7aec11f79dad50965a5ca5fa9de009f7d6bde08be2156f1538a0a296d4d0e
|
||||
F src/table.c b46ad567748f24a326d9de40e5b9659f96ffff34
|
||||
@ -564,10 +564,10 @@ F src/utf.c 810fbfebe12359f10bc2a011520a6e10879ab2a163bcb26c74768eab82ea62a5
|
||||
F src/util.c d9eb0a6c4aae1b00a7369eadd7ca0bbe946cb4c953b6751aa20d357c2f482157
|
||||
F src/vacuum.c 762ee9bbf8733d87d8cd06f58d950e881982e416f8c767334a40ffd341b6bff5
|
||||
F src/vdbe.c 066a4e1de2ed83e253adfd2e97a684cf562eaa41d31ee7f3d3e4c8aea4485a55
|
||||
F src/vdbe.h 574ce9a0d57b026fc93ac379a339b8d391977f335ab4176a7e21ba902e9184bd
|
||||
F src/vdbe.h dc29bf94721964da367debfc4472f5546e910b26f66850a8936ae4db174699d8
|
||||
F src/vdbeInt.h 95f7adfdc5c8f1353321f55a6c5ec00a90877e3b85af5159e393afb41ff54110
|
||||
F src/vdbeapi.c 29d2baf9c1233131ec467d7bed1b7c8a03c27579048d768c4b04acf427838858
|
||||
F src/vdbeaux.c 944bae5207bbce456c466d01dcf2aac3ad49c957325d35c0ba7de882d3e5c25c
|
||||
F src/vdbeaux.c 58129ae46be079613df5c2c3714d80a78605415bfa10c9528634c7c2d2147727
|
||||
F src/vdbeblob.c f5c70f973ea3a9e915d1693278a5f890dc78594300cf4d54e64f2b0917c94191
|
||||
F src/vdbemem.c 0cbe9b9560e42b72983cf9e1bceba48f297e51142bfb6b57f3747cf60106b92d
|
||||
F src/vdbesort.c 731a09e5cb9e96b70c394c1b7cf3860fbe84acca7682e178615eb941a3a0ef2f
|
||||
@ -579,7 +579,7 @@ F src/wal.h 8de5d2d3de0956d6f6cb48c83a4012d5f227b8fe940f3a349a4b7e85ebcb492a
|
||||
F src/walker.c da987a20d40145c0a03c07d8fefcb2ed363becc7680d0500d9c79915591f5b1f
|
||||
F src/where.c 7a1c5555c00bcf49c677472ae83bb49bf24c8d8e9a060d475e86dee39be2fb3a
|
||||
F src/whereInt.h 2610cb87dd95509995b63decc674c60f2757697a206cfe0c085ee53d9c43cfff
|
||||
F src/wherecode.c 3cb591fe7323e1bfd49059d75fcab75e383dfcf16a51c0e4f9368ac1b93e291b
|
||||
F src/wherecode.c 13b831d258ab8468cb0469a882f0778632b55d787f329751e50d92b8133ea594
|
||||
F src/whereexpr.c e90b2e76dcabc81edff56633bf281bc01d93b71e0c81482dc06925ce39f5844a
|
||||
F test/8_3_names.test ebbb5cd36741350040fd28b432ceadf495be25b2
|
||||
F test/affinity2.test a6d901b436328bd67a79b41bb0ac2663918fe3bd
|
||||
@ -1727,7 +1727,10 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
|
||||
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
|
||||
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
|
||||
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
|
||||
P 08665a9e2e50a0a1e62529884cf65f8090debe89a306a3904b53268729ab5ad5
|
||||
R 65d0d1f1b897970475b65cf976ea92c3
|
||||
P 853f3163597b9946c0cbeb808ea6fd33a0cf48ae6b8f4459c4165db377f33a9e
|
||||
R 4157eb92d30294276c327d965c794c5a
|
||||
T *branch * rework-EQP
|
||||
T *sym-rework-EQP *
|
||||
T -sym-trunk *
|
||||
U drh
|
||||
Z fbedda4f45c8a34310211b3e1037138b
|
||||
Z 60e64f1bce0fa6f0f61a0e5e536f0823
|
||||
|
@ -1 +1 @@
|
||||
853f3163597b9946c0cbeb808ea6fd33a0cf48ae6b8f4459c4165db377f33a9e
|
||||
70b48a7972dfbb44af3ccd8ccd830e984bec88d80a78b3566a5de86a16e7fc14
|
36
src/expr.c
36
src/expr.c
@ -2410,11 +2410,8 @@ int sqlite3FindInIndex(
|
||||
if( colUsed==(MASKBIT(nExpr)-1) ){
|
||||
/* If we reach this point, that means the index pIdx is usable */
|
||||
int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
|
||||
sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
|
||||
P4_DYNAMIC);
|
||||
#endif
|
||||
ExplainQueryPlan((pParse, 0,
|
||||
"USING INDEX %s FOR IN-OPERATOR",pIdx->zName));
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
@ -2646,17 +2643,10 @@ int sqlite3CodeSubselect(
|
||||
Select *pSelect = pExpr->x.pSelect;
|
||||
ExprList *pEList = pSelect->pEList;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %sLIST SUBQUERY %d",
|
||||
jmpIfDynamic>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "
|
||||
));
|
||||
ExplainQueryPlanSetId(pParse, pSelect);
|
||||
assert( !isRowid );
|
||||
/* If the LHS and RHS of the IN operator do not match, that
|
||||
** error will have been caught long before we reach this point. */
|
||||
@ -2777,18 +2767,10 @@ int sqlite3CodeSubselect(
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %sSCALAR SUBQUERY %d",
|
||||
jmpIfDynamic>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
pSel = pExpr->x.pSelect;
|
||||
ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "));
|
||||
ExplainQueryPlanSetId(pParse, pSel);
|
||||
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
|
||||
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
|
||||
pParse->nMem += nReg;
|
||||
|
94
src/select.c
94
src/select.c
@ -21,7 +21,7 @@
|
||||
/***/ int sqlite3SelectTrace = 0;
|
||||
# define SELECTTRACE(K,P,S,X) \
|
||||
if(sqlite3SelectTrace&(K)) \
|
||||
sqlite3DebugPrintf("%s/%d/%p: ",(S)->zSelName,(P)->iSelectId,(S)),\
|
||||
sqlite3DebugPrintf("%s/%d/%p: ",(S)->zSelName,(P)->addrExplain,(S)),\
|
||||
sqlite3DebugPrintf X
|
||||
#else
|
||||
# define SELECTTRACE(K,P,S,X)
|
||||
@ -147,6 +147,9 @@ Select *sqlite3SelectNew(
|
||||
pNew->iOffset = 0;
|
||||
#if SELECTTRACE_ENABLED
|
||||
pNew->zSelName[0] = 0;
|
||||
#endif
|
||||
#if SELECTTRACE_ENABLED || !defined(SQLITE_OMIT_EXPLAIN)
|
||||
pNew->iSelectId = 0;
|
||||
#endif
|
||||
pNew->addrOpenEphm[0] = -1;
|
||||
pNew->addrOpenEphm[1] = -1;
|
||||
@ -1293,11 +1296,7 @@ static const char *selectOpName(int id){
|
||||
** is determined by the zUsage argument.
|
||||
*/
|
||||
static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s", zUsage));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1321,11 +1320,10 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
|
||||
** where the caption is of one of the two forms:
|
||||
**
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
|
||||
** "COMPOSITE (op)"
|
||||
** "COMPOSITE USING TEMP B-TREE (op)"
|
||||
**
|
||||
** where iSub1 and iSub2 are the integers passed as the corresponding
|
||||
** function parameters, and op is the text representation of the parameter
|
||||
** where op is the text representation of the parameter
|
||||
** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
|
||||
** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
|
||||
** false, or the second form if it is true.
|
||||
@ -1333,23 +1331,15 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
static void explainComposite(
|
||||
Parse *pParse, /* Parse context */
|
||||
int op, /* One of TK_UNION, TK_EXCEPT etc. */
|
||||
int iSub1, /* Subquery id 1 */
|
||||
int iSub2, /* Subquery id 2 */
|
||||
int bUseTmp /* True if a temp table was used */
|
||||
){
|
||||
assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
|
||||
bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
ExplainQueryPlan((pParse, 1, "COMPOUND %s(%s)",
|
||||
bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)));
|
||||
}
|
||||
#else
|
||||
/* No-op versions of the explainXXX() functions and macros. */
|
||||
# define explainComposite(v,w,x,y,z)
|
||||
# define explainComposite(v,y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
@ -2474,10 +2464,6 @@ static int multiSelect(
|
||||
SelectDest dest; /* Alternative data destination */
|
||||
Select *pDelete = 0; /* Chain of simple selects to delete */
|
||||
sqlite3 *db; /* Database connection */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1 = 0; /* EQP id of left-hand query */
|
||||
int iSub2 = 0; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
||||
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
||||
@ -2532,6 +2518,7 @@ static int multiSelect(
|
||||
|
||||
/* Generate code for the left and right SELECT statements.
|
||||
*/
|
||||
explainComposite(pParse, p->op, p->op!=TK_ALL);
|
||||
switch( p->op ){
|
||||
case TK_ALL: {
|
||||
int addr = 0;
|
||||
@ -2540,7 +2527,6 @@ static int multiSelect(
|
||||
pPrior->iLimit = p->iLimit;
|
||||
pPrior->iOffset = p->iOffset;
|
||||
pPrior->pLimit = p->pLimit;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &dest);
|
||||
p->pLimit = 0;
|
||||
if( rc ){
|
||||
@ -2557,7 +2543,6 @@ static int multiSelect(
|
||||
p->iLimit, p->iOffset+1, p->iOffset);
|
||||
}
|
||||
}
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
@ -2609,7 +2594,6 @@ static int multiSelect(
|
||||
*/
|
||||
assert( !pPrior->pOrderBy );
|
||||
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &uniondest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@ -2627,7 +2611,6 @@ static int multiSelect(
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
uniondest.eDest = op;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &uniondest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
||||
@ -2690,7 +2673,6 @@ static int multiSelect(
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
@ -2705,7 +2687,6 @@ static int multiSelect(
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
intersectdest.iSDParm = tab2;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &intersectdest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
@ -2737,7 +2718,7 @@ static int multiSelect(
|
||||
}
|
||||
}
|
||||
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
|
||||
/* Compute collating sequences used by
|
||||
** temporary tables needed to implement the compound select.
|
||||
@ -3076,10 +3057,6 @@ static int multiSelectOrderBy(
|
||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||
int nOrderBy; /* Number of terms in the ORDER BY clause */
|
||||
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1; /* EQP id of left-hand query */
|
||||
int iSub2; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
assert( p->pOrderBy!=0 );
|
||||
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
|
||||
@ -3198,6 +3175,8 @@ static int multiSelectOrderBy(
|
||||
regOutB = ++pParse->nMem;
|
||||
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
|
||||
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
|
||||
explainComposite(pParse, p->op, 0);
|
||||
|
||||
|
||||
/* Generate a coroutine to evaluate the SELECT statement to the
|
||||
** left of the compound operator - the "A" select.
|
||||
@ -3206,7 +3185,6 @@ static int multiSelectOrderBy(
|
||||
addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
|
||||
VdbeComment((v, "left SELECT"));
|
||||
pPrior->iLimit = regLimitA;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, pPrior, &destA);
|
||||
sqlite3VdbeEndCoroutine(v, regAddrA);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
@ -3221,7 +3199,6 @@ static int multiSelectOrderBy(
|
||||
savedOffset = p->iOffset;
|
||||
p->iLimit = regLimitB;
|
||||
p->iOffset = 0;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
sqlite3Select(pParse, p, &destB);
|
||||
p->iLimit = savedLimit;
|
||||
p->iOffset = savedOffset;
|
||||
@ -3333,7 +3310,7 @@ static int multiSelectOrderBy(
|
||||
|
||||
/*** TBD: Insert subroutine calls to close cursors on incomplete
|
||||
**** subqueries ****/
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, 0);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
return pParse->nErr!=0;
|
||||
}
|
||||
#endif
|
||||
@ -5121,14 +5098,11 @@ static void explainSimpleCount(
|
||||
){
|
||||
if( pParse->explain==2 ){
|
||||
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
|
||||
char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
|
||||
sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s",
|
||||
pTab->zName,
|
||||
bCover ? " USING COVERING INDEX " : "",
|
||||
bCover ? pIdx->zName : ""
|
||||
);
|
||||
sqlite3VdbeAddOp4(
|
||||
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
|
||||
);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@ -5341,22 +5315,21 @@ int sqlite3Select(
|
||||
ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
|
||||
u8 minMaxFlag; /* Flag for min/max queries */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iRestoreSelectId = pParse->iSelectId;
|
||||
pParse->iSelectId = pParse->iNextSelectId++;
|
||||
#endif
|
||||
|
||||
db = pParse->db;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( p==0 || db->mallocFailed || pParse->nErr ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
|
||||
memset(&sAggInfo, 0, sizeof(sAggInfo));
|
||||
#if SELECTTRACE_ENABLED
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
p->iSelectId = pParse->iSelectId;
|
||||
if( p->iSelectId==0 && pParse->addrExplain ){
|
||||
ExplainQueryPlan((pParse, 1, "SUBQUERY"));
|
||||
p->iSelectId = pParse->addrExplain;
|
||||
}
|
||||
#endif
|
||||
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->iSelectId));
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@ -5393,10 +5366,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get a pointer the VDBE under construction, allocating a new VDBE if one
|
||||
** does not already exist */
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto select_end;
|
||||
if( pDest->eDest==SRT_Output ){
|
||||
generateColumnNames(pParse, p);
|
||||
}
|
||||
@ -5491,11 +5460,11 @@ int sqlite3Select(
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
|
||||
if( pParse->iSelectId==0 && (sqlite3SelectTrace & 0x2000)!=0 ){
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
sqlite3VdbeExplainPop(pParse);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
@ -5607,7 +5576,8 @@ int sqlite3Select(
|
||||
VdbeComment((v, "%s", pItem->pTab->zName));
|
||||
pItem->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
ExplainQueryPlan((pParse, 1, "CO-ROUTINE %p", pSub));
|
||||
ExplainQueryPlanSetId(pParse, pSub);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->fg.viaCoroutine = 1;
|
||||
@ -5642,12 +5612,12 @@ int sqlite3Select(
|
||||
pPrior = isSelfJoinView(pTabList, pItem);
|
||||
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);
|
||||
ExplainQueryPlan((pParse, 1, "MATERIALIZE %p", pSub));
|
||||
ExplainQueryPlanSetId(pParse,pSub);
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
}
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
@ -6285,10 +6255,10 @@ select_end:
|
||||
sqlite3DbFree(db, sAggInfo.aFunc);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end processing\n"));
|
||||
if( pParse->iSelectId==0 && (sqlite3SelectTrace & 0x2000)!=0 ){
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
return rc;
|
||||
}
|
||||
|
@ -981,7 +981,8 @@ struct ExpertInfo {
|
||||
/* A single line in the EQP output */
|
||||
typedef struct EQPGraphRow EQPGraphRow;
|
||||
struct EQPGraphRow {
|
||||
int iSelectId; /* The SelectID for this row */
|
||||
int iEqpId; /* ID for this row */
|
||||
int iParentId; /* ID of the parent row */
|
||||
EQPGraphRow *pNext; /* Next row in sequence */
|
||||
char zText[1]; /* Text to display for this row */
|
||||
};
|
||||
@ -1003,6 +1004,7 @@ struct ShellState {
|
||||
sqlite3 *db; /* The database */
|
||||
u8 autoExplain; /* Automatically turn on .explain mode */
|
||||
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
||||
u8 autoEQPtest; /* autoEQP is in test mode */
|
||||
u8 statsOn; /* True to display memory stats before each finalize */
|
||||
u8 scanstatsOn; /* True to display scan stats before each finalize */
|
||||
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
|
||||
@ -1053,10 +1055,10 @@ struct ShellState {
|
||||
|
||||
/* Allowed values for ShellState.autoEQP
|
||||
*/
|
||||
#define AUTOEQP_off 0
|
||||
#define AUTOEQP_on 1
|
||||
#define AUTOEQP_trigger 2
|
||||
#define AUTOEQP_full 3
|
||||
#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
|
||||
#define AUTOEQP_on 1 /* Automatic EQP is on */
|
||||
#define AUTOEQP_trigger 2 /* On and also show plans for triggers */
|
||||
#define AUTOEQP_full 3 /* Show full EXPLAIN */
|
||||
|
||||
/* Allowed values for ShellState.openMode
|
||||
*/
|
||||
@ -1667,12 +1669,16 @@ static int wsToEol(const char *z){
|
||||
/*
|
||||
** Add a new entry to the EXPLAIN QUERY PLAN data
|
||||
*/
|
||||
static void eqp_append(ShellState *p, int iSelectId, const char *zText){
|
||||
static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
|
||||
EQPGraphRow *pNew;
|
||||
int nText = strlen30(zText);
|
||||
if( p->autoEQPtest ){
|
||||
utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
|
||||
}
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
|
||||
if( pNew==0 ) shell_out_of_memory();
|
||||
pNew->iSelectId = iSelectId;
|
||||
pNew->iEqpId = iEqpId;
|
||||
pNew->iParentId = p2;
|
||||
memcpy(pNew->zText, zText, nText+1);
|
||||
pNew->pNext = 0;
|
||||
if( p->sGraph.pLast ){
|
||||
@ -1696,46 +1702,29 @@ static void eqp_reset(ShellState *p){
|
||||
memset(&p->sGraph, 0, sizeof(p->sGraph));
|
||||
}
|
||||
|
||||
/* Return the next EXPLAIN QUERY PLAN line with iSelectId that occurs after
|
||||
/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
|
||||
** pOld, or return the first such line if pOld is NULL
|
||||
*/
|
||||
static EQPGraphRow *eqp_next_row(ShellState *p, int iSelectId, EQPGraphRow *pOld){
|
||||
static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){
|
||||
EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
|
||||
while( pRow && pRow->iSelectId!=iSelectId ) pRow = pRow->pNext;
|
||||
while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
|
||||
return pRow;
|
||||
}
|
||||
|
||||
/* Render a single level of the graph shell having iSelectId. Called
|
||||
/* Render a single level of the graph that has iEqpId as its parent. Called
|
||||
** recursively to render sublevels.
|
||||
*/
|
||||
static void eqp_render_level(ShellState *p, int iSelectId){
|
||||
static void eqp_render_level(ShellState *p, int iEqpId){
|
||||
EQPGraphRow *pRow, *pNext;
|
||||
int i;
|
||||
int n = strlen30(p->sGraph.zPrefix);
|
||||
char *z;
|
||||
for(pRow = eqp_next_row(p, iSelectId, 0); pRow; pRow = pNext){
|
||||
pNext = eqp_next_row(p, iSelectId, pRow);
|
||||
for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
|
||||
pNext = eqp_next_row(p, iEqpId, pRow);
|
||||
z = pRow->zText;
|
||||
utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
|
||||
if( n<sizeof(p->sGraph.zPrefix)-7 && (z = strstr(z, " SUBQUER"))!=0 ){
|
||||
if( n<sizeof(p->sGraph.zPrefix)-7 ){
|
||||
memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
|
||||
if( strncmp(z, " SUBQUERY ", 9)==0 && (i = atoi(z+10))>iSelectId ){
|
||||
eqp_render_level(p, i);
|
||||
}else if( strncmp(z, " SUBQUERIES ", 12)==0 ){
|
||||
i = atoi(z+12);
|
||||
if( i>iSelectId ){
|
||||
utf8_printf(p->out, "%s|--SUBQUERY %d\n", p->sGraph.zPrefix, i);
|
||||
memcpy(&p->sGraph.zPrefix[n+3],"| ",4);
|
||||
eqp_render_level(p, i);
|
||||
}
|
||||
z = strstr(z, " AND ");
|
||||
if( z && (i = atoi(z+5))>iSelectId ){
|
||||
p->sGraph.zPrefix[n+3] = 0;
|
||||
utf8_printf(p->out, "%s`--SUBQUERY %d\n", p->sGraph.zPrefix, i);
|
||||
memcpy(&p->sGraph.zPrefix[n+3]," ",4);
|
||||
eqp_render_level(p, i);
|
||||
}
|
||||
}
|
||||
eqp_render_level(p, pRow->iEqpId);
|
||||
p->sGraph.zPrefix[n] = 0;
|
||||
}
|
||||
}
|
||||
@ -2114,7 +2103,7 @@ static int shell_callback(
|
||||
break;
|
||||
}
|
||||
case MODE_EQP: {
|
||||
eqp_append(p, atoi(azArg[0]), azArg[3]);
|
||||
eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2956,9 +2945,10 @@ static int shell_exec(
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
||||
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
|
||||
int iSelectId = sqlite3_column_int(pExplain, 0);
|
||||
int iEqpId = sqlite3_column_int(pExplain, 0);
|
||||
int iParentId = sqlite3_column_int(pExplain, 1);
|
||||
if( zEQPLine[0]=='-' ) eqp_render(pArg);
|
||||
eqp_append(pArg, iSelectId, zEQPLine);
|
||||
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
|
||||
}
|
||||
eqp_render(pArg);
|
||||
}
|
||||
@ -5914,10 +5904,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->autoEQPtest = 0;
|
||||
if( strcmp(azArg[1],"full")==0 ){
|
||||
p->autoEQP = AUTOEQP_full;
|
||||
}else if( strcmp(azArg[1],"trigger")==0 ){
|
||||
p->autoEQP = AUTOEQP_trigger;
|
||||
}else if( strcmp(azArg[1],"test")==0 ){
|
||||
p->autoEQP = AUTOEQP_on;
|
||||
p->autoEQPtest = 1;
|
||||
}else{
|
||||
p->autoEQP = (u8)booleanValue(azArg[1]);
|
||||
}
|
||||
|
@ -2609,9 +2609,6 @@ struct SrcList {
|
||||
unsigned viaCoroutine :1; /* Implemented as a co-routine */
|
||||
unsigned isRecursive :1; /* True for recursive reference in WITH */
|
||||
} fg;
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
|
||||
#endif
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
Expr *pOn; /* The ON clause of a join */
|
||||
IdList *pUsing; /* The USING clause of a join */
|
||||
@ -2783,6 +2780,8 @@ struct Select {
|
||||
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
||||
#if SELECTTRACE_ENABLED
|
||||
char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
|
||||
#endif
|
||||
#if defined(SQLITETRACE_ENABLED) || !defined(SQLITE_OMIT_EXPLAIN)
|
||||
u32 iSelectId; /* EXPLAIN QUERY PLAN select ID */
|
||||
#endif
|
||||
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
|
||||
@ -3095,8 +3094,7 @@ struct Parse {
|
||||
#endif
|
||||
int nHeight; /* Expression tree height of current sub-select */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSelectId; /* ID of current select for EXPLAIN output */
|
||||
int iNextSelectId; /* Next available select ID for EXPLAIN output */
|
||||
int addrExplain; /* Address of current OP_Explain opcode */
|
||||
#endif
|
||||
VList *pVList; /* Mapping between variable names and numbers */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
|
16
src/vdbe.h
16
src/vdbe.h
@ -197,7 +197,21 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int);
|
||||
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
|
||||
# define sqlite3VdbeVerifyNoResultRow(A)
|
||||
#endif
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
void sqlite3VdbeExplain(Parse*,u8,const char*,...);
|
||||
void sqlite3VdbeExplainPop(Parse*);
|
||||
int sqlite3VdbeExplainParent(Parse*);
|
||||
# define ExplainQueryPlan(P) sqlite3VdbeExplain P
|
||||
# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
|
||||
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
|
||||
# define ExplainQueryPlanSetId(P,S) (S)->iSelectId=(P)->addrExplain
|
||||
#else
|
||||
# define ExplainQueryPlan(P)
|
||||
# define ExplainQueryPlanPop(P)
|
||||
# define ExplainQueryPlanParent(P) 0
|
||||
# define ExplainQueryPlanSetId(P,S)
|
||||
#endif
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
|
@ -303,6 +303,49 @@ int sqlite3VdbeAddOp4Dup8(
|
||||
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/*
|
||||
** Return the address of the current EXPLAIN QUERY PLAN baseline.
|
||||
** 0 means "none".
|
||||
*/
|
||||
int sqlite3VdbeExplainParent(Parse *pParse){
|
||||
VdbeOp *pOp;
|
||||
if( pParse->addrExplain==0 ) return 0;
|
||||
pOp = sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain);
|
||||
return pOp->p2;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new OP_Explain opcode.
|
||||
**
|
||||
** If the bPush flag is true, then make this opcode the parent for
|
||||
** subsequent Explains until sqlite3VdbeExplainPop() is called.
|
||||
*/
|
||||
void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
va_list ap;
|
||||
int iThis;
|
||||
va_start(ap, zFmt);
|
||||
zMsg = sqlite3VMPrintf(pParse->db, zFmt, ap);
|
||||
va_end(ap);
|
||||
v = pParse->pVdbe;
|
||||
iThis = v->nOp;
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
|
||||
zMsg, P4_DYNAMIC);
|
||||
if( bPush) pParse->addrExplain = iThis;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the EXPLAIN QUERY PLAN stack one level.
|
||||
*/
|
||||
void sqlite3VdbeExplainPop(Parse *pParse){
|
||||
pParse->addrExplain = sqlite3VdbeExplainParent(pParse);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
/*
|
||||
** Add an OP_ParseSchema opcode. This routine is broken out from
|
||||
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
|
||||
|
@ -134,7 +134,6 @@ int sqlite3WhereExplainOneScan(
|
||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
||||
u32 flags; /* Flags that describe this loop */
|
||||
@ -153,7 +152,7 @@ int sqlite3WhereExplainOneScan(
|
||||
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
if( pItem->pSelect ){
|
||||
sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
|
||||
sqlite3XPrintf(&str, " SUBQUERY %p", pItem->pSelect);
|
||||
}else{
|
||||
sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
|
||||
}
|
||||
@ -214,7 +213,8 @@ int sqlite3WhereExplainOneScan(
|
||||
}
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
|
||||
pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user