On Mon, Aug 31, 2026 at 3:57 PM Michael Paquier <[email protected]> wrote:
>
> On Thu, Aug 27, 2026 at 04:09:21PM +0800, Ewan Young wrote:
> > Thanks for the thorough review, and for the history -- that context on why
> > the checks were removed is helpful.
>
> Question: do we get elog(ERROR) problems, assertion failures or
> backend breakages when we insert these values or is the backend OK
> with them?
No hard breakage. On an assertion-enabled build of master I injected
NaN and +/-Infinity through every unchecked argument (null_frac,
n_distinct, correlation, most_common_freqs, most_common_elem_freqs,
elem_count_histogram) and ran queries exercising each stat slot
(IS NULL, =, IN, GROUP BY, equijoins, array @>/<@, ORDER BY + LIMIT
over an index): no assertion failures, no elog(ERROR), no crashes.
And since only estimates are affected, query results stay correct.
What the values do poison is the cost model, in two distinct ways:
1. NaN probabilities sail through CLAMP_PROBABILITY (both of its
comparisons are false for NaN); the NaN selectivity then hits
clamp_row_est(), whose isnan() guard turns it into
MAXIMUM_ROWCOUNT. With null_frac = NaN:
Seq Scan on tf (cost=0.00..20.00 rows=1e100 width=47)
Filter: (a IS NULL)
(rows is printed as the full 101-digit integer), and every join or
aggregate above such a scan now plans against 1e100 rows. The
same happens for = / IN / join selectivity when most_common_freqs
contains NaN. +/-Infinity is tamer here, since Inf > 1.0 is true
and CLAMP_PROBABILITY catches it.
2. NaN correlation flows into the index-scan cost arithmetic
unclamped, producing paths whose cost is literally NaN:
Index Scan using tf_a_idx on tf (cost=0.28..NaN rows=889 ...)
Every comparison involving a NaN cost is false, so path cost
comparisons degenerate and the chosen plan is essentially
arbitrary. A NaN also propagates up through the whole plan tree
(Limit/GroupAggregate above it print cost=..NaN too).
There is no self-healing: the values sit in pg_statistic until some
later ANALYZE happens to overwrite them.
So the damage class is the same as the reltuples case fixed by
7cb9060dcde: nothing crashes, but it's stored garbage the planner has
no defense against, and rejecting it at import time seems much
cheaper than teaching every consumer of pg_statistic to cope with
non-finite inputs.
> --
> Michael
--
Regards,
Ewan Young