Tuesday, June 24, 2008

Re: [PATCHES] variadic function support

*** ./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);
+
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 <tgl@sss.pgh.pa.us>:
> Andrew Dunstan <andrew@dunslane.net> 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
>

No comments: