]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Import the experimental parse-tree explainer, with fixes, from the
authordrh <drh@noemail.net>
Sat, 10 Dec 2011 15:55:01 +0000 (15:55 +0000)
committerdrh <drh@noemail.net>
Sat, 10 Dec 2011 15:55:01 +0000 (15:55 +0000)
tree-explain branch.

FossilOrigin-Name: bcbc7152d49107afa926c8950360c61a6cf3d244

1  2 
manifest
manifest.uuid
src/expr.c
src/main.c
src/select.c
src/sqliteInt.h
src/vdbeInt.h
src/vdbeaux.c
src/vdbetrace.c

diff --cc manifest
index ec33d4e62523bebe35e1a9c06eb8a245ebcd69be,2040e56124527f765b1ac225906ec6558bc7f213..14ed07d1eb242833686be750b98a8192bce470a9
+++ b/manifest
@@@ -1,5 -1,5 +1,5 @@@
- C Always\suse\s_msize()\sto\sget\smemory\sallocation\ssizes\son\swindows,\swithout\shaving\nto\sdo\sanything\sspecial\sin\sthe\smakefile.
- D 2011-12-10T13:49:44.752
 -C Merge\sthe\slatest\strunk\schanges\sinto\stree-explain\sbranch.
 -D 2011-12-10T14:44:31.699
++C Import\sthe\sexperimental\sparse-tree\sexplainer,\swith\sfixes,\sfrom\sthe\ntree-explain\sbranch.
++D 2011-12-10T15:55:01.825
  F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
  F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
  F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@@ -134,7 -134,7 +134,7 @@@ F src/complete.c dc1d136c0feee03c2f7550
  F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
  F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
  F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
- F src/expr.c b5920f108d6955b262f8db8e38f40f59b2bfcd7d
 -F src/expr.c d3a969a22368077b0efcb6028faa1c48ba7d2598
++F src/expr.c a68c194c7709388302266e9a5bd9de2243486835
  F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
  F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
  F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
@@@ -147,7 -147,7 +147,7 @@@ F src/journal.c 552839e54d1bf76fb8f7abe
  F src/legacy.c a199d7683d60cef73089e892409113e69c23a99f
  F src/lempar.c 0ee69fca0be54cd93939df98d2aca4ca46f44416
  F src/loadext.c d0d2022a5a07274d408820b978b9e549189d314f
- F src/main.c 8be1ee70dd90ef7562c801dbe946a4f9f93bb128
 -F src/main.c 0e0b9dd5b054ed1aa3861b257035910aff9e1842
++F src/main.c e10d5ad24ae1964d1dc53fbc283557c1c5cd29f3
  F src/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
  F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
  F src/mem1.c 7998e7003a3047e323c849a26dda004debc04d03
@@@ -180,11 -180,11 +180,11 @@@ F src/printf.c 7ffb4ebb8b341f67e049695b
  F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
  F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
  F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
- F src/select.c fd3046fb39a1de8dce269e9b5ecbf58fe7e7e480
- F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8
- F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5
 -F src/select.c 97275df6f40f1df1c389e275c2185a4edf202ad2
++F src/select.c 4d64e3ccccfd2b46e60e7a4cfd21175c3341e859
+ F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b
+ F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a
  F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
- F src/sqliteInt.h de3db02a1e4762a6ec9e1ab604ebc02d77948030
 -F src/sqliteInt.h d781d89415ada4815794fc2587601cd44272f94d
++F src/sqliteInt.h 7a64e24659e067f3882eeb75f31d59e7b3723b8b
  F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
  F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
  F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@@ -241,13 -241,13 +241,13 @@@ F src/util.c 01238e2b0f24a14779181dbf99
  F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
  F src/vdbe.c 029add0c5197a61db588824a58570547330b9d8f
  F src/vdbe.h 18f581cac1f4339ec3299f3e0cc6e11aec654cdb
- F src/vdbeInt.h 23a9506c9ab31e7823d7257d1828d2d7843443a0
 -F src/vdbeInt.h 72222af1a52bcdfa94e1165e083734484fc0863f
++F src/vdbeInt.h 48c158b2fceca9682d1577e61c62da3c58cf0748
  F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
- F src/vdbeaux.c 71a0cd6ae14ddb2e2a6d6b97d5b54ae753272682
 -F src/vdbeaux.c 1b99a1f6c56aecfebc172ff6985ba9818f6c8caa
