From: drh Date: Tue, 17 Apr 2012 16:38:53 +0000 (+0000) Subject: Improved handling of aggregate subqueries within an aggregate query. X-Git-Tag: version-3.7.12~33 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=374fdce485ab55739dfd307ac00a59ebe8ce8d25;p=thirdparty%2Fsqlite.git Improved handling of aggregate subqueries within an aggregate query. FossilOrigin-Name: 430bb59d798286a86c351de92c429345f016b3f0 --- diff --git a/manifest b/manifest index 5d4be74e07..75fe99adec 100644 --- a/manifest +++ b/manifest @@ -1,5 +1,5 @@ -C Add\san\sundocumented\sand\spossibly\sephemeral\s".breakpoint"\scommand\sto\sthe\s\ncommand-line\sshell,\sto\scall\sa\sno-op\sroutine\son\swhich\sit\sis\sconvenient\sto\s\nset\sa\ssymbolic\sdebugger\sbreakpoint. -D 2012-04-17T09:09:33.765 +C Improved\shandling\sof\saggregate\ssubqueries\swithin\san\saggregate\squery. +D 2012-04-17T16:38:53.916 F Makefile.arm-wince-mingw32ce-gcc d6df77f1f48d690bd73162294bbba7f59507c72f F Makefile.in 2f37e468503dbe79d35c9f6dffcf3fae1ae9ec20 F Makefile.linux-gcc 91d710bdc4998cb015f39edf3cb314ec4f4d7e23 @@ -135,7 +135,7 @@ F src/complete.c dc1d136c0feee03c2f7550bafc0d29075e36deac F src/ctime.c a9c26822515f81ec21588cbb482ca6724be02e33 F src/date.c 067a81c9942c497aafd2c260e13add8a7d0c7dd4 F src/delete.c 4c20ea4f6213b3bc1c6a510586864b679946e05e -F src/expr.c ebb0e2b21379d4ec0c5c2c7c952784cb300c8436 +F src/expr.c 1b2383adc4391ddae38abb71fd4690a3af8efb01 F src/fault.c 160a0c015b6c2629d3899ed2daf63d75754a32bb F src/fkey.c 657212460bf5cfd3ae607d12ea62092844c227b5 F src/func.c c6b3c94320253a35bda43fb69cc292618e3285d6 @@ -185,7 +185,7 @@ F src/select.c d7b9018b7dd2e821183d69477ab55c39b8272335 F src/shell.c 11185a9a4574f363bd4268a2780d37480ae00040 F src/sqlite.h.in 4338f299fc83dada8407358d585c0e240ecb76a3 F src/sqlite3ext.h 6904f4aadf976f95241311fbffb00823075d9477 -F src/sqliteInt.h ce7d8404f15db6cbe73cf196d3d6198aaa4e3924 +F src/sqliteInt.h c5e917c4f1453f3972b1fd0c81105dfe4f09cc32 F src/sqliteLimit.h 164b0e6749d31e0daa1a4589a169d31c0dec7b3d F src/status.c 35939e7e03abf1b7577ce311f48f682c40de3208 F src/table.c 2cd62736f845d82200acfa1287e33feb3c15d62e @@ -713,7 +713,7 @@ F test/speed4p.test 0e51908951677de5a969b723e03a27a1c45db38b F test/sqllimits1.test b1aae27cc98eceb845e7f7adf918561256e31298 F test/stat.test 08e8185b3fd5b010c90d7ad82b9dd4ea1cbf14b0 F test/stmt.test 25d64e3dbf9a3ce89558667d7f39d966fe2a71b9 -F test/subquery.test b524f57c9574b2c0347045b4510ef795d4686796 +F test/subquery.test c5e0d183f1ae6251453338a465b32ae11326e0fa F test/subquery2.test edcad5c118f0531c2e21bf16a09bbb105252d4cd F test/subselect.test d24fd8757daf97dafd2e889c73ea4c4272dcf4e4 F test/substr.test 18f57c4ca8a598805c4d64e304c418734d843c1a @@ -1000,7 +1000,7 @@ F tool/tostr.awk e75472c2f98dd76e06b8c9c1367f4ab07e122d06 F tool/vdbe-compress.tcl d70ea6d8a19e3571d7ab8c9b75cba86d1173ff0f F tool/warnings-clang.sh a8a0a3babda96dfb1ff51adda3cbbf3dfb7266c2 F tool/warnings.sh fbc018d67fd7395f440c28f33ef0f94420226381 -P 020b5e90f950a42299074ff770612b9e68850d95 -R b1350dfbc2eb46aaca5347bd8a799d07 +P 8e2363ad76446e863d03ead91fd621e59d5cb495 +R f4f12ce6163c2f6bb1a71deb9cb7039c U drh -Z ed107cd283f4ba98c58e464c44f875e3 +Z 58b2a5ebe7a572f77bef677b95126ab0 diff --git a/manifest.uuid b/manifest.uuid index 849339b16d..59d3d01d77 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -8e2363ad76446e863d03ead91fd621e59d5cb495 \ No newline at end of file +430bb59d798286a86c351de92c429345f016b3f0 \ No newline at end of file diff --git a/src/expr.c b/src/expr.c index 4751ec91bd..328de4e5ea 100644 --- a/src/expr.c +++ b/src/expr.c @@ -3778,7 +3778,7 @@ int sqlite3ExprCompare(Expr *pA, Expr *pB){ if( !ExprHasProperty(pB, EP_IntValue) || pA->u.iValue!=pB->u.iValue ){ return 2; } - }else if( pA->op!=TK_COLUMN && pA->u.zToken ){ + }else if( pA->op!=TK_COLUMN && pA->op!=TK_AGG_COLUMN && pA->u.zToken ){ if( ExprHasProperty(pB, EP_IntValue) || NEVER(pB->u.zToken==0) ) return 2; if( strcmp(pA->u.zToken,pB->u.zToken)!=0 ){ return 2; @@ -3815,6 +3815,41 @@ int sqlite3ExprListCompare(ExprList *pA, ExprList *pB){ return 0; } +/* +** This is the expression callback for sqlite3FunctionUsesOtherSrc(). +** +** Determine if an expression references any table other than one of the +** tables in pWalker->u.pSrcList and abort if it does. +*/ +static int exprUsesOtherSrc(Walker *pWalker, Expr *pExpr){ + if( pExpr->op==TK_COLUMN || pExpr->op==TK_AGG_COLUMN ){ + int i; + SrcList *pSrc = pWalker->u.pSrcList; + for(i=0; inSrc; i++){ + if( pExpr->iTable==pSrc->a[i].iCursor ) return WRC_Continue; + } + return WRC_Abort; + }else{ + 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; + assert( pExpr->op==TK_AGG_FUNCTION ); + memset(&w, 0, sizeof(w)); + w.xExprCallback = exprUsesOtherSrc; + w.u.pSrcList = pSrcList; + if( sqlite3WalkExprList(&w, pExpr->x.pList)!=WRC_Continue ) return 1; + return 0; +} + /* ** Add a new element to the pAggInfo->aCol[] array. Return the index of ** the new element. Return a negative number if malloc fails. @@ -3930,9 +3965,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ return WRC_Prune; } case TK_AGG_FUNCTION: { - /* The pNC->nDepth==0 test causes aggregate functions in subqueries - ** to be ignored */ - if( pNC->nDepth==0 ){ + if( !sqlite3FunctionUsesOtherSrc(pExpr, pSrcList) ){ /* Check to see if pExpr is a duplicate of another aggregate ** function that is already in the pAggInfo structure */ @@ -3976,15 +4009,7 @@ static int analyzeAggregate(Walker *pWalker, Expr *pExpr){ return WRC_Continue; } static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ - NameContext *pNC = pWalker->u.pNC; - if( pNC->nDepth==0 ){ - pNC->nDepth++; - sqlite3WalkSelect(pWalker, pSelect); - pNC->nDepth--; - return WRC_Prune; - }else{ - return WRC_Continue; - } + return WRC_Continue; } /* @@ -3997,6 +4022,7 @@ static int analyzeAggregatesInSelect(Walker *pWalker, Select *pSelect){ */ void sqlite3ExprAnalyzeAggregates(NameContext *pNC, Expr *pExpr){ Walker w; + memset(&w, 0, sizeof(w)); w.xExprCallback = analyzeAggregate; w.xSelectCallback = analyzeAggregatesInSelect; w.u.pNC = pNC; diff --git a/src/sqliteInt.h b/src/sqliteInt.h index ccffe09c2e..cb178ff97c 100644 --- a/src/sqliteInt.h +++ b/src/sqliteInt.h @@ -2011,7 +2011,6 @@ struct NameContext { u8 allowAgg; /* Aggregate functions allowed here */ u8 hasAgg; /* True if aggregates are seen */ u8 isCheck; /* True if resolving names in a CHECK constraint */ - int nDepth; /* Depth of subquery recursion. 1 for no recursion */ AggInfo *pAggInfo; /* Information about aggregates at this level */ NameContext *pNext; /* Next outer name context. NULL for outermost */ }; @@ -2477,6 +2476,7 @@ struct Walker { union { /* Extra data for callback */ NameContext *pNC; /* Naming context */ int i; /* Integer value */ + SrcList *pSrcList; /* FROM clause */ } u; }; diff --git a/test/subquery.test b/test/subquery.test index 169cedace6..0010e45ff8 100644 --- a/test/subquery.test +++ b/test/subquery.test @@ -331,6 +331,53 @@ do_test subquery-3.3.5 { } } {1 1 2 1} +# The following tests check for aggregate subqueries in an aggregate +# query. +# +do_test subquery-3.4.1 { + execsql { + CREATE TABLE t34(x,y); + INSERT INTO t34 VALUES(106,4), (107,3), (106,5), (107,5); + SELECT a.x, avg(a.y) + FROM t34 AS a + GROUP BY a.x + HAVING NOT EXISTS( SELECT b.x, avg(b.y) + FROM t34 AS b + GROUP BY b.x + HAVING avg(a.y) > avg(b.y)); + } +} {107 4.0} +do_test subquery-3.4.2 { + execsql { + SELECT a.x, avg(a.y) AS avg1 + FROM t34 AS a + GROUP BY a.x + HAVING NOT EXISTS( SELECT b.x, avg(b.y) AS avg2 + FROM t34 AS b + GROUP BY b.x + HAVING avg1 > avg2); + } +} {107 4.0} +do_test subquery-3.4.3 { + execsql { + SELECT + a.x, + avg(a.y), + NOT EXISTS ( SELECT b.x, avg(b.y) + FROM t34 AS b + GROUP BY b.x + HAVING avg(a.y) > avg(b.y)), + EXISTS ( SELECT c.x, avg(c.y) + FROM t34 AS c + GROUP BY c.x + HAVING avg(a.y) > avg(c.y)) + FROM t34 AS a + GROUP BY a.x + ORDER BY a.x; + } +} {106 4.5 0 1 107 4.0 1 0} + + #------------------------------------------------------------------ # These tests - subquery-4.* - use the TCL statement cache to try # and expose bugs to do with re-using statements that have been