From 7e02e5e6b54ea4a6b58249210c0df67af80b11a1 Mon Sep 17 00:00:00 2001 From: drh Date: Tue, 6 Dec 2011 19:44:51 +0000 Subject: [PATCH 1/5] Begin adding the data-structure explaining subsystem. All is contained within FossilOrigin-Name: 79ae51c5b1b20ed0a425a87e65a32a096a80b7e1 --- manifest | 34 +++++++------- manifest.uuid | 2 +- src/expr.c | 50 +++++++++++++++++++++ src/main.c | 14 ++++++ src/parse.y | 3 ++ src/printf.c | 6 +-- src/select.c | 103 +++++++++++++++--------------------------- src/shell.c | 9 ++++ src/sqlite.h.in | 3 +- src/sqliteInt.h | 24 ++++++++++ src/vdbeInt.h | 17 +++++++ src/vdbeaux.c | 4 ++ src/vdbetrace.c | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 297 insertions(+), 89 deletions(-) diff --git a/manifest b/manifest index 1fd88a2a92..a93f5c0b29 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index 1ffea396bc..12034d7e1a 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -162421dadf93e9201c3290d800c597cbeeacdb40 \ No newline at end of file +79ae51c5b1b20ed0a425a87e65a32a096a80b7e1 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index e8a9d64108..d157743ab0 100644 --- a/src/expr.c +++ b/src/expr.c @@ -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; inExpr; i++){ + sqlite3ExplainPrintf(pOut, "%02d: ", i); + sqlite3ExplainPush(pOut); + sqlite3ExplainExpr(pOut, pList->a[i].pExpr); + sqlite3ExplainPop(pOut); + if( inExpr-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 --git a/src/main.c b/src/main.c index 8562a90727..ca98d31402 100644 --- a/src/main.c +++ b/src/main.c @@ -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 */ diff --git a/src/parse.y b/src/parse.y index b838215d42..2b8e9170f3 100644 --- a/src/parse.y +++ b/src/parse.y @@ -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); } diff --git a/src/printf.c b/src/printf.c index 0babee5141..58cfd2bd41 100644 --- a/src/printf.c +++ b/src/printf.c @@ -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); diff --git a/src/select.c b/src/select.c index da75a51cd9..75b6a60e05 100644 --- a/src/select.c +++ b/src/select.c @@ -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); +void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ + if( p==0 ){ + sqlite3ExplainPrintf(pVdbe, "(empty-select)"); + return; } - if( p->pLeft ){ - sqlite3DebugPrintf(" "); - sqlite3PrintExpr(p->pLeft); - } - if( p->pRight ){ - sqlite3DebugPrintf(" "); - sqlite3PrintExpr(p->pRight); - } - sqlite3DebugPrintf(")"); -} -void sqlite3PrintExprList(ExprList *pList){ - int i; - for(i=0; inExpr; i++){ - sqlite3PrintExpr(pList->a[i].pExpr); - if( inExpr-1 ){ - sqlite3DebugPrintf(", "); - } - } -} -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; ipSrc->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); + sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); } - if( ipSrc->nSrc-1 ){ - sqlite3DebugPrintf(","); - } - 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) */ diff --git a/src/shell.c b/src/shell.c index e33a0687b4..93ed9a6db3 100644 --- a/src/shell.c +++ b/src/shell.c @@ -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. */ diff --git a/src/sqlite.h.in b/src/sqlite.h.in index bd5b41f431..8c9e220e0e 100644 --- a/src/sqlite.h.in +++ b/src/sqlite.h.in @@ -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 diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 890f44f5e1..1e7af8e342 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -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); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index 803ae1630e..44071e6851 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -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 */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 7c69d28cf6..510248a5f7 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -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); } diff --git a/src/vdbetrace.c b/src/vdbetrace.c index de123b550f..bc05e5897a 100644 --- a/src/vdbetrace.c +++ b/src/vdbetrace.c @@ -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->nIndentaIndent) ){ + 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 && xaIndent[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) */ From a84203a0749e49cfff15fb99588a0158b6fd085e Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Dec 2011 01:23:51 +0000 Subject: [PATCH 2/5] Improvements to the data-structure explain subsystem. Most queries now give a reasonably detailed graph of their parse tree. FossilOrigin-Name: 0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 --- manifest | 14 +-- manifest.uuid | 2 +- src/expr.c | 266 +++++++++++++++++++++++++++++++++++++++++++++----- src/select.c | 54 +++++++--- 4 files changed, 287 insertions(+), 49 deletions(-) diff --git a/manifest b/manifest index a93f5c0b29..e924999ec2 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Begin\sadding\sthe\sdata-structure\sexplaining\ssubsystem.\s\sAll\sis\scontained\swithin -D 2011-12-06T19:44:51.212 +C Improvements\sto\sthe\sdata-structure\sexplain\ssubsystem.\s\sMost\squeries\snow\ngive\sa\sreasonably\sdetailed\sgraph\sof\stheir\sparse\stree. +D 2011-12-07T01:23:51.800 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 942171222a30af8cf4f9504a43ef6cadaf993dae +F src/expr.c 62f6ad2a1dcfbf684e6916c0662d5b4f28b98346 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9 @@ -180,7 +180,7 @@ F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c 2923f3129afcddb8023971ab591b07cdc868c163 +F src/select.c 352ac58d6070f9a5564e9648bc2202b9d6339747 F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 @@ -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 162421dadf93e9201c3290d800c597cbeeacdb40 -R ddbfe0a87d25503aebbe5e810f678862 +P 79ae51c5b1b20ed0a425a87e65a32a096a80b7e1 +R 33f29d39c143746ce94119c4aaec65fd U drh -Z ed29fdae1618daadbff0e860a54f25e4 +Z 8a3e65c823a12538f790d9d174308040 diff --git a/manifest.uuid b/manifest.uuid index 12034d7e1a..dbbd003458 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -79ae51c5b1b20ed0a425a87e65a32a096a80b7e1 \ No newline at end of file +0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index d157743ab0..0d217ed405 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2943,25 +2943,238 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){ /* ** 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); +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{ - sqlite3ExplainPrintf(pOut, "(op=%d", p->op); + op = pExpr->op; } - if( p->pLeft ){ - sqlite3ExplainPrintf(pOut, " left="); - sqlite3ExplainExpr(pOut, p->pLeft); + switch( op ){ + case TK_AGG_COLUMN: { + sqlite3ExplainPrintf(pOut, "AGG_COLUMN(%s:%d:%d)", + pExpr->pTab->zName, 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, "COLUMN(%s:%d:%d)", + pExpr->pTab->zName, pExpr->iTable, pExpr->iColumn); + } + break; + } + case TK_INTEGER: { + if( pExpr->flags & EP_IntValue ){ + sqlite3ExplainPrintf(pOut, "INTEGER(%d)", pExpr->u.iValue); + }else{ + sqlite3ExplainPrintf(pOut, "INTEGER(%s)", pExpr->u.zToken); + } + break; + } +#ifndef SQLITE_OMIT_FLOATING_POINT + case TK_FLOAT: { + sqlite3ExplainPrintf(pOut,"REAL(%s)", pExpr->u.zToken); + break; + } +#endif + case TK_STRING: { + sqlite3ExplainPrintf(pOut,"STRING(%s)", pExpr->u.zToken); + break; + } + case TK_NULL: { + sqlite3ExplainPrintf(pOut,"NULL"); + break; + } +#ifndef SQLITE_OMIT_BLOB_LITERAL + case TK_BLOB: { + int n; + const char *z; + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); + assert( pExpr->u.zToken[1]=='\'' ); + z = &pExpr->u.zToken[2]; + n = sqlite3Strlen30(z) - 1; + assert( z[n]=='\'' ); + sqlite3ExplainPrintf(pOut,"BLOB(%.*s)", n, z); + 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( p->pRight ){ - sqlite3ExplainPrintf(pOut, " right="); - sqlite3ExplainExpr(pOut, p->pRight); + 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,")"); } - sqlite3ExplainPrintf(pOut, ")"); } #endif /* defined(SQLITE_DEBUG) */ @@ -2971,21 +3184,24 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *p){ */ void sqlite3ExplainExprList(Vdbe *pOut, ExprList *pList){ int i; - if( pList==0 ){ + if( pList==0 || pList->nExpr==0 ){ sqlite3ExplainPrintf(pOut, "(empty-list)"); return; - } - sqlite3ExplainPush(pOut); - for(i=0; inExpr; i++){ - sqlite3ExplainPrintf(pOut, "%02d: ", i); + }else if( pList->nExpr==1 ){ + sqlite3ExplainExpr(pOut, pList->a[0].pExpr); + }else{ sqlite3ExplainPush(pOut); - sqlite3ExplainExpr(pOut, pList->a[i].pExpr); - sqlite3ExplainPop(pOut); - if( inExpr-1 ){ - sqlite3ExplainNL(pOut); + for(i=0; inExpr; i++){ + sqlite3ExplainPrintf(pOut, "item[%d] = ", i); + sqlite3ExplainPush(pOut); + sqlite3ExplainExpr(pOut, pList->a[i].pExpr); + sqlite3ExplainPop(pOut); + if( inExpr-1 ){ + sqlite3ExplainNL(pOut); + } } + sqlite3ExplainPop(pOut); } - sqlite3ExplainPop(pOut); } #endif /* SQLITE_DEBUG */ diff --git a/src/select.c b/src/select.c index 75b6a60e05..539e4156a1 100644 --- a/src/select.c +++ b/src/select.c @@ -4498,47 +4498,43 @@ select_end: /* ** Generate a human-readable description of a the Select object. */ -void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ - if( p==0 ){ - sqlite3ExplainPrintf(pVdbe, "(empty-select)"); - return; - } +static void explainOneSelect(Vdbe *pVdbe, Select *p){ sqlite3ExplainPrintf(pVdbe, "SELECT "); - sqlite3ExplainPush(pVdbe); sqlite3ExplainExprList(pVdbe, p->pEList); sqlite3ExplainNL(pVdbe); - sqlite3ExplainPop(pVdbe); if( p->pSrc && p->pSrc->nSrc ){ int i; - sqlite3ExplainPrintf(pVdbe, " FROM "); + sqlite3ExplainPrintf(pVdbe, "FROM "); sqlite3ExplainPush(pVdbe); for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - sqlite3ExplainPrintf(pVdbe, "%02d: ", i); - sqlite3ExplainPush(pVdbe); + sqlite3ExplainPrintf(pVdbe, "src[%d] = ", i); if( pItem->pSelect ){ sqlite3ExplainSelect(pVdbe, pItem->pSelect); }else if( pItem->zName ){ sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName); } if( pItem->pTab ){ - sqlite3ExplainPrintf(pVdbe, " (table: %s)", pItem->pTab->zName); + sqlite3ExplainPrintf(pVdbe, " (name=%s:%d)", + pItem->pTab->zName, pItem->iCursor); + } + if( pItem->jointype & JT_LEFT ){ + sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN"); } if( pItem->zAlias ){ sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); } sqlite3ExplainNL(pVdbe); - sqlite3ExplainPop(pVdbe); } sqlite3ExplainPop(pVdbe); } if( p->pWhere ){ - sqlite3ExplainPrintf(pVdbe, " WHERE "); + sqlite3ExplainPrintf(pVdbe, "WHERE "); sqlite3ExplainExpr(pVdbe, p->pWhere); sqlite3ExplainNL(pVdbe); } if( p->pGroupBy ){ - sqlite3ExplainPrintf(pVdbe, " GROUP BY "); + sqlite3ExplainPrintf(pVdbe, "GROUPBY "); sqlite3ExplainExprList(pVdbe, p->pGroupBy); sqlite3ExplainNL(pVdbe); } @@ -4548,11 +4544,37 @@ void sqlite3ExplainSelect(Vdbe *pVdbe, Select *p){ sqlite3ExplainNL(pVdbe); } if( p->pOrderBy ){ - sqlite3ExplainPrintf(pVdbe, " ORDER BY "); + sqlite3ExplainPrintf(pVdbe, "ORDERBY "); sqlite3ExplainExprList(pVdbe, p->pOrderBy); sqlite3ExplainNL(pVdbe); } - sqlite3ExplainPrintf(pVdbe, " END"); + 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 From e7e6a54504d246345e79384a5d28f11f18878b84 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Dec 2011 01:47:27 +0000 Subject: [PATCH 3/5] Bug fix in sqlite3SelectDup(). Make sure the pNext pointer is valid. FossilOrigin-Name: 7e5b56b1c602d4adfd4496a9c877f3b685b2d360 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/expr.c | 6 ++++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/manifest b/manifest index e924999ec2..8900db8ee7 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Improvements\sto\sthe\sdata-structure\sexplain\ssubsystem.\s\sMost\squeries\snow\ngive\sa\sreasonably\sdetailed\sgraph\sof\stheir\sparse\stree. -D 2011-12-07T01:23:51.800 +C Bug\sfix\sin\ssqlite3SelectDup().\s\sMake\ssure\sthe\spNext\spointer\sis\svalid. +D 2011-12-07T01:47:27.299 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 62f6ad2a1dcfbf684e6916c0662d5b4f28b98346 +F src/expr.c 3d800a2280d1ea4eab33ab500af59ebb7b878e64 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9 @@ -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 79ae51c5b1b20ed0a425a87e65a32a096a80b7e1 -R 33f29d39c143746ce94119c4aaec65fd +P 0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 +R 8f6a328e6230a075adb0590e80910ddf U drh -Z 8a3e65c823a12538f790d9d174308040 +Z 657cde2c7390fad792d576ed9952c864 diff --git a/manifest.uuid b/manifest.uuid index dbbd003458..e28fee5f60 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 \ No newline at end of file +7e5b56b1c602d4adfd4496a9c877f3b685b2d360 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 0d217ed405..09f07abf42 100644 --- a/src/expr.c +++ b/src/expr.c @@ -940,7 +940,7 @@ IdList *sqlite3IdListDup(sqlite3 *db, IdList *p){ return pNew; } Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ - Select *pNew; + Select *pNew, *pPrior; if( p==0 ) return 0; pNew = sqlite3DbMallocRaw(db, sizeof(*p) ); if( pNew==0 ) return 0; @@ -951,7 +951,9 @@ Select *sqlite3SelectDup(sqlite3 *db, Select *p, int flags){ pNew->pHaving = sqlite3ExprDup(db, p->pHaving, flags); pNew->pOrderBy = sqlite3ExprListDup(db, p->pOrderBy, flags); pNew->op = p->op; - pNew->pPrior = sqlite3SelectDup(db, p->pPrior, flags); + pNew->pPrior = pPrior = sqlite3SelectDup(db, p->pPrior, flags); + if( pPrior ) pPrior->pNext = pNew; + pNew->pNext = 0; pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); pNew->pOffset = sqlite3ExprDup(db, p->pOffset, flags); pNew->iLimit = 0; From 04b8342b8313dc7fe57a93a2cc7a2e9e5a2afd26 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Dec 2011 15:33:14 +0000 Subject: [PATCH 4/5] More compact notation for the parse-tree view. FossilOrigin-Name: 0eb3f8b1e3a196811fb54a5e2645debe6119610a --- manifest | 14 +++++++------- manifest.uuid | 2 +- src/expr.c | 26 +++++++++----------------- src/select.c | 13 ++++++------- 4 files changed, 23 insertions(+), 32 deletions(-) diff --git a/manifest b/manifest index 8900db8ee7..69c5847820 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Bug\sfix\sin\ssqlite3SelectDup().\s\sMake\ssure\sthe\spNext\spointer\sis\svalid. -D 2011-12-07T01:47:27.299 +C More\scompact\snotation\sfor\sthe\sparse-tree\sview. +D 2011-12-07T15:33:14.109 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 3d800a2280d1ea4eab33ab500af59ebb7b878e64 +F src/expr.c 59a530af612b95d4d29c066bf81f12def813d16d F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c 6261ce00aad9c63cd5b4219249b05683979060e9 @@ -180,7 +180,7 @@ F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c 352ac58d6070f9a5564e9648bc2202b9d6339747 +F src/select.c 984ff4d2865fbf13a0ddeedbf892a93094f2b9dd F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 @@ -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 0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 -R 8f6a328e6230a075adb0590e80910ddf +P 7e5b56b1c602d4adfd4496a9c877f3b685b2d360 +R 7004d54e96eda502d15d7553ec65e84c U drh -Z 657cde2c7390fad792d576ed9952c864 +Z 6c7ae5dd374ffa9280bde6b5186b14b5 diff --git a/manifest.uuid b/manifest.uuid index e28fee5f60..870fbb8eb1 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -7e5b56b1c602d4adfd4496a9c877f3b685b2d360 \ No newline at end of file +0eb3f8b1e3a196811fb54a5e2645debe6119610a \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 09f07abf42..6af583ce0a 100644 --- a/src/expr.c +++ b/src/expr.c @@ -2956,8 +2956,8 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ } switch( op ){ case TK_AGG_COLUMN: { - sqlite3ExplainPrintf(pOut, "AGG_COLUMN(%s:%d:%d)", - pExpr->pTab->zName, pExpr->iTable, pExpr->iColumn); + sqlite3ExplainPrintf(pOut, "AGG{%d:%d}", + pExpr->iTable, pExpr->iColumn); break; } case TK_COLUMN: { @@ -2965,27 +2965,27 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ /* This only happens when coding check constraints */ sqlite3ExplainPrintf(pOut, "COLUMN(%d)", pExpr->iColumn); }else{ - sqlite3ExplainPrintf(pOut, "COLUMN(%s:%d:%d)", - pExpr->pTab->zName, pExpr->iTable, pExpr->iColumn); + sqlite3ExplainPrintf(pOut, "{%d:%d}", + pExpr->iTable, pExpr->iColumn); } break; } case TK_INTEGER: { if( pExpr->flags & EP_IntValue ){ - sqlite3ExplainPrintf(pOut, "INTEGER(%d)", pExpr->u.iValue); + sqlite3ExplainPrintf(pOut, "%d", pExpr->u.iValue); }else{ - sqlite3ExplainPrintf(pOut, "INTEGER(%s)", pExpr->u.zToken); + sqlite3ExplainPrintf(pOut, "%s", pExpr->u.zToken); } break; } #ifndef SQLITE_OMIT_FLOATING_POINT case TK_FLOAT: { - sqlite3ExplainPrintf(pOut,"REAL(%s)", pExpr->u.zToken); + sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); break; } #endif case TK_STRING: { - sqlite3ExplainPrintf(pOut,"STRING(%s)", pExpr->u.zToken); + sqlite3ExplainPrintf(pOut,"%Q", pExpr->u.zToken); break; } case TK_NULL: { @@ -2994,15 +2994,7 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ } #ifndef SQLITE_OMIT_BLOB_LITERAL case TK_BLOB: { - int n; - const char *z; - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - assert( pExpr->u.zToken[0]=='x' || pExpr->u.zToken[0]=='X' ); - assert( pExpr->u.zToken[1]=='\'' ); - z = &pExpr->u.zToken[2]; - n = sqlite3Strlen30(z) - 1; - assert( z[n]=='\'' ); - sqlite3ExplainPrintf(pOut,"BLOB(%.*s)", n, z); + sqlite3ExplainPrintf(pOut,"%s", pExpr->u.zToken); break; } #endif diff --git a/src/select.c b/src/select.c index 539e4156a1..d51ae103ee 100644 --- a/src/select.c +++ b/src/select.c @@ -4508,22 +4508,21 @@ static void explainOneSelect(Vdbe *pVdbe, Select *p){ sqlite3ExplainPush(pVdbe); for(i=0; ipSrc->nSrc; i++){ struct SrcList_item *pItem = &p->pSrc->a[i]; - sqlite3ExplainPrintf(pVdbe, "src[%d] = ", i); + sqlite3ExplainPrintf(pVdbe, "{%d,*} = ", pItem->iCursor); if( pItem->pSelect ){ sqlite3ExplainSelect(pVdbe, pItem->pSelect); + if( pItem->pTab ){ + sqlite3ExplainPrintf(pVdbe, " (tabname=%s)", pItem->pTab->zName); + } }else if( pItem->zName ){ sqlite3ExplainPrintf(pVdbe, "%s", pItem->zName); } - if( pItem->pTab ){ - sqlite3ExplainPrintf(pVdbe, " (name=%s:%d)", - pItem->pTab->zName, pItem->iCursor); + if( pItem->zAlias ){ + sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); } if( pItem->jointype & JT_LEFT ){ sqlite3ExplainPrintf(pVdbe, " LEFT-JOIN"); } - if( pItem->zAlias ){ - sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias); - } sqlite3ExplainNL(pVdbe); } sqlite3ExplainPop(pVdbe); From 4e2a9c32721086d5f73f84c075c9883018038b89 Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 7 Dec 2011 22:49:42 +0000 Subject: [PATCH 5/5] Additional detail added to the tree-explain output for SELECT statements. FossilOrigin-Name: 7b457ea4551ba411a4747d74fb78b795cc8d9ee6 --- manifest | 12 ++++++------ manifest.uuid | 2 +- src/select.c | 10 ++++++++++ 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/manifest b/manifest index 69c5847820..3aac83707e 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C More\scompact\snotation\sfor\sthe\sparse-tree\sview. -D 2011-12-07T15:33:14.109 +C Additional\sdetail\sadded\sto\sthe\stree-explain\soutput\sfor\sSELECT\sstatements. +D 2011-12-07T22:49:42.588 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 5b4a3e12a850b021547e43daf886b25133b44c07 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -180,7 +180,7 @@ F src/printf.c 7ffb4ebb8b341f67e049695ba031da717b3d2699 F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 F src/resolve.c 365ab1c870e38596d6869e76fb544fe6e4ffc809 F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697 -F src/select.c 984ff4d2865fbf13a0ddeedbf892a93094f2b9dd +F src/select.c 912bea602425df403c464c7da9a89a56281623a3 F src/shell.c a1eadb2fdbfa45e54307263f0c8da8ee8cd61b8b F src/sqlite.h.in 1dc07194eb1a2c69c8ef75f88022b170be08024a F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 @@ -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 7e5b56b1c602d4adfd4496a9c877f3b685b2d360 -R 7004d54e96eda502d15d7553ec65e84c +P 0eb3f8b1e3a196811fb54a5e2645debe6119610a +R c8f99de136cf931e1dc991f6945ded2c U drh -Z 6c7ae5dd374ffa9280bde6b5186b14b5 +Z 90b0a98552fa9f4d4fff3f8a611f18ca diff --git a/manifest.uuid b/manifest.uuid index 870fbb8eb1..3c8a88f6d3 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -0eb3f8b1e3a196811fb54a5e2645debe6119610a \ No newline at end of file +7b457ea4551ba411a4747d74fb78b795cc8d9ee6 \ No newline at end of file diff --git a/src/select.c b/src/select.c index d51ae103ee..f761a08977 100644 --- a/src/select.c +++ b/src/select.c @@ -4500,6 +4500,16 @@ select_end: */ 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, " "); + } sqlite3ExplainExprList(pVdbe, p->pEList); sqlite3ExplainNL(pVdbe); if( p->pSrc && p->pSrc->nSrc ){