diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.c | 4 | ||||
-rw-r--r-- | src/pragma.c | 6 | ||||
-rw-r--r-- | src/shell.c | 103 | ||||
-rw-r--r-- | src/sqliteInt.h | 1 | ||||
-rw-r--r-- | src/vdbe.c | 66 | ||||
-rw-r--r-- | src/vdbe.h | 1 | ||||
-rw-r--r-- | src/vdbeInt.h | 3 | ||||
-rw-r--r-- | src/vdbeaux.c | 27 | ||||
-rw-r--r-- | src/where.c | 6 |
9 files changed, 160 insertions, 57 deletions
diff --git a/src/build.c b/src/build.c index c3999f2b9..d1615a128 100644 --- a/src/build.c +++ b/src/build.c @@ -193,10 +193,6 @@ void sqlite3FinishCoding(Parse *pParse){ /* Get the VDBE program ready for execution */ if( v && ALWAYS(pParse->nErr==0) && !db->mallocFailed ){ -#ifdef SQLITE_DEBUG - FILE *trace = (db->flags & SQLITE_VdbeTrace)!=0 ? stdout : 0; - sqlite3VdbeTrace(v, trace); -#endif assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ /* A minimum of one cursor is required if autoincrement is used * See ticket [a696379c1f08866] */ diff --git a/src/pragma.c b/src/pragma.c index 9211a2cb0..76a452c46 100644 --- a/src/pragma.c +++ b/src/pragma.c @@ -434,6 +434,10 @@ static const struct sPragmaNames { /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, /* iArg: */ SQLITE_SqlTrace|SQLITE_VdbeListing|SQLITE_VdbeTrace }, + { /* zName: */ "vdbe_eqp", + /* ePragTyp: */ PragTyp_FLAG, + /* ePragFlag: */ 0, + /* iArg: */ SQLITE_VdbeEQP }, { /* zName: */ "vdbe_listing", /* ePragTyp: */ PragTyp_FLAG, /* ePragFlag: */ 0, @@ -461,7 +465,7 @@ static const struct sPragmaNames { /* iArg: */ SQLITE_WriteSchema|SQLITE_RecoveryMode }, #endif }; -/* Number of pragmas: 56 on by default, 68 total. */ +/* Number of pragmas: 56 on by default, 69 total. */ /* End of the automatically generated pragma table. ***************************************************************************/ diff --git a/src/shell.c b/src/shell.c index c3aee0463..61df7d6d5 100644 --- a/src/shell.c +++ b/src/shell.c @@ -464,6 +464,8 @@ struct callback_data { const char *zVfs; /* Name of VFS to use */ sqlite3_stmt *pStmt; /* Current statement if any. */ FILE *pLog; /* Write log output here */ + int *aiIndent; /* Array of indents used in MODE_Explain */ + int nIndent; /* Size of array aiIndent[] */ }; /* @@ -765,10 +767,15 @@ static int shell_callback(void *pArg, int nArg, char **azArg, char **azCol, int }else{ w = 10; } - if( p->mode==MODE_Explain && azArg[i] && - strlen30(azArg[i])>w ){ + if( p->mode==MODE_Explain && azArg[i] && strlen30(azArg[i])>w ){ w = strlen30(azArg[i]); } + if( i==1 && p->aiIndent && p->pStmt ){ + int iOp = sqlite3_column_int(p->pStmt, 0); + if( iOp<p->nIndent ){ + fprintf(p->out, "%*.s", p->aiIndent[iOp], ""); + } + } if( w<0 ){ fprintf(p->out,"%*.*s%s",-w,-w, azArg[i] ? azArg[i] : p->nullvalue, i==nArg-1 ? "\n": " "); @@ -1142,6 +1149,90 @@ static int display_stats( } /* +** Parameter azArray points to a zero-terminated array of strings. zStr +** points to a single nul-terminated string. Return non-zero if zStr +** is equal, according to strcmp(), to any of the strings in the array. +** Otherwise, return zero. +*/ +static int str_in_array(const char *zStr, const char **azArray){ + int i; + for(i=0; azArray[i]; i++){ + if( 0==strcmp(zStr, azArray[i]) ) return 1; + } + return 0; +} + +/* +** If compiled statement pSql appears to be an EXPLAIN statement, allocate +** and populate the callback_data.aiIndent[] array with the number of +** spaces each opcode should be indented before it is output. +** +** The indenting rules are: +** +** * For each "Next", "Prev", "VNext" or "VPrev" instruction, indent +** all opcodes that occur between the p2 jump destination and the opcode +** itself by 2 spaces. +** +** * For each "Goto", if the jump destination is a "Yield", "SeekGt", +** or "SeekLt" instruction that occurs earlier in the program than +** the Goto itself, indent all opcodes between the earlier instruction +** and "Goto" by 2 spaces. +*/ +static void explain_data_prepare(struct callback_data *p, sqlite3_stmt *pSql){ + const char *zSql; /* The text of the SQL statement */ + const char *z; /* Used to check if this is an EXPLAIN */ + int *abYield = 0; /* True if op is an OP_Yield */ + int nAlloc = 0; /* Allocated size of p->aiIndent[], abYield */ + int iOp; + + const char *azNext[] = { "Next", "Prev", "VPrev", "VNext", 0 }; + const char *azYield[] = { "Yield", "SeekLt", "SeekGt", 0 }; + const char *azGoto[] = { "Goto", 0 }; + + /* Try to figure out if this is really an EXPLAIN statement. If this + ** cannot be verified, return early. */ + zSql = sqlite3_sql(pSql); + if( zSql==0 ) return; + for(z=zSql; *z==' ' || *z=='\t' || *z=='\n' || *z=='\f' || *z=='\r'; z++); + if( sqlite3_strnicmp(z, "explain", 7) ) return; + + for(iOp=0; SQLITE_ROW==sqlite3_step(pSql); iOp++){ + int i; + const char *zOp = (const char*)sqlite3_column_text(pSql, 1); + int p2 = sqlite3_column_int(pSql, 3); + + /* Grow the p->aiIndent array as required */ + if( iOp>=nAlloc ){ + nAlloc += 100; + p->aiIndent = (int*)sqlite3_realloc(p->aiIndent, nAlloc*sizeof(int)); + abYield = (int*)sqlite3_realloc(abYield, nAlloc*sizeof(int)); + } + abYield[iOp] = str_in_array(zOp, azYield); + p->aiIndent[iOp] = 0; + p->nIndent = iOp+1; + + if( str_in_array(zOp, azNext) ){ + for(i=p2; i<iOp; i++) p->aiIndent[i] += 2; + } + if( str_in_array(zOp, azGoto) && p2<p->nIndent && abYield[p2] ){ + for(i=p2+1; i<iOp; i++) p->aiIndent[i] += 2; + } + } + + sqlite3_free(abYield); + sqlite3_reset(pSql); +} + +/* +** Free the array allocated by explain_data_prepare(). +*/ +static void explain_data_delete(struct callback_data *p){ + sqlite3_free(p->aiIndent); + p->aiIndent = 0; + p->nIndent = 0; +} + +/* ** Execute a statement or set of statements. Print ** any result rows/columns depending on the current mode ** set via the supplied callback. @@ -1202,6 +1293,12 @@ static int shell_exec( } } + /* If the shell is currently in ".explain" mode, gather the extra + ** data required to add indents to the output.*/ + if( pArg->mode==MODE_Explain ){ + explain_data_prepare(pArg, pStmt); + } + /* perform the first step. this will tell us if we ** have a result set or not and how wide it is. */ @@ -1259,6 +1356,8 @@ static int shell_exec( } } + explain_data_delete(pArg); + /* print usage stats if stats on */ if( pArg && pArg->statsOn ){ display_stats(db, pArg, 0); diff --git a/src/sqliteInt.h b/src/sqliteInt.h index 0ae55edf3..8838f7aa9 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -1043,6 +1043,7 @@ struct sqlite3 { #define SQLITE_EnableTrigger 0x00800000 /* True to enable triggers */ #define SQLITE_DeferFKs 0x01000000 /* Defer all FK constraints */ #define SQLITE_QueryOnly 0x02000000 /* Disable database changes */ +#define SQLITE_VdbeEQP 0x04000000 /* Debug EXPLAIN QUERY PLAN */ /* diff --git a/src/vdbe.c b/src/vdbe.c index d3fe0a450..c73bf5963 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -417,37 +417,36 @@ void sqlite3VdbeMemPrettyPrint(Mem *pMem, char *zBuf){ /* ** Print the value of a register for tracing purposes: */ -static void memTracePrint(FILE *out, Mem *p){ +static void memTracePrint(Mem *p){ if( p->flags & MEM_Invalid ){ - fprintf(out, " undefined"); + printf(" undefined"); }else if( p->flags & MEM_Null ){ - fprintf(out, " NULL"); + printf(" NULL"); }else if( (p->flags & (MEM_Int|MEM_Str))==(MEM_Int|MEM_Str) ){ - fprintf(out, " si:%lld", p->u.i); + printf(" si:%lld", p->u.i); }else if( p->flags & MEM_Int ){ - fprintf(out, " i:%lld", p->u.i); + printf(" i:%lld", p->u.i); #ifndef SQLITE_OMIT_FLOATING_POINT }else if( p->flags & MEM_Real ){ - fprintf(out, " r:%g", p->r); + printf(" r:%g", p->r); #endif }else if( p->flags & MEM_RowSet ){ - fprintf(out, " (rowset)"); + printf(" (rowset)"); }else{ char zBuf[200]; sqlite3VdbeMemPrettyPrint(p, zBuf); - fprintf(out, " "); - fprintf(out, "%s", zBuf); + printf(" %s", zBuf); } } -static void registerTrace(FILE *out, int iReg, Mem *p){ - fprintf(out, "REG[%d] = ", iReg); - memTracePrint(out, p); - fprintf(out, "\n"); +static void registerTrace(int iReg, Mem *p){ + printf("REG[%d] = ", iReg); + memTracePrint(p); + printf("\n"); } #endif #ifdef SQLITE_DEBUG -# define REGISTER_TRACE(R,M) if(p->trace)registerTrace(p->trace,R,M) +# define REGISTER_TRACE(R,M) if(db->flags&SQLITE_VdbeTrace)registerTrace(R,M) #else # define REGISTER_TRACE(R,M) #endif @@ -586,13 +585,28 @@ int sqlite3VdbeExec( #endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); - if( p->pc==0 && (p->db->flags & SQLITE_VdbeListing)!=0 ){ + if( p->pc==0 + && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0 + ){ int i; - printf("VDBE Program Listing:\n"); + int once = 1; sqlite3VdbePrintSql(p); - for(i=0; i<p->nOp; i++){ - sqlite3VdbePrintOp(stdout, i, &aOp[i]); + if( p->db->flags & SQLITE_VdbeListing ){ + printf("VDBE Program Listing:\n"); + for(i=0; i<p->nOp; i++){ + sqlite3VdbePrintOp(stdout, i, &aOp[i]); + } + } + if( p->db->flags & SQLITE_VdbeEQP ){ + for(i=0; i<p->nOp; i++){ + if( aOp[i].opcode==OP_Explain ){ + if( once ) printf("VDBE Query Plan:\n"); + printf("%s\n", aOp[i].p4.z); + once = 0; + } + } } + if( p->db->flags & SQLITE_VdbeTrace ) printf("VDBE Trace:\n"); } sqlite3EndBenignMalloc(); #endif @@ -609,12 +623,8 @@ int sqlite3VdbeExec( /* Only allow tracing if SQLITE_DEBUG is defined. */ #ifdef SQLITE_DEBUG - if( p->trace ){ - if( pc==0 ){ - printf("VDBE Execution Trace:\n"); - sqlite3VdbePrintSql(p); - } - sqlite3VdbePrintOp(p->trace, pc, pOp); + if( db->flags & SQLITE_VdbeTrace ){ + sqlite3VdbePrintOp(stdout, pc, pOp); } #endif @@ -6255,13 +6265,13 @@ default: { /* This is really OP_Noop and OP_Explain */ assert( pc>=-1 && pc<p->nOp ); #ifdef SQLITE_DEBUG - if( p->trace ){ - if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc); + if( db->flags & SQLITE_VdbeTrace ){ + if( rc!=0 ) printf("rc=%d\n",rc); if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ - registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]); + registerTrace(pOp->p2, &aMem[pOp->p2]); } if( pOp->opflags & OPFLG_OUT3 ){ - registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]); + registerTrace(pOp->p3, &aMem[pOp->p3]); } } #endif /* SQLITE_DEBUG */ diff --git a/src/vdbe.h b/src/vdbe.h index 91d6a0f79..62d9aa271 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -189,7 +189,6 @@ void sqlite3VdbeResolveLabel(Vdbe*, int); int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG int sqlite3VdbeAssertMayAbort(Vdbe *, int); - void sqlite3VdbeTrace(Vdbe*,FILE*); #endif void sqlite3VdbeResetStepResult(Vdbe*); void sqlite3VdbeRewind(Vdbe*); diff --git a/src/vdbeInt.h b/src/vdbeInt.h index b7f5ab1a3..4f63189f5 100644 --- a/src/vdbeInt.h +++ b/src/vdbeInt.h @@ -355,9 +355,6 @@ struct Vdbe { i64 nStmtDefImmCons; /* Number of def. imm constraints when stmt started */ char *zSql; /* Text of the SQL statement that generated this */ 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 */ diff --git a/src/vdbeaux.c b/src/vdbeaux.c index 7ce9c2a6c..166cf7508 100644 --- a/src/vdbeaux.c +++ b/src/vdbeaux.c @@ -78,15 +78,6 @@ void sqlite3VdbeSwap(Vdbe *pA, Vdbe *pB){ pB->isPrepareV2 = pA->isPrepareV2; } -#ifdef SQLITE_DEBUG -/* -** Turn tracing on or off -*/ -void sqlite3VdbeTrace(Vdbe *p, FILE *trace){ - p->trace = trace; -} -#endif - /* ** Resize the Vdbe.aOp array so that it is at least one op larger than ** it was. @@ -1415,15 +1406,17 @@ int sqlite3VdbeList( ** Print the SQL that was used to generate a VDBE program. */ void sqlite3VdbePrintSql(Vdbe *p){ - int nOp = p->nOp; - VdbeOp *pOp; - if( nOp<1 ) return; - pOp = &p->aOp[0]; - if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ - const char *z = pOp->p4.z; - while( sqlite3Isspace(*z) ) z++; - printf("SQL: [%s]\n", z); + const char *z = 0; + if( p->zSql ){ + z = p->zSql; + }else if( p->nOp>=1 ){ + const VdbeOp *pOp = &p->aOp[0]; + if( pOp->opcode==OP_Trace && pOp->p4.z!=0 ){ + z = pOp->p4.z; + while( sqlite3Isspace(*z) ) z++; + } } + if( z ) printf("SQL: [%s]\n", z); } #endif diff --git a/src/where.c b/src/where.c index 2fd7c600c..c4f25675c 100644 --- a/src/where.c +++ b/src/where.c @@ -2650,7 +2650,10 @@ static void explainOneScan( int iFrom, /* Value for "from" column of output */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ){ - if( pParse->explain==2 ){ +#ifndef SQLITE_DEBUG + if( pParse->explain==2 ) +#endif + { struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom]; Vdbe *v = pParse->pVdbe; /* VM being constructed */ sqlite3 *db = pParse->db; /* Database handle */ @@ -3852,6 +3855,7 @@ static void whereLoopOutputAdjust(WhereClause *pWC, WhereLoop *pLoop){ if( (pTerm->prereqAll & notAllowed)!=0 ) continue; for(j=pLoop->nLTerm-1; j>=0; j--){ pX = pLoop->aLTerm[j]; + if( pX==0 ) continue; if( pX==pTerm ) break; if( pX->iParent>=0 && (&pWC->a[pX->iParent])==pTerm ) break; } |