BUG #19749: bpchar_ops declares equalimage although bpchar equality ignores trailing spaces

From: PG Bug reporting form <noreply(at)postgresql(dot)org>
To: pgsql-bugs(at)lists(dot)postgresql(dot)org
Cc: kehan5800(at)gmail(dot)com
Subject: BUG #19749: bpchar_ops declares equalimage although bpchar equality ignores trailing spaces
Date: 2026-10-04 05:20:01
Message-ID: 19749-d46adf7c7d910b07@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: 19749
Logged by: Ke
Email address: kehan5800(at)gmail(dot)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.

Responses

Browse pgsql-bugs by date

  From Date Subject
Next Message shihao zhong 2026-10-04 05:23:29 Re: BUG #19742: `INTERSECT` under a `UNION ALL` with an empty arm fails with "could not find pathkey item t"
Previous Message PG Bug reporting form 2026-10-04 05:19:27 BUG #19748: tsvectorrecv accepts duplicate lexemes and position 0 that tsvectorin rejects