Re: variadic function support

"Pavel Stehule" <[email protected]>
Newsgroups gmane.comp.db.postgresql.devel.patches
Message-ID <[email protected]>
2008/6/25 Tom Lane <[email protected]>:
> "Pavel Stehule" <[email protected]> writes:
>>> Tom Lane wrote:
>>>> Your point about the syntax is good though.  It would be better if
>>>> the syntax were like
>>>> create function foo (a text, variadic b int[])
>>>> or maybe even better
>>>> create function foo (a text, variadic b int)
>
>> I don't see problem with your syntax. It well block combination OUT
>> and VARIADIC parameter - my one request, variadic parameter have to be
>> array.
>
> Well, we should certainly store the parameter type as an array in
> proargtypes, because that makes this feature transparent to all the
> PLs.  However, it doesn't follow that the CREATE FUNCTION syntax
> has to specify the array type rather than the element type.  I think
> the Java precedent might be good reason to go with using the element
> type in the function declaration.
>
>                        regards, tom lane
>
Hello

this is third variant with variadic argumen as scalar. But I still
strongly prefer second variant with conformance declared variadic
array with used array variable.

Regards
Pavel Stehule


-- 
Sent via pgsql-patches mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-patches
variadic.2.2.1.diff (text/x-patch, 35.2 KB)
*** ./doc/src/sgml/ref/create_function.sgml.orig	2008-06-24 16:46:47.000000000 +0200
--- ./doc/src/sgml/ref/create_function.sgml	2008-06-24 16:47:46.000000000 +0200
***************
*** 102,108 ****
       <listitem>
        <para>
         The mode of an argument: either <literal>IN</>, <literal>OUT</>,
!        or <literal>INOUT</>.  If omitted, the default is <literal>IN</>.
        </para>
       </listitem>
      </varlistentry>
--- 102,109 ----
       <listitem>
        <para>
         The mode of an argument: either <literal>IN</>, <literal>OUT</>,
!        <literal>INOUT</> or <literal>VARIADIC</literal>.  If omitted, 
!        the default is <literal>IN</>.
        </para>
       </listitem>
      </varlistentry>
*** ./doc/src/sgml/xfunc.sgml.orig	2008-06-24 16:53:58.000000000 +0200
--- ./doc/src/sgml/xfunc.sgml	2008-06-26 13:34:20.000000000 +0200
***************
*** 578,584 ****
  
      <para>
       Parameters can be marked as <literal>IN</> (the default),
!      <literal>OUT</>, or <literal>INOUT</>.  An <literal>INOUT</>
       parameter serves as both an input parameter (part of the calling
       argument list) and an output parameter (part of the result record type).
      </para>
--- 578,585 ----
  
      <para>
       Parameters can be marked as <literal>IN</> (the default),
!      <literal>OUT</>, <literal>INOUT</>, or <literal>VARIADIC</literal>.  
!      An <literal>INOUT</>
       parameter serves as both an input parameter (part of the calling
       argument list) and an output parameter (part of the result record type).
      </para>
***************
*** 805,810 ****
--- 806,833 ----
  </screen>
      </para>
     </sect2>
+ 
+    <sect2>
+     <title>Variadic <acronym>SQL</acronym> Functions</title>
+ 
+     <para>
+      <acronym>SQL</acronym> functions can be declared to accept
+      variable number of arguments.
+ <screen>
+ CREATE FUNCTION mleast(variadic args numeric) RETURNS numeric AS $$
+     SELECT min($1[i])
+        FROM generate_subscripts($1,1) g(i);
+ $$ LANGUAGE SQL;
+ 
+ SELECT mleast(10, -1, 5, 4);
+  mleast 
+ --------
+      -1
+ (1 row)
+ </screen>
+     </para>
+    </sect2>
+ 
    </sect1>
  
    <sect1 id="xfunc-overload">
*** ./src/backend/catalog/namespace.c.orig	2008-06-24 11:24:34.000000000 +0200
--- ./src/backend/catalog/namespace.c	2008-06-26 16:41:06.000000000 +0200
***************
*** 606,614 ****
  		int			pronargs = procform->pronargs;
  		int			pathpos = 0;
  		FuncCandidateList newResult;
  
  		/* Ignore if it doesn't match requested argument count */
! 		if (nargs >= 0 && pronargs != nargs)
  			continue;
  
  		if (OidIsValid(namespaceId))
--- 606,645 ----
  		int			pronargs = procform->pronargs;
  		int			pathpos = 0;
  		FuncCandidateList newResult;
+ 		Oid	va_oid = InvalidOid;
+ 		bool	variadic = false;
+ 		bool	isnull;
+ 		Datum 		proargmodes;
+ 		
+ 		/* 
+ 		 * Search type of variadic argument,
+ 		 */
+ 		proargmodes = SysCacheGetAttr(PROCOID, proctup,
+ 								Anum_pg_proc_proargmodes, &isnull);
+ 		if (!isnull)
+ 		{
+ 			ArrayType	*ar = DatumGetArrayTypeP(proargmodes);
+ 			char		*argmodes;
+ 			int	j;
+ 
+ 			argmodes = ARR_DATA_PTR(ar);
+ 			for (j = 0; j < ARR_DIMS(ar)[0]; j++)
+ 				if (argmodes[j] == PROARGMODE_VARIADIC)
+ 				{
+ 					variadic = true;
+ 					va_oid = get_variadic_element_type(
+ 									    procform->proargtypes.values[j]);
+ 					Assert(OidIsValid(va_oid));
+ 					break;
+ 				}
+ 		}
  
  		/* Ignore if it doesn't match requested argument count */
