aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/pl/plpgsql/src/expected/plpgsql_record.out35
-rw-r--r--src/pl/plpgsql/src/pl_comp.c34
-rw-r--r--src/pl/plpgsql/src/sql/plpgsql_record.sql11
3 files changed, 76 insertions, 4 deletions
diff --git a/src/pl/plpgsql/src/expected/plpgsql_record.out b/src/pl/plpgsql/src/expected/plpgsql_record.out
index 4c5d95c79ec..afb922df29b 100644
--- a/src/pl/plpgsql/src/expected/plpgsql_record.out
+++ b/src/pl/plpgsql/src/expected/plpgsql_record.out
@@ -2,6 +2,7 @@
-- Tests for PL/pgSQL handling of composite (record) variables
--
create type two_int4s as (f1 int4, f2 int4);
+create type more_int4s as (f0 text, f1 int4, f2 int4);
create type two_int8s as (q1 int8, q2 int8);
create type nested_int8s as (c1 two_int8s, c2 two_int8s);
-- base-case return of a composite type
@@ -426,6 +427,18 @@ select getf1(row(1,2));
1
(1 row)
+select getf1(row(1,2)::two_int4s);
+ getf1
+-------
+ 1
+(1 row)
+
+select getf1(row('foo',123,456)::more_int4s);
+ getf1
+-------
+ 123
+(1 row)
+
-- the context stack is different when debug_discard_caches
-- is set, so suppress context output
\set SHOW_CONTEXT never
@@ -438,6 +451,28 @@ select getf1(row(1,2));
1
(1 row)
+-- this seemingly-equivalent case behaves a bit differently,
+-- because the core parser's handling of $N symbols is simplistic
+create function getf2(record) returns int language plpgsql as
+$$ begin return $1.f2; end $$;
+select getf2(row(1,2)); -- ideally would work, but does not
+ERROR: could not identify column "f2" in record data type
+LINE 1: $1.f2
+ ^
+QUERY: $1.f2
+CONTEXT: PL/pgSQL function getf2(record) line 1 at RETURN
+select getf2(row(1,2)::two_int4s);
+ getf2
+-------
+ 2
+(1 row)
+
+select getf2(row('foo',123,456)::more_int4s);
+ getf2
+-------
+ 456
+(1 row)
+
-- check behavior when assignment to FOR-loop variable requires coercion
do $$
declare r two_int8s;
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 61fbdf0686e..b286f2a50c1 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -2498,9 +2498,15 @@ compute_function_hashkey(FunctionCallInfo fcinfo,
/*
* This is the same as the standard resolve_polymorphic_argtypes() function,
- * but with a special case for validation: assume that polymorphic arguments
- * are integer, integer-array or integer-range. Also, we go ahead and report
- * the error if we can't resolve the types.
+ * except that:
+ * 1. We go ahead and report the error if we can't resolve the types.
+ * 2. We treat RECORD-type input arguments (not output arguments) as if
+ * they were polymorphic, replacing their types with the actual input
+ * types if we can determine those. This allows us to create a separate
+ * function cache entry for each named composite type passed to such an
+ * argument.
+ * 3. In validation mode, we have no inputs to look at, so assume that
+ * polymorphic arguments are integer, integer-array or integer-range.
*/
static void
plpgsql_resolve_polymorphic_argtypes(int numargs,
@@ -2512,6 +2518,8 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
if (!forValidator)
{
+ int inargno;
+
/* normal case, pass to standard routine */
if (!resolve_polymorphic_argtypes(numargs, argtypes, argmodes,
call_expr))
@@ -2520,10 +2528,28 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
errmsg("could not determine actual argument "
"type for polymorphic function \"%s\"",
proname)));
+ /* also, treat RECORD inputs (but not outputs) as polymorphic */
+ inargno = 0;
+ for (i = 0; i < numargs; i++)
+ {
+ char argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
+
+ if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_TABLE)
+ continue;
+ if (argtypes[i] == RECORDOID || argtypes[i] == RECORDARRAYOID)
+ {
+ Oid resolvedtype = get_call_expr_argtype(call_expr,
+ inargno);
+
+ if (OidIsValid(resolvedtype))
+ argtypes[i] = resolvedtype;
+ }
+ inargno++;
+ }
}
else
{
- /* special validation case */
+ /* special validation case (no need to do anything for RECORD) */
for (i = 0; i < numargs; i++)
{
switch (argtypes[i])
diff --git a/src/pl/plpgsql/src/sql/plpgsql_record.sql b/src/pl/plpgsql/src/sql/plpgsql_record.sql
index 535a3407a4c..db655335b1e 100644
--- a/src/pl/plpgsql/src/sql/plpgsql_record.sql
+++ b/src/pl/plpgsql/src/sql/plpgsql_record.sql
@@ -3,6 +3,7 @@
--
create type two_int4s as (f1 int4, f2 int4);
+create type more_int4s as (f0 text, f1 int4, f2 int4);
create type two_int8s as (q1 int8, q2 int8);
create type nested_int8s as (c1 two_int8s, c2 two_int8s);
@@ -257,6 +258,8 @@ create function getf1(x record) returns int language plpgsql as
$$ begin return x.f1; end $$;
select getf1(1);
select getf1(row(1,2));
+select getf1(row(1,2)::two_int4s);
+select getf1(row('foo',123,456)::more_int4s);
-- the context stack is different when debug_discard_caches
-- is set, so suppress context output
\set SHOW_CONTEXT never
@@ -264,6 +267,14 @@ select getf1(row(1,2)::two_int8s);
\set SHOW_CONTEXT errors
select getf1(row(1,2));
+-- this seemingly-equivalent case behaves a bit differently,
+-- because the core parser's handling of $N symbols is simplistic
+create function getf2(record) returns int language plpgsql as
+$$ begin return $1.f2; end $$;
+select getf2(row(1,2)); -- ideally would work, but does not
+select getf2(row(1,2)::two_int4s);
+select getf2(row('foo',123,456)::more_int4s);
+
-- check behavior when assignment to FOR-loop variable requires coercion
do $$
declare r two_int8s;