Commit 59eb5512 authored by Robert Haas's avatar Robert Haas

Fix EXPLAIN VERBOSE output for parallel aggregate.

The way that PartialAggregate and FinalizeAggregate plan nodes were
displaying output columns before was bogus.  Now, FinalizeAggregate
produces the same outputs as an Aggregate would have produced, while
PartialAggregate produces each of those outputs prefixed by the word
PARTIAL.

Discussion: 12585.1460737650@sss.pgh.pa.us

Patch by me, reviewed by David Rowley.
parent 72a98a63
......@@ -1244,6 +1244,8 @@ _copyAggref(const Aggref *from)
COPY_NODE_FIELD(aggfilter);
COPY_SCALAR_FIELD(aggstar);
COPY_SCALAR_FIELD(aggvariadic);
COPY_SCALAR_FIELD(aggpartial);
COPY_SCALAR_FIELD(aggcombine);
COPY_SCALAR_FIELD(aggkind);
COPY_SCALAR_FIELD(agglevelsup);
COPY_LOCATION_FIELD(location);
......
......@@ -202,6 +202,8 @@ _equalAggref(const Aggref *a, const Aggref *b)
COMPARE_NODE_FIELD(aggfilter);
COMPARE_SCALAR_FIELD(aggstar);
COMPARE_SCALAR_FIELD(aggvariadic);
COMPARE_SCALAR_FIELD(aggcombine);
COMPARE_SCALAR_FIELD(aggpartial);
COMPARE_SCALAR_FIELD(aggkind);
COMPARE_SCALAR_FIELD(agglevelsup);
COMPARE_LOCATION_FIELD(location);
......
......@@ -1040,6 +1040,8 @@ _outAggref(StringInfo str, const Aggref *node)
WRITE_NODE_FIELD(aggfilter);
WRITE_BOOL_FIELD(aggstar);
WRITE_BOOL_FIELD(aggvariadic);
WRITE_BOOL_FIELD(aggcombine);
WRITE_BOOL_FIELD(aggpartial);
WRITE_CHAR_FIELD(aggkind);
WRITE_UINT_FIELD(agglevelsup);
WRITE_LOCATION_FIELD(location);
......
......@@ -556,6 +556,8 @@ _readAggref(void)
READ_NODE_FIELD(aggfilter);
READ_BOOL_FIELD(aggstar);
READ_BOOL_FIELD(aggvariadic);
READ_BOOL_FIELD(aggcombine);
READ_BOOL_FIELD(aggpartial);
READ_CHAR_FIELD(aggkind);
READ_UINT_FIELD(agglevelsup);
READ_LOCATION_FIELD(location);
......
......@@ -2100,6 +2100,10 @@ search_indexed_tlist_for_partial_aggref(Aggref *aggref, indexed_tlist *itlist,
continue;
if (aggref->aggvariadic != tlistaggref->aggvariadic)
continue;
/*
* it would be harmless to compare aggcombine and aggpartial, but
* it's also unnecessary
*/
if (aggref->aggkind != tlistaggref->aggkind)
continue;
if (aggref->agglevelsup != tlistaggref->agglevelsup)
......@@ -2463,6 +2467,7 @@ fix_combine_agg_expr_mutator(Node *node, fix_upper_expr_context *context)
newtle = makeTargetEntry((Expr *) newvar, 1, NULL, false);
newaggref = (Aggref *) copyObject(aggref);
newaggref->args = list_make1(newtle);
newaggref->aggcombine = true;
return (Node *) newaggref;
}
......
......@@ -792,6 +792,9 @@ apply_partialaggref_adjustment(PathTarget *target)
else
newaggref->aggoutputtype = aggform->aggtranstype;
/* flag it as partial */
newaggref->aggpartial = true;
lfirst(lc) = newaggref;
ReleaseSysCache(aggTuple);
......
......@@ -392,6 +392,11 @@ static void get_rule_windowspec(WindowClause *wc, List *targetList,
deparse_context *context);
static char *get_variable(Var *var, int levelsup, bool istoplevel,
deparse_context *context);
static void get_special_variable(Node *node, deparse_context *context,
void *private);
static void resolve_special_varno(Node *node, deparse_context *context,
void *private,
void (*callback) (Node *, deparse_context *, void *));
static Node *find_param_referent(Param *param, deparse_context *context,
deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
static void get_parameter(Param *param, deparse_context *context);
......@@ -407,7 +412,10 @@ static void get_rule_expr_toplevel(Node *node, deparse_context *context,
static void get_oper_expr(OpExpr *expr, deparse_context *context);
static void get_func_expr(FuncExpr *expr, deparse_context *context,
bool showimplicit);
static void get_agg_expr(Aggref *aggref, deparse_context *context);
static void get_agg_expr(Aggref *aggref, deparse_context *context,
Aggref *original_aggref);
static void get_agg_combine_expr(Node *node, deparse_context *context,
void *private);
static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
static void get_coercion_expr(Node *arg, deparse_context *context,
Oid resulttype, int32 resulttypmod,
......@@ -5877,7 +5885,6 @@ get_utility_query_def(Query *query, deparse_context *context)
}
}
/*
* Display a Var appropriately.
*
......@@ -5930,82 +5937,11 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
colinfo = deparse_columns_fetch(var->varno, dpns);
attnum = var->varattno;
}
else if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
* simple expression.
*/
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) tle->expr, context, true);
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INNER_VAR && dpns->inner_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
/*
* Force parentheses because our caller probably assumed a Var is a
* simple expression.
*/
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) tle->expr, context, true);
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INDEX_VAR && dpns->index_tlist)
{
TargetEntry *tle;
tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
/*
* Force parentheses because our caller probably assumed a Var is a
* simple expression.
*/
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) tle->expr, context, true);
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
return NULL;
}
else
{
elog(ERROR, "bogus varno: %d", var->varno);
return NULL; /* keep compiler quiet */
resolve_special_varno((Node *) var, context, NULL,
get_special_variable);
return NULL;
}
/*
......@@ -6118,6 +6054,101 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
return attname;
}
/*
* Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
* routine is actually a callback for get_special_varno, which handles finding
* the correct TargetEntry. We get the expression contained in that
* TargetEntry and just need to deparse it, a job we can throw back on
* get_rule_expr.
*/
static void
get_special_variable(Node *node, deparse_context *context, void *private)
{
StringInfo buf = context->buf;
/*
* Force parentheses because our caller probably assumed a Var is a simple
* expression.
*/
if (!IsA(node, Var))
appendStringInfoChar(buf, '(');
get_rule_expr(node, context, true);
if (!IsA(node, Var))
appendStringInfoChar(buf, ')');
}
/*
* Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
* INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
* invoke the callback provided.
*/
static void
resolve_special_varno(Node *node, deparse_context *context, void *private,
void (*callback) (Node *, deparse_context *, void *))
{
Var *var;
deparse_namespace *dpns;
/* If it's not a Var, invoke the callback. */
if (!IsA(node, Var))
{
callback(node, context, private);
return;
}
/* Find appropriate nesting depth */
var = (Var *) node;
dpns = (deparse_namespace *) list_nth(context->namespaces,
var->varlevelsup);
/*
* It's a special RTE, so recurse.
*/
if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
resolve_special_varno((Node *) tle->expr, context, private, callback);
pop_child_plan(dpns, &save_dpns);
return;
}
else if (var->varno == INNER_VAR && dpns->inner_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
resolve_special_varno((Node *) tle->expr, context, private, callback);
pop_child_plan(dpns, &save_dpns);
return;
}
else if (var->varno == INDEX_VAR && dpns->index_tlist)
{
TargetEntry *tle;
tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
resolve_special_varno((Node *) tle->expr, context, private, callback);
return;
}
else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
elog(ERROR, "bogus varno: %d", var->varno);
/* Not special. Just invoke the callback. */
callback(node, context, private);
}
/*
* Get the name of a field of an expression of composite type. The
......@@ -7080,7 +7111,7 @@ get_rule_expr(Node *node, deparse_context *context,
break;
case T_Aggref:
get_agg_expr((Aggref *) node, context);
get_agg_expr((Aggref *) node, context, (Aggref *) node);
break;
case T_GroupingFunc:
......@@ -8236,13 +8267,36 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
* get_agg_expr - Parse back an Aggref node
*/
static void
get_agg_expr(Aggref *aggref, deparse_context *context)
get_agg_expr(Aggref *aggref, deparse_context *context,
Aggref *original_aggref)
{
StringInfo buf = context->buf;
Oid argtypes[FUNC_MAX_ARGS];
int nargs;
bool use_variadic;
/*
* For a combining aggregate, we look up and deparse the corresponding
* partial aggregate instead. This is necessary because our input
* argument list has been replaced; the new argument list always has just
* one element, which will point to a partial Aggref that supplies us with
* transition states to combine.
*/
if (aggref->aggcombine)
{
TargetEntry *tle = linitial(aggref->args);
Assert(list_length(aggref->args) == 1);
Assert(IsA(tle, TargetEntry));
resolve_special_varno((Node *) tle->expr, context, original_aggref,
get_agg_combine_expr);
return;
}
/* Mark as PARTIAL, if appropriate. */
if (original_aggref->aggpartial)
appendStringInfoString(buf, "PARTIAL ");
/* Extract the argument types as seen by the parser */
nargs = get_aggregate_argtypes(aggref, argtypes);
......@@ -8311,6 +8365,24 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
appendStringInfoChar(buf, ')');
}
/*
* This is a helper function for get_agg_expr(). It's used when we deparse
* a combining Aggref; resolve_special_varno locates the corresponding partial
* Aggref and then calls this.
*/
static void
get_agg_combine_expr(Node *node, deparse_context *context, void *private)
{
Aggref *aggref;
Aggref *original_aggref = private;
if (!IsA(node, Aggref))
elog(ERROR, "combining Aggref does not point to an Aggref");
aggref = (Aggref *) node;
get_agg_expr(aggref, context, original_aggref);
}
/*
* get_windowfunc_expr - Parse back a WindowFunc node
*/
......
......@@ -280,6 +280,8 @@ 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 aggcombine; /* combining agg; input is a transvalue */
bool aggpartial; /* partial agg; output is a transvalue */
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 */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment