BUG #19857: Stats restore rejects empty arrays, losing range and array stats

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: 125atishprak(at)gmail(dot)com
Subject: BUG #19857: Stats restore rejects empty arrays, losing range and array stats
Date: 2026-11-09 13:15:35
Message-ID: 19857-6e808a09ff4b75d2@postgresql.org
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-bugs

The following bug has been logged on the website:

Bug reference: 19857
Logged by: Atish Prakash
Email address: 125atishprak(at)gmail(dot)com
PostgreSQL version: 18.6
Operating system: Linux x86_64
Description:

If a range column has fewer than two non-empty values, ANALYZE stores an
empty
range length histogram. Restoring those statistics fails with a WARNING, so
range_empty_frac is lost too, and estimates change a lot.

CREATE TABLE r(a int4range);
INSERT INTO r SELECT 'empty' FROM generate_series(1, 10000);
ANALYZE r;

Then dump with "pg_dump --statistics" and restore into an empty database:

master: WARNING: "range_length_histogram" must be a one-dimensional
array
18.6: WARNING: argument "range_length_histogram" must be specified
when
argument "range_empty_frac" is specified (message shortened)

Estimates on master, with autovacuum disabled on the table so that a later
ANALYZE
does not replace the statistics:

query actual before after
SELECT * FROM r WHERE a <@ '[1,2)' 10000 10000 89
SELECT * FROM r WHERE a && '[1,2)' 0 1 178

A table with one non-empty value and 9,999 empty ones is affected the same
way
(estimate 9999 before, 87 after, for the first form of query, with actual
10000).
Multirange columns also produce the warning.

pg_upgrade loses them too: after an upgrade from 18.6 to 19rc1,
range_empty_frac for
table r went from 1 to NULL, and the first query above was estimated at 50
rows.

On 19rc1 and master the same check rejects most_common_elems when it is
empty, which
commit 261f89a976 makes common. An ordinary array column with all-distinct
elements
is enough, and tsvector columns are affected too:

CREATE TABLE tags(id int, a int[]);
INSERT INTO tags
SELECT i, ARRAY[i, i + 1000000] FROM generate_series(1, 50000) i;
ANALYZE tags;

WARNING: "most_common_elems" must be a one-dimensional array

query actual before after
SELECT * FROM tags WHERE a @> '{5}' 1 3 ~270
SELECT * FROM tags WHERE a && '{5,6}' 2 7 ~530

Extended statistics are hit harder on master. An empty array in one
expression's
statistics rejects the statistics for every expression in that object,
including
unrelated ones:

WARNING: could not parse "exprs": invalid element in expression -1

Expected: the statistics restore as they were, without a warning.

Cause: on master, pg_stats shows range_length_histogram as '{}' and pg_dump
emits
that. statatt_build_stavalues() requires a one-dimensional array, and an
empty
array has zero dimensions. On 18.6, pg_stats shows it as NULL, so pg_dump
omits it
while still emitting range_empty_frac, and the restore function rejects that
pair.

I have not written a patch.

Found with dump and restore differential tests. I reproduced and checked the
result myself.

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message Jesper St John 2026-11-09 13:19:29 Re: Timing issue in pg_basebackup
Previous Message Atisss hh 2026-11-09 13:01:30 Re: BUG #19856: GiST index on tsquery misses empty tsquery rows for <@