commit 76d258edf9ef8e9645f47645a18d79f0d4245d41
Author: Pavel Stehule <pavel.stehule@gooddata.com>
Date:   Fri Oct 30 11:48:33 2015 +0100

    enhancing referenced types - possibility to get array or element type of referenced variable type.
    row variables and row values are supported now too.

diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 1ae4bb7..333d2bc 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -1617,6 +1617,62 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
 	return false;
 }
 
+/*
+ * Derive type from ny base type controlled by reftype_mode
+ *
+ */
+static PLpgSQL_type *
+derive_type(PLpgSQL_type *base_type, int reftype_mode)
+{
+	Oid typoid;
+
+	switch (reftype_mode)
+	{
+		case PLPGSQL_REFTYPE_TYPE:
+			return base_type;
+
+		case PLPGSQL_REFTYPE_ELEMENT:
+		{
+			typoid = get_element_type(base_type->typoid);
+			if (!OidIsValid(typoid))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("referenced variable should be an array, not type %s",
+								format_type_be(base_type->typoid))));
+
+			return plpgsql_build_datatype(typoid, -1,
+							plpgsql_curr_compile->fn_input_collation);
+		}
+
+		case PLPGSQL_REFTYPE_ARRAY:
+		{
+			/*
+			 * Question: can we allow anyelement (array or nonarray) -> array direction.
+			 * if yes, then probably we have to modify enforce_generic_type_consistency,
+			 * parse_coerce.c where still is check on scalar type -> raise error
+			 * ERROR:  42704: could not find array type for data type integer[]
+			 *
+			if (OidIsValid(get_element_type(base_type->typoid)))
+				return base_type;
+			*/
+
+			typoid = get_array_type(base_type->typoid);
+			if (!OidIsValid(typoid))
+				ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("there are not array type for type %s",
+									format_type_be(base_type->typoid))));
+
+			return plpgsql_build_datatype(typoid, -1,
+							plpgsql_curr_compile->fn_input_collation);
+		}
+
+		default:
+			return NULL;
+	}
+}
+
+
 
 /* ----------
  * plpgsql_parse_wordtype	The scanner found word%TYPE. word can be
@@ -1626,7 +1682,7 @@ plpgsql_parse_tripword(char *word1, char *word2, char *word3,
  * ----------
  */
 PLpgSQL_type *
-plpgsql_parse_wordtype(char *ident)
+plpgsql_parse_wordtype(char *ident, int reftype_mode)
 {
 	PLpgSQL_type *dtype;
 	PLpgSQL_nsitem *nse;
@@ -1644,10 +1700,23 @@ plpgsql_parse_wordtype(char *ident)
 		switch (nse->itemtype)
 		{
 			case PLPGSQL_NSTYPE_VAR:
-				return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+			{
+				dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+				return derive_type(dtype, reftype_mode);
+			}
 
-				/* XXX perhaps allow REC/ROW here? */
+			case PLPGSQL_NSTYPE_ROW:
+			{
+				dtype = ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
+				return derive_type(dtype, reftype_mode);
+			}
 
+			/*
+			 * XXX perhaps allow REC here? Probably it has not any sense, because
+			 * in this moment, because PLpgSQL doesn't support rec parameters, so
+			 * there should not be any rec polymorphic parameter, and any work can
+			 * be done inside function.
+			 */
 			default:
 				return NULL;
 		}
@@ -1689,7 +1758,7 @@ plpgsql_parse_wordtype(char *ident)
  * ----------
  */
 PLpgSQL_type *
-plpgsql_parse_cwordtype(List *idents)
+plpgsql_parse_cwordtype(List *idents, int reftype_mode)
 {
 	PLpgSQL_type *dtype = NULL;
 	PLpgSQL_nsitem *nse;
@@ -1718,10 +1787,24 @@ plpgsql_parse_cwordtype(List *idents)
 								NULL,
 								NULL);
 
-		if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
+		if (nse != NULL)
 		{
-			dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
-			goto done;
+			PLpgSQL_type *ref_type;
+
+			if (nse->itemtype == PLPGSQL_NSTYPE_VAR)
+			{
+				ref_type = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
+				dtype = derive_type(ref_type, reftype_mode);
+
+				goto done;
+			}
+			else if (nse->itemtype == PLPGSQL_NSTYPE_ROW)
+			{
+				ref_type = ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
+				dtype = derive_type(ref_type, reftype_mode);
+
+				goto done;
+			}
 		}
 
 		/*
@@ -1903,6 +1986,7 @@ plpgsql_build_variable(const char *refname, int lineno, PLpgSQL_type *dtype,
 				row->dtype = PLPGSQL_DTYPE_ROW;
 				row->refname = pstrdup(refname);
 				row->lineno = lineno;
+				row->datatype = dtype;
 
 				plpgsql_adddatum((PLpgSQL_datum *) row);
 				if (add2namespace)
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 841a8d6..a9f87d7 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -248,6 +248,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 %token <keyword>	K_ALIAS
 %token <keyword>	K_ALL
 %token <keyword>	K_ARRAY
+%token <keyword>	K_ARRAYTYPE
 %token <keyword>	K_ASSERT
 %token <keyword>	K_BACKWARD
 %token <keyword>	K_BEGIN
@@ -270,6 +271,7 @@ static	void			check_raise_parameters(PLpgSQL_stmt_raise *stmt);
 %token <keyword>	K_DETAIL
 %token <keyword>	K_DIAGNOSTICS
 %token <keyword>	K_DUMP
+%token <keyword>	K_ELEMENTTYPE
 %token <keyword>	K_ELSE
 %token <keyword>	K_ELSIF
 %token <keyword>	K_END
@@ -2390,6 +2392,7 @@ unreserved_keyword	:
 				K_ABSOLUTE
 				| K_ALIAS
 				| K_ARRAY
+				| K_ARRAYTYPE
 				| K_ASSERT
 				| K_BACKWARD
 				| K_CLOSE
@@ -2408,6 +2411,7 @@ unreserved_keyword	:
 				| K_DETAIL
 				| K_DIAGNOSTICS
 				| K_DUMP
+				| K_ELEMENTTYPE
 				| K_ELSIF
 				| K_ERRCODE
 				| K_ERROR
@@ -2703,8 +2707,8 @@ read_datatype(int tok)
 	startlocation = yylloc;
 
 	/*
-	 * If we have a simple or composite identifier, check for %TYPE
-	 * and %ROWTYPE constructs.
+	 * If we have a simple or composite identifier, check for %TYPE,
+	 * %ELEMENTTYPE, %ARRAYTYPE and %ROWTYPE constructs.
 	 */
 	if (tok == T_WORD)
 	{
@@ -2717,7 +2721,21 @@ read_datatype(int tok)
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 			{
-				result = plpgsql_parse_wordtype(dtname);
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_TYPE);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ELEMENTTYPE, "elementtype"))
+			{
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_ELEMENT);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ARRAYTYPE, "arraytype"))
+			{
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_ARRAY);
 				if (result)
 					return result;
 			}