++F src/vdbeaux.c 61a3cc2b3a713aa244598a13035471cc30b307e1
  F src/vdbeblob.c 32f2a4899d67f69634ea4dd93e3f651936d732cb
  F src/vdbemem.c 2fc78b3e0fabcc1eaa23cd79dd2e30e6dcfe1e56
  F src/vdbesort.c 468d43c057063e54da4f1988b38b4f46d60e7790
- F src/vdbetrace.c 5d0dc3d5fd54878cc8d6d28eb41deb8d5885b114
 -F src/vdbetrace.c 7e5946109138ff6f7f94e79fc702755bf79373a8
++F src/vdbetrace.c d6e50e04e1ec498150e519058f617d91b8f5c843
  F src/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
  F src/wal.c 7e6e7fe68ee649505dca38c8ab83eda0d0d96ae5
  F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
@@@ -978,7 -978,7 +978,7 @@@ F tool/tostr.awk e75472c2f98dd76e06b8c9
  F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
  F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
  F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
- P 0064bab77149768640d7758a3e271d5a1d63b256
- R 24b1670e7f54ceef1719656e8887fd23
 -P 7b457ea4551ba411a4747d74fb78b795cc8d9ee6 256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5
 -R aa2b80b8c93dd58f9be82d377cb63c4d
++P 256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5 1a360da0f8314f232c224c71829646bc7558892b
++R 3dfa94d97a11ee2a078f9f1b12bd877f
  U drh
- Z 1195788bf6e0e867e75c58852af50db9
 -Z 940f9f8f15ab9108dec876188e59d100
++Z 7f30df8f1f79a1cfbb2b882f754d8454
diff --cc manifest.uuid
index 77180fe99e3ec0fecf94989495a228de1e89af1b,253949ba53001348eedaca4fdbbc5f4234db56c4..a0c27bc5c95f0cfba4560866ed502263665e2c79
@@@ -1,1 -1,1 +1,1 @@@
- 256e27bd118ed3ab6ecb19ad6a6494b71ac9bdd5
 -1a360da0f8314f232c224c71829646bc7558892b
