commit 45f52ed92f079ca91ce34343630911ca5cb4c00e
Author: David G. Johnston <david.g.johnston@gmail.com>
Date:   Mon May 2 02:00:45 2022 +0000

    cache record input functions based on call arguments

diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index b791c23f06..5b3035c560 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2492,6 +2492,31 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
 		memcpy(hashkey->argtypes, procStruct->proargtypes.values,
 			   procStruct->pronargs * sizeof(Oid));
 
+		/*
+		 * Saved compiled functions with record-typed input args to a hashkey
+		 * that substitutes all known actual composite type OIDs in the
+		 * call signature for the corresponding generic record OID from
+		 * the definition signature.  This avoids a probable error:
+		 * "type of parameter ... does not match that when preparing the plan"
+		 * when such a record variable is used in a query within the function.
+		 */
+		for (int i = 0; i < procStruct->pronargs; i++)
+		{
+			if (hashkey->argtypes[i] != RECORDOID)
+				continue;
+
+			// ??? I presume other parts of the system synchronize these two arrays
+			// In particular for default arguments not specified in the function call
+			// or named-argument function call syntax.
+
+			/* Don't bother trying to substitute once we run out of input arguments */
+			if (i > fcinfo->nargs - 1)
+				break;
+
+			hashkey->argtypes[i] =
+				get_call_expr_argtype(fcinfo->flinfo->fn_expr, i);
+		}
+
 		/* resolve any polymorphic argument types */
 		plpgsql_resolve_polymorphic_argtypes(procStruct->pronargs,
 											 hashkey->argtypes,
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 08e42f17dc..c86930c12b 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5779,3 +5779,32 @@ END; $$ LANGUAGE plpgsql;
 ERROR:  "x" is not a scalar variable
 LINE 3:   GET DIAGNOSTICS x = ROW_COUNT;
                           ^
+CREATE OR REPLACE FUNCTION record_to_form_data(p_r record)
+ RETURNS text
+ LANGUAGE plpgsql
+AS $function$
+begin
+
+return (SELECT format('%s',p_r));
+
+end;
+$function$;
+create table fruit1(id varchar not null,name varchar not null,color varchar);
+create table fruit2(id varchar not null,name varchar not null);
+insert into fruit1 values('1','apple','red');
+insert into fruit2 values('1','apple');
+select record_to_form_data(fruit1) from fruit1;
+ record_to_form_data 
+---------------------
+ (1,apple,red)
+(1 row)
+
+select record_to_form_data(fruit2) from fruit2;
+ record_to_form_data 
+---------------------
+ (1,apple)
+(1 row)
+
+DROP FUNCTION record_to_form_data(record);
+DROP TABLE fruit1;
+DROP TABLE fruit2;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 588c331033..b02b20760c 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4711,3 +4711,28 @@ BEGIN
   GET DIAGNOSTICS x = ROW_COUNT;
   RETURN;
 END; $$ LANGUAGE plpgsql;
+
+
+CREATE OR REPLACE FUNCTION record_to_form_data(p_r record)
+ RETURNS text
+ LANGUAGE plpgsql
+AS $function$
+begin
+
+return (SELECT format('%s',p_r));
+
+end;
+$function$;
+
+create table fruit1(id varchar not null,name varchar not null,color varchar);
+create table fruit2(id varchar not null,name varchar not null);
+
+insert into fruit1 values('1','apple','red');
+insert into fruit2 values('1','apple');
+
+select record_to_form_data(fruit1) from fruit1;
+select record_to_form_data(fruit2) from fruit2;
+
+DROP FUNCTION record_to_form_data(record);
+DROP TABLE fruit1;
+DROP TABLE fruit2;
