Add a row of EXPLAIN QUERY PLAN output for each composite select operation (UNION, EXCEPT etc.) in the query.

FossilOrigin-Name: 00fb8468b5f2c48a3c91b86803bf306a0331496f
This commit is contained in:
dan 2010-11-11 16:46:40 +00:00
parent acf1987d18
commit 7f61e92c3a
4 changed files with 109 additions and 26 deletions

View File

@ -1,5 +1,5 @@
C Fix\sa\sbug\sin\sthe\sEXPLAIN\sQUERY\sPLAN\scode.
D 2010-11-11T11:43:01
C Add\sa\srow\sof\sEXPLAIN\sQUERY\sPLAN\soutput\sfor\seach\scomposite\sselect\soperation\s(UNION,\sEXCEPT\setc.)\sin\sthe\squery.
D 2010-11-11T16:46:40
F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f
F Makefile.in e7a59672eaeb04408d1fa8501618d7501a3c5e39
F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23
@ -174,7 +174,7 @@ F src/printf.c 8ae5082dd38a1b5456030c3755ec3a392cd51506
F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50
F src/resolve.c 1c0f32b64f8e3f555fe1f732f9d6f501a7f05706
F src/rowset.c 69afa95a97c524ba6faf3805e717b5b7ae85a697
F src/select.c 3d5086dfccb245af4801234d42b6d2888a30e2b1
F src/select.c de0cf4881f3503a72fba7a2cf59dc56dd563c5a5
F src/shell.c 8517fc1f9c59ae4007e6cc8b9af91ab231ea2056
F src/sqlite.h.in f47e09412fc9a129f759fa4d96ef21f4b3d529eb
F src/sqlite3ext.h c90bd5507099f62043832d73f6425d8d5c5da754
@ -365,7 +365,7 @@ F test/enc.test e54531cd6bf941ee6760be041dff19a104c7acea
F test/enc2.test 6d91a5286f59add0cfcbb2d0da913b76f2242398
F test/enc3.test 5c550d59ff31dccdba5d1a02ae11c7047d77c041
F test/enc4.test 4b575ef09e0eff896e73bd24076f96c2aa6a42de
F test/eqp.test b573765656ce6488b9ca579baaa27c4600e3ec00
F test/eqp.test d4a411b212a687115a6efb7e0e143cf6592ce8af
F test/eval.test bc269c365ba877554948441e91ad5373f9f91be3
F test/exclusive.test 53e1841b422e554cecf0160f937c473d6d0e3062
F test/exclusive2.test 76e63c05349cb70d09d60b99d2ae625525ff5155
@ -886,7 +886,7 @@ F tool/speedtest2.tcl ee2149167303ba8e95af97873c575c3e0fab58ff
F tool/speedtest8.c 2902c46588c40b55661e471d7a86e4dd71a18224
F tool/speedtest8inst1.c 293327bc76823f473684d589a8160bde1f52c14e
F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f
P 30904ef8412348464e893e9e1551ef22cad24a3e
R ad4bf36ec916b4929497eadda39bfc05
P 7ae068952fba4395b4aa437613a5ed2bd9ddf941
R e485a0cebad0641fc6f1d38e91a1d634
U dan
Z fb3b99730d456e246e3837735140f4ed
Z c839f7f76a308e5561815c81b3e77973

View File

@ -1 +1 @@
7ae068952fba4395b4aa437613a5ed2bd9ddf941
00fb8468b5f2c48a3c91b86803bf306a0331496f

View File