++bcbc7152d49107afa926c8950360c61a6cf3d244
diff --cc src/expr.c
index 3c69c564b55a53e93b5ff6fad6af3f485ee16acb,8e51bee2fcfb9ed42f0b921b818fa9f365e84a08..ede9c9d39cdfcbf8d5b3a6447de01430d0973a99
@@@ -2949,6 -2949,264 +2949,264 @@@ int sqlite3ExprCodeAndCache(Parse *pPar
    return inReg;
  }
  
 -#if defined(SQLITE_DEBUG)
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ /*
+ ** Generate a human-readable explanation of an expression tree.
+ */
+ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){
+   int op;                   /* The opcode being coded */
+   const char *zBinOp = 0;   /* Binary operator */
+   const char *zUniOp = 0;   /* Unary operator */
+   if( pExpr==0 ){
+     op = TK_NULL;
+   }else{
+     op = pExpr->op;
+   }
+   switch( op ){
+     case TK_AGG_COLUMN: {
+       sqlite3ExplainPrintf(pOut, "AGG{%d:%d}",
+             pExpr->iTable, pExpr->iColumn);
+       break;
+     }
+     case TK_COLUMN: {
+       if( pExpr->iTable<0 ){
+         /* This only happens when coding check constraints */
+         sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn);
+       }else{
+         sqlite3ExplainPrintf(pOut, "{%d:%d}",
+                              pExpr->iTable, pExpr->iColumn);
+       }
+       break;
+     }
+     case TK_INTEGER: {
+       if( pExpr->flags & EP_IntValue ){
+         sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue);
+       }else{
+         sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken);
+       }
+       break;
+     }
+ #ifndef SQLITE_OMIT_FLOATING_POINT
+     case TK_FLOAT: {
+       sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+       break;
+     }
+ #endif
+     case TK_STRING: {
+       sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken);
+       break;
+     }
+     case TK_NULL: {
+       sqlite3ExplainPrintf(pOut,"NULL");
+       break;
+     }
+ #ifndef SQLITE_OMIT_BLOB_LITERAL
+     case TK_BLOB: {
+       sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken);
+       break;
+     }
+ #endif
+     case TK_VARIABLE: {
+       sqlite3ExplainPrintf(pOut,"VARIABLE(%s,%d)",
+                            pExpr->u.zToken, pExpr->iColumn);
+       break;
+     }
+     case TK_REGISTER: {
+       sqlite3ExplainPrintf(pOut,"REGISTER(%d)", pExpr->iTable);
+       break;
+     }
+     case TK_AS: {
+       sqlite3ExplainExpr(pOut, pExpr->pLeft);
+       break;
+     }
+ #ifndef SQLITE_OMIT_CAST
+     case TK_CAST: {
+       /* Expressions of the form:   CAST(pLeft AS token) */
+       const char *zAff = "unk";
+       switch( sqlite3AffinityType(pExpr->u.zToken) ){
+         case SQLITE_AFF_TEXT:    zAff = "TEXT";     break;
+         case SQLITE_AFF_NONE:    zAff = "NONE";     break;
+         case SQLITE_AFF_NUMERIC: zAff = "NUMERIC";  break;
+         case SQLITE_AFF_INTEGER: zAff = "INTEGER";  break;
+         case SQLITE_AFF_REAL:    zAff = "REAL";     break;
+       }
+       sqlite3ExplainPrintf(pOut, "CAST-%s(", zAff);
+       sqlite3ExplainExpr(pOut, pExpr->pLeft);
+       sqlite3ExplainPrintf(pOut, ")");
+       break;
+     }
+ #endif /* SQLITE_OMIT_CAST */
+     case TK_LT:      zBinOp = "LT";     break;
+     case TK_LE:      zBinOp = "LE";     break;
+     case TK_GT:      zBinOp = "GT";     break;
+     case TK_GE:      zBinOp = "GE";     break;
+     case TK_NE:      zBinOp = "NE";     break;
+     case TK_EQ:      zBinOp = "EQ";     break;
+     case TK_IS:      zBinOp = "IS";     break;
+     case TK_ISNOT:   zBinOp = "ISNOT";  break;
+     case TK_AND:     zBinOp = "AND";    break;
+     case TK_OR:      zBinOp = "OR";     break;
+     case TK_PLUS:    zBinOp = "ADD";    break;
+     case TK_STAR:    zBinOp = "MUL";    break;
+     case TK_MINUS:   zBinOp = "SUB";    break;
+     case TK_REM:     zBinOp = "REM";    break;
+     case TK_BITAND:  zBinOp = "BITAND"; break;
+     case TK_BITOR:   zBinOp = "BITOR";  break;
+     case TK_SLASH:   zBinOp = "DIV";    break;
+     case TK_LSHIFT:  zBinOp = "LSHIFT"; break;
+     case TK_RSHIFT:  zBinOp = "RSHIFT"; break;
+     case TK_CONCAT:  zBinOp = "CONCAT"; break;
+     case TK_UMINUS:  zUniOp = "UMINUS"; break;
+     case TK_UPLUS:   zUniOp = "UPLUS";  break;
+     case TK_BITNOT:  zUniOp = "BITNOT"; break;
+     case TK_NOT:     zUniOp = "NOT";    break;
+     case TK_ISNULL:  zUniOp = "ISNULL"; break;
+     case TK_NOTNULL: zUniOp = "NOTNULL"; break;
+     case TK_AGG_FUNCTION:
+     case TK_CONST_FUNC:
+     case TK_FUNCTION: {
+       ExprList *pFarg;       /* List of function arguments */
+       if( ExprHasAnyProperty(pExpr, EP_TokenOnly) ){
+         pFarg = 0;
+       }else{
+         pFarg = pExpr->x.pList;
+       }
+       sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(",
+                            op==TK_AGG_FUNCTION ? "AGG_" : "",
+                            pExpr->u.zToken);
+       if( pFarg ){
+         sqlite3ExplainExprList(pOut, pFarg);
+       }
+       sqlite3ExplainPrintf(pOut, ")");
+       break;
+     }
+ #ifndef SQLITE_OMIT_SUBQUERY
+     case TK_EXISTS: {
+       sqlite3ExplainPrintf(pOut, "EXISTS(");
+       sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+       sqlite3ExplainPrintf(pOut,")");
+       break;
+     }
+     case TK_SELECT: {
+       sqlite3ExplainPrintf(pOut, "(");
+       sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+       sqlite3ExplainPrintf(pOut, ")");
+       break;
+     }
+     case TK_IN: {
+       sqlite3ExplainPrintf(pOut, "IN(");
+       sqlite3ExplainExpr(pOut, pExpr->pLeft);
+       sqlite3ExplainPrintf(pOut, ",");
+       if( ExprHasProperty(pExpr, EP_xIsSelect) ){
+         sqlite3ExplainSelect(pOut, pExpr->x.pSelect);
+       }else{
+         sqlite3ExplainExprList(pOut, pExpr->x.pList);
+       }
+       sqlite3ExplainPrintf(pOut, ")");
+       break;
+     }
+ #endif /* SQLITE_OMIT_SUBQUERY */
+     /*
+     **    x BETWEEN y AND z
+     **
+     ** This is equivalent to
+     **
+     **    x>=y AND x<=z
+     **
+     ** X is stored in pExpr->pLeft.
+     ** Y is stored in pExpr->pList->a[0].pExpr.
+     ** Z is stored in pExpr->pList->a[1].pExpr.
+     */
+     case TK_BETWEEN: {
+       Expr *pX = pExpr->pLeft;
+       Expr *pY = pExpr->x.pList->a[0].pExpr;
+       Expr *pZ = pExpr->x.pList->a[1].pExpr;
+       sqlite3ExplainPrintf(pOut, "BETWEEN(");
+       sqlite3ExplainExpr(pOut, pX);
+       sqlite3ExplainPrintf(pOut, ",");
+       sqlite3ExplainExpr(pOut, pY);
+       sqlite3ExplainPrintf(pOut, ",");
+       sqlite3ExplainExpr(pOut, pZ);
+       sqlite3ExplainPrintf(pOut, ")");
+       break;
+     }
+     case TK_TRIGGER: {
+       /* If the opcode is TK_TRIGGER, then the expression is a reference
+       ** to a column in the new.* or old.* pseudo-tables available to
+       ** trigger programs. In this case Expr.iTable is set to 1 for the
+       ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn
+       ** is set to the column of the pseudo-table to read, or to -1 to
+       ** read the rowid field.
+       */
+       sqlite3ExplainPrintf(pOut, "%s(%d)", 
+           pExpr->iTable ? "NEW" : "OLD", pExpr->iColumn);
+       break;
+     }
+     case TK_CASE: {
+       sqlite3ExplainPrintf(pOut, "CASE(");
+       sqlite3ExplainExpr(pOut, pExpr->pLeft);
+       sqlite3ExplainPrintf(pOut, ",");
+       sqlite3ExplainExprList(pOut, pExpr->x.pList);
+       break;
+     }
+ #ifndef SQLITE_OMIT_TRIGGER
+     case TK_RAISE: {
+       const char *zType = "unk";
+       switch( pExpr->affinity ){
+         case OE_Rollback:   zType = "rollback";  break;
+         case OE_Abort:      zType = "abort";     break;
+         case OE_Fail:       zType = "fail";      break;
+         case OE_Ignore:     zType = "ignore";    break;
+       }
+       sqlite3ExplainPrintf(pOut, "RAISE-%s(%s)", zType, pExpr->u.zToken);
+       break;
+     }
+ #endif
+   }
+   if( zBinOp ){
+     sqlite3ExplainPrintf(pOut,"%s(", zBinOp);
+     sqlite3ExplainExpr(pOut, pExpr->pLeft);
+     sqlite3ExplainPrintf(pOut,",");
+     sqlite3ExplainExpr(pOut, pExpr->pRight);
+     sqlite3ExplainPrintf(pOut,")");
+   }else if( zUniOp ){
+     sqlite3ExplainPrintf(pOut,"%s(", zUniOp);
+     sqlite3ExplainExpr(pOut, pExpr->pLeft);
+     sqlite3ExplainPrintf(pOut,")");
+   }
+ }
 -#endif /* defined(SQLITE_DEBUG) */
