Hi

2016-02-24 22:18 GMT+01:00 Peter Eisentraut <pete...@gmx.net>:

> On 1/18/16 4:21 PM, Robert Haas wrote:
> > One idea that occurs to me is: If you can DECLARE BAR FOO%TYPE, but
> > then you want to make BAR an array of that type rather than a scalar,
> > why not write that as DECLARE BAR FOO%TYPE[]?  That seems quite
> > natural to me.
>
> Right, and it's arguably dubious that that doesn't already work.
> Unfortunately, these % things are just random plpgsql parser hacks, not
> real types.  Maybe this should be done in the main PostgreSQL parser
> with parameter hooks, if we wanted this feature to be available outside
> plpgsql as well.
>
> > I think the part of this patch that makes %TYPE work for more kinds of
> > types is probably a good idea, although I haven't carefully studied
> > exactly what it does.
>
> I agree that this should be more general.  For instance, this patch
> would allow you to get the element type of an array-typed variable, but
> there is no way to get the element type of just another type.  If we
> could do something like
>
> DECLARE
>   var ELEMENT OF point;
>
> (not necessary that syntax)
>
> then
>
> DECLARE
>   var ELEMENT OF othervar%TYPE;
>
> should just fall into place.
>
>
I am sending update of this patch. The basic concept is same, syntax was
changed per your and Robert requirement.

Regards

Pavel
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
new file mode 100644
index 9786242..5587839
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
*************** url varchar;
*** 322,334 ****
  myrow tablename%ROWTYPE;
  myfield tablename.columnname%TYPE;
  arow RECORD;
  </programlisting>
      </para>
  
      <para>
       The general syntax of a variable declaration is:
  <synopsis>
! <replaceable>name</replaceable> <optional> CONSTANT </optional> <replaceable>type</replaceable> <optional> COLLATE <replaceable>collation_name</replaceable> </optional> <optional> NOT NULL </optional> <optional> { DEFAULT | := | = } <replaceable>expression</replaceable> </optional>;
  </synopsis>
        The <literal>DEFAULT</> clause, if given, specifies the initial value assigned
        to the variable when the block is entered.  If the <literal>DEFAULT</> clause
--- 322,336 ----
  myrow tablename%ROWTYPE;
  myfield tablename.columnname%TYPE;
  arow RECORD;
+ myarray tablename.columnname%TYPE[];
+ myelement ELEMENT OF arrayparam%TYPE;
  </programlisting>
      </para>
  
      <para>
       The general syntax of a variable declaration is:
  <synopsis>
! <replaceable>name</replaceable> <optional> CONSTANT </optional> <optional> ELEMENT OF </optional> <replaceable>type</replaceable> <optional> COLLATE <replaceable>collation_name</replaceable> </optional> <optional> NOT NULL </optional> <optional> { DEFAULT | := | = } <replaceable>expression</replaceable> </optional>;
  </synopsis>
        The <literal>DEFAULT</> clause, if given, specifies the initial value assigned
        to the variable when the block is entered.  If the <literal>DEFAULT</> clause
*************** arow RECORD;
*** 337,342 ****
--- 339,347 ----
        The <literal>CONSTANT</> option prevents the variable from being
        assigned to after initialization, so that its value will remain constant
        for the duration of the block.
+       The <literal>ELEMENT OF</> ensure using the element type of a given array type.
+       This construct is valuable in polymorphic functions, since the data types needed
+       for internal variables can change from one call to the next call.
        The <literal>COLLATE</> option specifies a collation to use for the
        variable (see <xref linkend="plpgsql-declaration-collation">).
        If <literal>NOT NULL</>
*************** user_id users.user_id%TYPE;
*** 611,616 ****
--- 616,666 ----
      change from one call to the next.  Appropriate variables can be
      created by applying <literal>%TYPE</literal> to the function's
      arguments or result placeholders.