! 		if (nargs >= 0 && pronargs != nargs && !variadic)
! 			continue;
! 
! 		/* Ignore variadic function with less arguments */
! 		if (nargs >= 0 && pronargs > nargs && variadic)
  			continue;
  
  		if (OidIsValid(namespaceId))
***************
*** 691,706 ****
  		/*
  		 * Okay to add it to result list
  		 */
! 		newResult = (FuncCandidateList)
! 			palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
! 				   + pronargs * sizeof(Oid));
  		newResult->pathpos = pathpos;
  		newResult->oid = HeapTupleGetOid(proctup);
- 		newResult->nargs = pronargs;
- 		memcpy(newResult->args, procform->proargtypes.values,
- 			   pronargs * sizeof(Oid));
- 
  		newResult->next = resultList;
  		resultList = newResult;
  	}
  
--- 722,762 ----
  		/*
  		 * Okay to add it to result list
  		 */
! 		if (variadic)
! 		{
! 			int	i;
! 
! 			Assert(nargs >= pronargs);
! 
! 			newResult = (FuncCandidateList)
! 				palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
! 					    + nargs * sizeof(Oid));
! 			newResult->nargs = nargs;
! 			newResult->nvargs = nargs - pronargs + 1;
! 			newResult->variadic_oid = va_oid;
! 			memcpy(newResult->args, procform->proargtypes.values,
! 				    (pronargs - 1) * sizeof(Oid));
! 
! 			/* Multiply variadic argument */
! 			for (i = pronargs - 1; i < nargs; i++)
! 				newResult->args[i] = va_oid;
! 		}
! 		else
! 		{
! 			newResult = (FuncCandidateList)
! 				palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid)
! 					   + pronargs * sizeof(Oid));
! 			newResult->nargs = pronargs;
! 			newResult->nvargs = 0;
! 			newResult->variadic_oid = 0;
! 			memcpy(newResult->args, procform->proargtypes.values,
! 				   pronargs * sizeof(Oid));
! 		}
! 
  		newResult->pathpos = pathpos;
  		newResult->oid = HeapTupleGetOid(proctup);
  		newResult->next = resultList;
+ 
  		resultList = newResult;
  	}
  
***************
*** 759,771 ****
  
  		for (; clist; clist = clist->next)
  		{
! 			if (memcmp(clist->args, procform->proargtypes.values,
! 					   nargs * sizeof(Oid)) == 0)
  			{
! 				/* Found the expected entry; is it the right proc? */
! 				visible = (clist->oid == funcid);
! 				break;
! 			}
  		}
  	}
  
--- 815,848 ----
  
  		for (; clist; clist = clist->next)
  		{
! 			/* 
! 			 * Oid of expanded variadic argument is different than record
! 			 * in proargtypes.values.
! 			 */
! 			if (clist->nvargs > 0)
  			{
! 				Oid	va_oid;
! 
! 				if (memcmp(clist->args, procform->proargtypes.values,
! 					    (nargs - 1) * sizeof(Oid)) != 0)
! 					continue;
! 				va_oid = get_variadic_element_type(
! 								    procform->proargtypes.values[nargs - 1]);
! 
! 				if (clist->args[nargs - 1] == va_oid)
! 				{
! 					visible = (clist->oid == funcid);
! 					break;
! 				}				
! 			} 
! 			else
! 				if (memcmp(clist->args, procform->proargtypes.values,
! 					   nargs * sizeof(Oid)) == 0)
! 				{
! 					/* Found the expected entry; is it the right proc? */
! 					visible = (clist->oid == funcid);
! 					break;
! 				}
  		}
  	}
  
*** ./src/backend/catalog/pg_aggregate.c.orig	2008-06-24 14:17:42.000000000 +0200
--- ./src/backend/catalog/pg_aggregate.c	2008-06-24 15:58:27.000000000 +0200
***************
*** 297,302 ****
--- 297,304 ----
  	FuncDetailCode fdresult;
  	AclResult	aclresult;
  	int			i;
+ 	int	nvargs;
+ 	Oid	va_oid;
  
  	/*
  	 * func_get_detail looks up the function in the catalogs, does
***************
*** 307,313 ****
  	 */
  	fdresult = func_get_detail(fnName, NIL, nargs, input_types,
  							   &fnOid, rettype, &retset,
! 							   &true_oid_array);
  
  	/* only valid case is a normal function not returning a set */
  	if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
--- 309,316 ----
  	 */
  	fdresult = func_get_detail(fnName, NIL, nargs, input_types,
  							   &fnOid, rettype, &retset,
! 							   &true_oid_array,
! 							   &nvargs, &va_oid);
  
  	/* only valid case is a normal function not returning a set */
  	if (fdresult != FUNCDETAIL_NORMAL || !OidIsValid(fnOid))
***************
*** 321,326 ****
--- 324,334 ----
  				 errmsg("function %s returns a set",
  						func_signature_string(fnName, nargs, input_types))));
  
+ 	if (nvargs > 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ 				 errmsg("aggregate function has variadic argument %d %d", nvargs, va_oid)));
+ 
  	/*
  	 * If there are any polymorphic types involved, enforce consistency, and
  	 * possibly refine the result type.  It's OK if the result is still
*** ./src/backend/commands/functioncmds.c.orig	2008-06-24 10:36:44.000000000 +0200
--- ./src/backend/commands/functioncmds.c	2008-06-25 16:30:29.000000000 +0200
***************
*** 173,178 ****
--- 173,179 ----
  	Datum	   *paramNames;
  	int			outCount = 0;
  	bool		have_names = false;
+ 	int			varCount = 0; 	
  	ListCell   *x;
  	int			i;
  
***************
*** 227,235 ****
  					 errmsg("functions cannot accept set arguments")));
  
  		if (fp->mode != FUNC_PARAM_OUT)
  			inTypes[inCount++] = toid;
  
! 		if (fp->mode != FUNC_PARAM_IN)
  		{
  			if (outCount == 0)	/* save first OUT param's type */
  				*requiredResultType = toid;
