Support use of function argument names to identify which actual arguments
authorTom Lane
Thu, 8 Oct 2009 02:39:25 +0000 (02:39 +0000)
committerTom Lane
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 @@
 
 
 
@@ -65,7 +65,7 @@ CREATE [ OR REPLACE ] FUNCTION
    Also, CREATE OR REPLACE FUNCTION will not let
    you change the return type of an existing function.  To do that,
    you must drop and recreate the function.  (When using OUT
-   parameters, that means you cannot change the names or types of any
+   parameters, that means you cannot change the types of any
    OUT parameters except by dropping the function.)
   
 
@@ -121,8 +121,11 @@ CREATE [ OR REPLACE ] FUNCTION
       
        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 
+       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.)
       
@@ -570,6 +573,18 @@ CREATE FUNCTION foo(int, int default 42) ...
     to replace it (this includes being a member of the owning role).
    
 
+   
+    When replacing an existing function with 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.
+   
+
  
 
  
index 0872eacee761537573bfa739ccc76d4553b49187..342f8e4ef73ee741a087fe03b89ecc71fc1f82c5 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   PostgreSQL Coding Conventions
@@ -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.")));
 
index 73db3235bd6f15250536cf96e4c8a24c140c01dc..20f7085a8d3375205347c73fe2e336df98791f38 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
 
  SQL Syntax
@@ -1505,6 +1505,11 @@ sqrt(2)
     The list of built-in functions is in .
     Other functions can be added by the user.
    
+
+   
+    The arguments can optionally have names attached.
+    See  for details.
+   
   
 
   
@@ -2123,4 +2128,168 @@ SELECT ... WHERE CASE WHEN x > 0 THEN y/x > 1.5 ELSE false END;
   
  
 
+  Calling Functions
+
+   
+    notation
+    functions
+   
+
+   
+    PostgreSQL allows functions that have named
+    parameters to be called using either positional or
+    named 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.
+   
+
+   
+    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.
+   
+
+   
+    PostgreSQL also supports
+    mixed notation, which combines positional and
+    named notation.  In this case, positional parameters are written first
+    and named parameters appear after them.
+   
+
+   
+    The following examples will illustrate the usage of all three
+    notations, using the following function definition:
+
+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;
+
+    Function concat_lower_or_upper has two mandatory
+    parameters, a and b.  Additionally
+    there is one optional parameter uppercase which defaults
+    to false.  The a and
+    b inputs will be concatenated, and forced to either
+    upper or lower case depending on the uppercase
+    parameter.  The remaining details of this function
+    definition are not important here (see  for
+    more information).
+   
+
+   
+    Using positional notation
+
+    
+     function
+     positional notation
+    
+
+    
+     Positional notation is the traditional mechanism for passing arguments
+     to functions in PostgreSQL.  An example is:
+
+SELECT concat_lower_or_upper('Hello', 'World', true);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+
+     All arguments are specified in order.  The result is upper case since
+     uppercase is specified as true.
+     Another example is:
+
+SELECT concat_lower_or_upper('Hello', 'World');
+ concat_lower_or_upper 
+-----------------------
+ hello world
+(1 row)
+
+     Here, the uppercase parameter is omitted, so it
+     receives its default value of false, resulting in
+     lower case output.  In positional notation, arguments can be omitted
+     from right to left so long as they have defaults.
+    
+   
+
+   
+    Using named notation
+
+    
+     function
+     named notation
+    
+
+    
+     In named notation, each argument's name is specified using the
+     AS keyword.  For example:
+
+SELECT concat_lower_or_upper('Hello' AS a, 'World' AS b);
+ concat_lower_or_upper 
+-----------------------
+ hello world
+(1 row)
+
+     Again, the argument uppercase was omitted
+     so it is set to false implicitly.  One advantage of
+     using named notation is that the arguments may be specified in any
+     order, for example:
+
+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)
+
+    
+   
+
+  
+   Using mixed notation
+
+   
+    function
+    mixed notation
+   
+
+   
+    The mixed notation combines positional and named notation. However, as
+    already mentioned, named arguments cannot precede positional arguments.
+    For example:
+
+SELECT concat_lower_or_upper('Hello', 'World', true AS uppercase);
+ concat_lower_or_upper 
+-----------------------
+ HELLO WORLD
+(1 row)
+
+    In the above query, the arguments a and
+    b are specified positionally, while
+    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.
+   
+  
+
 
index 6d85d2d262908850f2544ea11f732e35eb1a1d25..1c20f15226a3cb377ece7591ce6a66b9733539f4 100644 (file)
@@ -1,4 +1,4 @@
-
+
 
  
   User-Defined Functions
@@ -517,6 +517,39 @@ SELECT getname(new_emp());
     
    
 
+   
+    <acronym>SQL</> Functions with Parameter Names
+
+   
+    function
+    named parameter
+   
+
+    
+     It is possible to attach names to a function's parameters, for example
+
+
+CREATE FUNCTION tf1 (acct_no integer, debit numeric) RETURNS numeric AS $$
+    UPDATE bank
+        SET balance = balance - $2
+        WHERE accountno = $1
+    RETURNING balance;
+$$ LANGUAGE SQL;
+
+
+     Here the first parameter has been given the name acct_no,
+     and the second parameter the name debit.
+     So far as the SQL function itself is concerned, these names are just
+     decoration; you must still refer to the parameters as $1,
+     $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
+     .
+    
+   
+
    
     <acronym>SQL</> Functions with Output Parameters
 
@@ -571,7 +604,10 @@ LANGUAGE SQL;
 
 
      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.)
     
 
     
@@ -621,7 +657,7 @@ DROP FUNCTION sum_n_product (int, int);
      must be declared as being of an array type.  For example:
 
 
-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.  VARIADIC can only be attached to the last
      actual argument of a function call.
     
+
+    
+     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 (
+     linkend="sql-syntax-calling-funcs">), except when you specify
+     VARIADIC.  For example, this will work:
+
+
+SELECT mleast(VARIADIC ARRAY[10, -1, 5, 4.4] AS arr);
+
+
+     but not these:
+
+
+SELECT mleast(10 AS arr);
+SELECT mleast(ARRAY[10, -1, 5, 4.4] AS arr);
+
+    
    
 
    
@@ -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.)
     
 
     
@@ -712,7 +769,7 @@ SELECT foo();  -- fails since there is no default for the first argument
 ERROR:  function foo() does not exist
 
      The = sign can also be used in place of the
-     key word DEFAULT,
+     key word DEFAULT.
     
    
 
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    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    func_arg_list
+%type    func_arg_expr
 %type    row type_list array_expr_list
 %type    case_expr case_arg when_clause case_default
 %type    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
+     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
+     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);