diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 4df4a9b..aeeac7b 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -4517,6 +4517,15 @@ ExecInitExpr(Expr *node, PlanState *parent) AggState *aggstate = (AggState *) parent; Aggref *aggref = (Aggref *) node; + if (aggstate->finalizeAggs == aggref->aggpartial) + { + /* planner messed up */ + if (aggref->aggpartial) + elog(ERROR, "partial Aggref found in finalized aggregate node"); + else + elog(ERROR, "non-partial Aggref found in partial aggregate node"); + } + if (aggstate->finalizeAggs && aggref->aggoutputtype != aggref->aggtype) { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index a21928b..04077ce 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1244,6 +1244,7 @@ _copyAggref(const Aggref *from) COPY_NODE_FIELD(aggfilter); COPY_SCALAR_FIELD(aggstar); COPY_SCALAR_FIELD(aggvariadic); + COPY_SCALAR_FIELD(aggpartial); COPY_SCALAR_FIELD(aggkind); COPY_SCALAR_FIELD(agglevelsup); COPY_LOCATION_FIELD(location); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 3c6c567..213cb15 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -202,6 +202,7 @@ _equalAggref(const Aggref *a, const Aggref *b) COMPARE_NODE_FIELD(aggfilter); COMPARE_SCALAR_FIELD(aggstar); COMPARE_SCALAR_FIELD(aggvariadic); + COMPARE_SCALAR_FIELD(aggpartial); COMPARE_SCALAR_FIELD(aggkind); COMPARE_SCALAR_FIELD(agglevelsup); COMPARE_LOCATION_FIELD(location); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 5ac7446..e8eaf10 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1040,6 +1040,7 @@ _outAggref(StringInfo str, const Aggref *node) WRITE_NODE_FIELD(aggfilter); WRITE_BOOL_FIELD(aggstar); WRITE_BOOL_FIELD(aggvariadic); + WRITE_BOOL_FIELD(aggpartial); WRITE_CHAR_FIELD(aggkind); WRITE_UINT_FIELD(agglevelsup); WRITE_LOCATION_FIELD(location); diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 8059594..7ff9c91 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -556,6 +556,7 @@ _readAggref(void) READ_NODE_FIELD(aggfilter); READ_BOOL_FIELD(aggstar); READ_BOOL_FIELD(aggvariadic); + READ_BOOL_FIELD(aggpartial); READ_CHAR_FIELD(aggkind); READ_UINT_FIELD(agglevelsup); READ_LOCATION_FIELD(location); diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 5537c14..a1b1db4 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2464,6 +2464,13 @@ fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context) newaggref = (Aggref *) copyObject(aggref); newaggref->args = list_make1(newtle); + /* + * A combine aggregate node does not perform FILTER, so we'd + * better remove any filter clauses so they're not shown in + * EXPLAIN. + */ + newaggref->aggfilter = NULL; + return (Node *) newaggref; } else diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 4c8c83d..c7872b6 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -785,6 +785,7 @@ apply_partialaggref_adjustment(PathTarget *target) aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); newaggref = (Aggref *) copyObject(aggref); + newaggref->aggpartial = true; /* use the serialization type, if one exists */ if (OidIsValid(aggform->aggserialtype)) diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 2b47e95..d22eb15 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -8215,6 +8215,31 @@ get_agg_expr(Aggref *aggref, deparse_context *context) /* Extract the argument types as seen by the parser */ nargs = get_aggregate_argtypes(aggref, argtypes); + /* + * We prefix partial aggregates which have final functions with the + * keyword "PARTIAL". This avoids printing confusing things like + * avg(avg(x)). We'll happily print max(max(x)), since aggregates like max + * have no final function. + */ + if (aggref->aggpartial) + { + HeapTuple aggTuple; + Form_pg_aggregate aggform; + + /* check if the aggregate has a final function */ + aggTuple = SearchSysCache1(AGGFNOID, + ObjectIdGetDatum(aggref->aggfnoid)); + if (!HeapTupleIsValid(aggTuple)) + elog(ERROR, "cache lookup failed for aggregate %u", + aggref->aggfnoid); + aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); + + if (OidIsValid(aggform->aggfinalfn)) + appendStringInfoString(buf, "PARTIAL "); + + ReleaseSysCache(aggTuple); + } + /* Print the aggregate name, schema-qualified if needed */ appendStringInfo(buf, "%s(%s", generate_function_name(aggref->aggfnoid, nargs, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 1ffc0a1..ac23cab 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -280,6 +280,7 @@ typedef struct Aggref bool aggstar; /* TRUE if argument list was really '*' */ bool aggvariadic; /* true if variadic arguments have been * combined into an array last argument */ + bool aggpartial; /* true if belongs to partial agg node */ char aggkind; /* aggregate kind (see pg_aggregate.h) */ Index agglevelsup; /* > 0 if agg belongs to outer query */ int location; /* token location, or -1 if unknown */