--- 228,270 ----
  					 errmsg("functions cannot accept set arguments")));
  
  		if (fp->mode != FUNC_PARAM_OUT)
+ 		{
+ 			if (fp->mode == FUNC_PARAM_VARIADIC)
+ 			{
+ 				if (varCount++ > 0)
+ 					ereport(ERROR,
+ 						    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 						     errmsg("function cannot accept two or more variadic arguments")));
+ 				/* 
+ 				 * type of declared variadic variable is cecked and changed to 
+ 		    		 * array.
+ 				 */  
+ 				switch (toid)
+ 				{
+ 					case ANYOID:
+ 						break;
+ 					case ANYELEMENTOID:
+ 						toid = ANYARRAYOID;
+ 						break;
+ 					default:
+ 						toid = get_array_type(toid);
+ 						if (!OidIsValid(toid))
+ 							ereport(ERROR,
+ 								    (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 								    errmsg("variadic argument isn't array")));
+ 				}
+ 			}
+ 			else
+ 				/* check if variadic argument is last IN argument */
+ 				if (varCount > 0 && fp->mode != FUNC_PARAM_OUT)
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 							 errmsg("variadic argument isn't last function's argument")));
+ 
  			inTypes[inCount++] = toid;
+ 		}
  
! 		if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
  		{
  			if (outCount == 0)	/* save first OUT param's type */
  				*requiredResultType = toid;
***************
*** 252,258 ****
  	/* Now construct the proper outputs as needed */
  	*parameterTypes = buildoidvector(inTypes, inCount);
  
! 	if (outCount > 0)
  	{
  		*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
  											 sizeof(Oid), true, 'i');
--- 287,293 ----
  	/* Now construct the proper outputs as needed */
  	*parameterTypes = buildoidvector(inTypes, inCount);
  
! 	if (outCount > 0 || varCount > 0)
  	{
  		*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
  											 sizeof(Oid), true, 'i');
*** ./src/backend/parser/gram.y.orig	2008-06-24 10:30:01.000000000 +0200
--- ./src/backend/parser/gram.y	2008-06-25 18:03:20.000000000 +0200
***************
*** 53,58 ****
--- 53,59 ----
  
  #include "catalog/index.h"
  #include "catalog/namespace.h"
+ #include "catalog/pg_type.h"
  #include "commands/defrem.h"
  #include "nodes/makefuncs.h"
  #include "parser/gramparse.h"
***************
*** 444,450 ****
  	UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
  	UPDATE USER USING
  
! 	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
  
  	WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
--- 445,451 ----
  	UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
  	UPDATE USER USING
  
! 	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
  
  	WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
***************
*** 4204,4209 ****
--- 4205,4211 ----
  			| OUT_P									{ $$ = FUNC_PARAM_OUT; }
  			| INOUT									{ $$ = FUNC_PARAM_INOUT; }
  			| IN_P OUT_P							{ $$ = FUNC_PARAM_INOUT; }
+ 			| VARIADIC							{ $$ = FUNC_PARAM_VARIADIC; }
  		;
  
  /*
*** ./src/backend/parser/keywords.c.orig	2008-06-24 10:29:58.000000000 +0200
--- ./src/backend/parser/keywords.c	2008-06-24 10:30:50.000000000 +0200
***************
*** 393,398 ****
--- 393,399 ----
  	{"value", VALUE_P, UNRESERVED_KEYWORD},
  	{"values", VALUES, COL_NAME_KEYWORD},
  	{"varchar", VARCHAR, COL_NAME_KEYWORD},
+ 	{"variadic", VARIADIC, UNRESERVED_KEYWORD},
  	{"varying", VARYING, UNRESERVED_KEYWORD},
  	{"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD},
  	{"version", VERSION_P, UNRESERVED_KEYWORD},
*** ./src/backend/parser/parse_func.c.orig	2008-06-24 13:09:17.000000000 +0200
--- ./src/backend/parser/parse_func.c	2008-06-26 14:47:10.000000000 +0200
***************
*** 76,81 ****
--- 76,83 ----
  	Node	   *retval;
  	bool		retset;
  	FuncDetailCode fdresult;
+ 	int			nvargs;
+ 	Oid			va_oid;
  
  	/*
  	 * Most of the rest of the parser just assumes that functions do not have
***************
*** 158,164 ****
  	 */
  	fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
  							   &funcid, &rettype, &retset,
! 							   &declared_arg_types);
  	if (fdresult == FUNCDETAIL_COERCION)
  	{
  		/*
--- 160,167 ----
  	 */
  	fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
  							   &funcid, &rettype, &retset,
! 							   &declared_arg_types,
! 							   &nvargs, &va_oid);
  	if (fdresult == FUNCDETAIL_COERCION)
  	{
  		/*
***************
*** 229,234 ****
--- 232,279 ----
  	}
  
  	/*
+ 	 * Last nvargs arguments are transformed to array when function is
+ 	 * non "any" variadic.
+ 	 */
+ 	if (nvargs> 0 && va_oid != ANYOID)
+ 	{
+ 		A_ArrayExpr *n = makeNode(A_ArrayExpr);
+ 		Node	*tn;
+ 
+ 		if (nvargs < nargs)
+ 		{
+ 			int 	non_var_args = nargs - nvargs;
+ 			List	*vargs;
+ 
+ 			vargs = list_copy_tail(fargs, non_var_args);
+ 			fargs = list_truncate(fargs, non_var_args);
+ 			n->elements = vargs;
+ 			tn = transformExpr(pstate, (Node *) n);
+ 			fargs = lappend(fargs, tn);
+ 		}
+ 		else
+ 		{
+ 			/* array from all argumenst */
+ 			n->elements = fargs;
+ 			tn= transformExpr(pstate, (Node *) n);
+ 			fargs = list_make1(tn);
+ 		}
+ 
+ 		/* 
+ 		 * Now we have to correct argument's metadata used in
+ 		 * enforce_generic_type_consistency and make_fn_arguments. These
+ 		 * functions needs actual values of nargs, actual_arg_types and
+ 		 * real_arg_types.
+ 		 */
+ 		nargs = nargs - nvargs + 1;
+ 		actual_arg_types[nargs - 1] = ((ArrayExpr *) tn)->array_typeid;
+ 		if (va_oid != ANYELEMENTOID)
+ 			declared_arg_types[nargs - 1] = get_array_type(va_oid);
+ 		else
+ 			declared_arg_types[nargs - 1] = ANYARRAYOID;
+ 	} 
+ 
+ 	/*
  	 * enforce consistency with polymorphic argument and return types,
  	 * possibly adjusting return type or declared_arg_types (which will be
  	 * used as the cast destination by make_fn_arguments)
***************
*** 697,703 ****
  				Oid *funcid,	/* return value */
  				Oid *rettype,	/* return value */
  				bool *retset,	/* return value */
! 				Oid **true_typeids)		/* return value */
  {
  	FuncCandidateList raw_candidates;
  	FuncCandidateList best_candidate;
--- 742,750 ----
  				Oid *funcid,	/* return value */
  				Oid *rettype,	/* return value */
  				bool *retset,	/* return value */
! 				Oid **true_typeids,		/* return value */
! 				int *nvargs,	/* return value */
! 				Oid *va_oid)	/* return value */
  {
  	FuncCandidateList raw_candidates;
  	FuncCandidateList best_candidate;
***************
*** 787,792 ****
--- 834,841 ----
  					*rettype = targetType;
  					*retset = false;
  					*true_typeids = argtypes;
+ 					*nvargs = 0;
+ 					*va_oid = InvalidOid;
  					return FUNCDETAIL_COERCION;
  				}
  			}
