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:
parent
acf1987d18
commit
7f61e92c3a
14
manifest
14
manifest
@ -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
|
||||
|
@ -1 +1 @@
|
||||
7ae068952fba4395b4aa437613a5ed2bd9ddf941
|
||||
00fb8468b5f2c48a3c91b86803bf306a0331496f
|
87
src/select.c
87
src/select.c
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user