diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/namespace.c | 118 | ||||
-rw-r--r-- | src/backend/catalog/pg_aggregate.c | 2 | ||||
-rw-r--r-- | src/backend/parser/parse_func.c | 148 | ||||
-rw-r--r-- | src/backend/parser/parse_oper.c | 69 | ||||
-rw-r--r-- | src/backend/utils/adt/regproc.c | 19 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 2 |
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); |