]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Begin adding the data-structure explaining subsystem. All is contained within
authordrh <drh@noemail.net>
Tue, 6 Dec 2011 19:44:51 +0000 (19:44 +0000)
committerdrh <drh@noemail.net>
Tue, 6 Dec 2011 19:44:51 +0000 (19:44 +0000)
FossilOrigin-Name: 79ae51c5b1b20ed0a425a87e65a32a096a80b7e1

13 files changed:
manifest
manifest.uuid
src/expr.c
src/main.c
src/parse.y
src/printf.c
src/select.c
src/shell.c
src/sqlite.h.in
src/sqliteInt.h
src/vdbeInt.h
src/vdbeaux.c
src/vdbetrace.c

index 1fd88a2a92183fb0ff23003cb89ddec2eb57f075..a93f5c0b29b2879cd5bf3ca6f6e2749b33266521 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sfile\stkt-3a77c9714e.test,\scontaining\stests\sto\sverify\sthat\sthe\sproblem\sdocumented\sby\sticket\s[3a77c9714e]\shas\sbeen\sfixed.
-D 2011-12-06T13:46:54.313
+C Begin\sadding\sthe\sdata-structure\sexplaining\ssubsystem.\s\sAll\sis\scontained\swithin
+D 2011-12-06T19:44:51.212
 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
 F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07
 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@@ -134,7 +134,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac
 F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33
 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4
 F src/delete.c 51d32f0a9c880663e54ce309f52e40c325d5e112
-F src/expr.c 319db0ad9f3c5d039d54bf5a65c98c08c7233924
+F src/expr.c 942171222a30af8cf4f9504a43ef6cadaf993dae
 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb
 F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5
 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9
@@ -147,7 +147,7 @@ F src/journal.c 552839e54d1bf76fb8f7abe51868b66acacf6a0e
 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/malloc.c 591aedb20ae40813f1045f2ef253438a334775d9
 F src/mem0.c 6a55ebe57c46ca1a7d98da93aaa07f99f1059645
 F src/mem1.c 7456e2ca0524609ebc06a9befeda5289d4575ad4
@@ -170,21 +170,21 @@ F src/os_unix.c 4fbb91726165e105c1679a2660f49a3f4c376e4f
 F src/os_win.c 6efe66a38215c38eaa7603ee5f76848159f8669d
 F src/pager.c d981f3bfcc0e4460537d983899620700ccf8f539
 F src/pager.h 5cd760857707529b403837d813d86b68938d6183
-F src/parse.y d02cc7bdb9ba11fb7aa212f55f99e59e4ee55439
+F src/parse.y fabb2e7047417d840e6fdb3ef0988a86849a08ba
 F src/pcache.c 1fdd77978c1525d1ca4b9ef48eb80abca710cb4c
 F src/pcache.h b1d8775a9bddf44e65edb0d20bfc57a4982f840f
 F src/pcache1.c 9d735349ac87ef08076c6b1230f04cd83b15c6da
 F src/pragma.c dd66f21fafe7be40e1a48ad4195764cc191cf583
 F src/prepare.c ec4989f7f480544bdc4192fe663470d2a2d7d61e
-F src/printf.c 03104cbff6959ff45df69dc9060ba6212f60a869
+F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699
 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
 F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809
 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
-F src/select.c 538b5f096f6aacedf717bf982f9ad9a901008514
-F src/shell.c 29812a900a780eb0f835c4bc65e216272689def8
-F src/sqlite.h.in 57081d8e6b53ce29541d7437c93bce6087ac53b5
+F src/select.c 2923f3129afcddb8023971ab591b07cdc868c163
+F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b
+F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a
 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477
-F src/sqliteInt.h 142ef5ed3fe0e6c662e99dbf92f38715b29a53b6
+F src/sqliteInt.h 96041958f4508dbc588c82298f75559ac683fe75
 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d
 F src/status.c 4568e72dfd36b6a5911f93457364deb072e0b03a
 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e
@@ -241,13 +241,13 @@ F src/util.c 01238e2b0f24a14779181dbf991fe02620a80e31
 F src/vacuum.c 0c0ba2242355c6048d65e2b333abe0f7c06348fa
 F src/vdbe.c a7ab9993ec5a4d9479dc99671faec061fbf9b889
 F src/vdbe.h f0725ee997db869ecae5bb70a71612aabeca7755
-F src/vdbeInt.h 9498fc98a2c9e349a4ef13455ff5a3e898f40176
+F src/vdbeInt.h a2b8f17783c85995f64432d6cbd546a9183a56f3
 F src/vdbeapi.c 86189ebba2c49791d75eaa12929f3ce6527596bd
