Test HAVING condition before computing targetlist of an Aggregate node.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Jul 2004 18:39:23 +0000 (18:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 10 Jul 2004 18:39:23 +0000 (18:39 +0000)
This is required by SQL spec to avoid failures in cases like
  SELECT sum(win)/sum(lose) FROM ... GROUP BY ... HAVING sum(lose) > 0;
AFAICT we have gotten this wrong since day one.  Kudos to Holger Jakobs
for being the first to notice.

src/backend/executor/nodeAgg.c

index 119f5da346c9f3f7408e9890a5313bed3c60d078..ceddae7a4eb9d8025db7ca73a2712a1affdc252d 100644 (file)
@@ -45,7 +45,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.122 2004/06/06 00:41:26 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.123 2004/07/10 18:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -674,7 +674,6 @@ agg_retrieve_direct(AggState *aggstate)
    AggStatePerGroup pergroup;
    TupleTableSlot *outerslot;
    TupleTableSlot *firstSlot;
-   TupleTableSlot *resultSlot;
    int         aggno;
 
    /*
@@ -696,11 +695,8 @@ agg_retrieve_direct(AggState *aggstate)
     * We loop retrieving groups until we find one matching
     * aggstate->ss.ps.qual
     */
-   do
+   while (!aggstate->agg_done)
    {
-       if (aggstate->agg_done)
-           return NULL;
-
        /*
         * If we don't already have the first tuple of the new group,
         * fetch it from the outer plan.
@@ -815,7 +811,7 @@ agg_retrieve_direct(AggState *aggstate)
        /*
         * If we have no first tuple (ie, the outerPlan didn't return
         * anything), create a dummy all-nulls input tuple for use by
-        * ExecProject. 99.44% of the time this is a waste of cycles,
+        * ExecQual/ExecProject. 99.44% of the time this is a waste of cycles,
         * because ordinarily the projected output tuple's targetlist
         * cannot contain any direct (non-aggregated) references to input
         * columns, so the dummy tuple will not be referenced. However
@@ -857,22 +853,28 @@ agg_retrieve_direct(AggState *aggstate)
        }
 
        /*
-        * Form a projection tuple using the aggregate results and the
-        * representative input tuple.  Store it in the result tuple slot.
-        * Note we do not support aggregates returning sets ...
+        * Use the representative input tuple for any references to
+        * non-aggregated input columns in the qual and tlist.
         */
        econtext->ecxt_scantuple = firstSlot;
-       resultSlot = ExecProject(projInfo, NULL);
 
        /*
-        * If the completed tuple does not match the qualifications, it is
-        * ignored and we loop back to try to process another group.
-        * Otherwise, return the tuple.
+        * Check the qual (HAVING clause); if the group does not match,
+        * ignore it and loop back to try to process another group.
         */
+       if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+       {
+           /*
+            * Form and return a projection tuple using the aggregate results
+            * and the representative input tuple.  Note we do not support
+            * aggregates returning sets ...
+            */
+           return ExecProject(projInfo, NULL);
+       }
    }
-   while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
 
-   return resultSlot;
+   /* No more groups */
+   return NULL;
 }
 
 /*
@@ -934,7 +936,6 @@ agg_retrieve_hash_table(AggState *aggstate)
    AggStatePerGroup pergroup;
    AggHashEntry entry;
    TupleTableSlot *firstSlot;
-   TupleTableSlot *resultSlot;
    int         aggno;
 
    /*
@@ -952,11 +953,8 @@ agg_retrieve_hash_table(AggState *aggstate)
     * We loop retrieving groups until we find one satisfying
     * aggstate->ss.ps.qual
     */
-   do
+   while (!aggstate->agg_done)
    {
-       if (aggstate->agg_done)
-           return NULL;
-
        /*
         * Find the next entry in the hash table
         */
@@ -999,22 +997,28 @@ agg_retrieve_hash_table(AggState *aggstate)
        }
 
        /*
-        * Form a projection tuple using the aggregate results and the
-        * representative input tuple.  Store it in the result tuple slot.
-        * Note we do not support aggregates returning sets ...
+        * Use the representative input tuple for any references to
+        * non-aggregated input columns in the qual and tlist.
         */
        econtext->ecxt_scantuple = firstSlot;
-       resultSlot = ExecProject(projInfo, NULL);
 
        /*
-        * If the completed tuple does not match the qualifications, it is
-        * ignored and we loop back to try to process another group.
-        * Otherwise, return the tuple.
+        * Check the qual (HAVING clause); if the group does not match,
+        * ignore it and loop back to try to process another group.
         */
+       if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+       {
+           /*
+            * Form and return a projection tuple using the aggregate results
+            * and the representative input tuple.  Note we do not support
+            * aggregates returning sets ...
+            */
+           return ExecProject(projInfo, NULL);
+       }
    }
-   while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
 
-   return resultSlot;
+   /* No more groups */
+   return NULL;
 }
 
 /* -----------------