2015-10-19 9:52 GMT+02:00 Pavel Stehule <pavel.steh...@gmail.com>:

> Hi,
>
> We cannot to declare variable with referenced type on other composite
> variable. This limit is probably artificial, because any composite type is
> any type too in PostgreSQL.
>
> The issue:
>
> referencing on composite variables doesn't work
>
> do $$ declare x int; y x%type; begin end; $$; -- ok
> do $$ declare x pg_class; y x%type; begin end; $$; -- invalid type name
> "x%type"
> do $$ declare x pg_class; y x%rowtype; begin end; $$; -- relation "x" does
> not exist
>
> The %ROWTYPE needs a record in pg_class. Probably we should not to change
> it. The change can bring a compatibility issues. So there are two
> possibilities:
>
> 1. %TYPE can be used for any kind of variables. This behave will be
> consistent with polymorphic parameters - we have "anyelement", and we have
> not "anyrow".
>
> 2. introduce new keyword - %RECTYPE .. it can work, but there will be gap
> between polymorphic parameters.
>
> Comments, notices?
>
>
Hi

I am sending patch that enables to use references to polymorphic parameters
of row types. Another functionality is possibility to get array or element
type of referenced variable. It removes some gaps when polymorphic
parameters are used.

 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)

Regards

Pavel



> Regards
>
> Pavel
>
>
>
commit 76d258edf9ef8e9645f47645a18d79f0d4245d41
Author: Pavel Stehule <pavel.steh...@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);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to