From ae41b4413b3214123c1d89abfe3ac01ce6fd8bd0 Mon Sep 17 00:00:00 2001 From: Jeevan Chalke 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