summaryrefslogtreecommitdiff
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.c409
1 files changed, 329 insertions, 80 deletions
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 98cb58a7cc0..04a20eefcd4 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -14,16 +14,20 @@
*/
#include "postgres.h"
+#include "catalog/pg_aggregate.h"
#include "catalog/pg_constraint.h"
+#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/tlist.h"
#include "parser/parse_agg.h"
#include "parser/parse_clause.h"
+#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
typedef struct
@@ -42,9 +46,13 @@ typedef struct
bool have_non_var_grouping;
List **func_grouped_rels;
int sublevels_up;
+ bool in_agg_direct_args;
} check_ungrouped_columns_context;
-static int check_agg_arguments(ParseState *pstate, List *args, Expr *filter);
+static int check_agg_arguments(ParseState *pstate,
+ List *directargs,
+ List *args,
+ Expr *filter);
static bool check_agg_arguments_walker(Node *node,
check_agg_arguments_context *context);
static void check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
@@ -59,15 +67,21 @@ 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 args, aggorder, aggdistinct and
- * agglevelsup. The passed-in args list has been through standard expression
- * transformation, while the passed-in aggorder list hasn't been transformed
- * at all.
+ * all the fields of the Aggref except aggdirectargs, args, aggorder,
+ * aggdistinct and agglevelsup. The passed-in args list has been through
+ * standard expression transformation and type coercion to match the agg's
+ * declared arg types, while the passed-in 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.)
+ * Here we separate the args list into direct and aggregated args, storing the
+ * former in agg->aggdirectargs and the latter in agg->args. The regular
+ * args, but not the direct args, are converted into a targetlist by inserting
+ * TargetEntry nodes. We then transform the aggorder and agg_distinct
+ * specifications to produce lists of SortGroupClause nodes for agg->aggorder
+ * and agg->aggdistinct. (For a regular aggregate, this might result in
+ * adding resjunk expressions to the targetlist; but for ordered-set
+ * aggregates the aggorder list will always be one-to-one with the aggregated
+ * args.)
*
* We must also determine which query level the aggregate actually belongs to,
* set agglevelsup accordingly, and mark p_hasAggs true in the corresponding
@@ -77,76 +91,122 @@ void
transformAggregateCall(ParseState *pstate, Aggref *agg,
List *args, List *aggorder, bool agg_distinct)
{
- List *tlist;
- List *torder;
+ List *tlist = NIL;
+ List *torder = NIL;
List *tdistinct = NIL;
- AttrNumber attno;
+ AttrNumber attno = 1;
int save_next_resno;
int min_varlevel;
ListCell *lc;
const char *err;
bool errkind;
- /*
- * 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, args)
+ if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
{
- Expr *arg = (Expr *) lfirst(lc);
- TargetEntry *tle = makeTargetEntry(arg, attno++, NULL, false);
+ /*
+ * For an ordered-set agg, the args list includes direct args and
+ * aggregated args; we must split them apart.
+ */
+ int numDirectArgs = list_length(args) - list_length(aggorder);
+ List *aargs;
+ ListCell *lc2;
- tlist = lappend(tlist, tle);
- }
+ Assert(numDirectArgs >= 0);
- /*
- * 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;
+ aargs = list_copy_tail(args, numDirectArgs);
+ agg->aggdirectargs = list_truncate(args, numDirectArgs);
- torder = transformSortClause(pstate,
- aggorder,
- &tlist,
- EXPR_KIND_ORDER_BY,
- true /* fix unknowns */ ,
- true /* force SQL99 rules */ );
+ /*
+ * Build a tlist from the aggregated args, and make a sortlist entry
+ * for each one. Note that the expressions in the SortBy nodes are
+ * ignored (they are the raw versions of the transformed args); we are
+ * just looking at the sort information in the SortBy nodes.
+ */
+ forboth(lc, aargs, lc2, aggorder)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+ SortBy *sortby = (SortBy *) lfirst(lc2);
+ TargetEntry *tle;
- /*
- * If we have DISTINCT, transform that to produce a distinctList.
- */
- if (agg_distinct)
+ /* We don't bother to assign column names to the entries */
+ tle = makeTargetEntry(arg, attno++, NULL, false);
+ tlist = lappend(tlist, tle);
+
+ torder = addTargetToSortList(pstate, tle,
+ torder, tlist, sortby,
+ true /* fix unknowns */ );
+ }
+
+ /* Never any DISTINCT in an ordered-set agg */
+ Assert(!agg_distinct);
+ }
+ else
{
- tdistinct = transformDistinctClause(pstate, &tlist, torder, true);
+ /* Regular aggregate, so it has no direct args */
+ agg->aggdirectargs = NIL;
+
+ /*
+ * Transform the plain list of Exprs into a targetlist.
+ */
+ foreach(lc, args)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+ TargetEntry *tle;
+
+ /* We don't bother to assign column names to the entries */
+ 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,
+ aggorder,
+ &tlist,
+ EXPR_KIND_ORDER_BY,
+ true /* fix unknowns */ ,
+ true /* force SQL99 rules */ );
/*
- * Remove this check if executor support for hashed distinct for
- * aggregates is ever added.
+ * If we have DISTINCT, transform that to produce a distinctList.
*/
- foreach(lc, tdistinct)
+ if (agg_distinct)
{
- SortGroupClause *sortcl = (SortGroupClause *) lfirst(lc);
+ tdistinct = transformDistinctClause(pstate, &tlist, torder, true);
- if (!OidIsValid(sortcl->sortop))
+ /*
+ * Remove this check if executor support for hashed distinct for
+ * aggregates is ever added.
+ */
+ foreach(lc, tdistinct)
{
- 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))));
+ 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))));
+ }
}
}
+
+ pstate->p_next_resno = save_next_resno;
}
/* Update the Aggref with the transformation results */
@@ -154,13 +214,14 @@ transformAggregateCall(ParseState *pstate, Aggref *agg,
agg->aggorder = torder;
agg->aggdistinct = tdistinct;
- pstate->p_next_resno = save_next_resno;
-
/*
* Check the arguments to compute the aggregate's level and detect
* improper nesting.
*/
- min_varlevel = check_agg_arguments(pstate, agg->args, agg->aggfilter);
+ min_varlevel = check_agg_arguments(pstate,
+ agg->aggdirectargs,
+ agg->args,
+ agg->aggfilter);
agg->agglevelsup = min_varlevel;
/* Mark the correct pstate level as having aggregates */
@@ -302,8 +363,17 @@ transformAggregateCall(ParseState *pstate, Aggref *agg,
* one is its parent, etc).
*
* The aggregate's level is the same as the level of the lowest-level variable
- * or aggregate in its arguments or filter expression; or if it contains no
- * variables at all, we presume it to be local.
+ * or aggregate in its aggregated arguments (including any ORDER BY columns)
+ * or filter expression; or if it contains no variables at all, we presume it
+ * to be local.
+ *
+ * Vars/Aggs in direct arguments are *not* counted towards determining the
+ * agg's level, as those arguments aren't evaluated per-row but only
+ * per-group, and so in some sense aren't really agg arguments. However,
+ * this can mean that we decide an agg is upper-level even when its direct
+ * args contain lower-level Vars/Aggs, and that case has to be disallowed.
+ * (This is a little strange, but the SQL standard seems pretty definite that
+ * direct args are not to be considered when setting the agg's level.)
*
* We also take this opportunity to detect any aggregates or window functions
* nested within the arguments. We can throw error immediately if we find
@@ -312,7 +382,10 @@ transformAggregateCall(ParseState *pstate, Aggref *agg,
* which we can't know until we finish scanning the arguments.
*/
static int
-check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
+check_agg_arguments(ParseState *pstate,
+ List *directargs,
+ List *args,
+ Expr *filter)
{
int agglevel;
check_agg_arguments_context context;
@@ -337,8 +410,9 @@ check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
if (context.min_varlevel < 0)
{
if (context.min_agglevel < 0)
- return 0;
- agglevel = context.min_agglevel;
+ agglevel = 0;
+ else
+ agglevel = context.min_agglevel;
}
else if (context.min_agglevel < 0)
agglevel = context.min_varlevel;
@@ -349,12 +423,49 @@ check_agg_arguments(ParseState *pstate, List *args, Expr *filter)
* If there's a nested aggregate of the same semantic level, complain.
*/
if (agglevel == context.min_agglevel)
+ {
+ int aggloc;
+
+ aggloc = locate_agg_of_level((Node *) args, agglevel);
+ if (aggloc < 0)
+ aggloc = locate_agg_of_level((Node *) filter, agglevel);
ereport(ERROR,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("aggregate function calls cannot be nested"),
- parser_errposition(pstate,
- locate_agg_of_level((Node *) args,
- agglevel))));
+ parser_errposition(pstate, aggloc)));
+ }
+
+ /*
+ * Now check for vars/aggs in the direct arguments, and throw error if
+ * needed. Note that we allow a Var of the agg's semantic level, but not
+ * an Agg of that level. In principle such Aggs could probably be
+ * supported, but it would create an ordering dependency among the
+ * aggregates at execution time. Since the case appears neither to be
+ * required by spec nor particularly useful, we just treat it as a
+ * nested-aggregate situation.
+ */
+ if (directargs)
+ {
+ context.min_varlevel = -1;
+ context.min_agglevel = -1;
+ (void) expression_tree_walker((Node *) directargs,
+ check_agg_arguments_walker,
+ (void *) &context);
+ if (context.min_varlevel >= 0 && context.min_varlevel < agglevel)
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("outer-level aggregate cannot contain a lower-level variable in its direct arguments"),
+ parser_errposition(pstate,
+ locate_var_of_level((Node *) directargs,
+ context.min_varlevel))));
+ if (context.min_agglevel >= 0 && context.min_agglevel <= agglevel)
+ ereport(ERROR,
+ (errcode(ERRCODE_GROUPING_ERROR),
+ errmsg("aggregate function calls cannot be nested"),
+ parser_errposition(pstate,
+ locate_agg_of_level((Node *) directargs,
+ context.min_agglevel))));
+ }
return agglevel;
}
@@ -442,6 +553,10 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
/*
* A window function call can't contain another one (but aggs are OK). XXX
* is this required by spec, or just an unimplemented feature?
+ *
+ * Note: we don't need to check the filter expression here, because the
+ * context checks done below and in transformAggregateCall would have
+ * already rejected any window funcs or aggs within the filter.
*/
if (pstate->p_hasWindowFuncs &&
contain_windowfuncs((Node *) wfunc->args))
@@ -800,6 +915,7 @@ check_ungrouped_columns(Node *node, ParseState *pstate, Query *qry,
context.have_non_var_grouping = have_non_var_grouping;
context.func_grouped_rels = func_grouped_rels;
context.sublevels_up = 0;
+ context.in_agg_direct_args = false;
check_ungrouped_columns_walker(node, &context);
}
@@ -815,16 +931,39 @@ check_ungrouped_columns_walker(Node *node,
IsA(node, Param))
return false; /* constants are always acceptable */
- /*
- * If we find an aggregate call of the original level, do not recurse into
- * its arguments or filter; ungrouped vars there are not an error. We can
- * also skip looking at aggregates of higher levels, since they could not
- * possibly contain Vars of concern to us (see transformAggregateCall).
- * We do need to look at aggregates of lower levels, however.
- */
- if (IsA(node, Aggref) &&
- (int) ((Aggref *) node)->agglevelsup >= context->sublevels_up)
- return false;
+ if (IsA(node, Aggref))
+ {
+ Aggref *agg = (Aggref *) node;
+
+ if ((int) agg->agglevelsup == context->sublevels_up)
+ {
+ /*
+ * If we find an aggregate call of the original level, do not
+ * recurse into its normal arguments, ORDER BY arguments, or
+ * filter; ungrouped vars there are not an error. But we should
+ * check direct arguments as though they weren't in an aggregate.
+ * We set a special flag in the context to help produce a useful
+ * error message for ungrouped vars in direct arguments.
+ */
+ bool result;
+
+ Assert(!context->in_agg_direct_args);
+ context->in_agg_direct_args = true;
+ result = check_ungrouped_columns_walker((Node *) agg->aggdirectargs,
+ context);
+ context->in_agg_direct_args = false;
+ return result;
+ }
+
+ /*
+ * We can skip recursing into aggregates of higher levels altogether,
+ * since they could not possibly contain Vars of concern to us (see
+ * transformAggregateCall). We do need to look at aggregates of lower
+ * levels, however.
+ */
+ if ((int) agg->agglevelsup > context->sublevels_up)
+ return false;
+ }
/*
* If we have any GROUP BY items that are not simple Vars, check to see if
@@ -917,6 +1056,8 @@ check_ungrouped_columns_walker(Node *node,
(errcode(ERRCODE_GROUPING_ERROR),
errmsg("column \"%s.%s\" must appear in the GROUP BY clause or be used in an aggregate function",
rte->eref->aliasname, attname),
+ context->in_agg_direct_args ?
+ errdetail("Direct arguments of an ordered-set aggregate must use only grouped columns.") : 0,
parser_errposition(context->pstate, var->location)));
else
ereport(ERROR,
@@ -944,6 +1085,93 @@ check_ungrouped_columns_walker(Node *node,
}
/*
+ * get_aggregate_argtypes
+ * Identify the specific datatypes passed to an aggregate call.
+ *
+ * Given an Aggref, extract the actual datatypes of the input arguments.
+ * The input datatypes are reported in a way that matches up with the
+ * aggregate's declaration, ie, any ORDER BY columns attached to a plain
+ * aggregate are ignored, but we report both direct and aggregated args of
+ * an ordered-set aggregate.
+ *
+ * Datatypes are returned into inputTypes[], which must reference an array
+ * of length FUNC_MAX_ARGS.
+ *
+ * The function result is the number of actual arguments.
+ */
+int
+get_aggregate_argtypes(Aggref *aggref, Oid *inputTypes)
+{
+ int numArguments = 0;
+ ListCell *lc;
+
+ /* Any direct arguments of an ordered-set aggregate come first */
+ foreach(lc, aggref->aggdirectargs)
+ {
+ Node *expr = (Node *) lfirst(lc);
+
+ inputTypes[numArguments] = exprType(expr);
+ numArguments++;
+ }
+
+ /* Now get the regular (aggregated) arguments */
+ foreach(lc, aggref->args)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(lc);
+
+ /* Ignore ordering columns of a plain aggregate */
+ if (tle->resjunk)
+ continue;
+
+ inputTypes[numArguments] = exprType((Node *) tle->expr);
+ numArguments++;
+ }
+
+ return numArguments;
+}
+
+/*
+ * resolve_aggregate_transtype
+ * Identify the transition state value's datatype for an aggregate call.
+ *
+ * This function resolves a polymorphic aggregate's state datatype.
+ * It must be passed the aggtranstype from the aggregate's catalog entry,
+ * as well as the actual argument types extracted by get_aggregate_argtypes.
+ * (We could fetch these values internally, but for all existing callers that
+ * would just duplicate work the caller has to do too, so we pass them in.)
+ */
+Oid
+resolve_aggregate_transtype(Oid aggfuncid,
+ Oid aggtranstype,
+ Oid *inputTypes,
+ int numArguments)
+{
+ /* resolve actual type of transition state, if polymorphic */
+ if (IsPolymorphicType(aggtranstype))
+ {
+ /* have to fetch the agg's declared input types... */
+ Oid *declaredArgTypes;
+ int agg_nargs;
+
+ (void) get_func_signature(aggfuncid, &declaredArgTypes, &agg_nargs);
+
+ /*
+ * VARIADIC ANY aggs could have more actual than declared args, but
+ * such extra args can't affect polymorphic type resolution.
+ */
+ Assert(agg_nargs <= numArguments);
+
+ aggtranstype = enforce_generic_type_consistency(inputTypes,
+ declaredArgTypes,
+ agg_nargs,
+ aggtranstype,
+ false);
+ pfree(declaredArgTypes);
+ }
+ return aggtranstype;
+}
+
+/*
* Create expression trees for the transition and final functions
* of an aggregate. These are needed so that polymorphic functions
* can be used within an aggregate --- without the expression trees,
@@ -956,6 +1184,9 @@ check_ungrouped_columns_walker(Node *node,
* resolved to actual types (ie, none should ever be ANYELEMENT etc).
* agg_input_collation is the aggregate function's input collation.
*
+ * For an ordered-set aggregate, remember that agg_input_types describes
+ * the direct arguments followed by the aggregated arguments.
+ *
* transfn_oid and finalfn_oid identify the funcs to be called; the latter
* may be InvalidOid.
*
@@ -965,6 +1196,8 @@ check_ungrouped_columns_walker(Node *node,
void
build_aggregate_fnexprs(Oid *agg_input_types,
int agg_num_inputs,
+ int agg_num_direct_inputs,
+ bool agg_ordered_set,
bool agg_variadic,
Oid agg_state_type,
Oid agg_result_type,
@@ -995,7 +1228,7 @@ build_aggregate_fnexprs(Oid *agg_input_types,
args = list_make1(argp);
- for (i = 0; i < agg_num_inputs; i++)
+ for (i = agg_num_direct_inputs; i < agg_num_inputs; i++)
{
argp = makeNode(Param);
argp->paramkind = PARAM_EXEC;
@@ -1035,10 +1268,26 @@ build_aggregate_fnexprs(Oid *agg_input_types,
argp->location = -1;
args = list_make1(argp);
+ if (agg_ordered_set)
+ {
+ for (i = 0; i < agg_num_inputs; i++)
+ {
+ argp = makeNode(Param);
+ argp->paramkind = PARAM_EXEC;
+ argp->paramid = -1;
+ argp->paramtype = agg_input_types[i];
+ argp->paramtypmod = -1;
+ argp->paramcollid = agg_input_collation;
+ argp->location = -1;
+ args = lappend(args, argp);
+ }
+ }
+
*finalfnexpr = (Expr *) makeFuncExpr(finalfn_oid,
agg_result_type,
args,
InvalidOid,
agg_input_collation,
COERCE_EXPLICIT_CALL);
+ /* finalfn is currently never treated as variadic */
}