-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
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
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
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
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
}
}
+/*
+** 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
#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
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
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.
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;
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;
*/
assert( !pPrior->pOrderBy );
sqlite3SelectDestInit(&uniondest, priorOp, unionTab);
+ explainSetInteger(iSub1, pParse->iNextSelectId);
rc = sqlite3Select(pParse, pPrior, &uniondest);
if( rc ){
goto multi_select_end;
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.
/* 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;
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;
}
}
+ 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.
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. */
*/
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);
savedOffset = p->iOffset;
p->iLimit = regLimitB;
p->iOffset = 0;
+ explainSetInteger(iSub2, pParse->iNextSelectId);
sqlite3Select(pParse, p, &destB);
p->iLimit = savedLimit;
p->iOffset = savedOffset;
/*** TBD: Insert subroutine calls to close cursors on incomplete
**** subqueries ****/
+ explainComposite(pParse, p->op, iSub1, iSub2, 0);
return SQLITE_OK;
}
#endif
goto select_end;
}
}
+ rc = multiSelect(pParse, p, pDest);
explainSetInteger(pParse->iSelectId, iRestoreSelectId);
- return multiSelect(pParse, p, pDest);
+ return rc;
}
#endif
#
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 }
} {
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
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
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
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
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 {
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
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
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
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 {
} {
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