summaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/namespace.c118
-rw-r--r--src/backend/catalog/pg_aggregate.c2
-rw-r--r--src/backend/parser/parse_func.c148
-rw-r--r--src/backend/parser/parse_oper.c69
-rw-r--r--src/backend/utils/adt/regproc.c19
-rw-r--r--src/backend/utils/adt/ruleutils.c2
6 files changed, 298 insertions, 60 deletions
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 8bd4d6c3d43..ed9aeee24bc 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -232,7 +232,7 @@ static void RemoveTempRelationsCallback(int code, Datum arg);
static void InvalidationCallback(Datum arg, int cacheid, uint32 hashvalue);
static bool MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
bool include_out_arguments, int pronargs,
- int **argnumbers);
+ int **argnumbers, int *fgc_flags);
/*
* Recomputing the namespace path can be costly when done frequently, such as
@@ -1117,15 +1117,15 @@ TypeIsVisibleExt(Oid typid, bool *is_missing)
/*
* FuncnameGetCandidates
- * Given a possibly-qualified function name and argument count,
+ * Given a possibly-qualified routine name, argument count, and arg names,
* retrieve a list of the possible matches.
*
- * If nargs is -1, we return all functions matching the given name,
+ * If nargs is -1, we return all routines matching the given name,
* 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.
+ * and only routines 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.
@@ -1183,14 +1183,21 @@ TypeIsVisibleExt(Oid typid, bool *is_missing)
* 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.
*
- * If missing_ok is true, an empty list (NULL) is returned if the name was
- * schema-qualified with a schema that does not exist. Likewise if no
- * candidate is found for other reasons.
+ * We return an empty list (NULL) if no suitable matches can be found.
+ * If the function name was schema-qualified with a schema that does not
+ * exist, then we return an empty list if missing_ok is true and otherwise
+ * throw an error. (missing_ok does not affect the behavior otherwise.)
+ *
+ * The output argument *fgc_flags is filled with a bitmask indicating how
+ * far we were able to match the supplied information. This is not of much
+ * interest if any candidates were found, but if not, it can help callers
+ * produce an on-point error message.
*/
FuncCandidateList
FuncnameGetCandidates(List *names, int nargs, List *argnames,
bool expand_variadic, bool expand_defaults,
- bool include_out_arguments, bool missing_ok)
+ bool include_out_arguments, bool missing_ok,
+ int *fgc_flags)
{
FuncCandidateList resultList = NULL;
bool any_special = false;
@@ -1203,15 +1210,20 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
/* check for caller error */
Assert(nargs >= 0 || !(expand_variadic | expand_defaults));
+ /* initialize output fgc_flags to empty */
+ *fgc_flags = 0;
+
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &funcname);
if (schemaname)
{
/* use exact schema given */
+ *fgc_flags |= FGC_SCHEMA_GIVEN; /* report that a schema is given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (!OidIsValid(namespaceId))
return NULL;
+ *fgc_flags |= FGC_SCHEMA_EXISTS; /* report that the schema exists */
}
else
{
@@ -1237,6 +1249,8 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
int *argnumbers = NULL;
FuncCandidateList newResult;
+ *fgc_flags |= FGC_NAME_EXISTS; /* the name is present in pg_proc */
+
if (OidIsValid(namespaceId))
{
/* Consider only procs in specified namespace */
@@ -1262,6 +1276,8 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
continue; /* proc is not in search path */
}
+ *fgc_flags |= FGC_NAME_VISIBLE; /* routine is in the right schema */
+
/*
* If we are asked to match to OUT arguments, then use the
* proallargtypes array (which includes those); otherwise use
@@ -1296,16 +1312,6 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
/*
* 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 */
@@ -1324,11 +1330,32 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
if (pronargs != nargs && !use_defaults)
continue;
+ /* We found a routine with a suitable number of arguments */
+ *fgc_flags |= FGC_ARGCOUNT_MATCH;
+
/* Check for argument name match, generate positional mapping */
if (!MatchNamedCall(proctup, nargs, argnames,
include_out_arguments, pronargs,
- &argnumbers))
+ &argnumbers, fgc_flags))
+ continue;
+
+ /*
+ * 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.
+ * However, we postpone the check until here because we want to
+ * perform argument name matching anyway (using the variadic array
+ * argument's name). This allows us to give an on-point error
+ * message if the user forgets to say VARIADIC in what would have
+ * been a valid call with it.
+ */
+ if (OidIsValid(procform->provariadic) && expand_variadic)
continue;
+ va_elem_type = InvalidOid;
+ variadic = false;
+
+ /* We found a fully-valid call using argument names */
+ *fgc_flags |= FGC_ARGNAMES_VALID;
/* Named argument matching is always "special" */
any_special = true;
@@ -1371,6 +1398,9 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
/* Ignore if it doesn't match requested argument count */
if (nargs >= 0 && pronargs != nargs && !variadic && !use_defaults)
continue;
+
+ /* We found a routine with a suitable number of arguments */
+ *fgc_flags |= FGC_ARGCOUNT_MATCH;
}
/*
@@ -1579,11 +1609,13 @@ FuncnameGetCandidates(List *names, int nargs, List *argnames,
* 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.
+ *
+ * We also add flag bits to *fgc_flags reporting on how far the match got.
*/
static bool
MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
bool include_out_arguments, int pronargs,
- int **argnumbers)
+ int **argnumbers, int *fgc_flags)
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
int numposargs = nargs - list_length(argnames);
@@ -1592,6 +1624,7 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
char **p_argnames;
char *p_argmodes;
bool arggiven[FUNC_MAX_ARGS];
+ bool arg_filled_twice = false;
bool isnull;
int ap; /* call args position */
int pp; /* proargs position */
@@ -1645,9 +1678,9 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
continue;
if (p_argnames[i] && strcmp(p_argnames[i], argname) == 0)
{
- /* fail if argname matches a positional argument */
+ /* note if argname matches a positional argument */
if (arggiven[pp])
- return false;
+ arg_filled_twice = true;
arggiven[pp] = true;
(*argnumbers)[ap] = pp;
found = true;
@@ -1664,6 +1697,16 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
Assert(ap == nargs); /* processed all actual parameters */
+ /* If we get here, the function did match all the supplied argnames */
+ *fgc_flags |= FGC_ARGNAMES_MATCH;
+
+ /* ... however, some of them might have been placed wrong */
+ if (arg_filled_twice)
+ return false; /* some argname matched a positional argument */
+
+ /* If we get here, the call doesn't have invalid mixed notation */
+ *fgc_flags |= FGC_ARGNAMES_NONDUP;
+
/* Check for default arguments */
if (nargs < pronargs)
{
@@ -1682,6 +1725,9 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
Assert(ap == pronargs); /* processed all function parameters */
+ /* If we get here, the call supplies all the required arguments */
+ *fgc_flags |= FGC_ARGNAMES_ALL;
+
return true;
}
@@ -1745,11 +1791,13 @@ FunctionIsVisibleExt(Oid funcid, bool *is_missing)
char *proname = NameStr(procform->proname);
int nargs = procform->pronargs;
FuncCandidateList clist;
+ int fgc_flags;
visible = false;
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- nargs, NIL, false, false, false, false);
+ nargs, NIL, false, false, false, false,
+ &fgc_flags);
for (; clist; clist = clist->next)
{
@@ -1882,9 +1930,20 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
*
* The returned items always have two args[] entries --- the first will be
* InvalidOid for a prefix oprkind. nargs is always 2, too.
+ *
+ * We return an empty list (NULL) if no suitable matches can be found. If the
+ * operator name was schema-qualified with a schema that does not exist, then
+ * we return an empty list if missing_schema_ok is true and otherwise throw an
+ * error. (missing_schema_ok does not affect the behavior otherwise.)
+ *
+ * The output argument *fgc_flags is filled with a bitmask indicating how
+ * far we were able to match the supplied information. This is not of much
+ * interest if any candidates were found, but if not, it can help callers
+ * produce an on-point error message.
*/
FuncCandidateList
-OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
+OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok,
+ int *fgc_flags)
{
FuncCandidateList resultList = NULL;
char *resultSpace = NULL;
@@ -1895,15 +1954,20 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
CatCList *catlist;
int i;
+ /* initialize output fgc_flags to empty */
+ *fgc_flags = 0;
+
/* deconstruct the name list */
DeconstructQualifiedName(names, &schemaname, &opername);
if (schemaname)
{
/* use exact schema given */
+ *fgc_flags |= FGC_SCHEMA_GIVEN; /* report that a schema is given */
namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
- if (missing_schema_ok && !OidIsValid(namespaceId))
+ if (!OidIsValid(namespaceId))
return NULL;
+ *fgc_flags |= FGC_SCHEMA_EXISTS; /* report that the schema exists */
}
else
{
@@ -1941,6 +2005,8 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
if (oprkind && operform->oprkind != oprkind)
continue;
+ *fgc_flags |= FGC_NAME_EXISTS; /* the name is present in pg_operator */
+
if (OidIsValid(namespaceId))
{
/* Consider only opers in specified namespace */
@@ -2014,6 +2080,8 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
}
}
+ *fgc_flags |= FGC_NAME_VISIBLE; /* operator is in the right schema */
+
/*
* Okay to add it to result list
*/
diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c
index c62e8acd413..a1cb5719a0c 100644
--- a/src/backend/catalog/pg_aggregate.c
+++ b/src/backend/catalog/pg_aggregate.c
@@ -836,6 +836,7 @@ lookup_agg_function(List *fnName,
Oid vatype;
Oid *true_oid_array;
FuncDetailCode fdresult;
+ int fgc_flags;
AclResult aclresult;
int i;
@@ -848,6 +849,7 @@ lookup_agg_function(List *fnName,
*/
fdresult = func_get_detail(fnName, NIL, NIL,
nargs, input_types, false, false, false,
+ &fgc_flags,
&fnOid, rettype, &retset,
&nvargs, &vatype,
&true_oid_array, NULL);
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 583bbbf232f..c43020a769d 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -42,6 +42,8 @@ typedef enum
FUNCLOOKUP_AMBIGUOUS,
} FuncLookupError;
+static int func_lookup_failure_details(int fgc_flags, List *argnames,
+ bool proc_call);
static void unify_hypothetical_args(ParseState *pstate,
List *fargs, int numAggregatedArgs,
Oid *actual_arg_types, Oid *declared_arg_types);
@@ -115,6 +117,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
int nvargs;
Oid vatype;
FuncDetailCode fdresult;
+ int fgc_flags;
char aggkind = 0;
ParseCallbackState pcbstate;
@@ -266,6 +269,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
fdresult = func_get_detail(funcname, fargs, argnames, nargs,
actual_arg_types,
!func_variadic, true, proc_call,
+ &fgc_flags,
&funcid, &rettype, &retset,
&nvargs, &vatype,
&declared_arg_types, &argdefaults);
@@ -563,8 +567,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("procedure %s is not unique",
func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
- errhint("Could not choose a best candidate procedure. "
- "You might need to add explicit type casts."),
+ errdetail("Could not choose a best candidate procedure."),
+ errhint("You might need to add explicit type casts."),
parser_errposition(pstate, location)));
else
ereport(ERROR,
@@ -572,8 +576,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("function %s is not unique",
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."),
+ errdetail("Could not choose a best candidate function."),
+ errhint("You might need to add explicit type casts."),
parser_errposition(pstate, location)));
}
else
@@ -601,7 +605,9 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
/*
* No function, and no column either. Since we're dealing with
- * function notation, report "function does not exist".
+ * function notation, report "function/procedure does not exist".
+ * Depending on what was returned in fgc_flags, we can add some color
+ * to that with detail or hint messages.
*/
if (list_length(agg_order) > 1 && !agg_within_group)
{
@@ -611,8 +617,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("function %s does not exist",
func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
- errhint("No aggregate function matches the given name and argument types. "
- "Perhaps you misplaced ORDER BY; ORDER BY must appear "
+ errdetail("No aggregate function matches the given name and argument types."),
+ errhint("Perhaps you misplaced ORDER BY; ORDER BY must appear "
"after all regular arguments of the aggregate."),
parser_errposition(pstate, location)));
}
@@ -622,8 +628,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("procedure %s does not exist",
func_signature_string(funcname, nargs, argnames,
actual_arg_types)),
- errhint("No procedure matches the given name and argument types. "
- "You might need to add explicit type casts."),
+ func_lookup_failure_details(fgc_flags, argnames,
+ proc_call),
parser_errposition(pstate, location)));
else
ereport(ERROR,
@@ -631,8 +637,8 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
errmsg("function %s does not exist",
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."),
+ func_lookup_failure_details(fgc_flags, argnames,
+ proc_call),
parser_errposition(pstate, location)));
}
@@ -905,6 +911,104 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
return retval;
}
+/*
+ * Interpret the fgc_flags and issue a suitable detail or hint message.
+ *
+ * Helper function to reduce code duplication while throwing a
+ * function-not-found error.
+ */
+static int
+func_lookup_failure_details(int fgc_flags, List *argnames, bool proc_call)
+{
+ /*
+ * If not FGC_NAME_VISIBLE, we shouldn't raise the question of whether the
+ * arguments are wrong. If the function name was not schema-qualified,
+ * it's helpful to distinguish between doesn't-exist-anywhere and
+ * not-in-search-path; but if it was, there's really nothing to add to the
+ * basic "function/procedure %s does not exist" message.
+ *
+ * Note: we passed missing_ok = false to FuncnameGetCandidates, so there's
+ * no need to consider FGC_SCHEMA_EXISTS here: we'd have already thrown an
+ * error if an explicitly-given schema doesn't exist.
+ */
+ if (!(fgc_flags & FGC_NAME_VISIBLE))
+ {
+ if (fgc_flags & FGC_SCHEMA_GIVEN)
+ return 0; /* schema-qualified name */
+ else if (!(fgc_flags & FGC_NAME_EXISTS))
+ {
+ if (proc_call)
+ return errdetail("There is no procedure of that name.");
+ else
+ return errdetail("There is no function of that name.");
+ }
+ else
+ {
+ if (proc_call)
+ return errdetail("A procedure of that name exists, but it is not in the search_path.");
+ else
+ return errdetail("A function of that name exists, but it is not in the search_path.");
+ }
+ }
+
+ /*
+ * Next, complain if nothing had the right number of arguments. (This
+ * takes precedence over wrong-argnames cases because we won't even look
+ * at the argnames unless there's a workable number of arguments.)
+ */
+ if (!(fgc_flags & FGC_ARGCOUNT_MATCH))
+ {
+ if (proc_call)
+ return errdetail("No procedure of that name accepts the given number of arguments.");
+ else
+ return errdetail("No function of that name accepts the given number of arguments.");
+ }
+
+ /*
+ * If there are argnames, and we failed to match them, again we should
+ * mention that and not bring up the argument types.
+ */
+ if (argnames != NIL && !(fgc_flags & FGC_ARGNAMES_MATCH))
+ {
+ if (proc_call)
+ return errdetail("No procedure of that name accepts the given argument names.");
+ else
+ return errdetail("No function of that name accepts the given argument names.");
+ }
+
+ /*
+ * We could have matched all the given argnames and still not have had a
+ * valid call, either because of improper use of mixed notation, or
+ * because of missing arguments, or because the user misused VARIADIC. The
+ * rules about named-argument matching are finicky enough that it's worth
+ * trying to be specific about the problem. (The messages here are chosen
+ * with full knowledge of the steps that namespace.c uses while checking a
+ * potential match.)
+ */
+ if (argnames != NIL && !(fgc_flags & FGC_ARGNAMES_NONDUP))
+ return errdetail("In the closest available match, "
+ "an argument was specified both positionally and by name.");
+
+ if (argnames != NIL && !(fgc_flags & FGC_ARGNAMES_ALL))
+ return errdetail("In the closest available match, "
+ "not all required arguments were supplied.");
+
+ if (argnames != NIL && !(fgc_flags & FGC_ARGNAMES_VALID))
+ return errhint("This call would be correct if the variadic array were labeled VARIADIC and placed last.");
+
+ if (fgc_flags & FGC_VARIADIC_FAIL)
+ return errhint("The VARIADIC parameter must be placed last, even when using argument names.");
+
+ /*
+ * Otherwise, the problem must be incorrect argument types.
+ */
+ if (proc_call)
+ (void) errdetail("No procedure of that name accepts the given argument types.");
+ else
+ (void) errdetail("No function of that name accepts the given argument types.");
+ return errhint("You might need to add explicit type casts.");
+}
+
/* func_match_argtypes()
*
@@ -1372,9 +1476,14 @@ func_select_candidate(int nargs,
* 1) check for possible interpretation as a type coercion request
* 2) apply the ambiguous-function resolution rules
*
- * 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.
+ * If there is no match at all, we return FUNCDETAIL_NOTFOUND, and *fgc_flags
+ * is filled with some flags that may be useful for issuing an on-point error
+ * message (see FuncnameGetCandidates).
+ *
+ * On success, 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
@@ -1400,6 +1509,7 @@ func_get_detail(List *funcname,
bool expand_variadic,
bool expand_defaults,
bool include_out_arguments,
+ int *fgc_flags, /* return value */
Oid *funcid, /* return value */
Oid *rettype, /* return value */
bool *retset, /* return value */
@@ -1424,7 +1534,8 @@ func_get_detail(List *funcname,
/* Get list of possible candidates from namespace search */
raw_candidates = FuncnameGetCandidates(funcname, nargs, fargnames,
expand_variadic, expand_defaults,
- include_out_arguments, false);
+ include_out_arguments, false,
+ fgc_flags);
/*
* Quickly check if there is an exact match to the input datatypes (there
@@ -1594,7 +1705,10 @@ func_get_detail(List *funcname,
*/
if (fargnames != NIL && !expand_variadic && nargs > 0 &&
best_candidate->argnumbers[nargs - 1] != nargs - 1)
+ {
+ *fgc_flags |= FGC_VARIADIC_FAIL;
return FUNCDETAIL_NOTFOUND;
+ }
*funcid = best_candidate->oid;
*nvargs = best_candidate->nvargs;
@@ -2053,6 +2167,7 @@ LookupFuncNameInternal(ObjectType objtype, List *funcname,
{
Oid result = InvalidOid;
FuncCandidateList clist;
+ int fgc_flags;
/* NULL argtypes allowed for nullary functions only */
Assert(argtypes != NULL || nargs == 0);
@@ -2062,7 +2177,8 @@ LookupFuncNameInternal(ObjectType objtype, List *funcname,
/* Get list of candidate objects */
clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false,
- include_out_arguments, missing_ok);
+ include_out_arguments, missing_ok,
+ &fgc_flags);
/* Scan list for a match to the arg types (if specified) and the objtype */
for (; clist != NULL; clist = clist->next)
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 0c4337563cf..7bd7a336fd6 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -72,7 +72,8 @@ static FuncDetailCode oper_select_candidate(int nargs,
Oid *operOid);
static void op_error(ParseState *pstate, List *op,
Oid arg1, Oid arg2,
- FuncDetailCode fdresult, int location);
+ FuncDetailCode fdresult, int fgc_flags, int location);
+static int oper_lookup_failure_details(int fgc_flags, bool is_unary_op);
static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key,
List *opname, Oid ltypeId, Oid rtypeId,
int location);
@@ -373,6 +374,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
Oid operOid;
OprCacheKey key;
bool key_ok;
+ int fgc_flags = 0;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup = NULL;
@@ -404,7 +406,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
FuncCandidateList clist;
/* Get binary operators of given name */
- clist = OpernameGetCandidates(opname, 'b', false);
+ clist = OpernameGetCandidates(opname, 'b', false, &fgc_flags);
/* No operators found? Then fail... */
if (clist != NULL)
@@ -434,7 +436,8 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
make_oper_cache_entry(&key, operOid);
}
else if (!noError)
- op_error(pstate, opname, ltypeId, rtypeId, fdresult, location);
+ op_error(pstate, opname, ltypeId, rtypeId,
+ fdresult, fgc_flags, location);
return (Operator) tup;
}
@@ -520,6 +523,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
Oid operOid;
OprCacheKey key;
bool key_ok;
+ int fgc_flags = 0;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup = NULL;
@@ -551,7 +555,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
FuncCandidateList clist;
/* Get prefix operators of given name */
- clist = OpernameGetCandidates(op, 'l', false);
+ clist = OpernameGetCandidates(op, 'l', false, &fgc_flags);
/* No operators found? Then fail... */
if (clist != NULL)
@@ -585,7 +589,8 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
make_oper_cache_entry(&key, operOid);
}
else if (!noError)
- op_error(pstate, op, InvalidOid, arg, fdresult, location);
+ op_error(pstate, op, InvalidOid, arg,
+ fdresult, fgc_flags, location);
return (Operator) tup;
}
@@ -621,30 +626,68 @@ op_signature_string(List *op, Oid arg1, Oid arg2)
static void
op_error(ParseState *pstate, List *op,
Oid arg1, Oid arg2,
- FuncDetailCode fdresult, int location)
+ FuncDetailCode fdresult, int fgc_flags, int location)
{
if (fdresult == FUNCDETAIL_MULTIPLE)
ereport(ERROR,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("operator is not unique: %s",
op_signature_string(op, arg1, arg2)),
- errhint("Could not choose a best candidate operator. "
- "You might need to add explicit type casts."),
+ errdetail("Could not choose a best candidate operator."),
+ errhint("You might need to add explicit type casts."),
parser_errposition(pstate, location)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("operator does not exist: %s",
op_signature_string(op, arg1, arg2)),
- (!arg1 || !arg2) ?
- errhint("No operator matches the given name and argument type. "
- "You might need to add an explicit type cast.") :
- errhint("No operator matches the given name and argument types. "
- "You might need to add explicit type casts."),
+ oper_lookup_failure_details(fgc_flags, (!arg1 || !arg2)),
parser_errposition(pstate, location)));
}
/*
+ * Interpret the fgc_flags and issue a suitable detail or hint message.
+ */
+static int
+oper_lookup_failure_details(int fgc_flags, bool is_unary_op)
+{
+ /*
+ * If not FGC_NAME_VISIBLE, we shouldn't raise the question of whether the
+ * arguments are wrong. If the operator name was not schema-qualified,
+ * it's helpful to distinguish between doesn't-exist-anywhere and
+ * not-in-search-path; but if it was, there's really nothing to add to the
+ * basic "operator does not exist" message.
+ *
+ * Note: we passed missing_ok = false to OpernameGetCandidates, so there's
+ * no need to consider FGC_SCHEMA_EXISTS here: we'd have already thrown an
+ * error if an explicitly-given schema doesn't exist.
+ */
+ if (!(fgc_flags & FGC_NAME_VISIBLE))
+ {
+ if (fgc_flags & FGC_SCHEMA_GIVEN)
+ return 0; /* schema-qualified name */
+ else if (!(fgc_flags & FGC_NAME_EXISTS))
+ return errdetail("There is no operator of that name.");
+ else
+ return errdetail("An operator of that name exists, but it is not in the search_path.");
+ }
+
+ /*
+ * Otherwise, the problem must be incorrect argument type(s).
+ */
+ if (is_unary_op)
+ {
+ (void) errdetail("No operator of that name accepts the given argument type.");
+ return errhint("You might need to add an explicit type cast.");
+ }
+ else
+ {
+ (void) errdetail("No operator of that name accepts the given argument types.");
+ return errhint("You might need to add explicit type casts.");
+ }
+}
+
+/*
* make_op()
* Operator expression construction.
*
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index af17a3421a0..e5c2246f2c9 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -71,6 +71,7 @@ regprocin(PG_FUNCTION_ARGS)
RegProcedure result;
List *names;
FuncCandidateList clist;
+ int fgc_flags;
/* Handle "-" or numeric OID */
if (parseDashOrOid(pro_name_or_oid, &result, escontext))
@@ -93,7 +94,8 @@ regprocin(PG_FUNCTION_ARGS)
if (names == NIL)
PG_RETURN_NULL();
- clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true);
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true,
+ &fgc_flags);
if (clist == NULL)
ereturn(escontext, (Datum) 0,
@@ -164,13 +166,15 @@ regprocout(PG_FUNCTION_ARGS)
{
char *nspname;
FuncCandidateList clist;
+ int fgc_flags;
/*
* Would this proc be found (uniquely!) by regprocin? If not,
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
- -1, NIL, false, false, false, false);
+ -1, NIL, false, false, false, false,
+ &fgc_flags);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
@@ -231,6 +235,7 @@ regprocedurein(PG_FUNCTION_ARGS)
int nargs;
Oid argtypes[FUNC_MAX_ARGS];
FuncCandidateList clist;
+ int fgc_flags;
/* Handle "-" or numeric OID */
if (parseDashOrOid(pro_name_or_oid, &result, escontext))
@@ -251,8 +256,8 @@ regprocedurein(PG_FUNCTION_ARGS)
escontext))
PG_RETURN_NULL();
- clist = FuncnameGetCandidates(names, nargs, NIL, false, false,
- false, true);
+ clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false, true,
+ &fgc_flags);
for (; clist; clist = clist->next)
{
@@ -483,6 +488,7 @@ regoperin(PG_FUNCTION_ARGS)
Oid result;
List *names;
FuncCandidateList clist;
+ int fgc_flags;
/* Handle "0" or numeric OID */
if (parseNumericOid(opr_name_or_oid, &result, escontext))
@@ -502,7 +508,7 @@ regoperin(PG_FUNCTION_ARGS)
if (names == NIL)
PG_RETURN_NULL();
- clist = OpernameGetCandidates(names, '\0', true);
+ clist = OpernameGetCandidates(names, '\0', true, &fgc_flags);
if (clist == NULL)
ereturn(escontext, (Datum) 0,
@@ -572,13 +578,14 @@ regoperout(PG_FUNCTION_ARGS)
else
{
FuncCandidateList clist;
+ int fgc_flags;
/*
* Would this oper be found (uniquely!) by regoperin? If not,
* qualify it.
*/
clist = OpernameGetCandidates(list_make1(makeString(oprname)),
- '\0', false);
+ '\0', false, &fgc_flags);
if (clist != NULL && clist->next == NULL &&
clist->oid == oprid)
result = pstrdup(oprname);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 3d6e6bdbfd2..0408a95941d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -13265,6 +13265,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
bool use_variadic;
char *nspname;
FuncDetailCode p_result;
+ int fgc_flags;
Oid p_funcid;
Oid p_rettype;
bool p_retset;
@@ -13323,6 +13324,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
p_result = func_get_detail(list_make1(makeString(proname)),
NIL, argnames, nargs, argtypes,
!use_variadic, true, false,
+ &fgc_flags,
&p_funcid, &p_rettype,
&p_retset, &p_nvargs, &p_vatype,
&p_true_typeids, NULL);