Hi

2016-01-18 21:37 GMT+01:00 Alvaro Herrera <alvhe...@2ndquadrant.com>:

> > diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
> > new file mode 100644
> > index 1ae4bb7..c819517
> > *** a/src/pl/plpgsql/src/pl_comp.c
> > --- b/src/pl/plpgsql/src/pl_comp.c
> > *************** plpgsql_parse_tripword(char *word1, char
> > *** 1617,1622 ****
> > --- 1617,1677 ----
> >       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;
>
> I think you should add a typedef to the REFTYPE enum, and have this
> function take that type rather than int.
>

done


>
> > +             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;
> > +                     */
>
> I think it would be better to resolve this question outside a code
> comment.
>

done


>
> > +                     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))));
>
> nodeFuncs.c uses this wording:
>         errmsg("could not find array type for data type %s",
> which I think you should adopt.
>

sure, fixed


>
> > --- 1681,1687 ----
> >    * ----------
> >    */
> >   PLpgSQL_type *
> > ! plpgsql_parse_wordtype(char *ident, int reftype_mode)
> >   {
> >       PLpgSQL_type *dtype;
> >       PLpgSQL_nsitem *nse;
>
> Use the typedef'ed enum, as above.
>
> > --- 1699,1721 ----
> >               switch (nse->itemtype)
> >               {
> >                       case PLPGSQL_NSTYPE_VAR:
> > !                     {
> > !                             dtype = ((PLpgSQL_var *)
> (plpgsql_Datums[nse->itemno]))->datatype;
> > !                             return derive_type(dtype, reftype_mode);
> > !                     }
> >
> > !                     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.
> > +                      */
>
> I think you should remove from the "?" onwards in that comment, i.e.
> just keep what was already in the original comment (minus the ROW)
>

I tried to fix it, not sure if understood well.


>
> > --- 1757,1763 ----
> >    * ----------
> >    */
> >   PLpgSQL_type *
> > ! plpgsql_parse_cwordtype(List *idents, int reftype_mode)
> >   {
> >       PLpgSQL_type *dtype = NULL;
> >       PLpgSQL_nsitem *nse;
>
> Typedef.
>
> > --- 2720,2737 ----
> >                       tok = yylex();
> >                       if (tok_is_keyword(tok, &yylval,
> >                                                          K_TYPE, "type"))
> > !                             result = plpgsql_parse_wordtype(dtname,
> PLPGSQL_REFTYPE_TYPE);
> > !                     else if (tok_is_keyword(tok, &yylval,
> > !
>  K_ELEMENTTYPE, "elementtype"))
> > !                             result = plpgsql_parse_wordtype(dtname,
> PLPGSQL_REFTYPE_ELEMENT);
> > !                     else if (tok_is_keyword(tok, &yylval,
> > !
>  K_ARRAYTYPE, "arraytype"))
> > !                             result = plpgsql_parse_wordtype(dtname,
> PLPGSQL_REFTYPE_ARRAY);
> >                       else if (tok_is_keyword(tok, &yylval,
> >
>  K_ROWTYPE, "rowtype"))
> >                               result = plpgsql_parse_wordrowtype(dtname);
> > !                     if (result)
> > !                             return result;
> >               }
>
> This plpgsql parser stuff is pretty tiresome.  (Not this patch's fault
> -- just saying.)
>
>
> > *************** extern bool plpgsql_parse_dblword(char *
> > *** 961,968 ****
> >                                         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_wordrowtype(char *ident);
> >   extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
> >   extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
> > --- 973,980 ----
> >                                         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, 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,
>
> By the way, these functions are misnamed after this patch.  They are
> called "wordtype" and "cwordtype" originally because they accept
> "word%TYPE" and "compositeword%TYPE", but after the patch they not only
> accept TYPE at the right of the percent sign but also ELEMENTTYPE and
> ARRAYTYPE.  Not sure that this is something we want to be too strict
> about.
>

Understand - used name ***reftype instead ****type



>
> > *** a/src/test/regress/expected/plpgsql.out
> > --- b/src/test/regress/expected/plpgsql.out
> > *************** end;
> > *** 5573,5575 ****
> > --- 5573,5667 ----
>
> I think you should also add your array_init() example to the test set.
>

done

Thank you for your comment

Attached updated patch

Regards

Pavel


>
>
> --
> Álvaro Herrera                http://www.2ndQuadrant.com/
> PostgreSQL Development, 24x7 Support, Remote DBA, Training & Services
>
diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml
new file mode 100644
index 9786242..140c81f
*** a/doc/src/sgml/plpgsql.sgml
--- b/doc/src/sgml/plpgsql.sgml
*************** SELECT merge_fields(t.*) FROM table1 t W
*** 710,715 ****
--- 710,792 ----
     </para>
    </sect2>
  
+   <sect2 id="plpgsql-declaration-arraytype">
+    <title>Array Types</title>
+ 
+ <synopsis>
+ <replaceable>variable</replaceable>%ARRAYTYPE
+ </synopsis>
+ 
+    <para>
+     <literal>%ARRAYTYPE</literal> provides the array type from a variable or
+     table column. <literal>%ARRAYTYPE</literal> is particularly valuable in
+     polymorphic functions, since the data types needed for internal variables can
+     change from one call to the next.  Appropriate variables can be
+     created by applying <literal>%ARRAYTYPE</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%ARRAYTYPE 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);
+ </programlisting>
+    </para>
+   </sect2>
+ 
+   <sect2 id="plpgsql-declaration-elementtype">
+    <title>Array Element Types</title>
+ 
+ <synopsis>
+ <replaceable>variable</replaceable>%ELEMENTTYPE
+ </synopsis>
+ 
+    <para>
+     <literal>%ELEMENTTYPE</literal> provides the element type of a given
+     array.  <literal>%ELEMENTTYPE</literal> is particularly valuable in polymorphic
+     functions, since the data types needed for internal variables can
+     change from one call to the next.  Appropriate variables can be
+     created by applying <literal>%ELEMENTTYPE</literal> to the function's
+     arguments or result placeholders:
+ <programlisting>
+ CREATE OR REPLACE FUNCTION bubble_sort(a anyarray)
+ RETURNS anyarray AS $$
+ DECLARE
+     aux a%ELEMENTTYPE;
+     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;
+ </programlisting>
+    </para>
+   </sect2>
+ 
    <sect2 id="plpgsql-declaration-collation">
     <title>Collation of <application>PL/pgSQL</application> Variables</title>
  
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
new file mode 100644
index ebe152d..98f74c3
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** plpgsql_parse_tripword(char *word1, char
*** 1617,1632 ****
  	return false;
  }
  
  
  /* ----------
!  * plpgsql_parse_wordtype	The scanner found word%TYPE. word can be
!  *				a variable name or a basetype.
   *
   * Returns datatype struct, or NULL if no match found for word.
   * ----------
   */
  PLpgSQL_type *
! plpgsql_parse_wordtype(char *ident)
  {
  	PLpgSQL_type *dtype;
  	PLpgSQL_nsitem *nse;
--- 1617,1683 ----
  	return false;
  }
  
+ /*
+  * Derive type from ny base type controlled by reftype.
+  *
+  * This routine allows to take array type from array variable. This behave
+  * is not consistent with enforce_generic_type_consistency, where same task
+  * fails on ERROR: 42704: could not find array type for data type xxxxxxx[].
+  */
+ static PLpgSQL_type *
+ derive_type(PLpgSQL_type *base_type, PLpgSQL_reftype reftype)
+ {
+ 	Oid typoid;
+ 
+ 	switch (reftype)
+ 	{
+ 		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:
+ 		{
+ 			/* Return base_type, when it is a array already */
+ 			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("could not find array type for data type %s",
+ 								format_type_be(base_type->typoid))));
+ 
+ 			return plpgsql_build_datatype(typoid, -1,
+ 							plpgsql_curr_compile->fn_input_collation);
+ 		}
+ 
+ 		default:
+ 			return NULL;		/* keep compiler quiet */
+ 	}
+ }
  
  /* ----------
!  * plpgsql_parse_wordreftype	The scanner found word%TYPE or word%ARRAYTYPE
!  *				or word%ELEMENTTYPE. word can be a variable name or a basetype.
   *
   * Returns datatype struct, or NULL if no match found for word.
   * ----------
   */
  PLpgSQL_type *
! plpgsql_parse_wordreftype(char *ident, PLpgSQL_reftype reftype)
  {
  	PLpgSQL_type *dtype;
  	PLpgSQL_nsitem *nse;
*************** plpgsql_parse_wordtype(char *ident)
*** 1644,1653 ****
  		switch (nse->itemtype)
  		{
  			case PLPGSQL_NSTYPE_VAR:
! 				return ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
  
! 				/* XXX perhaps allow REC/ROW here? */
  
  			default:
  				return NULL;
  		}
--- 1695,1715 ----
  		switch (nse->itemtype)
  		{
  			case PLPGSQL_NSTYPE_VAR:
! 			{
! 				dtype = ((PLpgSQL_var *) (plpgsql_Datums[nse->itemno]))->datatype;
! 				return derive_type(dtype, reftype);
! 			}
  
! 			case PLPGSQL_NSTYPE_ROW:
! 			{
! 				dtype = ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
! 				return derive_type(dtype, reftype);
! 			}
  
+ 			/*
+ 			 * PLpgSQL doesn't support REC parameters, so we should not to
+ 			 * support REC here.
+ 			 */
  			default:
  				return NULL;
  		}
*************** plpgsql_parse_wordtype(char *ident)
*** 1685,1695 ****
  
  
  /* ----------
!  * plpgsql_parse_cwordtype		Same lookup for compositeword%TYPE
   * ----------
   */
  PLpgSQL_type *
! plpgsql_parse_cwordtype(List *idents)
  {
  	PLpgSQL_type *dtype = NULL;
  	PLpgSQL_nsitem *nse;
--- 1747,1758 ----
  
  
  /* ----------
!  * plpgsql_parse_cwordreftype		Same lookup for compositeword%TYPE
!  *					compositeword%ARRAYTYPE or compositeword%ELEMENTTYPE
   * ----------
   */
  PLpgSQL_type *
! plpgsql_parse_cwordreftype(List *idents, PLpgSQL_reftype reftype)
  {
  	PLpgSQL_type *dtype = NULL;
  	PLpgSQL_nsitem *nse;
*************** 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;
  		}
  
  		/*
--- 1781,1804 ----
  								NULL,
  								NULL);
  
! 		if (nse != NULL)
  		{
! 			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);
! 
! 				goto done;
! 			}
! 			else if (nse->itemtype == PLPGSQL_NSTYPE_ROW)
! 			{
! 				ref_type = ((PLpgSQL_row *) (plpgsql_Datums[nse->itemno]))->datatype;
! 				dtype = derive_type(ref_type, reftype);
! 
! 				goto done;
! 			}
  		}
  
  		/*
*************** plpgsql_build_variable(const char *refna
*** 1903,1908 ****
--- 1980,1986 ----
  				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 b14c22d..5048f54
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
*************** static	void			check_raise_parameters(PLp
*** 248,253 ****
--- 248,254 ----
  %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
*************** static	void			check_raise_parameters(PLp
*** 270,275 ****
--- 271,277 ----
  %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
*************** unreserved_keyword	:
*** 2390,2395 ****
--- 2392,2398 ----
  				K_ABSOLUTE
  				| K_ALIAS
  				| K_ARRAY
+ 				| K_ARRAYTYPE
  				| K_ASSERT
  				| K_BACKWARD
  				| K_CLOSE
*************** unreserved_keyword	:
*** 2408,2413 ****
--- 2411,2417 ----
  				| K_DETAIL
  				| K_DIAGNOSTICS
  				| K_DUMP
+ 				| K_ELEMENTTYPE
  				| K_ELSIF
  				| K_ERRCODE
  				| K_ERROR
*************** read_datatype(int tok)
*** 2690,2696 ****
  	StringInfoData		ds;
  	char			   *type_name;
  	int					startlocation;
! 	PLpgSQL_type		*result;
  	int					parenlevel = 0;
  
  	/* Should only be called while parsing DECLARE sections */
--- 2694,2700 ----
  	StringInfoData		ds;
  	char			   *type_name;
  	int					startlocation;
! 	PLpgSQL_type		*result = 0;
  	int					parenlevel = 0;
  
  	/* Should only be called while parsing DECLARE sections */
*************** read_datatype(int tok)
*** 2703,2710 ****
  	startlocation = yylloc;
  
  	/*
! 	 * If we have a simple or composite identifier, check for %TYPE
! 	 * and %ROWTYPE constructs.
  	 */
  	if (tok == T_WORD)
  	{
--- 2707,2714 ----
  	startlocation = yylloc;
  
  	/*
! 	 * If we have a simple or composite identifier, check for %TYPE,
! 	 * %ELEMENTTYPE, %ARRAYTYPE and %ROWTYPE constructs.
  	 */
  	if (tok == T_WORD)
  	{
*************** read_datatype(int tok)
*** 2716,2733 ****
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   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))
--- 2720,2737 ----
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   K_TYPE, "type"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_TYPE);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ELEMENTTYPE, "elementtype"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_ELEMENT);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ARRAYTYPE, "arraytype"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_ARRAY);
  			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))
