From ae41b4413b3214123c1d89abfe3ac01ce6fd8bd0 Mon Sep 17 00:00:00 2001
From: Jeevan Chalke <jeevan.chalke@enterprisedb.com>
Date: Mon, 26 Oct 2026 16:19:48 +0530
Subject: [PATCH v1] Fix PL/pgSQL validation of anycompatiblemultirange.

When validating a PL/pgSQL function with polymorphic arguments or
result type, we substitute a plausible concrete type for each
polymorphic type.  anycompatiblemultirange was missed in both places
that do this, when multiranges were added.  As a result, CREATE
FUNCTION with an anycompatiblemultirange argument failed with
"PL/pgSQL functions cannot accept type anycompatiblemultirange" when
check_function_bodies is on.

Also, a function returning anycompatiblemultirange was compiled by
the validator with an int4 result type, and since the function cache
key doesn't include the result type, a later call in the same session
with int4range arguments reused that entry and failed trying to cast
the multirange result to integer.

Map anycompatiblemultirange to int4multirange, as is done for
anymultirange, to fix both issues.
---
 src/backend/utils/cache/funccache.c   |  1 +
 src/pl/plpgsql/src/pl_comp.c          |  3 ++-
 src/test/regress/expected/plpgsql.out | 24 ++++++++++++++++++++++++
 src/test/regress/sql/plpgsql.sql      | 20 ++++++++++++++++++++
 4 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/src/backend/utils/cache/funccache.c b/src/backend/utils/cache/funccache.c
index 64f7b51ac41..b3093ae5940 100644
--- a/src/backend/utils/cache/funccache.c
+++ b/src/backend/utils/cache/funccache.c
@@ -420,6 +420,7 @@ cfunc_resolve_polymorphic_argtypes(int numargs,
 					argtypes[i] = INT4RANGEOID;
 					break;
 				case ANYMULTIRANGEOID:
+				case ANYCOMPATIBLEMULTIRANGEOID:
 					argtypes[i] = INT4MULTIRANGEOID;
 					break;
 				default:
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 75184b75707..180a20f868a 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -410,7 +410,8 @@ plpgsql_compile_callback(FunctionCallInfo fcinfo,
 					else if (rettypeid == ANYRANGEOID ||
 							 rettypeid == ANYCOMPATIBLERANGEOID)
 						rettypeid = INT4RANGEOID;
-					else if (rettypeid == ANYMULTIRANGEOID)
+					else if (rettypeid == ANYMULTIRANGEOID ||
+							 rettypeid == ANYCOMPATIBLEMULTIRANGEOID)
 						rettypeid = INT4MULTIRANGEOID;
 					else		/* ANYELEMENT or ANYNONARRAY or ANYCOMPATIBLE */
 						rettypeid = INT4OID;
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index b37b2abaf80..78a84bd0336 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1870,6 +1870,30 @@ select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[
 (1 row)
 
 drop function f1(x anycompatiblerange, y anycompatiblearray);
+create function f1(x anycompatiblemultirange) returns anycompatiblemultirange as $$
+begin
+  return x;
+end$$ language plpgsql;
+select f1(int4multirange(int4range(42, 49))) as int, f1(nummultirange(numrange(4.5, 7.8))) as num;
+    int    |     num     
+-----------+-------------
+ {[42,49)} | {[4.5,7.8)}
+(1 row)
+
+drop function f1(x anycompatiblemultirange);
+-- validation must not compile an anycompatiblemultirange result as int4,
+-- else a later call with a different range type would reuse that function
+create function f1(x anycompatiblerange) returns anycompatiblemultirange as $$
+begin
+  return multirange(x);
+end$$ language plpgsql;
+select f1(int4range(42, 49)) as int, f1(numrange(4.5, 7.8)) as num;
+    int    |     num     
+-----------+-------------
+ {[42,49)} | {[4.5,7.8)}
+(1 row)
+
+drop function f1(x anycompatiblerange);
 create function f1(a anyelement, b anyarray,
                    c anycompatible, d anycompatible,
                    OUT x anyarray, OUT y anycompatiblearray)
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index ae6b67e3e22..4f85aade1a8 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -1653,6 +1653,26 @@ select f1(int4range(42, 49), array[11]) as int, f1(float8range(4.5, 7.8), array[
 
 drop function f1(x anycompatiblerange, y anycompatiblearray);
 
+create function f1(x anycompatiblemultirange) returns anycompatiblemultirange as $$
+begin
+  return x;
+end$$ language plpgsql;
+
+select f1(int4multirange(int4range(42, 49))) as int, f1(nummultirange(numrange(4.5, 7.8))) as num;
+
+drop function f1(x anycompatiblemultirange);
+
+-- validation must not compile an anycompatiblemultirange result as int4,
+-- else a later call with a different range type would reuse that function
+create function f1(x anycompatiblerange) returns anycompatiblemultirange as $$
+begin
+  return multirange(x);
+end$$ language plpgsql;
+
+select f1(int4range(42, 49)) as int, f1(numrange(4.5, 7.8)) as num;
+
+drop function f1(x anycompatiblerange);
+
 create function f1(a anyelement, b anyarray,
                    c anycompatible, d anycompatible,
                    OUT x anyarray, OUT y anycompatiblearray)
-- 
2.43.0