@ -771,6 +771,22 @@ static KeyInfo *keyInfoFromExprList(Parse *pParse, ExprList *pList){
return pInfo;
}
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
static const char *selectOpName(int id){
char *z;
switch( id ){
case TK_ALL: z = "UNION ALL"; break;
case TK_INTERSECT: z = "INTERSECT"; break;
case TK_EXCEPT: z = "EXCEPT"; break;
default: z = "UNION"; break;
}
return z;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
#ifndef SQLITE_OMIT_EXPLAIN
/*
** Unless an "EXPLAIN QUERY PLAN" command is being processed, this function
@ -790,6 +806,38 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
}
}
/*
** 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, "COMPOSITE 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);
}
}
/*
** Assign expression b to lvalue a. A second, no-op, version of this macro
** is provided when SQLITE_OMIT_EXPLAIN is defined. This allows the code
@ -802,6 +850,7 @@ static void explainTempTable(Parse *pParse, const char *zUsage){
#else
/* No-op versions of the explainXXX() functions and macros. */
# define explainTempTable(y,z)
# define explainComposite(v,w,x,y,z)
# define explainSetInteger(y,z)
#endif
@ -1151,22 +1200,6 @@ static void generateColumnNames(
generateColumnTypes(pParse, pTabList, pEList);
}
#ifndef SQLITE_OMIT_COMPOUND_SELECT
/*
** Name of the connection operator, used for error messages.
*/
static const char *selectOpName(int id){
char *z;
switch( id ){
case TK_ALL: z = "UNION ALL"; break;
case TK_INTERSECT: z = "INTERSECT"; break;
case TK_EXCEPT: z = "EXCEPT"; break;
default: z = "UNION"; break;
}
return z;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */
/*
** Given a an expression list (which is really the list of expressions
** that form the result set of a SELECT statement) compute appropriate
@ -1500,6 +1533,10 @@ 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; /* EQP id of left-hand query */
int iSub2; /* 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.
@ -1560,6 +1597,7 @@ static int multiSelect(
assert( !pPrior->pLimit );
pPrior->pLimit = p->pLimit;
pPrior->pOffset = p->pOffset;
explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &dest);
p->pLimit = 0;
p->pOffset = 0;
@ -1573,6 +1611,7 @@ static int multiSelect(
addr = sqlite3VdbeAddOp1(v, OP_IfZero, p->iLimit);
VdbeComment((v, "Jump ahead if LIMIT reached"));
}
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &dest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@ -1620,6 +1659,7 @@ static int multiSelect(
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
@ -1639,6 +1679,7 @@ static int multiSelect(
pOffset = p->pOffset;
p->pOffset = 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.
@ -1704,6 +1745,7 @@ static int multiSelect(
/* 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;
@ -1720,6 +1762,7 @@ static int multiSelect(
pOffset = p->pOffset;
p->pOffset = 0;
intersectdest.iParm = tab2;
explainSetInteger(iSub2, pParse->iNextSelectId);
rc = sqlite3Select(pParse, p, &intersectdest);
testcase( rc!=SQLITE_OK );
pDelete = p->pPrior;
@ -1756,6 +1799,8 @@ static int multiSelect(
}
}
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.
@ -2099,6 +2144,10 @@ 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. */
@ -2252,6 +2301,7 @@ static int multiSelectOrderBy(
*/
VdbeNoopComment((v, "Begin coroutine for left SELECT"));
pPrior->iLimit = regLimitA;
explainSetInteger(iSub1, pParse->iNextSelectId);
sqlite3Select(pParse, pPrior, &destA);
sqlite3VdbeAddOp2(v, OP_Integer, 1, regEofA);
sqlite3VdbeAddOp1(v, OP_Yield, regAddrA);
@ -2266,6 +2316,7 @@ static int multiSelectOrderBy(
savedOffset = p->iOffset;
p->iLimit = regLimitB;
p->iOffset = 0;
explainSetInteger(iSub2, pParse->iNextSelectId);
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
@ -2396,6 +2447,7 @@ static int multiSelectOrderBy(
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
explainComposite(pParse, p->op, iSub1, iSub2, 0);
return SQLITE_OK;
}
#endif
@ -3738,8 +3790,9 @@ int sqlite3Select(
goto select_end;
}
}
rc = multiSelect(pParse, p, pDest);
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
return multiSelect(pParse, p, pDest);
return rc;
}
#endif

View File

@ -24,7 +24,6 @@ set testprefix eqp
#
proc do_eqp_test {name sql res} {
set res [list {*}$res]
uplevel do_execsql_test $name [list "EXPLAIN QUERY PLAN $sql"] [list $res]
}
proc det {args} { uplevel do_eqp_test $args }
@ -218,6 +217,7 @@ do_eqp_test 4.1.1 {
} {
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.1.2 {
SELECT * FROM t1 UNION ALL SELECT * FROM t2 ORDER BY 2
@ -226,6 +226,7 @@ do_eqp_test 4.1.2 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.1.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 2
@ -234,6 +235,7 @@ do_eqp_test 4.1.3 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (UNION)}
}
do_eqp_test 4.1.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 2
@ -242,6 +244,7 @@ do_eqp_test 4.1.4 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (INTERSECT)}
}
do_eqp_test 4.1.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 2
@ -250,6 +253,7 @@ do_eqp_test 4.1.5 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (EXCEPT)}
}
do_eqp_test 4.2.2 {
@ -258,6 +262,7 @@ do_eqp_test 4.2.2 {
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 BY INDEX t2i1 (~1000000 rows)}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (UNION ALL)}
}
do_eqp_test 4.2.3 {
SELECT * FROM t1 UNION SELECT * FROM t2 ORDER BY 1
@ -266,6 +271,7 @@ do_eqp_test 4.2.3 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (UNION)}
}
do_eqp_test 4.2.4 {
SELECT * FROM t1 INTERSECT SELECT * FROM t2 ORDER BY 1
@ -274,6 +280,7 @@ do_eqp_test 4.2.4 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (INTERSECT)}
}
do_eqp_test 4.2.5 {
SELECT * FROM t1 EXCEPT SELECT * FROM t2 ORDER BY 1
@ -282,6 +289,7 @@ do_eqp_test 4.2.5 {
1 0 0 {USE TEMP B-TREE FOR ORDER BY}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 (EXCEPT)}
}
do_eqp_test 4.3.1 {
@ -289,6 +297,28 @@ do_eqp_test 4.3.1 {
} {
1 0 0 {SCAN TABLE t1 (~1000000 rows)}
2 0 0 {SCAN TABLE t2 (~1000000 rows)}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 2 USING TEMP B-TREE (UNION)}
}
do_eqp_test 4.3.2 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1
} {
2 0 0 {SCAN TABLE t1 (~1000000 rows)}
3 0 0 {SCAN TABLE t2 (~1000000 rows)}
1 0 0 {COMPOSITE SUBQUERIES 2 AND 3 USING TEMP B-TREE (UNION)}
4 0 0 {SCAN TABLE t1 (~1000000 rows)}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 4 USING TEMP B-TREE (UNION)}
}
do_eqp_test 4.3.3 {
SELECT x FROM t1 UNION SELECT x FROM t2 UNION SELECT x FROM t1 ORDER BY 1
} {
2 0 0 {SCAN TABLE t1 (~1000000 rows)}
2 0 0 {USE TEMP B-TREE FOR ORDER BY}
3 0 0 {SCAN TABLE t2 BY COVERING INDEX t2i1 (~1000000 rows)}
1 0 0 {COMPOSITE SUBQUERIES 2 AND 3 (UNION)}
4 0 0 {SCAN TABLE t1 (~1000000 rows)}
4 0 0 {USE TEMP B-TREE FOR ORDER BY}
0 0 0 {COMPOSITE SUBQUERIES 1 AND 4 (UNION)}
}
finish_test