*************** read_datatype(int tok)
*** 2740,2757 ****
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   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)
--- 2744,2761 ----
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   K_TYPE, "type"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_TYPE);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ELEMENTTYPE, "elementtype"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_ELEMENT);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ARRAYTYPE, "arraytype"))
! 				result = plpgsql_parse_wordreftype(dtname, PLPGSQL_REFTYPE_ARRAY);
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  				result = plpgsql_parse_wordrowtype(dtname);
! 			if (result)
! 				return result;
  		}
  	}
  	else if (tok == T_CWORD)
*************** read_datatype(int tok)
*** 2764,2781 ****
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   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;
! 			}
  		}
  	}
  
--- 2768,2785 ----
  			tok = yylex();
  			if (tok_is_keyword(tok, &yylval,
  							   K_TYPE, "type"))
! 				result = plpgsql_parse_cwordreftype(dtnames, PLPGSQL_REFTYPE_TYPE);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ELEMENTTYPE, "elementtype"))
! 				result = plpgsql_parse_cwordreftype(dtnames, PLPGSQL_REFTYPE_ELEMENT);
! 			else if (tok_is_keyword(tok, &yylval,
! 									K_ARRAYTYPE, "arraytype"))
! 				result = plpgsql_parse_cwordreftype(dtnames, PLPGSQL_REFTYPE_ARRAY);
  			else if (tok_is_keyword(tok, &yylval,
  									K_ROWTYPE, "rowtype"))
  				result = plpgsql_parse_cwordrowtype(dtnames);
