Support use of function argument names to identify which actual arguments
authorTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Oct 2009 02:39:25 +0000 (02:39 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Thu, 8 Oct 2009 02:39:25 +0000 (02:39 +0000)
match which function parameters.  The syntax uses AS, for example
funcname(value AS arg1, anothervalue AS arg2)

Pavel Stehule

34 files changed:
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/sources.sgml
doc/src/sgml/syntax.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/namespace.c
src/backend/catalog/pg_aggregate.c
src/backend/catalog/pg_proc.c
src/backend/commands/aggregatecmds.c
src/backend/commands/functioncmds.c
src/backend/commands/tsearchcmds.c
src/backend/commands/typecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/nodeFuncs.c
src/backend/nodes/outfuncs.c
src/backend/nodes/readfuncs.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/utils/adt/regproc.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/fmgr/funcapi.c
src/include/catalog/catversion.h
src/include/catalog/namespace.h
src/include/funcapi.h
src/include/nodes/nodes.h
src/include/nodes/primnodes.h
src/include/parser/parse_func.h
src/test/regress/expected/polymorphism.out
src/test/regress/expected/rangefuncs.out
src/test/regress/sql/polymorphism.sql
src/test/regress/sql/rangefuncs.sql

index 0843a175638a4dd0fd80ea455c1940abf5495c70..6d7eb84d8f26e434d7fd2eb4649903ee57d6de21 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.87 2009/10/02 18:13:04 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_function.sgml,v 1.88 2009/10/08 02:39:14 tgl Exp $
 -->
 
 <refentry id="SQL-CREATEFUNCTION">
@@ -65,7 +65,7 @@ CREATE [ OR REPLACE ] FUNCTION
    Also, <command>CREATE OR REPLACE FUNCTION</command> will not let
    you change the return type of an existing function.  To do that,
    you must drop and recreate the function.  (When using <literal>OUT</>
-   parameters, that means you cannot change the names or types of any
+   parameters, that means you cannot change the types of any
    <literal>OUT</> parameters except by dropping the function.)
   </para>
 
@@ -121,8 +121,11 @@ CREATE [ OR REPLACE ] FUNCTION
       <para>
        The name of an argument. Some languages (currently only PL/pgSQL) let
        you use the name in the function body.  For other languages the
-       name of an input argument is just extra documentation.  But the name
-       of an output argument is significant, since it defines the column
+       name of an input argument is just extra documentation, so far as
+       the function itself is concerned; but you can use input argument names
+       when calling a function to improve readability (see <xref
+       linkend="sql-syntax-calling-funcs">).  In any case, the name
+       of an output argument is significant, because it defines the column
        name in the result row type.  (If you omit the name for an output
        argument, the system will choose a default column name.)
       </para>
@@ -570,6 +573,18 @@ CREATE FUNCTION foo(int, int default 42) ...
     to replace it (this includes being a member of the owning role).
    </para>
 
+   <para>
+    When replacing an existing function with <command>CREATE OR REPLACE
+    FUNCTION</>, there are restrictions on changing parameter names.
+    You cannot change the name already assigned to any input parameter
+    (although you can add names to parameters that had none before).
+    If there is more than one output parameter, you cannot change the
+    names of the output parameters, because that would change the
+    column names of the anonymous composite type that describes the
+    function's result.  These restrictions are made to ensure that
+    existing calls of the function do not stop working when it is replaced.
+   </para>
+
  </refsect1>
 
  <refsect1 id="sql-createfunction-examples">
index 0872eacee761537573bfa739ccc76d4553b49187..342f8e4ef73ee741a087fe03b89ecc71fc1f82c5 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.34 2009/06/04 18:33:06 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/sources.sgml,v 2.35 2009/10/08 02:39:16 tgl Exp $ -->
 
  <chapter id="source">
   <title>PostgreSQL Coding Conventions</title>
@@ -125,7 +125,7 @@ ereport(ERROR,
         (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
          errmsg("function %s is not unique",
                 func_signature_string(funcname, nargs,
-                                      actual_arg_types)),
+                                      NIL, actual_arg_types)),
          errhint("Unable to choose a best candidate function. "
                  "You might need to add explicit typecasts.")));
 </programlisting>
index 73db3235bd6f15250536cf96e4c8a24c140c01dc..20f7085a8d3375205347c73fe2e336df98791f38 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.136 2009/09/22 23:52:53 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.137 2009/10/08 02:39:16 tgl Exp $ -->
 
 <chapter id="sql-syntax">
  <title>SQL Syntax</title>
@@ -1505,6 +1505,11 @@ sqrt(2)
     The list of built-in functions is in <xref linkend="functions">.
     Other functions can be added by the user.
    </para>
+
+   <para>
+    The arguments can optionally have names attached.
+    See <xref linkend="sql-syntax-calling-funcs"> for details.
+   </para>
   </sect2>
 
   <sect2 id="syntax-aggregates">
@@ -2123,4 +2128,168 @@ SELECT ... WHERE CASE WHEN x &gt; 0 THEN y/x &gt; 1.5 ELSE false END;
   </sect2>
  </sect1>
 
+ <sect1 id="sql-syntax-calling-funcs">
+  <title>Calling Functions</title>
+
+   <indexterm zone="sql-syntax-calling-funcs">
+    <primary>notation</primary>
+    <secondary>functions</secondary>
+   </indexterm>
+
+   <para>
+    <productname>PostgreSQL</productname> allows functions that have named
+    parameters to be called using either <firstterm>positional</firstterm> or
+    <firstterm>named</firstterm> notation.  Named notation is especially
+    useful for functions that have a large number of parameters, since it
+    makes the associations between parameters and actual arguments more
+    explicit and reliable.
+    In positional notation, a function call is written with
+    its argument values in the same order as they are defined in the function
+    declaration.  In named notation, the arguments are matched to the
+    function parameters by name and can be written in any order.
+   </para>
+
+   <para>
+    In either notation, parameters that have default values given in the
+    function declaration need not be written in the call at all.  But this
+    is particularly useful in named notation, since any combination of
+    parameters can be omitted; while in positional notation parameters can
+    only be omitted from right to left.
+   </para>
+
+   <para>
+    <productname>PostgreSQL</productname> also supports
+    <firstterm>mixed</firstterm> notation, which combines positional and
+    named notation.  In this case, positional parameters are written first
+    and named parameters appear after them.
+   </para>
+
+   <para>
+    The following examples will illustrate the usage of all three
+    notations, using the following function definition:
+<programlisting>
+CREATE FUNCTION concat_lower_or_upper(a text, b text, uppercase boolean DEFAULT false)
+RETURNS text
+AS
+$$
+ SELECT CASE
+        WHEN $3 THEN UPPER($1 || ' ' || $2)
+        ELSE LOWER($1 || ' ' || $2)
+        END;
+$$
+LANGUAGE SQL IMMUTABLE STRICT;
+</programlisting>
+    Function <function>concat_lower_or_upper</function> has two mandatory
+    parameters, <literal>a</literal> and <literal>b</literal>.  Additionally
+    there is one optional parameter <literal>uppercase</literal> which defaults
+    to <literal>false</literal>.  The <literal>a</literal> and
+    <literal>b</literal> inputs will be concatenated, and forced to either
+    upper or lower case depending on the <literal>uppercase</literal>
+    parameter.  The remaining details of this function
+    definition are not important here (see <xref linkend="extend"> for
+    more information).
+   </para>
+
+   <sect2 id="sql-syntax-calling-funcs-positional">
+    <title>Using positional notation</title>
+
+    <indexterm>
+     <primary>function</primary>
+     <secondary>positional notation</secondary>
+    </indexterm>
+
+    <para>
+     Positional notation is the traditional mechanism for passing arguments
+     to functions in <productname>PostgreSQL</productname>.  An example is:
+<screen>
+SELECT concat_lower_or_upper('Hello', 'World', true);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+</screen>
+     All arguments are specified in order.  The result is upper case since
+     <literal>uppercase</literal> is specified as <literal>true</literal>.
+     Another example is:
+<screen>
+SELECT concat_lower_or_upper('Hello', 'World');
+ concat_lower_or_upper 
+-----------------------
+ hello world
+(1 row)
+</screen>
+     Here, the <literal>uppercase</literal> parameter is omitted, so it
+     receives its default value of <literal>false</literal>, resulting in
+     lower case output.  In positional notation, arguments can be omitted
+     from right to left so long as they have defaults.
+    </para>
+   </sect2>
+
+   <sect2 id="sql-syntax-calling-funcs-named">
+    <title>Using named notation</title>
+
+    <indexterm>
+     <primary>function</primary>
+     <secondary>named notation</secondary>
+    </indexterm>
+
+    <para>
+     In named notation, each argument's name is specified using the
+     <literal>AS</literal> keyword.  For example:
+<screen>
+SELECT concat_lower_or_upper('Hello' AS a, 'World' AS b);
+ concat_lower_or_upper 
+-----------------------
+ hello world
+(1 row)
+</screen>
+     Again, the argument <literal>uppercase</literal> was omitted
+     so it is set to <literal>false</literal> implicitly.  One advantage of
+     using named notation is that the arguments may be specified in any
+     order, for example:
+<screen>
+SELECT concat_lower_or_upper('Hello' AS a, 'World' AS b, true AS uppercase);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+
+SELECT concat_lower_or_upper('Hello' AS a, true AS uppercase, 'World' AS b);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+</screen>
+    </para>
+   </sect2>
+
+  <sect2 id="sql-syntax-calling-funcs-mixed">
+   <title>Using mixed notation</title>
+
+   <indexterm>
+    <primary>function</primary>
+    <secondary>mixed notation</secondary>
+   </indexterm>
+
+   <para>
+    The mixed notation combines positional and named notation. However, as
+    already mentioned, named arguments cannot precede positional arguments.
+    For example:
+<screen>
+SELECT concat_lower_or_upper('Hello', 'World', true AS uppercase);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+</screen>
+    In the above query, the arguments <literal>a</literal> and
+    <literal>b</literal> are specified positionally, while
+    <literal>uppercase</> is specified by name.  In this example,
+    that adds little except documentation.  With a more complex function
+    having numerous parameters that have default values, named or mixed
+    notation can save a great deal of writing and reduce chances for error.
+   </para>
+  </sect2>
+ </sect1>
+
 </chapter>
index 6d85d2d262908850f2544ea11f732e35eb1a1d25..1c20f15226a3cb377ece7591ce6a66b9733539f4 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.139 2009/09/03 22:11:07 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.140 2009/10/08 02:39:16 tgl Exp $ -->
 
  <sect1 id="xfunc">
   <title>User-Defined Functions</title>
@@ -517,6 +517,39 @@ SELECT getname(new_emp());
     </para>
    </sect2>
 
+   <sect2 id="xfunc-named-parameters">
+    <title><acronym>SQL</> Functions with Parameter Names</title>
+
+   <indexterm>
+    <primary>function</primary>
+    <secondary>named parameter</secondary>
+   </indexterm>
+
+    <para>
+     It is possible to attach names to a function's parameters, for example
+
+<programlisting>
+CREATE FUNCTION tf1 (acct_no integer, debit numeric) RETURNS numeric AS $$
+    UPDATE bank
+        SET balance = balance - $2
+        WHERE accountno = $1
+    RETURNING balance;
+$$ LANGUAGE SQL;
+</programlisting>
+
+     Here the first parameter has been given the name <literal>acct_no</>,
+     and the second parameter the name <literal>debit</>.
+     So far as the SQL function itself is concerned, these names are just
+     decoration; you must still refer to the parameters as <literal>$1</>,
+     <literal>$2</>, etc within the function body.  (Some procedural
+     languages let you use the parameter names instead.)  However,
+     attaching names to the parameters is useful for documentation purposes.
+     When a function has many parameters, it is also useful to use the names
+     while calling the function, as described in
+     <xref linkend="sql-syntax-calling-funcs">.
+    </para>
+   </sect2>
+
    <sect2 id="xfunc-output-parameters">
     <title><acronym>SQL</> Functions with Output Parameters</title>
 
@@ -571,7 +604,10 @@ LANGUAGE SQL;
 </screen>
 
      but not having to bother with the separate composite type definition
-     is often handy.
+     is often handy.  Notice that the names attached to the output parameters
+     are not just decoration, but determine the column names of the anonymous
+     composite type.  (If you omit a name for an output parameter, the
+     system will choose a name on its own.)
     </para>
 
     <para>
@@ -621,7 +657,7 @@ DROP FUNCTION sum_n_product (int, int);
      must be declared as being of an array type.  For example:
 
 <screen>
-CREATE FUNCTION mleast(VARIADIC numeric[]) RETURNS numeric AS $$
+CREATE FUNCTION mleast(VARIADIC arr numeric[]) RETURNS numeric AS $$
     SELECT min($1[i]) FROM generate_subscripts($1, 1) g(i);
 $$ LANGUAGE SQL;
 
@@ -661,6 +697,25 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
      normally.  <literal>VARIADIC</> can only be attached to the last
      actual argument of a function call.
     </para>
+
+    <para>
+     The array element parameters generated from a variadic parameter are
+     treated as not having any names of their own.  This means it is not
+     possible to call a variadic function using named arguments (<xref
+     linkend="sql-syntax-calling-funcs">), except when you specify
+     <literal>VARIADIC</>.  For example, this will work:
+
+<screen>
+SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4] AS arr);
+</screen>
+
+     but not these:
+
+<screen>
+SELECT mleast(10 AS arr);
+SELECT mleast(ARRAY[10, -1, 5, 4.4] AS arr);
+</screen>
+    </para>
    </sect2>
 
    <sect2 id="xfunc-sql-parameter-defaults">
@@ -677,7 +732,9 @@ SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4]);
      called with insufficiently many actual arguments.  Since arguments
      can only be omitted from the end of the actual argument list, all
      parameters after a parameter with a default value have to have
-     default values as well.
+     default values as well.  (Although the use of named argument notation
+     could allow this restriction to be relaxed, it's still enforced so that
+     positional argument notation works sensibly.)
     </para>
 
     <para>
@@ -712,7 +769,7 @@ SELECT foo();  -- fails since there is no default for the first argument
 ERROR:  function foo() does not exist
 </screen>
      The <literal>=</literal> sign can also be used in place of the
-     key word <literal>DEFAULT</literal>,
+     key word <literal>DEFAULT</literal>.
     </para>
    </sect2>
 
index 12cb1720cde5bb9260708b525bf2c76206f09976..2c327d45f15c3235bdadfe889e5a6e1fd2bedcba 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.118 2009/06/11 14:48:55 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.119 2009/10/08 02:39:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
 #include "catalog/pg_ts_template.h"
 #include "catalog/pg_type.h"
 #include "commands/dbcommands.h"
+#include "funcapi.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "parser/parse_func.h"
@@ -188,6 +189,8 @@ static void InitTempTableNamespace(void);
 static void RemoveTempRelations(Oid tempNamespaceId);
 static void RemoveTempRelationsCallback(int code, Datum arg);
 static void NamespaceCallback(Datum arg, int cacheid, ItemPointer tuplePtr);
