]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Add a row of EXPLAIN QUERY PLAN output for each composite select operation (UNION...
authordan <dan@noemail.net>
Thu, 11 Nov 2010 16:46:40 +0000 (16:46 +0000)
committerdan <dan@noemail.net>
Thu, 11 Nov 2010 16:46:40 +0000 (16:46 +0000)
FossilOrigin-Name: 00fb8468b5f2c48a3c91b86803bf306a0331496f

manifest
manifest.uuid
src/select.c
test/eqp.test

index 27478ced54ddbb001d0d063842fe9f3234fc99f7..38c37d760a14d19fc78a06354f8f3483a346fada 100644 (file)
--- a/manifest
+++ b/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
index b51d64bd8a8b9efd197b8d1a05757e513ad5bdac..578ad0ea1d23486f12b2225d71d6c44a06b06386 100644 (file)
@@ -1 +1 @@
-7ae068952fba4395b4aa437613a5ed2bd9ddf941
\ No newline at end of file
+00fb8468b5f2c48a3c91b86803bf306a0331496f
\ No newline at end of file
index 9e0cba59f710d7d4d99de8c1bddaf218791a93f8..fb3fb61afa06df8c6a63acb639c0f388a48cf442 100644 (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
 
index f52847d3ba482f9070e3ef8b3cb26730d54a637f..3b57657cfe45d3c29634f751b9d7a0ca53f377e7 100644 (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