The following bug has been logged on the website:
Bug reference: 19749
Logged by: Ke
Email address: kehan5800@gmail.com
PostgreSQL version: 18.6
Operating system: Ubuntu 22.04.2 x86_64
Description:
bpchar_ops registers btvarstrequalimage as btree support function 4
(src/include/catalog/pg_amproc.dat:33-35). btvarstrequalimage
(src/backend/utils/adt/varlena.c:2311) returns true for any deterministic
collation. But bpchar equality ignores trailing spaces, so for bpchar
"equal" does not imply "identical image". A bpchar column without typmod
(CREATE TABLE t (c bpchar)) stores values at their own length, so such
pairs can coexist in one index:
SELECT 'a'::bpchar = 'a '::bpchar, -- t
octet_length('a'::bpchar), octet_length('a '::bpchar); -- 1, 3
On an assert-enabled build, a plain CREATE INDEX hits the check in
_bt_keep_natts() (src/backend/access/nbtree/nbtutils.c:882-883):
CREATE TABLE t (c bpchar);
INSERT INTO t SELECT x FROM generate_series(1, 500),
(VALUES ('a'::bpchar), ('a '::bpchar)) v(x);
CREATE INDEX ti ON t (c);
TRAP: failed Assert("!itup_key->allequalimage || keepnatts ==
_bt_keep_natts_fast(rel, lastleft, firstright)"),
File: "nbtutils.c", Line: 882
LOG: client backend (PID ...) was terminated by signal 6: Aborted
The assertion is only reached on a leaf page split, so small tables do not
show it: 365 rows (730 index tuples) build fine, 370 rows abort.
This is the same assertion that interval_ops tripped in 2023
(https://www.postgresql.org/message-id/20231011013317.22.nmisch@google.com),
fixed by removing interval_ops' equalimage support function. In that
message's audit, bpchar_ops is listed under btvarstrequalimage and was
judged okay. Of the opclasses using btvarstrequalimage (bpchar_ops,
text_ops for text and name, varchar_ops via text), only bpchar has an
equality that ignores part of the value.
Impact on a non-assert build (checked on an 18.6 build without assertions):
I could not demonstrate wrong results.
amcheck bt_index_parent_check(heapallindexed, rootdescend) passes, index
and sequential scans agree, index-only scans return the heap's bytes, and
deduplication does not merge 'a' and 'a ' (_bt_dedup_pass compares images,
which differ). The demonstrated problem is a reachable Assert (developer
and buildfarm builds) plus an optimisation whose stated precondition
(equal implies image-equal) does not hold for this opclass.
Expected: bpchar_ops does not claim equalimage, or CREATE INDEX on bpchar
does not violate the nbtree invariant.
Actual: allequalimage is set for bpchar indexes, and an assert-enabled
server aborts on CREATE INDEX with equal-but-differently-padded values.
Suggested fix (the same as for interval_ops): drop the amprocnum 4 entry
for btree/bpchar_ops from pg_amproc.dat (catversion bump). As with
interval_ops, existing indexes keep the allequalimage flag in their
metapage, so a REINDEX note would be needed if back-patched. A
typmod-dependent answer is not possible, since equalimage is called per
opclass and collation, not per column.