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;
 }