Allow SQL-language functions to reference parameters by name.
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Feb 2012 00:23:49 +0000 (19:23 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Feb 2012 00:23:49 +0000 (19:23 -0500)
Matthew Draper, reviewed by Hitoshi Harada

doc/src/sgml/xfunc.sgml
src/backend/executor/functions.c
src/test/regress/input/create_function_2.source
src/test/regress/input/misc.source
src/test/regress/output/create_function_2.source
src/test/regress/output/misc.source

index 70643122046f1787574054807a6bf384b74c16da..d81d63f922fde450def1c17efaf6060b2760524e 100644 (file)
@@ -154,14 +154,50 @@ SELECT clean_emp();
     the function (see <xref linkend="sql-syntax-strings">).
    </para>
 
-   <para>
-    Arguments to the SQL function are referenced in the function
-    body using the syntax <literal>$<replaceable>n</></>: <literal>$1</>
-    refers to the first argument, <literal>$2</> to the second, and so on.
-    If an argument is of a composite type, then the dot notation,
-    e.g., <literal>$1.name</literal>, can be used to access attributes
-    of the argument.  The arguments can only be used as data values,
-    not as identifiers.  Thus for example this is reasonable:
+   <sect2 id="xfunc-sql-function-arguments">
+    <title>Arguments for <acronym>SQL</acronym> Functions</title>
+
+   <indexterm>
+    <primary>function</primary>
+    <secondary>named argument</secondary>
+   </indexterm>
+
+    <para>
+     Arguments of a SQL function can be referenced in the function
+     body using either names or numbers.  Examples of both methods appear
+     below.
+    </para>
+
+    <para>
+     To use a name, declare the function argument as having a name, and
+     then just write that name in the function body.  If the argument name
+     is the same as any column name in the current SQL command within the
+     function, the column name will take precedence.  To override this,
+     qualify the argument name with the name of the function itself, that is
+     <literal><replaceable>function_name</>.<replaceable>argument_name</></literal>.
+     (If this would conflict with a qualified column name, again the column
+     name wins.  You can avoid the ambiguity by choosing a different alias for
+     the table within the SQL command.)
+    </para>
+
+    <para>
+     In the older numeric approach, arguments are referenced using the syntax
+     <literal>$<replaceable>n</></>: <literal>$1</> refers to the first input
+     argument, <literal>$2</> to the second, and so on.  This will work
+     whether or not the particular argument was declared with a name.
+    </para>
+
+    <para>
+     If an argument is of a composite type, then the dot notation,
+     e.g., <literal>argname.fieldname</literal> or
+     <literal>$1.fieldname</literal>, can be used to access attributes of the
+     argument.  Again, you might need to qualify the argument's name with the
+     function name to make the form with an argument name unambiguous.
+    </para>
+
+    <para>
+     SQL function arguments can only be used as data values,
+     not as identifiers.  Thus for example this is reasonable:
 <programlisting>
 INSERT INTO mytable VALUES ($1);
 </programlisting>
@@ -169,7 +205,16 @@ but this will not work:
 <programlisting>
 INSERT INTO $1 VALUES (42);
 </programlisting>
-   </para>
+    </para>
+
+    <note>
+     <para>
+      The ability to use names to reference SQL function arguments was added
+      in <productname>PostgreSQL</productname> 9.2.  Functions to be used in
+      older servers must use the <literal>$<replaceable>n</></> notation.
+     </para>
+    </note>
+   </sect2>
 
    <sect2 id="xfunc-sql-base-functions">
     <title><acronym>SQL</acronym> Functions on Base Types</title>
@@ -205,9 +250,24 @@ SELECT one();
 
     <para>
      It is almost as easy to define <acronym>SQL</acronym> functions
-     that take base types as arguments.  In the example below, notice
-     how we refer to the arguments within the function as <literal>$1</>
-     and <literal>$2</>.
+     that take base types as arguments:
+
+<screen>
+CREATE FUNCTION add_em(x integer, y integer) RETURNS integer AS $$
+    SELECT x + y;
+$$ LANGUAGE SQL;
+
+SELECT add_em(1, 2) AS answer;
+
+ answer
+--------
+      3
+</screen>
+    </para>
+
+    <para>
+     Alternatively, we could dispense with names for the arguments and
+     use numbers:
 
 <screen>
 CREATE FUNCTION add_em(integer, integer) RETURNS integer AS $$
@@ -227,10 +287,10 @@ SELECT add_em(1, 2) AS answer;
      bank account:
 
 <programlisting>
-CREATE FUNCTION tf1 (integer, numeric) RETURNS integer AS $$
+CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$
     UPDATE bank
-        SET balance = balance - $2
-        WHERE accountno = $1;
+        SET balance = balance - debit
+        WHERE accountno = tf1.accountno;
     SELECT 1;
 $$ LANGUAGE SQL;
 </programlisting>
@@ -243,17 +303,26 @@ SELECT tf1(17, 100.0);
 </programlisting>
     </para>
 
+    <para>
+     In this example, we chose the name <literal>accountno</> for the first
+     argument, but this is the same as the name of a column in the
+     <literal>bank</> table.  Within the <command>UPDATE</> command,
+     <literal>accountno</> refers to the column <literal>bank.accountno</>,
+     so <literal>tf1.accountno</> must be used to refer to the argument.
+     We could of course avoid this by using a different name for the argument.
+    </para>
+
     <para>
      In practice one would probably like a more useful result from the
      function than a constant 1, so a more likely definition
      is:
 
 <programlisting>
-CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
+CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$
     UPDATE bank
-        SET balance = balance - $2
-        WHERE accountno = $1;
-    SELECT balance FROM bank WHERE accountno = $1;
+        SET balance = balance - debit
+        WHERE accountno = tf1.accountno;
+    SELECT balance FROM bank WHERE accountno = tf1.accountno;
 $$ LANGUAGE SQL;
 </programlisting>
 
@@ -261,10 +330,10 @@ $$ LANGUAGE SQL;
      The same thing could be done in one command using <literal>RETURNING</>:
 
 <programlisting>
-CREATE FUNCTION tf1 (integer, numeric) RETURNS numeric AS $$
+CREATE FUNCTION tf1 (accountno integer, debit numeric) RETURNS integer AS $$
     UPDATE bank
-        SET balance = balance - $2
-        WHERE accountno = $1
+        SET balance = balance - debit
+        WHERE accountno = tf1.accountno
     RETURNING balance;
 $$ LANGUAGE SQL;
 </programlisting>
@@ -275,11 +344,9 @@ $$ LANGUAGE SQL;
     <title><acronym>SQL</acronym> Functions on Composite Types</title>
 
     <para>
-     When writing  functions with arguments of composite
-     types, we must  not  only  specify  which
-     argument  we  want (as we did above with <literal>$1</> and <literal>$2</literal>) but
-     also the desired attribute (field) of  that  argument.   For  example,
-     suppose that
+     When writing functions with arguments of composite types, we must not
+     only specify which argument we want but also the desired attribute
+     (field) of that argument.  For example, suppose that
      <type>emp</type> is a table containing employee data, and therefore
      also the name of the composite type of each row of the table.  Here
      is a function <function>double_salary</function> that computes what someone's
@@ -524,39 +591,6 @@ SELECT getname(new_emp());
     </para>
    </sect2>
 
-   <sect2 id="xfunc-named-parameters">
-    <title><acronym>SQL</> Functions with Parameter Names</title>
-
-   <indexterm>
-    <primary>function</primary>
-    <secondary>named parameter</secondary>
-   </indexterm>
-
-    <para>
-     It is possible to attach names to a function's parameters, for example
-
-<programlisting>
-CREATE FUNCTION tf1 (acct_no integer, debit numeric) RETURNS numeric AS $$
-    UPDATE bank
-        SET balance = balance - $2
-        WHERE accountno = $1
-    RETURNING balance;
-$$ LANGUAGE SQL;
-</programlisting>
-
-     Here the first parameter has been given the name <literal>acct_no</>,
-     and the second parameter the name <literal>debit</>.
-     So far as the SQL function itself is concerned, these names are just
-     decoration; you must still refer to the parameters as <literal>$1</>,
-     <literal>$2</>, etc within the function body.  (Some procedural
-     languages let you use the parameter names instead.)  However,
-     attaching names to the parameters is useful for documentation purposes.
-     When a function has many parameters, it is also useful to use the names
-     while calling the function, as described in
-     <xref linkend="sql-syntax-calling-funcs">.
-    </para>
-   </sect2>
-
    <sect2 id="xfunc-output-parameters">
     <title><acronym>SQL</> Functions with Output Parameters</title>
 
@@ -571,7 +605,7 @@ $$ LANGUAGE SQL;
 
 <screen>
 CREATE FUNCTION add_em (IN x int, IN y int, OUT sum int)
-AS 'SELECT $1 + $2'
+AS 'SELECT x + y'
 LANGUAGE SQL;
 
 SELECT add_em(3,7);
@@ -588,7 +622,7 @@ SELECT add_em(3,7);
 
 <screen>
 CREATE FUNCTION sum_n_product (x int, y int, OUT sum int, OUT product int)
-AS 'SELECT $1 + $2, $1 * $2'
+AS 'SELECT x + y, x * y'
 LANGUAGE SQL;
 
  SELECT * FROM sum_n_product(11,42);
index 56426871b76b7b2ec7057d52f9b4e7f6c9f9fe62..61f462254ff55cc0bfed5be2db486e1257baa527 100644 (file)
@@ -23,6 +23,7 @@
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_func.h"
 #include "tcop/utility.h"
 #include "utils/builtins.h"
 #include "utils/datum.h"
@@ -115,14 +116,23 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
  */
 typedef struct SQLFunctionParseInfo
 {
-       Oid                *argtypes;           /* resolved types of input arguments */
+       char       *fname;                      /* function's name */
        int                     nargs;                  /* number of input arguments */
+       Oid                *argtypes;           /* resolved types of input arguments */
+       char      **argnames;           /* names of input arguments; NULL if none */
+       /* Note that argnames[i] can be NULL, if some args are unnamed */
        Oid                     collation;              /* function's input collation, if known */
 }      SQLFunctionParseInfo;
 
 
 /* non-export function prototypes */
 static Node *sql_fn_param_ref(ParseState *pstate, ParamRef *pref);
+static Node *sql_fn_post_column_ref(ParseState *pstate,
+                                                                       ColumnRef *cref, Node *var);
+static Node *sql_fn_make_param(SQLFunctionParseInfoPtr pinfo,
+                                                          int paramno, int location);
+static Node *sql_fn_resolve_param_name(SQLFunctionParseInfoPtr pinfo,
+                                                                          const char *paramname, int location);
 static List *init_execution_state(List *queryTree_list,
                                         SQLFunctionCachePtr fcache,
                                         bool lazyEvalOK);
@@ -163,6 +173,9 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
 
        pinfo = (SQLFunctionParseInfoPtr) palloc0(sizeof(SQLFunctionParseInfo));
 
+       /* Function's name (only) can be used to qualify argument names */
+       pinfo->fname = pstrdup(NameStr(procedureStruct->proname));
+
        /* Save the function's input collation */
        pinfo->collation = inputCollation;
 
@@ -200,6 +213,38 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
                pinfo->argtypes = argOidVect;
        }
 
