EXPLAIN VERBOSE with parallel Aggregate

Started by David Rowleyabout 10 years ago30 messageshackers
Jump to latest
#1David Rowley
dgrowleyml@gmail.com

There's 2 problems:

1)

I recently noticed that EXPLAIN VERBOSE is a bit bogus when it comes
to parallel aggregates with FILTER (WHERE ...) clauses.

We get;

Output: pg_catalog.sum((sum(num) FILTER (WHERE (num > 0)))) FILTER
(WHERE (num > 0))

Which is simply a lie, we only filter on the partial aggregate, not the combine.

The attached patch just nullifies the combine aggregate's aggfilter
clause during setrefs. We cannot nullify it any sooner, as the
aggfilter is required so that we find the correct partial Aggref in
search_indexed_tlist_for_partial_aggref().

With the attached we get;

Output: pg_catalog.sum((sum(num) FILTER (WHERE (num > 0))))

The patch is very simple.

2)

I'm unsure if the schema prefix on the combine aggregate is a problem
or not. The code path which generates it is rather unfortunate and is
down to func_get_detail() returning FUNCDETAIL_NOTFOUND in
generate_function_name() due to not being able to find a function
named "sum" with the transtype as its only argument. I had thought
that maybe we should add a pg_proc entry for the aggregate with the
transtype, if not already covered by the entry for aggfnoid.
Aggregates where the transtype is the same as the input type work just
fine;

Output: max((max(num)))

The problem with that is adding the pg_proc entry still won't get rid
of the schema as the "p_funcid == funcid" test in
generate_function_name() will fail causing the schema qualification to
occur again. But at least func_get_detail() would be successful in
finding the function.

Any one have any thoughts on if this is a problem?

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

parallel_agg_filter_fix.patchapplication/octet-stream; name=parallel_agg_filter_fix.patchDownload+7-0
#2Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#1)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Wed, Apr 13, 2016 at 11:03 PM, David Rowley
<david.rowley@2ndquadrant.com> wrote:

There's 2 problems:

1)

I recently noticed that EXPLAIN VERBOSE is a bit bogus when it comes
to parallel aggregates with FILTER (WHERE ...) clauses.

We get;

Output: pg_catalog.sum((sum(num) FILTER (WHERE (num > 0)))) FILTER
(WHERE (num > 0))

Which is simply a lie, we only filter on the partial aggregate, not the combine.

The attached patch just nullifies the combine aggregate's aggfilter
clause during setrefs. We cannot nullify it any sooner, as the
aggfilter is required so that we find the correct partial Aggref in
search_indexed_tlist_for_partial_aggref().

With the attached we get;

Output: pg_catalog.sum((sum(num) FILTER (WHERE (num > 0))))

The patch is very simple.

2)

I'm unsure if the schema prefix on the combine aggregate is a problem
or not. The code path which generates it is rather unfortunate and is
down to func_get_detail() returning FUNCDETAIL_NOTFOUND in
generate_function_name() due to not being able to find a function
named "sum" with the transtype as its only argument. I had thought
that maybe we should add a pg_proc entry for the aggregate with the
transtype, if not already covered by the entry for aggfnoid.
Aggregates where the transtype is the same as the input type work just
fine;

Output: max((max(num)))

The problem with that is adding the pg_proc entry still won't get rid
of the schema as the "p_funcid == funcid" test in
generate_function_name() will fail causing the schema qualification to
occur again. But at least func_get_detail() would be successful in
finding the function.

Any one have any thoughts on if this is a problem?

I definitely agree that the current output is messed up, but I'm not
sure your proposed output is much better. I wonder if it shouldn't
say something like:

Output: serialfn(transfn(args))

for the partial aggregate and

Output: finalfn(combinefn(deserialfn(args)))

for the finalize aggregate step.