+static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
+                          int **argnumbers);
 
 /* These don't really need to appear in any header file */
 Datum      pg_table_is_visible(PG_FUNCTION_ARGS);
@@ -562,8 +565,14 @@ 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.  (expand_variadic and expand_defaults must be
- * false in this case.)
+ * regardless of argument count.  (argnames must be NIL, and expand_variadic
+ * and expand_defaults must be false, in this case.)
+ *
+ * If argnames isn't NIL, we are considering a named- or mixed-notation call,
+ * and only functions having all the listed argument names will be returned.
+ * (We assume that length(argnames) <= nargs and all the passed-in names are
+ * distinct.)  The returned structs will include an argnumbers array showing
+ * the actual argument index for each logical argument position.
  *
  * If expand_variadic is true, then variadic functions having the same number
  * or fewer arguments will be retrieved, with the variadic argument and any
@@ -583,6 +592,13 @@ TypeIsVisible(Oid typid)
  * than nargs arguments while the variadic transformation requires the same
  * number or less.
  *
+ * When argnames isn't NIL, the returned args[] type arrays are not ordered
+ * according to the functions' declarations, but rather according to the call:
+ * first any positional arguments, then the named arguments, then defaulted
+ * arguments (if needed and allowed by expand_defaults).  The argnumbers[]
+ * array can be used to map this back to the catalog information.
+ * argnumbers[k] is set to the proargtypes index of the k'th call argument.
+ *
  * We search a single namespace if the function name is qualified, else
  * all namespaces in the search path.  In the multiple-namespace case,
  * we arrange for entries in earlier namespaces to mask identical entries in
@@ -596,15 +612,16 @@ TypeIsVisible(Oid typid)
  * It is guaranteed that the return list will never contain multiple entries
  * with identical argument lists.  When expand_defaults is true, the entries
  * could have more than nargs positions, but we still guarantee that they are
- * distinct in the first nargs positions.  However, if either expand_variadic
- * or expand_defaults is true, there might be multiple candidate functions
- * that expand to identical argument lists.  Rather than throw error here,
- * we report such situations by setting oid = 0 in the ambiguous entries.
+ * distinct in the first nargs positions.  However, if argnames isn't NIL or
+ * either expand_variadic or expand_defaults is true, there might be multiple
+ * candidate functions that expand to identical argument lists.  Rather than
+ * throw error here, we report such situations by returning a single entry
+ * with oid = 0 that represents a set of such conflicting candidates.
  * The caller might end up discarding such an entry anyway, but if it selects
  * such an entry it should react as though the call were ambiguous.
  */
 FuncCandidateList
-FuncnameGetCandidates(List *names, int nargs,
+FuncnameGetCandidates(List *names, int nargs, List *argnames,
                      bool expand_variadic, bool expand_defaults)
 {
    FuncCandidateList resultList = NULL;
@@ -648,42 +665,9 @@ FuncnameGetCandidates(List *names, int nargs,
        bool        variadic;
        bool        use_defaults;
        Oid         va_elem_type;
+       int        *argnumbers = NULL;
        FuncCandidateList newResult;
 
-       /*
-        * Check if function is variadic, and get variadic element type if so.
-        * If expand_variadic is false, we should just ignore variadic-ness.
-        */
-       if (pronargs <= nargs && expand_variadic)
-       {
-           va_elem_type = procform->provariadic;
-           variadic = OidIsValid(va_elem_type);
-           any_special |= variadic;
-       }
-       else
-       {
-           va_elem_type = InvalidOid;
-           variadic = false;
-       }
-
-       /*
-        * Check if function can match by using parameter defaults.
-        */
-       if (pronargs > nargs && expand_defaults)
-       {
-           /* Ignore if not enough default expressions */
-           if (nargs + procform->pronargdefaults < pronargs)
-               continue;
-           use_defaults = true;
-           any_special = true;
-       }
-       else
-           use_defaults = false;
-
-       /* Ignore if it doesn't match requested argument count */
-       if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
-           continue;
-
        if (OidIsValid(namespaceId))
        {
            /* Consider only procs in specified namespace */
@@ -709,6 +693,88 @@ FuncnameGetCandidates(List *names, int nargs,
                continue;       /* proc is not in search path */
        }
 
+       if (argnames != NIL)
+       {
+           /*
+            * Call uses named or mixed notation
+            *
+            * Named or mixed notation can match a variadic function only
+            * if expand_variadic is off; otherwise there is no way to match
+            * the presumed-nameless parameters expanded from the variadic
+            * array.
+            */
+           if (OidIsValid(procform->provariadic) && expand_variadic)
+               continue;
+           va_elem_type = InvalidOid;
+           variadic = false;
+
+           /*
+            * Check argument count.
+            */
+           Assert(nargs >= 0);         /* -1 not supported with argnames */
+
+           if (pronargs > nargs && expand_defaults)
+           {
+               /* Ignore if not enough default expressions */
+               if (nargs + procform->pronargdefaults < pronargs)
+                   continue;
+               use_defaults = true;
+           }
+           else
+               use_defaults = false;
+
+           /* Ignore if it doesn't match requested argument count */
+           if (pronargs != nargs && !use_defaults)
+               continue;
+
+           /* Check for argument name match, generate positional mapping */
+           if (!MatchNamedCall(proctup, nargs, argnames,
+                               &argnumbers))
+               continue;
+
+           /* Named argument matching is always "special" */
+           any_special = true;
+       }
+       else
+       {
+           /*
+            * Call uses positional notation
+            *
+            * Check if function is variadic, and get variadic element type if
+            * so.  If expand_variadic is false, we should just ignore
+            * variadic-ness.
+            */
+           if (pronargs <= nargs && expand_variadic)
+           {
+               va_elem_type = procform->provariadic;
+               variadic = OidIsValid(va_elem_type);
+               any_special |= variadic;
+           }
+           else
+           {
+               va_elem_type = InvalidOid;
+               variadic = false;
+           }
+
+           /*
+            * Check if function can match by using parameter defaults.
+            */
+           if (pronargs > nargs && expand_defaults)
+           {
+               /* Ignore if not enough default expressions */
+               if (nargs + procform->pronargdefaults < pronargs)
+                   continue;
+               use_defaults = true;
+               any_special = true;
+           }
+           else
+               use_defaults = false;
+
+           /* Ignore if it doesn't match requested argument count */
+           if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
+               continue;
+       }
+
        /*
         * 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
@@ -722,8 +788,22 @@ FuncnameGetCandidates(List *names, int nargs,
        newResult->pathpos = pathpos;
        newResult->oid = HeapTupleGetOid(proctup);
        newResult->nargs = effective_nargs;
-       memcpy(newResult->args, procform->proargtypes.values,
-              pronargs * sizeof(Oid));
+       newResult->argnumbers = argnumbers;
+       if (argnumbers)
+       {
+           /* Re-order the argument types into call's logical order */
+           Oid        *proargtypes = procform->proargtypes.values;
+           int         i;
+
+           for (i = 0; i < pronargs; i++)
+               newResult->args[i] = proargtypes[argnumbers[i]];
+       }
+       else
+       {
+           /* Simple positional case, just copy proargtypes as-is */
+           memcpy(newResult->args, procform->proargtypes.values,
+                  pronargs * sizeof(Oid));
+       }
        if (variadic)
        {
            int         i;
@@ -741,9 +821,9 @@ FuncnameGetCandidates(List *names, int nargs,
         * Does it have the same arguments as something we already accepted?
         * If so, decide what to do to avoid returning duplicate argument
         * lists.  We can skip this check for the single-namespace case if no
-        * special (variadic or defaults) match has been made, since then the
-        * unique index on pg_proc guarantees all the matches have different
-        * argument lists.
+        * special (named, variadic or defaults) match has been made, since
+        * then the unique index on pg_proc guarantees all the matches have
+        * different argument lists.
         */
        if (resultList != NULL &&
            (any_special || !OidIsValid(namespaceId)))
@@ -827,7 +907,8 @@ FuncnameGetCandidates(List *names, int nargs,
                     * both foo(numeric, variadic numeric[]) and
                     * foo(variadic numeric[]) in the same namespace, or
                     * both foo(int) and foo (int, int default something)
-                    * in the same namespace.
+                    * in the same namespace, or both foo(a int, b text)
+                    * and foo(b text, a int) in the same namespace.
                     *----------
                     */
                    preference = 0;
@@ -885,6 +966,125 @@ FuncnameGetCandidates(List *names, int nargs,
    return resultList;
 }
 
+/*
+ * MatchNamedCall
+ *     Given a pg_proc heap tuple and a call's list of argument names,
+ *     check whether the function could match the call.
+ *
+ * The call could match if all supplied argument names are accepted by
+ * the function, in positions after the last positional argument, and there
+ * are defaults for all unsupplied arguments.
+ *
+ * The number of positional arguments is nargs - list_length(argnames).
+ * Note caller has already done basic checks on argument count.
+ *
+ * On match, return true and fill *argnumbers with a palloc'd array showing
+ * the mapping from call argument positions to actual function argument
+ * numbers.  Defaulted arguments are included in this map, at positions
+ * after the last supplied argument.
+ */
+static bool
+MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
+              int **argnumbers)
+{
+   Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
+   int         pronargs = procform->pronargs;
+   int         numposargs = nargs - list_length(argnames);
+   int         pronallargs;
+   Oid        *p_argtypes;
+   char      **p_argnames;
+   char       *p_argmodes;
+   bool        arggiven[FUNC_MAX_ARGS];
+   bool        isnull;
+   int         ap;             /* call args position */
+   int         pp;             /* proargs position */
+   ListCell   *lc;
+
+   Assert(argnames != NIL);
+   Assert(numposargs >= 0);
+   Assert(nargs <= pronargs);
+
+   /* Ignore this function if its proargnames is null */
+   (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proargnames,
+                          &isnull);
+   if (isnull)
+       return false;
+
+   /* OK, let's extract the argument names and types */
+   pronallargs = get_func_arg_info(proctup,
+                                   &p_argtypes, &p_argnames, &p_argmodes);
+   Assert(p_argnames != NULL);
+
+   /* initialize state for matching */
+   *argnumbers = (int *) palloc(pronargs * sizeof(int));
+   memset(arggiven, false, pronargs * sizeof(bool));
+
+   /* there are numposargs positional args before the named args */
+   for (ap = 0; ap < numposargs; ap++)
+   {
+       (*argnumbers)[ap] = ap;
+       arggiven[ap] = true;
+   }
+
+   /* now examine the named args */
+   foreach(lc, argnames)
+   {
+       char   *argname = (char *) lfirst(lc);
+       bool    found;
+       int     i;
+
+       pp = 0;
+       found = false;
+       for (i = 0; i < pronallargs; i++)
+       {
+           /* consider only input parameters */
+           if (p_argmodes &&
+               (p_argmodes[i] != FUNC_PARAM_IN &&
+                p_argmodes[i] != FUNC_PARAM_INOUT &&
+                p_argmodes[i] != FUNC_PARAM_VARIADIC))
+               continue;
+           if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
+           {
+               /* fail if argname matches a positional argument */
+               if (arggiven[pp])
+                   return false;
+               arggiven[pp] = true;
+               (*argnumbers)[ap] = pp;
+               found = true;
+               break;
+           }
+           /* increase pp only for input parameters */
+           pp++;
+       }
+       /* if name isn't in proargnames, fail */
+       if (!found)
+           return false;
+       ap++;
+   }
+
+   Assert(ap == nargs);        /* processed all actual parameters */
+
+   /* Check for default arguments */
+   if (nargs < pronargs)
+   {
+       int     first_arg_with_default = pronargs - procform->pronargdefaults;
+
+       for (pp = numposargs; pp < pronargs; pp++)
+       {
+           if (arggiven[pp])
+               continue;
+           /* fail if arg not given and no default available */
+           if (pp < first_arg_with_default)
+               return false;
+           (*argnumbers)[ap++] = pp;
+       }
+   }
+
+   Assert(ap == pronargs);     /* processed all function parameters */
+
+   return true;
+}
+
 /*
  * FunctionIsVisible
  *     Determine whether a function (identified by OID) is visible in the
@@ -932,7 +1132,7 @@ FunctionIsVisible(Oid funcid)
        visible = false;
 
        clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-                                     nargs, false, false);
+                                     nargs, NIL, false, false);
 
        for (; clist; clist = clist->next)
        {
@@ -1202,6 +1402,7 @@ OpernameGetCandidates(List *names, char oprkind)
        newResult->nargs = 2;
        newResult->nvargs = 0;
        newResult->ndargs = 0;
+       newResult->argnumbers = NULL;
        newResult->args[0] = operform->oprleft;
        newResult->args[1] = operform->oprright;
        newResult->next = resultList;
index 8c9f8bce7a78d6ce4cd0b80cfff4abf89e0f2772..7fae4b66b4dc7663a24a05d7a86cdbf5b1c1fb1b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.102 2009/06/11 14:48:55 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.103 2009/10/08 02:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,7 +321,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, false, false,
+   fdresult = func_get_detail(fnName, NIL, NIL,
+                              nargs, input_types, false, false,
                               &fnOid, rettype, &retset, &nvargs,
                               &true_oid_array, NULL);
 
@@ -330,12 +331,14 @@ lookup_agg_function(List *fnName,
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(fnName, nargs, input_types))));
+                       func_signature_string(fnName, nargs,
+                                             NIL, input_types))));
    if (retset)
        ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                 errmsg("function %s returns a set",
-                       func_signature_string(fnName, nargs, input_types))));
+                       func_signature_string(fnName, nargs,
+                                             NIL, input_types))));
 
    /*
     * If there are any polymorphic types involved, enforce consistency, and
@@ -359,7 +362,8 @@ lookup_agg_function(List *fnName,
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
                     errmsg("function %s requires run-time type coercion",
-                    func_signature_string(fnName, nargs, true_oid_array))));
+                    func_signature_string(fnName, nargs,
+                                          NIL, true_oid_array))));
    }
 
    /* Check aggregate creator has permission to call the function */
index db69c127ad4798e9af80deb7e75cf792fd4c3efd..c7ee17a57b721da9e7f170cdd211f72281175747 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.167 2009/10/05 19:24:36 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.168 2009/10/08 02:39:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -348,6 +348,8 @@ ProcedureCreate(const char *procedureName,
    {
        /* There is one; okay to replace it? */
        Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup);
+       Datum       proargnames;
+       bool        isnull;
 
        if (!replace)
            ereport(ERROR,
@@ -393,6 +395,49 @@ ProcedureCreate(const char *procedureName,
                         errhint("Use DROP FUNCTION first.")));
        }
 
+       /*
+        * If there were any named input parameters, check to make sure the
+        * names have not been changed, as this could break existing calls.
+        * We allow adding names to formerly unnamed parameters, though.
+        */
+       proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+                                     Anum_pg_proc_proargnames,
+                                     &isnull);
+       if (!isnull)
+       {
+           Datum       proargmodes;
+           char      **old_arg_names;
+           char      **new_arg_names;
+           int         n_old_arg_names;
+           int         n_new_arg_names;
+           int         j;
+
+           proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup,
+                                         Anum_pg_proc_proargmodes,
+                                         &isnull);
+           if (isnull)
+               proargmodes = PointerGetDatum(NULL);    /* just to be sure */
+
+           n_old_arg_names = get_func_input_arg_names(proargnames,
+                                                      proargmodes,
+                                                      &old_arg_names);
+           n_new_arg_names = get_func_input_arg_names(parameterNames,
+                                                      parameterModes,
+                                                      &new_arg_names);
+           for (j = 0; j < n_old_arg_names; j++)
+           {
+               if (old_arg_names[j] == NULL)
+                   continue;
+               if (j >= n_new_arg_names || new_arg_names[j] == NULL ||
+                   strcmp(old_arg_names[j], new_arg_names[j]) != 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                            errmsg("cannot change name of input parameter \"%s\"",
+                                   old_arg_names[j]),
+                            errhint("Use DROP FUNCTION first.")));
+           }
+        }
+
        /*
         * If there are existing defaults, check compatibility: redefinition
         * must not remove any defaults nor change their types.  (Removing a
@@ -404,7 +449,6 @@ ProcedureCreate(const char *procedureName,
        if (oldproc->pronargdefaults != 0)
        {
            Datum       proargdefaults;
-           bool        isnull;
            List       *oldDefaults;
            ListCell   *oldlc;
            ListCell   *newlc;
index 461f81005c244c9da98353b3de1260df9f53157d..bafc28566063f067416557949440c67fd14d8716 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.49 2009/06/11 14:48:55 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.50 2009/10/08 02:39:18 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -297,6 +297,7 @@ RenameAggregate(List *name, List *args, const char *newname)
                 errmsg("function %s already exists in schema \"%s\"",
                        funcname_signature_string(newname,
                                                  procForm->pronargs,
+                                                 NIL,
                                               procForm->proargtypes.values),
                        get_namespace_name(namespaceOid))));
 
index cf206b3f090fee193cd2fd2d943a02f4d09d386c..40097a80c7245760753b5e3409090698f24b477e 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.111 2009/09/22 23:43:37 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.112 2009/10/08 02:39:19 tgl Exp $
  *
  * DESCRIPTION
  *   These routines take the parse tree and pick out the
@@ -285,6 +285,39 @@ examine_parameter_list(List *parameters, Oid languageOid,
 
        if (fp->name && fp->name[0])
        {
+           ListCell   *px;
+
+           /*
+            * As of Postgres 8.5 we disallow using the same name for two
+            * input or two output function parameters.  Depending on the
+            * function's language, conflicting input and output names might
+            * be bad too, but we leave it to the PL to complain if so.
+            */
+           foreach(px, parameters)
+           {
+               FunctionParameter *prevfp = (FunctionParameter *) lfirst(px);
+
+               if (prevfp == fp)
+                   break;
+               /* pure in doesn't conflict with pure out */
+               if ((fp->mode == FUNC_PARAM_IN ||
+                    fp->mode == FUNC_PARAM_VARIADIC) &&
+                   (prevfp->mode == FUNC_PARAM_OUT ||
+                    prevfp->mode == FUNC_PARAM_TABLE))
+                   continue;
+               if ((prevfp->mode == FUNC_PARAM_IN ||
+                    prevfp->mode == FUNC_PARAM_VARIADIC) &&
+                   (fp->mode == FUNC_PARAM_OUT ||
+                    fp->mode == FUNC_PARAM_TABLE))
+                   continue;
+               if (prevfp->name && prevfp->name[0] &&
+                   strcmp(prevfp->name, fp->name) == 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                            errmsg("parameter name \"%s\" used more than once",
+                                   fp->name)));
+           }
+
            paramNames[i] = CStringGetTextDatum(fp->name);
            have_names = true;
        }
