]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Enhance the query flattener to handle subqueries that are joins.
authordrh <drh@noemail.net>
Fri, 2 May 2003 16:04:17 +0000 (16:04 +0000)
committerdrh <drh@noemail.net>
Fri, 2 May 2003 16:04:17 +0000 (16:04 +0000)
All regressions pass but new tests need to be added before release.
Ticket #272. (CVS 948)

FossilOrigin-Name: ad57693e9f1b83a8cc4d028264b35018a9a4a701

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

index 8e7eec4f888276166278e3ca3018ea4c1e1cef7d..9c3e31a3cd7fc22dab2f92934f77282c131bcae5 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C VDBE\scursors\snumbers\sfor\stables\sin\sa\sjoin\sdo\snot\shave\sto\sbe\sconsecutive.\nThis\sis\sone\sstep\son\sthe\sroad\sto\sfixing\sticket\s#272.\s(CVS\s947)
-D 2003-05-02T14:32:13
+C Enhance\sthe\squery\sflattener\sto\shandle\ssubqueries\sthat\sare\sjoins.\nAll\sregressions\spass\sbut\snew\stests\sneed\sto\sbe\sadded\sbefore\srelease.\nTicket\s#272.\s(CVS\s948)
+D 2003-05-02T16:04:17
 F Makefile.in 004acec253ecdde985c8ecd5b7c9accdb210378f
 F Makefile.linux-gcc b86a99c493a5bfb402d1d9178dcdc4bd4b32f906
 F README f1de682fbbd94899d50aca13d387d1b3fd3be2dd
@@ -43,7 +43,7 @@ F src/parse.y 39b5240cb78047dc56d6d37c398baed7ba556779
 F src/pragma.c 118fe400d71b7fdcc03580d5eab6bb5aa00772a5
 F src/printf.c fc5fdef6e92ad205005263661fe9716f55a49f3e
 F src/random.c 19e8e00fe0df32a742f115773f57651be327cabe
-F src/select.c 493360f3003719ad61d49863c4f5db0bad9022cb
+F src/select.c 3fe63e3a29df661ba72a67eecd77e8ee82801def
 F src/shell.c 6f59240f69e65a1c4e1d06492eb9238092defc34
 F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e
 F src/sqlite.h.in eec06462cba262c0ee03f38462a18a4bc66dda4e
@@ -127,7 +127,7 @@ F test/unique.test 22a46df72a3e0a3fd1a2d39e96fb59f18448dd5f
 F test/update.test 198360dfa14e65354dbcc66d5b98d8070780e42b
 F test/vacuum.test 4d8c8af30338577af03e563bc815d7898ae65258
 F test/version.test 605fd0d7e7d571370c32b12dbf395b58953de246
-F test/view.test 8b3b0b30674865af2c87acbdf945e369f92012a5
+F test/view.test 408fa464da35cf9c1fd9054c988f7e755a1cb0b6
 F test/where.test d719129a052280fe245a2ddcbd09bcc0b8c17ce4
 F tool/diffdb.c 7524b1b5df217c20cd0431f6789851a4e0cb191b
 F tool/lemon.c 14fedcde9cf70aa6040b89de164cf8f56f92a4b9
@@ -165,7 +165,7 @@ F www/speed.tcl cb4c10a722614aea76d2c51f32ee43400d5951be
 F www/sqlite.tcl ae3dcfb077e53833b59d4fcc94d8a12c50a44098
 F www/tclsqlite.tcl 1db15abeb446aad0caf0b95b8b9579720e4ea331
 F www/vdbe.tcl 2013852c27a02a091d39a766bc87cff329f21218
-P 6d019e0baa3219614a9bc5b550a0f9fe3f7e731a
-R f7e61e2db6268bcec64f93ca941fa9e1
+P be7aed2011b4af868b6a0c370c3d41354ae0cdf4
+R ed36fbd16d08ea65dbe969ae9a7bbf4c
 U drh
-Z affbf408d9fd8c226a5447c7e8084f51
+Z 80e0fdd6a1502d3a16e3bb09c6a28adb
index a5fdcd2ea50ee659f9e12208495111326c371606..d68424a0394709f0b9938ebf649a55e304caaa9c 100644 (file)
@@ -1 +1 @@
-be7aed2011b4af868b6a0c370c3d41354ae0cdf4
\ No newline at end of file
+ad57693e9f1b83a8cc4d028264b35018a9a4a701
\ No newline at end of file
index 7b8733f4a8b71851048c7e87b6b3750bc107d6be..0f8c3c88443e608b7a903ec864c4df401fc841e0 100644 (file)
@@ -12,7 +12,7 @@
 ** This file contains C code routines that are called by the parser
 ** to handle SELECT statements in SQLite.
 **
