Re: pg_class.reltuples can become non-finite and never recovers

From: Jan Nidzwetzki <jan(at)planetscale(dot)com>
To: Tomas Vondra <tomas(at)vondra(dot)me>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: pg_class.reltuples can become non-finite and never recovers
Date: 2026-07-28 14:08:27
Message-ID: 4F1151D9-B4DD-49BA-B7F1-9D175EA2CDFB@planetscale.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hello Tomas,

> On 27. Jul 2026, at 14:06, Jan Nidzwetzki <jan(at)planetscale(dot)com> wrote:
>
>> On 24. Jul 2026, at 15:55, Tomas Vondra <tomas(at)vondra(dot)me> wrote:

[...]

>>>> 4. Statistics import accepts Infinity and NaN
>>>> ---------------------------------------------
>>>>
>>>> Finally, the reltuples argument of pg_restore_relation_stats() (and the
>>>> shared relation_statistics_update_internal() path) is only validated
>>>> with:
>>>>
>>>> if (reltuples < -1.0) /* relation_stats.c:126 */
>>>> {
>>>> ereport(WARNING, ...);
>>>> result = false; /* update skipped */
>>>> }
>
> [...]
>
>>>> We have not addressed this one in the attached patches, because
>>>> rejecting non-finite values here is a slightly larger policy question
>>>> (error vs. clamp-to -1). If there is agreement on the desired
>>>> behavior, we are glad to propose a patch for this as well.
>>>>
>>>
>>> OTOH this seems like something we might want to do, to validate and
>>> reject clearly bogus values.
>>>
>>
>> No opinion on this. But I was checking how we validate the values when
>> importing stats, and I noticed relation_statistics_update_internal does
>> this:
>>
>> if (reltuples < -1.0)
>> {
>> ereport(WARNING,
>> (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
>> errmsg("argument \"%s\" must not be less than -1.0",
>> "reltuples")));
>> result = false;
>> }
>>
>> Isn't that a bit strange it's not (reltuplees < 0.0)?
>
> This caught my attention as well. From my understanding, -1.0 should be
> allowed, since in 3d351d916b2 the value -1 was introduced as a sentinel
> meaning "unknown / never analyzed". But we also allow values between
> -1.0 and 0 here. I think the looseness below zero is harmless because
> every reader treats negative as "unknown".

As discussed upthread, here's the follow-up patch to validate and
reject clearly bogus reltuples at import.

Currently, the function relation_statistics_update_internal() validates
the incoming reltuples only with

if (reltuples < -1.0) { ereport(WARNING, ...); result = false; }

This means that both Infinity and NaN values pass this test since
neither is less than -1.0. As a result, non-finite values are accepted
and stored in pg_class.reltuples.

The patch rejects non-finite reltuples the same non-fatal way the
existing out-of-range check does: ereport(WARNING), set result = false,
and skip that field, so an otherwise-valid stats import (or pg_upgrade)
still proceeds and only the bad value is dropped.

It also adds a regression test to stats_import covering the rejected
values (Infinity, -Infinity, NaN) and confirming that a legitimate -1
is still accepted.

The statistics import functions are new in v18, so this applies to v18+.

Best regards
Jan

Attachment Content-Type Size
0001-Reject-non-finite-reltuples-in-the-statistics-import.patch application/octet-stream 6.7 KB
unknown_filename text/plain 54 bytes

In response to

Browse pgsql-hackers by date

  From Date Subject
Previous Message Robert Haas 2026-07-28 14:00:03 Re: log_postmaster_stats