@@ -1097,6 +1130,7 @@ RenameFunction(List *name, List *argtypes, const char *newname)
                 errmsg("function %s already exists in schema \"%s\"",
                        funcname_signature_string(newname,
                                                  procForm->pronargs,
+                                                 NIL,
                                               procForm->proargtypes.values),
                        get_namespace_name(namespaceOid))));
    }
index 5339e1783c44c10a638b39cf7407b88ace519e13..8696ea857302e12dd51221e35f91cee8869a77ce 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.17 2009/06/11 14:48:56 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tsearchcmds.c,v 1.18 2009/10/08 02:39:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -107,7 +107,7 @@ get_ts_parser_func(DefElem *defel, int attnum)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                 errmsg("function %s should return type %s",
-                       func_signature_string(funcName, nargs, typeId),
+                       func_signature_string(funcName, nargs, NIL, typeId),
                        format_type_be(retTypeId))));
 
    return ObjectIdGetDatum(procOid);
@@ -945,7 +945,7 @@ get_ts_template_func(DefElem *defel, int attnum)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
                 errmsg("function %s should return type %s",
-                       func_signature_string(funcName, nargs, typeId),
+                       func_signature_string(funcName, nargs, NIL, typeId),
                        format_type_be(retTypeId))));
 
    return ObjectIdGetDatum(procOid);
index 5fa2ac6bc90e235cc5f588d55529aeaefca4ca97..4f997a0dce1a7e01a12c4f8ccc90026cd1437ff0 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.137 2009/07/30 02:45:36 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.138 2009/10/08 02:39:19 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -1267,7 +1267,7 @@ findTypeInputFunction(List *procname, Oid typeOid)
    ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
             errmsg("function %s does not exist",
-                   func_signature_string(procname, 1, argList))));
+                   func_signature_string(procname, 1, NIL, argList))));
 
    return InvalidOid;          /* keep compiler quiet */
 }
@@ -1318,7 +1318,7 @@ findTypeOutputFunction(List *procname, Oid typeOid)
    ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
             errmsg("function %s does not exist",
-                   func_signature_string(procname, 1, argList))));
+                   func_signature_string(procname, 1, NIL, argList))));
 
    return InvalidOid;          /* keep compiler quiet */
 }
@@ -1349,7 +1349,7 @@ findTypeReceiveFunction(List *procname, Oid typeOid)
    ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
             errmsg("function %s does not exist",
-                   func_signature_string(procname, 1, argList))));
+                   func_signature_string(procname, 1, NIL, argList))));
 
    return InvalidOid;          /* keep compiler quiet */
 }
@@ -1372,7 +1372,7 @@ findTypeSendFunction(List *procname, Oid typeOid)
    ereport(ERROR,
            (errcode(ERRCODE_UNDEFINED_FUNCTION),
             errmsg("function %s does not exist",
-                   func_signature_string(procname, 1, argList))));
+                   func_signature_string(procname, 1, NIL, argList))));
 
    return InvalidOid;          /* keep compiler quiet */
 }
@@ -1393,7 +1393,7 @@ findTypeTypmodinFunction(List *procname)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(procname, 1, argList))));
+                       func_signature_string(procname, 1, NIL, argList))));
 
    if (get_func_rettype(procOid) != INT4OID)
        ereport(ERROR,
@@ -1420,7 +1420,7 @@ findTypeTypmodoutFunction(List *procname)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(procname, 1, argList))));
+                       func_signature_string(procname, 1, NIL, argList))));
 
    if (get_func_rettype(procOid) != CSTRINGOID)
        ereport(ERROR,
@@ -1447,7 +1447,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(procname, 1, argList))));
+                       func_signature_string(procname, 1, NIL, argList))));
 
    if (get_func_rettype(procOid) != BOOLOID)
        ereport(ERROR,
index 9319aa84c5edb1d9d525989b48246d105032d8c2..27c231701d6278b9c515a1e8cda2532f55191846 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.441 2009/10/07 22:14:20 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.442 2009/10/08 02:39:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1019,6 +1019,22 @@ _copyFuncExpr(FuncExpr *from)
    return newnode;
 }
 
+/*
+ * _copyNamedArgExpr *
+ */
+static NamedArgExpr *
+_copyNamedArgExpr(NamedArgExpr *from)
+{
+   NamedArgExpr *newnode = makeNode(NamedArgExpr);
+
+   COPY_NODE_FIELD(arg);
+   COPY_STRING_FIELD(name);
+   COPY_SCALAR_FIELD(argnumber);
+   COPY_LOCATION_FIELD(location);
+
+   return newnode;
+}
+
 /*
  * _copyOpExpr
  */
@@ -3587,6 +3603,9 @@ copyObject(void *from)
        case T_FuncExpr:
            retval = _copyFuncExpr(from);
            break;
+       case T_NamedArgExpr:
+           retval = _copyNamedArgExpr(from);
+           break;
        case T_OpExpr:
            retval = _copyOpExpr(from);
            break;
index 6a61112b99c366d960470dde1e310e4025eb129d..5766f37c14394ac0db80f76e1410ebc0f36a7897 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.364 2009/10/07 22:14:20 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.365 2009/10/08 02:39:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -241,6 +241,17 @@ _equalFuncExpr(FuncExpr *a, FuncExpr *b)
    return true;
 }
 
+static bool
+_equalNamedArgExpr(NamedArgExpr *a, NamedArgExpr *b)
+{
+   COMPARE_NODE_FIELD(arg);
+   COMPARE_STRING_FIELD(name);
+   COMPARE_SCALAR_FIELD(argnumber);
+   COMPARE_LOCATION_FIELD(location);
+
+   return true;
+}
+
 static bool
 _equalOpExpr(OpExpr *a, OpExpr *b)
 {
@@ -2375,6 +2386,9 @@ equal(void *a, void *b)
        case T_FuncExpr:
            retval = _equalFuncExpr(a, b);
            break;
+       case T_NamedArgExpr:
+           retval = _equalNamedArgExpr(a, b);
+           break;
        case T_OpExpr:
            retval = _equalOpExpr(a, b);
            break;
index 61c0e21db0843767bb587e04baaee94103494b10..04a39f17526ca1cf7bd2de79fe2ec97a9f800f0e 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.42 2009/07/30 02:45:37 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.43 2009/10/08 02:39:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -69,6 +69,9 @@ exprType(Node *expr)
        case T_FuncExpr:
            type = ((FuncExpr *) expr)->funcresulttype;
            break;
+       case T_NamedArgExpr:
+           type = exprType((Node *) ((NamedArgExpr *) expr)->arg);
+           break;
        case T_OpExpr:
            type = ((OpExpr *) expr)->opresulttype;
            break;
@@ -259,6 +262,8 @@ exprTypmod(Node *expr)
                    return coercedTypmod;
            }
            break;
+       case T_NamedArgExpr:
+           return exprTypmod((Node *) ((NamedArgExpr *) expr)->arg);
        case T_SubLink:
            {
                SubLink    *sublink = (SubLink *) expr;
@@ -676,6 +681,15 @@ exprLocation(Node *expr)
                                  exprLocation((Node *) fexpr->args));
            }
            break;
+       case T_NamedArgExpr:
+           {
+               NamedArgExpr *na = (NamedArgExpr *) expr;
+
+               /* consider both argument name and value */
+               loc = leftmostLoc(na->location,
+                                 exprLocation((Node *) na->arg));
+           }
+           break;
        case T_OpExpr:
        case T_DistinctExpr:    /* struct-equivalent to OpExpr */
        case T_NullIfExpr:      /* struct-equivalent to OpExpr */
@@ -1117,6 +1131,8 @@ expression_tree_walker(Node *node,
                    return true;
            }
            break;
