BUG #19833: ALTER TABLE that rebuilds an extended statistics object silently drops its SET STATISTICS target (st

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 #19833: ALTER TABLE that rebuilds an extended statistics object silently drops its SET STATISTICS target (st
Date: 2026-11-05 09:37:25
Message-ID: 19833-37de35b8f159e62a@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: 19833
Logged by: chunling qin
Email address: 303677365(at)qq(dot)com
PostgreSQL version: 18.6
Operating system: Linux x86_64 (official Docker image postgres:lates
Description:

**Description:**

`ALTER TABLE ... ALTER COLUMN TYPE` (and `ALTER COLUMN ... SET EXPRESSION`)
drops and rebuilds extended statistics objects that reference the column.
The rebuild preserves the definition (columns/expressions/kinds), the owner,
and the comment — but silently resets `stxstattarget` (the value set by
`ALTER STATISTICS ... SET STATISTICS N`) to NULL. The next ANALYZE then uses
the default target instead of the user's requested one, degrading the
statistics detail and planner estimates.

Since `pg_dump` dumps `ALTER STATISTICS ... SET STATISTICS N` as a persisted
property, dropping it on an unrelated ALTER TABLE is an inconsistency, and
the owner/comment preservation shows the rebuild path is *meant* to carry
user-visible properties across.

**Reproduction (self-contained SQL, works on the official image):**

```sql
CREATE TABLE st2 (c int, d int);
INSERT INTO st2 SELECT g, g FROM generate_series(1, 1000) g;
CREATE STATISTICS st2_s ON c, (c + 1) FROM st2;
ALTER STATISTICS st2_s SET STATISTICS 250;
ANALYZE st2;

SELECT s.stxstattarget AS target FROM pg_statistic_ext s WHERE s.stxname =
'st2_s';
-- 250

ALTER TABLE st2 ALTER COLUMN c TYPE bigint; -- rebuilds the statistics
object

SELECT s.oid, s.stxstattarget AS target,
obj_description(s.oid, 'pg_statistic_ext') AS comment
FROM pg_statistic_ext s WHERE s.stxname = 'st2_s';
-- new oid, target = NULL, comment = 'keep me' (comment and definition
survive)

-- Control: altering an UNREFERENCED column does not rebuild, target
survives:
ALTER STATISTICS st2_s SET STATISTICS 333;
ALTER TABLE st2 ALTER COLUMN d TYPE bigint;
SELECT s.stxstattarget FROM pg_statistic_ext s WHERE s.stxname = 'st2_s';
-- 333

-- Second trigger: virtual generated column SET EXPRESSION also rebuilds and
drops the target
CREATE TABLE st3 (a int, g int GENERATED ALWAYS AS (a * 2) VIRTUAL);
CREATE STATISTICS st3_s ON g, (a + g) FROM st3;
ALTER STATISTICS st3_s SET STATISTICS 400;
ALTER TABLE st3 ALTER COLUMN g SET EXPRESSION AS (a * 3);
SELECT s.stxstattarget FROM pg_statistic_ext s WHERE s.stxname = 'st3_s';
-- NULL (was 400)
```

**Root cause:** the statistics-object rebuild path in
`src/backend/commands/tablecmds.c` (`RememberStatisticsForRebuilding` and
friends) restores owner and comment but not `stxstattarget`.

**Expected behavior:** the rebuild should preserve `stxstattarget`, the same
way it preserves the owner and the comment.

Browse pgsql-bugs by date

  From Date Subject
Next Message PG Bug reporting form 2026-11-05 09:39:38 BUG #19834: eager aggregation still evaluates volatile join clauses when the volatile function is inside a SubPl
Previous Message PG Bug reporting form 2026-11-05 09:32:54 BUG #19832: convert() between BIG5 and EUC_TW returns different characters than PostgreSQL's own UTF-8 mappings