aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_agg.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_agg.c')
-rw-r--r--src/backend/parser/parse_agg.c94
1 files changed, 89 insertions, 5 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 05943969215..3ffa4f82605 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.88 2009/06/11 14:49:00 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_agg.c,v 1.89 2009/12/15 17:57:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -19,8 +19,10 @@
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_agg.h"
+#include "parser/parse_clause.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
#include "utils/lsyscache.h"
@@ -43,15 +45,97 @@ static bool check_ungrouped_columns_walker(Node *node,
* Finish initial transformation of an aggregate call
*
* parse_func.c has recognized the function as an aggregate, and has set
- * up all the fields of the Aggref except agglevelsup. Here we must
- * determine which query level the aggregate actually belongs to, set
- * agglevelsup accordingly, and mark p_hasAggs true in the corresponding
+ * up all the fields of the Aggref except aggdistinct and agglevelsup.
+ * However, the args list is just bare expressions, and the aggorder list
+ * hasn't been transformed at all.
+ *
+ * Here we convert the args list into a targetlist by inserting TargetEntry
+ * nodes, and then transform the aggorder and agg_distinct specifications to
+ * produce lists of SortGroupClause nodes. (That might also result in adding
+ * resjunk expressions to the targetlist.)
+ *
+ * We must also determine which query level the aggregate actually belongs to,
+ * set agglevelsup accordingly, and mark p_hasAggs true in the corresponding
* pstate level.
*/
void
-transformAggregateCall(ParseState *pstate, Aggref *agg)
+transformAggregateCall(ParseState *pstate, Aggref *agg, bool agg_distinct)
{
+ List *tlist;
+ List *torder;
+ List *tdistinct = NIL;
+ AttrNumber attno;
+ int save_next_resno;
int min_varlevel;
+ ListCell *lc;
+
+ /*
+ * Transform the plain list of Exprs into a targetlist. We don't bother
+ * to assign column names to the entries.
+ */
+ tlist = NIL;
+ attno = 1;
+ foreach(lc, agg->args)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+ TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
+
+ tlist = lappend(tlist, tle);
+ }
+
+ /*
+ * If we have an ORDER BY, transform it. This will add columns to the
+ * tlist if they appear in ORDER BY but weren't already in the arg list.
+ * They will be marked resjunk = true so we can tell them apart from
+ * regular aggregate arguments later.
+ *
+ * We need to mess with p_next_resno since it will be used to number any
+ * new targetlist entries.
+ */
+ save_next_resno = pstate->p_next_resno;
+ pstate->p_next_resno = attno;
+
+ torder = transformSortClause(pstate,
+ agg->aggorder,
+ &tlist,
+ true /* fix unknowns */,
+ true /* force SQL99 rules */);
+
+ /*
+ * If we have DISTINCT, transform that to produce a distinctList.
+ */
+ if (agg_distinct)
+ {
+ tdistinct = transformDistinctClause(pstate, &tlist, torder, true);
+
+ /*
+ * Remove this check if executor support for hashed distinct for
+ * aggregates is ever added.
+ */
+ foreach(lc, tdistinct)
+ {
+ SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
+
+ if (!OidIsValid(sortcl->sortop))
+ {
+ Node *expr = get_sortgroupclause_expr(sortcl, tlist);
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an ordering operator for type %s",
+ format_type_be(exprType(expr))),
+ errdetail("Aggregates with DISTINCT must be able to sort their inputs."),
+ parser_errposition(pstate, exprLocation(expr))));
+ }
+ }
+ }
+
+ /* Update the Aggref with the transformation results */
+ agg->args = tlist;
+ agg->aggorder = torder;
+ agg->aggdistinct = tdistinct;
+
+ pstate->p_next_resno = save_next_resno;
/*
* The aggregate's level is the same as the level of the lowest-level