+       case T_NamedArgExpr:
+           return walker(((NamedArgExpr *) node)->arg, context);
        case T_OpExpr:
            {
                OpExpr     *expr = (OpExpr *) node;
@@ -1623,6 +1639,16 @@ expression_tree_mutator(Node *node,
                return (Node *) newnode;
            }
            break;
+       case T_NamedArgExpr:
+           {
+               NamedArgExpr *nexpr = (NamedArgExpr *) node;
+               NamedArgExpr *newnode;
+
+               FLATCOPY(newnode, nexpr, NamedArgExpr);
+               MUTATE(newnode->arg, nexpr->arg, Expr *);
+               return (Node *) newnode;
+           }
+           break;
        case T_OpExpr:
            {
                OpExpr     *expr = (OpExpr *) node;
@@ -2382,6 +2408,8 @@ bool
                /* function name is deemed uninteresting */
            }
            break;
+       case T_NamedArgExpr:
+           return walker(((NamedArgExpr *) node)->arg, context);
        case T_A_Indices:
            {
                A_Indices  *indices = (A_Indices *) node;
index 6751cb1a344635a5cacb0a1546e1570a7601b4bd..79665ed12a04c44cc9f61a7eaf865483c1374232 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.365 2009/10/06 00:55:26 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.366 2009/10/08 02:39:21 tgl Exp $
  *
  * NOTES
  *   Every node type that can appear in stored rules' parsetrees *must*
@@ -875,6 +875,17 @@ _outFuncExpr(StringInfo str, FuncExpr *node)
    WRITE_LOCATION_FIELD(location);
 }
 
+static void
+_outNamedArgExpr(StringInfo str, NamedArgExpr *node)
+{
+   WRITE_NODE_TYPE("NAMEDARGEXPR");
+
+   WRITE_NODE_FIELD(arg);
+   WRITE_STRING_FIELD(name);
+   WRITE_INT_FIELD(argnumber);
+   WRITE_LOCATION_FIELD(location);
+}
+
 static void
 _outOpExpr(StringInfo str, OpExpr *node)
 {
@@ -2514,6 +2525,9 @@ _outNode(StringInfo str, void *obj)
            case T_FuncExpr:
                _outFuncExpr(str, obj);
                break;
+           case T_NamedArgExpr:
+               _outNamedArgExpr(str, obj);
+               break;
            case T_OpExpr:
                _outOpExpr(str, obj);
                break;
index 8f5264d3575e394f6f8ff41c5c43f4198d94ff09..205b6da4cd87d2a5267eaaa467557c8c3e61310c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.223 2009/07/16 06:33:42 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/readfuncs.c,v 1.224 2009/10/08 02:39:21 tgl Exp $
  *
  * NOTES
  *   Path and Plan nodes do not have any readfuncs support, because we
@@ -525,6 +525,22 @@ _readFuncExpr(void)
    READ_DONE();
 }
 
+/*
+ * _readNamedArgExpr
+ */
+static NamedArgExpr *
+_readNamedArgExpr(void)
+{
+   READ_LOCALS(NamedArgExpr);
+
+   READ_NODE_FIELD(arg);
+   READ_STRING_FIELD(name);
+   READ_INT_FIELD(argnumber);
+   READ_LOCATION_FIELD(location);
+
+   READ_DONE();
+}
+
 /*
  * _readOpExpr
  */
@@ -1207,6 +1223,8 @@ parseNodeString(void)
        return_value = _readArrayRef();
    else if (MATCH("FUNCEXPR", 8))
        return_value = _readFuncExpr();
+   else if (MATCH("NAMEDARGEXPR", 12))
+       return_value = _readNamedArgExpr();
    else if (MATCH("OPEXPR", 6))
        return_value = _readOpExpr();
    else if (MATCH("DISTINCTEXPR", 12))
index 3f344b3a145177e12e70b56d9b6416f9af263291..64f77a5c71e38dbe038b808c084274e299401739 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.256 2009/06/11 14:48:59 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.257 2009/10/08 02:39:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -526,7 +526,8 @@ preprocess_expression(PlannerInfo *root, Node *expr, int kind)
    /*
     * Simplify constant expressions.
     *
-    * Note: one essential effect here is to insert the current actual values
+    * Note: an essential effect of this is to convert named-argument function
+    * calls to positional notation and insert the current actual values
     * of any default arguments for functions.  To ensure that happens, we
     * *must* process all expressions here.  Previous PG versions sometimes
     * skipped const-simplification if it didn't seem worth the trouble, but
@@ -2658,9 +2659,10 @@ get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
  * Currently, we disallow sublinks in standalone expressions, so there's no
  * real "planning" involved here.  (That might not always be true though.)
  * What we must do is run eval_const_expressions to ensure that any function
- * default arguments get inserted. The fact that constant subexpressions
- * get simplified is a side-effect that is useful when the expression will
- * get evaluated more than once.  Also, we must fix operator function IDs.
+ * calls are converted to positional notation and function default arguments
+ * get inserted.  The fact that constant subexpressions get simplified is a
+ * side-effect that is useful when the expression will get evaluated more than
+ * once.  Also, we must fix operator function IDs.
  *
  * Note: this must not make any damaging changes to the passed-in expression
  * tree.  (It would actually be okay to apply fix_opfuncids to it, but since
@@ -2672,7 +2674,10 @@ expression_planner(Expr *expr)
 {
    Node       *result;
 
-   /* Insert default arguments and simplify constant subexprs */
+   /*
+    * Convert named-argument function calls, insert default arguments and
+    * simplify constant subexprs
+    */
    result = eval_const_expressions(NULL, (Node *) expr);
 
    /* Fill in opfuncid values if missing */
index f2038c73af14282d0f52ae3c2d599d07a542f6ec..dcfc731a17a087ff7fb78a55e25d69f03cc73852 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.278 2009/07/20 00:24:30 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.279 2009/10/08 02:39:21 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -95,11 +95,18 @@ static List *simplify_and_arguments(List *args,
 static Expr *simplify_boolean_equality(Oid opno, List *args);
 static Expr *simplify_function(Oid funcid,
                  Oid result_type, int32 result_typmod, List **args,
+                 bool has_named_args,
                  bool allow_inline,
                  eval_const_expressions_context *context);
+static List *reorder_function_arguments(List *args, Oid result_type,
+                          HeapTuple func_tuple,
+                          eval_const_expressions_context *context);
 static List *add_function_defaults(List *args, Oid result_type,
                      HeapTuple func_tuple,
                      eval_const_expressions_context *context);
+static List *fetch_function_defaults(HeapTuple func_tuple);
+static void recheck_cast_function_args(List *args, Oid result_type,
+                                      HeapTuple func_tuple);
 static Expr *evaluate_function(Oid funcid,
                  Oid result_type, int32 result_typmod, List *args,
                  HeapTuple func_tuple,
@@ -2003,7 +2010,8 @@ rowtype_field_matches(Oid rowtypeid, int fieldnum,
  * OR clauses into N-argument form.  See comments in prepqual.c.
  *
  * NOTE: another critical effect is that any function calls that require
- * default arguments will be expanded.
+ * default arguments will be expanded, and named-argument calls will be
+ * converted to positional notation.  The executor won't handle either.
  *--------------------
  */
 Node *
@@ -2113,17 +2121,26 @@ eval_const_expressions_mutator(Node *node,
    {
        FuncExpr   *expr = (FuncExpr *) node;
        List       *args;
+       bool        has_named_args;
        Expr       *simple;
        FuncExpr   *newexpr;
+       ListCell   *lc;
 
        /*
-        * Reduce constants in the FuncExpr's arguments.  We know args is
-        * either NIL or a List node, so we can call expression_tree_mutator
-        * directly rather than recursing to self.
+        * Reduce constants in the FuncExpr's arguments, and check to see
+        * if there are any named args.
         */
-       args = (List *) expression_tree_mutator((Node *) expr->args,
-                                             eval_const_expressions_mutator,
-                                               (void *) context);
+       args = NIL;
+       has_named_args = false;
+       foreach(lc, expr->args)
+       {
+           Node   *arg = (Node *) lfirst(lc);
+
+           arg = eval_const_expressions_mutator(arg, context);
+           if (IsA(arg, NamedArgExpr))
+               has_named_args = true;
+           args = lappend(args, arg);
+       }
 
        /*
         * Code for op/func reduction is pretty bulky, so split it out as a
@@ -2134,14 +2151,15 @@ eval_const_expressions_mutator(Node *node,
        simple = simplify_function(expr->funcid,
                                   expr->funcresulttype, exprTypmod(node),
                                   &args,
-                                  true, context);
+                                  has_named_args, true, context);
        if (simple)             /* successfully simplified it */
            return (Node *) simple;
 
        /*
         * The expression cannot be simplified any further, so build and
         * return a replacement FuncExpr node using the possibly-simplified
-        * arguments.
+        * arguments.  Note that we have also converted the argument list
+        * to positional notation.
         */
        newexpr = makeNode(FuncExpr);
        newexpr->funcid = expr->funcid;
@@ -2181,7 +2199,7 @@ eval_const_expressions_mutator(Node *node,
        simple = simplify_function(expr->opfuncid,
                                   expr->opresulttype, -1,
                                   &args,
-                                  true, context);
+                                  false, true, context);
        if (simple)             /* successfully simplified it */
            return (Node *) simple;
 
@@ -2274,7 +2292,7 @@ eval_const_expressions_mutator(Node *node,
            simple = simplify_function(expr->opfuncid,
                                       expr->opresulttype, -1,
                                       &args,
-                                      false, context);
+                                      false, false, context);
            if (simple)         /* successfully simplified it */
            {
                /*
@@ -2466,7 +2484,7 @@ eval_const_expressions_mutator(Node *node,
        simple = simplify_function(outfunc,
                                   CSTRINGOID, -1,
                                   &args,
-                                  true, context);
+                                  false, true, context);
        if (simple)             /* successfully simplified output fn */
        {
            /*
@@ -2484,7 +2502,7 @@ eval_const_expressions_mutator(Node *node,
            simple = simplify_function(infunc,
                                       expr->resulttype, -1,
                                       &args,
-                                      true, context);
+                                      false, true, context);
            if (simple)         /* successfully simplified input fn */
                return (Node *) simple;
        }
@@ -3241,15 +3259,16 @@ simplify_boolean_equality(Oid opno, List *args)
  * Returns a simplified expression if successful, or NULL if cannot
  * simplify the function call.
  *
- * This function is also responsible for adding any default argument
- * expressions onto the function argument list; which is a bit grotty,
- * but it avoids an extra fetch of the function's pg_proc tuple.  For this
- * reason, the args list is pass-by-reference, and it may get modified
- * even if simplification fails.
+ * This function is also responsible for converting named-notation argument
+ * lists into positional notation and/or adding any needed default argument
+ * expressions; which is a bit grotty, but it avoids an extra fetch of the
+ * function's pg_proc tuple.  For this reason, the args list is
+ * pass-by-reference, and it may get modified even if simplification fails.
  */
 static Expr *
 simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
                  List **args,
+                 bool has_named_args,
                  bool allow_inline,
                  eval_const_expressions_context *context)
 {
@@ -3270,8 +3289,14 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
    if (!HeapTupleIsValid(func_tuple))
        elog(ERROR, "cache lookup failed for function %u", funcid);
 
-   /* While we have the tuple, check if we need to add defaults */
-   if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
+   /*
+    * While we have the tuple, reorder named arguments and add default
+    * arguments if needed.
+    */
+   if (has_named_args)
+       *args = reorder_function_arguments(*args, result_type, func_tuple,
+                                          context);
+   else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
        *args = add_function_defaults(*args, result_type, func_tuple, context);
 
    newexpr = evaluate_function(funcid, result_type, result_typmod, *args,
@@ -3286,13 +3311,113 @@ simplify_function(Oid funcid, Oid result_type, int32 result_typmod,
    return newexpr;
 }
 
+/*
+ * reorder_function_arguments: convert named-notation args to positional args
+ *
+ * This function also inserts default argument values as needed, since it's
+ * impossible to form a truly valid positional call without that.
+ */
+static List *
+reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
+                          eval_const_expressions_context *context)
+{
+   Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+   int         pronargs = funcform->pronargs;
+   int         nargsprovided = list_length(args);
+   Node       *argarray[FUNC_MAX_ARGS];
+   Bitmapset  *defargnumbers;
+   ListCell   *lc;
+   int         i;
+
+   Assert(nargsprovided <= pronargs);
+   if (pronargs > FUNC_MAX_ARGS)
+       elog(ERROR, "too many function arguments");
+   MemSet(argarray, 0, pronargs * sizeof(Node *));
+
+   /* Deconstruct the argument list into an array indexed by argnumber */
+   i = 0;
+   foreach(lc, args)
+   {
+       Node   *arg = (Node *) lfirst(lc);
+
+       if (!IsA(arg, NamedArgExpr))
+       {
+           /* positional argument, assumed to precede all named args */
+           Assert(argarray[i] == NULL);
+           argarray[i++] = arg;
+       }
+       else
+       {
+           NamedArgExpr *na = (NamedArgExpr *) arg;
+
+           Assert(argarray[na->argnumber] == NULL);
+           argarray[na->argnumber] = (Node *) na->arg;
+       }
+   }
+
+   /*
+    * Fetch default expressions, if needed, and insert into array at
+    * proper locations (they aren't necessarily consecutive or all used)
+    */
+   defargnumbers = NULL;
+   if (nargsprovided < pronargs)
+   {
+       List   *defaults = fetch_function_defaults(func_tuple);
+
+       i = pronargs - funcform->pronargdefaults;
+       foreach(lc, defaults)
+       {
+           if (argarray[i] == NULL)
+           {
+               argarray[i] = (Node *) lfirst(lc);
+               defargnumbers = bms_add_member(defargnumbers, i);
+           }
+           i++;
+       }
+   }
+
+   /* Now reconstruct the args list in proper order */
+   args = NIL;
+   for (i = 0; i < pronargs; i++)
+   {
+       Assert(argarray[i] != NULL);
+       args = lappend(args, argarray[i]);
+   }
+
+   /* Recheck argument types and add casts if needed */
+   recheck_cast_function_args(args, result_type, func_tuple);
+
+   /*
+    * Lastly, we have to recursively simplify the defaults we just added
+    * (but don't recurse on the args passed in, as we already did those).
+    * This isn't merely an optimization, it's *necessary* since there could
+    * be functions with named or defaulted arguments down in there.
+    *
+    * Note that we do this last in hopes of simplifying any typecasts that
+    * were added by recheck_cast_function_args --- there shouldn't be any new
+    * casts added to the explicit arguments, but casts on the defaults are
+    * possible.
+    */
+   if (defargnumbers != NULL)
+   {
+       i = 0;
+       foreach(lc, args)
+       {
+           if (bms_is_member(i, defargnumbers))
+               lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
+                                                           context);
+           i++;
+       }
+   }
+
+   return args;
+}
+
 /*
  * add_function_defaults: add missing function arguments from its defaults
  *
- * It is possible for some of the defaulted arguments to be polymorphic;
- * therefore we can't assume that the default expressions have the correct
- * data types already. We have to re-resolve polymorphics and do coercion
- * just like the parser did.
+ * This is used only when the argument list was positional to begin with,
+ * and so we know we just need to add defaults at the end.
  */
 static List *
 add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
@@ -3300,41 +3425,96 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
 {
    Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
    int         nargsprovided = list_length(args);
-   Datum       proargdefaults;
-   bool        isnull;
-   char       *str;
    List       *defaults;
    int         ndelete;
-   int         nargs;
-   Oid         actual_arg_types[FUNC_MAX_ARGS];
-   Oid         declared_arg_types[FUNC_MAX_ARGS];
-   Oid         rettype;
    ListCell   *lc;
 
-   /* The error cases here shouldn't happen, but check anyway */
-   proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
-                                    Anum_pg_proc_proargdefaults,
-                                    &isnull);
-   if (isnull)
-       elog(ERROR, "not enough default arguments");
-   str = TextDatumGetCString(proargdefaults);
-   defaults = (List *) stringToNode(str);
-   Assert(IsA(defaults, List));
-   pfree(str);
+   /* Get all the default expressions from the pg_proc tuple */
+   defaults = fetch_function_defaults(func_tuple);
+
    /* Delete any unused defaults from the list */
    ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
    if (ndelete < 0)
        elog(ERROR, "not enough default arguments");
    while (ndelete-- > 0)
        defaults = list_delete_first(defaults);
+
    /* And form the combined argument list */
    args = list_concat(args, defaults);
-   Assert(list_length(args) == funcform->pronargs);
+
+   /* Recheck argument types and add casts if needed */
+   recheck_cast_function_args(args, result_type, func_tuple);
 
    /*
-    * The next part should be a no-op if there are no polymorphic arguments,
-    * but we do it anyway to be sure.
+    * Lastly, we have to recursively simplify the defaults we just added
+    * (but don't recurse on the args passed in, as we already did those).
+    * This isn't merely an optimization, it's *necessary* since there could
+    * be functions with named or defaulted arguments down in there.
+    *
+    * Note that we do this last in hopes of simplifying any typecasts that
+    * were added by recheck_cast_function_args --- there shouldn't be any new
+    * casts added to the explicit arguments, but casts on the defaults are
+    * possible.
     */
+   foreach(lc, args)
+   {
+       if (nargsprovided-- > 0)
+           continue;           /* skip original arg positions */
+       lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
+                                                   context);
+   }
+
+   return args;
+}
+
+/*
+ * fetch_function_defaults: get function's default arguments as expression list
+ */
+static List *
+fetch_function_defaults(HeapTuple func_tuple)
+{
+   List       *defaults;
+   Datum       proargdefaults;
+   bool        isnull;
+   char       *str;
+
+   /* The error cases here shouldn't happen, but check anyway */
+   proargdefaults = SysCacheGetAttr(PROCOID, func_tuple,
+                                    Anum_pg_proc_proargdefaults,
+                                    &isnull);
+   if (isnull)
+       elog(ERROR, "not enough default arguments");
+   str = TextDatumGetCString(proargdefaults);
+   defaults = (List *) stringToNode(str);
+   Assert(IsA(defaults, List));
+   pfree(str);
+   return defaults;
+}
+
+/*
+ * recheck_cast_function_args: recheck function args and typecast as needed
+ * after adding defaults.
+ *
+ * It is possible for some of the defaulted arguments to be polymorphic;
+ * therefore we can't assume that the default expressions have the correct
+ * data types already. We have to re-resolve polymorphics and do coercion
+ * just like the parser did.
+ *
+ * This should be a no-op if there are no polymorphic arguments,
+ * but we do it anyway to be sure.
+ *
+ * Note: if any casts are needed, the args list is modified in-place.
+ */
+static void
+recheck_cast_function_args(List *args, Oid result_type, HeapTuple func_tuple)
+{
+   Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+   int         nargs;
+   Oid         actual_arg_types[FUNC_MAX_ARGS];
+   Oid         declared_arg_types[FUNC_MAX_ARGS];
+   Oid         rettype;
+   ListCell   *lc;
+
    if (list_length(args) > FUNC_MAX_ARGS)
        elog(ERROR, "too many function arguments");
    nargs = 0;
@@ -3342,6 +3522,7 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
    {
        actual_arg_types[nargs++] = exprType((Node *) lfirst(lc));
    }
+   Assert(nargs == funcform->pronargs);
    memcpy(declared_arg_types, funcform->proargtypes.values,
           funcform->pronargs * sizeof(Oid));
    rettype = enforce_generic_type_consistency(actual_arg_types,
@@ -3355,22 +3536,6 @@ add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
 
    /* perform any necessary typecasting of arguments */
    make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types);
-
-   /*
-    * Lastly, we have to recursively simplify the arguments we just added
-    * (but don't recurse on the ones passed in, as we already did those).
-    * This isn't merely an optimization, it's *necessary* since there could
-    * be functions with defaulted arguments down in there.
-    */
-   foreach(lc, args)
-   {
-       if (nargsprovided-- > 0)
-           continue;           /* skip original arg positions */
-       lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
-                                                   context);
-   }
-
-   return args;
 }
 
 /*
@@ -3916,6 +4081,7 @@ Query *
 inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
 {
    FuncExpr   *fexpr;
+   Oid         func_oid;
    HeapTuple   func_tuple;
    Form_pg_proc funcform;
    Oid        *argtypes;
@@ -3944,6 +4110,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
    fexpr = (FuncExpr *) rte->funcexpr;
    if (fexpr == NULL || !IsA(fexpr, FuncExpr))
        return NULL;
+   func_oid = fexpr->funcid;
 
    /*
     * The function must be declared to return a set, else inlining would
@@ -3967,17 +4134,17 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
        return NULL;
 
    /* Check permission to call function (fail later, if not) */
-   if (pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
+   if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK)
        return NULL;
 
    /*
     * OK, let's take a look at the function's pg_proc entry.
     */
    func_tuple = SearchSysCache(PROCOID,
-                               ObjectIdGetDatum(fexpr->funcid),
+                               ObjectIdGetDatum(func_oid),
                                0, 0, 0);
    if (!HeapTupleIsValid(func_tuple))
-       elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
+       elog(ERROR, "cache lookup failed for function %u", func_oid);
    funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
 
    /*
@@ -3985,16 +4152,15 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
     * properties.  In particular it mustn't be declared STRICT, since we
     * couldn't enforce that.  It also mustn't be VOLATILE, because that is
     * supposed to cause it to be executed with its own snapshot, rather than
-    * sharing the snapshot of the calling query.  (The nargs check is just
-    * paranoia, ditto rechecking proretset.)
+    * sharing the snapshot of the calling query.  (Rechecking proretset is
+    * just paranoia.)
     */
    if (funcform->prolang != SQLlanguageId ||
        funcform->proisstrict ||
        funcform->provolatile == PROVOLATILE_VOLATILE ||
        funcform->prosecdef ||
        !funcform->proretset ||
-       !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
-       funcform->pronargs != list_length(fexpr->args))
+       !heap_attisnull(func_tuple, Anum_pg_proc_proconfig))
    {
        ReleaseSysCache(func_tuple);
        return NULL;
@@ -4020,6 +4186,24 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
                                  ALLOCSET_DEFAULT_MAXSIZE);
    oldcxt = MemoryContextSwitchTo(mycxt);
 
+   /*
+    * Run eval_const_expressions on the function call.  This is necessary
+    * to ensure that named-argument notation is converted to positional
+    * notation and any default arguments are inserted.  It's a bit of
+    * overkill for the arguments, since they'll get processed again later,
+    * but no harm will be done.
+    */
+   fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr);
+
+   /* It should still be a call of the same function, but let's check */
+   if (!IsA(fexpr, FuncExpr) ||
+       fexpr->funcid != func_oid)
+       goto fail;
+
+   /* Arg list length should now match the function */
+   if (list_length(fexpr->args) != funcform->pronargs)
+       goto fail;
+
    /* Check for polymorphic arguments, and substitute actual arg types */
    argtypes = (Oid *) palloc(funcform->pronargs * sizeof(Oid));
    memcpy(argtypes, funcform->proargtypes.values,
@@ -4038,7 +4222,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
                          Anum_pg_proc_prosrc,
                          &isNull);
    if (isNull)
-       elog(ERROR, "null prosrc for function %u", fexpr->funcid);
+       elog(ERROR, "null prosrc for function %u", func_oid);
    src = TextDatumGetCString(tmp);
 
    /*
@@ -4076,7 +4260,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
     * shows it's returning a whole tuple result; otherwise what it's
     * returning is a single composite column which is not what we need.
     */
-   if (!check_sql_fn_retval(fexpr->funcid, fexpr->funcresulttype,
+   if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype,
                             querytree_list,
                             true, NULL) &&
        (get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE ||
@@ -4116,7 +4300,7 @@ inline_set_returning_function(PlannerInfo *root, RangeTblEntry *rte)
     * Since there is now no trace of the function in the plan tree, we must
     * explicitly record the plan's dependency on the function.
     */
-   record_plan_function_dependency(root->glob, fexpr->funcid);
+   record_plan_function_dependency(root->glob, func_oid);
 
    return querytree;
 
index ed265f516b293deda53d3a5ed3cc3c1179baa0a0..b86df8a870f9293bcf3a1c1117937fefa83fb09f 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.681 2009/10/07 22:14:21 alvherre Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.682 2009/10/08 02:39:22 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -354,6 +354,8 @@ static TypeName *TableFuncTypeName(List *columns);
 %type <node>   def_arg columnElem where_clause where_or_current_clause
                a_expr b_expr c_expr func_expr AexprConst indirection_el
                columnref in_expr having_clause func_table array_expr
+%type <list>   func_arg_list
+%type <node>   func_arg_expr
 %type <list>   row type_list array_expr_list
 %type <node>   case_expr case_arg when_clause case_default
 %type <list>   when_clause_list
@@ -9055,7 +9057,7 @@ func_expr:    func_name '(' ')' over_clause
                    n->location = @1;
                    $$ = (Node *)n;
                }
-           | func_name '(' expr_list ')' over_clause
+           | func_name '(' func_arg_list ')' over_clause
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
@@ -9067,7 +9069,7 @@ func_expr:    func_name '(' ')' over_clause
                    n->location = @1;
                    $$ = (Node *)n;
                }
-           | func_name '(' VARIADIC a_expr ')' over_clause
+           | func_name '(' VARIADIC func_arg_expr ')' over_clause
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
@@ -9079,7 +9081,7 @@ func_expr:    func_name '(' ')' over_clause
                    n->location = @1;
                    $$ = (Node *)n;
                }
