From ed551b95a4a0f71be84820b31d4c865eea294407 Mon Sep 17 00:00:00 2001 From: drh Date: Thu, 23 Aug 2012 19:46:11 +0000 Subject: [PATCH] Add test cases and fix bugs associated with the previous check-in enhancements to nested aggregate subquery processing. FossilOrigin-Name: 00b1dc71be4c3420730b5f7840af824ea86165e7 --- manifest | 21 +++++++++------------ manifest.uuid | 2 +- src/expr.c | 35 +++++++++-------------------------- src/resolve.c | 41 +++++++++++++++++++++++++++++++++++++---- src/walker.c | 12 +++++++++--- test/aggnested.test | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 98 insertions(+), 46 deletions(-) diff --git a/manifest b/manifest index a43f6d1a8f..e26aaf6a30 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Further\simprovements\sto\sthe\sprocessing\sof\snested\saggregate\squeries. -D 2012-08-23T16:18:10.544 +C Add\stest\scases\sand\sfix\sbugs\sassociated\swith\sthe\sprevious\scheck-in\nenhancements\sto\snested\saggregate\ssubquery\sprocessing. +D 2012-08-23T19:46:11.832 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in abd5c10d21d1395f140d9e50ea999df8fa4d6376 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -132,7 +132,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c 500d019da966631ad957c37705642be87524463b F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e -F src/expr.c e03a5509b5c8f0d0b5e8210ea43addabc4a17f47 +F src/expr.c 94bac8cc555d97fe4518529ad53d9ba926df9d62 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c 18dfedfb857e100b05755a1b12e88b389f957879 @@ -174,7 +174,7 @@ F src/pragma.c 97f9357f0e7e5fb46a2519f14539550aa07db49f F src/prepare.c 33291b83cca285718048d219c67b8298501fa3a5 F src/printf.c 4a9f882f1c1787a8b494a2987765acf9d97ac21f F src/random.c cd4a67b3953b88019f8cd4ccd81394a8ddfaba50 -F src/resolve.c e60d1f7ce1f1d1ae13acf1c39b4e8bfd9e243847 +F src/resolve.c 9e28280ec98035f31900fdd1db01f86f68ca6c32 F src/rowset.c f6a49f3e9579428024662f6e2931832511f831a1 F src/select.c cd051b460e7d0c3ac42e7727eef075fb29c23769 F src/shell.c 076e1c90d594644f36027c8ecff9a392cf2d3a06 @@ -249,11 +249,11 @@ F src/vdbetrace.c 8bd5da325fc90f28464335e4cc4ad1407fe30835 F src/vtab.c bb8ea3a26608bb1357538a5d2fc72beba6638998 F src/wal.c 9294df6f96aae5909ae1a9b733fd1e1b4736978b F src/wal.h 29c197540b19044e6cd73487017e5e47a1d3dac6 -F src/walker.c 3112bb3afe1d85dc52317cb1d752055e9a781f8f +F src/walker.c 3d75ba73de15e0f8cd0737643badbeb0e002f07b F src/where.c 24c7494d8875ead994b4dfe5461340c27fd424ca F test/8_3_names.test 631ea964a3edb091cf73c3b540f6bcfdb36ce823 F test/aggerror.test a867e273ef9e3d7919f03ef4f0e8c0d2767944f2 -F test/aggnested.test 6c1efa2ed9f5dd0f80035b4fe794c6a278cd1ead +F test/aggnested.test 0be144b453e0622a085fae8665c32f5676708e00 F test/alias.test 4529fbc152f190268a15f9384a5651bbbabc9d87 F test/all.test 52fc8dee494092031a556911d404ca30a749a30b F test/alter.test 57d96ec9b320bd07af77567034488dcb6642c748 @@ -1012,10 +1012,7 @@ F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh f6aa929dc20ef1f856af04a730772f59283631d4 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 F tool/win/sqlite.vsix 67d8a99aceb56384a81b3f30d6c71743146d2cc9 -P b1dbf490869d7fc55ce797cf80cf3bf7141d2d15 -R 6f0858104ecac7c70bd80af9dbc9e718 -T *branch * nested-agg -T *sym-nested-agg * -T -sym-trunk * +P 3c3ffa901f5ce8a523028ff15563ce3e0f55a641 +R 7ac9abcbd2554a39d90c74812c59041d U drh -Z fbb7622480ad2005f08fba3df589ccf6 +Z 5534967a0ee04b7aa372747f5ce8ed30 diff --git a/manifest.uuid b/manifest.uuid index 3c6db4542e..e2e4740fdd 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -3c3ffa901f5ce8a523028ff15563ce3e0f55a641 \ No newline at end of file +00b1dc71be4c3420730b5f7840af824ea86165e7 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index e2f8b45786..9fe8994023 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3123,9 +3123,12 @@ void sqlite3ExplainExpr(Vdbe *pOut, Expr *pExpr){ }else{ pFarg = pExpr->x.pList; } - sqlite3ExplainPrintf(pOut, "%sFUNCTION:%s(", - op==TK_AGG_FUNCTION ? "AGG_" : "", - pExpr->u.zToken); + if( op==TK_AGG_FUNCTION ){ + sqlite3ExplainPrintf(pOut, "AGG_FUNCTION%d:%s(", + pExpr->op2, pExpr->u.zToken); + }else{ + sqlite3ExplainPrintf(pOut, "FUNCTION:%s(", pExpr->u.zToken); + } if( pFarg ){ sqlite3ExplainExprList(pOut, pFarg); } @@ -3818,8 +3821,8 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){ /* ** An instance of the following structure is used by the tree walker ** to count references to table columns in the arguments of an -** aggregate function, in order to implement the sqlite3FunctionUsesOtherSrc() -** and sqlite3FunctionThisSrc() routines. +** aggregate function, in order to implement the +** sqlite3FunctionThisSrc() routine. */ struct SrcCount { SrcList *pSrc; /* One particular FROM clause in a nested query */ @@ -3847,26 +3850,6 @@ static int exprSrcCount(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } -/* -** Determine if any of the arguments to the pExpr Function references -** any SrcList other than pSrcList. Return true if they do. Return -** false if pExpr has no argument or has only constant arguments or -** only references tables named in pSrcList. -*/ -static int sqlite3FunctionUsesOtherSrc(Expr *pExpr, SrcList *pSrcList){ - Walker w; - struct SrcCount cnt; - assert( pExpr->op==TK_AGG_FUNCTION ); - memset(&w, 0, sizeof(w)); - w.xExprCallback = exprSrcCount; - w.u.pSrcCount = &cnt; - cnt.pSrc = pSrcList; - cnt.nThis = 0; - cnt.nOther = 0; - sqlite3WalkExprList(&w, pExpr->x.pList); - return cnt.nOther>0; -} - /* ** Determine if any of the arguments to the pExpr Function reference ** pSrcList. Return true if they do. Also return true if the function @@ -4003,7 +3986,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ } case TK_AGG_FUNCTION: { if( (pNC->ncFlags & NC_InAggFunc)==0 - && !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) + && pWalker->walkerDepth==pExpr->op2 ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure diff --git a/src/resolve.c b/src/resolve.c index bfbcd20419..b87d231ac2 100644 --- a/src/resolve.c +++ b/src/resolve.c @@ -18,6 +18,29 @@ #include #include +/* +** Walk the expression tree pExpr and increase the aggregate function +** depth (the Expr.op2 field) by N on every TK_AGG_FUNCTION node. +** This needs to occur when copying a TK_AGG_FUNCTION node from an +** outer query into an inner subquery. +** +** incrAggFunctionDepth(pExpr,n) is the main routine. incrAggDepth(..) +** is a helper function - a callback for the tree walker. +*/ +static int incrAggDepth(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_AGG_FUNCTION ) pExpr->op2 += pWalker->u.i; + return WRC_Continue; +} +static void incrAggFunctionDepth(Expr *pExpr, int N){ + if( N>0 ){ + Walker w; + memset(&w, 0, sizeof(w)); + w.xExprCallback = incrAggDepth; + w.u.i = N; + sqlite3WalkExpr(&w, pExpr); + } +} + /* ** Turn the pExpr expression into an alias for the iCol-th column of the ** result set in pEList. @@ -44,13 +67,20 @@ ** The result of random()%5 in the GROUP BY clause is probably different ** from the result in the result-set. We might fix this someday. Or ** then again, we might not... +** +** The nSubquery parameter specifies how many levels of subquery the +** alias is removed from the original expression. The usually value is +** zero but it might be more if the alias is contained within a subquery +** of the original expression. The Expr.op2 field of TK_AGG_FUNCTION +** structures must be increased by the nSubquery amount. */ static void resolveAlias( Parse *pParse, /* Parsing context */ ExprList *pEList, /* A result set */ int iCol, /* A column in the result set. 0..pEList->nExpr-1 */ Expr *pExpr, /* Transform this into an alias to the result set */ - const char *zType /* "GROUP" or "ORDER" or "" */ + const char *zType, /* "GROUP" or "ORDER" or "" */ + int nSubquery /* Number of subqueries that the label is moving */ ){ Expr *pOrig; /* The iCol-th column of the result set */ Expr *pDup; /* Copy of pOrig */ @@ -63,6 +93,7 @@ static void resolveAlias( db = pParse->db; if( pOrig->op!=TK_COLUMN && zType[0]!='G' ){ pDup = sqlite3ExprDup(db, pOrig, 0); + incrAggFunctionDepth(pDup, nSubquery); pDup = sqlite3PExpr(pParse, TK_AS, pDup, 0, 0); if( pDup==0 ) return; if( pEList->a[iCol].iAlias==0 ){ @@ -151,9 +182,10 @@ static int lookupName( NameContext *pNC, /* The name context used to resolve the name */ Expr *pExpr /* Make this EXPR node point to the selected column */ ){ - int i, j; /* Loop counters */ + int i, j; /* Loop counters */ int cnt = 0; /* Number of matching column names */ int cntTab = 0; /* Number of matching table names */ + int nSubquery = 0; /* How many levels of subquery */ sqlite3 *db = pParse->db; /* The database connection */ struct SrcList_item *pItem; /* Use for looping over pSrcList items */ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ @@ -315,7 +347,7 @@ static int lookupName( sqlite3ErrorMsg(pParse, "misuse of aliased aggregate %s", zAs); return WRC_Abort; } - resolveAlias(pParse, pEList, j, pExpr, ""); + resolveAlias(pParse, pEList, j, pExpr, "", nSubquery); cnt = 1; pMatch = 0; assert( zTab==0 && zDb==0 ); @@ -329,6 +361,7 @@ static int lookupName( */ if( cnt==0 ){ pNC = pNC->pNext; + nSubquery++; } } @@ -859,7 +892,7 @@ int sqlite3ResolveOrderGroupBy( resolveOutOfRangeError(pParse, zType, i+1, pEList->nExpr); return 1; } - resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType); + resolveAlias(pParse, pEList, pItem->iOrderByCol-1, pItem->pExpr, zType,0); } } return 0; diff --git a/src/walker.c b/src/walker.c index c95a9c169d..eab96ea24d 100644 --- a/src/walker.c +++ b/src/walker.c @@ -125,12 +125,18 @@ int sqlite3WalkSelect(Walker *pWalker, Select *p){ int rc; if( p==0 || pWalker->xSelectCallback==0 ) return WRC_Continue; rc = WRC_Continue; - while( p ){ + pWalker->walkerDepth++; + while( p ){ rc = pWalker->xSelectCallback(pWalker, p); if( rc ) break; - if( sqlite3WalkSelectExpr(pWalker, p) ) return WRC_Abort; - if( sqlite3WalkSelectFrom(pWalker, p) ) return WRC_Abort; + if( sqlite3WalkSelectExpr(pWalker, p) + || sqlite3WalkSelectFrom(pWalker, p) + ){ + pWalker->walkerDepth--; + return WRC_Abort; + } p = p->pPrior; } + pWalker->walkerDepth--; return rc & WRC_Abort; } diff --git a/test/aggnested.test b/test/aggnested.test index d76398093f..22f0fb6b9a 100644 --- a/test/aggnested.test +++ b/test/aggnested.test @@ -34,5 +34,38 @@ do_test aggnested-1.2 { FROM t1; } } {1x2x3-4y5} +do_test aggnested-1.3 { + db eval { + SELECT (SELECT group_concat(b1,a1) FROM t2) FROM t1; + } +} {415 425 435} +do_test aggnested-1.4 { + db eval { + SELECT (SELECT group_concat(a1,b1) FROM t2) FROM t1; + } +} {151 252 353} + + +# This test case is a copy of the one in +# http://www.mail-archive.com/sqlite-users@sqlite.org/msg70787.html +# +do_test aggnested-2.0 { + sqlite3 db2 :memory: + db2 eval { + CREATE TABLE t1 (A1 INTEGER NOT NULL,A2 INTEGER NOT NULL,A3 INTEGER NOT + NULL,A4 INTEGER NOT NULL,PRIMARY KEY(A1)); + REPLACE INTO t1 VALUES(1,11,111,1111); + REPLACE INTO t1 VALUES(2,22,222,2222); + REPLACE INTO t1 VALUES(3,33,333,3333); + CREATE TABLE t2 (B1 INTEGER NOT NULL,B2 INTEGER NOT NULL,B3 INTEGER NOT + NULL,B4 INTEGER NOT NULL,PRIMARY KEY(B1)); + REPLACE INTO t2 VALUES(1,88,888,8888); + REPLACE INTO t2 VALUES(2,99,999,9999); + SELECT (SELECT GROUP_CONCAT(CASE WHEN a1=1 THEN'A' ELSE 'B' END) FROM t2), + t1.* + FROM t1; + } +} {A,B,B 3 33 333 3333} +db2 close finish_test