+       /*
+        * Collect names of arguments, too, if any
+        */
+       if (nargs > 0)
+       {
+               Datum           proargnames;
+               Datum           proargmodes;
+               int                     n_arg_names;
+               bool            isNull;
+
+               proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+                                                                         Anum_pg_proc_proargnames,
+                                                                         &isNull);
+               if (isNull)
+                       proargnames = PointerGetDatum(NULL);    /* just to be sure */
+
+               proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, procedureTuple,
+                                                                         Anum_pg_proc_proargmodes,
+                                                                         &isNull);
+               if (isNull)
+                       proargmodes = PointerGetDatum(NULL);    /* just to be sure */
+
+               n_arg_names = get_func_input_arg_names(proargnames, proargmodes,
+                                                                                          &pinfo->argnames);
+
+               /* Paranoia: ignore the result if too few array entries */
+               if (n_arg_names < nargs)
+                       pinfo->argnames = NULL;
+       }
+       else
+               pinfo->argnames = NULL;
+
        return pinfo;
 }
 
@@ -209,14 +254,121 @@ prepare_sql_fn_parse_info(HeapTuple procedureTuple,
 void
 sql_fn_parser_setup(struct ParseState *pstate, SQLFunctionParseInfoPtr pinfo)
 {
-       /* Later we might use these hooks to support parameter names */
        pstate->p_pre_columnref_hook = NULL;
-       pstate->p_post_columnref_hook = NULL;
+       pstate->p_post_columnref_hook = sql_fn_post_column_ref;
        pstate->p_paramref_hook = sql_fn_param_ref;
        /* no need to use p_coerce_param_hook */
        pstate->p_ref_hook_state = (void *) pinfo;
 }
 
+/*
+ * sql_fn_post_column_ref              parser callback for ColumnRefs
+ */
+static Node *
+sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
+{
+       SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
+       int                     nnames;
+       Node       *field1;
+       Node       *subfield = NULL;
+       const char *name1;
+       const char *name2 = NULL;
+       Node       *param;
+
+       /*
+        * Never override a table-column reference.  This corresponds to
+        * considering the parameter names to appear in a scope outside the
+        * individual SQL commands, which is what we want.
+        */
+       if (var != NULL)
+               return NULL;
+
+       /*----------
+        * The allowed syntaxes are:
+        *
+        * A            A = parameter name
+        * A.B          A = function name, B = parameter name
+        *                      OR: A = record-typed parameter name, B = field name
+        *                      (the first possibility takes precedence)
+        * A.B.C        A = function name, B = record-typed parameter name,
+        *                      C = field name
+        *----------
+        */
+       nnames = list_length(cref->fields);
+
+       if (nnames > 3)
+               return NULL;
+
+       field1 = (Node *) linitial(cref->fields);
+       Assert(IsA(field1, String));
+       name1 = strVal(field1);
+       if (nnames > 1)
+       {
+               subfield = (Node *) lsecond(cref->fields);
+               Assert(IsA(subfield, String));
+               name2 = strVal(subfield);
+       }
+
+       if (nnames == 3)
+       {
+               /*
+                * Three-part name: if the first part doesn't match the function name,
+                * we can fail immediately. Otherwise, look up the second part, and
+                * take the third part to be a field reference.
+                */
+               if (strcmp(name1, pinfo->fname) != 0)
+                       return NULL;
+
+               param = sql_fn_resolve_param_name(pinfo, name2, cref->location);
+
+               subfield = (Node *) lthird(cref->fields);
+               Assert(IsA(subfield, String));
+       }
+       else if (nnames == 2 && strcmp(name1, pinfo->fname) == 0)
+       {
+               /*
+                * Two-part name with first part matching function name: first see if
+                * second part matches any parameter name.
+                */
+               param = sql_fn_resolve_param_name(pinfo, name2, cref->location);
+
+               if (param)
+               {
+                       /* Yes, so this is a parameter reference, no subfield */
+                       subfield = NULL;
+               }
+               else
+               {
+                       /* No, so try to match as parameter name and subfield */
+                       param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
+               }
+       }
+       else
+       {
+               /* Single name, or parameter name followed by subfield */
+               param = sql_fn_resolve_param_name(pinfo, name1, cref->location);
+       }
+
+       if (!param)
+               return NULL;                    /* No match */
+
+       if (subfield)
+       {
+               /*
+                * Must be a reference to a field of a composite parameter; otherwise
+                * ParseFuncOrColumn will return NULL, and we'll fail back at the
+                * caller.
+                */
+               param = ParseFuncOrColumn(pstate,
+                                                                 list_make1(subfield),
+                                                                 list_make1(param),
+                                                                 NIL, false, false, false,
+                                                                 NULL, true, cref->location);
+       }
+
+       return param;
+}
+
 /*
  * sql_fn_param_ref            parser callback for ParamRefs ($n symbols)
  */
@@ -225,19 +377,30 @@ sql_fn_param_ref(ParseState *pstate, ParamRef *pref)
 {
        SQLFunctionParseInfoPtr pinfo = (SQLFunctionParseInfoPtr) pstate->p_ref_hook_state;
        int                     paramno = pref->number;
-       Param      *param;
 
        /* Check parameter number is valid */
        if (paramno <= 0 || paramno > pinfo->nargs)
                return NULL;                    /* unknown parameter number */
 
+       return sql_fn_make_param(pinfo, paramno, pref->location);
+}
+
+/*
+ * sql_fn_make_param           construct a Param node for the given paramno
+ */
+static Node *
+sql_fn_make_param(SQLFunctionParseInfoPtr pinfo,
+                                 int paramno, int location)
+{
+       Param      *param;
+
        param = makeNode(Param);
        param->paramkind = PARAM_EXTERN;
        param->paramid = paramno;
        param->paramtype = pinfo->argtypes[paramno - 1];
        param->paramtypmod = -1;
        param->paramcollid = get_typcollation(param->paramtype);
-       param->location = pref->location;
+       param->location = location;
 
        /*
         * If we have a function input collation, allow it to override the
@@ -250,6 +413,29 @@ sql_fn_param_ref(ParseState *pstate, ParamRef *pref)
        return (Node *) param;
 }
 
+/*
+ * Search for a function parameter of the given name; if there is one,
+ * construct and return a Param node for it.  If not, return NULL.
+ * Helper function for sql_fn_post_column_ref.
+ */
+static Node *
+sql_fn_resolve_param_name(SQLFunctionParseInfoPtr pinfo,
+                                                 const char *paramname, int location)
+{
+       int             i;
+
+       if (pinfo->argnames == NULL)
+               return NULL;
+
+       for (i = 0; i < pinfo->nargs; i++)
+       {
+               if (pinfo->argnames[i] && strcmp(pinfo->argnames[i], paramname) == 0)
+                       return sql_fn_make_param(pinfo, i + 1, location);
+       }
+
+       return NULL;
+}
+
 /*
  * Set up the per-query execution_state records for a SQL function.
  *
index 6aed5f008b937af30f40cb535019d2b71e31bedc..1b013aedcbdbd598416a3b3dcdf3a1aa3600f88e 100644 (file)
@@ -13,6 +13,12 @@ CREATE FUNCTION hobby_construct(text, text)
    LANGUAGE SQL;
 
 
+CREATE FUNCTION hobby_construct_named(name text, hobby text)
+   RETURNS hobbies_r
+   AS 'select name, hobby'
+   LANGUAGE SQL;
+
+
 CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
    RETURNS hobbies_r.person%TYPE
    AS 'select person from hobbies_r where name = $1'
@@ -25,6 +31,37 @@ CREATE FUNCTION equipment(hobbies_r)
    LANGUAGE SQL;
 
 
+CREATE FUNCTION equipment_named(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name'
+   LANGUAGE SQL;
+
+CREATE FUNCTION equipment_named_ambiguous_1a(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = equipment_named_ambiguous_1a.hobby.name'
+   LANGUAGE SQL;
+
+CREATE FUNCTION equipment_named_ambiguous_1b(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = hobby.name'
+   LANGUAGE SQL;
+
+CREATE FUNCTION equipment_named_ambiguous_1c(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = hobby.name'
+   LANGUAGE SQL;
+
+CREATE FUNCTION equipment_named_ambiguous_2a(hobby text)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = equipment_named_ambiguous_2a.hobby'
+   LANGUAGE SQL;
+
+CREATE FUNCTION equipment_named_ambiguous_2b(hobby text)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = hobby'
+   LANGUAGE SQL;
+
+
 CREATE FUNCTION user_relns()
    RETURNS setof name
    AS 'select relname
index 7cd26cb192dc60699a1f2a0c9014d1c66eb1584a..e16dc21f407dce5d399006efd0ef3ac47ab0b4b9 100644 (file)
@@ -223,6 +223,20 @@ SELECT user_relns() AS user_relns
 
 SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
 
+SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer')));
+
+SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer')));
+
+SELECT name(equipment_named_ambiguous_1a(hobby_construct_named(text 'skywalking', text 'mer')));
+
+SELECT name(equipment_named_ambiguous_1b(hobby_construct_named(text 'skywalking', text 'mer')));
+
+SELECT name(equipment_named_ambiguous_1c(hobby_construct_named(text 'skywalking', text 'mer')));
+
+SELECT name(equipment_named_ambiguous_2a(text 'skywalking'));
+
+SELECT name(equipment_named_ambiguous_2b(text 'skywalking'));
+
 SELECT hobbies_by_name('basketball');
 
 SELECT name, overpaid(emp.*) FROM emp;
index 94ab7eba566034736333b3a9f7e4f87d0009682d..98e1c297337884e6c2c339c8d9a75a2409b5f196 100644 (file)
@@ -9,6 +9,10 @@ CREATE FUNCTION hobby_construct(text, text)
    RETURNS hobbies_r
    AS 'select $1 as name, $2 as hobby'
    LANGUAGE SQL;
+CREATE FUNCTION hobby_construct_named(name text, hobby text)
+   RETURNS hobbies_r
+   AS 'select name, hobby'
+   LANGUAGE SQL;
 CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
    RETURNS hobbies_r.person%TYPE
    AS 'select person from hobbies_r where name = $1'
@@ -19,6 +23,30 @@ CREATE FUNCTION equipment(hobbies_r)
    RETURNS setof equipment_r
    AS 'select * from equipment_r where hobby = $1.name'
    LANGUAGE SQL;
+CREATE FUNCTION equipment_named(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = equipment_named.hobby.name'
+   LANGUAGE SQL;
+CREATE FUNCTION equipment_named_ambiguous_1a(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = equipment_named_ambiguous_1a.hobby.name'
+   LANGUAGE SQL;
+CREATE FUNCTION equipment_named_ambiguous_1b(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = hobby.name'
+   LANGUAGE SQL;
+CREATE FUNCTION equipment_named_ambiguous_1c(hobby hobbies_r)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = hobby.name'
+   LANGUAGE SQL;
+CREATE FUNCTION equipment_named_ambiguous_2a(hobby text)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where hobby = equipment_named_ambiguous_2a.hobby'
+   LANGUAGE SQL;
+CREATE FUNCTION equipment_named_ambiguous_2b(hobby text)
+   RETURNS setof equipment_r
+   AS 'select * from equipment_r where equipment_r.hobby = hobby'
+   LANGUAGE SQL;
 CREATE FUNCTION user_relns()
    RETURNS setof name
    AS 'select relname
index 2f4d482cbe523886bb3186a8235916286eae8deb..979ed337b29db0a30f3e28dafdd75559fedf4484 100644 (file)
@@ -693,6 +693,51 @@ SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer')));
  guts
 (1 row)
 
+SELECT name(equipment(hobby_construct_named(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named(hobby_construct_named(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named_ambiguous_1a(hobby_construct_named(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named_ambiguous_1b(hobby_construct_named(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named_ambiguous_1c(hobby_construct_named(text 'skywalking', text 'mer')));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named_ambiguous_2a(text 'skywalking'));
+ name 
+------
+ guts
+(1 row)
+
+SELECT name(equipment_named_ambiguous_2b(text 'skywalking'));
+     name      
+---------------
+ advil
+ peet's coffee
+ hightops
+ guts
+(4 rows)
+
 SELECT hobbies_by_name('basketball');
  hobbies_by_name 
 -----------------