***************
*** 836,841 ****
--- 885,892 ----
  
  		*funcid = best_candidate->oid;
  		*true_typeids = best_candidate->args;
+ 		*nvargs = best_candidate->nvargs;
+ 		*va_oid = best_candidate->variadic_oid;
  
  		ftup = SearchSysCache(PROCOID,
  							  ObjectIdGetDatum(best_candidate->oid),
***************
*** 1190,1200 ****
  	FuncCandidateList clist;
  
  	clist = FuncnameGetCandidates(funcname, nargs);
- 
  	while (clist)
  	{
  		if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0)
  			return clist->oid;
  		clist = clist->next;
  	}
  
--- 1241,1251 ----
  	FuncCandidateList clist;
  
  	clist = FuncnameGetCandidates(funcname, nargs);
  	while (clist)
  	{
  		if (memcmp(argtypes, clist->args, nargs * sizeof(Oid)) == 0)
  			return clist->oid;
+ 
  		clist = clist->next;
  	}
  
*** ./src/backend/utils/adt/oid.c.orig	2008-06-25 15:22:20.000000000 +0200
--- ./src/backend/utils/adt/oid.c	2008-06-26 16:19:41.000000000 +0200
***************
*** 21,27 ****
  #include "libpq/pqformat.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
! 
  
  #define OidVectorSize(n)	(offsetof(oidvector, values) + (n) * sizeof(Oid))
  
--- 21,27 ----
  #include "libpq/pqformat.h"
  #include "utils/array.h"
  #include "utils/builtins.h"
! #include "utils/lsyscache.h"
  
  #define OidVectorSize(n)	(offsetof(oidvector, values) + (n) * sizeof(Oid))
  
***************
*** 419,421 ****
--- 419,451 ----
  
  	PG_RETURN_BOOL(cmp > 0);
  }
+ 
+ /*
+  * pg_get_element_type function is used for overwriting real variadic type
+  */
+ Oid 
+ get_variadic_element_type(Oid oid)
+ {
+ 	Oid	result;
+ 
+ 	switch (oid)
+ 	{
+ 		case ANYOID:
+ 			result = ANYOID;
+ 			break;
+ 		case ANYARRAYOID:
+ 			result = ANYELEMENTOID;
+ 			break;
+ 		default:
+ 			result = get_element_type(oid);
+ 	}
+ 	return result;
+ }
+ 
+ Datum
+ pg_get_element_type(PG_FUNCTION_ARGS)
+ {
+ 	Oid	oid = PG_GETARG_OID(0);
+ 
+ 	PG_RETURN_OID(get_variadic_element_type(oid));
+ }
*** ./src/backend/utils/adt/regproc.c.orig	2008-06-24 13:12:03.000000000 +0200
--- ./src/backend/utils/adt/regproc.c	2008-06-26 16:32:54.000000000 +0200
***************
*** 38,43 ****
--- 38,44 ----
  #include "utils/lsyscache.h"
  #include "utils/syscache.h"
  #include "utils/tqual.h"
+ #include "utils/array.h"
  
  static void parseNameAndArgTypes(const char *string, bool allowNone,
  					 List **names, int *nargs, Oid *argtypes);
