Re: variadic function support

"Pavel Stehule" <[email protected]>
Newsgroups gmane.comp.db.postgresql.devel.patches
Message-ID <[email protected]>
Hello

this version implements syntax based on argmodes.


CREATE FUNCTION mleast(variadic numeric[]) RETURNS numeric AS $$
    SELECT min($1[i])
       FROM generate_subscripts($1,1) g(i);
$$ LANGUAGE SQL;

Regards
Pavel Stehule

2008/6/24 Tom Lane <[email protected]>:
> Andrew Dunstan <[email protected]> writes:
>> But if I have
>>   foo( a text, b int[])
>> it looks odd if both these calls are legal:
>>   foo('a',1,2,3,)
>>   foo('a',ARRAY[1,2,3])
>> which I understand would be the case with the current patch.
>
> Maybe I misunderstand what is supposed to happen, but I believe that
> if the function is marked VARIADIC then the second case would in fact
> be rejected: the signature of the function for parameter-matching
> purposes is text followed by one or more ints, never text and int[].
>
>> I'm also still curious to know how the following would be handled:
>>   foo(a text[], b text[])
>
> I think a is just text[], full stop.  Only the last parameter is
> interpreted differently for variadic.
>
> 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)
>
> since (a) this makes it much more obvious to the reader what the
> function might match, and (b) it leaves the door open for marking
> multiple parameters as variadic, if we can figure out what that means.
>
>                        regards, tom lane
>


-- 
Sent via pgsql-patches mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-patches
variadic.2.0.0.diff (text/x-patch, 30.9 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-24 16:59:42.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 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-24 13:58:31.000000000 +0200
***************
*** 570,576 ****
   * identical entries in later namespaces.
   */
  FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs)
  {
  	FuncCandidateList resultList = NULL;
  	char	   *schemaname;
--- 570,576 ----
   * identical entries in later namespaces.
   */
  FuncCandidateList
! FuncnameGetCandidates(List *names, int nargs, bool transform_variadic)
  {
  	FuncCandidateList resultList = NULL;
  	char	   *schemaname;
***************
*** 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,655 ----
  		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;
+ 					switch (procform->proargtypes.values[j])
+ 					{
+ 						case ANYOID:
+ 							va_oid = ANYOID;
+ 							break;
+ 						case ANYARRAYOID:
+ 							va_oid = ANYELEMENTOID;
+ 							break;
+ 						default:
+ 							va_oid = get_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;
  	}
  
--- 732,772 ----
  		/*
  		 * Okay to add it to result list
  		 */
! 		if (variadic && transform_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;
  	}
  
***************
*** 755,761 ****
  
  		visible = false;
  
! 		clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs);
  
  		for (; clist; clist = clist->next)
  		{
--- 821,827 ----
  
  		visible = false;
  
! 		clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs, false);
  
  		for (; clist; clist = clist->next)
  		{
*** ./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-24 12:37:26.000000000 +0200
***************
*** 173,178 ****
--- 173,179 ----
  	Datum	   *paramNames;
  	int			outCount = 0;
  	bool		have_names = false;
+ 	int			varCount = 0; 	
  	ListCell   *x;
  	int			i;
  
***************
*** 227,241 ****
  					 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;
  			outCount++;
  		}
  
  		allTypes[i] = ObjectIdGetDatum(toid);
  
  		paramModes[i] = CharGetDatum(fp->mode);
--- 228,270 ----
  					 errmsg("functions cannot accept set arguments")));
  
  		if (fp->mode != FUNC_PARAM_OUT)
+ 		{
  			inTypes[inCount++] = toid;
+ 			/* 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")));
+ 		}
  
! 		if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC)
  		{
  			if (outCount == 0)	/* save first OUT param's type */
  				*requiredResultType = toid;
  			outCount++;
  		}
  
+ 		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")));
+ 
+ 			/* check variadic parameter type */
+ 			switch (toid)
+ 			{
+ 				case ANYARRAYOID:
+ 				case ANYOID:
+ 					break;
+ 				default:
+ 					if (!OidIsValid(get_element_type(toid)))
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+ 								 errmsg("variadic argument isn't array")));
+ 			}
+ 		}
+ 
  		allTypes[i] = ObjectIdGetDatum(toid);
  
  		paramModes[i] = CharGetDatum(fp->mode);
***************
*** 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');
--- 281,287 ----
  	/* 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-24 10:32:36.000000000 +0200
***************
*** 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
--- 444,450 ----
  	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 ****
--- 4204,4210 ----
  			| 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-24 15:58:44.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,709 ****
  				Oid *funcid,	/* return value */
  				Oid *rettype,	/* return value */
  				bool *retset,	/* return value */