-F src/vdbeaux.c 45713a5f8f4f36195f503b30153ddef292323f88
+F src/vdbeaux.c ed7205125304fd8aab32ab0ea975f2c1088cace5
 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/vtab.c e9318d88feac85be8e27ee783ac8f5397933fc8a
 F src/wal.c 5fe1ba55b8fab9d3936bc9093af61ab9f1c580a1
 F src/wal.h 66b40bd91bc29a5be1c88ddd1f5ade8f3f48728a
@@ -977,7 +977,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06
 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
 F tool/warnings-clang.sh 9f406d66e750e8ac031c63a9ef3248aaa347ef2a
 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381
-P 092d53315e50be42b51ef7b3069c82c32a129b6a
-R 8d2fc7565d4aa525192ff0541b1c9cf2
-U dan
-Z d04d6b5cb95ec0b64c1e1542ea707494
+P 162421dadf93e9201c3290d800c597cbeeacdb40
+R ddbfe0a87d25503aebbe5e810f678862
+U drh
+Z ed29fdae1618daadbff0e860a54f25e4
index 1ffea396bcf822b60dd408e71f6feada676bf619..12034d7e1af682270c79be6a8b11f7d07bffbfbc 100644 (file)
@@ -1 +1 @@
-162421dadf93e9201c3290d800c597cbeeacdb40
\ No newline at end of file
+79ae51c5b1b20ed0a425a87e65a32a096a80b7e1
\ No newline at end of file
index e8a9d641080eabb9456f7a1a6807179336518dd6..d157743ab0403babfc8ad303fb28549502409455 100644 (file)
@@ -2939,6 +2939,56 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
   return inReg;
 }
 
