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
This commit is contained in:
drh 2003-05-02 16:04:17 +00:00
parent 6a3ea0e6ef
commit c31c2eb82c
4 changed files with 113 additions and 24 deletions

View File

@ -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

View File

@ -1 +1 @@
be7aed2011b4af868b6a0c370c3d41354ae0cdf4
ad57693e9f1b83a8cc4d028264b35018a9a4a701

View 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;

View 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;