From d89737d31c03d90a2b0412e63953493576a2a3d7 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 16 Jul 2008 01:30:23 +0000 Subject: [PATCH] Support "variadic" functions, which can accept a variable number of arguments so long as all the trailing arguments are of the same (non-array) type. The function receives them as a single array argument (which is why they have to all be the same type). It might be useful to extend this facility to aggregates, but this patch doesn't do that. This patch imposes a noticeable slowdown on function lookup --- a follow-on patch will fix that by adding a redundant column to pg_proc. Pavel Stehule --- doc/src/sgml/catalogs.sgml | 16 +- doc/src/sgml/extend.sgml | 12 +- doc/src/sgml/plpgsql.sgml | 9 +- doc/src/sgml/ref/alter_function.sgml | 11 +- doc/src/sgml/ref/comment.sgml | 11 +- doc/src/sgml/ref/create_function.sgml | 7 +- doc/src/sgml/ref/drop_function.sgml | 11 +- doc/src/sgml/typeconv.sgml | 14 +- doc/src/sgml/xfunc.sgml | 136 ++++++++++++++- src/backend/catalog/information_schema.sql | 3 +- src/backend/catalog/namespace.c | 184 +++++++++++++++++---- src/backend/catalog/pg_aggregate.c | 7 +- src/backend/commands/functioncmds.c | 33 +++- src/backend/nodes/copyfuncs.c | 3 +- src/backend/nodes/equalfuncs.c | 3 +- src/backend/nodes/outfuncs.c | 3 +- src/backend/parser/gram.y | 67 +++++++- src/backend/parser/keywords.c | 3 +- src/backend/parser/parse_expr.c | 36 ++-- src/backend/parser/parse_func.c | 75 ++++++--- src/backend/parser/parse_utilcmd.c | 3 +- src/backend/utils/adt/regproc.c | 9 +- src/backend/utils/adt/ruleutils.c | 65 ++++++-- src/backend/utils/fmgr/funcapi.c | 8 +- src/bin/pg_dump/pg_dump.c | 11 +- src/bin/psql/describe.c | 5 +- src/include/catalog/catversion.h | 4 +- src/include/catalog/namespace.h | 6 +- src/include/catalog/pg_proc.h | 3 +- src/include/nodes/parsenodes.h | 6 +- src/include/parser/parse_func.h | 10 +- src/interfaces/ecpg/preproc/preproc.y | 6 +- src/pl/plpgsql/src/pl_comp.c | 9 +- src/pl/plpython/plpython.c | 8 +- src/test/regress/expected/plpgsql.out | 90 ++++++++++ src/test/regress/expected/polymorphism.out | 108 ++++++++++++ src/test/regress/sql/plpgsql.sql | 47 ++++++ src/test/regress/sql/polymorphism.sql | 43 +++++ 38 files changed, 915 insertions(+), 170 deletions(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 76198b0f83..7accea0f76 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ - + @@ -3643,7 +3643,8 @@ pg_type.oid An array with the data types of the function arguments. This includes - only input arguments (including INOUT arguments), and thus represents + only input arguments (including INOUT and + VARIADIC arguments), and thus represents the call signature of the function @@ -3654,8 +3655,9 @@ pg_type.oid An array with the data types of the function arguments. This includes - all arguments (including OUT and INOUT arguments); however, if all the - arguments are IN arguments, this field will be null. + all arguments (including OUT and + INOUT arguments); however, if all the + arguments are IN arguments, this field will be null. Note that subscripting is 1-based, whereas for historical reasons proargtypes is subscripted from 0 @@ -3669,8 +3671,10 @@ An array with the modes of the function arguments, encoded as i for IN arguments, o for OUT arguments, - b for INOUT arguments. - If all the arguments are IN arguments, this field will be null. + b for INOUT arguments, + v for VARIADIC arguments. + If all the arguments are IN arguments, + this field will be null. Note that subscripts correspond to positions of proallargtypes not proargtypes diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index e2805b4191..caeda7e75c 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -1,4 +1,4 @@ - + Extending <acronym>SQL</acronym> @@ -254,6 +254,16 @@ is equivalent to declaring it as f(anyenum, anyenum): both actual arguments have to be the same enum type. + + + A variadic function (one taking a variable number of arguments, as in + ) can be + polymorphic: this is accomplished by declaring its last parameter as + VARIADIC anyarray. For purposes of argument + matching and determining the actual result type, such a function behaves + the same as if you had written the appropriate number of + anynonarray parameters. + diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index cd1531999b..42bd6048b6 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ - + <application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language @@ -121,6 +121,13 @@ calling query, as discussed in . + + PL/pgSQL functions can be declared to accept a variable + number of arguments by using the VARIADIC marker. This + works exactly the same way as for SQL functions, as discussed in + . + + PL/pgSQL functions can also be declared to accept and return the polymorphic types diff --git a/doc/src/sgml/ref/alter_function.sgml b/doc/src/sgml/ref/alter_function.sgml index bee2f6f439..abedfe7d50 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -1,5 +1,5 @@ @@ -81,13 +81,14 @@ where action is one of: - The mode of an argument: either IN, OUT, - or INOUT. If omitted, the default is IN. + The mode of an argument: IN, OUT, + INOUT, or VARIADIC. + If omitted, the default is IN. Note that ALTER FUNCTION does not actually pay any attention to OUT arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the IN and INOUT - arguments. + So it is sufficient to list the IN, INOUT, + and VARIADIC arguments. diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index c8993e915b..2da64f87ac 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -1,5 +1,5 @@ @@ -136,13 +136,14 @@ COMMENT ON - The mode of a function argument: either IN, OUT, - or INOUT. If omitted, the default is IN. + The mode of a function argument: IN, OUT, + INOUT, or VARIADIC. + If omitted, the default is IN. Note that COMMENT ON FUNCTION does not actually pay any attention to OUT arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the IN and INOUT - arguments. + So it is sufficient to list the IN, INOUT, + and VARIADIC arguments. diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml index 8c542982d5..18b9bf7bee 100644 --- a/doc/src/sgml/ref/create_function.sgml +++ b/doc/src/sgml/ref/create_function.sgml @@ -1,5 +1,5 @@ @@ -101,8 +101,9 @@ CREATE [ OR REPLACE ] FUNCTION - The mode of an argument: either IN, OUT, - or INOUT. If omitted, the default is IN. + The mode of an argument: IN, OUT, + INOUT, or VARIADIC. + If omitted, the default is IN. diff --git a/doc/src/sgml/ref/drop_function.sgml b/doc/src/sgml/ref/drop_function.sgml index bf39f5356d..256cafe684 100644 --- a/doc/src/sgml/ref/drop_function.sgml +++ b/doc/src/sgml/ref/drop_function.sgml @@ -1,5 +1,5 @@ @@ -65,13 +65,14 @@ DROP FUNCTION [ IF EXISTS ] name ( - The mode of an argument: either IN, OUT, - or INOUT. If omitted, the default is IN. + The mode of an argument: IN, OUT, + INOUT, or VARIADIC. + If omitted, the default is IN. Note that DROP FUNCTION does not actually pay any attention to OUT arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the IN and INOUT - arguments. + So it is sufficient to list the IN, INOUT, + and VARIADIC arguments. diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index 451555cd02..4f04801210 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -1,4 +1,4 @@ - + Type Conversion @@ -503,6 +503,18 @@ different argument types are considered on an equal footing regardless of search path position. + + +If a function is declared with a VARIADIC array parameter, and +the call does not use the VARIADIC keyword, then the function +is treated as if the array parameter were replaced by one or more occurrences +of its element type, as needed to match the call. After such expansion the +function might have effective argument types identical to some non-variadic +function. In that case the function appearing earlier in the search path is +used, or if the two functions are in the same schema, the non-variadic one is +selected. + + diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index efee5d8d46..55ed719ec6 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,4 +1,4 @@ - + User-Defined Functions @@ -495,7 +495,7 @@ SELECT getname(new_emp()); None (1 row) - + Still another way to use a function that returns a composite type is to @@ -505,7 +505,7 @@ SELECT getname(new_emp()); - Functions with Output Parameters + <acronym>SQL</> Functions with Output Parameters function @@ -578,9 +578,75 @@ DROP FUNCTION sum_n_product (int, int); Parameters can be marked as IN (the default), - OUT, or INOUT. An INOUT + OUT, INOUT, or VARIADIC. + An INOUT parameter serves as both an input parameter (part of the calling argument list) and an output parameter (part of the result record type). + VARIADIC parameters are input parameters, but are treated + specially as described next. + + + + + <acronym>SQL</> Functions with Variable Numbers of Arguments + + + function + variadic + + + + variadic function + + + + SQL functions can be declared to accept + variable numbers of arguments, so long as all the optional + arguments are of the same data type. The optional arguments will be + passed to the function as an array. The function is declared by + marking the last parameter as VARIADIC; this parameter + must be declared as being of an array type. For example: + + +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.4); + mleast +-------- + -1 +(1 row) + + + Effectively, all the actual arguments at or beyond the + VARIADIC position are gathered up into a one-dimensional + array, as if you had written + + +SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work + + + You can't actually write that, though — or at least, it will + not match this function definition. A parameter marked + VARIADIC matches one or more occurrences of its element + type, not of its own type. + + + + Sometimes it is useful to be able to pass an already-constructed array + to a variadic function; this is particularly handy when one variadic + function wants to pass on its array parameter to another one. You can + do that by specifying VARIADIC in the call: + + +SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]); + + + This prevents expansion of the function's variadic parameter into its + element type, thereby allowing the array argument value to match + normally. VARIADIC can only be attached to the last + actual argument of a function call. @@ -795,13 +861,45 @@ DETAIL: A function returning a polymorphic type must have at least one polymorp For example: CREATE FUNCTION dup (f1 anyelement, OUT f2 anyelement, OUT f3 anyarray) -AS 'select $1, array[$1,$1]' LANGUAGE sql; +AS 'select $1, array[$1,$1]' LANGUAGE SQL; SELECT * FROM dup(22); f2 | f3 ----+--------- 22 | {22,22} (1 row) + + + + + Polymorphism can also be used with variadic functions. + For example: + +CREATE FUNCTION anyleast (VARIADIC anyarray) RETURNS anyelement AS $$ + SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i); +$$ LANGUAGE SQL; + +SELECT anyleast(10, -1, 5, 4); + anyleast +---------- + -1 +(1 row) + +SELECT anyleast('abc'::text, 'def'); + anyleast +---------- + abc +(1 row) + +CREATE FUNCTION concat(text, VARIADIC anyarray) RETURNS text AS $$ + SELECT array_to_string($2, $1); +$$ LANGUAGE SQL; + +SELECT concat('|', 1, 4, 2); + concat +-------- + 1|4|2 +(1 row) @@ -852,6 +950,16 @@ CREATE FUNCTION test(smallint, double precision) RETURNS ... avoid the problem by not choosing conflicting names. + + Another possible conflict is between variadic and non-variadic functions. + For instance, it is possible to create both foo(numeric) and + foo(VARIADIC numeric[]). In this case it is unclear which one + should be matched to a call providing a single numeric argument, such as + foo(10.1). The rule is that the function appearing + earlier in the search path is used, or if the two functions are in the + same schema, the non-variadic one is preferred. + + When overloading C-language functions, there is an additional constraint: The C name of each function in the family of @@ -2952,7 +3060,25 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray LANGUAGE C IMMUTABLE; + + + There is a variant of polymorphism that is only available to C-language + functions: they can be declared to take parameters of type + "any". (Note that this type name must be double-quoted, + since it's also a SQL reserved word.) This works like + anyelement except that it does not constrain different + "any" arguments to be the same type, nor do they help + determine the function's result type. A C-language function can also + declare its final parameter to be VARIADIC "any". This will + match one or more actual arguments of any type (not necessarily the same + type). These arguments will not be gathered into an array + as happens with normal variadic functions; they will just be passed to + the function separately. The PG_NARGS() macro and the + methods described above must be used to determine the number of actual + arguments and their types when using this feature. + + Shared Memory and LWLocks diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index a9566d9d3f..0e2452fa0f 100644 --- a/src/backend/catalog/information_schema.sql +++ b/src/backend/catalog/information_schema.sql @@ -4,7 +4,7 @@ * * Copyright (c) 2003-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.43 2008/01/01 19:45:48 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/information_schema.sql,v 1.44 2008/07/16 01:30:21 tgl Exp $ */ /* @@ -1006,6 +1006,7 @@ CREATE VIEW parameters AS WHEN proargmodes[(ss.x).n] = 'i' THEN 'IN' WHEN proargmodes[(ss.x).n] = 'o' THEN 'OUT' WHEN proargmodes[(ss.x).n] = 'b' THEN 'INOUT' + WHEN proargmodes[(ss.x).n] = 'v' THEN 'IN' END AS character_data) AS parameter_mode, CAST('NO' AS character_data) AS is_result, CAST('NO' AS character_data) AS as_locator, diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index dfaea41cfe..48b8ee45e6 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -13,7 +13,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.107 2008/07/01 02:09:34 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.108 2008/07/16 01:30:21 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,6 +38,7 @@ #include "commands/dbcommands.h" #include "miscadmin.h" #include "nodes/makefuncs.h" +#include "parser/parse_func.h" #include "storage/backendid.h" #include "storage/ipc.h" #include "utils/acl.h" @@ -561,24 +562,36 @@ TypeIsVisible(Oid typid) * retrieve a list of the possible matches. * * If nargs is -1, we return all functions matching the given name, - * regardless of argument count. + * regardless of argument count. (expand_variadic must be false in this case.) + * + * If expand_variadic is true, then variadic functions having the same number + * or fewer arguments will be retrieved, with the variadic argument and any + * additional argument positions filled with the variadic element type. + * nvargs in the returned struct is set to the number of such arguments. + * If expand_variadic is false, variadic arguments are not treated specially, + * and the returned nvargs will always be zero. * * We search a single namespace if the function name is qualified, else * all namespaces in the search path. The return list will never contain * multiple entries with identical argument lists --- in the multiple- * namespace case, we arrange for entries in earlier namespaces to mask - * identical entries in later namespaces. + * identical entries in later namespaces. We also arrange for non-variadic + * functions to mask variadic ones if the expanded argument list is the same. */ FuncCandidateList -FuncnameGetCandidates(List *names, int nargs) +FuncnameGetCandidates(List *names, int nargs, bool expand_variadic) { FuncCandidateList resultList = NULL; + bool any_variadic = false; char *schemaname; char *funcname; Oid namespaceId; CatCList *catlist; int i; + /* check for caller error */ + Assert(nargs >= 0 || !expand_variadic); + /* deconstruct the name list */ DeconstructQualifiedName(names, &schemaname, &funcname); @@ -604,11 +617,57 @@ FuncnameGetCandidates(List *names, int nargs) HeapTuple proctup = &catlist->members[i]->tuple; Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup); int pronargs = procform->pronargs; + int effective_nargs; int pathpos = 0; + bool variadic = false; + Oid va_elem_type = InvalidOid; FuncCandidateList newResult; + /* + * Check if function is variadic, and get variadic element type if so. + * If expand_variadic is false, we can just ignore variadic-ness. + * + * XXX it's annoying to inject something as expensive as this even + * when there are no variadic functions involved. Find a better way. + */ + if (expand_variadic) + { + Datum proargmodes; + bool isnull; + + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + int j; + + argmodes = ARR_DATA_PTR(ar); + j = ARR_DIMS(ar)[0] - 1; + if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC) + { + variadic = any_variadic = true; + switch (procform->proargtypes.values[j]) + { + case ANYOID: + va_elem_type = ANYOID; + break; + case ANYARRAYOID: + va_elem_type = ANYELEMENTOID; + break; + default: + va_elem_type = get_element_type(procform->proargtypes.values[j]); + Assert(OidIsValid(va_elem_type)); + break; + } + } + } + } + /* Ignore if it doesn't match requested argument count */ - if (nargs >= 0 && pronargs != nargs) + if (nargs >= 0 && + (variadic ? (pronargs > nargs) : (pronargs != nargs))) continue; if (OidIsValid(namespaceId)) @@ -616,7 +675,6 @@ FuncnameGetCandidates(List *names, int nargs) /* Consider only procs in specified namespace */ if (procform->pronamespace != namespaceId) continue; - /* No need to check args, they must all be different */ } else { @@ -635,28 +693,63 @@ FuncnameGetCandidates(List *names, int nargs) } if (nsp == NULL) continue; /* proc is not in search path */ + } + /* + * We must compute the effective argument list so that we can easily + * compare it to earlier results. We waste a palloc cycle if it gets + * masked by an earlier result, but really that's a pretty infrequent + * case so it's not worth worrying about. + */ + effective_nargs = Max(pronargs, nargs); + newResult = (FuncCandidateList) + palloc(sizeof(struct _FuncCandidateList) - sizeof(Oid) + + effective_nargs * sizeof(Oid)); + newResult->pathpos = pathpos; + newResult->oid = HeapTupleGetOid(proctup); + newResult->nargs = effective_nargs; + memcpy(newResult->args, procform->proargtypes.values, + pronargs * sizeof(Oid)); + if (variadic) + { + int i; + + newResult->nvargs = effective_nargs - pronargs + 1; + /* Expand variadic argument into N copies of element type */ + for (i = pronargs - 1; i < effective_nargs; i++) + newResult->args[i] = va_elem_type; + } + else + newResult->nvargs = 0; + + /* + * Does it have the same arguments as something we already accepted? + * If so, decide which one to keep. We can skip this check for the + * single-namespace case if no variadic match has been made, since + * then the unique index on pg_proc guarantees all the matches have + * different argument lists. + */ + if (any_variadic || !OidIsValid(namespaceId)) + { /* - * Okay, it's in the search path, but does it have the same - * arguments as something we already accepted? If so, keep only - * the one that appears earlier in the search path. - * * If we have an ordered list from SearchSysCacheList (the normal * case), then any conflicting proc must immediately adjoin this * one in the list, so we only need to look at the newest result * item. If we have an unordered list, we have to scan the whole - * result list. + * result list. Also, if either the current candidate or any + * previous candidate is a variadic match, we can't assume that + * conflicts are adjacent. */ if (resultList) { FuncCandidateList prevResult; - if (catlist->ordered) + if (catlist->ordered && !any_variadic) { - if (pronargs == resultList->nargs && - memcmp(procform->proargtypes.values, + if (effective_nargs == resultList->nargs && + memcmp(newResult->args, resultList->args, - pronargs * sizeof(Oid)) == 0) + effective_nargs * sizeof(Oid)) == 0) prevResult = resultList; else prevResult = NULL; @@ -667,22 +760,58 @@ FuncnameGetCandidates(List *names, int nargs) prevResult; prevResult = prevResult->next) { - if (pronargs == prevResult->nargs && - memcmp(procform->proargtypes.values, + if (effective_nargs == prevResult->nargs && + memcmp(newResult->args, prevResult->args, - pronargs * sizeof(Oid)) == 0) + effective_nargs * sizeof(Oid)) == 0) break; } } if (prevResult) { - /* We have a match with a previous result */ - Assert(pathpos != prevResult->pathpos); + /* + * We have a match with a previous result. Prefer the + * one that's earlier in the search path. + */ if (pathpos > prevResult->pathpos) + { + pfree(newResult); continue; /* keep previous result */ + } + else if (pathpos == prevResult->pathpos) + { + /* + * With variadic functions we could have, for example, + * both foo(numeric) and foo(variadic numeric[]) in + * the same namespace; if so we prefer the + * non-variadic match on efficiency grounds. It's + * also possible to have conflicting variadic + * functions, such as foo(numeric, variadic numeric[]) + * and foo(variadic numeric[]). If you're silly + * enough to do that, we throw an error. (XXX It'd be + * better to detect such conflicts when the functions + * are created.) + */ + if (variadic) + { + if (prevResult->nvargs > 0) + ereport(ERROR, + (errcode(ERRCODE_AMBIGUOUS_FUNCTION), + errmsg("variadic function %s conflicts with another", + func_signature_string(names, pronargs, + procform->proargtypes.values)))); + /* else, previous result wasn't variadic */ + pfree(newResult); + continue; /* keep previous result */ + } + /* non-variadic can replace a previous variadic */ + Assert(prevResult->nvargs > 0); + } /* replace previous result */ prevResult->pathpos = pathpos; - prevResult->oid = HeapTupleGetOid(proctup); + prevResult->oid = newResult->oid; + prevResult->nvargs = newResult->nvargs; + pfree(newResult); continue; /* args are same, of course */ } } @@ -691,15 +820,6 @@ FuncnameGetCandidates(List *names, int nargs) /* * 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; } @@ -755,7 +875,8 @@ FunctionIsVisible(Oid funcid) visible = false; - clist = FuncnameGetCandidates(list_make1(makeString(proname)), nargs); + clist = FuncnameGetCandidates(list_make1(makeString(proname)), + nargs, false); for (; clist; clist = clist->next) { @@ -1023,6 +1144,7 @@ OpernameGetCandidates(List *names, char oprkind) newResult->pathpos = pathpos; newResult->oid = HeapTupleGetOid(opertup); newResult->nargs = 2; + newResult->nvargs = 0; newResult->args[0] = operform->oprleft; newResult->args[1] = operform->oprright; newResult->next = resultList; diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 1ff7261877..e1c67ce5cd 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.93 2008/06/19 00:46:04 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.94 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -293,6 +293,7 @@ lookup_agg_function(List *fnName, { Oid fnOid; bool retset; + int nvargs; Oid *true_oid_array; FuncDetailCode fdresult; AclResult aclresult; @@ -305,8 +306,8 @@ lookup_agg_function(List *fnName, * function's return value. it also returns the true argument types to * the function. */ - fdresult = func_get_detail(fnName, NIL, nargs, input_types, - &fnOid, rettype, &retset, + fdresult = func_get_detail(fnName, NIL, nargs, input_types, false, + &fnOid, rettype, &retset, &nvargs, &true_oid_array); /* only valid case is a normal function not returning a set */ diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 9831692eea..cb249d9c7d 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.95 2008/07/12 10:44:56 petere Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.96 2008/07/16 01:30:22 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -173,6 +173,7 @@ examine_parameter_list(List *parameters, Oid languageOid, Datum *paramModes; Datum *paramNames; int outCount = 0; + int varCount = 0; bool have_names = false; ListCell *x; int i; @@ -228,15 +229,41 @@ examine_parameter_list(List *parameters, Oid languageOid, errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) + { + /* only OUT parameters can follow a VARIADIC parameter */ + if (varCount > 0) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("VARIADIC parameter must be the last input parameter"))); inTypes[inCount++] = toid; + } - if (fp->mode != FUNC_PARAM_IN) + 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) + { + varCount++; + /* validate variadic parameter type */ + switch (toid) + { + case ANYARRAYOID: + case ANYOID: + /* okay */ + break; + default: + if (!OidIsValid(get_element_type(toid))) + ereport(ERROR, + (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), + errmsg("VARIADIC parameter must be an array"))); + break; + } + } + allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); @@ -253,7 +280,7 @@ examine_parameter_list(List *parameters, Oid languageOid, /* Now construct the proper outputs as needed */ *parameterTypes = buildoidvector(inTypes, inCount); - if (outCount > 0) + if (outCount > 0 || varCount > 0) { *allParameterTypes = construct_array(allTypes, parameterCount, OIDOID, sizeof(Oid), true, 'i'); diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index a42c40327f..2e1ce4cb0d 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.395 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1651,6 +1651,7 @@ _copyFuncCall(FuncCall *from) COPY_NODE_FIELD(args); COPY_SCALAR_FIELD(agg_star); COPY_SCALAR_FIELD(agg_distinct); + COPY_SCALAR_FIELD(func_variadic); COPY_SCALAR_FIELD(location); return newnode; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 435ee6a6af..41999226b6 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.324 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1705,6 +1705,7 @@ _equalFuncCall(FuncCall *a, FuncCall *b) COMPARE_NODE_FIELD(args); COMPARE_SCALAR_FIELD(agg_star); COMPARE_SCALAR_FIELD(agg_distinct); + COMPARE_SCALAR_FIELD(func_variadic); COMPARE_SCALAR_FIELD(location); return true; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 51b46a83ed..a03063ce1e 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.326 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.327 2008/07/16 01:30:22 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1610,6 +1610,7 @@ _outFuncCall(StringInfo str, FuncCall *node) WRITE_NODE_FIELD(args); WRITE_BOOL_FIELD(agg_star); WRITE_BOOL_FIELD(agg_distinct); + WRITE_BOOL_FIELD(func_variadic); WRITE_INT_FIELD(location); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 4e59e37da9..70bbe940af 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.616 2008/06/15 01:25:54 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.617 2008/07/16 01:30:22 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -444,7 +444,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING - VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING + VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE @@ -4200,10 +4200,11 @@ func_arg: ; /* INOUT is SQL99 standard, IN OUT is for Oracle compatibility */ -arg_class: IN_P { $$ = FUNC_PARAM_IN; } - | OUT_P { $$ = FUNC_PARAM_OUT; } - | INOUT { $$ = FUNC_PARAM_INOUT; } - | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } +arg_class: IN_P { $$ = FUNC_PARAM_IN; } + | OUT_P { $$ = FUNC_PARAM_OUT; } + | INOUT { $$ = FUNC_PARAM_INOUT; } + | IN_P OUT_P { $$ = FUNC_PARAM_INOUT; } + | VARIADIC { $$ = FUNC_PARAM_VARIADIC; } ; /* @@ -7336,6 +7337,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, $1); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @2; $$ = (Node *) n; } @@ -7394,6 +7396,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @4; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~", $1, (Node *) n, @2); } @@ -7406,6 +7409,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~", $1, (Node *) n, @2); } @@ -7418,6 +7422,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($3, $5); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @4; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~~*", $1, (Node *) n, @2); } @@ -7430,6 +7435,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~~*", $1, (Node *) n, @2); } @@ -7441,6 +7447,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, makeNullAConst()); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @2; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } @@ -7451,6 +7458,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($4, $6); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "~", $1, (Node *) n, @2); } @@ -7461,6 +7469,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, makeNullAConst()); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @5; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } @@ -7471,6 +7480,7 @@ a_expr: c_expr { $$ = $1; } n->args = list_make2($5, $7); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @6; $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "!~", $1, (Node *) n, @2); } @@ -7862,6 +7872,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7872,6 +7883,29 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; + n->location = @1; + $$ = (Node *)n; + } + | func_name '(' VARIADIC a_expr ')' + { + FuncCall *n = makeNode(FuncCall); + n->funcname = $1; + n->args = list_make1($4); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = TRUE; + n->location = @1; + $$ = (Node *)n; + } + | func_name '(' expr_list ',' VARIADIC a_expr ')' + { + FuncCall *n = makeNode(FuncCall); + n->funcname = $1; + n->args = lappend($3, $6); + n->agg_star = FALSE; + n->agg_distinct = FALSE; + n->func_variadic = TRUE; n->location = @1; $$ = (Node *)n; } @@ -7886,6 +7920,7 @@ func_expr: func_name '(' ')' * "must be an aggregate", but there's no provision * for that in FuncCall at the moment. */ + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7896,6 +7931,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = TRUE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7916,6 +7952,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = TRUE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -7974,6 +8011,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8043,6 +8081,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8053,6 +8092,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8063,6 +8103,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8073,6 +8114,7 @@ func_expr: func_name '(' ')' n->args = NIL; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8085,6 +8127,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8100,6 +8143,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8111,6 +8155,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8124,6 +8169,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8143,6 +8189,7 @@ func_expr: func_name '(' ')' n->args = list_make1($3); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8156,6 +8203,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8166,6 +8214,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8176,6 +8225,7 @@ func_expr: func_name '(' ')' n->args = $4; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -8186,6 +8236,7 @@ func_expr: func_name '(' ')' n->args = $3; n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = @1; $$ = (Node *)n; } @@ -9362,6 +9413,7 @@ reserved_keyword: | UNIQUE | USER | USING + | VARIADIC | WHEN | WHERE ; @@ -9566,6 +9618,7 @@ makeOverlaps(List *largs, List *rargs, int location) n->args = list_concat(largs, rargs); n->agg_star = FALSE; n->agg_distinct = FALSE; + n->func_variadic = FALSE; n->location = location; return n; } @@ -9625,7 +9678,7 @@ extractArgTypes(List *parameters) { FunctionParameter *p = (FunctionParameter *) lfirst(i); - if (p->mode != FUNC_PARAM_OUT) /* keep if IN or INOUT */ + if (p->mode != FUNC_PARAM_OUT) /* keep if IN, INOUT, VARIADIC */ result = lappend(result, p->argType); } return result; diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 43013e1e77..97fba9c956 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.198 2008/07/03 20:58:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.199 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -393,6 +393,7 @@ const ScanKeyword ScanKeywords[] = { {"value", VALUE_P, UNRESERVED_KEYWORD}, {"values", VALUES, COL_NAME_KEYWORD}, {"varchar", VARCHAR, COL_NAME_KEYWORD}, + {"variadic", VARIADIC, RESERVED_KEYWORD}, {"varying", VARYING, UNRESERVED_KEYWORD}, {"verbose", VERBOSE, TYPE_FUNC_NAME_KEYWORD}, {"version", VERSION_P, UNRESERVED_KEYWORD}, diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 362108ba3f..8addb53e51 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.228 2008/04/29 14:59:16 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.229 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -358,8 +358,8 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection) result = ParseFuncOrColumn(pstate, list_make1(n), list_make1(result), - false, false, true, - -1); + false, false, false, + true, -1); } } /* process trailing subscripts, if any */ @@ -481,8 +481,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name2)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -511,8 +511,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name3)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -552,8 +552,8 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref) node = ParseFuncOrColumn(pstate, list_make1(makeString(name4)), list_make1(node), - false, false, true, - cref->location); + false, false, false, + true, cref->location); } break; } @@ -1018,25 +1018,21 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) List *targs; ListCell *args; - /* - * Transform the list of arguments. We use a shallow list copy and then - * transform-in-place to avoid O(N^2) behavior from repeated lappend's. - * - * XXX: repeated lappend() would no longer result in O(n^2) behavior; - * worth reconsidering this design? - */ - targs = list_copy(fn->args); - foreach(args, targs) + /* Transform the list of arguments ... */ + targs = NIL; + foreach(args, fn->args) { - lfirst(args) = transformExpr(pstate, - (Node *) lfirst(args)); + targs = lappend(targs, transformExpr(pstate, + (Node *) lfirst(args))); } + /* ... and hand off to ParseFuncOrColumn */ return ParseFuncOrColumn(pstate, fn->funcname, targs, fn->agg_star, fn->agg_distinct, + fn->func_variadic, false, fn->location); } diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 3d44c2520b..3bb5c452a8 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.202 2008/03/26 21:10:38 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.203 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -56,14 +56,14 @@ static void unknown_attribute(ParseState *pstate, Node *relref, char *attname, * intended to be used only to deliver an appropriate error message, * not to affect the semantics. When is_column is true, we should have * a single argument (the putative table), unqualified function name - * equal to the column name, and no aggregate decoration. + * equal to the column name, and no aggregate or variadic decoration. * * The argument expressions (in fargs) must have been transformed already. */ Node * ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, - bool agg_star, bool agg_distinct, bool is_column, - int location) + bool agg_star, bool agg_distinct, bool func_variadic, + bool is_column, int location) { Oid rettype; Oid funcid; @@ -75,6 +75,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, Oid *declared_arg_types; Node *retval; bool retset; + int nvargs; FuncDetailCode fdresult; /* @@ -126,9 +127,10 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * Check for column projection: if function has one argument, and that * argument is of complex type, and function name is not qualified, then * the "function call" could be a projection. We also check that there - * wasn't any aggregate decoration. + * wasn't any aggregate or variadic decoration. */ - if (nargs == 1 && !agg_star && !agg_distinct && list_length(funcname) == 1) + if (nargs == 1 && !agg_star && !agg_distinct && !func_variadic && + list_length(funcname) == 1) { Oid argtype = actual_arg_types[0]; @@ -153,11 +155,15 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, * func_get_detail looks up the function in the catalogs, does * disambiguation for polymorphic functions, handles inheritance, and * returns the funcid and type and set or singleton status of the - * function's return value. it also returns the true argument types to - * the function. + * function's return value. It also returns the true argument types to + * the function. (In the case of a variadic function call, the reported + * "true" types aren't really what is in pg_proc: the variadic argument is + * replaced by a suitable number of copies of its element type. We'll fix + * it up below.) */ fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types, - &funcid, &rettype, &retset, + !func_variadic, + &funcid, &rettype, &retset, &nvargs, &declared_arg_types); if (fdresult == FUNCDETAIL_COERCION) { @@ -242,6 +248,34 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, /* perform the necessary typecasting of arguments */ make_fn_arguments(pstate, fargs, actual_arg_types, declared_arg_types); + /* + * If it's a variadic function call, transform the last nvargs arguments + * into an array --- unless it's an "any" variadic. + */ + if (nvargs > 0 && declared_arg_types[nargs - 1] != ANYOID) + { + ArrayExpr *newa = makeNode(ArrayExpr); + int non_var_args = nargs - nvargs; + List *vargs; + + Assert(non_var_args >= 0); + vargs = list_copy_tail(fargs, non_var_args); + fargs = list_truncate(fargs, non_var_args); + + newa->elements = vargs; + /* assume all the variadic arguments were coerced to the same type */ + newa->element_typeid = exprType((Node *) linitial(vargs)); + newa->array_typeid = get_array_type(newa->element_typeid); + if (!OidIsValid(newa->array_typeid)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(newa->element_typeid)))); + newa->multidims = false; + + fargs = lappend(fargs, newa); + } + /* build the appropriate output structure */ if (fdresult == FUNCDETAIL_NORMAL) { @@ -668,21 +702,12 @@ func_select_candidate(int nargs, * Find the named function in the system catalogs. * * Attempt to find the named function in the system catalogs with - * arguments exactly as specified, so that the normal case - * (exact match) is as quick as possible. + * arguments exactly as specified, so that the normal case (exact match) + * is as quick as possible. * * If an exact match isn't found: * 1) check for possible interpretation as a type coercion request - * 2) get a vector of all possible input arg type arrays constructed - * from the superclasses of the original input arg types - * 3) get a list of all possible argument type arrays to the function - * with given name and number of arguments - * 4) for each input arg type array from vector #1: - * a) find how many of the function arg type arrays from list #2 - * it can be coerced to - * b) if the answer is one, we have our function - * c) if the answer is more than one, attempt to resolve the conflict - * d) if the answer is zero, try the next array from vector #1 + * 2) apply the ambiguous-function resolution rules * * Note: we rely primarily on nargs/argtypes as the argument description. * The actual expression node list is passed in fargs so that we can check @@ -694,16 +719,18 @@ func_get_detail(List *funcname, List *fargs, int nargs, Oid *argtypes, + bool expand_variadic, Oid *funcid, /* return value */ Oid *rettype, /* return value */ bool *retset, /* return value */ + int *nvargs, /* 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); + raw_candidates = FuncnameGetCandidates(funcname, nargs, expand_variadic); /* * Quickly check if there is an exact match to the input datatypes (there @@ -786,6 +813,7 @@ func_get_detail(List *funcname, *funcid = InvalidOid; *rettype = targetType; *retset = false; + *nvargs = 0; *true_typeids = argtypes; return FUNCDETAIL_COERCION; } @@ -835,6 +863,7 @@ func_get_detail(List *funcname, FuncDetailCode result; *funcid = best_candidate->oid; + *nvargs = best_candidate->nvargs; *true_typeids = best_candidate->args; ftup = SearchSysCache(PROCOID, @@ -1189,7 +1218,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) { FuncCandidateList clist; - clist = FuncnameGetCandidates(funcname, nargs); + clist = FuncnameGetCandidates(funcname, nargs, false); while (clist) { diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 33f5b8015f..bbdea4642c 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -19,7 +19,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.13 2008/04/29 14:59:17 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.14 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -388,6 +388,7 @@ transformColumnDefinition(ParseState *pstate, CreateStmtContext *cxt, funccallnode->args = list_make1(castnode); funccallnode->agg_star = false; funccallnode->agg_distinct = false; + funccallnode->func_variadic = false; funccallnode->location = -1; constraint = makeNode(Constraint); diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c index 986bac041d..d50dc23d77 100644 --- a/src/backend/utils/adt/regproc.c +++ b/src/backend/utils/adt/regproc.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.107 2008/06/19 00:46:05 alvherre Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.108 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -131,7 +131,7 @@ regprocin(PG_FUNCTION_ARGS) * pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid); - clist = FuncnameGetCandidates(names, -1); + clist = FuncnameGetCandidates(names, -1, false); if (clist == NULL) ereport(ERROR, @@ -189,7 +189,8 @@ regprocout(PG_FUNCTION_ARGS) * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ - clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1); + clist = FuncnameGetCandidates(list_make1(makeString(proname)), + -1, false); if (clist != NULL && clist->next == NULL && clist->oid == proid) nspname = NULL; @@ -276,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS) */ parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes); - clist = FuncnameGetCandidates(names, nargs); + clist = FuncnameGetCandidates(names, nargs, false); for (; clist; clist = clist->next) { diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 3754b4981e..dc4a6cc4a8 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.275 2008/06/06 17:59:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.276 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -193,7 +193,8 @@ static Node *processIndirection(Node *node, deparse_context *context, bool printit); static void printSubscripts(ArrayRef *aref, deparse_context *context); static char *generate_relation_name(Oid relid); -static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes); +static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes, + bool *is_variadic); static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2); static text *string_to_text(char *str); static char *flatten_reloptions(Oid relid); @@ -531,7 +532,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS) appendStringInfo(&buf, "FOR EACH STATEMENT "); appendStringInfo(&buf, "EXECUTE PROCEDURE %s(", - generate_function_name(trigrec->tgfoid, 0, NULL)); + generate_function_name(trigrec->tgfoid, 0, NULL, NULL)); if (trigrec->tgnargs > 0) { @@ -4293,6 +4294,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context, Oid funcoid = expr->funcid; Oid argtypes[FUNC_MAX_ARGS]; int nargs; + bool is_variadic; ListCell *l; /* @@ -4343,8 +4345,17 @@ get_func_expr(FuncExpr *expr, deparse_context *context, } appendStringInfo(buf, "%s(", - generate_function_name(funcoid, nargs, argtypes)); - get_rule_expr((Node *) expr->args, context, true); + generate_function_name(funcoid, nargs, argtypes, + &is_variadic)); + nargs = 0; + foreach(l, expr->args) + { + if (nargs++ > 0) + appendStringInfoString(buf, ", "); + if (is_variadic && lnext(l) == NULL) + appendStringInfoString(buf, "VARIADIC "); + get_rule_expr((Node *) lfirst(l), context, true); + } appendStringInfoChar(buf, ')'); } @@ -4371,7 +4382,8 @@ get_agg_expr(Aggref *aggref, deparse_context *context) } appendStringInfo(buf, "%s(%s", - generate_function_name(aggref->aggfnoid, nargs, argtypes), + generate_function_name(aggref->aggfnoid, + nargs, argtypes, NULL), aggref->aggdistinct ? "DISTINCT " : ""); /* aggstar can be set only in zero-argument aggregates */ if (aggref->aggstar) @@ -5329,10 +5341,12 @@ generate_relation_name(Oid relid) * given that it is being called with the specified actual arg types. * (Arg types matter because of ambiguous-function resolution rules.) * - * The result includes all necessary quoting and schema-prefixing. + * The result includes all necessary quoting and schema-prefixing. We can + * also pass back an indication of whether the function is variadic. */ static char * -generate_function_name(Oid funcid, int nargs, Oid *argtypes) +generate_function_name(Oid funcid, int nargs, Oid *argtypes, + bool *is_variadic) { HeapTuple proctup; Form_pg_proc procform; @@ -5343,6 +5357,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) Oid p_funcid; Oid p_rettype; bool p_retset; + int p_nvargs; Oid *p_true_typeids; proctup = SearchSysCache(PROCOID, @@ -5352,7 +5367,7 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) elog(ERROR, "cache lookup failed for function %u", funcid); procform = (Form_pg_proc) GETSTRUCT(proctup); proname = NameStr(procform->proname); - Assert(nargs == procform->pronargs); + Assert(nargs >= procform->pronargs); /* * The idea here is to schema-qualify only if the parser would fail to @@ -5360,9 +5375,9 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) * specified argtypes. */ p_result = func_get_detail(list_make1(makeString(proname)), - NIL, nargs, argtypes, + NIL, nargs, argtypes, false, &p_funcid, &p_rettype, - &p_retset, &p_true_typeids); + &p_retset, &p_nvargs, &p_true_typeids); if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) && p_funcid == funcid) nspname = NULL; @@ -5371,6 +5386,34 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes) result = quote_qualified_identifier(nspname, proname); + /* Check variadic-ness if caller cares */ + if (is_variadic) + { + /* XXX change this if we simplify code in FuncnameGetCandidates */ + Datum proargmodes; + bool isnull; + + *is_variadic = false; + + proargmodes = SysCacheGetAttr(PROCOID, proctup, + Anum_pg_proc_proargmodes, &isnull); + if (!isnull) + { + ArrayType *ar = DatumGetArrayTypeP(proargmodes); + char *argmodes; + int j; + + argmodes = ARR_DATA_PTR(ar); + j = ARR_DIMS(ar)[0] - 1; + if (j >= 0 && argmodes[j] == PROARGMODE_VARIADIC) + { + /* "any" variadics are not treated as variadics for listing */ + if (procform->proargtypes.values[j] != ANYOID) + *is_variadic = true; + } + } + } + ReleaseSysCache(proctup); return result; diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 42586f13aa..7cba375ee0 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -7,7 +7,7 @@ * Copyright (c) 2002-2008, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.39 2008/03/25 22:42:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.40 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -844,7 +844,8 @@ get_func_result_name(Oid functionId) numoutargs = 0; for (i = 0; i < numargs; i++) { - if (argmodes[i] == PROARGMODE_IN) + if (argmodes[i] == PROARGMODE_IN || + argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); @@ -994,7 +995,8 @@ build_function_result_tupdesc_d(Datum proallargtypes, { char *pname; - if (argmodes[i] == PROARGMODE_IN) + if (argmodes[i] == PROARGMODE_IN || + argmodes[i] == PROARGMODE_VARIADIC) continue; Assert(argmodes[i] == PROARGMODE_OUT || argmodes[i] == PROARGMODE_INOUT); diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 88ed75fd50..c22a5be9d3 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.493 2008/07/01 11:46:48 heikki Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.494 2008/07/16 01:30:22 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -6435,15 +6435,18 @@ format_function_arguments(FuncInfo *finfo, int nallargs, { switch (argmodes[j][0]) { - case 'i': + case PROARGMODE_IN: argmode = ""; break; - case 'o': + case PROARGMODE_OUT: argmode = "OUT "; break; - case 'b': + case PROARGMODE_INOUT: argmode = "INOUT "; break; + case PROARGMODE_VARIADIC: + argmode = "VARIADIC "; + break; default: write_msg(NULL, "WARNING: bogus value in proargmodes array\n"); argmode = ""; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 8eb64f38ee..f8f6a657d2 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -8,7 +8,7 @@ * * Copyright (c) 2000-2008, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.181 2008/07/15 16:06:06 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.182 2008/07/16 01:30:23 tgl Exp $ */ #include "postgres_fe.h" @@ -204,7 +204,8 @@ describeFunctions(const char *pattern, bool verbose) " CASE\n" " 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] = '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" diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 07f66e80a7..b67d5b2208 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.468 2008/07/16 00:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.469 2008/07/16 01:30:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200807151 +#define CATALOG_VERSION_NO 200807152 #endif diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h index b161b7108b..749297eaca 100644 --- a/src/include/catalog/namespace.h +++ b/src/include/catalog/namespace.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.54 2008/07/01 02:09:34 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.55 2008/07/16 01:30:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -29,6 +29,7 @@ typedef struct _FuncCandidateList 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 args to become variadic array */ Oid args[1]; /* arg types --- VARIABLE LENGTH ARRAY */ } *FuncCandidateList; /* VARIABLE LENGTH STRUCT */ @@ -51,7 +52,8 @@ extern bool RelationIsVisible(Oid relid); extern Oid TypenameGetTypid(const char *typname); extern bool TypeIsVisible(Oid typid); -extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs); +extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs, + bool expand_variadic); extern bool FunctionIsVisible(Oid funcid); extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 63f1cc10d2..8256ca5ba9 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.506 2008/07/16 00:48:53 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.507 2008/07/16 01:30:23 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -4474,5 +4474,6 @@ DESCR("is txid visible in snapshot?"); #define PROARGMODE_IN 'i' #define PROARGMODE_OUT 'o' #define PROARGMODE_INOUT 'b' +#define PROARGMODE_VARIADIC 'v' #endif /* PG_PROC_H */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 472c07b508..561cc9129d 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.366 2008/05/16 23:36:05 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.367 2008/07/16 01:30:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -261,6 +261,7 @@ typedef struct FuncCall List *args; /* the arguments (list of exprs) */ bool agg_star; /* argument was really '*' */ bool agg_distinct; /* arguments were labeled DISTINCT */ + bool func_variadic; /* last argument was labeled VARIADIC */ int location; /* token location, or -1 if unknown */ } FuncCall; @@ -1568,7 +1569,8 @@ typedef enum FunctionParameterMode /* 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_INOUT = 'b', /* both */ + FUNC_PARAM_VARIADIC = 'v' /* variadic */ } FunctionParameterMode; typedef struct FunctionParameter diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h index 3635f2eede..ec619a4ac5 100644 --- a/src/include/parser/parse_func.h +++ b/src/include/parser/parse_func.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.59 2008/01/01 19:45:58 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.60 2008/07/16 01:30:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -43,13 +43,13 @@ typedef enum extern Node *ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs, - bool agg_star, bool agg_distinct, bool is_column, - int location); + bool agg_star, bool agg_distinct, bool func_variadic, + bool is_column, int location); extern FuncDetailCode func_get_detail(List *funcname, List *fargs, - int nargs, Oid *argtypes, + int nargs, Oid *argtypes, bool expand_variadic, Oid *funcid, Oid *rettype, - bool *retset, Oid **true_typeids); + bool *retset, int *nvargs, Oid **true_typeids); extern int func_match_argtypes(int nargs, Oid *input_typeids, diff --git a/src/interfaces/ecpg/preproc/preproc.y b/src/interfaces/ecpg/preproc/preproc.y index 56e38c3497..73ad1a577b 100644 --- a/src/interfaces/ecpg/preproc/preproc.y +++ b/src/interfaces/ecpg/preproc/preproc.y @@ -1,4 +1,4 @@ -/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.368 2008/06/26 08:04:05 meskes Exp $ */ +/* $PostgreSQL: pgsql/src/interfaces/ecpg/preproc/preproc.y,v 1.369 2008/07/16 01:30:23 tgl Exp $ */ /* Copyright comment */ %{ @@ -489,7 +489,7 @@ add_typedef(char *name, char * dimension, char * length, enum ECPGttype type_enu UNCOMMITTED UNENCRYPTED UNION UNIQUE UNKNOWN UNLISTEN UNTIL UPDATE USER USING - VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARYING + VACUUM VALID VALIDATOR VALUE_P VALUES VARCHAR VARIADIC VARYING VERBOSE VERSION_P VIEW VOLATILE WHEN WHERE WHITESPACE_P WITH WITHOUT WORK WRITE @@ -2629,6 +2629,7 @@ arg_class: IN_P { $$ = make_str("in"); } | OUT_P { $$ = make_str("out"); } | INOUT { $$ = make_str("inout"); } | IN_P OUT_P { $$ = make_str("in out"); } + | VARIADIC { $$ = make_str("variadic"); } ; func_as: StringConst @@ -6857,6 +6858,7 @@ reserved_keyword: | UNIQUE { $$ = make_str("unique"); } | USER { $$ = make_str("user"); } | USING { $$ = make_str("using"); } + | VARIADIC { $$ = make_str("variadic"); } | WHEN { $$ = make_str("when"); } | WHERE { $$ = make_str("where"); } ; diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 737bac5888..1b2cba3881 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.126 2008/05/13 22:10:29 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.127 2008/07/16 01:30:23 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -426,7 +426,8 @@ do_compile(FunctionCallInfo fcinfo, { argitemtype = PLPGSQL_NSTYPE_VAR; /* input argument vars are forced to be CONSTANT */ - if (argmode == PROARGMODE_IN) + if (argmode == PROARGMODE_IN || + argmode == PROARGMODE_VARIADIC) ((PLpgSQL_var *) argvariable)->isconst = true; } else @@ -436,7 +437,9 @@ do_compile(FunctionCallInfo fcinfo, } /* Remember arguments in appropriate arrays */ - if (argmode == PROARGMODE_IN || argmode == PROARGMODE_INOUT) + 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; diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index a2da9a6dce..8e85c6707e 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.110 2008/05/12 00:00:54 alvherre Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.111 2008/07/16 01:30:23 tgl Exp $ * ********************************************************************* */ @@ -1271,7 +1271,7 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) /* proc->nargs was initialized to 0 above */ for (i = 0; i < total; i++) { - if (modes[i] != 'o') + if (modes[i] != PROARGMODE_OUT) (proc->nargs)++; } } @@ -1282,8 +1282,8 @@ PLy_procedure_create(HeapTuple procTup, Oid tgreloid, char *key) HeapTuple argTypeTup; Form_pg_type argTypeStruct; - if (modes && modes[i] == 'o') /* skip OUT arguments */ - continue; + if (modes && modes[i] == PROARGMODE_OUT) + continue; /* skip OUT arguments */ Assert(types[i] == procStruct->proargtypes.values[pos]); diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out index 582c06785a..1e4ef2645f 100644 --- a/src/test/regress/expected/plpgsql.out +++ b/src/test/regress/expected/plpgsql.out @@ -3544,3 +3544,93 @@ select case_test(13); drop function catch(); drop function case_test(bigint); +-- test variadic functions +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) + +select vari(variadic array[5,6,7]); +NOTICE: 5 +NOTICE: 6 +NOTICE: 7 + vari +------ + +(1 row) + +drop function vari(int[]); +-- coercion 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) + +-- in case of conflict, non-variadic version is preferred +create or replace function pleast(numeric) +returns numeric as $$ +begin + raise notice 'non-variadic function called'; + return $1; +end; +$$ language plpgsql immutable strict; +select pleast(10); +NOTICE: non-variadic function called + pleast +-------- + 10 +(1 row) + +drop function pleast(numeric[]); +drop function pleast(numeric); diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index a208203c6d..3779f8e58c 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -613,3 +613,111 @@ create aggregate build_group(int8, integer) ( SFUNC = add_group, STYPE = int8[] ); +-- test variadic polymorphic functions +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) + +select myleast('z'::text); + myleast +--------- + z +(1 row) + +select myleast(); -- fail +ERROR: function myleast() does not exist +LINE 1: select myleast(); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +-- test with variadic call parameter +select myleast(variadic array[1,2,3,4,-1]); + myleast +--------- + -1 +(1 row) + +select myleast(variadic array[1.1, -5.5]); + myleast +--------- + -5.5 +(1 row) + +--test with empty variadic call parameter +select myleast(variadic array[]::int[]); + myleast +--------- + +(1 row) + +-- an example with some ordinary arguments too +create function concat(text, variadic anyarray) returns text 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) + +select concat('|', variadic array[1,2,33]); + concat +-------- + 1|2|33 +(1 row) + +select concat('|', variadic array[]::int[]); + concat +-------- + +(1 row) + +drop function concat(text, anyarray); +-- mix variadic with anyelement +create function formarray(anyelement, variadic anyarray) returns anyarray as $$ + select array_prepend($1, $2); +$$ language sql immutable strict; +select formarray(1,2,3,4,5); + formarray +------------- + {1,2,3,4,5} +(1 row) + +select formarray(1.1, variadic array[1.2,55.5]); + formarray +---------------- + {1.1,1.2,55.5} +(1 row) + +select formarray(1.1, array[1.2,55.5]); -- fail without variadic +ERROR: function formarray(numeric, numeric[]) does not exist +LINE 1: select formarray(1.1, array[1.2,55.5]); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +select formarray(1, 'x'::text); -- fail, type mismatch +ERROR: function formarray(integer, text) does not exist +LINE 1: select formarray(1, 'x'::text); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +select formarray(1, variadic array['x'::text]); -- fail, type mismatch +ERROR: function formarray(integer, text[]) does not exist +LINE 1: select formarray(1, variadic array['x'::text]); + ^ +HINT: No function matches the given name and argument types. You might need to add explicit type casts. +drop function formarray(anyelement, variadic anyarray); diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql index 0267dda30c..4d45dba2ef 100644 --- a/src/test/regress/sql/plpgsql.sql +++ b/src/test/regress/sql/plpgsql.sql @@ -2878,3 +2878,50 @@ select case_test(13); drop function catch(); drop function case_test(bigint); + +-- test variadic functions + +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); +select vari(variadic array[5,6,7]); + +drop function vari(int[]); + +-- coercion 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); + +-- in case of conflict, non-variadic version is preferred +create or replace function pleast(numeric) +returns numeric as $$ +begin + raise notice 'non-variadic function called'; + return $1; +end; +$$ language plpgsql immutable strict; + +select pleast(10); + +drop function pleast(numeric[]); +drop function pleast(numeric); diff --git a/src/test/regress/sql/polymorphism.sql b/src/test/regress/sql/polymorphism.sql index 2df963952f..a4e2b2da3e 100644 --- a/src/test/regress/sql/polymorphism.sql +++ b/src/test/regress/sql/polymorphism.sql @@ -426,3 +426,46 @@ create aggregate build_group(int8, integer) ( SFUNC = add_group, STYPE = int8[] ); + +-- test variadic polymorphic functions + +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); +select myleast('z'::text); +select myleast(); -- fail + +-- test with variadic call parameter +select myleast(variadic array[1,2,3,4,-1]); +select myleast(variadic array[1.1, -5.5]); + +--test with empty variadic call parameter +select myleast(variadic array[]::int[]); + +-- an example with some ordinary arguments too +create function concat(text, variadic anyarray) returns text as $$ + select array_to_string($2, $1); +$$ language sql immutable strict; + +select concat('%', 1, 2, 3, 4, 5); +select concat('|', 'a'::text, 'b', 'c'); +select concat('|', variadic array[1,2,33]); +select concat('|', variadic array[]::int[]); + +drop function concat(text, anyarray); + +-- mix variadic with anyelement +create function formarray(anyelement, variadic anyarray) returns anyarray as $$ + select array_prepend($1, $2); +$$ language sql immutable strict; + +select formarray(1,2,3,4,5); +select formarray(1.1, variadic array[1.2,55.5]); +select formarray(1.1, array[1.2,55.5]); -- fail without variadic +select formarray(1, 'x'::text); -- fail, type mismatch +select formarray(1, variadic array['x'::text]); -- fail, type mismatch + +drop function formarray(anyelement, variadic anyarray); -- 2.40.0