1
0
mirror of https://github.com/sqlite/sqlite.git synced 2025-11-06 15:49:35 +03:00

Begin adding the data-structure explaining subsystem. All is contained within

FossilOrigin-Name: 79ae51c5b1b20ed0a425a87e65a32a096a80b7e1
This commit is contained in:
drh
2011-12-06 19:44:51 +00:00
parent ed51f29774
commit 7e02e5e6b5
13 changed files with 297 additions and 89 deletions

View File

@@ -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

View File

@@ -1 +1 @@
162421dadf93e9201c3290d800c597cbeeacdb40
79ae51c5b1b20ed0a425a87e65a32a096a80b7e1

View 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:

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

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

@@ -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; i<pList->nExpr; i++){
sqlite3PrintExpr(pList->a[i].pExpr);
if( i<pList->nExpr-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; 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);
sqlite3ExplainPrintf(pVdbe, " (AS %s)", pItem->zAlias);
}
if( i<p->pSrc->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) */

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

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

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

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

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