+ <programlisting>
+ CREATE OR REPLACE FUNCTION array_init(v anyelement, size integer)
+ RETURNS anyarray AS $$
+ DECLARE
+     result v%TYPE[] DEFAULT '{}';
+ BEGIN
+     -- prefer builtin function array_fill
+     FOR i IN 1 .. size
+     LOOP
+         result := result || v;
+     END LOOP;
+     RETURN result;
+ END;
+ $$ LANGUAGE plpgsql;
+ 
+ SELECT array_init(0::numeric, 10);
+ SELECT array_init(''::varchar, 10);
+ 
+ 
+ CREATE OR REPLACE FUNCTION bubble_sort(a anyarray)
+ RETURNS anyarray AS $$
+ DECLARE
+     aux ELEMENT OF a%TYPE;
+     repeat_again boolean DEFAULT true;
+ BEGIN
+     -- Don't use this code for large arrays!
+     -- use builtin sort
+     WHILE repeat_again
+     LOOP
+         repeat_again := false;
+         FOR i IN array_lower(a, 1) .. array_upper(a, 1)
+         LOOP
+             IF a[i] > a[i+1] THEN
+                 aux := a[i+1];
+                 a[i+1] := a[i]; a[i] := aux;
+                 repeat_again := true;
+             END IF;
+         END LOOP;
+     END LOOP;
+     RETURN a;
+ END;
+ $$ LANGUAGE plpgsql;
+ 
+ SELECT bubble_sort(ARRAY[3,2,4,6,1]);
+ </programlisting>
     </para>
  
    </sect2>
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
new file mode 100644
index 2aeab96..b77117e
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** plpgsql_parse_wordtype(char *ident)
*** 1646,1653 ****
  			case PLPGSQL_NSTYPE_VAR:
  				return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
  
! 				/* XXX perhaps allow REC/ROW here? */
  
  			default:
  				return NULL;
  		}
--- 1646,1660 ----
  			case PLPGSQL_NSTYPE_VAR:
  				return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
  
! 			case PLPGSQL_NSTYPE_ROW:
! 			{
! 				return ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
! 			}
  
+ 			/*
+ 			 * XXX perhaps allow REC here? Currently PLpgSQL doesn't allow
+ 			 * REC parameters, so REC support is not required.
+ 			 */
  			default:
  				return NULL;
  		}
*************** plpgsql_parse_cwordtype(List *idents)
*** 1718,1727 ****
  								NULL,
  								NULL);
  
! 		if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
  		{
! 			dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
! 			goto done;
  		}
  
  		/*
--- 1725,1742 ----
  								NULL,
  								NULL);
  
! 		if (nse != NULL)
  		{
! 			if (nse->itemtype == PLPGSQL_NSTYPE_VAR)
! 			{
! 				dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
! 				goto done;
! 			}
! 			else if (nse->itemtype == PLPGSQL_NSTYPE_ROW)
! 			{
! 				dtype = ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
! 				goto done;
! 			}
  		}
  
  		/*
*************** plpgsql_parse_cwordrowtype(List *idents)
*** 1852,1857 ****
--- 1867,1925 ----
  }
  
  /*
+  * This routine is used for generating element or array type from base type.
+  * The options to_element_type and to_array_type can be used together, when
+  * we would to ensure valid result. The array array type is original type, so
+  * this direction is safe. The element of scalar type is not allowed, but if
+  * we do "to array" transformation first, then this direction should be safe
+  * too. This design is tolerant, because we should to support a design of
+  * polymorphic parameters, where a array value can be passed as anyelement
+  * or anyarray parameter.
+  */
+ PLpgSQL_type *
+ plpgsql_derive_type(PLpgSQL_type *base_type,
+ 						bool to_element_type, bool to_array_type)
+ {
+ 	Oid		typid = base_type->typoid;
+ 
+ 	if (to_array_type)
+ 	{
+ 		/* do nothing if base_type is a array already */
+ 		if (!OidIsValid(get_element_type(typid)))
+ 		{
+ 			Oid array_typid = get_array_type(typid);
+ 
+ 			if (!OidIsValid(array_typid))
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 						 errmsg("could not find array type for data type %s",
+ 								format_type_be(typid))));
+ 			typid = array_typid;
+ 		}
+ 	}
+ 
+ 	if (to_element_type)
+ 	{
+ 		Oid element_typid = get_element_type(typid);
+ 
+ 		if (!OidIsValid(element_typid))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_DATATYPE_MISMATCH),
+ 					 errmsg("referenced variable should be an array, not type %s",
+ 							format_type_be(typid))));
+ 			typid = element_typid;
+ 	}
+ 
+ 	/* when type is changed, construct new datatype */
+ 	if (typid != base_type->typoid)
+ 		return plpgsql_build_datatype(typid, -1,
+ 						plpgsql_curr_compile->fn_input_collation);
+ 
+ 	/* return original base_type, when any change is not required */
+ 	return base_type;
+ }
+ 
+ /*
   * plpgsql_build_variable - build a datum-array entry of a given
   * datatype
   *
*************** plpgsql_build_variable(const char *refna
*** 1903,1908 ****
--- 1971,1977 ----
  				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
new file mode 100644
index df09575..5a05fed
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
*************** static	void			check_raise_parameters(PLp
*** 270,275 ****
--- 270,276 ----
  %token <keyword>	K_DETAIL
  %token <keyword>	K_DIAGNOSTICS
  %token <keyword>	K_DUMP
+ %token <keyword>	K_ELEMENT
  %token <keyword>	K_ELSE
  %token <keyword>	K_ELSIF
  %token <keyword>	K_END
*************** static	void			check_raise_parameters(PLp
*** 303,308 ****
--- 304,310 ----
  %token <keyword>	K_NOT
  %token <keyword>	K_NOTICE
  %token <keyword>	K_NULL
+ %token <keyword>	K_OF
  %token <keyword>	K_OPEN
  %token <keyword>	K_OPTION
  %token <keyword>	K_OR
*************** unreserved_keyword	:
*** 2408,2413 ****
--- 2410,2416 ----
  				| K_DETAIL
  				| K_DIAGNOSTICS
  				| K_DUMP
+ 				| K_ELEMENT
  				| K_ELSIF
  				| K_ERRCODE
  				| K_ERROR
*************** unreserved_keyword	:
*** 2429,2434 ****
--- 2432,2438 ----
  				| K_NEXT
  				| K_NO
  				| K_NOTICE
+ 				| K_OF
  				| K_OPEN
  				| K_OPTION
  				| K_PERFORM
*************** read_sql_construct(int until,
*** 2684,2697 ****
  	return expr;
  }
  
  static PLpgSQL_type *
  read_datatype(int tok)
  {
  	StringInfoData		ds;
  	char			   *type_name;
  	int					startlocation;
! 	PLpgSQL_type		*result;
  	int					parenlevel = 0;
  
  	/* Should only be called while parsing DECLARE sections */
  	Assert(plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_DECLARE);