***************
*** 318,325 ****
--- 319,347 ----
  		int			i;
  		char	   *nspname;
  		StringInfoData buf;
+ 		Datum 		proargmodes;
+ 		bool		isnull;
+ 		bool		variadic = false;
  
  		/* XXX no support here for bootstrap mode */
+ 		
+ 		/*
+ 		 * variadic function detection
+ 		 */
+ 		proargmodes = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargmodes, &isnull);
+ 		if (!isnull)
+ 		{
+  			ArrayType	*ar = DatumGetArrayTypeP(proargmodes);
+  			char		*argmodes;
+  
+  			argmodes = ARR_DATA_PTR(ar);
+ 			for (i = 0; i < ARR_DIMS(ar)[0]; i++)
+ 				if (argmodes[i] == PROARGMODE_VARIADIC)
+ 				{
+ 					variadic = true;
+ 					break;
+ 				}
+ 		}
  
  		initStringInfo(&buf);
  
***************
*** 340,345 ****
--- 362,374 ----
  
  			if (i > 0)
  				appendStringInfoChar(&buf, ',');
+ 			/* last variadic argument */
+ 			if (variadic && i == (nargs - 1))
+ 			{
+ 				appendStringInfoString(&buf, "VARIADIC ");
+ 				thisargtype = get_variadic_element_type(thisargtype);
+ 			}
+ 			
  			appendStringInfoString(&buf, format_type_be(thisargtype));
  		}
  		appendStringInfoChar(&buf, ')');
*** ./src/backend/utils/adt/ruleutils.c.orig	2008-06-24 14:37:19.000000000 +0200
--- ./src/backend/utils/adt/ruleutils.c	2008-06-24 14:38:28.000000000 +0200
***************
*** 5344,5349 ****
--- 5344,5351 ----
  	Oid			p_rettype;
  	bool		p_retset;
  	Oid		   *p_true_typeids;
+ 	int	nvargs;
+ 	Oid	va_oid;
  
  	proctup = SearchSysCache(PROCOID,
  							 ObjectIdGetDatum(funcid),
***************
*** 5362,5368 ****
  	p_result = func_get_detail(list_make1(makeString(proname)),
  							   NIL, nargs, argtypes,
  							   &p_funcid, &p_rettype,
! 							   &p_retset, &p_true_typeids);
  	if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) &&
  		p_funcid == funcid)
  		nspname = NULL;
--- 5364,5371 ----
  	p_result = func_get_detail(list_make1(makeString(proname)),
  							   NIL, nargs, argtypes,
  							   &p_funcid, &p_rettype,
! 							   &p_retset, &p_true_typeids,
! 							   &nvargs, &va_oid);
  	if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) &&
  		p_funcid == funcid)
  		nspname = NULL;