@@ -2741,7 +2759,21 @@ read_datatype(int tok)
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 			{
-				result = plpgsql_parse_wordtype(dtname);
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_TYPE);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ELEMENTTYPE, "elementtype"))
+			{
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_ELEMENT);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ARRAYTYPE, "arraytype"))
+			{
+				result = plpgsql_parse_wordtype(dtname, PLPGSQL_REFTYPE_ARRAY);
 				if (result)
 					return result;
 			}
@@ -2765,7 +2797,21 @@ read_datatype(int tok)
 			if (tok_is_keyword(tok, &yylval,
 							   K_TYPE, "type"))
 			{
-				result = plpgsql_parse_cwordtype(dtnames);
+				result = plpgsql_parse_cwordtype(dtnames, PLPGSQL_REFTYPE_TYPE);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ELEMENTTYPE, "elementtype"))
+			{
+				result = plpgsql_parse_cwordtype(dtnames, PLPGSQL_REFTYPE_ELEMENT);
+				if (result)
+					return result;
+			}
+			if (tok_is_keyword(tok, &yylval,
+							   K_ARRAYTYPE, "arraytype"))
+			{
+				result = plpgsql_parse_cwordtype(dtnames, PLPGSQL_REFTYPE_ARRAY);
 				if (result)
 					return result;
 			}
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
index 683fdab..6e34605 100644
--- a/src/pl/plpgsql/src/pl_scanner.c
+++ b/src/pl/plpgsql/src/pl_scanner.c
@@ -98,6 +98,7 @@ static const ScanKeyword unreserved_keywords[] = {
 	PG_KEYWORD("absolute", K_ABSOLUTE, UNRESERVED_KEYWORD)
 	PG_KEYWORD("alias", K_ALIAS, UNRESERVED_KEYWORD)
 	PG_KEYWORD("array", K_ARRAY, UNRESERVED_KEYWORD)
+	PG_KEYWORD("arraytype", K_ARRAYTYPE, UNRESERVED_KEYWORD)
 	PG_KEYWORD("assert", K_ASSERT, UNRESERVED_KEYWORD)
 	PG_KEYWORD("backward", K_BACKWARD, UNRESERVED_KEYWORD)
 	PG_KEYWORD("close", K_CLOSE, UNRESERVED_KEYWORD)
@@ -116,6 +117,7 @@ static const ScanKeyword unreserved_keywords[] = {
 	PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
 	PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD)
 	PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
+	PG_KEYWORD("elementtype", K_ELEMENTTYPE, UNRESERVED_KEYWORD)
 	PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD)
 	PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD)
 	PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 696fb61..5b1a074 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -84,6 +84,17 @@ enum
 };
 
 /* ----------
+ * Possible modes for type references
+ * ----------
+ */
+enum
+{
+	PLPGSQL_REFTYPE_TYPE,		/* use type of some variable */
+	PLPGSQL_REFTYPE_ELEMENT,	/* use a element type of referenced variable */
+	PLPGSQL_REFTYPE_ARRAY		/* use a array type of referenced variable */
+};
+
+/* ----------
  * Execution tree node types
  * ----------
  */