--- 2688,2745 ----
  	return expr;
  }
  
+ /*
+  * Returns true when following two tokens after %TYPE/%ROWTYPE are []
+  */
+ static bool
+ array_type_is_required(void)
+ {
+ 	int tok = yylex();
+ 
+ 	if (tok == '[')
+ 	{
+ 		tok = yylex();
+ 		if (tok != ']')
+ 			yyerror("syntax error, expected \"]\"");
+ 
+ 		return true;
+ 	}
+ 	else
+ 		plpgsql_push_back_token(tok);
+ 
+ 	return false;
+ }
+ 
+ /*
+  * Returns true when type is introducted by ELEMENT OF tokens
+  */
+ static bool
+ element_type_is_required(int tok)
+ {
+ 	if (tok_is_keyword(tok, &yylval,
+ 					   K_ELEMENT, "element"))
+ 	{
+ 		tok = yylex();
+ 		if (!tok_is_keyword(tok, &yylval, K_OF, "of"))
+ 			yyerror("syntax error, expected \"OF\"");
+ 
+ 		tok = yylex();
+ 		return true;
+ 	}
+ 
+ 	return false;
+ }
+ 
  static PLpgSQL_type *
  read_datatype(int tok)
  {
  	StringInfoData		ds;
  	char			   *type_name;
  	int					startlocation;
! 	PLpgSQL_type		*result = NULL;
  	int					parenlevel = 0;
+ 	bool				to_element_type = false;
+ 	bool				to_array_type = false;
  
  	/* Should only be called while parsing DECLARE sections */
  	Assert(plpgsql_IdentifierLookup == IDENTIFIER_LOOKUP_DECLARE);
*************** read_datatype(int tok)
*** 2700,2705 ****
--- 2748,2758 ----
  	if (tok == YYEMPTY)
  		tok = yylex();
  
+ 	/*
+ 	 * The request of element type can be first.
+ 	 */
+ 	to_element_type = element_type_is_required(tok);
+ 
  	startlocation = yylloc;
  
  	/*
*************** read_datatype(int tok)
*** 2718,2739 ****
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_wordtype(dtname);
- 				if (result)
- 					return result;
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_wordrowtype(dtname);
! 				if (result)
! 					return result;
  			}
  		}
  	}
  	else if (plpgsql_token_is_unreserved_keyword(tok))
  	{
  		char   *dtname = pstrdup(yylval.keyword);
- 
  		tok = yylex();
  		if (tok == '%')
  		{
--- 2771,2794 ----
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_wordtype(dtname);
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_wordrowtype(dtname);
! 			}
! 
! 			if (result != NULL)
! 			{
! 				to_array_type = array_type_is_required();
! 				return plpgsql_derive_type(result,
! 											  to_element_type, to_array_type);
  			}
  		}
  	}
  	else if (plpgsql_token_is_unreserved_keyword(tok))
  	{
  		char   *dtname = pstrdup(yylval.keyword);
  		tok = yylex();
  		if (tok == '%')
  		{
*************** read_datatype(int tok)
*** 2742,2763 ****
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_wordtype(dtname);
- 				if (result)
- 					return result;
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_wordrowtype(dtname);
! 				if (result)
! 					return result;
  			}
  		}
  	}
  	else if (tok == T_CWORD)
  	{
  		List   *dtnames = yylval.cword.idents;
- 
  		tok = yylex();
  		if (tok == '%')
  		{
--- 2797,2820 ----
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_wordtype(dtname);
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_wordrowtype(dtname);
! 			}
! 
! 			if (result != NULL)
! 			{
! 				to_array_type = array_type_is_required();
! 				return plpgsql_derive_type(result,
! 											  to_element_type, to_array_type);
  			}
  		}
  	}
  	else if (tok == T_CWORD)
  	{
  		List   *dtnames = yylval.cword.idents;
  		tok = yylex();
  		if (tok == '%')
  		{
*************** read_datatype(int tok)
*** 2766,2780 ****
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_cwordtype(dtnames);
- 				if (result)
- 					return result;
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_cwordrowtype(dtnames);
! 				if (result)
! 					return result;
  			}
  		}
  	}
--- 2823,2840 ----
  							   K_TYPE, "type"))
  			{
  				result = plpgsql_parse_cwordtype(dtnames);
  			}
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  			{
  				result = plpgsql_parse_cwordrowtype(dtnames);
! 			}
! 
! 			if (result != NULL)
! 			{
! 				to_array_type = array_type_is_required();
! 				return plpgsql_derive_type(result,
! 											  to_element_type, to_array_type);
  			}
  		}
  	}
*************** read_datatype(int tok)
*** 2817,2823 ****
  
  	plpgsql_push_back_token(tok);
  
! 	return result;
  }
  
  static PLpgSQL_stmt *
--- 2877,2884 ----
  
  	plpgsql_push_back_token(tok);
  
! 	return plpgsql_derive_type(result,
! 								    to_element_type, to_array_type);
  }
  
  static PLpgSQL_stmt *
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
new file mode 100644
index bb0f25b..82959a9
*** a/src/pl/plpgsql/src/pl_scanner.c
--- b/src/pl/plpgsql/src/pl_scanner.c
*************** static const ScanKeyword unreserved_keyw
*** 116,121 ****
--- 116,122 ----
  	PG_KEYWORD("detail", K_DETAIL, UNRESERVED_KEYWORD)
  	PG_KEYWORD("diagnostics", K_DIAGNOSTICS, UNRESERVED_KEYWORD)
  	PG_KEYWORD("dump", K_DUMP, UNRESERVED_KEYWORD)
+ 	PG_KEYWORD("element", K_ELEMENT, UNRESERVED_KEYWORD)
  	PG_KEYWORD("elseif", K_ELSIF, UNRESERVED_KEYWORD)
  	PG_KEYWORD("elsif", K_ELSIF, UNRESERVED_KEYWORD)
  	PG_KEYWORD("errcode", K_ERRCODE, UNRESERVED_KEYWORD)
*************** static const ScanKeyword unreserved_keyw
*** 138,143 ****
--- 139,145 ----
  	PG_KEYWORD("next", K_NEXT, UNRESERVED_KEYWORD)
  	PG_KEYWORD("no", K_NO, UNRESERVED_KEYWORD)
  	PG_KEYWORD("notice", K_NOTICE, UNRESERVED_KEYWORD)
+ 	PG_KEYWORD("of", K_OF, UNRESERVED_KEYWORD)
  	PG_KEYWORD("open", K_OPEN, UNRESERVED_KEYWORD)
  	PG_KEYWORD("option", K_OPTION, UNRESERVED_KEYWORD)
  	PG_KEYWORD("perform", K_PERFORM, UNRESERVED_KEYWORD)
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
new file mode 100644
index a1e900d..f3c563c
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct
*** 281,286 ****
--- 281,287 ----
  	char	   *refname;
  	int			lineno;
  
+ 	PLpgSQL_type *datatype;
  	TupleDesc	rowtupdesc;
  
  	/*
*************** extern PLpgSQL_type *plpgsql_parse_wordt
*** 965,970 ****
--- 966,973 ----
  extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
  extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
  extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
+ extern PLpgSQL_type *plpgsql_derive_type(PLpgSQL_type *base_type,
+ 						bool to_element_type, bool to_array_type);
  extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
  					   Oid collation);
  extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
new file mode 100644
index e30c579..2cb207e
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
*************** end;
*** 5573,5575 ****
--- 5573,5729 ----
  $$;
  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 domain array_domain as int[];
+ create domain int_domain as 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%type[];
+ begin
+   result := ARRAY[x];
+   raise notice '% %', pg_typeof(result), result;
+   return result;
+ end;
+ $$ language plpgsql;
+ create or replace function test_array_init(v anyelement, size integer)
+ returns anyarray as $$
+ declare result v%type[] default '{}';
+ begin
+   -- prefer builtin function array_fill
+   for i in 1 .. size
+   loop
+     result := result || v;
+   end loop;
+   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)
+ 
+ select test_array_init(1.0::numeric, 10);
+               test_array_init              
+ -------------------------------------------
+  {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0}
+ (1 row)
+ 
+ select test_array_init(1::int, 10);
+     test_array_init    
+ -----------------------
+  {1,1,1,1,1,1,1,1,1,1}
+ (1 row)
+ 
+ -- should fail, there are no array type for scalar domain
+ select test_poly_element(1::int_domain);
+ ERROR:  could not find array type for data type int_domain
+ select test_array_init(1::int_domain, 10);
+ ERROR:  could not find array type for data type int_domain
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result element of x%type;
+ 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)
+ 
+ select test_poly_array(ARRAY[1,2,3,4]::array_domain);
+ NOTICE:  integer 1
+  test_poly_array 
+ -----------------
+                1
+ (1 row)
+ 
+ drop function test_simple(anyelement);
+ drop type test_composite_type;
+ drop domain array_domain;
+ drop domain int_domain;
+ drop function test_poly_element(anyelement);
+ drop function test_array_init(anyelement, int);
+ drop function test_poly_array(anyarray);
+ -- should fail, syntax errors
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result element x%type;
+ begin
+   return result;
+ end;
+ $$ language plpgsql;
+ ERROR:  syntax error, expected "OF" at or near "x"
+ LINE 3: declare result element x%type;
+                                ^
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result x%type[;
+ begin
+   return result;
+ end;
+ $$ language plpgsql;
+ ERROR:  syntax error, expected "]" at or near ";"
+ LINE 3: declare result x%type[;
+                               ^
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
new file mode 100644
index 7ffef89..0e16260
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
*************** exception when others then
*** 4386,4388 ****
--- 4386,4482 ----
    null; -- do nothing
  end;
  $$;
+ 
+ 
+ -- test referenced types
+ create type test_composite_type as (x int, y int);
+ create domain array_domain as int[];
+ create domain int_domain as 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%type[];
+ begin
+   result := ARRAY[x];
+   raise notice '% %', pg_typeof(result), result;
+   return result;
+ end;
+ $$ language plpgsql;
+ 
+ create or replace function test_array_init(v anyelement, size integer)
+ returns anyarray as $$
+ declare result v%type[] default '{}';
+ begin
+   -- prefer builtin function array_fill
+   for i in 1 .. size
+   loop
+     result := result || v;
+   end loop;
+   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);
+ 
+ select test_array_init(1.0::numeric, 10);
+ select test_array_init(1::int, 10);
+ 
+ -- should fail, there are no array type for scalar domain
+ select test_poly_element(1::int_domain);
+ select test_array_init(1::int_domain, 10);
+ 
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result element of x%type;
+ 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]);
+ select test_poly_array(ARRAY[1,2,3,4]::array_domain);
+ 
+ drop function test_simple(anyelement);
+ drop type test_composite_type;
+ drop domain array_domain;
+ drop domain int_domain;
+ 
+ drop function test_poly_element(anyelement);
+ drop function test_array_init(anyelement, int);
+ drop function test_poly_array(anyarray);
+ 
+ -- should fail, syntax errors
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result element x%type;
+ begin
+   return result;
+ end;
+ $$ language plpgsql;
+ 
+ create or replace function test_poly_array(x anyarray)
+ returns anyelement as $$
+ declare result x%type[;
+ begin
+   return result;
+ end;
+ $$ language plpgsql;
-- 
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