Or maybe just insert the word PARTIAL before each partial aggregate
step, like PARTIAL sum(num) for the partial step and then just
sum(num) for the final step. I think ending up with sum(sum(num)) is
right out. It doesn't look so bad for that case but avg(avg(num))
would certainly imply something that's not the actual behavior.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#2)
Re: EXPLAIN VERBOSE with parallel Aggregate

Robert Haas <robertmhaas@gmail.com> writes:

I definitely agree that the current output is messed up, but I'm not
sure your proposed output is much better. I wonder if it shouldn't
say something like:
Output: serialfn(transfn(args))
for the partial aggregate and
Output: finalfn(combinefn(deserialfn(args)))
for the finalize aggregate step.

Or maybe just insert the word PARTIAL before each partial aggregate
step, like PARTIAL sum(num) for the partial step and then just
sum(num) for the final step.

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

I think ending up with sum(sum(num)) is
right out. It doesn't look so bad for that case but avg(avg(num))
would certainly imply something that's not the actual behavior.

Agreed.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#4David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#3)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 16 April 2016 at 04:27, Tom Lane <tgl@sss.pgh.pa.us> wrote:

Robert Haas <robertmhaas@gmail.com> writes:

I definitely agree that the current output is messed up, but I'm not
sure your proposed output is much better. I wonder if it shouldn't
say something like:
Output: serialfn(transfn(args))
for the partial aggregate and
Output: finalfn(combinefn(deserialfn(args)))
for the finalize aggregate step.

Or maybe just insert the word PARTIAL before each partial aggregate
step, like PARTIAL sum(num) for the partial step and then just
sum(num) for the final step.

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

It does not really seem all that convenient to do this. It also seems
a bit strange to me to have a parent node report a column which does
not exist in any nodes descending from it. Remember that the combine
Aggref does not have the same ->args as its corresponding partial
Aggref. It's not all that clear to me if there is any nice way to do
have this work the way you'd like. If we were to just call
get_rule_expr() on the first arg of the combine aggregate node, it
would re-print the PARTIAL keyword again.

In the attached I have it displaying as:

postgres=# explain verbose select count(*),max(a),avg(a) filter (where
a = 0) from t;
QUERY PLAN
-------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=14739.56..14739.57 rows=1 width=44)
Output: pg_catalog.count(*), max((max(a))), pg_catalog.avg((PARTIAL
avg(a) FILTER (WHERE (a = 0))))
-> Gather (cost=14739.33..14739.54 rows=2 width=44)
Output: (count(*)), (max(a)), (PARTIAL avg(a) FILTER (WHERE (a = 0)))
Workers Planned: 2
-> Partial Aggregate (cost=13739.33..13739.34 rows=1 width=44)
Output: count(*), max(a), PARTIAL avg(a) FILTER (WHERE (a = 0))
-> Parallel Seq Scan on public.t (cost=0.00..9572.67
rows=416667 width=4)
Output: a
(9 rows)

I like this much better, as there's no fudging of any function
arguments to print them as something they're not.

Note that max() does not get tagged with PARTIAL since it has no
finalfn. Obtaining this information does require a syscache lookup in
get_agg_expr(), but I've managed to make that only happen when
aggpartial is true.

I think ending up with sum(sum(num)) is
right out. It doesn't look so bad for that case but avg(avg(num))
would certainly imply something that's not the actual behavior.

Agreed.

Note that I've done nothing for the weird schema prefixing problem I
mentioned. I think I'd need input on the best way to solve this. If
it's actually a problem.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

parallel_agg_explain_verbose_v2.patchapplication/octet-stream; name=parallel_agg_explain_verbose_v2.patchDownload+47-0
#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#4)
Re: EXPLAIN VERBOSE with parallel Aggregate

David Rowley <david.rowley@2ndquadrant.com> writes:

On 16 April 2016 at 04:27, Tom Lane <tgl@sss.pgh.pa.us> wrote:

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

