diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 76198b0f832ba10c39a49619b8111e952133418b..7accea0f76c721bd2aad3cd7a0f074ac2ef2888b 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.168 2008/07/14 00:51:45 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.169 2008/07/16 01:30:21 tgl Exp $ --> <!-- Documentation of the system catalogs, directed toward PostgreSQL developers --> @@ -3643,7 +3643,8 @@ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry> An array with the data types of the function arguments. This includes - only input arguments (including <literal>INOUT</literal> arguments), and thus represents + only input arguments (including <literal>INOUT</literal> and + <literal>VARIADIC</> arguments), and thus represents the call signature of the function </entry> </row> @@ -3654,8 +3655,9 @@ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry> <entry> An array with the data types of the function arguments. This includes - all arguments (including <literal>OUT</literal> and <literal>INOUT</literal> arguments); however, if all the - arguments are IN arguments, this field will be null. + all arguments (including <literal>OUT</literal> and + <literal>INOUT</literal> arguments); however, if all the + arguments are <literal>IN</literal> arguments, this field will be null. Note that subscripting is 1-based, whereas for historical reasons <structfield>proargtypes</> is subscripted from 0 </entry> @@ -3669,8 +3671,10 @@ An array with the modes of the function arguments, encoded as <literal>i</literal> for <literal>IN</> arguments, <literal>o</literal> for <literal>OUT</> arguments, - <literal>b</literal> for <literal>INOUT</> arguments. - If all the arguments are <literal>IN</literal> arguments, this field will be null. + <literal>b</literal> for <literal>INOUT</> arguments, + <literal>v</literal> for <literal>VARIADIC</> arguments. + If all the arguments are <literal>IN</literal> arguments, + this field will be null. Note that subscripts correspond to positions of <structfield>proallargtypes</> not <structfield>proargtypes</> </entry> diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index e2805b4191753d30efb2227cb13e3213d38f2643..caeda7e75cad27d6d0060ddce35aec033fee785d 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.36 2007/08/31 21:33:48 momjian Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $ --> <chapter id="extend"> <title>Extending <acronym>SQL</acronym></title> @@ -254,6 +254,16 @@ is equivalent to declaring it as <literal>f(anyenum, anyenum)</>: both actual arguments have to be the same enum type. </para> + + <para> + A variadic function (one taking a variable number of arguments, as in + <xref linkend="xfunc-sql-variadic-functions">) can be + polymorphic: this is accomplished by declaring its last parameter as + <literal>VARIADIC</> <type>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 + <type>anynonarray</> parameters. + </para> </sect2> </sect1> diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index cd1531999b706ac911159a8020923f2ec4cd283f..42bd6048b68758c4278027b5ba88033136300856 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.131 2008/06/27 01:52:59 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.132 2008/07/16 01:30:21 tgl Exp $ --> <chapter id="plpgsql"> <title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title> @@ -121,6 +121,13 @@ calling query, as discussed in <xref linkend="queries-tablefunctions">. </para> + <para> + <application>PL/pgSQL</> functions can be declared to accept a variable + number of arguments by using the <literal>VARIADIC</> marker. This + works exactly the same way as for SQL functions, as discussed in + <xref linkend="xfunc-sql-variadic-functions">. + </para> + <para> <application>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 bee2f6f4390f201be9b3c7fdc4cfe038bf7bb2eb..abedfe7d50becbb515d33d9dab028d2b22b7c3b3 100644 --- a/doc/src/sgml/ref/alter_function.sgml +++ b/doc/src/sgml/ref/alter_function.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.15 2007/09/03 18:46:29 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/alter_function.sgml,v 1.16 2008/07/16 01:30:21 tgl Exp $ PostgreSQL documentation --> @@ -81,13 +81,14 @@ where <replaceable class="PARAMETER">action</replaceable> is one of: <listitem> <para> - The mode of an argument: either <literal>IN</>, <literal>OUT</>, - or <literal>INOUT</>. If omitted, the default is <literal>IN</>. + The mode of an argument: <literal>IN</>, <literal>OUT</>, + <literal>INOUT</>, or <literal>VARIADIC</>. + If omitted, the default is <literal>IN</>. Note that <command>ALTER FUNCTION</command> does not actually pay any attention to <literal>OUT</> arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the <literal>IN</> and <literal>INOUT</> - arguments. + So it is sufficient to list the <literal>IN</>, <literal>INOUT</>, + and <literal>VARIADIC</> arguments. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/comment.sgml b/doc/src/sgml/ref/comment.sgml index c8993e915b8e683f4fc722f7feda32eeb7e6a8ae..2da64f87aca4ab5b9349aeb3a89af430845d4536 100644 --- a/doc/src/sgml/ref/comment.sgml +++ b/doc/src/sgml/ref/comment.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.36 2007/08/21 21:08:47 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/comment.sgml,v 1.37 2008/07/16 01:30:21 tgl Exp $ PostgreSQL documentation --> @@ -136,13 +136,14 @@ COMMENT ON <listitem> <para> - The mode of a function argument: either <literal>IN</>, <literal>OUT</>, - or <literal>INOUT</>. If omitted, the default is <literal>IN</>. + The mode of a function argument: <literal>IN</>, <literal>OUT</>, + <literal>INOUT</>, or <literal>VARIADIC</>. + If omitted, the default is <literal>IN</>. Note that <command>COMMENT ON FUNCTION</command> does not actually pay any attention to <literal>OUT</> arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the <literal>IN</> and <literal>INOUT</> - arguments. + So it is sufficient to list the <literal>IN</>, <literal>INOUT</>, + and <literal>VARIADIC</> arguments. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml index 8c542982d52ba32733dd658ef13bd9394ec021e0..18b9bf7beeac466b43ffa4fbe8b95e2b3129e51f 100644 --- a/doc/src/sgml/ref/create_function.sgml +++ b/doc/src/sgml/ref/create_function.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.78 2007/09/11 00:06:41 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.79 2008/07/16 01:30:21 tgl Exp $ --> <refentry id="SQL-CREATEFUNCTION"> @@ -101,8 +101,9 @@ CREATE [ OR REPLACE ] FUNCTION <listitem> <para> - The mode of an argument: either <literal>IN</>, <literal>OUT</>, - or <literal>INOUT</>. If omitted, the default is <literal>IN</>. + The mode of an argument: <literal>IN</>, <literal>OUT</>, + <literal>INOUT</>, or <literal>VARIADIC</>. + If omitted, the default is <literal>IN</>. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/ref/drop_function.sgml b/doc/src/sgml/ref/drop_function.sgml index bf39f5356ddff9f3b1c1294fcc0364c6707942cb..256cafe684f1e1bb2b28402024dd7cd74f5e1145 100644 --- a/doc/src/sgml/ref/drop_function.sgml +++ b/doc/src/sgml/ref/drop_function.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.33 2007/01/31 23:26:03 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/drop_function.sgml,v 1.34 2008/07/16 01:30:21 tgl Exp $ PostgreSQL documentation --> @@ -65,13 +65,14 @@ DROP FUNCTION [ IF EXISTS ] <replaceable class="parameter">name</replaceable> ( <listitem> <para> - The mode of an argument: either <literal>IN</>, <literal>OUT</>, - or <literal>INOUT</>. If omitted, the default is <literal>IN</>. + The mode of an argument: <literal>IN</>, <literal>OUT</>, + <literal>INOUT</>, or <literal>VARIADIC</>. + If omitted, the default is <literal>IN</>. Note that <command>DROP FUNCTION</command> does not actually pay any attention to <literal>OUT</> arguments, since only the input arguments are needed to determine the function's identity. - So it is sufficient to list the <literal>IN</> and <literal>INOUT</> - arguments. + So it is sufficient to list the <literal>IN</>, <literal>INOUT</>, + and <literal>VARIADIC</> arguments. </para> </listitem> </varlistentry> diff --git a/doc/src/sgml/typeconv.sgml b/doc/src/sgml/typeconv.sgml index 451555cd022708b89f7c025e2a9746eafbe3e52a..4f04801210bdb79a182a551dc3ce948271a6e136 100644 --- a/doc/src/sgml/typeconv.sgml +++ b/doc/src/sgml/typeconv.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.54 2008/07/11 07:02:43 petere Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/typeconv.sgml,v 1.55 2008/07/16 01:30:21 tgl Exp $ --> <chapter Id="typeconv"> <title>Type Conversion</title> @@ -503,6 +503,18 @@ different argument types are considered on an equal footing regardless of search path position. </para> </step> +<step performance="optional"> +<para> +If a function is declared with a <literal>VARIADIC</> array parameter, and +the call does not use the <literal>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. +</para> +</step> </substeps> </step> diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index efee5d8d46914b57c6a52c1d514940e835e327f7..55ed719ec64c1701f22fbd8d6308a951349b3ff3 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,4 +1,4 @@ -<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.130 2007/11/10 20:14:36 tgl Exp $ --> +<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.131 2008/07/16 01:30:21 tgl Exp $ --> <sect1 id="xfunc"> <title>User-Defined Functions</title> @@ -495,7 +495,7 @@ SELECT getname(new_emp()); None (1 row) </screen> - </para> + </para> <para> Still another way to use a function that returns a composite type is to @@ -505,7 +505,7 @@ SELECT getname(new_emp()); </sect2> <sect2 id="xfunc-output-parameters"> - <title>Functions with Output Parameters</title> + <title><acronym>SQL</> Functions with Output Parameters</title> <indexterm> <primary>function</primary> @@ -578,9 +578,75 @@ DROP FUNCTION sum_n_product (int, int); <para> Parameters can be marked as <literal>IN</> (the default), - <literal>OUT</>, or <literal>INOUT</>. An <literal>INOUT</> + <literal>OUT</>, <literal>INOUT</>, or <literal>VARIADIC</>. + 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). + <literal>VARIADIC</> parameters are input parameters, but are treated + specially as described next. + </para> + </sect2> + + <sect2 id="xfunc-sql-variadic-functions"> + <title><acronym>SQL</> Functions with Variable Numbers of Arguments</title> + + <indexterm> + <primary>function</primary> + <secondary>variadic</secondary> + </indexterm> + + <indexterm> + <primary>variadic function</primary> + </indexterm> + + <para> + <acronym>SQL</acronym> functions can be declared to accept + variable numbers of arguments, so long as all the <quote>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 <literal>VARIADIC</>; this parameter + must be declared as being of an array type. For example: + +<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.4); + mleast +-------- + -1 +(1 row) +</screen> + + Effectively, all the actual arguments at or beyond the + <literal>VARIADIC</> position are gathered up into a one-dimensional + array, as if you had written + +<screen> +SELECT mleast(ARRAY[10, -1, 5, 4.4]); -- doesn't work +</screen> + + You can't actually write that, though — or at least, it will + not match this function definition. A parameter marked + <literal>VARIADIC</> matches one or more occurrences of its element + type, not of its own type. + </para> + + <para> + 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 <literal>VARIADIC</> in the call: + +<screen> +SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]); +</screen> + + This prevents expansion of the function's variadic parameter into its + element type, thereby allowing the array argument value to match + normally. <literal>VARIADIC</> can only be attached to the last + actual argument of a function call. </para> </sect2> @@ -795,13 +861,45 @@ DETAIL: A function returning a polymorphic type must have at least one polymorp For example: <screen> 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) +</screen> + </para> + + <para> + Polymorphism can also be used with variadic functions. + For example: +<screen> +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) </screen> </para> </sect2> @@ -852,6 +950,16 @@ CREATE FUNCTION test(smallint, double precision) RETURNS ... avoid the problem by not choosing conflicting names. </para> + <para> + Another possible conflict is between variadic and non-variadic functions. + For instance, it is possible to create both <literal>foo(numeric)</> and + <literal>foo(VARIADIC numeric[])</>. In this case it is unclear which one + should be matched to a call providing a single numeric argument, such as + <literal>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. + </para> + <para> 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; </programlisting> </para> + + <para> + There is a variant of polymorphism that is only available to C-language + functions: they can be declared to take parameters of type + <literal>"any"</>. (Note that this type name must be double-quoted, + since it's also a SQL reserved word.) This works like + <type>anyelement</> except that it does not constrain different + <literal>"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 <literal>VARIADIC "any"</>. This will + match one or more actual arguments of any type (not necessarily the same + type). These arguments will <emphasis>not</> be gathered into an array + as happens with normal variadic functions; they will just be passed to + the function separately. The <function>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. + </para> </sect2> + <sect2> <title>Shared Memory and LWLocks</title> diff --git a/src/backend/catalog/information_schema.sql b/src/backend/catalog/information_schema.sql index a9566d9d3f36b8fe73ce00253038688cbfa2c582..0e2452fa0f6ab7fc3c15ba0c20460c36289cfb6a 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 dfaea41cfe4846e213d564d14bf14e84b0c0ce2c..48b8ee45e6b2bdbd10673dc17d5a243b94b5af52 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 1ff7261877f1ae74b096a9fda993abd5b290df47..e1c67ce5cd4cfce53263b858ccec33e4f8746547 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 9831692eea0a4fa82b54f2202fdbae63ce23459c..cb249d9c7d1987de4a6f47935d8a9f11b414e524 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 a42c40327f2b25bdc1ca029eae8a0366328ee584..2e1ce4cb0d7bca52f23bd247d2bbc93cc9659a76 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 435ee6a6afbbe4ac2d5c30c601fcbe8090dabbcd..41999226b6d625a40714a48876c074547d7ea5c9 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 51b46a83edfb6ab3603562f4164a5598e87b85b9..a03063ce1e69f3c71d9640f12023219b38e5c9d8 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 4e59e37da91f04cb777705c441571c1b2a800178..70bbe940afd643610cbbc268bac35d0bd05577fa 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 43013e1e7723d0c273bd11e270780811e24e3506..97fba9c9562e1739f2170083762dedb40dcf1cf5 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 362108ba3fa054188c55e7ea2e1069b72095e468..8addb53e51ee5ac765067cb9619f96b46d4f5120 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 3d44c2520beaf23b9fdf2f9b224d87d5ccb1771f..3bb5c452a8ea629f6602077832eaf46773e0ec61 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 33f5b8015f6040f5fc5238a51d14bcc667e58d64..bbdea4642cc10f64c7a2b5ef87a5a2e077ee2eaf 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 986bac041d927023c507ec5a8a2c5df3215d65be..d50dc23d7783f4e870ff9f98cdfe420ed1a2a15c 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 3754b4981edbd2646841346cf4cbe476b097909a..dc4a6cc4a8f853f0ab6fdfc0a5d3ec1f0ee98081 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 42586f13aa16cafccb6bd74f94a4f69616f1f41f..7cba375ee038284382abd1fe3f2ccdef4a52c416 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 88ed75fd50563c4842e3d4d17b8ecbec602376ff..c22a5be9d3922ce30c057981a70fe6888bded051 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 8eb64f38ee2f104a232ae202f3f99773c60104c3..f8f6a657d213f953a8aa4cbcf45fdbfc1d365f5c 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 07f66e80a7f464068691a4b8e14a348db9954cbe..b67d5b2208069e575286d743c550c27d2c3915fa 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 b161b7108b6452f2566c5a61cb7cbb6337bf4fa4..749297eaca661f676b22a10569d98c265c71033f 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 63f1cc10d2ef14dc76d73259810ede31efa31572..8256ca5ba9da826ac298529cc0f4fb1498636e21 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 472c07b50816a0e4b72b204d28b4e8637bf5901d..561cc9129dba3027f3362d7c4b9c8995b73cb1d0 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 3635f2eede4fafb8f5a4f1e11c379971d607dbea..ec619a4ac544b48592c68533cc2184b15d7b93bd 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 56e38c3497b906678d8c3930de63d9f6e8b4d409..73ad1a577b862c637592de5567b7204e6529932c 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 737bac5888119ab1d4adc1f303120fabcda6c76f..1b2cba38815d17d14190c13069f279eed6fb6e07 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 a2da9a6dcee2606de3b2cdadb8e3d375407c040d..8e85c6707e2d044817d15a1da2d31e6296cea3b8 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 582c06785a8aee50c04bcbd95756676da4ff9335..1e4ef2645fa801e82a5747b7ac0ffdce3fa4ec87 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 a208203c6d39881a911919f9a221ce5c0e96a7b0..3779f8e58ce20f52e324d70acc9f1d7e0a99891a 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 0267dda30ca21662ee7dbe0b6cf659aab93c01d0..4d45dba2efcbcfdba463467bbd1fdf7093445fb2 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 2df963952f4e2ff9583a5ad63a4520cbb6df3d51..a4e2b2da3e3ae8ce2b24db974cade79e05b3ee47 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);