From c31c2eb82cc67e62cfcf5e6182a46026395e125b Mon Sep 17 00:00:00 2001 From: drh Date: Fri, 2 May 2003 16:04:17 +0000 Subject: [PATCH] Enhance the query flattener to handle subqueries that are joins. All regressions pass but new tests need to be added before release. Ticket #272. (CVS 948) FossilOrigin-Name: ad57693e9f1b83a8cc4d028264b35018a9a4a701 --- manifest | 14 ++++----- manifest.uuid | 2 +- src/select.c | 79 +++++++++++++++++++++++++++++++++++++++++--------- test/view.test | 42 +++++++++++++++++++++++++-- 4 files changed, 113 insertions(+), 24 deletions(-) diff --git a/manifest b/manifest index 8e7eec4f88..9c3e31a3cd 100644 --- 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 diff --git a/manifest.uuid b/manifest.uuid index a5fdcd2ea5..d68424a039 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -be7aed2011b4af868b6a0c370c3d41354ae0cdf4 \ No newline at end of file +ad57693e9f1b83a8cc4d028264b35018a9a4a701 \ No newline at end of file diff --git a/src/select.c b/src/select.c index 7b8733f4a8..0f8c3c8844 100644 --- a/src/select.c +++ b/src/select.c @@ -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; ipSrc = pSrc; + for(i=pSrc->nSrc-1; i-extra>=iFrom; i--){ + pSrc->a[i] = pSrc->a[i-extra]; + } + } + for(i=0; ia[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; inExpr; 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; inSrc; 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; diff --git a/test/view.test b/test/view.test index 57043cfc01..f8a3544bbd 100644 --- a/test/view.test +++ b/test/view.test @@ -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;