diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2005-03-10 23:21:26 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2005-03-10 23:21:26 +0000 |
commit | 595ed2a8550e34c0abe64569a104d92ad077ec08 (patch) | |
tree | 004764220f537256d96637d5a119fd2086ed40ec /src/backend/executor/nodeGroup.c | |
parent | 609e32b929cf8684f8ae3a2b9f1655b372fb8b85 (diff) | |
download | postgresql-595ed2a8550e34c0abe64569a104d92ad077ec08.tar.gz postgresql-595ed2a8550e34c0abe64569a104d92ad077ec08.zip |
Make the behavior of HAVING without GROUP BY conform to the SQL spec.
Formerly, if such a clause contained no aggregate functions we mistakenly
treated it as equivalent to WHERE. Per spec it must cause the query to
be treated as a grouped query of a single group, the same as appearance
of aggregate functions would do. Also, the HAVING filter must execute
after aggregate function computation even if it itself contains no
aggregate functions.
Diffstat (limited to 'src/backend/executor/nodeGroup.c')
-rw-r--r-- | src/backend/executor/nodeGroup.c | 120 |
1 files changed, 75 insertions, 45 deletions
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index bdc7fd8992a..f8bd18f3499 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -15,7 +15,7 @@ * locate group boundaries. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.59 2004/12/31 21:59:45 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeGroup.c,v 1.60 2005/03/10 23:21:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,23 +35,19 @@ TupleTableSlot * ExecGroup(GroupState *node) { - EState *estate; ExprContext *econtext; TupleDesc tupdesc; int numCols; AttrNumber *grpColIdx; - HeapTuple outerTuple = NULL; + HeapTuple outerTuple; HeapTuple firsttuple; TupleTableSlot *outerslot; - ProjectionInfo *projInfo; - TupleTableSlot *resultSlot; /* * get state info from node */ if (node->grp_done) return NULL; - estate = node->ss.ps.state; econtext = node->ss.ps.ps_ExprContext; tupdesc = ExecGetScanType(&node->ss); numCols = ((Group *) node->ss.ps.plan)->numCols; @@ -62,67 +58,101 @@ ExecGroup(GroupState *node) * reset the per-tuple memory context once per input tuple. */ - /* If we don't already have first tuple of group, fetch it */ - /* this should occur on the first call only */ + /* + * If first time through, acquire first input tuple and determine + * whether to return it or not. + */ firsttuple = node->grp_firstTuple; if (firsttuple == NULL) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { + /* empty input, so return nothing */ node->grp_done = TRUE; return NULL; } - node->grp_firstTuple = firsttuple = - heap_copytuple(outerslot->val); + node->grp_firstTuple = firsttuple = heap_copytuple(outerslot->val); + /* Set up tuple as input for qual test and projection */ + ExecStoreTuple(firsttuple, + node->ss.ss_ScanTupleSlot, + InvalidBuffer, + false); + econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot; + /* + * Check the qual (HAVING clause); if the group does not match, + * ignore it and fall into scan loop. + */ + if (ExecQual(node->ss.ps.qual, econtext, false)) + { + /* + * Form and return a projection tuple using the first input + * tuple. + */ + return ExecProject(node->ss.ps.ps_ProjInfo, NULL); + } } /* - * Scan over all tuples that belong to this group + * This loop iterates once per input tuple group. At the head of the + * loop, we have finished processing the first tuple of the group and + * now need to scan over all the other group members. */ for (;;) { - outerslot = ExecProcNode(outerPlanState(node)); - if (TupIsNull(outerslot)) + /* + * Scan over all remaining tuples that belong to this group + */ + for (;;) { - node->grp_done = TRUE; - outerTuple = NULL; - break; - } - outerTuple = outerslot->val; + outerslot = ExecProcNode(outerPlanState(node)); + if (TupIsNull(outerslot)) + { + /* no more groups, so we're done */ + node->grp_done = TRUE; + return NULL; + } + outerTuple = outerslot->val; + /* + * Compare with first tuple and see if this tuple is of the same + * group. If so, ignore it and keep scanning. + */ + if (!execTuplesMatch(firsttuple, outerTuple, + tupdesc, + numCols, grpColIdx, + node->eqfunctions, + econtext->ecxt_per_tuple_memory)) + break; + } /* - * Compare with first tuple and see if this tuple is of the same - * group. + * We have the first tuple of the next input group. See if we + * want to return it. */ - if (!execTuplesMatch(firsttuple, outerTuple, - tupdesc, - numCols, grpColIdx, - node->eqfunctions, - econtext->ecxt_per_tuple_memory)) - break; - } - - /* - * form a projection tuple based on the (copied) first tuple of the - * group, and store it in the result tuple slot. - */ - ExecStoreTuple(firsttuple, - node->ss.ss_ScanTupleSlot, - InvalidBuffer, - false); - econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot; - projInfo = node->ss.ps.ps_ProjInfo; - resultSlot = ExecProject(projInfo, NULL); - - /* save first tuple of next group, if we are not done yet */ - if (!node->grp_done) - { heap_freetuple(firsttuple); - node->grp_firstTuple = heap_copytuple(outerTuple); + node->grp_firstTuple = firsttuple = heap_copytuple(outerTuple); + /* Set up tuple as input for qual test and projection */ + ExecStoreTuple(firsttuple, + node->ss.ss_ScanTupleSlot, + InvalidBuffer, + false); + econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot; + /* + * Check the qual (HAVING clause); if the group does not match, + * ignore it and loop back to scan the rest of the group. + */ + if (ExecQual(node->ss.ps.qual, econtext, false)) + { + /* + * Form and return a projection tuple using the first input + * tuple. + */ + return ExecProject(node->ss.ps.ps_ProjInfo, NULL); + } } - return resultSlot; + /* NOTREACHED */ + return NULL; } /* ----------------- |