! 			if (result)
! 				return result;
  		}
  	}
  
diff --git a/src/pl/plpgsql/src/pl_scanner.c b/src/pl/plpgsql/src/pl_scanner.c
new file mode 100644
index bb0f25b..9a5c54a
*** a/src/pl/plpgsql/src/pl_scanner.c
--- b/src/pl/plpgsql/src/pl_scanner.c
*************** static const ScanKeyword unreserved_keyw
*** 98,103 ****
--- 98,104 ----
  	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)
*************** static const ScanKeyword unreserved_keyw
*** 116,121 ****
--- 117,123 ----
  	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
new file mode 100644
index f4e9f62..41c3d74
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** enum
*** 84,89 ****
--- 84,100 ----
  };
  
  /* ----------
+  * Possible modes for type references
+  * ----------
+  */
+ typedef 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 */
+ } PLpgSQL_reftype;
+ 
+ /* ----------
   * Execution tree node types
   * ----------
   */
*************** typedef struct
*** 281,286 ****
--- 292,298 ----
  	char	   *refname;
  	int			lineno;
  
+ 	PLpgSQL_type *datatype;
  	TupleDesc	rowtupdesc;
  
  	/*
*************** extern bool plpgsql_parse_dblword(char *
*** 961,968 ****
  					  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_wordrowtype(char *ident);
  extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
  extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
--- 973,980 ----
  					  PLwdatum *wdatum, PLcword *cword);
  extern bool plpgsql_parse_tripword(char *word1, char *word2, char *word3,
  					   PLwdatum *wdatum, PLcword *cword);
! extern PLpgSQL_type *plpgsql_parse_wordreftype(char *ident, PLpgSQL_reftype reftype);
! extern PLpgSQL_type *plpgsql_parse_cwordreftype(List *idents, PLpgSQL_reftype reftype);
  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
new file mode 100644
index e30c579..1d6117e
*** a/src/test/regress/expected/plpgsql.out
--- b/src/test/regress/expected/plpgsql.out
*************** end;
*** 5573,5575 ****
--- 5573,5692 ----
  $$;
  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;
+ create or replace function test_array_init(v anyelement, size integer)
+ returns anyarray as $$
+ declare result v%arraytype 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)
+ 
+ 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_array_init(anyelement, int);
+ drop function test_poly_array(anyarray);
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
new file mode 100644
index 7ffef89..85b8174
*** a/src/test/regress/sql/plpgsql.sql
--- b/src/test/regress/sql/plpgsql.sql
*************** exception when others then
*** 4386,4388 ****
--- 4386,4455 ----
    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;
+ 
+ create or replace function test_array_init(v anyelement, size integer)
+ returns anyarray as $$
+ declare result v%arraytype 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);
+ 
+ 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_array_init(anyelement, int);
+ 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