-** $Id: select.c,v 1.136 2003/05/02 14:32:13 drh Exp $
+** $Id: select.c,v 1.137 2003/05/02 16:04:17 drh Exp $
 */
 #include "sqliteInt.h"
 
@@ -1555,7 +1555,7 @@ substExprList(ExprList *pList, int iTable, ExprList *pEList){
 **
 **   (2)  The subquery is not an aggregate or the outer query is not a join.
 **
-**   (3)  The subquery is not a join.
+**   (3)  (No longer a restriction)
 **
 **   (4)  The subquery is not DISTINCT or the outer query is not a join.
 **
@@ -1613,7 +1613,7 @@ static int flattenSubquery(
   if( subqueryIsAgg && pSrc->nSrc>1 ) return 0;
   pSubSrc = pSub->pSrc;
   assert( pSubSrc );
-  if( pSubSrc->nSrc!=1 ) return 0;
+  if( pSubSrc->nSrc==0 ) return 0;
   if( (pSub->isDistinct || pSub->nLimit>=0) &&  (pSrc->nSrc>1 || isAgg) ){
      return 0;
   }
@@ -1623,8 +1623,52 @@ static int flattenSubquery(
   /* If we reach this point, it means flattening is permitted for the
   ** iFrom-th entry of the FROM clause in the outer query.
   */
+
+  /* Move all of the FROM elements of the subquery into the
+  ** the FROM clause of the outer query.  Before doing this, remember
+  ** the cursor number for the original outer query FROM element in
+  ** iParent.  The iParent cursor will never be used.  Subsequent code
+  ** will scan expressions looking for iParent references and replace
+  ** those references with expressions that resolve to the subquery FROM
+  ** elements we are now copying in.
+  */
   iParent = pSrc->a[iFrom].iCursor;
-  pSrc->a[iFrom].iCursor = pSubSrc->a[0].iCursor;
+  {
+    int nSubSrc = pSubSrc->nSrc;
+
+    if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
+      sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
+    }
+    sqliteFree(pSrc->a[iFrom].zName);
+    sqliteFree(pSrc->a[iFrom].zAlias);
+    if( nSubSrc>1 ){
+      int extra = nSubSrc - 1;
+      for(i=1; i<nSubSrc; i++){
+        pSrc = sqliteSrcListAppend(pSrc, 0, 0);
+      }
+      p->pSrc = pSrc;
+      for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){
+        pSrc->a[i] = pSrc->a[i-extra];
+      }
+    }
+    for(i=0; i<nSubSrc; i++){
+      pSrc->a[i+iFrom] = pSubSrc->a[i];
+      memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
+    }
+  }
+
+  /* Now begin substituting subquery result set expressions for 
+  ** references to the iParent in the outer query.
+  ** 
+  ** Example:
+  **
+  **   SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
+  **   \                     \_____________ subquery __________/          /
+  **    \_____________________ outer query ______________________________/
+  **
+  ** We look at every expression in the outer query and every place we see
+  ** "a" we substitute "x*3" and every place we see "b" we substitute "y+10".
+  */
   substExprList(p->pEList, iParent, pSub->pEList);
   pList = p->pEList;
   for(i=0; i<pList->nExpr; i++){
@@ -1672,8 +1716,15 @@ static int flattenSubquery(
       p->pWhere = sqliteExpr(TK_AND, p->pWhere, pWhere, 0);
     }
   }
+
+  /* The flattened query is distinct if either the inner or the
+  ** outer query is distinct. 
+  */
   p->isDistinct = p->isDistinct || pSub->isDistinct;
 
+  /* Transfer the limit expression from the subquery to the outer
+  ** query.
+  */
   if( pSub->nLimit>=0 ){
     if( p->nLimit<0 ){
       p->nLimit = pSub->nLimit;
@@ -1683,14 +1734,9 @@ static int flattenSubquery(
   }
   p->nOffset += pSub->nOffset;
 
-  if( pSrc->a[iFrom].pTab && pSrc->a[iFrom].pTab->isTransient ){
-    sqliteDeleteTable(0, pSrc->a[iFrom].pTab);
-  }
-  pSrc->a[iFrom].pTab = pSubSrc->a[0].pTab;
-  pSubSrc->a[0].pTab = 0;
-  assert( pSrc->a[iFrom].pSelect==pSub );
-  pSrc->a[iFrom].pSelect = pSubSrc->a[0].pSelect;
-  pSubSrc->a[0].pSelect = 0;
+  /* Finially, delete what is left of the subquery and return
+  ** success.
+  */
   sqliteSelectDelete(pSub);
   return 1;
 }
@@ -2079,19 +2125,24 @@ int sqliteSelect(
   */
   for(i=0; i<pTabList->nSrc; i++){
     const char *zSavedAuthContext;
+    int needRestoreContext;
+
     if( pTabList->a[i].pSelect==0 ) continue;
     if( pTabList->a[i].zName!=0 ){
       zSavedAuthContext = pParse->zAuthContext;
       pParse->zAuthContext = pTabList->a[i].zName;
+      needRestoreContext = 1;
+    }else{
+      needRestoreContext = 0;
     }
     sqliteSelect(pParse, pTabList->a[i].pSelect, SRT_TempTable, 
                  pTabList->a[i].iCursor, p, i, &isAgg);
-    if( pTabList->a[i].zName!=0 ){
+    if( needRestoreContext ){
       pParse->zAuthContext = zSavedAuthContext;
     }
     pTabList = p->pSrc;
     pWhere = p->pWhere;
-    if( eDest==SRT_Callback ){
+    if( eDest!=SRT_Union && eDest!=SRT_Except && eDest!=SRT_Discard ){
       pOrderBy = p->pOrderBy;
     }
     pGroupBy = p->pGroupBy;
index 57043cfc01a90e61ae37627815ecc0b7529d42f2..f8a3544bbd3e5a40600ff2ee32438b14a2fcc049 100644 (file)
@@ -11,7 +11,7 @@
 # This file implements regression tests for SQLite library.  The
 # focus of this file is testing VIEW statements.
 #
-# $Id: view.test,v 1.14 2003/05/02 14:32:15 drh Exp $
+# $Id: view.test,v 1.15 2003/05/02 16:04:17 drh Exp $
 set testdir [file dirname $argv0]
 source $testdir/tester.tcl
 
@@ -197,11 +197,49 @@ do_test view-5.1 {
 do_test view-5.2 {
   execsql {
     CREATE VIEW v5 AS
-      SELECT t1.x, t2.y FROM t1 JOIN t2 USING(a);
+      SELECT t1.x AS v, t2.y AS w FROM t1 JOIN t2 USING(a);
     SELECT * FROM v5;
   }
 } {1 22 4 55}
 
+# Verify that the view v5 gets flattened.  see sqliteFlattenSubquery().
+# Ticket #272
+do_test view-5.3 {
+  lsearch [execsql {
+    EXPLAIN SELECT * FROM v5;
+  }] OpenTemp
+} {-1}
+do_test view-5.4 {
+  execsql {
+    SELECT * FROM v5 AS a, t2 AS b WHERE a.w=b.y;
+  }
+} {1 22 22 2 4 55 55 5}
+do_test view-5.5 {
+  lsearch [execsql {
+    EXPLAIN SELECT * FROM v5 AS a, t2 AS b WHERE a.w=b.y;
+  }] OpenTemp
+} {-1}
+do_test view-5.6 {
+  execsql {
+    SELECT * FROM t2 AS b, v5 AS a WHERE a.w=b.y;
+  }
+} {22 2 1 22 55 5 4 55}
+do_test view-5.7 {
+  lsearch [execsql {
+    EXPLAIN SELECT * FROM t2 AS b, v5 AS a WHERE a.w=b.y;
+  }] OpenTemp
+} {-1}
+do_test view-5.8 {
+  execsql {
+    SELECT * FROM t1 AS a, v5 AS b, t2 AS c WHERE a.x=b.v AND b.w=c.y;
+  }
+} {1 2 3 4 1 22 22 2 4 5 6 7 4 55 55 5}
+do_test view-5.9 {
+  lsearch [execsql {
+    EXPLAIN SELECT * FROM t1 AS a, v5 AS b, t2 AS c WHERE a.x=b.v AND b.w=c.y;
+  }] OpenTemp
+} {-1}
+
 do_test view-6.1 {
   execsql {
     SELECT min(x), min(a), min(b), min(c), min(a+b+c) FROM v2;