-           | func_name '(' expr_list ',' VARIADIC a_expr ')' over_clause
+           | func_name '(' func_arg_list ',' VARIADIC func_arg_expr ')' over_clause
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
@@ -9091,7 +9093,7 @@ func_expr:    func_name '(' ')' over_clause
                    n->location = @1;
                    $$ = (Node *)n;
                }
-           | func_name '(' ALL expr_list ')' over_clause
+           | func_name '(' ALL func_arg_list ')' over_clause
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
@@ -9107,7 +9109,7 @@ func_expr:    func_name '(' ')' over_clause
                    n->location = @1;
                    $$ = (Node *)n;
                }
-           | func_name '(' DISTINCT expr_list ')' over_clause
+           | func_name '(' DISTINCT func_arg_list ')' over_clause
                {
                    FuncCall *n = makeNode(FuncCall);
                    n->funcname = $1;
@@ -9830,6 +9832,32 @@ expr_list:   a_expr
                }
        ;
 
+/* function arguments can have names */
+func_arg_list:  func_arg_expr
+               {
+                   $$ = list_make1($1);
+               }
+           | func_arg_list ',' func_arg_expr
+               {
+                   $$ = lappend($1, $3);
+               }
+       ;
+
+func_arg_expr:  a_expr
+               {
+                   $$ = $1;
+               }
+           | a_expr AS param_name
+               {
+                   NamedArgExpr *na = makeNode(NamedArgExpr);
+                   na->arg = (Expr *) $1;
+                   na->name = $3;
+                   na->argnumber = -1;     /* until determined */
+                   na->location = @3;
+                   $$ = (Node *) na;
+               }
+       ;
+
 type_list: Typename                                { $$ = list_make1($1); }
            | type_list ',' Typename                { $$ = lappend($1, $3); }
        ;
@@ -10296,10 +10324,27 @@ AexprConst: Iconst
                    t->location = @1;
                    $$ = makeStringConstCast($2, @2, t);
                }
-           | func_name '(' expr_list ')' Sconst
+           | func_name '(' func_arg_list ')' Sconst
                {
                    /* generic syntax with a type modifier */
                    TypeName *t = makeTypeNameFromNameList($1);
+                   ListCell *lc;
+
+                   /*
+                    * We must use func_arg_list in the production to avoid
+                    * reduce/reduce conflicts, but we don't actually wish
+                    * to allow NamedArgExpr in this context.
+                    */
+                   foreach(lc, $3)
+                   {
+                       NamedArgExpr *arg = (NamedArgExpr *) lfirst(lc);
+
+                       if (IsA(arg, NamedArgExpr))
+                           ereport(ERROR,
+                                   (errcode(ERRCODE_SYNTAX_ERROR),
+                                    errmsg("type modifier cannot have AS name"),
+                                    parser_errposition(arg->location)));
+                   }
                    t->typmods = $3;
                    t->location = @1;
                    $$ = makeStringConstCast($5, @5, t);
index 39d66dcc12f675c6ae39776b30e0d9aa803f97db..3fccba1ab5aee531fc54b0c493a09994c968fbf3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.243 2009/09/09 03:32:52 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.244 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -228,6 +228,15 @@ transformExpr(ParseState *pstate, Node *expr)
            result = transformFuncCall(pstate, (FuncCall *) expr);
            break;
 
+       case T_NamedArgExpr:
+           {
+               NamedArgExpr *na = (NamedArgExpr *) expr;
+
+               na->arg = (Expr *) transformExpr(pstate, (Node *) na->arg);
+               result = expr;
+               break;
+           }
+
        case T_SubLink:
            result = transformSubLink(pstate, (SubLink *) expr);
            break;
index fd0706e96086b5237016f3a0a3580a8fb2f9c3fd..e752dd8d1ec19d3d2911a9d668bc35403522176b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.216 2009/06/11 14:49:00 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.217 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +70,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
    int         nargsplusdefs;
    Oid         actual_arg_types[FUNC_MAX_ARGS];
    Oid        *declared_arg_types;
+   List       *argnames;
    List       *argdefaults;
    Node       *retval;
    bool        retset;
@@ -117,6 +118,46 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
        actual_arg_types[nargs++] = argtype;
    }
 
+   /*
+    * Check for named arguments; if there are any, build a list of names.
+    *
+    * We allow mixed notation (some named and some not), but only with all
+    * the named parameters after all the unnamed ones.  So the name list
+    * corresponds to the last N actual parameters and we don't need any
+    * extra bookkeeping to match things up.
+    */
+   argnames = NIL;
+   foreach(l, fargs)
+   {
+       Node   *arg = lfirst(l);
+
+       if (IsA(arg, NamedArgExpr))
+       {
+           NamedArgExpr *na = (NamedArgExpr *) arg;
+           ListCell   *lc;
+
+           /* Reject duplicate arg names */
+           foreach(lc, argnames)
+           {
+               if (strcmp(na->name, (char *) lfirst(lc)) == 0)
+                   ereport(ERROR,
+                           (errcode(ERRCODE_SYNTAX_ERROR),
+                            errmsg("argument name \"%s\" used more than once",
+                                   na->name),
+                            parser_errposition(pstate, na->location)));
+           }
+           argnames = lappend(argnames, na->name);
+       }
+       else
+       {
+           if (argnames != NIL)
+               ereport(ERROR,
+                       (errcode(ERRCODE_SYNTAX_ERROR),
+                        errmsg("positional argument cannot follow named argument"),
+                        parser_errposition(pstate, exprLocation(arg))));
+       }
+   }
+
    if (fargs)
    {
        first_arg = linitial(fargs);
@@ -127,10 +168,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 or variadic decoration.
+    * wasn't any aggregate or variadic decoration, nor an argument name.
     */
    if (nargs == 1 && !agg_star && !agg_distinct && over == NULL &&
-       !func_variadic && list_length(funcname) == 1)
+       !func_variadic && argnames == NIL && list_length(funcname) == 1)
    {
        Oid         argtype = actual_arg_types[0];
 
@@ -156,12 +197,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
     * 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.  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.  We may also have to deal with default arguments.
+    * the function.
+    *
+    * Note: for a named-notation or variadic function call, the reported
+    * "true" types aren't really what is in pg_proc: the types are reordered
+    * to match the given argument order of named arguments, and a variadic
+    * argument is replaced by a suitable number of copies of its element
+    * type.  We'll fix up the variadic case below.  We may also have to deal
+    * with default arguments.
     */
-   fdresult = func_get_detail(funcname, fargs, nargs, actual_arg_types,
+   fdresult = func_get_detail(funcname, fargs, argnames, nargs,
+                              actual_arg_types,
                               !func_variadic, true,
                               &funcid, &rettype, &retset, &nvargs,
                               &declared_arg_types, &argdefaults);
@@ -225,7 +271,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
            ereport(ERROR,
                    (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
                     errmsg("function %s is not unique",
-                           func_signature_string(funcname, nargs,
+                           func_signature_string(funcname, nargs, argnames,
                                                  actual_arg_types)),
                     errhint("Could not choose a best candidate function. "
                             "You might need to add explicit type casts."),
@@ -234,7 +280,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_FUNCTION),
                     errmsg("function %s does not exist",
-                           func_signature_string(funcname, nargs,
+                           func_signature_string(funcname, nargs, argnames,
                                                  actual_arg_types)),
            errhint("No function matches the given name and argument types. "
                    "You might need to add explicit type casts."),
@@ -353,6 +399,18 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                     errmsg("aggregates cannot return sets"),
                     parser_errposition(pstate, location)));
 
+       /*
+        * Currently it's not possible to define an aggregate with named
+        * arguments, so this case should be impossible.  Check anyway
+        * because the planner and executor wouldn't cope with NamedArgExprs
+        * in an Aggref node.
+        */
+       if (argnames != NIL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("aggregates cannot use named arguments"),
+                    parser_errposition(pstate, location)));
+
        /* parse_agg.c does additional aggregate-specific processing */
        transformAggregateCall(pstate, aggref);
 
@@ -406,6 +464,17 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                     errmsg("window functions cannot return sets"),
                     parser_errposition(pstate, location)));
 
