From f5264368a05bb49b4ce0e4ff586fb5da6b1f5cc4 Mon Sep 17 00:00:00 2001
From: Matthias van de Meent <boekewurm+postgres@gmail.com>
Date: Wed, 10 Dec 2025 18:45:19 +0100
Subject: [PATCH v1] Add SQL-level datum equality tests

This enables improved performance for users that need to test for
exact bytewise differences in SQL; e.g. to see if an UPDATE is
really necessary for externally provided values.

A workaround around this limitation was possible through various
methods, but a generic method was not available for all types.
---
 src/backend/utils/adt/pseudotypes.c      | 51 ++++++++++++++++++++++++
 src/include/catalog/pg_proc.dat          |  7 ++++
 src/test/regress/expected/opr_sanity.out |  1 +
 3 files changed, 59 insertions(+)

diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c
index 317a1f2b282..216d895d2f7 100644
--- a/src/backend/utils/adt/pseudotypes.c
+++ b/src/backend/utils/adt/pseudotypes.c
@@ -23,7 +23,9 @@
 #include "postgres.h"
 
 #include "libpq/pqformat.h"
+#include "utils/datum.h"
 #include "utils/fmgrprotos.h"
+#include "utils/lsyscache.h"
 
 
 /*
@@ -375,3 +377,52 @@ PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
 PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
+
+/*
+ * Compares two datums of the same (any) type, and returns whether they have
+ * the same binary representation.
+ */
+Datum
+pg_datum_image_equal(PG_FUNCTION_ARGS)
+{
+	bool		eq;
+
+	if (PG_ARGISNULL(0) != PG_ARGISNULL(1))
+	{
+		eq = false;
+	}
+	else if (PG_ARGISNULL(0))
+	{
+		/* both NULL */
+		eq = true;
+	}
+	else
+	{
+		Oid		typ;
+		Datum	arg0;
+		Datum	arg1;
+		bool	typbyval;
+		char	typalign;
+		int16	typlen;
+
+		typ = get_fn_expr_argtype(fcinfo->flinfo, 0);
+
+		if (!OidIsValid(typ))
+		{
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("could not determine type")));
+		}
+
+		Assert(typ == get_fn_expr_argtype(fcinfo->flinfo, 1));
+
+		arg0 = PG_GETARG_DATUM(0);
+		arg1 = PG_GETARG_DATUM(1);
+
+		get_typlenbyvalalign(typ, &typlen, &typbyval, &typalign);
+
+		eq = datum_image_eq(arg0, arg1, typbyval, typlen);
+	}
+
+	PG_RETURN_BOOL(eq);
+}
diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index fd9448ec7b9..2f1b1ba8370 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -12612,4 +12612,11 @@
   proargnames => '{pid,io_id,io_generation,state,operation,off,length,target,handle_data_len,raw_result,result,target_desc,f_sync,f_localmem,f_buffered}',
   prosrc => 'pg_get_aios' },
 
+{ oid => '9200',
+  descr => 'test if two values have the same binary representation',
+  proname => 'pg_datum_image_equal', proisstrict => 'f',
+  proleakproof => 't', prorettype => 'bool',
+  proargtypes => 'anyelement anyelement',
+  prosrc => 'pg_datum_image_equal' },
+
 ]
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index a357e1d0c0e..57fe8d6ede8 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -880,6 +880,7 @@ bytea(integer)
 bytea(bigint)
 bytea_larger(bytea,bytea)
 bytea_smaller(bytea,bytea)
+pg_datum_image_equal(anyelement,anyelement)
 -- Check that functions without argument are not marked as leakproof.
 SELECT p1.oid::regprocedure
 FROM pg_proc p1 JOIN pg_namespace pn
-- 
2.50.1 (Apple Git-155)

