aboutsummaryrefslogtreecommitdiff
path: root/src/backend/catalog/namespace.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/namespace.c')
-rw-r--r--src/backend/catalog/namespace.c299
1 files changed, 250 insertions, 49 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 12cb1720cde..2c327d45f15 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.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;
@@ -886,6 +967,125 @@ FuncnameGetCandidates(List *names, int nargs,
}
/*
+ * 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
* current search path. Visible means "would be found by searching
@@ -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;