*** ./src/backend/utils/fmgr/funcapi.c.orig	2008-06-24 16:18:51.000000000 +0200
--- ./src/backend/utils/fmgr/funcapi.c	2008-06-24 16:21:54.000000000 +0200
***************
*** 844,850 ****
  		numoutargs = 0;
  		for (i = 0; i < numargs; i++)
  		{
! 			if (argmodes[i] == PROARGMODE_IN)
  				continue;
  			Assert(argmodes[i] == PROARGMODE_OUT ||
  				   argmodes[i] == PROARGMODE_INOUT);
--- 844,850 ----
  		numoutargs = 0;
  		for (i = 0; i < numargs; i++)
  		{
! 			if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC)
  				continue;
  			Assert(argmodes[i] == PROARGMODE_OUT ||
  				   argmodes[i] == PROARGMODE_INOUT);
***************
*** 994,1000 ****
  	{
  		char	   *pname;
  
! 		if (argmodes[i] == PROARGMODE_IN)
  			continue;
  		Assert(argmodes[i] == PROARGMODE_OUT ||
  			   argmodes[i] == PROARGMODE_INOUT);
--- 994,1000 ----
  	{
  		char	   *pname;
  
! 		if (argmodes[i] == PROARGMODE_IN || argmodes[i] == PROARGMODE_VARIADIC)
  			continue;
  		Assert(argmodes[i] == PROARGMODE_OUT ||
  			   argmodes[i] == PROARGMODE_INOUT);
*** ./src/bin/pg_dump/pg_dump.c.orig	2008-06-24 15:05:21.000000000 +0200
--- ./src/bin/pg_dump/pg_dump.c	2008-06-25 17:15:08.000000000 +0200
***************
*** 6429,6449 ****
  		const char *argname;
  
  		typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
! 		typname = getFormattedTypeName(typid, zeroAsOpaque);
  
  		if (argmodes)
  		{
  			switch (argmodes[j][0])
  			{
! 				case 'i':
  					argmode = "";
  					break;
! 				case 'o':
  					argmode = "OUT ";
  					break;
! 				case 'b':
  					argmode = "INOUT ";
  					break;
  				default:
  					write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
  					argmode = "";
--- 6429,6452 ----
  		const char *argname;
  
  		typid = allargtypes ? atooid(allargtypes[j]) : finfo->argtypes[j];
! 		typname = getFormattedTypeName(typid, zeroAsOpaque | (argmodes[j][0] == PROARGMODE_VARIADIC)? variadicArgument : 0);
  
  		if (argmodes)
  		{
  			switch (argmodes[j][0])
  			{
! 				case PROARGMODE_IN:
  					argmode = "";
  					break;
! 				case PROARGMODE_OUT:
  					argmode = "OUT ";
  					break;
! 				case PROARGMODE_INOUT:
  					argmode = "INOUT ";
  					break;
+ 				case PROARGMODE_VARIADIC:
+ 					argmode = "VARIADIC ";
+ 					break;
  				default:
  					write_msg(NULL, "WARNING: bogus value in proargmodes array\n");
  					argmode = "";
***************
*** 10260,10266 ****
  	}
  
  	query = createPQExpBuffer();
! 	if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
  						  oid);
--- 10263,10274 ----
  	}
  
  	query = createPQExpBuffer();
! 	if ((opts & variadicArgument) != 0)
! 	{
! 		appendPQExpBuffer(query, "SELECT pg_catalog.format_type(pg_catalog.pg_get_element_type('%u'::pg_catalog.oid), NULL)",
! 						oid);	
! 	}
! 	else if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT pg_catalog.format_type('%u'::pg_catalog.oid, NULL)",
  						  oid);
*** ./src/bin/pg_dump/pg_dump.h.orig	2008-06-25 17:07:25.000000000 +0200
--- ./src/bin/pg_dump/pg_dump.h	2008-06-25 17:13:18.000000000 +0200
***************
*** 436,442 ****
  	zeroAsOpaque = 1,
  	zeroAsAny = 2,
  	zeroAsStar = 4,
! 	zeroAsNone = 8
  } OidOptions;
  
  extern void AssignDumpId(DumpableObject *dobj);
--- 436,443 ----
  	zeroAsOpaque = 1,
  	zeroAsAny = 2,
  	zeroAsStar = 4,
! 	zeroAsNone = 8,
! 	variadicArgument = 16,
  } OidOptions;
  
  extern void AssignDumpId(DumpableObject *dobj);
*** ./src/bin/psql/describe.c.orig	2008-06-24 15:17:23.000000000 +0200
--- ./src/bin/psql/describe.c	2008-06-25 16:41:17.000000000 +0200
***************
*** 187,198 ****
  					  "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
  					  "          WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
  					"          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
  					  "        END ||\n"
  					  "        CASE\n"
  			 "          WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
  					  "          ELSE p.proargnames[s.i] || ' ' \n"
  					  "        END ||\n"
! 			  "        pg_catalog.format_type(p.proallargtypes[s.i], NULL)\n"
  					  "      FROM\n"
  					  "        pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
  					  "    ), ', ')\n"
--- 187,202 ----
  					  "          WHEN p.proargmodes[s.i] = 'i' THEN ''\n"
  					  "          WHEN p.proargmodes[s.i] = 'o' THEN 'OUT '\n"
  					"          WHEN p.proargmodes[s.i] = 'b' THEN 'INOUT '\n"
+ 					"          WHEN p.proargmodes[s.i] = 'v' THEN 'VARIADIC '\n"
  					  "        END ||\n"
  					  "        CASE\n"
  			 "          WHEN COALESCE(p.proargnames[s.i], '') = '' THEN ''\n"
  					  "          ELSE p.proargnames[s.i] || ' ' \n"
  					  "        END ||\n"
! 			  "        pg_catalog.format_type(CASE\n"
! 					"                   WHEN p.proargmodes[s.i] = 'v' THEN pg_catalog.pg_get_element_type(p.proallargtypes[s.i])\n"
!  					"                   ELSE p.proallargtypes[s.i]\n"
! 					"                 END, NULL)\n"
  					  "      FROM\n"
  					  "        pg_catalog.generate_series(1, pg_catalog.array_upper(p.proallargtypes, 1)) AS s(i)\n"
  					  "    ), ', ')\n"
*** ./src/include/catalog/namespace.h.orig	2008-06-24 12:48:23.000000000 +0200
--- ./src/include/catalog/namespace.h	2008-06-26 14:45:13.000000000 +0200
***************
*** 29,34 ****
--- 29,36 ----
  	int			pathpos;		/* for internal use of namespace lookup */
  	Oid			oid;			/* the function or operator's OID */
  	int			nargs;			/* number of arg types returned */
+ 	int			nvargs;			/* number of variadic arguments */
+ 	Oid			variadic_oid;		/* Oid of variadic argument */
  	Oid			args[1];		/* arg types --- VARIABLE LENGTH ARRAY */
  }	*FuncCandidateList;	/* VARIABLE LENGTH STRUCT */
  
*** ./src/include/catalog/pg_proc.h.orig	2008-06-24 13:33:45.000000000 +0200
--- ./src/include/catalog/pg_proc.h	2008-06-25 16:10:41.000000000 +0200
***************
*** 2660,2665 ****
--- 2660,2668 ----
  DATA(insert OID = 1799 (  oidout		   PGNSP PGUID 12 1 0 f f t f i 1 2275 "26" _null_ _null_ _null_ oidout - _null_ _null_ ));
  DESCR("I/O");
  
+ DATA(insert OID = 2162 (  pg_get_element_type	   PGNSP PGUID 12 1 0 f f t f i 1 26 "26" _null_ _null_ _null_ pg_get_element_type - _null_ _null_ ));
+ DESCR("returns element type of array");
+ 
  
  DATA(insert OID = 1810 (  bit_length	   PGNSP PGUID 14 1 0 f f t f i 1 23 "17" _null_ _null_ _null_ "select pg_catalog.octet_length($1) * 8" - _null_ _null_ ));
  DESCR("length in bits");
***************
*** 4466,4470 ****
--- 4469,4474 ----
  #define PROARGMODE_IN		'i'
  #define PROARGMODE_OUT		'o'
  #define PROARGMODE_INOUT	'b'
+ #define PROARGMODE_VARIADIC	'v'
  
  #endif   /* PG_PROC_H */