+       /*
+        * We might want to support this later, but for now reject it
+        * because the planner and executor wouldn't cope with NamedArgExprs
+        * in a WindowFunc node.
+        */
+       if (argnames != NIL)
+           ereport(ERROR,
+                   (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                    errmsg("window functions cannot use named arguments"),
+                    parser_errposition(pstate, location)));
+
        /* parse_agg.c does additional window-func-specific processing */
        transformWindowFuncCall(pstate, wfunc, over);
 
@@ -801,14 +870,29 @@ func_select_candidate(int nargs,
  * 1) check for possible interpretation as a type coercion request
  * 2) apply the ambiguous-function resolution rules
  *
- * Note: we rely primarily on nargs/argtypes as the argument description.
+ * Return values *funcid through *true_typeids receive info about the function.
+ * If argdefaults isn't NULL, *argdefaults receives a list of any default
+ * argument expressions that need to be added to the given arguments.
+ *
+ * When processing a named- or mixed-notation call (ie, fargnames isn't NIL),
+ * the returned true_typeids and argdefaults are ordered according to the
+ * call's argument ordering: first any positional arguments, then the named
+ * arguments, then defaulted arguments (if needed and allowed by
+ * expand_defaults).  Some care is needed if this information is to be compared
+ * to the function's pg_proc entry, but in practice the caller can usually
+ * just work with the call's argument ordering.
+ *
+ * We rely primarily on fargnames/nargs/argtypes as the argument description.
  * The actual expression node list is passed in fargs so that we can check
- * for type coercion of a constant.  Some callers pass fargs == NIL
- * indicating they don't want that check made.
+ * for type coercion of a constant.  Some callers pass fargs == NIL indicating
+ * they don't need that check made.  Note also that when fargnames isn't NIL,
+ * the fargs list must be passed if the caller wants actual argument position
+ * information to be returned into the NamedArgExpr nodes.
  */
 FuncDetailCode
 func_get_detail(List *funcname,
                List *fargs,
+               List *fargnames,
                int nargs,
                Oid *argtypes,
                bool expand_variadic,
@@ -833,7 +917,7 @@ func_get_detail(List *funcname,
        *argdefaults = NIL;
 
    /* Get list of possible candidates from namespace search */
-   raw_candidates = FuncnameGetCandidates(funcname, nargs,
+   raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
                                           expand_variadic, expand_defaults);
 
    /*
@@ -884,7 +968,7 @@ func_get_detail(List *funcname,
         * coerce_type can't handle, we'll cause infinite recursion between
         * this module and coerce_type!
         */
-       if (nargs == 1 && fargs != NIL)
+       if (nargs == 1 && fargs != NIL && fargnames == NIL)
        {
            Oid         targetType = FuncNameAsType(funcname);
 
@@ -967,17 +1051,47 @@ func_get_detail(List *funcname,
        FuncDetailCode result;
 
        /*
-        * If expanding variadics or defaults, the "best candidate" might
-        * represent multiple equivalently good functions; treat this case as
-        * ambiguous.
+        * If processing named args or expanding variadics or defaults, the
+        * "best candidate" might represent multiple equivalently good
+        * functions; treat this case as ambiguous.
         */
        if (!OidIsValid(best_candidate->oid))
            return FUNCDETAIL_MULTIPLE;
 
+       /*
+        * We disallow VARIADIC with named arguments unless the last
+        * argument (the one with VARIADIC attached) actually matched the
+        * variadic parameter.  This is mere pedantry, really, but some
+        * folks insisted.
+        */
+       if (fargnames != NIL && !expand_variadic && nargs > 0 &&
+           best_candidate->argnumbers[nargs - 1] != nargs - 1)
+           return FUNCDETAIL_NOTFOUND;
+
        *funcid = best_candidate->oid;
        *nvargs = best_candidate->nvargs;
        *true_typeids = best_candidate->args;
 
+       /*
+        * If processing named args, return actual argument positions into
+        * NamedArgExpr nodes in the fargs list.  This is a bit ugly but not
+        * worth the extra notation needed to do it differently.
+        */
+       if (best_candidate->argnumbers != NULL)
+       {
+           int         i = 0;
+           ListCell   *lc;
+
+           foreach(lc, fargs)
+           {
+               NamedArgExpr *na = (NamedArgExpr *) lfirst(lc);
+
+               if (IsA(na, NamedArgExpr))
+                   na->argnumber = best_candidate->argnumbers[i];
+               i++;
+           }
+       }
+
        ftup = SearchSysCache(PROCOID,
                              ObjectIdGetDatum(best_candidate->oid),
                              0, 0, 0);
@@ -988,36 +1102,73 @@ func_get_detail(List *funcname,
        *rettype = pform->prorettype;
        *retset = pform->proretset;
        /* fetch default args if caller wants 'em */
-       if (argdefaults)
+       if (argdefaults && best_candidate->ndargs > 0)
        {
-           if (best_candidate->ndargs > 0)
+           Datum       proargdefaults;
+           bool        isnull;
+           char       *str;
+           List       *defaults;
+
+           /* shouldn't happen, FuncnameGetCandidates messed up */
+           if (best_candidate->ndargs > pform->pronargdefaults)
+               elog(ERROR, "not enough default arguments");
+
+           proargdefaults = SysCacheGetAttr(PROCOID, ftup,
+                                            Anum_pg_proc_proargdefaults,
+                                            &isnull);
+           Assert(!isnull);
+           str = TextDatumGetCString(proargdefaults);
+           defaults = (List *) stringToNode(str);
+           Assert(IsA(defaults, List));
+           pfree(str);
+
+           /* Delete any unused defaults from the returned list */
+           if (best_candidate->argnumbers != NULL)
+           {
+               /*
+                * This is a bit tricky in named notation, since the supplied
+                * arguments could replace any subset of the defaults.  We
+                * work by making a bitmapset of the argnumbers of defaulted
+                * arguments, then scanning the defaults list and selecting
+                * the needed items.  (This assumes that defaulted arguments
+                * should be supplied in their positional order.)
+                */
+               Bitmapset *defargnumbers;
+               int    *firstdefarg;
+               List   *newdefaults;
+               ListCell *lc;
+               int     i;
+
+               defargnumbers = NULL;
+               firstdefarg = &best_candidate->argnumbers[best_candidate->nargs - best_candidate->ndargs];
+               for (i = 0; i < best_candidate->ndargs; i++)
+                   defargnumbers = bms_add_member(defargnumbers,
+                                                  firstdefarg[i]);
+               newdefaults = NIL;
+               i = pform->pronargs - pform->pronargdefaults;
+               foreach(lc, defaults)
+               {
+                   if (bms_is_member(i, defargnumbers))
+                       newdefaults = lappend(newdefaults, lfirst(lc));
+                   i++;
+               }
+               Assert(list_length(newdefaults) == best_candidate->ndargs);
+               bms_free(defargnumbers);
+               *argdefaults = newdefaults;
+           }
+           else
            {
-               Datum       proargdefaults;
-               bool        isnull;
-               char       *str;
-               List       *defaults;
+               /*
+                * Defaults for positional notation are lots easier;
+                * just remove any unwanted ones from the front.
+                */
                int         ndelete;
 
-               /* shouldn't happen, FuncnameGetCandidates messed up */
-               if (best_candidate->ndargs > pform->pronargdefaults)
-                   elog(ERROR, "not enough default arguments");
-
-               proargdefaults = SysCacheGetAttr(PROCOID, ftup,
-                                                Anum_pg_proc_proargdefaults,
-                                                &isnull);
-               Assert(!isnull);
-               str = TextDatumGetCString(proargdefaults);
-               defaults = (List *) stringToNode(str);
-               Assert(IsA(defaults, List));
-               pfree(str);
-               /* Delete any unused defaults from the returned list */
                ndelete = list_length(defaults) - best_candidate->ndargs;
                while (ndelete-- > 0)
                    defaults = list_delete_first(defaults);
                *argdefaults = defaults;
            }
-           else
-               *argdefaults = NIL;
        }
        if (pform->proisagg)
            result = FUNCDETAIL_AGGREGATE;
@@ -1060,13 +1211,36 @@ make_fn_arguments(ParseState *pstate,
        /* types don't match? then force coercion using a function call... */
        if (actual_arg_types[i] != declared_arg_types[i])
        {
-           lfirst(current_fargs) = coerce_type(pstate,
-                                               lfirst(current_fargs),
-                                               actual_arg_types[i],
-                                               declared_arg_types[i], -1,
-                                               COERCION_IMPLICIT,
-                                               COERCE_IMPLICIT_CAST,
-                                               -1);
+           Node   *node = (Node *) lfirst(current_fargs);
+
+           /*
+            * If arg is a NamedArgExpr, coerce its input expr instead ---
+            * we want the NamedArgExpr to stay at the top level of the list.
+            */
+           if (IsA(node, NamedArgExpr))
+           {
+               NamedArgExpr *na = (NamedArgExpr *) node;
+
+               node = coerce_type(pstate,
+                                  (Node *) na->arg,
+                                  actual_arg_types[i],
+                                  declared_arg_types[i], -1,
+                                  COERCION_IMPLICIT,
+                                  COERCE_IMPLICIT_CAST,
+                                  -1);
+               na->arg = (Expr *) node;
+           }
+           else
+           {
+               node = coerce_type(pstate,
+                                  node,
+                                  actual_arg_types[i],
+                                  declared_arg_types[i], -1,
+                                  COERCION_IMPLICIT,
+                                  COERCE_IMPLICIT_CAST,
+                                  -1);
+               lfirst(current_fargs) = node;
+           }
        }
        i++;
    }
@@ -1223,25 +1397,39 @@ unknown_attribute(ParseState *pstate, Node *relref, char *attname,
  *     Build a string representing a function name, including arg types.
  *     The result is something like "foo(integer)".
  *
+ * If argnames isn't NIL, it is a list of C strings representing the actual
+ * arg names for the last N arguments.  This must be considered part of the
+ * function signature too, when dealing with named-notation function calls.
+ *
  * This is typically used in the construction of function-not-found error
  * messages.
  */
 const char *
-funcname_signature_string(const char *funcname,
-                         int nargs, const Oid *argtypes)
+funcname_signature_string(const char *funcname, int nargs,
+                         List *argnames, const Oid *argtypes)
 {
    StringInfoData argbuf;
+   int         numposargs;
+   ListCell   *lc;
    int         i;
 
    initStringInfo(&argbuf);
 
    appendStringInfo(&argbuf, "%s(", funcname);
 
+   numposargs = nargs - list_length(argnames);
+   lc = list_head(argnames);
+
    for (i = 0; i < nargs; i++)
    {
        if (i)
            appendStringInfoString(&argbuf, ", ");
        appendStringInfoString(&argbuf, format_type_be(argtypes[i]));
+       if (i >= numposargs)
+       {
+           appendStringInfo(&argbuf, " AS %s", (char *) lfirst(lc));
+           lc = lnext(lc);
+       }
    }
 
    appendStringInfoChar(&argbuf, ')');
@@ -1254,10 +1442,11 @@ funcname_signature_string(const char *funcname,
  *     As above, but function name is passed as a qualified name list.
  */
 const char *
-func_signature_string(List *funcname, int nargs, const Oid *argtypes)
+func_signature_string(List *funcname, int nargs,
+                     List *argnames, const Oid *argtypes)
 {
    return funcname_signature_string(NameListToString(funcname),
-                                    nargs, argtypes);
+                                    nargs, argnames, argtypes);
 }
 
 /*
@@ -1276,7 +1465,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
 {
    FuncCandidateList clist;
 
-   clist = FuncnameGetCandidates(funcname, nargs, false, false);
+   clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false);
 
    while (clist)
    {
@@ -1289,7 +1478,8 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError)
        ereport(ERROR,
                (errcode(ERRCODE_UNDEFINED_FUNCTION),
                 errmsg("function %s does not exist",
-                       func_signature_string(funcname, nargs, argtypes))));
+                       func_signature_string(funcname, nargs,
+                                             NIL, argtypes))));
 
    return InvalidOid;
 }
@@ -1401,8 +1591,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
            ereport(ERROR,
                    (errcode(ERRCODE_UNDEFINED_FUNCTION),
                     errmsg("aggregate %s does not exist",
-                           func_signature_string(aggname,
-                                                 argcount, argoids))));
+                           func_signature_string(aggname, argcount,
+                                                 NIL, argoids))));
    }
 
    /* Make sure it's an aggregate */
@@ -1422,8 +1612,8 @@ LookupAggNameTypeNames(List *aggname, List *argtypes, bool noError)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("function %s is not an aggregate",
-                       func_signature_string(aggname,
-                                             argcount, argoids))));
+                       func_signature_string(aggname, argcount,
+                                             NIL, argoids))));
    }
 
    ReleaseSysCache(ftup);
index fd90a2902202e8ed9f9efc9ded3a31ca8285daf8..dd2a53f5a5f545e2b116e6d0a7c7c0b4eabd0a51 100644 (file)
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.110 2009/01/01 17:23:49 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.111 2009/10/08 02:39:23 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, false, false);
+   clist = FuncnameGetCandidates(names, -1, NIL, false, false);
 
    if (clist == NULL)
        ereport(ERROR,
@@ -190,7 +190,7 @@ regprocout(PG_FUNCTION_ARGS)
             * qualify it.
             */
            clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-                                         -1, false, false);
+                                         -1, NIL, false, false);
            if (clist != NULL && clist->next == NULL &&
                clist->oid == proid)
                nspname = NULL;
@@ -277,7 +277,7 @@ regprocedurein(PG_FUNCTION_ARGS)
     */
    parseNameAndArgTypes(pro_name_or_oid, false, &names, &nargs, argtypes);
 
-   clist = FuncnameGetCandidates(names, nargs, false, false);
+   clist = FuncnameGetCandidates(names, nargs, NIL, false, false);
 
    for (; clist; clist = clist->next)
    {
index 38057a0bfdca71099d59235fc66c93d05da5ab31..4c04bafd7c62be89979e4e7fefaee85383dfeb30 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.306 2009/08/01 19:59:41 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.307 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -218,8 +218,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, List *namespaces);
-static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
-                      bool *is_variadic);
+static char *generate_function_name(Oid funcid, int nargs, List *argnames,
+                                   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);
@@ -558,7 +558,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
        appendStringInfo(&buf, "FOR EACH STATEMENT ");
 
    appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
-                    generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
+                    generate_function_name(trigrec->tgfoid, 0,
+                                           NIL, NULL, NULL));
 
    if (trigrec->tgnargs > 0)
    {
@@ -4324,6 +4325,15 @@ get_rule_expr(Node *node, deparse_context *context,
            get_func_expr((FuncExpr *) node, context, showimplicit);
            break;
 
+       case T_NamedArgExpr:
+           {
+               NamedArgExpr *na = (NamedArgExpr *) node;
+
+               get_rule_expr((Node *) na->arg, context, showimplicit);
+               appendStringInfo(buf, " AS %s", quote_identifier(na->name));
+           }
+           break;
+
        case T_OpExpr:
            get_oper_expr((OpExpr *) node, context);
            break;
@@ -5187,6 +5197,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
    Oid         funcoid = expr->funcid;
    Oid         argtypes[FUNC_MAX_ARGS];
    int         nargs;
+   List       *argnames;
    bool        is_variadic;
    ListCell   *l;
 
@@ -5231,14 +5242,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
                 errmsg("too many arguments")));
    nargs = 0;
