| From: | PG Bug reporting form <noreply(at)postgresql(dot)org> |
|---|---|
| To: | pgsql-bugs(at)lists(dot)postgresql(dot)org |
| Cc: | 303677365(at)qq(dot)com |
| Subject: | BUG #19814: pg_restore_attribute_stats: MCV accepted without eq operator; ANALYZE range slot rejected |
| Date: | 2026-10-29 07:00:28 |
| Message-ID: | 19814-5005fc464c4ef1ea@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: 19814
Logged by: chunling qin
Email address: 303677365(at)qq(dot)com
PostgreSQL version: 18.6
Operating system: Linux x86_64
Description:
Two defects in pg_restore_attribute_stats(), in opposite directions: one
check is missing (invalid statistics are accepted), one check is too strict
(statistics that ANALYZE itself produces are rejected). Both reproduce on
the official postgres:latest docker image (18.6).
1. MCV statistics are accepted for types with no default equality operator
CREATE TABLE jt (j json, p point) WITH (autovacuum_enabled = false);
INSERT INTO jt SELECT ('{"k":'||(g%5)||'}')::json, point(g%5,g%5) FROM
generate_series(1,300) g;
ANALYZE jt;
ANALYZE produces no statistics slot for these columns (stakind1 = 0 - no
default btree/hash opclass). Restoring MCV values is nevertheless accepted:
SELECT pg_restore_attribute_stats(
'schemaname', current_schema()::text, 'relname', 'jt', 'attname', 'j',
'inherited', false,
'most_common_vals', '{"1","2"}'::text, 'most_common_freqs',
'{0.4,0.4}'::real[]);
-> true (no warning)
The result is a STATISTIC_KIND_MCV slot (stakind1 = 1) with staop1 = 0
(InvalidOid) - a state ANALYZE can never produce - and pg_stats now reports
most_common_vals for the column.
The sibling checks in the same function behave correctly on the same column:
histogram_bounds -> WARNING: could not determine less-than operator
... -> false
most_common_elems -> WARNING: could not determine element type ...
-> false
Only the MCV path lacks the equality-operator (eq_opr) guard.
2. The range_empty_frac-only slot that ANALYZE produces cannot be restored
CREATE TABLE w1 (r int4range) WITH (autovacuum_enabled = false);
INSERT INTO w1 SELECT 'empty'::int4range FROM generate_series(1,20000)
g;
ANALYZE w1;
ANALYZE produces a STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM slot with
stavalues1 = NULL (no length histogram; the sample has fewer than two
non-empty ranges) and stanumbers1 = {1} (the empty fraction):
SELECT stakind1, stavalues1 IS NULL AS vals_null, stanumbers1
FROM pg_statistic WHERE starelid = 'w1'::regclass;
-> stakind1 = 6, vals_null = t, stanumbers1 = {1}
pg_restore_attribute_stats() requires range_length_histogram to be specified
whenever range_empty_frac is, so restoring exactly the values ANALYZE
produced fails:
SELECT pg_restore_attribute_stats(
'schemaname', current_schema()::text, 'relname', 'w1', 'attname', 'r',
'inherited', false,
'null_frac', <the analyzed value>, 'avg_width', <...>, 'n_distinct',
<...>,
'range_empty_frac', <the analyzed value>);
WARNING: argument "range_length_histogram" must be specified when
argument "range_empty_frac" is specified
-> false
and the statistics row ends up empty - the slot is lost. Restoring the same
row without the range arguments succeeds (returns true), confirming that the
range pairing rule is what rejects it. The export/restore round trip
therefore loses statistics that ANALYZE itself computed - for example in the
pre-ANALYZE step of an upgrade, where the target has no statistics row yet.
Note: when passing these arguments, use values that are already of type real
(e.g. carried through a table or a subquery); untyped numeric literals raise
unrelated "has type numeric, expected type real" warnings.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | John Naylor | 2026-10-29 08:37:37 | spg_quad_inner_consistent() doesn't check all four box corners |
| Previous Message | PG Bug reporting form | 2026-10-29 06:51:13 | BUG #19813: jsonpath: datetime errors bypass silent/ON ERROR; boolean() inconsistent; PASSING rejects bpchar/nam |