| From: | Ewan Young <kdbase(dot)hack(at)gmail(dot)com> |
|---|---|
| To: | Michael Paquier <michael(at)paquier(dot)xyz> |
| Cc: | Corey Huinker <corey(dot)huinker(at)gmail(dot)com>, PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>, Tomas Vondra <tomas(dot)vondra(at)postgresql(dot)org>, Jan Nidzwetzki <jan(at)planetscale(dot)com>, Jeff Davis <pgsql(at)j-davis(dot)com> |
| Subject: | Re: pg_restore_attribute_stats() accepts non-finite values |
| Date: | 2026-08-31 09:56:06 |
| Message-ID: | CAON2xHNe-rFvLbMcFUGDHxsazKFxJp4FPmB+WCsQfZCimhaTtA@mail.gmail.com |
| Views: | Whole Thread | Raw Message | Download mbox | Resend email |
| Thread: | |
| Lists: | pgsql-hackers |
On Mon, Aug 31, 2026 at 3:57 PM Michael Paquier <michael(at)paquier(dot)xyz> 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
| From | Date | Subject | |
|---|---|---|---|
| Next Message | Mihail Nikalayeu | 2026-08-31 10:00:49 | Re: Routed ON CONFLICT inserts broken by partition-local deferrable unique constraints in 19 and master |
| Previous Message | JoongHyuk Shin | 2026-08-31 09:49:37 | Re: [PATCH] Prevent repeated deadlock-check signals in standby buffer pin waits |