From c449e58c884f835cbdf8fe0ab7b16585bbffadcb Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Tue, 6 Oct 2026 13:32:38 -0400
Subject: [PATCH v2_REL_18_STABLE] Rescind unsafe deduplication support.

bpchar equality ignores trailing spaces, so equal values in a column
with no length can have different images.  Similarly, oidvector equality
intentionally pays no attention to the array lower bound field.  We
nevertheless allowed nbtree deduplication to be used with indexes built
with one of these opclass.  This could lead to an index-only scan that
returns the wrong value following a posting list split.

To fix, drop support function 4 from all three affected opclasses on
master and 19.  It isn't possible to change catalog contents on earlier
branches, so handle the problem there by explicitly teaching the
relevant support functions to reject the two bpchar opclasses only.  We
deliberately avoid changing anything about the oidvector opclass on
these earlier back branches, though.  The oidvector issue is benign in
practice, and it doesn't seem worth the trouble that it'd cause users
who routinely run pg_amcheck.  amcheck would otherwise flag affected
system catalog indexes (such as pg_proc_proname_args_nsp_index) as
containing a corrupt metapage, even though the underlying issue can't
cause any harm with idiomatic usage within system catalogs.

Author: Shihao Zhong <zhong950419@gmail.com>
Co-authored-by: Peter Geoghegan <pg@bowt.ie>
Reported-by: Ke <kehan5800@gmail.com>
Reported-by: Tom Lane <tgl@sss.pgh.pa.us>
Bug: #19749
Discussion: https://postgr.es/m/19749-d46adf7c7d910b07@postgresql.org
Backpatch-through: 14
---
 src/backend/utils/adt/datum.c   | 14 ++++++--------
 src/backend/utils/adt/varlena.c |  9 ++++++++-
 contrib/amcheck/verify_nbtree.c | 16 +---------------
 doc/src/sgml/btree.sgml         | 14 ++++++++++----
 4 files changed, 25 insertions(+), 28 deletions(-)

diff --git a/src/backend/utils/adt/datum.c b/src/backend/utils/adt/datum.c
index 5aab1edb7..2e906add9 100644
--- a/src/backend/utils/adt/datum.c
+++ b/src/backend/utils/adt/datum.c
@@ -43,6 +43,7 @@
 #include "postgres.h"
 
 #include "access/detoast.h"
+#include "catalog/pg_type_d.h"
 #include "common/hashfn.h"
 #include "fmgr.h"
 #include "utils/datum.h"
@@ -421,20 +422,17 @@ datum_image_hash(Datum value, bool typByVal, int typLen)
  * datum_image_eq() in all cases can use this as their "equalimage" support
  * function.
  *
- * Currently, we unconditionally assume that any B-Tree operator class that
- * registers btequalimage as its support function 4 must be able to safely use
- * optimizations like deduplication (i.e. we return true unconditionally).  If
- * it ever proved necessary to rescind support for an operator class, we could
- * do that in a targeted fashion by doing something with the opcintype
- * argument.
+ * Earlier minor releases erroneously associated this function with
+ * bpchar_pattern_ops.  Detect that case to rescind deduplication support,
+ * without requiring initdb.
  *-------------------------------------------------------------------------
  */
 Datum
 btequalimage(PG_FUNCTION_ARGS)
 {
-	/* Oid		opcintype = PG_GETARG_OID(0); */
+	Oid			opcintype = PG_GETARG_OID(0);
 
-	PG_RETURN_BOOL(true);
+	PG_RETURN_BOOL(opcintype != BPCHAROID);
 }
 
 /*-------------------------------------------------------------------------
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index 63662e3f7..51f6ab096 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2655,16 +2655,23 @@ varstr_abbrev_abort(int memtupcount, SortSupport ssup)
 /*
  * Generic equalimage support function for character type's operator classes.
  * Disables the use of deduplication with nondeterministic collations.
+ *
+ * Earlier minor releases erroneously associated this function with
+ * bpchar_ops.  Detect that case to rescind deduplication support, without
+ * requiring initdb.
  */
 Datum
 btvarstrequalimage(PG_FUNCTION_ARGS)
 {
-	/* Oid		opcintype = PG_GETARG_OID(0); */
+	Oid			opcintype = PG_GETARG_OID(0);
 	Oid			collid = PG_GET_COLLATION();
 	pg_locale_t locale;
 
 	check_collation_set(collid);
 
+	if (opcintype == BPCHAROID)
+		PG_RETURN_BOOL(false);
+
 	locale = pg_newlocale_from_collation(collid);
 
 	PG_RETURN_BOOL(locale->deterministic);
diff --git a/contrib/amcheck/verify_nbtree.c b/contrib/amcheck/verify_nbtree.c
index c7cd8d3f0..3073f39de 100644
--- a/contrib/amcheck/verify_nbtree.c
+++ b/contrib/amcheck/verify_nbtree.c
@@ -329,24 +329,10 @@ bt_index_check_callback(Relation indrel, Relation heaprel, void *state, bool rea
 				 errmsg("index \"%s\" metapage has equalimage field set on unsupported nbtree version",
 						RelationGetRelationName(indrel))));
 	if (allequalimage && !_bt_allequalimage(indrel, false))
-	{
-		bool		has_interval_ops = false;
-
-		for (int i = 0; i < IndexRelationGetNumberOfKeyAttributes(indrel); i++)
-			if (indrel->rd_opfamily[i] == INTERVAL_BTREE_FAM_OID)
-			{
-				has_interval_ops = true;
-				break;
-			}
-
 		ereport(ERROR,
 				(errcode(ERRCODE_INDEX_CORRUPTED),
 				 errmsg("index \"%s\" metapage incorrectly indicates that deduplication is safe",
-						RelationGetRelationName(indrel)),
-				 has_interval_ops
-				 ? errhint("This is known of \"interval\" indexes last built on a version predating 2023-11.")
-				 : 0));
-	}
+						RelationGetRelationName(indrel))));
 
 	/* Check index, possibly against table it is an index on */
 	bt_check_every_level(indrel, heaprel, heapkeyspace, readonly,
diff --git a/doc/src/sgml/btree.sgml b/doc/src/sgml/btree.sgml
index 027361f20..78ba106bc 100644
--- a/doc/src/sgml/btree.sgml
+++ b/doc/src/sgml/btree.sgml
@@ -875,10 +875,16 @@ options(<replaceable>relopts</replaceable> <type>local_relopts *</type>) returns
    <itemizedlist>
     <listitem>
      <para>
-      <type>text</type>, <type>varchar</type>, and <type>char</type>
-      cannot use deduplication when a
-      <emphasis>nondeterministic</emphasis> collation is used.  Case
-      and accent differences must be preserved among equal datums.
+      <type>text</type> and <type>varchar</type> cannot use deduplication when
+      a <emphasis>nondeterministic</emphasis> collation is used.  Case and
+      accent differences must be preserved among equal datums.
+     </para>
+    </listitem>
+
+    <listitem>
+     <para>
+      <type>char</type> cannot use deduplication.  Trailing spaces must be
+      preserved among equal datums.
      </para>
     </listitem>
 
-- 
2.55.0