It does not really seem all that convenient to do this. It also seems
a bit strange to me to have a parent node report a column which does
not exist in any nodes descending from it. Remember that the combine
Aggref does not have the same ->args as its corresponding partial
Aggref. It's not all that clear to me if there is any nice way to do
have this work the way you'd like. If we were to just call
get_rule_expr() on the first arg of the combine aggregate node, it
would re-print the PARTIAL keyword again.

This suggests to me that the parsetree representation for partial
aggregates was badly chosen. If EXPLAIN can't recognize them, then
neither can anything else, and we may soon find needs for telling
the difference that are not just cosmetic. Maybe we need more
fields in Aggref.

Note that I've done nothing for the weird schema prefixing problem I
mentioned. I think I'd need input on the best way to solve this. If
it's actually a problem.

It sure looks like a problem to me. Maybe it's only cosmetic, but
it's going to cause confusion and bug reports. EXPLAIN output for
parallel queries is going to be confusing enough anyway; we need
to avoid having artifacts like this.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#6Noah Misch
noah@leadboat.com
In reply to: Tom Lane (#5)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Sun, Apr 17, 2016 at 10:22:24AM -0400, Tom Lane wrote:

David Rowley <david.rowley@2ndquadrant.com> writes:

On 16 April 2016 at 04:27, Tom Lane <tgl@sss.pgh.pa.us> wrote:

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

It does not really seem all that convenient to do this. It also seems
a bit strange to me to have a parent node report a column which does
not exist in any nodes descending from it. Remember that the combine
Aggref does not have the same ->args as its corresponding partial
Aggref. It's not all that clear to me if there is any nice way to do
have this work the way you'd like. If we were to just call
get_rule_expr() on the first arg of the combine aggregate node, it
would re-print the PARTIAL keyword again.

This suggests to me that the parsetree representation for partial
aggregates was badly chosen. If EXPLAIN can't recognize them, then
neither can anything else, and we may soon find needs for telling
the difference that are not just cosmetic. Maybe we need more
fields in Aggref.

[This is a generic notification.]

The above-described topic is currently a PostgreSQL 9.6 open item. Robert,
since you committed the patch believed to have created it, you own this open
item. If that responsibility lies elsewhere, please let us know whose
responsibility it is to fix this. Since new open items may be discovered at
any time and I want to plan to have them all fixed well in advance of the ship
date, I will appreciate your efforts toward speedy resolution. Please
present, within 72 hours, a plan to fix the defect within seven days of this
message. Thanks.

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#7Robert Haas
robertmhaas@gmail.com
In reply to: Noah Misch (#6)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Tue, Apr 19, 2016 at 9:22 PM, Noah Misch <noah@leadboat.com> wrote:

On Sun, Apr 17, 2016 at 10:22:24AM -0400, Tom Lane wrote:

David Rowley <david.rowley@2ndquadrant.com> writes:

On 16 April 2016 at 04:27, Tom Lane <tgl@sss.pgh.pa.us> wrote:

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

It does not really seem all that convenient to do this. It also seems
a bit strange to me to have a parent node report a column which does
not exist in any nodes descending from it. Remember that the combine
Aggref does not have the same ->args as its corresponding partial
Aggref. It's not all that clear to me if there is any nice way to do
have this work the way you'd like. If we were to just call
get_rule_expr() on the first arg of the combine aggregate node, it
would re-print the PARTIAL keyword again.

This suggests to me that the parsetree representation for partial
aggregates was badly chosen. If EXPLAIN can't recognize them, then
neither can anything else, and we may soon find needs for telling
the difference that are not just cosmetic. Maybe we need more
fields in Aggref.

[This is a generic notification.]

The above-described topic is currently a PostgreSQL 9.6 open item. Robert,
since you committed the patch believed to have created it, you own this open
item. If that responsibility lies elsewhere, please let us know whose
responsibility it is to fix this. Since new open items may be discovered at
any time and I want to plan to have them all fixed well in advance of the ship
date, I will appreciate your efforts toward speedy resolution. Please
present, within 72 hours, a plan to fix the defect within seven days of this
message. Thanks.

I'll do my best to work on this soon. I'm not happy with the output
produced by David's patch, but I don't expect I'll be able to do
better without putting some time into it.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#8David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#5)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 18 April 2016 at 02:22, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <david.rowley@2ndquadrant.com> writes:

Note that I've done nothing for the weird schema prefixing problem I
mentioned. I think I'd need input on the best way to solve this. If
it's actually a problem.

It sure looks like a problem to me. Maybe it's only cosmetic, but
it's going to cause confusion and bug reports. EXPLAIN output for
parallel queries is going to be confusing enough anyway; we need
to avoid having artifacts like this.

I'd like to admit that I'm a bit confused as to why
generate_function_name(), when it already knows the funcid, bothers to
call func_get_detail(), which performs a search for the function based
on the name and argument types, to find the function, most likely with
the same funcid as the one which it already knew.

Could you explain why this has to happen?

I also tried patching with the attached and running the regression
tests to see what breaks... nothing did. So it seems that, at least in
the tests that that code path is never hit.

With that in mind, perhaps the fix for the namespace problem is just
to special case the combine Aggrefs in get_agg_expr() and have it just
lookup the pg_proc entry for the aggred->aggfnoid, and use that
proname.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

generate_function_name_experiment.patchapplication/octet-stream; name=generate_function_name_experiment.patchDownload+5-0
#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: David Rowley (#8)
Re: EXPLAIN VERBOSE with parallel Aggregate

David Rowley <david.rowley@2ndquadrant.com> writes:

I'd like to admit that I'm a bit confused as to why
generate_function_name(), when it already knows the funcid, bothers to
call func_get_detail(), which performs a search for the function based
on the name and argument types, to find the function, most likely with
the same funcid as the one which it already knew.

Could you explain why this has to happen?

The point is exactly to find out whether a search for the function
given only the name and argument types would find the same function,
or a similar function in a different schema --- which would happen
if that other schema is earlier in the search path than the desired
one, or maybe the desired one isn't in search_path at all. In such
a case we must schema-qualify the function name in the printed
expression.

To some extent this is because ruleutils serves two masters. We would
probably not care that much about schema exactness for EXPLAIN's purposes,
but we do care for dumping views and rules.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#10David Rowley
dgrowleyml@gmail.com
In reply to: Tom Lane (#9)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 21 April 2016 at 17:14, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <david.rowley@2ndquadrant.com> writes:

I'd like to admit that I'm a bit confused as to why
generate_function_name(), when it already knows the funcid, bothers to
call func_get_detail(), which performs a search for the function based
on the name and argument types, to find the function, most likely with
the same funcid as the one which it already knew.

Could you explain why this has to happen?

The point is exactly to find out whether a search for the function
given only the name and argument types would find the same function,
or a similar function in a different schema --- which would happen
if that other schema is earlier in the search path than the desired
one, or maybe the desired one isn't in search_path at all. In such
a case we must schema-qualify the function name in the printed
expression.

Thanks. That makes more sense now.

To some extent this is because ruleutils serves two masters. We would
probably not care that much about schema exactness for EXPLAIN's purposes,
but we do care for dumping views and rules.

OK, so here's my thoughts. Currently, as mentioned above, I've
included a PARTIAL prefix for partial aggregates. This is
syntactically incorrect for the dumping of views etc, but that should
not matter as partial Aggrefs never come from the parser, they're only
perhaps generated later in the planner. Same goes for combine
aggregates too.

In the attached I'm proposing that we simply just use the
pg_proc.proname which belongs to the aggref->aggfnoid for combine
aggregates. This gets around the function not being found by
generate_function_name() and the namespace problem, that code should
never be executed when getting a view def, since we can't have combine
aggs there.

The attached still does not get the output into the way Robert would
have liked, but I still stand by my dislike to pretending the combine
aggregate is a normal aggregate. It's not all that clear if FILTER
should be displayed in the combine agg. Combine Aggs don't do FILTER.

This makes the output:

postgres=# explain verbose select avg(num) FILTER (WHERE num >
0),sum(num),count(*) from i;
QUERY PLAN
-------------------------------------------------------------------------------------------
Finalize Aggregate (cost=13758.56..13758.57 rows=1 width=48)
Output: avg((PARTIAL avg(num) FILTER (WHERE (num > 0)))),
sum((sum(num))), count(*)
-> Gather (cost=13758.33..13758.54 rows=2 width=48)
Output: (PARTIAL avg(num) FILTER (WHERE (num > 0))),
(sum(num)), (count(*))
Workers Planned: 2
-> Partial Aggregate (cost=12758.33..12758.34 rows=1 width=48)
Output: PARTIAL avg(num) FILTER (WHERE (num > 0)),
sum(num), count(*)
-> Parallel Seq Scan on public.i (cost=0.00..8591.67
rows=416667 width=4)
Output: num

Comments welcome.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

parallel_agg_explain_verbose_v3.patchapplication/octet-stream; name=parallel_agg_explain_verbose_v3.patchDownload+98-8
#11Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#10)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Thu, Apr 21, 2016 at 8:57 PM, David Rowley
<david.rowley@2ndquadrant.com> wrote:

OK, so here's my thoughts. Currently, as mentioned above, I've
included a PARTIAL prefix for partial aggregates. This is
syntactically incorrect for the dumping of views etc, but that should
not matter as partial Aggrefs never come from the parser, they're only
perhaps generated later in the planner. Same goes for combine
aggregates too.

Yeah. As I've said a few times, I would like to have SQL syntax that
emits the unfinalized (but serialized, if type internal) values, so
that postgres_fdw can use that to partial aggregation to the remote
side. Maybe we should consider what would be reasonable syntax for
this; but that's a second-order consideration for now.

The attached still does not get the output into the way Robert would
have liked, but I still stand by my dislike to pretending the combine
aggregate is a normal aggregate. It's not all that clear if FILTER
should be displayed in the combine agg. Combine Aggs don't do FILTER.

I am rather confused by this, for two reasons:

1. The "Output" line is supposed to display the columns that the plan
node is producing. And a FinalizeAggregate had darned well better be
producing the same results as an Aggregate, so it's entirely
reasonable for it to produce the same output that an Aggregate would
have given us.

2. You're using the term "combine agg", but as far as the EXPLAIN
ANALYZE output is concerned, that's not a thing. There is
PartialAggregate, Aggregate, and FinalizeAggregate. I think you mean
FinalizeAggregate when you say "combine aggregate", since we identify
a node as a FinalizeAggregate by observing that combineStates = true.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#12Robert Haas
robertmhaas@gmail.com
In reply to: Tom Lane (#5)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Sun, Apr 17, 2016 at 10:22 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:

David Rowley <david.rowley@2ndquadrant.com> writes:

On 16 April 2016 at 04:27, Tom Lane <tgl@sss.pgh.pa.us> wrote:

+1 for the latter, if we can do it conveniently. I think exposing
the names of the aggregate implementation functions would be very
user-unfriendly, as nobody but us hackers knows what those are.

It does not really seem all that convenient to do this. It also seems
a bit strange to me to have a parent node report a column which does
not exist in any nodes descending from it. Remember that the combine
Aggref does not have the same ->args as its corresponding partial
Aggref. It's not all that clear to me if there is any nice way to do
have this work the way you'd like. If we were to just call
get_rule_expr() on the first arg of the combine aggregate node, it
would re-print the PARTIAL keyword again.

This suggests to me that the parsetree representation for partial
aggregates was badly chosen. If EXPLAIN can't recognize them, then
neither can anything else, and we may soon find needs for telling
the difference that are not just cosmetic. Maybe we need more
fields in Aggref.

So the basic problem is this code in setrefs.c:

newvar = search_indexed_tlist_for_partial_aggref(aggref,
context->subplan_itlist,
context->newvarno);
if (newvar)
{
Aggref *newaggref;
TargetEntry *newtle;

/*
* Now build a new TargetEntry for the Aggref's arguments which is
* a single Var which references the corresponding AggRef in the
* node below.
*/
newtle = makeTargetEntry((Expr *) newvar, 1, NULL, false);
newaggref = (Aggref *) copyObject(aggref);
newaggref->args = list_make1(newtle);

So what ends up happening is that, before setrefs processing, the
FinalizeAggregate's aggref has the same argument list as what the user
originally specified, but during that process, we replace the original
argument list with a 1-element argument list that points to the output
of the partial aggregate step. After that, it's unsurprising that the
deparsing logic goes wrong; consider especially the case of an
aggregate that originally took any number of arguments other than one.
Offhand, I see two somewhat reasonable strategies for trying to fix
this:

1. Add a field "List *origargs" to Aggref, and in this case set
newaggref->origargs to a copy of the original argument list. Use
origargs for deparsing, unless it's NULL, in which case use args. Or
alternative, always populate origargs, but in other cases just make it
equal to args.

2. Add a field "bool aggcombine" to args, and set it to true in this
case. When we see that in deparsing, expect the argument list to be
one element long, a TargetEntry containing a Var. Use that to dig out
the partial Aggref to which it points, and deparse that instead. I
guess maybe get_variable() could be used for this purpose.

There might be another approach, too. Thoughts?

(Note that I'm assuming here that the final aggregate's target list
output should match what we would have gotten from a regular
aggregate, despite the combining stage in the middle. I think that's
correct; we're outputting the same thing, even though we computed it
differently.)

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#13David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#11)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 23 April 2016 at 06:35, Robert Haas <robertmhaas@gmail.com> wrote:

2. You're using the term "combine agg", but as far as the EXPLAIN
ANALYZE output is concerned, that's not a thing. There is
PartialAggregate, Aggregate, and FinalizeAggregate. I think you mean
FinalizeAggregate when you say "combine aggregate", since we identify
a node as a FinalizeAggregate by observing that combineStates = true.

I really do mean combine when I say combine. I'm pretty sure that I've
coded everything properly to work even if there was 10 stages of
aggregation, in this case 9 of those would be combine nodes, and only
1 of them a finalize node. I can see why you'd say that at a glace of
explain.c, but it's more relevant that agg->finalizeAggs is true,
giving the combine test is in the else if condition. The test for
combineStates is only there so we don't output Finalize Aggregate for
normal 1 stage aggregates.

I simply don't think FILTER should be shown if combineStates is true.
This means that it will only be shown at the first aggregate node, the
one with combineStates == false. I think this is a good idea due to
the fact that this is the only place where FILTERing is done.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#14David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#12)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 23 April 2016 at 09:19, Robert Haas <robertmhaas@gmail.com> wrote:

2. Add a field "bool aggcombine" to args, and set it to true in this
case. When we see that in deparsing, expect the argument list to be
one element long, a TargetEntry containing a Var. Use that to dig out
the partial Aggref to which it points, and deparse that instead. I
guess maybe get_variable() could be used for this purpose.

I did do that at one stage. I think perhaps one of the above patches
did it that way. The problem was that because I was just detecting
combine Aggrefs and printing the first item in args, it meant the
PARTIAL word was printed again, and that was pretty bogus, since it
was not a partial agg. I didn't see a way to do this without adding
some extra bool parameter to that whole series of functions. But I
only looked at using get_rule_expr(). I didn't look at what
get_variable() is.

There might be another approach, too. Thoughts?

(Note that I'm assuming here that the final aggregate's target list
output should match what we would have gotten from a regular
aggregate, despite the combining stage in the middle. I think that's
correct; we're outputting the same thing, even though we computed it
differently.)

Please note that in this case the final and combine node are the same
node, so I'm confused by the "combining stage in the middle" part.
There's only 2 aggregate nodes. I'm not sure how one of those is in
the middle.

I really don't think that we should print FILTER details in a combine
aggregate node. We'd be claiming to be doing something that we're
actually not doing. Please see advance_aggregates() in nodeAgg.c, and
compare that to combine_aggregates(), which is used when combineStates
== true. Notice that only advance_aggregates() bothers with the
aggfilter clause.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#15Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#14)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Fri, Apr 22, 2016 at 5:36 PM, David Rowley
<david.rowley@2ndquadrant.com> wrote:

I really don't think that we should print FILTER details in a combine
aggregate node. We'd be claiming to be doing something that we're
actually not doing. Please see advance_aggregates() in nodeAgg.c, and
compare that to combine_aggregates(), which is used when combineStates
== true. Notice that only advance_aggregates() bothers with the
aggfilter clause.

I think you're wrong. The Output line says what that node outputs,
not how it's computed. And a FinalizeAggregate on top of a
PartialAggregate produces the same output as an Aggregate.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#16David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#15)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 23 April 2016 at 13:58, Robert Haas <robertmhaas@gmail.com> wrote:

On Fri, Apr 22, 2016 at 5:36 PM, David Rowley
<david.rowley@2ndquadrant.com> wrote:

I really don't think that we should print FILTER details in a combine
aggregate node. We'd be claiming to be doing something that we're
actually not doing. Please see advance_aggregates() in nodeAgg.c, and
compare that to combine_aggregates(), which is used when combineStates
== true. Notice that only advance_aggregates() bothers with the
aggfilter clause.

I think you're wrong. The Output line says what that node outputs,
not how it's computed. And a FinalizeAggregate on top of a
PartialAggregate produces the same output as an Aggregate.

The most basic thing I can think of to rationalise my thinking for this is:

# create table v (v varchar);
# create view v_v as select lower(v) v from v;
# explain verbose select upper(v) from v_v;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on public.v (cost=0.00..30.40 rows=1360 width=32)
Output: upper(lower((v.v)::text))
(2 rows)

It seems that you're proposing that the aggregate equivalence of this should be;

QUERY PLAN
-------------------------------------------------------------
Seq Scan on public.v (cost=0.00..30.40 rows=1360 width=32)
Output: upper((v)::text)
(2 rows)

which to me seems wrong, as it's hiding the fact that lower() was called.

My arguments don't seem to be holding much weight, so I'll back down,
although it would still be interesting to hear what others have to say
about this.

In any case, thanks for stepping up to fix this. I'm sure what you're
proposing will be much better than what's there at the moment.

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#17Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#16)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Fri, Apr 22, 2016 at 10:11 PM, David Rowley
<david.rowley@2ndquadrant.com> wrote:

The most basic thing I can think of to rationalise my thinking for this is:

# create table v (v varchar);
# create view v_v as select lower(v) v from v;
# explain verbose select upper(v) from v_v;
QUERY PLAN
-------------------------------------------------------------
Seq Scan on public.v (cost=0.00..30.40 rows=1360 width=32)
Output: upper(lower((v.v)::text))
(2 rows)

It seems that you're proposing that the aggregate equivalence of this should be;

QUERY PLAN
-------------------------------------------------------------
Seq Scan on public.v (cost=0.00..30.40 rows=1360 width=32)
Output: upper((v)::text)
(2 rows)

which to me seems wrong, as it's hiding the fact that lower() was called.

My arguments don't seem to be holding much weight, so I'll back down,
although it would still be interesting to hear what others have to say
about this.

Yeah, I'd be happy to have more people chime in. I think your example
is interesting, but it doesn't persuade me, because the rule has
always been that EXPLAIN shows the output *columns*, not the output
*rows*. The disappearance of some rows doesn't change the list of
output columns. For scans and joins this rule is easy to apply; for
aggregates, where many rows become one, less so. Some of the existing
choices there are certainly arguable, like the fact that FILTER is
shown anywhere at all, which seems like an artifact to me. But I
think that now is not the time to rethink those decisions.

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#18Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#17)
Re: EXPLAIN VERBOSE with parallel Aggregate

On Mon, Apr 25, 2016 at 1:03 PM, Robert Haas <robertmhaas@gmail.com> wrote:

Yeah, I'd be happy to have more people chime in. I think your example
is interesting, but it doesn't persuade me, because the rule has
always been that EXPLAIN shows the output *columns*, not the output
*rows*. The disappearance of some rows doesn't change the list of
output columns. For scans and joins this rule is easy to apply; for
aggregates, where many rows become one, less so. Some of the existing
choices there are certainly arguable, like the fact that FILTER is
shown anywhere at all, which seems like an artifact to me. But I
think that now is not the time to rethink those decisions.

My proposed fix for this issue is attached. Review is welcome;
otherwise, I'll just commit this. The output looks like what I
suggested upthread:

rhaas=# explain verbose select count(*), corr(aid, bid) from pgbench_accounts ;
QUERY PLAN
--------------------------------------------------------------------------------------------------------------
Finalize Aggregate (cost=742804.22..742804.23 rows=1 width=16)
Output: count(*), corr((aid)::double precision, (bid)::double precision)
-> Gather (cost=742804.00..742804.21 rows=2 width=40)
Output: (PARTIAL count(*)), (PARTIAL corr((aid)::double
precision, (bid)::double precision))
Workers Planned: 2
-> Partial Aggregate (cost=741804.00..741804.01 rows=1 width=40)
Output: PARTIAL count(*), PARTIAL corr((aid)::double
precision, (bid)::double precision)
-> Parallel Seq Scan on public.pgbench_accounts
(cost=0.00..616804.00 rows=12500000 width=8)
Output: aid, bid, abalance, filler
(9 rows)

--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company

Attachments:

parallel-aggregate-explain.patchapplication/x-patch; name=parallel-aggregate-explain.patchDownload+166-78
#19Tom Lane
tgl@sss.pgh.pa.us
In reply to: Robert Haas (#18)
Re: EXPLAIN VERBOSE with parallel Aggregate

Robert Haas <robertmhaas@gmail.com> writes:

My proposed fix for this issue is attached. Review is welcome;
otherwise, I'll just commit this. The output looks like what I
suggested upthread:

I haven't read the patch, but the sample output looks sane.

regards, tom lane

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

#20David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#18)
Re: EXPLAIN VERBOSE with parallel Aggregate

On 27 April 2016 at 08:46, Robert Haas <robertmhaas@gmail.com> wrote:

My proposed fix for this issue is attached. Review is welcome;
otherwise, I'll just commit this. The output looks like what I
suggested upthread:

+ if (!aggref->aggpartial)
+ elog(ERROR, "referenced Aggref is not partial");

I think this is overly restrictive; ruleutils seems a bit out of line
here to say that plans can't have > 1 combine aggregate node.

When coding the combine aggs stuff I had test code to inject
additional combine paths to make sure everything worked as expected.
It did, but it won't if you add these two lines. I'd say just remove
them.

If you apply the attached and execute;

create table t1 (num int not null);
insert into t1 select generate_series(1,2000000);

explain verbose select avg(num) from t1;

You'll get;

ERROR: referenced Aggref is not partial

--
David Rowley http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services

Attachments:

extra_combine_node.patchapplication/octet-stream; name=extra_combine_node.patchDownload+17-1
#21Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#20)
#22David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#21)
#23Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#22)
#24Robert Haas
robertmhaas@gmail.com
In reply to: Robert Haas (#23)
#25David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#24)
#26Andres Freund
andres@anarazel.de
In reply to: David Rowley (#25)
#27Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#25)
#28David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#27)
#29Robert Haas
robertmhaas@gmail.com
In reply to: David Rowley (#28)
#30David Rowley
dgrowleyml@gmail.com
In reply to: Robert Haas (#29)