1
0
mirror of https://github.com/sqlite/sqlite.git synced 2026-01-06 08:01:16 +03:00

Import the experimental parse-tree explainer, with fixes, from the

tree-explain branch.

FossilOrigin-Name: bcbc7152d49107afa926c8950360c61a6cf3d244
This commit is contained in:
drh
2011-12-10 15:55:01 +00:00
13 changed files with 544 additions and 93 deletions

View File

@@ -2949,6 +2949,264 @@ int sqlite3ExprCodeAndCache(Parse *pParse, Expr *pExpr, int target){
return inReg;
}
#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_ENABLE_TREE_EXPLAIN) */
#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:

View File

@@ -2936,6 +2936,22 @@ int sqlite3_test_control(int op, ...){
break;
}
#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 */

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

View 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);

View File

@@ -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) */

View 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.
*/

View 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

View File

@@ -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 @@ char *sqlite3MAppendf(sqlite3*,char*,const char*,...);
#if defined(SQLITE_TEST)
void *sqlite3TestTextToPtr(const char*);
#endif
/* 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*);
@@ -3032,6 +3055,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);

View 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
@@ -257,6 +260,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.
@@ -322,6 +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 */

View File

@@ -2474,6 +2474,10 @@ void sqlite3VdbeDeleteObject(sqlite3 *db, Vdbe *p){
sqlite3DbFree(db, p->aColName);
sqlite3DbFree(db, p->zSql);
sqlite3DbFree(db, p->pFree);
#if defined(SQLITE_ENABLE_TREE_EXPLAIN)
sqlite3DbFree(db, p->zExplain);
sqlite3DbFree(db, p->pExplain);
#endif
sqlite3DbFree(db, p);
}

View 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 parse-tree 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_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) */