| 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 #19809: oidvector index dedup still returns wrong results on 18.6; CTAS persists the corrupted rows |
| Date: | 2026-10-29 06:26:24 |
| Message-ID: | 19809-c48ecb1edfc5f2d8@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: 19809
Logged by: chunling qin
Email address: 303677365(at)qq(dot)com
PostgreSQL version: 18.6
Operating system: Linux x86_64
Description:
Commit 882fd41 (2026-10-06, "Rescind unsafe deduplication support", fixes
bug #19749) removed nbtree deduplication for bpchar but deliberately left
oidvector_ops deduplication enabled on branches 14 through 18, to avoid
making pg_amcheck report a corrupt metapage for
pg_proc_proname_args_nsp_index. Its commit message reasons that "a user can
only hit it by indexing an oidvector column that stores the output of a
function like trim_array(). It seems very unlikely that any user would ever
do that."
This is a short report to (1) provide a minimal deterministic reproduction
on the official 18.6 docker image, and (2) show that the damage is not
limited to query-time wrong results: CREATE TABLE AS persists the corrupted
index images into a new table, where a later REINDEX of the original index
cannot fix them.
Background: oidvector equality intentionally ignores the array lower-bound
field, so trim_array('1 2 3 4'::oidvector, 1) (lower bound 1) and '1 2
3'::oidvector (lower bound 0) compare equal but have different byte images.
A posting-list split can attach a TID to the "equal but not identical" key,
and index-only scans then return the wrong image.
Minimal reproduction (self-contained):
CREATE TABLE ov_repro(id int, a oidvector);
INSERT INTO ov_repro
SELECT g, trim_array('1 2 3 4'::oidvector, 1) FROM generate_series(1,60) g;
-- fillfactor 10 keeps everything on one leaf page (a single posting list)
CREATE INDEX ov_repro_i ON ov_repro(a) WITH (fillfactor = 10);
VACUUM ov_repro;
-- free ctids in the middle, so later inserts reuse them
DELETE FROM ov_repro WHERE id BETWEEN 20 AND 30;
VACUUM ov_repro;
-- 5 rows storing the other image; they reuse the freed ctids
INSERT INTO ov_repro SELECT 1000 + g, '1 2 3'::oidvector FROM
generate_series(1,5) g;
VACUUM ANALYZE ov_repro;
-- symmetry breaker: remove the image-A rows again; the heap is now all
image B
DELETE FROM ov_repro WHERE id > 1000;
VACUUM ANALYZE ov_repro;
-- heap truth: all 49 surviving rows are image B (a[1] = 1)
SET enable_indexonlyscan = off; SET enable_indexscan = off; SET
enable_bitmapscan = off;
SELECT a[1], count(*) FROM ov_repro WHERE a = '1 2 3'::oidvector GROUP BY 1;
-> 1 | 49
-- the same rows through an index-only scan
SET enable_seqscan = off; SET enable_indexonlyscan = on; SET
enable_indexscan = on;
SELECT a[1], count(*) FROM ov_repro WHERE a = '1 2 3'::oidvector GROUP BY 1;
-> 1 | 44
2 | 5 <- wrong image for 5 rows; the heap stores no such row
SELECT count(*) FROM ov_repro WHERE a = '1 2 3'::oidvector AND a[1] = 2;
-> 5 <- must be 0: no surviving heap row satisfies this
This also reproduces with fully default settings at a larger scale
(20000-row table, plain CREATE INDEX, ordinary DELETE/INSERT/VACUUM churn):
10000 surviving rows, heap all image B, index-only scan reports 2498 rows
with the wrong image.
Persistence beyond the query: a CREATE TABLE AS over such a table copies the
index-provided values, so the wrong images become permanent data. REINDEX on
the original index afterwards repairs the index, but the copied table keeps
the corrupted rows (verified end-to-end with array_lower(a,1) as the
discriminator: source table 0 rows with lower bound 0 after REINDEX, the
CTAS copy still has them).
Given that the exposure is silent wrong results plus permanently corrupted
data via CTAS, it may be worth revisiting the tradeoff made in 882fd41 -
e.g. a backpatchable fix that does not trip pg_amcheck's metapage check, or
at least documenting the hazard for users who index oidvector columns.
| From | Date | Subject | |
|---|---|---|---|
| Next Message | PG Bug reporting form | 2026-10-29 06:32:46 | BUG #19810: to_char(): float V-format overflow yields 'Infinity' or NaN; format after decimal point dropped |
| Previous Message | PG Bug reporting form | 2026-10-29 06:20:55 | BUG #19808: NOT NULL NO INHERIT copied by CREATE TABLE (LIKE) not enforced on partitioned table |