Thread: [HACKERS] Aggregate FILTER option is broken in v10

[HACKERS] Aggregate FILTER option is broken in v10

From
Tom Lane
Date:
Consider

regression=# select sum(1/ten) filter (where ten>0) from tenk1;
ERROR:  division by zero

This query works without error in versions before 10.  It was evidently
broken by commit 8ed3f11bb, which rearranged nodeAgg.c to evaluate all
aggregate input expressions before considering any FILTERs.

This is not an acceptable behavioral change.  This sort of thing seems
like perhaps the primary use-case for FILTER.  It's stated to work by
our own manual --- see the last sentence in
https://www.postgresql.org/docs/devel/static/sql-expressions.html#syntax-express-eval
And it's required by the SQL spec, which states clearly that the
aggregate's input expression is only evaluated at rows for which
the filter expression yields true.  (In SQL:2011, see 10.9 <aggregate
function> general rules 4 and 5a.)

I think possibly the best answer is to revert 8ed3f11bb.  We could
think about some compromise solution like merging the projections
only for aggregates without FILTER.  But that would require two
code paths through the relevant functions in nodeAgg.c, which would
be a substantial maintenance burden; and the extra branches required
means that this would be a net negative for performance in the
simplest case with only one aggregate.  In any case, since that
patch went in before the v10 expression evaluation rewrite, I think
any argument that it's worth keeping would need to be made afresh.
The overhead that it was hoping to save should be much lower now.

Thoughts?
        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

Re: [HACKERS] Aggregate FILTER option is broken in v10

From
Tom Lane
Date:
I wrote:
> I think possibly the best answer is to revert 8ed3f11bb.  We could
> think about some compromise solution like merging the projections
> only for aggregates without FILTER.  But that would require two
> code paths through the relevant functions in nodeAgg.c, which would
> be a substantial maintenance burden; and the extra branches required
> means that this would be a net negative for performance in the
> simplest case with only one aggregate.

Hmm ... on closer inspection, the only performance-critical place
where this matters is advance_aggregates, and that already has a check
for whether the particular aggregate has a filter.  So we could do
something like
       /* Skip anything FILTERed out */       if (filter)       {           // existing code to eval/check filter expr
+
+           /* Now it's safe to evaluate this agg's arguments */
+           slot = ExecProject(pertrans->argproj);       }
+       else
+           slot = aggstate->evalslot;

which seems like a pretty minimal extra cost for the normal case
with no filter.

Let me see what I can make of that ...
        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

Re: [HACKERS] Aggregate FILTER option is broken in v10

From
Andres Freund
Date:
On 2017-10-16 11:12:09 -0400, Tom Lane wrote:
> I wrote:
> > I think possibly the best answer is to revert 8ed3f11bb.  We could
> > think about some compromise solution like merging the projections
> > only for aggregates without FILTER.  But that would require two
> > code paths through the relevant functions in nodeAgg.c, which would
> > be a substantial maintenance burden; and the extra branches required
> > means that this would be a net negative for performance in the
> > simplest case with only one aggregate.
> 
> Hmm ... on closer inspection, the only performance-critical place
> where this matters is advance_aggregates, and that already has a check
> for whether the particular aggregate has a filter.  So we could do
> something like
> 
>         /* Skip anything FILTERed out */
>         if (filter)
>         {
>             // existing code to eval/check filter expr
> +
> +           /* Now it's safe to evaluate this agg's arguments */
> +           slot = ExecProject(pertrans->argproj);
>         }
> +       else
> +           slot = aggstate->evalslot;
> 
> which seems like a pretty minimal extra cost for the normal case
> with no filter.

Thanks, that looks like a reasonable fix.

Greetings,

Andres Freund


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