diff --git a/src/backend/commands/amcmds.c b/src/backend/commands/amcmds.c index 1ab2e469ee..784d7553e8 100644 --- a/src/backend/commands/amcmds.c +++ b/src/backend/commands/amcmds.c @@ -254,7 +254,7 @@ lookup_index_am_handler_func(List *handler_name, char amtype) errmsg("handler function is not specified"))); /* handlers have one argument of type internal */ - handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false); + handlerOid = LookupFuncName(handler_name, 1, funcargtypes, false, false); /* check that handler has the correct return type */ switch (amtype) diff --git a/src/backend/commands/conversioncmds.c b/src/backend/commands/conversioncmds.c index 5afe86712f..122573464f 100644 --- a/src/backend/commands/conversioncmds.c +++ b/src/backend/commands/conversioncmds.c @@ -77,7 +77,7 @@ CreateConversionCommand(CreateConversionStmt *stmt) * a qualified name. */ funcoid = LookupFuncName(func_name, sizeof(funcargs) / sizeof(Oid), - funcargs, false); + funcargs, false, false); /* Check it returns VOID, else it's probably the wrong function */ if (get_func_rettype(funcoid) != VOIDOID) diff --git a/src/backend/commands/event_trigger.c b/src/backend/commands/event_trigger.c index e508334981..34f72114fa 100644 --- a/src/backend/commands/event_trigger.c +++ b/src/backend/commands/event_trigger.c @@ -239,7 +239,7 @@ CreateEventTrigger(CreateEventTrigStmt *stmt) stmt->trigname))); /* Find and validate the trigger function. */ - funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false); + funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false, false); funcrettype = get_func_rettype(funcoid); if (funcrettype != EVTTRIGGEROID) ereport(ERROR, diff --git a/src/backend/commands/foreigncmds.c b/src/backend/commands/foreigncmds.c index 5ada4a8594..16c9242241 100644 --- a/src/backend/commands/foreigncmds.c +++ b/src/backend/commands/foreigncmds.c @@ -484,7 +484,8 @@ lookup_fdw_handler_func(DefElem *handler) return InvalidOid; /* handlers have no arguments */ - handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false); + handlerOid = LookupFuncName((List *) handler->arg, 0, funcargtypes, false, + false); /* check that handler has correct return type */ if (get_func_rettype(handlerOid) != FDW_HANDLEROID) @@ -511,7 +512,8 @@ lookup_fdw_validator_func(DefElem *validator) funcargtypes[0] = TEXTARRAYOID; funcargtypes[1] = OIDOID; - return LookupFuncName((List *) validator->arg, 2, funcargtypes, false); + return LookupFuncName((List *) validator->arg, 2, funcargtypes, false, + false); /* validator's return value is ignored, so we don't check the type */ } diff --git a/src/backend/commands/operatorcmds.c b/src/backend/commands/operatorcmds.c index f7c00e2f48..ade1a23550 100644 --- a/src/backend/commands/operatorcmds.c +++ b/src/backend/commands/operatorcmds.c @@ -208,7 +208,7 @@ DefineOperator(List *names, List *parameters) typeId[1] = typeId2; nargs = 2; } - functionOid = LookupFuncName(functionName, nargs, typeId, false); + functionOid = LookupFuncName(functionName, nargs, typeId, false, false); /* * We require EXECUTE rights for the function. This isn't strictly @@ -271,7 +271,7 @@ ValidateRestrictionEstimator(List *restrictionName) typeId[2] = INTERNALOID; /* args list */ typeId[3] = INT4OID; /* varRelid */ - restrictionOid = LookupFuncName(restrictionName, 4, typeId, false); + restrictionOid = LookupFuncName(restrictionName, 4, typeId, false, false); /* estimators must return float8 */ if (get_func_rettype(restrictionOid) != FLOAT8OID) @@ -312,12 +312,12 @@ ValidateJoinEstimator(List *joinName) * arguments, but we still allow the old 4-argument form. Try the * preferred form first. */ - joinOid = LookupFuncName(joinName, 5, typeId, true); + joinOid = LookupFuncName(joinName, 5, typeId, true, false); if (!OidIsValid(joinOid)) - joinOid = LookupFuncName(joinName, 4, typeId, true); + joinOid = LookupFuncName(joinName, 4, typeId, true, false); /* If not found, reference the 5-argument signature in error msg */ if (!OidIsValid(joinOid)) - joinOid = LookupFuncName(joinName, 5, typeId, false); + joinOid = LookupFuncName(joinName, 5, typeId, false, false); /* estimators must return float8 */ if (get_func_rettype(joinOid) != FLOAT8OID) diff --git a/src/backend/commands/proclang.c b/src/backend/commands/proclang.c index b010d23466..2fe4d84997 100644 --- a/src/backend/commands/proclang.c +++ b/src/backend/commands/proclang.c @@ -107,7 +107,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) * return type. */ funcname = SystemFuncName(pltemplate->tmplhandler); - handlerOid = LookupFuncName(funcname, 0, funcargtypes, true); + handlerOid = LookupFuncName(funcname, 0, funcargtypes, true, false); if (OidIsValid(handlerOid)) { funcrettype = get_func_rettype(handlerOid); @@ -155,7 +155,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) { funcname = SystemFuncName(pltemplate->tmplinline); funcargtypes[0] = INTERNALOID; - inlineOid = LookupFuncName(funcname, 1, funcargtypes, true); + inlineOid = LookupFuncName(funcname, 1, funcargtypes, true, + false); if (!OidIsValid(inlineOid)) { tmpAddr = ProcedureCreate(pltemplate->tmplinline, @@ -197,7 +198,7 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) { funcname = SystemFuncName(pltemplate->tmplvalidator); funcargtypes[0] = OIDOID; - valOid = LookupFuncName(funcname, 1, funcargtypes, true); + valOid = LookupFuncName(funcname, 1, funcargtypes, true, false); if (!OidIsValid(valOid)) { tmpAddr = ProcedureCreate(pltemplate->tmplvalidator, @@ -262,7 +263,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) * Lookup the PL handler function and check that it is of the expected * return type */ - handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false); + handlerOid = LookupFuncName(stmt->plhandler, 0, funcargtypes, false, + false); funcrettype = get_func_rettype(handlerOid); if (funcrettype != LANGUAGE_HANDLEROID) { @@ -291,7 +293,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) if (stmt->plinline) { funcargtypes[0] = INTERNALOID; - inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false); + inlineOid = LookupFuncName(stmt->plinline, 1, funcargtypes, false, + false); /* return value is ignored, so we don't check the type */ } else @@ -301,7 +304,8 @@ CreateProceduralLanguage(CreatePLangStmt *stmt) if (stmt->plvalidator) { funcargtypes[0] = OIDOID; - valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false); + valOid = LookupFuncName(stmt->plvalidator, 1, funcargtypes, false, + false); /* return value is ignored, so we don't check the type */ } else diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 2daffae8cd..b1a4850406 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -669,7 +669,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, * Find and validate the trigger function. */ if (!OidIsValid(funcoid)) - funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false); + funcoid = LookupFuncName(stmt->funcname, 0, fargtypes, false, false); if (!isInternal) { aclresult = pg_proc_aclcheck(funcoid, GetUserId(), ACL_EXECUTE); diff --git a/src/backend/commands/tsearchcmds.c b/src/backend/commands/tsearchcmds.c index cda21675f0..41f2ab3f58 100644 --- a/src/backend/commands/tsearchcmds.c +++ b/src/backend/commands/tsearchcmds.c @@ -109,7 +109,7 @@ get_ts_parser_func(DefElem *defel, int attnum) nargs = 0; /* keep compiler quiet */ } - procOid = LookupFuncName(funcName, nargs, typeId, false); + procOid = LookupFuncName(funcName, nargs, typeId, false, false); if (get_func_rettype(procOid) != retTypeId) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), @@ -680,7 +680,7 @@ get_ts_template_func(DefElem *defel, int attnum) nargs = 0; /* keep compiler quiet */ } - procOid = LookupFuncName(funcName, nargs, typeId, false); + procOid = LookupFuncName(funcName, nargs, typeId, false, false); if (get_func_rettype(procOid) != retTypeId) ereport(ERROR, (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 9ca30b0443..b4f9c8897a 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -1699,28 +1699,28 @@ findTypeInputFunction(List *procname, Oid typeOid) */ argList[0] = CSTRINGOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (OidIsValid(procOid)) return procOid; argList[1] = OIDOID; argList[2] = INT4OID; - procOid = LookupFuncName(procname, 3, argList, true); + procOid = LookupFuncName(procname, 3, argList, true, false); if (OidIsValid(procOid)) return procOid; /* No luck, try it with OPAQUE */ argList[0] = OPAQUEOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (!OidIsValid(procOid)) { argList[1] = OIDOID; argList[2] = INT4OID; - procOid = LookupFuncName(procname, 3, argList, true); + procOid = LookupFuncName(procname, 3, argList, true, false); } if (OidIsValid(procOid)) @@ -1765,14 +1765,14 @@ findTypeOutputFunction(List *procname, Oid typeOid) */ argList[0] = typeOid; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (OidIsValid(procOid)) return procOid; /* No luck, try it with OPAQUE */ argList[0] = OPAQUEOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (OidIsValid(procOid)) { @@ -1814,14 +1814,14 @@ findTypeReceiveFunction(List *procname, Oid typeOid) */ argList[0] = INTERNALOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (OidIsValid(procOid)) return procOid; argList[1] = OIDOID; argList[2] = INT4OID; - procOid = LookupFuncName(procname, 3, argList, true); + procOid = LookupFuncName(procname, 3, argList, true, false); if (OidIsValid(procOid)) return procOid; @@ -1844,7 +1844,7 @@ findTypeSendFunction(List *procname, Oid typeOid) */ argList[0] = typeOid; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (OidIsValid(procOid)) return procOid; @@ -1867,7 +1867,7 @@ findTypeTypmodinFunction(List *procname) */ argList[0] = CSTRINGARRAYOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (!OidIsValid(procOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), @@ -1894,7 +1894,7 @@ findTypeTypmodoutFunction(List *procname) */ argList[0] = INT4OID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (!OidIsValid(procOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), @@ -1921,7 +1921,7 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid) */ argList[0] = INTERNALOID; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (!OidIsValid(procOid)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), @@ -1997,7 +1997,7 @@ findRangeCanonicalFunction(List *procname, Oid typeOid) */ argList[0] = typeOid; - procOid = LookupFuncName(procname, 1, argList, true); + procOid = LookupFuncName(procname, 1, argList, true, false); if (!OidIsValid(procOid)) ereport(ERROR, @@ -2039,7 +2039,7 @@ findRangeSubtypeDiffFunction(List *procname, Oid subtype) argList[0] = subtype; argList[1] = subtype; - procOid = LookupFuncName(procname, 2, argList, true); + procOid = LookupFuncName(procname, 2, argList, true, false); if (!OidIsValid(procOid)) ereport(ERROR, diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c index 6963922b0e..752f1cc43a 100644 --- a/src/backend/parser/parse_clause.c +++ b/src/backend/parser/parse_clause.c @@ -934,7 +934,7 @@ transformRangeTableSample(ParseState *pstate, RangeTableSample *rts) */ funcargtypes[0] = INTERNALOID; - handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true); + handlerOid = LookupFuncName(rts->method, 1, funcargtypes, true, false); /* we want error to complain about no-such-method, not no-such-function */ if (!OidIsValid(handlerOid)) diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 4661fc4f62..6f69d360f6 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -2032,17 +2032,21 @@ func_signature_string(List *funcname, int nargs, * namespace search path. * * If the function is not found, we return InvalidOid if noError is true, - * else raise an error. + * else raise an error. missing_ok is similar to noError except we still + * raise an error for an ambigious function, regardless of what missing_ok is + * set to. */ Oid -LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) +LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError, + bool missing_ok) { FuncCandidateList clist; /* Passing NULL for argtypes is no longer allowed */ Assert(argtypes); - clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, noError); + clist = FuncnameGetCandidates(funcname, nargs, NIL, false, false, + noError || missing_ok); /* * If no arguments were specified, the name must yield a unique candidate. @@ -2065,7 +2069,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) } else { - if (!noError) + if (!noError && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("could not find a function named \"%s\"", @@ -2080,7 +2084,7 @@ LookupFuncName(List *funcname, int nargs, const Oid *argtypes, bool noError) clist = clist->next; } - if (!noError) + if (!noError && !missing_ok) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("function %s does not exist", @@ -2135,10 +2139,11 @@ LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError) /* * When looking for a function or routine, we pass noError through to - * LookupFuncName and let it make any error messages. Otherwise, we make - * our own errors for the aggregate and procedure cases. + * LookupFuncName's missing_ok parameter and let it make any error + * messages. Otherwise, we make our own errors for the aggregate and + * procedure cases. */ - oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, + oid = LookupFuncName(func->objname, func->args_unspecified ? -1 : argcount, argoids, false, (objtype == OBJECT_FUNCTION || objtype == OBJECT_ROUTINE) ? noError : true); if (objtype == OBJECT_FUNCTION) diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h index ff1477e64f..4e8f7af0f3 100644 --- a/src/include/parser/parse_func.h +++ b/src/include/parser/parse_func.h @@ -63,7 +63,7 @@ extern const char *func_signature_string(List *funcname, int nargs, List *argnames, const Oid *argtypes); extern Oid LookupFuncName(List *funcname, int nargs, const Oid *argtypes, - bool noError); + bool noError, bool missing_ok); extern Oid LookupFuncWithArgs(ObjectType objtype, ObjectWithArgs *func, bool noError); diff --git a/src/test/regress/expected/drop_if_exists.out b/src/test/regress/expected/drop_if_exists.out index b80c5ed2d8..dd4e6c0f6d 100644 --- a/src/test/regress/expected/drop_if_exists.out +++ b/src/test/regress/expected/drop_if_exists.out @@ -138,6 +138,16 @@ DROP FUNCTION test_function_exists(int, text, int[]); ERROR: function test_function_exists(integer, text, integer[]) does not exist DROP FUNCTION IF EXISTS test_function_exists(int, text, int[]); NOTICE: function test_function_exists(pg_catalog.int4,text,pg_catalog.int4[]) does not exist, skipping +-- Ensure we still receive an ambiguous function error when there are +-- multiple matching functions. +CREATE FUNCTION test_ambiguous_funcname(int) returns int as $$ select $1; $$ language sql; +CREATE FUNCTION test_ambiguous_funcname(text) returns text as $$ select $1; $$ language sql; +DROP FUNCTION IF EXISTS test_ambiguous_funcname; +ERROR: function name "test_ambiguous_funcname" is not unique +HINT: Specify the argument list to select the function unambiguously. +-- cleanup +DROP FUNCTION test_ambiguous_funcname(int); +DROP FUNCTION test_ambiguous_funcname(text); -- aggregate DROP AGGREGATE test_aggregate_exists(*); ERROR: aggregate test_aggregate_exists(*) does not exist diff --git a/src/test/regress/sql/drop_if_exists.sql b/src/test/regress/sql/drop_if_exists.sql index c1d30bc4a5..ad4ef76460 100644 --- a/src/test/regress/sql/drop_if_exists.sql +++ b/src/test/regress/sql/drop_if_exists.sql @@ -153,6 +153,16 @@ DROP FUNCTION IF EXISTS test_function_exists(); DROP FUNCTION test_function_exists(int, text, int[]); DROP FUNCTION IF EXISTS test_function_exists(int, text, int[]); +-- Ensure we still receive an ambiguous function error when there are +-- multiple matching functions. +CREATE FUNCTION test_ambiguous_funcname(int) returns int as $$ select $1; $$ language sql; +CREATE FUNCTION test_ambiguous_funcname(text) returns text as $$ select $1; $$ language sql; +DROP FUNCTION IF EXISTS test_ambiguous_funcname; + +-- cleanup +DROP FUNCTION test_ambiguous_funcname(int); +DROP FUNCTION test_ambiguous_funcname(text); + -- aggregate DROP AGGREGATE test_aggregate_exists(*); DROP AGGREGATE IF EXISTS test_aggregate_exists(*);