summaryrefslogtreecommitdiff
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.c305
1 files changed, 158 insertions, 147 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index e665e9594d..892ca66591 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.114 2008/12/15 18:09:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.115 2008/12/18 18:20:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -562,7 +562,8 @@ 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 must be false in this case.)
+ * regardless of argument count. (expand_variadic and expand_defaults must be
+ * false in this case.)
*
* If expand_variadic is true, then variadic functions having the same number
* or fewer arguments will be retrieved, with the variadic argument and any
@@ -571,23 +572,43 @@ TypeIsVisible(Oid typid)
* If expand_variadic is false, variadic arguments are not treated specially,
* and the returned nvargs will always be zero.
*
- * If expand_variadic is true, functions with argument default values
- * will also be retrieved. If expand_variadic is false, default
- * values will not be taken into account and functions that do not
- * have exactly nargs arguments in total will not be considered.
+ * If expand_defaults is true, functions that could match after insertion of
+ * default argument values will also be retrieved. In this case the returned
+ * structs could have nargs > passed-in nargs, and ndargs is set to the number
+ * of additional args (which can be retrieved from the function's
+ * proargdefaults entry).
+ *
+ * It is not possible for nvargs and ndargs to both be nonzero in the same
+ * list entry, since default insertion allows matches to functions with more
+ * than nargs arguments while the variadic transformation requires the same
+ * number or less.
*
* We search a single namespace if the function name is qualified, else
- * all namespaces in the search path. The return list will never contain
- * multiple entries with identical argument lists --- in the multiple-
- * namespace case, we arrange for entries in earlier namespaces to mask
- * identical entries in later namespaces. We also arrange for non-variadic
- * functions to mask variadic ones if the expanded argument list is the same.
+ * all namespaces in the search path. In the multiple-namespace case,
+ * we arrange for entries in earlier namespaces to mask identical entries in
+ * later namespaces.
+ *
+ * When expanding variadics, we arrange for non-variadic functions to mask
+ * variadic ones if the expanded argument list is the same. It is still
+ * possible for there to be conflicts between different variadic functions,
+ * however.
+ *
+ * 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.
+ * 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, bool expand_variadic)
+FuncnameGetCandidates(List *names, int nargs,
+ bool expand_variadic, bool expand_defaults)
{
FuncCandidateList resultList = NULL;
- bool any_variadic = false;
+ bool any_special = false;
char *schemaname;
char *funcname;
Oid namespaceId;
@@ -595,7 +616,7 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
int i;
/* check for caller error */
- Assert(nargs >= 0 || !expand_variadic);
+ Assert(nargs >= 0 || !(expand_variadic | expand_defaults));
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &funcname);
@@ -625,42 +646,11 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
int effective_nargs;
int pathpos = 0;
bool variadic;
+ bool use_defaults;
Oid va_elem_type;
- List *defaults = NIL;
FuncCandidateList newResult;
/*
- * Check if function has some parameter defaults if some
- * parameters are missing.
- */
- if (pronargs > nargs && expand_variadic)
- {
- bool isnull;
- Datum proargdefaults;
- char *str;
-
- /* skip when not enough default expressions */
- if (nargs + procform->pronargdefaults < pronargs)
- continue;
-
- proargdefaults = SysCacheGetAttr(PROCOID, proctup,
- Anum_pg_proc_proargdefaults, &isnull);
- Assert(!isnull);
- str = TextDatumGetCString(proargdefaults);
- defaults = (List *) stringToNode(str);
-
- Assert(IsA(defaults, List));
-
- /*
- * If we don't have to use all default parameters, we skip
- * some cells from the left.
- */
- defaults = list_copy_tail(defaults, procform->pronargdefaults - pronargs + nargs);
-
- pfree(str);
- }
-
- /*
* Check if function is variadic, and get variadic element type if so.
* If expand_variadic is false, we should just ignore variadic-ness.
*/
@@ -668,6 +658,7 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
{
va_elem_type = procform->provariadic;
variadic = OidIsValid(va_elem_type);
+ any_special |= variadic;
}
else
{
@@ -675,16 +666,24 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
variadic = false;
}
- Assert(!variadic || !defaults);
+ /*
+ * 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 &&
- (variadic ? (pronargs > nargs) : (defaults ? (pronargs < nargs) : (pronargs != nargs))))
+ if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
- Assert(!variadic || (pronargs <= nargs));
- Assert(!defaults || (pronargs > nargs));
-
if (OidIsValid(namespaceId))
{
/* Consider only procs in specified namespace */
@@ -723,7 +722,6 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
newResult->pathpos = pathpos;
newResult->oid = HeapTupleGetOid(proctup);
newResult->nargs = effective_nargs;
- newResult->argdefaults = defaults;
memcpy(newResult->args, procform->proargtypes.values,
pronargs * sizeof(Oid));
if (variadic)
@@ -737,128 +735,140 @@ FuncnameGetCandidates(List *names, int nargs, bool expand_variadic)
}
else
newResult->nvargs = 0;
-
- any_variadic = variadic || defaults;
+ newResult->ndargs = use_defaults ? pronargs - nargs : 0;
/*
* Does it have the same arguments as something we already accepted?
- * If so, decide which one to keep. We can skip this check for the
- * single-namespace case if no variadic match has been made, since
- * then the unique index on pg_proc guarantees all the matches have
- * different argument lists.
+ * If 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.
*/
- if (any_variadic || !OidIsValid(namespaceId))
+ if (resultList != NULL &&
+ (any_special || !OidIsValid(namespaceId)))
{
- if (defaults)
- effective_nargs = nargs;
-
/*
* If we have an ordered list from SearchSysCacheList (the normal
* case), then any conflicting proc must immediately adjoin this
* one in the list, so we only need to look at the newest result
* item. If we have an unordered list, we have to scan the whole
* result list. Also, if either the current candidate or any
- * previous candidate is a variadic match, we can't assume that
+ * previous candidate is a special match, we can't assume that
* conflicts are adjacent.
+ *
+ * We ignore defaulted arguments in deciding what is a match.
*/
- if (resultList)
+ FuncCandidateList prevResult;
+
+ if (catlist->ordered && !any_special)
{
- FuncCandidateList prevResult;
+ /* ndargs must be 0 if !any_special */
+ if (effective_nargs == resultList->nargs &&
+ memcmp(newResult->args,
+ resultList->args,
+ effective_nargs * sizeof(Oid)) == 0)
+ prevResult = resultList;
+ else
+ prevResult = NULL;
+ }
+ else
+ {
+ int cmp_nargs = newResult->nargs - newResult->ndargs;
- if (catlist->ordered && !any_variadic)
+ for (prevResult = resultList;
+ prevResult;
+ prevResult = prevResult->next)
{
- if (effective_nargs == resultList->nargs &&
+ if (cmp_nargs == prevResult->nargs - prevResult->ndargs &&
memcmp(newResult->args,
- resultList->args,
- effective_nargs * sizeof(Oid)) == 0)
- prevResult = resultList;
- else
- prevResult = NULL;
+ prevResult->args,
+ cmp_nargs * sizeof(Oid)) == 0)
+ break;
}
- else
+ }
+
+ if (prevResult)
+ {
+ /*
+ * We have a match with a previous result. Decide which one
+ * to keep, or mark it ambiguous if we can't decide. The
+ * logic here is preference > 0 means prefer the old result,
+ * preference < 0 means prefer the new, preference = 0 means
+ * ambiguous.
+ */
+ int preference;
+
+ if (pathpos != prevResult->pathpos)
{
- for (prevResult = resultList;
- prevResult;
- prevResult = prevResult->next)
- {
- if (!defaults)
- {
- if (effective_nargs == prevResult->nargs &&
- memcmp(newResult->args,
- prevResult->args,
- effective_nargs * sizeof(Oid)) == 0)
- break;
- }
- else
- {
- if (memcmp(newResult->args,
- prevResult->args,
- effective_nargs * sizeof(Oid)) == 0)
- break;
- }
- }
+ /*
+ * Prefer the one that's earlier in the search path.
+ */
+ preference = pathpos - prevResult->pathpos;
}
- if (prevResult)
+ else if (variadic && prevResult->nvargs == 0)
{
/*
- * We have a match with a previous result. Prefer the
- * one that's earlier in the search path.
+ * With variadic functions we could have, for example,
+ * both foo(numeric) and foo(variadic numeric[]) in
+ * the same namespace; if so we prefer the
+ * non-variadic match on efficiency grounds.
*/
- if (pathpos > prevResult->pathpos)
- {
- pfree(newResult);
- continue; /* keep previous result */
- }
- else if (pathpos == prevResult->pathpos)
+ preference = 1;
+ }
+ else if (!variadic && prevResult->nvargs > 0)
+ {
+ preference = -1;
+ }
+ else
+ {
+ /*----------
+ * We can't decide. This can happen with, for example,
+ * 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.
+ *----------
+ */
+ preference = 0;
+ }
+
+ if (preference > 0)
+ {
+ /* keep previous result */
+ pfree(newResult);
+ continue;
+ }
+ else if (preference < 0)
+ {
+ /* remove previous result from the list */
+ if (prevResult == resultList)
+ resultList = prevResult->next;
+ else
{
- /*
- * With variadic functions we could have, for example,
- * both foo(numeric) and foo(variadic numeric[]) in
- * the same namespace; if so we prefer the
- * non-variadic match on efficiency grounds. It's
- * also possible to have conflicting variadic
- * functions, such as foo(numeric, variadic numeric[])
- * and foo(variadic numeric[]). If you're silly
- * enough to do that, we throw an error. (XXX It'd be
- * better to detect such conflicts when the functions
- * are created.)
- */
- if (variadic)
- {
- if (prevResult->nvargs > 0)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("variadic function %s conflicts with another",
- func_signature_string(names, pronargs,
- procform->proargtypes.values))));
- /* else, previous result wasn't variadic */
- pfree(newResult);
- continue; /* keep previous result */
- }
+ FuncCandidateList prevPrevResult;
- if (defaults)
+ for (prevPrevResult = resultList;
+ prevPrevResult;
+ prevPrevResult = prevPrevResult->next)
{
- if (prevResult->argdefaults != NIL)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("functions with parameter defaults %s and %s are ambiguous",
- func_signature_string(names, pronargs, procform->proargtypes.values),
- func_signature_string(names, prevResult->nargs, prevResult->args))));
- /* else, previous result didn't have defaults */
- pfree(newResult);
- continue; /* keep previous result */
+ if (prevResult == prevPrevResult->next)
+ {
+ prevPrevResult->next = prevResult->next;
+ break;
+ }
}
-
- /* non-variadic can replace a previous variadic */
- Assert(prevResult->nvargs > 0);
+ Assert(prevPrevResult); /* assert we found it */
}
- /* replace previous result */
- prevResult->pathpos = pathpos;
- prevResult->oid = newResult->oid;
- prevResult->nvargs = newResult->nvargs;
- prevResult->argdefaults = newResult->argdefaults;
+ pfree(prevResult);
+ /* fall through to add newResult to list */
+ }
+ else
+ {
+ /* mark old result as ambiguous, discard new */
+ prevResult->oid = InvalidOid;
pfree(newResult);
- continue; /* args are same, of course */
+ continue;
}
}
}
@@ -922,7 +932,7 @@ FunctionIsVisible(Oid funcid)
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, false);
+ nargs, false, false);
for (; clist; clist = clist->next)
{
@@ -1191,6 +1201,7 @@ OpernameGetCandidates(List *names, char oprkind)
newResult->oid = HeapTupleGetOid(opertup);
newResult->nargs = 2;
newResult->nvargs = 0;
+ newResult->ndargs = 0;
newResult->args[0] = operform->oprleft;
newResult->args[1] = operform->oprright;
newResult->next = resultList;