]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Improved handling of aggregate subqueries within an aggregate query.
authordrh <drh@noemail.net>
Tue, 17 Apr 2012 16:38:53 +0000 (16:38 +0000)
committerdrh <drh@noemail.net>
Tue, 17 Apr 2012 16:38:53 +0000 (16:38 +0000)
FossilOrigin-Name: 430bb59d798286a86c351de92c429345f016b3f0

manifest
manifest.uuid
src/expr.c
src/sqliteInt.h
test/subquery.test

index 5d4be74e07fb3492d4601c03bc3f3cd8969a5474..75fe99adec7ca7ddbbf46919e42f422c6aabdbda 100644 (file)
--- 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
index 849339b16d96468597feff0a3d46f99f515b6036..59d3d01d7789bb6b3baff951df9941f1c15b8b0e 100644 (file)
@@ -1 +1 @@
-8e2363ad76446e863d03ead91fd621e59d5cb495
\ No newline at end of file
+430bb59d798286a86c351de92c429345f016b3f0
\ No newline at end of file
index 4751ec91bde8f31bfa08b74cbb9b7da182e55f03..328de4e5ea980ea4baf58c31c954641fa3d15883 100644 (file)
@@ -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; i<pSrc->nSrc; 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;
index ccffe09c2e864e15b61304734f641cdd777c9bbb..cb178ff97cc6a116ae4afd16fe00756c9d011d11 100644 (file)
@@ -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;
 };
 
index 169cedace6e435d750b73b1a113e4959032ef5b0..0010e45ff83cb82aefebb58a7d76170a673dbefd 100644 (file)
@@ -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