++#endif /* defined(SQLITE_ENABLE_TREE_EXPLAIN) */
 -#if defined(SQLITE_DEBUG)
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ /*
+ ** Generate a human-readable explanation of an expression list.
+ */
+ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
+   int i;
+   if( pList==0 || pList->nExpr==0 ){
+     sqlite3ExplainPrintf(pOut, "(empty-list)");
+     return;
+   }else if( pList->nExpr==1 ){
+     sqlite3ExplainExpr(pOut, pList->a[0].pExpr);
+   }else{
+     sqlite3ExplainPush(pOut);
+     for(i=0; i<pList->nExpr; i++){
+       sqlite3ExplainPrintf(pOut, "item[%d] = ", i);
+       sqlite3ExplainPush(pOut);
+       sqlite3ExplainExpr(pOut, pList->a[i].pExpr);
+       sqlite3ExplainPop(pOut);
+       if( i<pList->nExpr-1 ){
+         sqlite3ExplainNL(pOut);
+       }
+     }
+     sqlite3ExplainPop(pOut);
+   }
+ }
+ #endif /* SQLITE_DEBUG */
  /*
  ** Return TRUE if pExpr is an constant expression that is appropriate
  ** for factoring out of a loop.  Appropriate expressions are:
diff --cc src/main.c
index 8562a90727cba5979d9443681c6740fe3f682a67,ca98d31402c6261b63f3182640bdcbb5e83459af..d6dc0c79ed17cbbaa33c7464ded9398b74e0ccb9
@@@ -2936,6 -2936,20 +2936,22 @@@ int sqlite3_test_control(int op, ...)
        break;
      }
  
 -    ** If compiled with SQLITE_DEBUG, each sqlite3_stmt holds a string that
 -    ** describes the optimized parse tree.  This test-control returns a
 -    ** pointer to that string.
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+     /*   sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
+     **                        sqlite3_stmt*,const char**);
+     **
++    ** If compiled with SQLITE_ENABLE_TREE_EXPLAIN, each sqlite3_stmt holds
++    ** a string that describes the optimized parse tree.  This test-control
++    ** returns a pointer to that string.
+     */
+     case SQLITE_TESTCTRL_EXPLAIN_STMT: {
+       sqlite3_stmt *pStmt = va_arg(ap, sqlite3_stmt*);
+       const char **pzRet = va_arg(ap, const char**);
+       *pzRet = sqlite3VdbeExplanation((Vdbe*)pStmt);
+       break;
+     }
++#endif
    }
    va_end(ap);
  #endif /* SQLITE_OMIT_BUILTIN_TEST */
