On Wed, 14 Oct 2026 at 03:38, Tatsuya Kawata
<[email protected]> wrote:
> - I changed how the statistics are kept, so that the workers and EXPLAIN
>   use the same values.  The tuplestore returned by
>   ExecMakeTableFunctionResult() already holds the whole result set of
>   the function and doesn't grow after that, so its statistics are taken
>   right after it's returned, and used to update the per-function maximum
>   and the node-level values.  EXPLAIN just reads the leader's values, and
>   each worker copies its own into shared memory when it ends.

I don't think reporting the sum of the maximum space used by each
function is an accurate way to report the storage space used. I
believe what we should be showing is the "high water mark" of *total
storage* used per rescan, not the sum of high water marks per
function. With what you have, we could report a storage amount that
was never used by the Function Scan at any single moment in time.

To look at it another way: if you have two bank accounts with ¥1000 in
one and ¥0 in the other. If today you transfer the ¥1000 into the
account with ¥0, would you report your total net worth as the sum of
the maximum balance of the accounts on that day? Surely the answer is
¥1000 and not ¥2000, as at no point in time in this scenario did you
have ¥2000.

Effectively, the patch is reporting the ¥2000 scenario:

explain (analyze, costs off, timing off, summary off) select * from
(values(1),(2000)) v(i), lateral (select * from rows from
(generate_series(1, v.i), generate_series(2000, v.i, -1)));

 Nested Loop (actual rows=4000.00 loops=1)
   ->  Values Scan on "*VALUES*" (actual rows=2.00 loops=1)
   ->  Function Scan on generate_series (actual rows=2000.00 loops=2)
         Storage: Memory  Maximum Storage: 189kB

In each rescan there's a function with a 1-row series and the other
has a 2000-row series.  The patch is reporting the memory used by 4000
rows instead of 2001 rows.

I'd expect to see this report 112kB, effectively 17kB + 95kB per:

explain (analyze, costs off, timing off, summary off) select * from
generate_Series(1,2000);

 Function Scan on generate_series (actual rows=2000.00 loops=1)
   Storage: Memory  Maximum Storage: 95kB

explain (analyze, costs off, timing off, summary off) select * from
generate_Series(2000,2000,-1);

 Function Scan on generate_series (actual rows=1.00 loops=1)
   Storage: Memory  Maximum Storage: 17kB

David


Reply via email to