+   argnames = NIL;
    foreach(l, expr->args)
    {
-       argtypes[nargs] = exprType((Node *) lfirst(l));
+       Node   *arg = (Node *) lfirst(l);
+
+       if (IsA(arg, NamedArgExpr))
+           argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
+       argtypes[nargs] = exprType(arg);
        nargs++;
    }
 
    appendStringInfo(buf, "%s(",
-                    generate_function_name(funcoid, nargs, argtypes,
+                    generate_function_name(funcoid, nargs,
+                                           argnames, argtypes,
                                            &is_variadic));
    nargs = 0;
    foreach(l, expr->args)
@@ -5270,13 +5287,16 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
    nargs = 0;
    foreach(l, aggref->args)
    {
-       argtypes[nargs] = exprType((Node *) lfirst(l));
+       Node   *arg = (Node *) lfirst(l);
+
+       Assert(!IsA(arg, NamedArgExpr));
+       argtypes[nargs] = exprType(arg);
        nargs++;
    }
 
    appendStringInfo(buf, "%s(%s",
-                    generate_function_name(aggref->aggfnoid,
-                                           nargs, argtypes, NULL),
+                    generate_function_name(aggref->aggfnoid, nargs,
+                                           NIL, argtypes, NULL),
                     aggref->aggdistinct ? "DISTINCT " : "");
    /* aggstar can be set only in zero-argument aggregates */
    if (aggref->aggstar)
@@ -5304,13 +5324,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
    nargs = 0;
    foreach(l, wfunc->args)
    {
-       argtypes[nargs] = exprType((Node *) lfirst(l));
+       Node   *arg = (Node *) lfirst(l);
+
+       Assert(!IsA(arg, NamedArgExpr));
+       argtypes[nargs] = exprType(arg);
        nargs++;
    }
 
-   appendStringInfo(buf, "%s(%s",
-                    generate_function_name(wfunc->winfnoid,
-                                           nargs, argtypes, NULL), "");
+   appendStringInfo(buf, "%s(",
+                    generate_function_name(wfunc->winfnoid, nargs,
+                                           NIL, argtypes, NULL));
    /* winstar can be set only in zero-argument aggregates */
    if (wfunc->winstar)
        appendStringInfoChar(buf, '*');
@@ -6338,15 +6361,15 @@ generate_relation_name(Oid relid, List *namespaces)
 /*
  * generate_function_name
  *     Compute the name to display for a function specified by OID,
- *     given that it is being called with the specified actual arg types.
- *     (Arg types matter because of ambiguous-function resolution rules.)
+ *     given that it is being called with the specified actual arg names and
+ *     types.  (Those matter because of ambiguous-function resolution rules.)
  *
  * 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,
-                      bool *is_variadic)
+generate_function_name(Oid funcid, int nargs, List *argnames,
+                      Oid *argtypes, bool *is_variadic)
 {
    HeapTuple   proctup;
    Form_pg_proc procform;
@@ -6371,10 +6394,12 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes,
    /*
     * The idea here is to schema-qualify only if the parser would fail to
     * resolve the correct function given the unqualified func name with the
-    * specified argtypes.
+    * specified argtypes.  If the function is variadic, we should presume
+    * that VARIADIC will be included in the call.
     */
    p_result = func_get_detail(list_make1(makeString(proname)),
-                              NIL, nargs, argtypes, false, true,
+                              NIL, argnames, nargs, argtypes,
+                              !OidIsValid(procform->provariadic), true,
                               &p_funcid, &p_rettype,
                               &p_retset, &p_nvargs, &p_true_typeids, NULL);
    if ((p_result == FUNCDETAIL_NORMAL ||
index 7ecd7812206eea4e7dcfe772bf42ed6e114a5e9f..313b777d04b2946184bda01c5715066d64c3102e 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2009, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.45 2009/06/11 14:49:05 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.46 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -766,6 +766,92 @@ get_func_arg_info(HeapTuple procTup,
 }
 
 
+/*
+ * get_func_input_arg_names
+ *
+ * Extract the names of input arguments only, given a function's
+ * proargnames and proargmodes entries in Datum form.
+ *
+ * Returns the number of input arguments, which is the length of the
+ * palloc'd array returned to *arg_names.  Entries for unnamed args
+ * are set to NULL.  You don't get anything if proargnames is NULL.
+ */
+int
+get_func_input_arg_names(Datum proargnames, Datum proargmodes,
+                        char ***arg_names)
+{
+   ArrayType  *arr;
+   int         numargs;
+   Datum      *argnames;
+   char       *argmodes;
+   char      **inargnames;
+   int         numinargs;
+   int         i;
+
+   /* Do nothing if null proargnames */
+   if (proargnames == PointerGetDatum(NULL))
+   {
+       *arg_names = NULL;
+       return 0;
+   }
+
+   /*
+    * We expect the arrays to be 1-D arrays of the right types; verify that.
+    * For proargmodes, we don't need to use deconstruct_array()
+    * since the array data is just going to look like a C array of values.
+    */
+   arr = DatumGetArrayTypeP(proargnames);      /* ensure not toasted */
+   if (ARR_NDIM(arr) != 1 ||
+       ARR_HASNULL(arr) ||
+       ARR_ELEMTYPE(arr) != TEXTOID)
+       elog(ERROR, "proargnames is not a 1-D text array");
+   deconstruct_array(arr, TEXTOID, -1, false, 'i',
+                     &argnames, NULL, &numargs);
+   if (proargmodes != PointerGetDatum(NULL))
+   {
+       arr = DatumGetArrayTypeP(proargmodes);  /* ensure not toasted */
+       if (ARR_NDIM(arr) != 1 ||
+           ARR_DIMS(arr)[0] != numargs ||
+           ARR_HASNULL(arr) ||
+           ARR_ELEMTYPE(arr) != CHAROID)
+           elog(ERROR, "proargmodes is not a 1-D char array");
+       argmodes = (char *) ARR_DATA_PTR(arr);
+   }
+   else
+       argmodes = NULL;
+
+   /* zero elements probably shouldn't happen, but handle it gracefully */
+   if (numargs <= 0)
+   {
+       *arg_names = NULL;
+       return 0;
+   }
+
+   /* extract input-argument names */
+   inargnames = (char **) palloc(numargs * sizeof(char *));
+   numinargs = 0;
+   for (i = 0; i < numargs; i++)
+   {
+       if (argmodes == NULL ||
+           argmodes[i] == PROARGMODE_IN ||
+           argmodes[i] == PROARGMODE_INOUT ||
+           argmodes[i] == PROARGMODE_VARIADIC)
+       {
+           char       *pname = TextDatumGetCString(argnames[i]);
+
+           if (pname[0] != '\0')
+               inargnames[numinargs] = pname;
+           else
+               inargnames[numinargs] = NULL;
+           numinargs++;
+       }
+   }
+
+   *arg_names = inargnames;
+   return numinargs;
+}
+
+
 /*
  * get_func_result_name
  *
index 20eac6aa1c752eba7c992193381aebe83291cec3..8959997ea96d6ea1ccd9e24c279de501fec1c0b1 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.542 2009/10/07 22:14:24 alvherre Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.543 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200910071
+#define CATALOG_VERSION_NO 200910072
 
 #endif
index ed9218c03a4caedc070dd0b86be23fa1c8c27690..2c2b88951a354b834a2685acb918b10eaa4b6da1 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.59 2009/06/11 14:49:09 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.60 2009/10/08 02:39:23 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,6 +32,7 @@ typedef struct _FuncCandidateList
    int         nargs;          /* number of arg types returned */
    int         nvargs;         /* number of args to become variadic array */
    int         ndargs;         /* number of defaulted args */
+   int        *argnumbers;     /* args' positional indexes, if named call */
    Oid         args[1];        /* arg types --- VARIABLE LENGTH ARRAY */
 }  *FuncCandidateList; /* VARIABLE LENGTH STRUCT */
 
@@ -54,7 +55,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, List *argnames,
                      bool expand_variadic,
                      bool expand_defaults);
 extern bool FunctionIsVisible(Oid funcid);
index 1373e4ad2458d8d03f3237b739142e5ea2a19bbf..b4fe22c492b315d4aff2f8a2506a636d62d1e71c 100644 (file)
@@ -9,7 +9,7 @@
  *
  * Copyright (c) 2002-2009, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.29 2009/06/11 14:49:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/funcapi.h,v 1.30 2009/10/08 02:39:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -173,6 +173,9 @@ extern int get_func_arg_info(HeapTuple procTup,
                  Oid **p_argtypes, char ***p_argnames,
                  char **p_argmodes);
 
+extern int get_func_input_arg_names(Datum proargnames, Datum proargmodes,
+                                   char ***arg_names);
+
 extern char *get_func_result_name(Oid functionId);
 
 extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
index 5fd046e95b85f5442b322f9bc3a97842b4a22749..2a4468799f9990f91943e60f806ccd34b05eefe8 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.227 2009/10/05 19:24:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.228 2009/10/08 02:39:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -123,6 +123,7 @@ typedef enum NodeTag
    T_WindowFunc,
    T_ArrayRef,
    T_FuncExpr,
+   T_NamedArgExpr,
    T_OpExpr,
    T_DistinctExpr,
    T_ScalarArrayOpExpr,
index 5f5d4125c65bbcb825ee55429aa850900eb2fa6b..0320e231553595d511a3b72e818dcb033e75b97b 100644 (file)
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.150 2009/07/16 06:33:45 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.151 2009/10/08 02:39:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -313,6 +313,29 @@ typedef struct FuncExpr
    int         location;       /* token location, or -1 if unknown */
 } FuncExpr;
 