*** ./src/include/nodes/parsenodes.h.orig	2008-06-24 10:35:02.000000000 +0200
--- ./src/include/nodes/parsenodes.h	2008-06-24 10:35:09.000000000 +0200
***************
*** 1568,1574 ****
  	/* the assigned enum values appear in pg_proc, don't change 'em! */
  	FUNC_PARAM_IN = 'i',		/* input only */
  	FUNC_PARAM_OUT = 'o',		/* output only */
! 	FUNC_PARAM_INOUT = 'b'		/* both */
  } FunctionParameterMode;
  
  typedef struct FunctionParameter
--- 1568,1575 ----
  	/* the assigned enum values appear in pg_proc, don't change 'em! */
  	FUNC_PARAM_IN = 'i',		/* input only */
  	FUNC_PARAM_OUT = 'o',		/* output only */
! 	FUNC_PARAM_INOUT = 'b',		/* both */
! 	FUNC_PARAM_VARIADIC = 'v'	/* variadic */
  } FunctionParameterMode;
  
  typedef struct FunctionParameter
*** ./src/include/parser/parse_func.h.orig	2008-06-24 14:16:40.000000000 +0200
--- ./src/include/parser/parse_func.h	2008-06-24 14:17:13.000000000 +0200
***************
*** 49,55 ****
  extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
  				int nargs, Oid *argtypes,
  				Oid *funcid, Oid *rettype,
! 				bool *retset, Oid **true_typeids);
  
  extern int func_match_argtypes(int nargs,
  					Oid *input_typeids,
--- 49,56 ----
  extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
  				int nargs, Oid *argtypes,
  				Oid *funcid, Oid *rettype,
! 				bool *retset, Oid **true_typeids,
! 				int *nvargs, Oid *va_oid);
  
  extern int func_match_argtypes(int nargs,
  					Oid *input_typeids,
*** ./src/include/utils/builtins.h.orig	2008-06-25 15:33:30.000000000 +0200
--- ./src/include/utils/builtins.h	2008-06-26 16:16:09.000000000 +0200
***************
*** 436,441 ****
--- 436,443 ----
  extern Datum oidvectorge(PG_FUNCTION_ARGS);
  extern Datum oidvectorgt(PG_FUNCTION_ARGS);
  extern oidvector *buildoidvector(const Oid *oids, int n);
+ extern Datum pg_get_element_type(PG_FUNCTION_ARGS);
+ extern Oid get_variadic_element_type(Oid oid);
  
  /* pseudotypes.c */
  extern Datum cstring_in(PG_FUNCTION_ARGS);
*** ./src/interfaces/ecpg/preproc/preproc.y.orig	2008-06-24 11:12:00.000000000 +0200
--- ./src/interfaces/ecpg/preproc/preproc.y	2008-06-24 11:13:52.000000000 +0200
***************
*** 489,495 ****
  	UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
  	UPDATE USER USING
  
! 	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
  	WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
  
--- 489,495 ----
  	UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL
  	UPDATE USER USING
  
! 	VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING
  	VERBOSE VERSION_P VIEW VOLATILE
  	WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE
  
***************
*** 6668,6673 ****
--- 6668,6674 ----
  		| VALIDATOR			{ $$ = make_str("validator"); }
  		| VALUE_P			{ $$ = make_str("value"); }
  		| VARYING			{ $$ = make_str("varying"); }
+ 		| VARIADIC			{ $$ = make_str("variadic"); }
  		| VERSION_P			{ $$ = make_str("version"); }
  		| VIEW				{ $$ = make_str("view"); }
  		| VOLATILE			{ $$ = make_str("volatile"); }
*** ./src/pl/plpgsql/src/pl_comp.c.orig	2008-06-24 16:23:47.000000000 +0200
--- ./src/pl/plpgsql/src/pl_comp.c	2008-06-24 16:25:53.000000000 +0200
***************
*** 426,432 ****
  				{
  					argitemtype = PLPGSQL_NSTYPE_VAR;
  					/* input argument vars are forced to be CONSTANT */
! 					if (argmode == PROARGMODE_IN)
  						((PLpgSQL_var *) argvariable)->isconst = true;
  				}
  				else
--- 426,432 ----
  				{
  					argitemtype = PLPGSQL_NSTYPE_VAR;
  					/* input argument vars are forced to be CONSTANT */
! 					if (argmode == PROARGMODE_IN || argmode == PROARGMODE_VARIADIC)
  						((PLpgSQL_var *) argvariable)->isconst = true;
  				}
  				else
***************
*** 436,442 ****
  				}
  
  				/* Remember arguments in appropriate arrays */
! 				if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT)
  					in_arg_varnos[num_in_args++] = argvariable->dno;
  				if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)
  					out_arg_variables[num_out_args++] = argvariable;
--- 436,443 ----
  				}
  
  				/* Remember arguments in appropriate arrays */
! 				if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT
! 					    || argmode == PROARGMODE_VARIADIC)
  					in_arg_varnos[num_in_args++] = argvariable->dno;
  				if (argmode == PROARGMODE_OUT || argmode == PROARGMODE_INOUT)
  					out_arg_variables[num_out_args++] = argvariable;
*** ./src/test/regress/expected/plpgsql.out.orig	2008-06-24 16:33:20.000000000 +0200
--- ./src/test/regress/expected/plpgsql.out	2008-06-26 16:45:05.000000000 +0200
***************
*** 3544,3546 ****
--- 3544,3611 ----
  
  drop function catch();
  drop function case_test(bigint);