! 				Oid **true_typeids)		/* return value */
  {
  	FuncCandidateList raw_candidates;
  	FuncCandidateList best_candidate;
  
  	/* Get list of possible candidates from namespace search */
! 	raw_candidates = FuncnameGetCandidates(funcname, nargs);
  
  	/*
  	 * Quickly check if there is an exact match to the input datatypes (there
--- 742,756 ----
  				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;
  
  	/* Get list of possible candidates from namespace search */
! 	raw_candidates = FuncnameGetCandidates(funcname, nargs, true);
  
  	/*
  	 * Quickly check if there is an exact match to the input datatypes (there
***************
*** 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),
***************
*** 1189,1195 ****
  {
  	FuncCandidateList clist;
  
! 	clist = FuncnameGetCandidates(funcname, nargs);
  
  	while (clist)
  	{
--- 1240,1246 ----
  {
  	FuncCandidateList clist;
  
! 	clist = FuncnameGetCandidates(funcname, nargs, false);
  
  	while (clist)
  	{
*** ./src/backend/utils/adt/regproc.c.orig	2008-06-24 13:12:03.000000000 +0200
--- ./src/backend/utils/adt/regproc.c	2008-06-24 13:12:53.000000000 +0200
***************
*** 131,137 ****
  	 * pg_proc entries in the current search path.
  	 */
  	names = stringToQualifiedNameList(pro_name_or_oid);
! 	clist = FuncnameGetCandidates(names, -1);
  
  	if (clist == NULL)
  		ereport(ERROR,
--- 131,137 ----
  	 * pg_proc entries in the current search path.
  	 */
  	names = stringToQualifiedNameList(pro_name_or_oid);
! 	clist = FuncnameGetCandidates(names, -1, false);
  
  	if (clist == NULL)
  		ereport(ERROR,
***************
*** 189,195 ****
  			 * Would this proc be found (uniquely!) by regprocin? If not,
  			 * qualify it.
  			 */
! 			clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1);
  			if (clist != NULL && clist->next == NULL &&
  				clist->oid == proid)
  				nspname = NULL;
--- 189,195 ----
  			 * Would this proc be found (uniquely!) by regprocin? If not,
  			 * qualify it.
  			 */
! 			clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1, false);
  			if (clist != NULL && clist->next == NULL &&
  				clist->oid == proid)
  				nspname = NULL;
***************
*** 276,282 ****
  	 */
  	parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
  
! 	clist = FuncnameGetCandidates(names, nargs);
  
  	for (; clist; clist = clist->next)
  	{
--- 276,282 ----
  	 */
  	parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
  
! 	clist = FuncnameGetCandidates(names, nargs, false);
  
  	for (; clist; clist = clist->next)
  	{
*** ./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-24 15:09:08.000000000 +0200
***************
*** 6435,6449 ****
  		{
  			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 = "";
--- 6435,6452 ----
  		{
  			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 = "";
*** ./src/bin/psql/describe.c.orig	2008-06-24 15:17:23.000000000 +0200
--- ./src/bin/psql/describe.c	2008-06-24 15:18:26.000000000 +0200
***************
*** 187,192 ****
--- 187,193 ----
  					  "          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"
*** ./src/include/catalog/namespace.h.orig	2008-06-24 12:48:23.000000000 +0200
--- ./src/include/catalog/namespace.h	2008-06-24 12:59:36.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 */
  
***************
*** 51,57 ****
  extern Oid	TypenameGetTypid(const char *typname);
  extern bool TypeIsVisible(Oid typid);
  
! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs);
  extern bool FunctionIsVisible(Oid funcid);
  
  extern Oid	OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
--- 53,60 ----
  extern Oid	TypenameGetTypid(const char *typname);
  extern bool TypeIsVisible(Oid typid);
  
! extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs,
! 							    bool transform_variadic);
  extern bool FunctionIsVisible(Oid funcid);
  
  extern Oid	OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
*** ./src/include/catalog/pg_proc.h.orig	2008-06-24 13:33:45.000000000 +0200
--- ./src/include/catalog/pg_proc.h	2008-06-24 13:33:24.000000000 +0200
***************
*** 4466,4470 ****
--- 4466,4471 ----
  #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/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-24 16:32:32.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(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(numeric[]);
*** ./src/test/regress/expected/polymorphism.out.orig	2008-06-24 16:10:52.000000000 +0200
--- ./src/test/regress/expected/polymorphism.out	2008-06-24 16:07:12.000000000 +0200
***************
*** 613,615 ****
--- 613,658 ----
    SFUNC = add_group,
    STYPE = int8[]
  );
+ --test for variadic polymorphic function 
+ create function myleast(variadic anyarray)
+ 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(anyarray)'::regprocedure::int);
+  pg_function_is_visible 
+ ------------------------
+  t
+ (1 row)
+ 
+ drop function myleast(anyarray);
+ create or replace function concat(varchar, variadic anyarray)
+ 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, anyarray);
*** ./src/test/regress/sql/plpgsql.sql.orig	2008-06-24 16:01:55.000000000 +0200
--- ./src/test/regress/sql/plpgsql.sql	2008-06-24 16:30:50.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(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(numeric[]);
*** ./src/test/regress/sql/polymorphism.sql.orig	2008-06-24 16:01:52.000000000 +0200
--- ./src/test/regress/sql/polymorphism.sql	2008-06-24 16:04:16.000000000 +0200
***************
*** 426,428 ****
--- 426,453 ----
    SFUNC = add_group,
    STYPE = int8[]
  );
+ 
+ --test for variadic polymorphic function 
+ create function myleast(variadic anyarray)
+ 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(anyarray)'::regprocedure::int);
+ 
+ drop function myleast(anyarray);
+ 
+ create or replace function concat(varchar, variadic anyarray)
+ 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, anyarray);
+
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.