+/*
+ * NamedArgExpr - a named argument of a function
+ *
+ * This node type can only appear in the args list of a FuncCall or FuncExpr
+ * node.  We support pure positional call notation (no named arguments),
+ * named notation (all arguments are named), and mixed notation (unnamed
+ * arguments followed by named ones).
+ *
+ * Parse analysis sets argnumber to the positional index of the argument,
+ * but doesn't rearrange the argument list.
+ *
+ * The planner will convert argument lists to pure positional notation
+ * during expression preprocessing, so execution never sees a NamedArgExpr.
+ */
+typedef struct NamedArgExpr
+{
+   Expr        xpr;
+   Expr       *arg;            /* the argument expression */
+   char       *name;           /* the name */
+   int         argnumber;      /* argument's number in positional notation */
+   int         location;       /* argument name location, or -1 if unknown */
+} NamedArgExpr;
+
 /*
  * OpExpr - expression node for an operator invocation
  *
index 7905f96e86c8e3b90bf2f592b490e94196493f2c..0a38f740b1c74477473d77343e8945a11d0b19cd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.65 2009/05/12 00:56:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_func.h,v 1.66 2009/10/08 02:39:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,7 +47,8 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
                  bool agg_star, bool agg_distinct, bool func_variadic,
                  WindowDef *over, bool is_column, int location);
 
-extern FuncDetailCode func_get_detail(List *funcname, List *fargs,
+extern FuncDetailCode func_get_detail(List *funcname,
+               List *fargs, List *fargnames,
                int nargs, Oid *argtypes,
                bool expand_variadic, bool expand_defaults,
                Oid *funcid, Oid *rettype,
@@ -68,10 +69,10 @@ extern void make_fn_arguments(ParseState *pstate,
                  Oid *actual_arg_types,
                  Oid *declared_arg_types);
 
-extern const char *funcname_signature_string(const char *funcname,
-                         int nargs, const Oid *argtypes);
-extern const char *func_signature_string(List *funcname,
-                     int nargs, const Oid *argtypes);
+extern const char *funcname_signature_string(const char *funcname, int nargs,
+                         List *argnames, const Oid *argtypes);
+extern const char *func_signature_string(List *funcname, int nargs,
+                     List *argnames, const Oid *argtypes);
 
 extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes,
               bool noError);
index 77f693c2b14ba19a1c645b1a2984edb491aacb65..36b31f09f43b5beeb836667741533e4084be0b13 100644 (file)
@@ -1,5 +1,6 @@
 -- Currently this tests polymorphic aggregates and indirectly does some
 -- testing of polymorphic SQL functions.  It ought to be extended.
+-- Tests for other features related to function-calling have snuck in, too.
 -- Legend:
 -----------
 -- A = type is ANY
@@ -19,7 +20,7 @@
 -- !> = not allowed
 -- E  = exists
 -- NE = not-exists
--- 
+--
 -- Possible states:
 -- ----------------
 -- B = (A || P || N)
@@ -60,7 +61,7 @@ CREATE FUNCTION ffp(anyarray) RETURNS anyarray AS
 CREATE FUNCTION ffnp(int[]) returns int[] as
 'select $1' LANGUAGE SQL;
 -- Try to cover all the possible states:
--- 
+--
 -- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
 -- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
 -- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@@ -837,7 +838,7 @@ select dfunc();
 
 -- verify it lists properly
 \df dfunc
-                                       List of functions
+                                           List of functions
  Schema | Name  | Result data type |                    Argument data types                    |  Type  
 --------+-------+------------------+-----------------------------------------------------------+--------
  public | dfunc | integer          | a integer DEFAULT 1, OUT sum integer, b integer DEFAULT 2 | normal
@@ -1005,7 +1006,7 @@ $$ select array_upper($1, 1) $$ language sql;
 ERROR:  cannot remove parameter defaults from existing function
 HINT:  Use DROP FUNCTION first.
 \df dfunc
-                                  List of functions
+                                      List of functions
  Schema | Name  | Result data type |               Argument data types               |  Type  
 --------+-------+------------------+-------------------------------------------------+--------
  public | dfunc | integer          | VARIADIC a integer[] DEFAULT ARRAY[]::integer[] | normal
@@ -1038,3 +1039,328 @@ select dfunc('Hi');
 drop function dfunc(int, int, int);
 drop function dfunc(int, int);
 drop function dfunc(text);
+--
+-- Tests for named- and mixed-notation function calling
+--
+create function dfunc(a int, b int, c int = 0, d int = 0)
+  returns table (a int, b int, c int, d int) as $$
+  select $1, $2, $3, $4;
+$$ language sql;
+select (dfunc(10,20,30)).*;
+ a  | b  | c  | d 
+----+----+----+---
+ 10 | 20 | 30 | 0
+(1 row)
+
+select (dfunc(10 as a, 20 as b, 30 as c)).*;
+ a  | b  | c  | d 
+----+----+----+---
+ 10 | 20 | 30 | 0
+(1 row)
+
+select * from dfunc(10 as a, 20 as b);
+ a  | b  | c | d 
+----+----+---+---
+ 10 | 20 | 0 | 0
+(1 row)
+
+select * from dfunc(10 as b, 20 as a);
+ a  | b  | c | d 
+----+----+---+---
+ 20 | 10 | 0 | 0
+(1 row)
+
+select * from dfunc(0);  -- fail
+ERROR:  function dfunc(integer) does not exist
+LINE 1: select * from dfunc(0);
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(1,2);
+ a | b | c | d 
+---+---+---+---
+ 1 | 2 | 0 | 0
+(1 row)
+
+select * from dfunc(1,2,3 as c);
+ a | b | c | d 
+---+---+---+---
+ 1 | 2 | 3 | 0
+(1 row)
+
+select * from dfunc(1,2,3 as d);
+ a | b | c | d 
+---+---+---+---
+ 1 | 2 | 0 | 3
+(1 row)
+
+select * from dfunc(10 as x, 20 as b, 30 as x);  -- fail, duplicate name
+ERROR:  argument name "x" used more than once
+LINE 1: select * from dfunc(10 as x, 20 as b, 30 as x);
+                                                    ^
+select * from dfunc(10, 20 as b, 30);  -- fail, named args must be last
+ERROR:  positional argument cannot follow named argument
+LINE 1: select * from dfunc(10, 20 as b, 30);
+                                         ^
+select * from dfunc(10 as x, 20 as b, 30 as c);  -- fail, unknown param
+ERROR:  function dfunc(integer AS x, integer AS b, integer AS c) does not exist
+LINE 1: select * from dfunc(10 as x, 20 as b, 30 as c);
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(10, 10, 20 as a);  -- fail, a overlaps positional parameter
+ERROR:  function dfunc(integer, integer, integer AS a) does not exist
+LINE 1: select * from dfunc(10, 10, 20 as a);
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
+ERROR:  function dfunc(integer, integer AS c, integer AS d) does not exist
+LINE 1: select * from dfunc(1,2 as c,3 as d);
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function dfunc(int, int, int, int);
+-- test with different parameter types
+create function dfunc(a varchar, b numeric, c date = current_date)
+  returns table (a varchar, b numeric, c date) as $$
+  select $1, $2, $3;
+$$ language sql;
+select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
+      a      | b  |     c      
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20, '2009-07-25'::date);
+      a      | b  |     c      
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
+      a      | b  |     c      
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
+      a      | b  |     c      
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
+      a      | b  |     c      
+-------------+----+------------
+ Hello World | 20 | 07-25-2009
+(1 row)
+
+select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b);  -- fail
+ERROR:  function dfunc(unknown, integer AS c, date AS b) does not exist
+LINE 1: select * from dfunc('Hello World', 20 as c, '2009-07-25'::da...
+                      ^
+HINT:  No function matches the given name and argument types. You might need to add explicit type casts.
+drop function dfunc(varchar, numeric, date);
+-- test out parameters with named params
+create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+select (dfunc()).*;
+  _a   | _c 
+-------+----
+ def a |   
+(1 row)
+
+select * from dfunc();
+  _a   | _c 
+-------+----
+ def a |   
+(1 row)
+
+select * from dfunc('Hello', 100);
+  _a   | _c  
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc('Hello' as a, 100 as c);
+  _a   | _c  
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc(100 as c, 'Hello' as a);
+  _a   | _c  
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc('Hello');
+  _a   | _c 
+-------+----
+ Hello |   
+(1 row)
+
+select * from dfunc('Hello', 100 as c);
+  _a   | _c  
+-------+-----
+ Hello | 100
+(1 row)
+
+select * from dfunc(100 as c);
+  _a   | _c  
+-------+-----
+ def a | 100
+(1 row)
+
+-- fail, can no longer change an input parameter's name
+create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+ERROR:  cannot change name of input parameter "c"
+HINT:  Use DROP FUNCTION first.
+create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+ERROR:  cannot change name of input parameter "c"
+HINT:  Use DROP FUNCTION first.
+drop function dfunc(varchar, numeric);
+--fail, named parameters are not unique
+create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
+ERROR:  parameter name "a" used more than once
+create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
+ERROR:  parameter name "a" used more than once
+create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
+ERROR:  parameter name "a" used more than once
+create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
+ERROR:  parameter name "a" used more than once
+-- valid
+create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
+select testfoo(37);
+ testfoo 
+---------
+      37
+(1 row)
+
+drop function testfoo(int);
+create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
+select * from testfoo(37);
+ a  
+----
+ 37
+(1 row)
+
+drop function testfoo(int);
+-- test polymorphic params and defaults
+create function dfunc(a anyelement, b anyelement = null, flag bool = true)
+returns anyelement as $$
+  select case when $3 then $1 else $2 end;
+$$ language sql;
+select dfunc(1,2);
+ dfunc 
+-------
+     1
+(1 row)
+
+select dfunc('a'::text, 'b'); -- positional notation with default
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc(1 as a, 2 as b);
+ dfunc 
+-------
+     1
+(1 row)
+
+select dfunc('a'::text as a, 'b' as b);
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
+ dfunc 
+-------
+ b
+(1 row)
+
+select dfunc('b'::text as b, 'a' as a); -- named notation with default
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, true as flag); -- named notation with default
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc('a'::text as a, false as flag); -- named notation with default
+ dfunc 
+-------
+(1 row)
+
+select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc('a'::text, 'b', false); -- full positional notation
+ dfunc 
+-------
+ b
+(1 row)
+
+select dfunc('a'::text, 'b', false as flag); -- mixed notation
+ dfunc 
+-------
+ b
+(1 row)
+
+select dfunc('a'::text, 'b', true); -- full positional notation
+ dfunc 
+-------
+ a
+(1 row)
+
+select dfunc('a'::text, 'b', true as flag); -- mixed notation
+ dfunc 
+-------
+ a
+(1 row)
+
+-- check reverse-listing of named-arg calls
+CREATE VIEW dfview AS
+   SELECT q1, q2,
+     dfunc(q1,q2, q1>q2 as flag) as c3,
+     dfunc(q1, q1<q2 as flag, q2 AS b) as c4
+     FROM int8_tbl;
+select * from dfview;
+        q1        |        q2         |        c3        |        c4         
+------------------+-------------------+------------------+-------------------
+              123 |               456 |              456 |               123
+              123 |  4567890123456789 | 4567890123456789 |               123
+ 4567890123456789 |               123 | 4567890123456789 |               123
+ 4567890123456789 |  4567890123456789 | 4567890123456789 |  4567890123456789
+ 4567890123456789 | -4567890123456789 | 4567890123456789 | -4567890123456789
+(5 rows)
+
+\d dfview
+    View "public.dfview"
+ Column |  Type  | Modifiers 
+--------+--------+-----------
+ q1     | bigint | 
+ q2     | bigint | 
+ c3     | bigint | 
+ c4     | bigint | 
+View definition:
+ SELECT int8_tbl.q1, int8_tbl.q2, dfunc(int8_tbl.q1, int8_tbl.q2, int8_tbl.q1 > int8_tbl.q2 AS flag) AS c3, dfunc(int8_tbl.q1, int8_tbl.q1 < int8_tbl.q2 AS flag, int8_tbl.q2 AS b) AS c4
+   FROM int8_tbl;
+
+drop view dfview;
+drop function dfunc(anyelement, anyelement, bool);
index 486dd3f3fe06f835cf9e7d414645937fc2dc3c6a..843bc53e4e75b7ee1f82f13654fc25d4194881ca 100644 (file)
@@ -159,7 +159,7 @@ SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
      1 |        1 | Joe
 (1 row)
 
-CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS 
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
 (fooid int, foosubid int, fooname text);
 SELECT * FROM vw_getfoo;
  fooid | foosubid | fooname 
@@ -515,7 +515,13 @@ SELECT * FROM dup('xyz'::text);
  xyz | {xyz,xyz}
 (1 row)
 
--- equivalent specification
+-- fails, as we are attempting to rename first argument
+CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+ERROR:  cannot change name of input parameter "f1"
+HINT:  Use DROP FUNCTION first.
+DROP FUNCTION dup(anyelement);
+-- equivalent behavior, though different name exposed for input arg
 CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;
 SELECT dup(22);
index c01871de007b0810d21fd8b96f5d545e323946a2..2071ce63da70b47bbd98a07e48277ac63af4f22f 100644 (file)
@@ -1,5 +1,6 @@
 -- Currently this tests polymorphic aggregates and indirectly does some
 -- testing of polymorphic SQL functions.  It ought to be extended.
+-- Tests for other features related to function-calling have snuck in, too.
 
 
 -- Legend:
@@ -21,7 +22,7 @@
 -- !> = not allowed
 -- E  = exists
 -- NE = not-exists
--- 
+--
 -- Possible states:
 -- ----------------
 -- B = (A || P || N)
@@ -69,7 +70,7 @@ CREATE FUNCTION ffnp(int[]) returns int[] as
 'select $1' LANGUAGE SQL;
 
 -- Try to cover all the possible states:
--- 
+--
 -- Note: in Cases 1 & 2, we are trying to return P. Therefore, if the transfn
 -- is stfnp, tfnp, or tf2p, we must use ffp as finalfn, because stfnp, tfnp,
 -- and tf2p do not return P. Conversely, in Cases 3 & 4, we are trying to
@@ -624,3 +625,123 @@ select dfunc('Hi');
 drop function dfunc(int, int, int);
 drop function dfunc(int, int);
 drop function dfunc(text);
+
+--
+-- Tests for named- and mixed-notation function calling
+--
+
+create function dfunc(a int, b int, c int = 0, d int = 0)
+  returns table (a int, b int, c int, d int) as $$
+  select $1, $2, $3, $4;
+$$ language sql;
+
+select (dfunc(10,20,30)).*;
+select (dfunc(10 as a, 20 as b, 30 as c)).*;
+select * from dfunc(10 as a, 20 as b);
+select * from dfunc(10 as b, 20 as a);
+select * from dfunc(0);  -- fail
+select * from dfunc(1,2);
+select * from dfunc(1,2,3 as c);
+select * from dfunc(1,2,3 as d);
+
+select * from dfunc(10 as x, 20 as b, 30 as x);  -- fail, duplicate name
+select * from dfunc(10, 20 as b, 30);  -- fail, named args must be last
+select * from dfunc(10 as x, 20 as b, 30 as c);  -- fail, unknown param
+select * from dfunc(10, 10, 20 as a);  -- fail, a overlaps positional parameter
+select * from dfunc(1,2 as c,3 as d); -- fail, no value for b
+
+drop function dfunc(int, int, int, int);
+
+-- test with different parameter types
+create function dfunc(a varchar, b numeric, c date = current_date)
+  returns table (a varchar, b numeric, c date) as $$
+  select $1, $2, $3;
+$$ language sql;
+
+select (dfunc('Hello World', 20, '2009-07-25'::date)).*;
+select * from dfunc('Hello World', 20, '2009-07-25'::date);
+select * from dfunc('2009-07-25'::date as c, 'Hello World' as a, 20 as b);
+select * from dfunc('Hello World', 20 as b, '2009-07-25'::date as c);
+select * from dfunc('Hello World', '2009-07-25'::date as c, 20 as b);
+select * from dfunc('Hello World', 20 as c, '2009-07-25'::date as b);  -- fail
+
+drop function dfunc(varchar, numeric, date);
+
+-- test out parameters with named params
+create function dfunc(a varchar = 'def a', out _a varchar, c numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+
+select (dfunc()).*;
+select * from dfunc();
+select * from dfunc('Hello', 100);
+select * from dfunc('Hello' as a, 100 as c);
+select * from dfunc(100 as c, 'Hello' as a);
+select * from dfunc('Hello');
+select * from dfunc('Hello', 100 as c);
+select * from dfunc(100 as c);
+
+-- fail, can no longer change an input parameter's name
+create or replace function dfunc(a varchar = 'def a', out _a varchar, x numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+
+create or replace function dfunc(a varchar = 'def a', out _a varchar, numeric = NULL, out _c numeric)
+returns record as $$
+  select $1, $2;
+$$ language sql;
+
+drop function dfunc(varchar, numeric);
+
+--fail, named parameters are not unique
+create function testfoo(a int, a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(int, out a int, out a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(out a int, inout a int) returns int as $$ select 1;$$ language sql;
+create function testfoo(a int, inout a int) returns int as $$ select 1;$$ language sql;
+
+-- valid
+create function testfoo(a int, out a int) returns int as $$ select $1;$$ language sql;
+select testfoo(37);
+drop function testfoo(int);
+create function testfoo(a int) returns table(a int) as $$ select $1;$$ language sql;
+select * from testfoo(37);
+drop function testfoo(int);
+
+-- test polymorphic params and defaults
+create function dfunc(a anyelement, b anyelement = null, flag bool = true)
+returns anyelement as $$
+  select case when $3 then $1 else $2 end;
+$$ language sql;
+
+select dfunc(1,2);
+select dfunc('a'::text, 'b'); -- positional notation with default
+
+select dfunc(1 as a, 2 as b);
+select dfunc('a'::text as a, 'b' as b);
+select dfunc('a'::text as a, 'b' as b, false as flag); -- named notation
+
+select dfunc('b'::text as b, 'a' as a); -- named notation with default
+select dfunc('a'::text as a, true as flag); -- named notation with default
+select dfunc('a'::text as a, false as flag); -- named notation with default
+select dfunc('b'::text as b, 'a' as a, true as flag); -- named notation
+
+select dfunc('a'::text, 'b', false); -- full positional notation
+select dfunc('a'::text, 'b', false as flag); -- mixed notation
+select dfunc('a'::text, 'b', true); -- full positional notation
+select dfunc('a'::text, 'b', true as flag); -- mixed notation
+
+-- check reverse-listing of named-arg calls
+CREATE VIEW dfview AS
+   SELECT q1, q2,
+     dfunc(q1,q2, q1>q2 as flag) as c3,
+     dfunc(q1, q1<q2 as flag, q2 AS b) as c4
+     FROM int8_tbl;
+
+select * from dfview;
+
+\d dfview
+
+drop view dfview;
+drop function dfunc(anyelement, anyelement, bool);
index 3727a36aaff501e3f4e3474fa19f7496015c349e..172bbc73a9e53aa8fa2a15b35569c98e85e1f1f5 100644 (file)
@@ -70,7 +70,7 @@ DROP VIEW vw_getfoo;
 DROP FUNCTION getfoo(int);
 CREATE FUNCTION getfoo(int) RETURNS RECORD AS 'SELECT * FROM foo WHERE fooid = $1;' LANGUAGE SQL;
 SELECT * FROM getfoo(1) AS t1(fooid int, foosubid int, fooname text);
-CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS 
+CREATE VIEW vw_getfoo AS SELECT * FROM getfoo(1) AS
 (fooid int, foosubid int, fooname text);
 SELECT * FROM vw_getfoo;
 
@@ -251,7 +251,13 @@ SELECT dup('xyz'); -- fails
 SELECT dup('xyz'::text);
 SELECT * FROM dup('xyz'::text);
 
--- equivalent specification
+-- fails, as we are attempting to rename first argument
+CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
+AS 'select $1, array[$1,$1]' LANGUAGE sql;
+
+DROP FUNCTION dup(anyelement);
+
+-- equivalent behavior, though different name exposed for input arg
 CREATE OR REPLACE FUNCTION dup (inout f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;
 SELECT dup(22);