+#if defined(SQLITE_DEBUG)
+/*
+** Generate a human-readable explanation of an expression tree.
+*/
+void sqlite3ExplainExpr(Vdbe *pOut, Expr *p){
+  if( p==0 ){
+    sqlite3ExplainPrintf(pOut, "(C-null)");
+    return;
+  }
+  if( !ExprHasProperty(p, EP_IntValue) && p->u.zToken ){
+    sqlite3ExplainPrintf(pOut, "(%s", p->u.zToken);
+  }else{
+    sqlite3ExplainPrintf(pOut, "(op=%d", p->op);
+  }
+  if( p->pLeft ){
+    sqlite3ExplainPrintf(pOut, " left=");
+    sqlite3ExplainExpr(pOut, p->pLeft);
+  }
+  if( p->pRight ){
+    sqlite3ExplainPrintf(pOut, " right=");
+    sqlite3ExplainExpr(pOut, p->pRight);
+  }
+  sqlite3ExplainPrintf(pOut, ")");
+}
+#endif /* defined(SQLITE_DEBUG) */
+
+#if defined(SQLITE_DEBUG)
+/*
+** Generate a human-readable explanation of an expression list.
+*/
+void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){
+  int i;
+  if( pList==0 ){
+    sqlite3ExplainPrintf(pOut, "(empty-list)");
+    return;
+  }
+  sqlite3ExplainPush(pOut);
+  for(i=0; i<pList->nExpr; i++){
+    sqlite3ExplainPrintf(pOut, "%02d: ", 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:
index 8562a90727cba5979d9443681c6740fe3f682a67..ca98d31402c6261b63f3182640bdcbb5e83459af 100644 (file)
@@ -2936,6 +2936,20 @@ int sqlite3_test_control(int op, ...){
       break;
     }
 
+    /*   sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT,
+    **                        sqlite3_stmt*,const char**);
+    **
+    ** 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.
+    */
+    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;
+    }
+
   }
   va_end(ap);
 #endif /* SQLITE_OMIT_BUILTIN_TEST */
index b838215d429f305a24552a65d24824684aee6e50..2b8e9170f32bfa7f6acdcf71a9ff580468087fac 100644 (file)
@@ -394,6 +394,9 @@ cmd ::= DROP VIEW ifexists(E) fullname(X). {
 cmd ::= select(X).  {
   SelectDest dest = {SRT_Output, 0, 0, 0, 0};
   sqlite3Select(pParse, X, &dest);
+  sqlite3ExplainBegin(pParse->pVdbe);
+  sqlite3ExplainSelect(pParse->pVdbe, X);
+  sqlite3ExplainFinish(pParse->pVdbe);
   sqlite3SelectDelete(pParse->db, X);
 }
 
index 0babee51416424096febb99091c35b4f88dc5e6a..58cfd2bd41417237db5ea092522e8370d313038e 100644 (file)
@@ -136,7 +136,7 @@ static char et_getdigit(LONGDOUBLE_TYPE *val, int *cnt){
 /*
 ** Append N space characters to the given string buffer.
 */
-static void appendSpace(StrAccum *pAccum, int N){
+void sqlite3AppendSpace(StrAccum *pAccum, int N){
   static const char zSpaces[] = "                             ";
   while( N>=(int)sizeof(zSpaces)-1 ){
     sqlite3StrAccumAppend(pAccum, zSpaces, sizeof(zSpaces)-1);
@@ -664,7 +664,7 @@ void sqlite3VXPrintf(
       register int nspace;
       nspace = width-length;
       if( nspace>0 ){
-        appendSpace(pAccum, nspace);
+        sqlite3AppendSpace(pAccum, nspace);
       }
     }
     if( length>0 ){
@@ -674,7 +674,7 @@ void sqlite3VXPrintf(
       register int nspace;
       nspace = width-length;
       if( nspace>0 ){
-        appendSpace(pAccum, nspace);
+        sqlite3AppendSpace(pAccum, nspace);
       }
     }
     sqlite3_free(zExtra);
index da75a51cd9add885ab747f5e10f9a673e5f87120..75b6a60e0550813c379d0d28db57bb443d538fb4 100644 (file)
@@ -4496,96 +4496,65 @@ select_end:
 
 #if defined(SQLITE_DEBUG)
 /*
-*******************************************************************************
-** 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(", ");
-    }
+void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){
+  if( p==0 ){
+    sqlite3ExplainPrintf(pVdbe, "(empty-select)");
+    return;
   }
-}
-void sqlite3PrintSelect(Select *p, int indent){
-  sqlite3DebugPrintf("%*sSELECT(%p) ", indent, "", p);
-  sqlite3PrintExprList(p->pEList);
-  sqlite3DebugPrintf("\n");
-  if( p->pSrc ){
-    char *zPrefix;
+  sqlite3ExplainPrintf(pVdbe, "SELECT ");
+  sqlite3ExplainPush(pVdbe);
+  sqlite3ExplainExprList(pVdbe, p->pEList);
+  sqlite3ExplainNL(pVdbe);
+  sqlite3ExplainPop(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, "%02d: ", i);
+      sqlite3ExplainPush(pVdbe);
       if( pItem->pSelect ){
-        sqlite3DebugPrintf("(\n");
-        sqlite3PrintSelect(pItem->pSelect, indent+10);
-        sqlite3DebugPrintf("%*s)", indent+8, "");
+        sqlite3ExplainSelect(pVdbe, pItem->pSelect);
       }else if( pItem->zName ){
-        sqlite3DebugPrintf("%s", pItem->zName);
+        sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName);
       }
       if( pItem->pTab ){
-        sqlite3DebugPrintf("(table: %s)", pItem->pTab->zName);
+        sqlite3ExplainPrintf(pVdbe, " (table: %s)", pItem->pTab->zName);
       }
       if( pItem->zAlias ){
-        sqlite3DebugPrintf(" AS %s", pItem->zAlias);
-      }
-      if( i<p->pSrc->nSrc-1 ){
-        sqlite3DebugPrintf(",");
+        sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
       }
-      sqlite3DebugPrintf("\n");
+      sqlite3ExplainNL(pVdbe);
+      sqlite3ExplainPop(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, " GROUP BY ");
+    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, " ORDER BY ");
+    sqlite3ExplainExprList(pVdbe, p->pOrderBy);
+    sqlite3ExplainNL(pVdbe);
   }
+  sqlite3ExplainPrintf(pVdbe, "   END");
 }
+
 /* End of the structure debug printing code
 *****************************************************************************/
 #endif /* defined(SQLITE_TEST) || defined(SQLITE_DEBUG) */
index e33a0687b4647ae6e9818c85d72fed7a9b97330d..93ed9a6db30f1019b4a0153dd93259053e579d43 100644 (file)
@@ -1127,6 +1127,15 @@ static int shell_exec(
         fprintf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
       }
 
+      /* Output TESTCTRL_EXPLAIN text of requested */
+      if( pArg && pArg->mode==MODE_Explain ){
+        const char *zExplain = 0;
+        sqlite3_test_control(SQLITE_TESTCTRL_EXPLAIN_STMT, pStmt, &zExplain);
+        if( zExplain && zExplain[0] ){
+          fprintf(pArg->out, "%s", zExplain);
+        }
+      }
+
       /* perform the first step.  this will tell us if we
       ** have a result set or not and how wide it is.
       */
index bd5b41f431f5a15d50ef9c0b58dd84692f644d7d..8c9e220e0eebaa36b86420853afc4abc0f50dcb8 100644 (file)
@@ -5682,7 +5682,8 @@ int sqlite3_test_control(int op, ...);
 #define SQLITE_TESTCTRL_ISKEYWORD               16
 #define SQLITE_TESTCTRL_SCRATCHMALLOC           17
 #define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
-#define SQLITE_TESTCTRL_LAST                    18
+#define SQLITE_TESTCTRL_EXPLAIN_STMT            19
+#define SQLITE_TESTCTRL_LAST                    19
 
 /*
 ** CAPI3REF: SQLite Runtime Status
index 890f44f5e10118c2189baf3fe932d4a09c7a3f08..1e7af8e342c610f345ebd738b9dbcdf4166f6e6a 100644 (file)
@@ -2650,6 +2650,29 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
 #if defined(SQLITE_TEST)
   void *sqlite3TestTextToPtr(const char*);
 #endif
+
+/* Output formatting for SQLITE_TESTCTRL_EXPLAIN */
+#if defined(SQLITE_DEBUG)
+  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*);
@@ -3030,6 +3053,7 @@ int sqlite3OpenTempDatabase(Parse *);
 
 void sqlite3StrAccumInit(StrAccum*, char*, int, int);
 void sqlite3StrAccumAppend(StrAccum*,const char*,int);
+void sqlite3AppendSpace(StrAccum*,int);
 char *sqlite3StrAccumFinish(StrAccum*);
 void sqlite3StrAccumReset(StrAccum*);
 void sqlite3SelectDestInit(SelectDest*,int,int);
index 803ae1630e4d4fa9f2ac9ba87d0256c72637fac7..44071e6851d1e25fb3b2d6a0c5ead903f7651f3b 100644 (file)
@@ -33,6 +33,9 @@ typedef unsigned char Bool;
 /* Opaque type used by code in vdbesort.c */
 typedef struct VdbeSorter VdbeSorter;
 
+/* Opaque type used by the explainer */
+typedef struct Explain Explain;
+
 /*
 ** A cursor is a pointer into a single BTree within a database file.
 ** The cursor can seek to a BTree entry with a particular key, or
@@ -255,6 +258,18 @@ struct sqlite3_context {
   CollSeq *pColl;       /* Collating sequence */
 };
 
+/*
+** An Explain object accumulates indented output which is helpful
+** in describing recursive data structures.
+*/
+struct Explain {
+  Vdbe *pVdbe;       /* Attach the explanation to this Vdbe */
+  StrAccum str;      /* The string being accumulated */
+  int nIndent;       /* Number of elements in aIndent */
+  u16 aIndent[100];  /* Levels of indentation */
+  char zBase[100];   /* Initial space */
+};
+
 /*
 ** An instance of the virtual machine.  This structure contains the complete
 ** state of the virtual machine.
@@ -320,6 +335,8 @@ struct Vdbe {
   void *pFree;            /* Free this when deleting the vdbe */
 #ifdef SQLITE_DEBUG
   FILE *trace;            /* Write an execution trace here, if not NULL */
+  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 */
index 7c69d28cf681e0dfc7d00d7dc0b67586472c05fb..510248a5f749bf8bb8a433e67b9915e575955e04 100644 (file)
@@ -2464,6 +2464,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
   sqlite3DbFree(db, p->aColName);
   sqlite3DbFree(db, p->zSql);
   sqlite3DbFree(db, p->pFree);
+#if defined(SQLITE_DEBUG)
+  sqlite3_free(p->zExplain);
+  sqlite3_free(p->pExplain);
+#endif
   sqlite3DbFree(db, p);
 }
 
index de123b550f9902c0afbfb2172e4affc5699ca067..bc05e5897a6e6dfbc697e72a69500c406414bfd6 100644 (file)
@@ -12,6 +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.
 */
 #include "sqliteInt.h"
 #include "vdbeInt.h"
@@ -152,3 +154,118 @@ char *sqlite3VdbeExpandSql(
 }
 
 #endif /* #ifndef SQLITE_OMIT_TRACE */
+
+/*****************************************************************************
+** The following code implements the data-structure explaining logic
+** for the Vdbe.
+*/
+
+#if defined(SQLITE_DEBUG)
+
+/*
+** 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) */