mirror of
https://github.com/sqlite/sqlite.git
synced 2025-11-12 13:01:09 +03:00
Overhaul of EXPLAIN QUERY PLAN. The output is now in the form of a tree.
More details of the query plan are shown, and what is shown is truer to what actually happens. FossilOrigin-Name: ff01bbdabc4b9db3db8b928979442c91b32d72082158e4f5fe62ae51a73649d2
This commit is contained in:
34
src/expr.c
34
src/expr.c
@@ -2410,11 +2410,8 @@ int sqlite3FindInIndex(
|
||||
if( colUsed==(MASKBIT(nExpr)-1) ){
|
||||
/* If we reach this point, that means the index pIdx is usable */
|
||||
int iAddr = sqlite3VdbeAddOp0(v, OP_Once); VdbeCoverage(v);
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, 0, 0, 0,
|
||||
sqlite3MPrintf(db, "USING INDEX %s FOR IN-OPERATOR",pIdx->zName),
|
||||
P4_DYNAMIC);
|
||||
#endif
|
||||
ExplainQueryPlan((pParse, 0,
|
||||
"USING INDEX %s FOR IN-OPERATOR",pIdx->zName));
|
||||
sqlite3VdbeAddOp3(v, OP_OpenRead, iTab, pIdx->tnum, iDb);
|
||||
sqlite3VdbeSetP4KeyInfo(pParse, pIdx);
|
||||
VdbeComment((v, "%s", pIdx->zName));
|
||||
@@ -2646,17 +2643,9 @@ int sqlite3CodeSubselect(
|
||||
Select *pSelect = pExpr->x.pSelect;
|
||||
ExprList *pEList = pSelect->pEList;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %sLIST SUBQUERY %d",
|
||||
jmpIfDynamic>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
ExplainQueryPlan((pParse, 1, "%sLIST SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "
|
||||
));
|
||||
assert( !isRowid );
|
||||
/* If the LHS and RHS of the IN operator do not match, that
|
||||
** error will have been caught long before we reach this point. */
|
||||
@@ -2777,18 +2766,9 @@ int sqlite3CodeSubselect(
|
||||
assert( pExpr->op==TK_EXISTS || pExpr->op==TK_SELECT );
|
||||
assert( ExprHasProperty(pExpr, EP_xIsSelect) );
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "EXECUTE %sSCALAR SUBQUERY %d",
|
||||
jmpIfDynamic>=0?"":"CORRELATED ",
|
||||
pParse->iNextSelectId
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg,
|
||||
P4_DYNAMIC);
|
||||
}
|
||||
#endif
|
||||
|
||||
pSel = pExpr->x.pSelect;
|
||||
ExplainQueryPlan((pParse, 1, "%sSCALAR SUBQUERY",
|
||||
jmpIfDynamic>=0?"":"CORRELATED "));
|
||||
nReg = pExpr->op==TK_SELECT ? pSel->pEList->nExpr : 1;
|
||||
sqlite3SelectDestInit(&dest, 0, pParse->nMem+1);
|
||||
pParse->nMem += nReg;
|
||||
|
||||
@@ -616,7 +616,7 @@ static int sqlite3Prepare(
|
||||
if( rc==SQLITE_OK && sParse.pVdbe && sParse.explain ){
|
||||
static const char * const azColName[] = {
|
||||
"addr", "opcode", "p1", "p2", "p3", "p4", "p5", "comment",
|
||||
"selectid", "order", "from", "detail"
|
||||
"id", "parent", "notused", "detail"
|
||||
};
|
||||
int iFirst, mx;
|
||||
if( sParse.explain==2 ){
|
||||
|
||||
503
src/select.c
503
src/select.c
@@ -21,7 +21,7 @@
|
||||
/***/ int sqlite3SelectTrace = 0;
|
||||
# define SELECTTRACE(K,P,S,X) \
|
||||
if(sqlite3SelectTrace&(K)) \
|
||||
sqlite3DebugPrintf("%s/%d/%p: ",(S)->zSelName,(P)->iSelectId,(S)),\
|
||||
sqlite3DebugPrintf("%s/%d/%p: ",(S)->zSelName,(P)->addrExplain,(S)),\
|
||||
sqlite3DebugPrintf X
|
||||
#else
|
||||
# define SELECTTRACE(K,P,S,X)
|
||||
@@ -1293,11 +1293,7 @@ static const char *selectOpName(int id){
|
||||
** is determined by the zUsage argument.
|
||||
*/
|
||||
static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(pParse->db, "USE TEMP B-TREE FOR %s", zUsage);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
ExplainQueryPlan((pParse, 0, "USE TEMP B-TREE FOR %s", zUsage));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1315,42 +1311,6 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
|
||||
# define explainSetInteger(y,z)
|
||||
#endif
|
||||
|
||||
#if !defined(SQLITE_OMIT_EXPLAIN) && !defined(SQLITE_OMIT_COMPOUND_SELECT)
|
||||
/*
|
||||
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
|
||||
** is a no-op. Otherwise, it adds a single row of output to the EQP result,
|
||||
** where the caption is of one of the two forms:
|
||||
**
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 (op)"
|
||||
** "COMPOSITE SUBQUERIES iSub1 and iSub2 USING TEMP B-TREE (op)"
|
||||
**
|
||||
** where iSub1 and iSub2 are the integers passed as the corresponding
|
||||
** function parameters, and op is the text representation of the parameter
|
||||
** of the same name. The parameter "op" must be one of TK_UNION, TK_EXCEPT,
|
||||
** TK_INTERSECT or TK_ALL. The first form is used if argument bUseTmp is
|
||||
** false, or the second form if it is true.
|
||||
*/
|
||||
static void explainComposite(
|
||||
Parse *pParse, /* Parse context */
|
||||
int op, /* One of TK_UNION, TK_EXCEPT etc. */
|
||||
int iSub1, /* Subquery id 1 */
|
||||
int iSub2, /* Subquery id 2 */
|
||||
int bUseTmp /* True if a temp table was used */
|
||||
){
|
||||
assert( op==TK_UNION || op==TK_EXCEPT || op==TK_INTERSECT || op==TK_ALL );
|
||||
if( pParse->explain==2 ){
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
char *zMsg = sqlite3MPrintf(
|
||||
pParse->db, "COMPOUND SUBQUERIES %d AND %d %s(%s)", iSub1, iSub2,
|
||||
bUseTmp?"USING TEMP B-TREE ":"", selectOpName(op)
|
||||
);
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, pParse->iSelectId, 0, 0, zMsg, P4_DYNAMIC);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* No-op versions of the explainXXX() functions and macros. */
|
||||
# define explainComposite(v,w,x,y,z)
|
||||
#endif
|
||||
|
||||
/*
|
||||
** If the inner loop was generated using a non-null pOrderBy argument,
|
||||
@@ -2327,6 +2287,7 @@ static void generateWithRecursiveQuery(
|
||||
|
||||
/* Store the results of the setup-query in Queue. */
|
||||
pSetup->pNext = 0;
|
||||
ExplainQueryPlan((pParse, 1, "SETUP"));
|
||||
rc = sqlite3Select(pParse, pSetup, &destQueue);
|
||||
pSetup->pNext = p;
|
||||
if( rc ) goto end_of_recursive_query;
|
||||
@@ -2361,6 +2322,7 @@ static void generateWithRecursiveQuery(
|
||||
sqlite3ErrorMsg(pParse, "recursive aggregate queries not supported");
|
||||
}else{
|
||||
p->pPrior = 0;
|
||||
ExplainQueryPlan((pParse, 1, "RECURSIVE STEP"));
|
||||
sqlite3Select(pParse, p, &destQueue);
|
||||
assert( p->pPrior==0 );
|
||||
p->pPrior = pSetup;
|
||||
@@ -2406,10 +2368,9 @@ static int multiSelectValues(
|
||||
Select *p, /* The right-most of SELECTs to be coded */
|
||||
SelectDest *pDest /* What to do with query results */
|
||||
){
|
||||
Select *pPrior;
|
||||
Select *pRightmost = p;
|
||||
int nRow = 1;
|
||||
int rc = 0;
|
||||
int bShowAll = p->pLimit==0;
|
||||
assert( p->selFlags & SF_MultiValue );
|
||||
do{
|
||||
assert( p->selFlags & SF_Values );
|
||||
@@ -2418,14 +2379,13 @@ static int multiSelectValues(
|
||||
if( p->pPrior==0 ) break;
|
||||
assert( p->pPrior->pNext==p );
|
||||
p = p->pPrior;
|
||||
nRow++;
|
||||
nRow += bShowAll;
|
||||
}while(1);
|
||||
ExplainQueryPlan((pParse, 0, "SCAN %d CONSTANT ROW%s", nRow,
|
||||
nRow==1 ? "" : "S"));
|
||||
while( p ){
|
||||
pPrior = p->pPrior;
|
||||
p->pPrior = 0;
|
||||
rc = sqlite3Select(pParse, p, pDest);
|
||||
p->pPrior = pPrior;
|
||||
if( rc || pRightmost->pLimit ) break;
|
||||
selectInnerLoop(pParse, p, -1, 0, 0, pDest, 1, 1);
|
||||
if( !bShowAll ) break;
|
||||
p->nSelectRow = nRow;
|
||||
p = p->pNext;
|
||||
}
|
||||
@@ -2474,10 +2434,6 @@ static int multiSelect(
|
||||
SelectDest dest; /* Alternative data destination */
|
||||
Select *pDelete = 0; /* Chain of simple selects to delete */
|
||||
sqlite3 *db; /* Database connection */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1 = 0; /* EQP id of left-hand query */
|
||||
int iSub2 = 0; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
/* Make sure there is no ORDER BY or LIMIT clause on prior SELECTs. Only
|
||||
** the last (right-most) SELECT in the series may have an ORDER BY or LIMIT.
|
||||
@@ -2528,217 +2484,231 @@ static int multiSelect(
|
||||
*/
|
||||
if( p->pOrderBy ){
|
||||
return multiSelectOrderBy(pParse, p, pDest);
|
||||
}else
|
||||
}else{
|
||||
|
||||
/* Generate code for the left and right SELECT statements.
|
||||
*/
|
||||
switch( p->op ){
|
||||
case TK_ALL: {
|
||||
int addr = 0;
|
||||
int nLimit;
|
||||
assert( !pPrior->pLimit );
|
||||
pPrior->iLimit = p->iLimit;
|
||||
pPrior->iOffset = p->iOffset;
|
||||
pPrior->pLimit = p->pLimit;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &dest);
|
||||
p->pLimit = 0;
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
p->pPrior = 0;
|
||||
p->iLimit = pPrior->iLimit;
|
||||
p->iOffset = pPrior->iOffset;
|
||||
if( p->iLimit ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
||||
VdbeComment((v, "Jump ahead if LIMIT reached"));
|
||||
if( p->iOffset ){
|
||||
sqlite3VdbeAddOp3(v, OP_OffsetLimit,
|
||||
p->iLimit, p->iOffset+1, p->iOffset);
|
||||
}
|
||||
}
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
if( pPrior->pLimit
|
||||
&& sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
|
||||
&& nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
|
||||
){
|
||||
p->nSelectRow = sqlite3LogEst((u64)nLimit);
|
||||
}
|
||||
if( addr ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
break;
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( pPrior->pPrior==0 ){
|
||||
ExplainQueryPlan((pParse, 1, "COMPOUND QUERY"));
|
||||
ExplainQueryPlan((pParse, 1, "LEFT-MOST SUBQUERY"));
|
||||
}
|
||||
case TK_EXCEPT:
|
||||
case TK_UNION: {
|
||||
int unionTab; /* Cursor number of the temporary table holding result */
|
||||
u8 op = 0; /* One of the SRT_ operations to apply to self */
|
||||
int priorOp; /* The SRT_ operation to apply to prior selects */
|
||||
Expr *pLimit; /* Saved values of p->nLimit */
|
||||
int addr;
|
||||
SelectDest uniondest;
|
||||
#endif
|
||||
|
||||
testcase( p->op==TK_EXCEPT );
|
||||
testcase( p->op==TK_UNION );
|
||||
priorOp = SRT_Union;
|
||||
if( dest.eDest==priorOp ){
|
||||
/* We can reuse a temporary table generated by a SELECT to our
|
||||
** right.
|
||||
/* Generate code for the left and right SELECT statements.
|
||||
*/
|
||||
switch( p->op ){
|
||||
case TK_ALL: {
|
||||
int addr = 0;
|
||||
int nLimit;
|
||||
assert( !pPrior->pLimit );
|
||||
pPrior->iLimit = p->iLimit;
|
||||
pPrior->iOffset = p->iOffset;
|
||||
pPrior->pLimit = p->pLimit;
|
||||
rc = sqlite3Select(pParse, pPrior, &dest);
|
||||
p->pLimit = 0;
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
p->pPrior = 0;
|
||||
p->iLimit = pPrior->iLimit;
|
||||
p->iOffset = pPrior->iOffset;
|
||||
if( p->iLimit ){
|
||||
addr = sqlite3VdbeAddOp1(v, OP_IfNot, p->iLimit); VdbeCoverage(v);
|
||||
VdbeComment((v, "Jump ahead if LIMIT reached"));
|
||||
if( p->iOffset ){
|
||||
sqlite3VdbeAddOp3(v, OP_OffsetLimit,
|
||||
p->iLimit, p->iOffset+1, p->iOffset);
|
||||
}
|
||||
}
|
||||
ExplainQueryPlan((pParse, 1, "UNION ALL"));
|
||||
rc = sqlite3Select(pParse, p, &dest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
if( pPrior->pLimit
|
||||
&& sqlite3ExprIsInteger(pPrior->pLimit->pLeft, &nLimit)
|
||||
&& nLimit>0 && p->nSelectRow > sqlite3LogEst((u64)nLimit)
|
||||
){
|
||||
p->nSelectRow = sqlite3LogEst((u64)nLimit);
|
||||
}
|
||||
if( addr ){
|
||||
sqlite3VdbeJumpHere(v, addr);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TK_EXCEPT:
|
||||
case TK_UNION: {
|
||||
int unionTab; /* Cursor number of the temp table holding result */
|
||||
u8 op = 0; /* One of the SRT_ operations to apply to self */
|
||||
int priorOp; /* The SRT_ operation to apply to prior selects */
|
||||
Expr *pLimit; /* Saved values of p->nLimit */
|
||||
int addr;
|
||||
SelectDest uniondest;
|
||||
|
||||
testcase( p->op==TK_EXCEPT );
|
||||
testcase( p->op==TK_UNION );
|
||||
priorOp = SRT_Union;
|
||||
if( dest.eDest==priorOp ){
|
||||
/* We can reuse a temporary table generated by a SELECT to our
|
||||
** right.
|
||||
*/
|
||||
assert( p->pLimit==0 ); /* Not allowed on leftward elements */
|
||||
unionTab = dest.iSDParm;
|
||||
}else{
|
||||
/* We will need to create our own temporary table to hold the
|
||||
** intermediate results.
|
||||
*/
|
||||
unionTab = pParse->nTab++;
|
||||
assert( p->pOrderBy==0 );
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
|
||||
assert( p->addrOpenEphm[0] == -1 );
|
||||
p->addrOpenEphm[0] = addr;
|
||||
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
||||
assert( p->pEList );
|
||||
}
|
||||
|
||||
/* Code the SELECT statements to our left
|
||||
*/
|
||||
assert( p->pLimit==0 ); /* Not allowed on leftward elements */
|
||||
unionTab = dest.iSDParm;
|
||||
}else{
|
||||
/* We will need to create our own temporary table to hold the
|
||||
** intermediate results.
|
||||
assert( !pPrior->pOrderBy );
|
||||
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
||||
rc = sqlite3Select(pParse, pPrior, &uniondest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
|
||||
/* Code the current SELECT statement
|
||||
*/
|
||||
unionTab = pParse->nTab++;
|
||||
if( p->op==TK_EXCEPT ){
|
||||
op = SRT_Except;
|
||||
}else{
|
||||
assert( p->op==TK_UNION );
|
||||
op = SRT_Union;
|
||||
}
|
||||
p->pPrior = 0;
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
uniondest.eDest = op;
|
||||
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
||||
selectOpName(p->op)));
|
||||
rc = sqlite3Select(pParse, p, &uniondest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
||||
** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
|
||||
sqlite3ExprListDelete(db, p->pOrderBy);
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->pOrderBy = 0;
|
||||
if( p->op==TK_UNION ){
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
}
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->iLimit = 0;
|
||||
p->iOffset = 0;
|
||||
|
||||
/* Convert the data in the temporary table into whatever form
|
||||
** it is that we currently need.
|
||||
*/
|
||||
assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
|
||||
if( dest.eDest!=priorOp ){
|
||||
int iCont, iBreak, iStart;
|
||||
assert( p->pEList );
|
||||
iBreak = sqlite3VdbeMakeLabel(v);
|
||||
iCont = sqlite3VdbeMakeLabel(v);
|
||||
computeLimitRegisters(pParse, p, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
|
||||
iStart = sqlite3VdbeCurrentAddr(v);
|
||||
selectInnerLoop(pParse, p, unionTab,
|
||||
0, 0, &dest, iCont, iBreak);
|
||||
sqlite3VdbeResolveLabel(v, iCont);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
|
||||
sqlite3VdbeResolveLabel(v, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert( p->op==TK_INTERSECT ); {
|
||||
int tab1, tab2;
|
||||
int iCont, iBreak, iStart;
|
||||
Expr *pLimit;
|
||||
int addr;
|
||||
SelectDest intersectdest;
|
||||
int r1;
|
||||
|
||||
/* INTERSECT is different from the others since it requires
|
||||
** two temporary tables. Hence it has its own case. Begin
|
||||
** by allocating the tables we will need.
|
||||
*/
|
||||
tab1 = pParse->nTab++;
|
||||
tab2 = pParse->nTab++;
|
||||
assert( p->pOrderBy==0 );
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, unionTab, 0);
|
||||
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
|
||||
assert( p->addrOpenEphm[0] == -1 );
|
||||
p->addrOpenEphm[0] = addr;
|
||||
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
||||
assert( p->pEList );
|
||||
}
|
||||
|
||||
/* Code the SELECT statements to our left
|
||||
*/
|
||||
assert( !pPrior->pOrderBy );
|
||||
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &uniondest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
|
||||
/* Code the current SELECT statement
|
||||
*/
|
||||
if( p->op==TK_EXCEPT ){
|
||||
op = SRT_Except;
|
||||
}else{
|
||||
assert( p->op==TK_UNION );
|
||||
op = SRT_Union;
|
||||
}
|
||||
p->pPrior = 0;
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
uniondest.eDest = op;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &uniondest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
/* Query flattening in sqlite3Select() might refill p->pOrderBy.
|
||||
** Be sure to delete p->pOrderBy, therefore, to avoid a memory leak. */
|
||||
sqlite3ExprListDelete(db, p->pOrderBy);
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
p->pOrderBy = 0;
|
||||
if( p->op==TK_UNION ){
|
||||
p->nSelectRow = sqlite3LogEstAdd(p->nSelectRow, pPrior->nSelectRow);
|
||||
}
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
p->iLimit = 0;
|
||||
p->iOffset = 0;
|
||||
|
||||
/* Convert the data in the temporary table into whatever form
|
||||
** it is that we currently need.
|
||||
*/
|
||||
assert( unionTab==dest.iSDParm || dest.eDest!=priorOp );
|
||||
if( dest.eDest!=priorOp ){
|
||||
int iCont, iBreak, iStart;
|
||||
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
||||
rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
|
||||
/* Code the current SELECT into temporary table "tab2"
|
||||
*/
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
|
||||
assert( p->addrOpenEphm[1] == -1 );
|
||||
p->addrOpenEphm[1] = addr;
|
||||
p->pPrior = 0;
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
intersectdest.iSDParm = tab2;
|
||||
ExplainQueryPlan((pParse, 1, "%s USING TEMP B-TREE",
|
||||
selectOpName(p->op)));
|
||||
rc = sqlite3Select(pParse, p, &intersectdest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
if( p->nSelectRow>pPrior->nSelectRow ){
|
||||
p->nSelectRow = pPrior->nSelectRow;
|
||||
}
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
|
||||
/* Generate code to take the intersection of the two temporary
|
||||
** tables.
|
||||
*/
|
||||
assert( p->pEList );
|
||||
iBreak = sqlite3VdbeMakeLabel(v);
|
||||
iCont = sqlite3VdbeMakeLabel(v);
|
||||
computeLimitRegisters(pParse, p, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, unionTab, iBreak); VdbeCoverage(v);
|
||||
iStart = sqlite3VdbeCurrentAddr(v);
|
||||
selectInnerLoop(pParse, p, unionTab,
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0);
|
||||
VdbeCoverage(v);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
selectInnerLoop(pParse, p, tab1,
|
||||
0, 0, &dest, iCont, iBreak);
|
||||
sqlite3VdbeResolveLabel(v, iCont);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, unionTab, iStart); VdbeCoverage(v);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
|
||||
sqlite3VdbeResolveLabel(v, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, unionTab, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: assert( p->op==TK_INTERSECT ); {
|
||||
int tab1, tab2;
|
||||
int iCont, iBreak, iStart;
|
||||
Expr *pLimit;
|
||||
int addr;
|
||||
SelectDest intersectdest;
|
||||
int r1;
|
||||
|
||||
/* INTERSECT is different from the others since it requires
|
||||
** two temporary tables. Hence it has its own case. Begin
|
||||
** by allocating the tables we will need.
|
||||
*/
|
||||
tab1 = pParse->nTab++;
|
||||
tab2 = pParse->nTab++;
|
||||
assert( p->pOrderBy==0 );
|
||||
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab1, 0);
|
||||
assert( p->addrOpenEphm[0] == -1 );
|
||||
p->addrOpenEphm[0] = addr;
|
||||
findRightmost(p)->selFlags |= SF_UsesEphemeral;
|
||||
assert( p->pEList );
|
||||
|
||||
/* Code the SELECTs to our left into temporary table "tab1".
|
||||
*/
|
||||
sqlite3SelectDestInit(&intersectdest, SRT_Union, tab1);
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, pPrior, &intersectdest);
|
||||
if( rc ){
|
||||
goto multi_select_end;
|
||||
}
|
||||
|
||||
/* Code the current SELECT into temporary table "tab2"
|
||||
*/
|
||||
addr = sqlite3VdbeAddOp2(v, OP_OpenEphemeral, tab2, 0);
|
||||
assert( p->addrOpenEphm[1] == -1 );
|
||||
p->addrOpenEphm[1] = addr;
|
||||
p->pPrior = 0;
|
||||
pLimit = p->pLimit;
|
||||
p->pLimit = 0;
|
||||
intersectdest.iSDParm = tab2;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
rc = sqlite3Select(pParse, p, &intersectdest);
|
||||
testcase( rc!=SQLITE_OK );
|
||||
pDelete = p->pPrior;
|
||||
p->pPrior = pPrior;
|
||||
if( p->nSelectRow>pPrior->nSelectRow ) p->nSelectRow = pPrior->nSelectRow;
|
||||
sqlite3ExprDelete(db, p->pLimit);
|
||||
p->pLimit = pLimit;
|
||||
|
||||
/* Generate code to take the intersection of the two temporary
|
||||
** tables.
|
||||
*/
|
||||
assert( p->pEList );
|
||||
iBreak = sqlite3VdbeMakeLabel(v);
|
||||
iCont = sqlite3VdbeMakeLabel(v);
|
||||
computeLimitRegisters(pParse, p, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); VdbeCoverage(v);
|
||||
r1 = sqlite3GetTempReg(pParse);
|
||||
iStart = sqlite3VdbeAddOp2(v, OP_RowData, tab1, r1);
|
||||
sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); VdbeCoverage(v);
|
||||
sqlite3ReleaseTempReg(pParse, r1);
|
||||
selectInnerLoop(pParse, p, tab1,
|
||||
0, 0, &dest, iCont, iBreak);
|
||||
sqlite3VdbeResolveLabel(v, iCont);
|
||||
sqlite3VdbeAddOp2(v, OP_Next, tab1, iStart); VdbeCoverage(v);
|
||||
sqlite3VdbeResolveLabel(v, iBreak);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab2, 0);
|
||||
sqlite3VdbeAddOp2(v, OP_Close, tab1, 0);
|
||||
break;
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
if( p->pNext==0 ){
|
||||
ExplainQueryPlanPop(pParse);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, p->op!=TK_ALL);
|
||||
|
||||
|
||||
/* Compute collating sequences used by
|
||||
** temporary tables needed to implement the compound select.
|
||||
** Attach the KeyInfo structure to all temporary tables.
|
||||
@@ -3076,10 +3046,6 @@ static int multiSelectOrderBy(
|
||||
ExprList *pOrderBy; /* The ORDER BY clause */
|
||||
int nOrderBy; /* Number of terms in the ORDER BY clause */
|
||||
int *aPermute; /* Mapping from ORDER BY terms to result set columns */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSub1; /* EQP id of left-hand query */
|
||||
int iSub2; /* EQP id of right-hand query */
|
||||
#endif
|
||||
|
||||
assert( p->pOrderBy!=0 );
|
||||
assert( pKeyDup==0 ); /* "Managed" code needs this. Ticket #3382. */
|
||||
@@ -3199,6 +3165,8 @@ static int multiSelectOrderBy(
|
||||
sqlite3SelectDestInit(&destA, SRT_Coroutine, regAddrA);
|
||||
sqlite3SelectDestInit(&destB, SRT_Coroutine, regAddrB);
|
||||
|
||||
ExplainQueryPlan((pParse, 1, "MERGE (%s)", selectOpName(p->op)));
|
||||
|
||||
/* Generate a coroutine to evaluate the SELECT statement to the
|
||||
** left of the compound operator - the "A" select.
|
||||
*/
|
||||
@@ -3206,7 +3174,7 @@ static int multiSelectOrderBy(
|
||||
addr1 = sqlite3VdbeAddOp3(v, OP_InitCoroutine, regAddrA, 0, addrSelectA);
|
||||
VdbeComment((v, "left SELECT"));
|
||||
pPrior->iLimit = regLimitA;
|
||||
explainSetInteger(iSub1, pParse->iNextSelectId);
|
||||
ExplainQueryPlan((pParse, 1, "LEFT"));
|
||||
sqlite3Select(pParse, pPrior, &destA);
|
||||
sqlite3VdbeEndCoroutine(v, regAddrA);
|
||||
sqlite3VdbeJumpHere(v, addr1);
|
||||
@@ -3221,7 +3189,7 @@ static int multiSelectOrderBy(
|
||||
savedOffset = p->iOffset;
|
||||
p->iLimit = regLimitB;
|
||||
p->iOffset = 0;
|
||||
explainSetInteger(iSub2, pParse->iNextSelectId);
|
||||
ExplainQueryPlan((pParse, 1, "RIGHT"));
|
||||
sqlite3Select(pParse, p, &destB);
|
||||
p->iLimit = savedLimit;
|
||||
p->iOffset = savedOffset;
|
||||
@@ -3333,7 +3301,7 @@ static int multiSelectOrderBy(
|
||||
|
||||
/*** TBD: Insert subroutine calls to close cursors on incomplete
|
||||
**** subqueries ****/
|
||||
explainComposite(pParse, p->op, iSub1, iSub2, 0);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
return pParse->nErr!=0;
|
||||
}
|
||||
#endif
|
||||
@@ -5121,14 +5089,11 @@ static void explainSimpleCount(
|
||||
){
|
||||
if( pParse->explain==2 ){
|
||||
int bCover = (pIdx!=0 && (HasRowid(pTab) || !IsPrimaryKeyIndex(pIdx)));
|
||||
char *zEqp = sqlite3MPrintf(pParse->db, "SCAN TABLE %s%s%s",
|
||||
sqlite3VdbeExplain(pParse, 0, "SCAN TABLE %s%s%s",
|
||||
pTab->zName,
|
||||
bCover ? " USING COVERING INDEX " : "",
|
||||
bCover ? pIdx->zName : ""
|
||||
);
|
||||
sqlite3VdbeAddOp4(
|
||||
pParse->pVdbe, OP_Explain, pParse->iSelectId, 0, 0, zEqp, P4_DYNAMIC
|
||||
);
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -5341,22 +5306,15 @@ int sqlite3Select(
|
||||
ExprList *pMinMaxOrderBy = 0; /* Added ORDER BY for min/max queries */
|
||||
u8 minMaxFlag; /* Flag for min/max queries */
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iRestoreSelectId = pParse->iSelectId;
|
||||
pParse->iSelectId = pParse->iNextSelectId++;
|
||||
#endif
|
||||
|
||||
db = pParse->db;
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( p==0 || db->mallocFailed || pParse->nErr ){
|
||||
return 1;
|
||||
}
|
||||
if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
|
||||
memset(&sAggInfo, 0, sizeof(sAggInfo));
|
||||
#if SELECTTRACE_ENABLED
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
p->iSelectId = pParse->iSelectId;
|
||||
#endif
|
||||
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->iSelectId));
|
||||
SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
|
||||
if( sqlite3SelectTrace & 0x100 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
@@ -5393,10 +5351,6 @@ int sqlite3Select(
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Get a pointer the VDBE under construction, allocating a new VDBE if one
|
||||
** does not already exist */
|
||||
v = sqlite3GetVdbe(pParse);
|
||||
if( v==0 ) goto select_end;
|
||||
if( pDest->eDest==SRT_Output ){
|
||||
generateColumnNames(pParse, p);
|
||||
}
|
||||
@@ -5491,11 +5445,11 @@ int sqlite3Select(
|
||||
rc = multiSelect(pParse, p, pDest);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
|
||||
if( pParse->iSelectId==0 && (sqlite3SelectTrace & 0x2000)!=0 ){
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
if( p->pNext==0 ) ExplainQueryPlanPop(pParse);
|
||||
return rc;
|
||||
}
|
||||
#endif
|
||||
@@ -5607,7 +5561,7 @@ int sqlite3Select(
|
||||
VdbeComment((v, "%s", pItem->pTab->zName));
|
||||
pItem->addrFillSub = addrTop;
|
||||
sqlite3SelectDestInit(&dest, SRT_Coroutine, pItem->regReturn);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
ExplainQueryPlan((pParse, 1, "CO-ROUTINE 0x%p", pSub));
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
pItem->fg.viaCoroutine = 1;
|
||||
@@ -5642,12 +5596,11 @@ int sqlite3Select(
|
||||
pPrior = isSelfJoinView(pTabList, pItem);
|
||||
if( pPrior ){
|
||||
sqlite3VdbeAddOp2(v, OP_OpenDup, pItem->iCursor, pPrior->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, pPrior->iSelectId);
|
||||
assert( pPrior->pSelect!=0 );
|
||||
pSub->nSelectRow = pPrior->pSelect->nSelectRow;
|
||||
}else{
|
||||
sqlite3SelectDestInit(&dest, SRT_EphemTab, pItem->iCursor);
|
||||
explainSetInteger(pItem->iSelectId, (u8)pParse->iNextSelectId);
|
||||
ExplainQueryPlan((pParse, 1, "MATERIALIZE 0x%p", pSub));
|
||||
sqlite3Select(pParse, pSub, &dest);
|
||||
}
|
||||
pItem->pTab->nRowLogEst = pSub->nSelectRow;
|
||||
@@ -6285,10 +6238,10 @@ select_end:
|
||||
sqlite3DbFree(db, sAggInfo.aFunc);
|
||||
#if SELECTTRACE_ENABLED
|
||||
SELECTTRACE(0x1,pParse,p,("end processing\n"));
|
||||
if( pParse->iSelectId==0 && (sqlite3SelectTrace & 0x2000)!=0 ){
|
||||
if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
|
||||
sqlite3TreeViewSelect(0, p, 0);
|
||||
}
|
||||
#endif
|
||||
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
|
||||
ExplainQueryPlanPop(pParse);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -981,7 +981,8 @@ struct ExpertInfo {
|
||||
/* A single line in the EQP output */
|
||||
typedef struct EQPGraphRow EQPGraphRow;
|
||||
struct EQPGraphRow {
|
||||
int iSelectId; /* The SelectID for this row */
|
||||
int iEqpId; /* ID for this row */
|
||||
int iParentId; /* ID of the parent row */
|
||||
EQPGraphRow *pNext; /* Next row in sequence */
|
||||
char zText[1]; /* Text to display for this row */
|
||||
};
|
||||
@@ -1003,6 +1004,7 @@ struct ShellState {
|
||||
sqlite3 *db; /* The database */
|
||||
u8 autoExplain; /* Automatically turn on .explain mode */
|
||||
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
|
||||
u8 autoEQPtest; /* autoEQP is in test mode */
|
||||
u8 statsOn; /* True to display memory stats before each finalize */
|
||||
u8 scanstatsOn; /* True to display scan stats before each finalize */
|
||||
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
|
||||
@@ -1053,10 +1055,10 @@ struct ShellState {
|
||||
|
||||
/* Allowed values for ShellState.autoEQP
|
||||
*/
|
||||
#define AUTOEQP_off 0
|
||||
#define AUTOEQP_on 1
|
||||
#define AUTOEQP_trigger 2
|
||||
#define AUTOEQP_full 3
|
||||
#define AUTOEQP_off 0 /* Automatic EXPLAIN QUERY PLAN is off */
|
||||
#define AUTOEQP_on 1 /* Automatic EQP is on */
|
||||
#define AUTOEQP_trigger 2 /* On and also show plans for triggers */
|
||||
#define AUTOEQP_full 3 /* Show full EXPLAIN */
|
||||
|
||||
/* Allowed values for ShellState.openMode
|
||||
*/
|
||||
@@ -1667,12 +1669,16 @@ static int wsToEol(const char *z){
|
||||
/*
|
||||
** Add a new entry to the EXPLAIN QUERY PLAN data
|
||||
*/
|
||||
static void eqp_append(ShellState *p, int iSelectId, const char *zText){
|
||||
static void eqp_append(ShellState *p, int iEqpId, int p2, const char *zText){
|
||||
EQPGraphRow *pNew;
|
||||
int nText = strlen30(zText);
|
||||
if( p->autoEQPtest ){
|
||||
utf8_printf(p->out, "%d,%d,%s\n", iEqpId, p2, zText);
|
||||
}
|
||||
pNew = sqlite3_malloc64( sizeof(*pNew) + nText );
|
||||
if( pNew==0 ) shell_out_of_memory();
|
||||
pNew->iSelectId = iSelectId;
|
||||
pNew->iEqpId = iEqpId;
|
||||
pNew->iParentId = p2;
|
||||
memcpy(pNew->zText, zText, nText+1);
|
||||
pNew->pNext = 0;
|
||||
if( p->sGraph.pLast ){
|
||||
@@ -1696,46 +1702,29 @@ static void eqp_reset(ShellState *p){
|
||||
memset(&p->sGraph, 0, sizeof(p->sGraph));
|
||||
}
|
||||
|
||||
/* Return the next EXPLAIN QUERY PLAN line with iSelectId that occurs after
|
||||
/* Return the next EXPLAIN QUERY PLAN line with iEqpId that occurs after
|
||||
** pOld, or return the first such line if pOld is NULL
|
||||
*/
|
||||
static EQPGraphRow *eqp_next_row(ShellState *p, int iSelectId, EQPGraphRow *pOld){
|
||||
static EQPGraphRow *eqp_next_row(ShellState *p, int iEqpId, EQPGraphRow *pOld){
|
||||
EQPGraphRow *pRow = pOld ? pOld->pNext : p->sGraph.pRow;
|
||||
while( pRow && pRow->iSelectId!=iSelectId ) pRow = pRow->pNext;
|
||||
while( pRow && pRow->iParentId!=iEqpId ) pRow = pRow->pNext;
|
||||
return pRow;
|
||||
}
|
||||
|
||||
/* Render a single level of the graph shell having iSelectId. Called
|
||||
/* Render a single level of the graph that has iEqpId as its parent. Called
|
||||
** recursively to render sublevels.
|
||||
*/
|
||||
static void eqp_render_level(ShellState *p, int iSelectId){
|
||||
static void eqp_render_level(ShellState *p, int iEqpId){
|
||||
EQPGraphRow *pRow, *pNext;
|
||||
int i;
|
||||
int n = strlen30(p->sGraph.zPrefix);
|
||||
char *z;
|
||||
for(pRow = eqp_next_row(p, iSelectId, 0); pRow; pRow = pNext){
|
||||
pNext = eqp_next_row(p, iSelectId, pRow);
|
||||
for(pRow = eqp_next_row(p, iEqpId, 0); pRow; pRow = pNext){
|
||||
pNext = eqp_next_row(p, iEqpId, pRow);
|
||||
z = pRow->zText;
|
||||
utf8_printf(p->out, "%s%s%s\n", p->sGraph.zPrefix, pNext ? "|--" : "`--", z);
|
||||
if( n<sizeof(p->sGraph.zPrefix)-7 && (z = strstr(z, " SUBQUER"))!=0 ){
|
||||
if( n<sizeof(p->sGraph.zPrefix)-7 ){
|
||||
memcpy(&p->sGraph.zPrefix[n], pNext ? "| " : " ", 4);
|
||||
if( strncmp(z, " SUBQUERY ", 9)==0 && (i = atoi(z+10))>iSelectId ){
|
||||
eqp_render_level(p, i);
|
||||
}else if( strncmp(z, " SUBQUERIES ", 12)==0 ){
|
||||
i = atoi(z+12);
|
||||
if( i>iSelectId ){
|
||||
utf8_printf(p->out, "%s|--SUBQUERY %d\n", p->sGraph.zPrefix, i);
|
||||
memcpy(&p->sGraph.zPrefix[n+3],"| ",4);
|
||||
eqp_render_level(p, i);
|
||||
}
|
||||
z = strstr(z, " AND ");
|
||||
if( z && (i = atoi(z+5))>iSelectId ){
|
||||
p->sGraph.zPrefix[n+3] = 0;
|
||||
utf8_printf(p->out, "%s`--SUBQUERY %d\n", p->sGraph.zPrefix, i);
|
||||
memcpy(&p->sGraph.zPrefix[n+3]," ",4);
|
||||
eqp_render_level(p, i);
|
||||
}
|
||||
}
|
||||
eqp_render_level(p, pRow->iEqpId);
|
||||
p->sGraph.zPrefix[n] = 0;
|
||||
}
|
||||
}
|
||||
@@ -2114,7 +2103,7 @@ static int shell_callback(
|
||||
break;
|
||||
}
|
||||
case MODE_EQP: {
|
||||
eqp_append(p, atoi(azArg[0]), azArg[3]);
|
||||
eqp_append(p, atoi(azArg[0]), atoi(azArg[1]), azArg[3]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2956,9 +2945,10 @@ static int shell_exec(
|
||||
if( rc==SQLITE_OK ){
|
||||
while( sqlite3_step(pExplain)==SQLITE_ROW ){
|
||||
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
|
||||
int iSelectId = sqlite3_column_int(pExplain, 0);
|
||||
int iEqpId = sqlite3_column_int(pExplain, 0);
|
||||
int iParentId = sqlite3_column_int(pExplain, 1);
|
||||
if( zEQPLine[0]=='-' ) eqp_render(pArg);
|
||||
eqp_append(pArg, iSelectId, zEQPLine);
|
||||
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
|
||||
}
|
||||
eqp_render(pArg);
|
||||
}
|
||||
@@ -5914,10 +5904,14 @@ static int do_meta_command(char *zLine, ShellState *p){
|
||||
|
||||
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
|
||||
if( nArg==2 ){
|
||||
p->autoEQPtest = 0;
|
||||
if( strcmp(azArg[1],"full")==0 ){
|
||||
p->autoEQP = AUTOEQP_full;
|
||||
}else if( strcmp(azArg[1],"trigger")==0 ){
|
||||
p->autoEQP = AUTOEQP_trigger;
|
||||
}else if( strcmp(azArg[1],"test")==0 ){
|
||||
p->autoEQP = AUTOEQP_on;
|
||||
p->autoEQPtest = 1;
|
||||
}else{
|
||||
p->autoEQP = (u8)booleanValue(azArg[1]);
|
||||
}
|
||||
|
||||
@@ -2609,9 +2609,6 @@ struct SrcList {
|
||||
unsigned viaCoroutine :1; /* Implemented as a co-routine */
|
||||
unsigned isRecursive :1; /* True for recursive reference in WITH */
|
||||
} fg;
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
u8 iSelectId; /* If pSelect!=0, the id of the sub-select in EQP */
|
||||
#endif
|
||||
int iCursor; /* The VDBE cursor number used to access this table */
|
||||
Expr *pOn; /* The ON clause of a join */
|
||||
IdList *pUsing; /* The USING clause of a join */
|
||||
@@ -2783,7 +2780,6 @@ struct Select {
|
||||
int iLimit, iOffset; /* Memory registers holding LIMIT & OFFSET counters */
|
||||
#if SELECTTRACE_ENABLED
|
||||
char zSelName[12]; /* Symbolic name of this SELECT use for debugging */
|
||||
u32 iSelectId; /* EXPLAIN QUERY PLAN select ID */
|
||||
#endif
|
||||
int addrOpenEphm[2]; /* OP_OpenEphem opcodes related to this select */
|
||||
SrcList *pSrc; /* The FROM clause */
|
||||
@@ -2824,8 +2820,7 @@ struct Select {
|
||||
#define SF_MaybeConvert 0x08000 /* Need convertCompoundSelectToSubquery() */
|
||||
#define SF_Converted 0x10000 /* By convertCompoundSelectToSubquery() */
|
||||
#define SF_IncludeHidden 0x20000 /* Include hidden columns in output */
|
||||
#define SF_ComplexResult 0x40000 /* Result set contains subquery or function */
|
||||
|
||||
#define SF_ComplexResult 0x40000 /* Result contains subquery or function */
|
||||
|
||||
/*
|
||||
** The results of a SELECT can be distributed in several ways, as defined
|
||||
@@ -3095,8 +3090,7 @@ struct Parse {
|
||||
#endif
|
||||
int nHeight; /* Expression tree height of current sub-select */
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
int iSelectId; /* ID of current select for EXPLAIN output */
|
||||
int iNextSelectId; /* Next available select ID for EXPLAIN output */
|
||||
int addrExplain; /* Address of current OP_Explain opcode */
|
||||
#endif
|
||||
VList *pVList; /* Mapping between variable names and numbers */
|
||||
Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */
|
||||
|
||||
@@ -141,10 +141,10 @@ void sqlite3TreeViewSelect(TreeView *pView, const Select *p, u8 moreToFollow){
|
||||
do{
|
||||
#if SELECTTRACE_ENABLED
|
||||
sqlite3TreeViewLine(pView,
|
||||
"SELECT%s%s (%s/%d/%p) selFlags=0x%x nSelectRow=%d",
|
||||
"SELECT%s%s (%s/%p) selFlags=0x%x nSelectRow=%d",
|
||||
((p->selFlags & SF_Distinct) ? " DISTINCT" : ""),
|
||||
((p->selFlags & SF_Aggregate) ? " agg_flag" : ""),
|
||||
p->zSelName, p->iSelectId, p, p->selFlags,
|
||||
p->zSelName, p, p->selFlags,
|
||||
(int)p->nSelectRow
|
||||
);
|
||||
#else
|
||||
|
||||
14
src/vdbe.h
14
src/vdbe.h
@@ -197,7 +197,19 @@ void sqlite3VdbeEndCoroutine(Vdbe*,int);
|
||||
# define sqlite3VdbeVerifyNoMallocRequired(A,B)
|
||||
# define sqlite3VdbeVerifyNoResultRow(A)
|
||||
#endif
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp, int iLineno);
|
||||
VdbeOp *sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp,int iLineno);
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
void sqlite3VdbeExplain(Parse*,u8,const char*,...);
|
||||
void sqlite3VdbeExplainPop(Parse*);
|
||||
int sqlite3VdbeExplainParent(Parse*);
|
||||
# define ExplainQueryPlan(P) sqlite3VdbeExplain P
|
||||
# define ExplainQueryPlanPop(P) sqlite3VdbeExplainPop(P)
|
||||
# define ExplainQueryPlanParent(P) sqlite3VdbeExplainParent(P)
|
||||
#else
|
||||
# define ExplainQueryPlan(P)
|
||||
# define ExplainQueryPlanPop(P)
|
||||
# define ExplainQueryPlanParent(P) 0
|
||||
#endif
|
||||
void sqlite3VdbeAddParseSchemaOp(Vdbe*,int,char*);
|
||||
void sqlite3VdbeChangeOpcode(Vdbe*, u32 addr, u8);
|
||||
void sqlite3VdbeChangeP1(Vdbe*, u32 addr, int P1);
|
||||
|
||||
@@ -303,6 +303,49 @@ int sqlite3VdbeAddOp4Dup8(
|
||||
return sqlite3VdbeAddOp4(p, op, p1, p2, p3, p4copy, p4type);
|
||||
}
|
||||
|
||||
#ifndef SQLITE_OMIT_EXPLAIN
|
||||
/*
|
||||
** Return the address of the current EXPLAIN QUERY PLAN baseline.
|
||||
** 0 means "none".
|
||||
*/
|
||||
int sqlite3VdbeExplainParent(Parse *pParse){
|
||||
VdbeOp *pOp;
|
||||
if( pParse->addrExplain==0 ) return 0;
|
||||
pOp = sqlite3VdbeGetOp(pParse->pVdbe, pParse->addrExplain);
|
||||
return pOp->p2;
|
||||
}
|
||||
|
||||
/*
|
||||
** Add a new OP_Explain opcode.
|
||||
**
|
||||
** If the bPush flag is true, then make this opcode the parent for
|
||||
** subsequent Explains until sqlite3VdbeExplainPop() is called.
|
||||
*/
|
||||
void sqlite3VdbeExplain(Parse *pParse, u8 bPush, const char *zFmt, ...){
|
||||
if( pParse->explain==2 ){
|
||||
char *zMsg;
|
||||
Vdbe *v = pParse->pVdbe;
|
||||
va_list ap;
|
||||
int iThis;
|
||||
va_start(ap, zFmt);
|
||||
zMsg = sqlite3VMPrintf(pParse->db, zFmt, ap);
|
||||
va_end(ap);
|
||||
v = pParse->pVdbe;
|
||||
iThis = v->nOp;
|
||||
sqlite3VdbeAddOp4(v, OP_Explain, iThis, pParse->addrExplain, 0,
|
||||
zMsg, P4_DYNAMIC);
|
||||
if( bPush) pParse->addrExplain = iThis;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Pop the EXPLAIN QUERY PLAN stack one level.
|
||||
*/
|
||||
void sqlite3VdbeExplainPop(Parse *pParse){
|
||||
pParse->addrExplain = sqlite3VdbeExplainParent(pParse);
|
||||
}
|
||||
#endif /* SQLITE_OMIT_EXPLAIN */
|
||||
|
||||
/*
|
||||
** Add an OP_ParseSchema opcode. This routine is broken out from
|
||||
** sqlite3VdbeAddOp4() since it needs to also needs to mark all btrees
|
||||
|
||||
@@ -4592,6 +4592,7 @@ WhereInfo *sqlite3WhereBegin(
|
||||
if( wctrlFlags & WHERE_WANT_DISTINCT ){
|
||||
pWInfo->eDistinct = WHERE_DISTINCT_UNIQUE;
|
||||
}
|
||||
ExplainQueryPlan((pParse, 0, "SCAN CONSTANT ROW"));
|
||||
}else{
|
||||
/* Assign a bit from the bitmask to every term in the FROM clause.
|
||||
**
|
||||
|
||||
@@ -134,7 +134,6 @@ int sqlite3WhereExplainOneScan(
|
||||
struct SrcList_item *pItem = &pTabList->a[pLevel->iFrom];
|
||||
Vdbe *v = pParse->pVdbe; /* VM being constructed */
|
||||
sqlite3 *db = pParse->db; /* Database handle */
|
||||
int iId = pParse->iSelectId; /* Select id (left-most output column) */
|
||||
int isSearch; /* True for a SEARCH. False for SCAN. */
|
||||
WhereLoop *pLoop; /* The controlling WhereLoop object */
|
||||
u32 flags; /* Flags that describe this loop */
|
||||
@@ -153,7 +152,7 @@ int sqlite3WhereExplainOneScan(
|
||||
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
|
||||
sqlite3StrAccumAppendAll(&str, isSearch ? "SEARCH" : "SCAN");
|
||||
if( pItem->pSelect ){
|
||||
sqlite3XPrintf(&str, " SUBQUERY %d", pItem->iSelectId);
|
||||
sqlite3XPrintf(&str, " SUBQUERY 0x%p", pItem->pSelect);
|
||||
}else{
|
||||
sqlite3XPrintf(&str, " TABLE %s", pItem->zName);
|
||||
}
|
||||
@@ -214,7 +213,8 @@ int sqlite3WhereExplainOneScan(
|
||||
}
|
||||
#endif
|
||||
zMsg = sqlite3StrAccumFinish(&str);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, iId, iLevel, iFrom, zMsg,P4_DYNAMIC);
|
||||
ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
|
||||
pParse->addrExplain, 0, zMsg,P4_DYNAMIC);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user