From: drh Date: Wed, 7 Dec 2011 01:23:51 +0000 (+0000) Subject: Improvements to the data-structure explain subsystem. Most queries now X-Git-Tag: version-3.7.10~19^2~70^2~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a84203a0749e49cfff15fb99588a0158b6fd085e;p=thirdparty%2Fsqlite.git Improvements to the data-structure explain subsystem. Most queries now give a reasonably detailed graph of their parse tree. FossilOrigin-Name: 0aa7d3d2346bdddcc4e1e25ee26d13c8594885e5 --- 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