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