1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-05 23:56:58 +03:00

Test HAVING condition before computing targetlist of an Aggregate node.

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.
This commit is contained in:
Tom Lane 2004-07-10 18:39:23 +00:00
parent afa035c204
commit 82f755ec80

View File

@ -45,7 +45,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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; AggStatePerGroup pergroup;
TupleTableSlot *outerslot; TupleTableSlot *outerslot;
TupleTableSlot *firstSlot; TupleTableSlot *firstSlot;
TupleTableSlot *resultSlot;
int aggno; int aggno;
/* /*
@ -696,11 +695,8 @@ agg_retrieve_direct(AggState *aggstate)
* We loop retrieving groups until we find one matching * We loop retrieving groups until we find one matching
* aggstate->ss.ps.qual * 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, * If we don't already have the first tuple of the new group,
* fetch it from the outer plan. * 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 * If we have no first tuple (ie, the outerPlan didn't return
* anything), create a dummy all-nulls input tuple for use by * 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 * because ordinarily the projected output tuple's targetlist
* cannot contain any direct (non-aggregated) references to input * cannot contain any direct (non-aggregated) references to input
* columns, so the dummy tuple will not be referenced. However * 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 * Use the representative input tuple for any references to
* representative input tuple. Store it in the result tuple slot. * non-aggregated input columns in the qual and tlist.
* Note we do not support aggregates returning sets ...
*/ */
econtext->ecxt_scantuple = firstSlot; econtext->ecxt_scantuple = firstSlot;
resultSlot = ExecProject(projInfo, NULL);
/* /*
* If the completed tuple does not match the qualifications, it is * Check the qual (HAVING clause); if the group does not match,
* ignored and we loop back to try to process another group. * ignore it and loop back to try to process another group.
* Otherwise, return the tuple.
*/ */
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; AggStatePerGroup pergroup;
AggHashEntry entry; AggHashEntry entry;
TupleTableSlot *firstSlot; TupleTableSlot *firstSlot;
TupleTableSlot *resultSlot;
int aggno; int aggno;
/* /*
@ -952,11 +953,8 @@ agg_retrieve_hash_table(AggState *aggstate)
* We loop retrieving groups until we find one satisfying * We loop retrieving groups until we find one satisfying
* aggstate->ss.ps.qual * aggstate->ss.ps.qual
*/ */
do while (!aggstate->agg_done)
{ {
if (aggstate->agg_done)
return NULL;
/* /*
* Find the next entry in the hash table * 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 * Use the representative input tuple for any references to
* representative input tuple. Store it in the result tuple slot. * non-aggregated input columns in the qual and tlist.
* Note we do not support aggregates returning sets ...
*/ */
econtext->ecxt_scantuple = firstSlot; econtext->ecxt_scantuple = firstSlot;
resultSlot = ExecProject(projInfo, NULL);
/* /*
* If the completed tuple does not match the qualifications, it is * Check the qual (HAVING clause); if the group does not match,
* ignored and we loop back to try to process another group. * ignore it and loop back to try to process another group.
* Otherwise, return the tuple.
*/ */
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;
} }
/* ----------------- /* -----------------