@@ -281,6 +292,7 @@ typedef struct
 	char	   *refname;
 	int			lineno;
 
+	PLpgSQL_type *datatype;
 	TupleDesc	rowtupdesc;
 
 	/*
@@ -961,8 +973,8 @@ extern bool plpgsql_parse_dblword(char *word1, char *word2,
 					  PLwdatum *wdatum, PLcword *cword);
 extern bool plpgsql_parse_tripword(char *word1, char *word2, char *word3,
 					   PLwdatum *wdatum, PLcword *cword);
-extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
-extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
+extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident, int reftype_mode);
+extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents, int reftype_mode);
 extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
 extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
 extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index e30c579..b6e848b 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -5573,3 +5573,95 @@ end;
 $$;
 ERROR:  unhandled assertion
 CONTEXT:  PL/pgSQL function inline_code_block line 3 at ASSERT
+-- test referenced types
+create type test_composite_type as (x int, y int);
+create or replace function test_simple(src anyelement)
+returns anyelement as $$
+declare dest src%type;
+begin
+  dest := src;
+  return dest;
+end;
+$$ language plpgsql;
+select test_simple(10);
+ test_simple 
+-------------
+          10
+(1 row)
+
+select test_simple('hoj'::text);
+ test_simple 
+-------------
+ hoj
+(1 row)
+
+select test_simple((10,20)::test_composite_type);
+ test_simple 
+-------------
+ (10,20)
+(1 row)
+
+create or replace function test_poly_element(x anyelement)
+returns anyarray as $$
+declare result x%arraytype;
+begin
+  result := ARRAY[x];
+  raise notice '% %', pg_typeof(result), result;
+  return result;
+end;
+$$ language plpgsql;
+select test_poly_element(1);
+NOTICE:  integer[] {1}
+ test_poly_element 
+-------------------
+ {1}
+(1 row)
+
+select test_poly_element('hoj'::text);
+NOTICE:  text[] {hoj}
+ test_poly_element 
+-------------------
+ {hoj}
+(1 row)
+
+select test_poly_element((10,20)::test_composite_type);
+NOTICE:  test_composite_type[] {"(10,20)"}
+ test_poly_element 
+-------------------
+ {"(10,20)"}
+(1 row)
+
+create or replace function test_poly_array(x anyarray)
+returns anyelement as $$
+declare result x%elementtype;
+begin
+  result := x[1];
+  raise notice '% %', pg_typeof(result), result;
+  return result;
+end;
+$$ language plpgsql;
+select test_poly_array(ARRAY[1]);
+NOTICE:  integer 1
+ test_poly_array 
+-----------------
+               1
+(1 row)
+
+select test_poly_array(ARRAY['hoj'::text]);
+NOTICE:  text hoj
+ test_poly_array 
+-----------------
+ hoj
+(1 row)
+
+select test_poly_array(ARRAY[(10,20)::test_composite_type]);
+NOTICE:  test_composite_type (10,20)
+ test_poly_array 
+-----------------
+ (10,20)
+(1 row)
+
+drop function test_simple(anyelement);
+drop type test_composite_type;
+drop function test_poly_element(anyelement);
+drop function test_poly_array(anyarray);
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 7ffef89..46bad10 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -4386,3 +4386,52 @@ exception when others then
   null; -- do nothing
 end;
 $$;
+
+-- test referenced types
+create type test_composite_type as (x int, y int);
+
+create or replace function test_simple(src anyelement)
+returns anyelement as $$
+declare dest src%type;
+begin
+  dest := src;
+  return dest;
+end;
+$$ language plpgsql;
+
+select test_simple(10);
+select test_simple('hoj'::text);
+select test_simple((10,20)::test_composite_type);
+
+create or replace function test_poly_element(x anyelement)
+returns anyarray as $$
+declare result x%arraytype;
+begin
+  result := ARRAY[x];
+  raise notice '% %', pg_typeof(result), result;
+  return result;
+end;
+$$ language plpgsql;
+
+select test_poly_element(1);
+select test_poly_element('hoj'::text);
+select test_poly_element((10,20)::test_composite_type);
+
+create or replace function test_poly_array(x anyarray)
+returns anyelement as $$
+declare result x%elementtype;
+begin
+  result := x[1];
+  raise notice '% %', pg_typeof(result), result;
+  return result;
+end;
+$$ language plpgsql;
+
+select test_poly_array(ARRAY[1]);
+select test_poly_array(ARRAY['hoj'::text]);
+select test_poly_array(ARRAY[(10,20)::test_composite_type]);
+
+drop function test_simple(anyelement);
+drop type test_composite_type;
+drop function test_poly_element(anyelement);
+drop function test_poly_array(anyarray);
