From f5264368a05bb49b4ce0e4ff586fb5da6b1f5cc4 Mon Sep 17 00:00:00 2001 From: Matthias van de Meent 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)