diff --cc src/select.c
index 793b849b793778daf71c57908e6be9bcf499fe1e,15b34e2b4e660a9984d27ddafbbf210e5470b67b..c41dc07f9eef1021a1ee069c4275658595c47e98
@@@ -4494,98 -4494,98 +4494,98 @@@ select_end
    return rc;
  }
  
--#if defined(SQLITE_DEBUG)
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
  /*
- *******************************************************************************
- ** The following code is used for testing and debugging only.  The code
- ** that follows does not appear in normal builds.
- **
- ** These routines are used to print out the content of all or part of a 
- ** parse structures such as Select or Expr.  Such printouts are useful
- ** for helping to understand what is happening inside the code generator
- ** during the execution of complex SELECT statements.
- **
- ** These routine are not called anywhere from within the normal
- ** code base.  Then are intended to be called from within the debugger
- ** or from temporary "printf" statements inserted for debugging.
+ ** Generate a human-readable description of a the Select object.
  */
- void sqlite3PrintExpr(Expr *p){
-   if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
-     sqlite3DebugPrintf("(%s", p->u.zToken);
-   }else{
-     sqlite3DebugPrintf("(%d", p->op);
-   }
-   if( p->pLeft ){
-     sqlite3DebugPrintf(" ");
-     sqlite3PrintExpr(p->pLeft);
-   }
-   if( p->pRight ){
-     sqlite3DebugPrintf(" ");
-     sqlite3PrintExpr(p->pRight);
-   }
-   sqlite3DebugPrintf(")");
- }
- void sqlite3PrintExprList(ExprList *pList){
-   int i;
-   for(i=0; i<pList->nExpr; i++){
-     sqlite3PrintExpr(pList->a[i].pExpr);
-     if( i<pList->nExpr-1 ){
-       sqlite3DebugPrintf(", ");
+ static void explainOneSelect(Vdbe *pVdbe, Select *p){
+   sqlite3ExplainPrintf(pVdbe, "SELECT ");
+   if( p->selFlags & (SF_Distinct|SF_Aggregate) ){
+     if( p->selFlags & SF_Distinct ){
+       sqlite3ExplainPrintf(pVdbe, "DISTINCT ");
+     }
+     if( p->selFlags & SF_Aggregate ){
+       sqlite3ExplainPrintf(pVdbe, "agg_flag ");
      }
+     sqlite3ExplainNL(pVdbe);
+     sqlite3ExplainPrintf(pVdbe, "   ");
    }
- }
- void sqlite3PrintSelect(Select *p, int indent){
-   sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
-   sqlite3PrintExprList(p->pEList);
-   sqlite3DebugPrintf("\n");
-   if( p->pSrc ){
-     char *zPrefix;
+   sqlite3ExplainExprList(pVdbe, p->pEList);
+   sqlite3ExplainNL(pVdbe);
+   if( p->pSrc && p->pSrc->nSrc ){
      int i;
-     zPrefix = "FROM";
+     sqlite3ExplainPrintf(pVdbe, "FROM ");
+     sqlite3ExplainPush(pVdbe);
      for(i=0; i<p->pSrc->nSrc; i++){
        struct SrcList_item *pItem = &p->pSrc->a[i];
-       sqlite3DebugPrintf("%*s ", indent+6, zPrefix);
-       zPrefix = "";
+       sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor);
        if( pItem->pSelect ){
-         sqlite3DebugPrintf("(\n");
-         sqlite3PrintSelect(pItem->pSelect, indent+10);
-         sqlite3DebugPrintf("%*s)", indent+8, "");
+         sqlite3ExplainSelect(pVdbe, pItem->pSelect);
+         if( pItem->pTab ){
+           sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName);
+         }
        }else if( pItem->zName ){
-         sqlite3DebugPrintf("%s", pItem->zName);
-       }
-       if( pItem->pTab ){
-         sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
+         sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
        }
        if( pItem->zAlias ){
-         sqlite3DebugPrintf(" AS %s", pItem->zAlias);
+         sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
        }
-       if( i<p->pSrc->nSrc-1 ){
-         sqlite3DebugPrintf(",");
+       if( pItem->jointype & JT_LEFT ){
+         sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN");
        }
-       sqlite3DebugPrintf("\n");
+       sqlite3ExplainNL(pVdbe);
      }
+     sqlite3ExplainPop(pVdbe);
    }
    if( p->pWhere ){
-     sqlite3DebugPrintf("%*s WHERE ", indent, "");
-     sqlite3PrintExpr(p->pWhere);
-     sqlite3DebugPrintf("\n");
+     sqlite3ExplainPrintf(pVdbe, "WHERE ");
+     sqlite3ExplainExpr(pVdbe, p->pWhere);
+     sqlite3ExplainNL(pVdbe);
    }
    if( p->pGroupBy ){
-     sqlite3DebugPrintf("%*s GROUP BY ", indent, "");
-     sqlite3PrintExprList(p->pGroupBy);
-     sqlite3DebugPrintf("\n");
+     sqlite3ExplainPrintf(pVdbe, "GROUPBY ");
+     sqlite3ExplainExprList(pVdbe, p->pGroupBy);
+     sqlite3ExplainNL(pVdbe);
    }
    if( p->pHaving ){
-     sqlite3DebugPrintf("%*s HAVING ", indent, "");
-     sqlite3PrintExpr(p->pHaving);
-     sqlite3DebugPrintf("\n");
+     sqlite3ExplainPrintf(pVdbe, "HAVING ");
+     sqlite3ExplainExpr(pVdbe, p->pHaving);
+     sqlite3ExplainNL(pVdbe);
    }
    if( p->pOrderBy ){
-     sqlite3DebugPrintf("%*s ORDER BY ", indent, "");
-     sqlite3PrintExprList(p->pOrderBy);
-     sqlite3DebugPrintf("\n");
+     sqlite3ExplainPrintf(pVdbe, "ORDERBY ");
+     sqlite3ExplainExprList(pVdbe, p->pOrderBy);
+     sqlite3ExplainNL(pVdbe);
+   }
+   if( p->pLimit ){
+     sqlite3ExplainPrintf(pVdbe, "LIMIT ");
+     sqlite3ExplainExpr(pVdbe, p->pLimit);
+     sqlite3ExplainNL(pVdbe);
+   }
+   if( p->pOffset ){
+     sqlite3ExplainPrintf(pVdbe, "OFFSET ");
+     sqlite3ExplainExpr(pVdbe, p->pOffset);
+     sqlite3ExplainNL(pVdbe);
+   }
+ }
+ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
+   if( p==0 ){
+     sqlite3ExplainPrintf(pVdbe, "(null-select)");
+     return;
    }
+   while( p->pPrior ) p = p->pPrior;
+   sqlite3ExplainPush(pVdbe);
+   while( p ){
+     explainOneSelect(pVdbe, p);
+     p = p->pNext;
+     if( p==0 ) break;
+     sqlite3ExplainNL(pVdbe);
+     sqlite3ExplainPrintf(pVdbe, "%s\n", selectOpName(p->op));
+   }
+   sqlite3ExplainPrintf(pVdbe, "END");
+   sqlite3ExplainPop(pVdbe);
  }
  /* End of the structure debug printing code
  *****************************************************************************/
  #endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
diff --cc src/sqliteInt.h
index bd4198c2f7ce83f436bfa29f75e14e49cd3272d4,7c57558ef2bd75d9d1c08001b10ce3a542dc4577..6266a9cb724ec6eb3b4d6d880ef5ea5d59fef0b9
@@@ -1707,10 -1707,10 +1707,10 @@@ struct Expr 
  #define EP_FixedDest  0x0200  /* Result needed in a specific register */
  #define EP_IntValue   0x0400  /* Integer value contained in u.iValue */
  #define EP_xIsSelect  0x0800  /* x.pSelect is valid (otherwise x.pList is) */
--
--#define EP_Reduced    0x1000  /* Expr struct is EXPR_REDUCEDSIZE bytes only */
--#define EP_TokenOnly  0x2000  /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
--#define EP_Static     0x4000  /* Held in memory not obtained from malloc() */
++#define EP_Hint       0x1000  /* Optimizer hint. Not required for correctness */
++#define EP_Reduced    0x2000  /* Expr struct is EXPR_REDUCEDSIZE bytes only */
++#define EP_TokenOnly  0x4000  /* Expr struct is EXPR_TOKENONLYSIZE bytes only */
++#define EP_Static     0x8000  /* Held in memory not obtained from malloc() */
  
  /*
  ** The following are the meanings of bits in the Expr.flags2 field.
@@@ -2651,6 -2651,29 +2651,29 @@@ char *sqlite3MAppendf(sqlite3*,char*,co
  #if defined(SQLITE_TEST)
    void *sqlite3TestTextToPtr(const char*);
  #endif
 -#if defined(SQLITE_DEBUG)
+ /* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+   void sqlite3ExplainBegin(Vdbe*);
+   void sqlite3ExplainPrintf(Vdbe*, const char*, ...);
+   void sqlite3ExplainNL(Vdbe*);
+   void sqlite3ExplainPush(Vdbe*);
+   void sqlite3ExplainPop(Vdbe*);
+   void sqlite3ExplainFinish(Vdbe*);
+   void sqlite3ExplainSelect(Vdbe*, Select*);
+   void sqlite3ExplainExpr(Vdbe*, Expr*);
+   void sqlite3ExplainExprList(Vdbe*, ExprList*);
+   const char *sqlite3VdbeExplanation(Vdbe*);
+ #else
+ # define sqlite3ExplainBegin(X)
+ # define sqlite3ExplainSelect(A,B)
+ # define sqlite3ExplainExpr(A,B)
+ # define sqlite3ExplainExprList(A,B)
+ # define sqlite3ExplainFinish(X)
+ # define sqlite3VdbeExplanation(X) 0
+ #endif
  void sqlite3SetString(char **, sqlite3*, const char*, ...);
  void sqlite3ErrorMsg(Parse*, const char*, ...);
  int sqlite3Dequote(char*);
diff --cc src/vdbeInt.h
index 5f75caaf09c09d2c50dbe523479a389017f8f4ff,fb49898a1b6e77d159bde8eec7c8a476d73d2973..a56dedf6afb14413ee860556b67682f6ed8a143b
@@@ -322,6 -337,8 +337,10 @@@ struct Vdbe 
    void *pFree;            /* Free this when deleting the vdbe */
  #ifdef SQLITE_DEBUG
    FILE *trace;            /* Write an execution trace here, if not NULL */
++#endif
++#ifdef SQLITE_ENABLE_TREE_EXPLAIN
+   Explain *pExplain;      /* The explainer */
+   char *zExplain;         /* Explanation of data structures */
  #endif
    VdbeFrame *pFrame;      /* Parent frame */
    VdbeFrame *pDelFrame;   /* List of frame objects to free on VM reset */
diff --cc src/vdbeaux.c
index 8b085ea6ce3231e4cb3287fac6deef986b1a7f76,1c67902f15d282949dc9efd67d81c41e3a120123..747c846d07ffdd22449e228b6e97e3fcf6861f02
@@@ -2474,6 -2474,10 +2474,10 @@@ void sqlite3VdbeDeleteObject(sqlite3 *d
    sqlite3DbFree(db, p->aColName);
    sqlite3DbFree(db, p->zSql);
    sqlite3DbFree(db, p->pFree);
 -#if defined(SQLITE_DEBUG)
 -  sqlite3_free(p->zExplain);
 -  sqlite3_free(p->pExplain);
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
++  sqlite3DbFree(db, p->zExplain);
++  sqlite3DbFree(db, p->pExplain);
+ #endif
    sqlite3DbFree(db, p);
  }
  
diff --cc src/vdbetrace.c
index de123b550f9902c0afbfb2172e4affc5699ca067,bc05e5897a6e6dfbc697e72a69500c406414bfd6..4c4be13737c2497afa44bec9ef63dbecc3abf8bc
@@@ -12,6 -12,8 +12,8 @@@
  **
  ** This file contains code used to insert the values of host parameters
  ** (aka "wildcards") into the SQL text output by sqlite3_trace().
 -** The Vdbe explainer is also found here.
+ **
++** The Vdbe parse-tree explainer is also found here.
  */
  #include "sqliteInt.h"
  #include "vdbeInt.h"
@@@ -152,3 -154,118 +154,118 @@@ char *sqlite3VdbeExpandSql
  }
  
  #endif /* #ifndef SQLITE_OMIT_TRACE */
 -#if defined(SQLITE_DEBUG)
+ /*****************************************************************************
+ ** The following code implements the data-structure explaining logic
+ ** for the Vdbe.
+ */
++#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
+ /*
+ ** Allocate a new Explain object
+ */
+ void sqlite3ExplainBegin(Vdbe *pVdbe){
+   if( pVdbe ){
+     sqlite3BeginBenignMalloc();
+     Explain *p = sqlite3_malloc( sizeof(Explain) );
+     if( p ){
+       memset(p, 0, sizeof(*p));
+       p->pVdbe = pVdbe;
+       sqlite3_free(pVdbe->pExplain);
+       pVdbe->pExplain = p;
+       sqlite3StrAccumInit(&p->str, p->zBase, sizeof(p->zBase),
+                           SQLITE_MAX_LENGTH);
+       p->str.useMalloc = 2;
+     }else{
+       sqlite3EndBenignMalloc();
+     }
+   }
+ }
+ /*
+ ** Return true if the Explain ends with a new-line.
+ */
+ static int endsWithNL(Explain *p){
+   return p && p->str.zText && p->str.nChar
+            && p->str.zText[p->str.nChar-1]=='\n';
+ }
+     
+ /*
+ ** Append text to the indentation
+ */
+ void sqlite3ExplainPrintf(Vdbe *pVdbe, const char *zFormat, ...){
+   Explain *p;
+   if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+     va_list ap;
+     if( p->nIndent && endsWithNL(p) ){
+       int n = p->nIndent;
+       if( n>ArraySize(p->aIndent) ) n = ArraySize(p->aIndent);
+       sqlite3AppendSpace(&p->str, p->aIndent[n-1]);
+     }   
+     va_start(ap, zFormat);
+     sqlite3VXPrintf(&p->str, 1, zFormat, ap);
+     va_end(ap);
+   }
+ }
+ /*
+ ** Append a '\n' if there is not already one.
+ */
+ void sqlite3ExplainNL(Vdbe *pVdbe){
+   Explain *p;
+   if( pVdbe && (p = pVdbe->pExplain)!=0 && !endsWithNL(p) ){
+     sqlite3StrAccumAppend(&p->str, "\n", 1);
+   }
+ }
+ /*
+ ** Push a new indentation level.  Subsequent lines will be indented
+ ** so that they begin at the current cursor position.
+ */
+ void sqlite3ExplainPush(Vdbe *pVdbe){
+   Explain *p;
+   if( pVdbe && (p = pVdbe->pExplain)!=0 ){
+     if( p->str.zText && p->nIndent<ArraySize(p->aIndent) ){
+       const char *z = p->str.zText;
+       int i = p->str.nChar-1;
+       int x;
+       while( i>=0 && z[i]!='\n' ){ i--; }
+       x = (p->str.nChar - 1) - i;
+       if( p->nIndent && x<p->aIndent[p->nIndent-1] ){
+         x = p->aIndent[p->nIndent-1];
+       }
+       p->aIndent[p->nIndent] = x;
+     }
+     p->nIndent++;
+   }
+ }
+ /*
+ ** Pop the indentation stack by one level.
+ */
+ void sqlite3ExplainPop(Vdbe *p){
+   if( p && p->pExplain ) p->pExplain->nIndent--;
+ }
+ /*
+ ** Free the indentation structure
+ */
+ void sqlite3ExplainFinish(Vdbe *pVdbe){
+   if( pVdbe && pVdbe->pExplain ){
+     sqlite3_free(pVdbe->zExplain);
+     sqlite3ExplainNL(pVdbe);
+     pVdbe->zExplain = sqlite3StrAccumFinish(&pVdbe->pExplain->str);
+     sqlite3_free(pVdbe->pExplain);
+     pVdbe->pExplain = 0;
+     sqlite3EndBenignMalloc();
+   }
+ }
+ /*
+ ** Return the explanation of a virtual machine.
+ */
+ const char *sqlite3VdbeExplanation(Vdbe *pVdbe){
+   return (pVdbe && pVdbe->zExplain) ? pVdbe->zExplain : 0;
+ }
+ #endif /* defined(SQLITE_DEBUG) */