+ -- variadic fuction test
+ create or replace function vari(variadic int) 
+ returns void as $$
+ begin 
+   for i in array_lower($1,1)..array_upper($1,1) loop 
+     raise notice '%', $1[i]; 
+   end loop; end; 
+ $$ language plpgsql;
+ select vari(1,2,3,4,5);
+ NOTICE:  1
+ NOTICE:  2
+ NOTICE:  3
+ NOTICE:  4
+ NOTICE:  5
+  vari 
+ ------
+  
+ (1 row)
+ 
+ select vari(3,4,5);
+ NOTICE:  3
+ NOTICE:  4
+ NOTICE:  5
+  vari 
+ ------
+  
+ (1 row)
+ 
+ drop function vari(variadic int);
+ -- coerce test
+ create or replace function pleast(variadic numeric)
+ returns numeric as $$
+ declare aux numeric = $1[array_lower($1,1)];
+ begin
+   for i in array_lower($1,1)+1..array_upper($1,1) loop
+     if $1[i] < aux then aux := $1[i]; end if;
+   end loop;
+   return aux;
+ end;
+ $$ language plpgsql immutable strict;
+ select pleast(10,1,2,3,-16);
+  pleast 
+ --------
+     -16
+ (1 row)
+ 
+ select pleast(10.2,2.2,-1.1);
+  pleast 
+ --------
+    -1.1
+ (1 row)
+ 
+ select pleast(10.2,10, -20);
+  pleast 
+ --------
+     -20
+ (1 row)
+ 
+ select pleast(10,20, -1.0);
+  pleast 
+ --------
+    -1.0
+ (1 row)
+ 
+ drop function pleast(variadic numeric);
*** ./src/test/regress/expected/polymorphism.out.orig	2008-06-24 16:10:52.000000000 +0200
--- ./src/test/regress/expected/polymorphism.out	2008-06-26 16:51:20.000000000 +0200
***************
*** 613,615 ****
--- 613,658 ----
    SFUNC = add_group,
    STYPE = int8[]
  );
+ --test for variadic polymorphic function 
+ create function myleast(variadic anyelement)
+ returns anyelement as $$
+   select min($1[i])
+      from generate_subscripts($1,1) g(i)
+ $$ language sql immutable strict;
+ select myleast(10, 1, 20, 33);
+  myleast 
+ ---------
+        1
+ (1 row)
+ 
+ select myleast(1.1, 0.22, 0.55);
+  myleast 
+ ---------
+     0.22
+ (1 row)
+ 
+ -- visibility test
+ select pg_catalog.pg_function_is_visible('myleast(anyelement)'::regprocedure::int);
+  pg_function_is_visible 
+ ------------------------
+  t
+ (1 row)
+ 
+ drop function myleast(anyelement);
+ create or replace function concat(varchar, variadic anyelement)
+ returns varchar as $$
+   select array_to_string($2, $1);
+ $$ language sql immutable strict;
+ select concat('%', 1, 2, 3, 4, 5);
+   concat   
+ -----------
+  1%2%3%4%5
+ (1 row)
+ 
+ select concat('|', 'a'::text, 'b', 'c');
+  concat 
+ --------
+  a|b|c
+ (1 row)
+ 
+ drop function concat(varchar, variadic anyelement);
*** ./src/test/regress/sql/plpgsql.sql.orig	2008-06-24 16:01:55.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql	2008-06-25 17:23:16.000000000 +0200
***************
*** 2878,2880 ****
--- 2878,2913 ----
  
  drop function catch();
  drop function case_test(bigint);
+ 
+ -- variadic fuction test
+ create or replace function vari(variadic int) 
+ returns void as $$
+ begin 
+   for i in array_lower($1,1)..array_upper($1,1) loop 
+     raise notice '%', $1[i]; 
+   end loop; end; 
+ $$ language plpgsql;
+ 
+ select vari(1,2,3,4,5);
+ select vari(3,4,5);
+ 
+ drop function vari(variadic int);
+ 
+ -- coerce test
+ create or replace function pleast(variadic numeric)
+ returns numeric as $$
+ declare aux numeric = $1[array_lower($1,1)];
+ begin
+   for i in array_lower($1,1)+1..array_upper($1,1) loop
+     if $1[i] < aux then aux := $1[i]; end if;
+   end loop;
+   return aux;
+ end;
+ $$ language plpgsql immutable strict;
+ 
+ select pleast(10,1,2,3,-16);
+ select pleast(10.2,2.2,-1.1);
+ select pleast(10.2,10, -20);
+ select pleast(10,20, -1.0);
+ 
+ drop function pleast(variadic numeric);
*** ./src/test/regress/sql/polymorphism.sql.orig	2008-06-24 16:01:52.000000000 +0200
--- ./src/test/regress/sql/polymorphism.sql	2008-06-26 16:50:09.000000000 +0200
***************
*** 426,428 ****
--- 426,453 ----
    SFUNC = add_group,
    STYPE = int8[]
  );
+ 
+ --test for variadic polymorphic function 
+ create function myleast(variadic anyelement)
+ returns anyelement as $$
+   select min($1[i])
+      from generate_subscripts($1,1) g(i)
+ $$ language sql immutable strict;
+ 
+ select myleast(10, 1, 20, 33);
+ select myleast(1.1, 0.22, 0.55);
+ -- visibility test
+ select pg_catalog.pg_function_is_visible('myleast(anyelement)'::regprocedure::int);
+ 
+ drop function myleast(anyelement);
+ 
+ create or replace function concat(varchar, variadic anyelement)
+ returns varchar as $$
+   select array_to_string($2, $1);
+ $$ language sql immutable strict;
+ 
+ select concat('%', 1, 2, 3, 4, 5);
+ select concat('|', 'a'::text, 'b', 'c');
+ 
+ drop function concat(varchar, variadic anyelement);
+
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.