diff --git a/manifest b/manifest index b0cba4a28b..c552ff2a44 100644 --- a/manifest +++ b/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 diff --git a/manifest.uuid b/manifest.uuid index 0bfeabe2c0..14555151db 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -853f3163597b9946c0cbeb808ea6fd33a0cf48ae6b8f4459c4165db377f33a9e \ No newline at end of file +70b48a7972dfbb44af3ccd8ccd830e984bec88d80a78b3566a5de86a16e7fc14 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 2d4a14c8d0..f2f1a86105 100644 --- a/src/expr.c +++ b/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; diff --git a/src/select.c b/src/select.c index ac313d0288..debafd1500 100644 --- a/src/select.c +++ b/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; } diff --git a/src/shell.c.in b/src/shell.c.in index e759921ab6..7675b3832d 100644 --- a/src/shell.c.in +++ b/src/shell.c.in @@ -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]); } diff --git a/src/sqliteInt.h b/src/sqliteInt.h index c700c261b8..f313baaec7 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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()) */ diff --git a/src/vdbe.h b/src/vdbe.h index 60525a9e9e..b7f44ca913 100644 --- a/src/vdbe.h +++ b/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); diff --git a/src/vdbeaux.c b/src/vdbeaux.c index be092b98b7..5a7b354cb9 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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 diff --git a/src/wherecode.c b/src/wherecode.c index 818a7a208b..c1987d6b08 100644 --- a/src/wherecode.c +++ b/src/wherecode.c @@ -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; }